Building a Multi‑Agent AI System: Easy‑Agent’s Foreman, Coder, and Researcher
This article explains how the easy‑agent project evolved from a single monolithic AI into a multi‑agent architecture with specialized Foreman, Coder, and Researcher agents, covering design principles, communication mechanisms, task decomposition, fault tolerance, parallel execution, observability, and future extensions, complete with code examples and open‑source links.
01 From Single Agent to Multi‑Dimensional Agent: Architectural Evolution
After adding streaming interaction, observability, and multimodal capabilities to easy-agent, a single all‑purpose agent behaves like a Swiss‑army knife—capable of many tasks but inefficient when switching between tool selection, code execution, and debugging. The solution is a collaborative system where each agent specializes in a domain:
Foreman : task decomposition and scheduling
Coder : code writing, execution, and debugging
Researcher : information retrieval and knowledge organization
# Multi‑Agent definition in configuration file
agents:
foreman:
role: "foreman"
system_prompt: |
You are a "Foreman" Agent. Your task is to decompose user requests and assign them to appropriate "worker" Agents.
allowed_tools: [call_coder, call_researcher]
coder:
role: "coder"
system_prompt: |
You are a "Coder" Agent responsible for all code‑related tasks.
allowed_tools: [run_code, read_file, write_file, git_cmd]02 Core Mechanism: Communication Bridge Between Agents
The key to multi‑agent collaboration is a unified communication mechanism. Two core tools enable cross‑agent calls.
Agent Call Tool
type CallCoderTool struct{}
func (t *CallCoderTool) Run(ctx context.Context, argsJSON string, a *Agent, events <-chan StreamEvent) (string, error) {
// Decode task description
var args struct {
Task struct {
Description string `json:"description"`
} `json:"task"`
}
// Get Coder Agent instance
coderAgent := a.otherAgents["coder"]
// Start sub‑agent streaming execution
subAgentEvents := make(chan StreamEvent)
go coderAgent.StreamRunWithSessionAndImages(ctx, args.Task.Description, "", nil, "", subAgentEvents)
// Forward sub‑agent events to main channel
for event := range subAgentEvents {
events <- event // show sub‑agent thinking to user
}
return "", nil
}Key Quote: "True collaboration is not simple task hand‑off, but transparent sharing of thought processes."
Seamless Event Stream Transfer
When Foreman calls Coder, every step—thinking, tool invocation, code output—is streamed to the user, creating the illusion of a single continuous agent.
03 Making Collaboration Smarter: Task Decomposition
Intelligent Task Judgment
system_prompt: |
You are a "Foreman" Agent. Your task is to decompose user requests and assign them to appropriate "worker" Agents.
You can use the following tools:
- call_coder: invoke the Coder Agent for code writing, modification, review, and execution.
- call_researcher: invoke the Researcher Agent for web and knowledge‑base searches.
Please select and call the appropriate tool based on the nature of the task.Concrete Collaboration Example
User request: "Help me write a Python crawler to scrape Douban Top 250 movies."
Foreman's reasoning:
Identify task type: code writing → call Coder.
Construct task description: translate user need into a concrete programming task.
Invoke Coder Agent.
call_coder(task="编写Python爬虫爬取豆瓣电影Top250数据")Coder's execution:
Analyze technical solution: choose requests + BeautifulSoup.
Write code implementing the crawler.
Debug and test in a sandbox.
Return usable crawler code.
[Foreman] 正在思考如何响应...
[Foreman] 检测到代码任务,正在调用Coder Agent...
[Coder] 正在分析技术方案...
[Coder] 编写爬虫代码中...
[Coder] 在沙箱中运行代码...
[Coder] 代码运行成功!返回结果...04 Fault Tolerance: Making Collaboration Reliable
Graceful Degradation Strategy
// Fault‑tolerance handling in CallCoderTool
for event := range subAgentEvents {
if event.Type == "error" {
Logger.Error().Str("coder_error", p.Message).Msg("Coder Agent failed")
return fmt.Errorf("coder agent error: %s", p.Message)
}
}Retry and Fallback
Record error information.
Attempt alternative implementations.
If necessary, report the issue to the user with suggestions.
05 Performance Optimization: Parallel Execution
Concurrent Tool Invocation
func (a *Agent) handleToolCalls(ctx context.Context, toolCalls []ToolCall, sessionID string, events chan<- StreamEvent) []ChatMessage {
var wg sync.WaitGroup
toolResults := make(chan ChatMessage, len(toolCalls))
for _, toolCall := range toolCalls {
wg.Add(1)
go func(tc ToolCall) {
defer wg.Done()
result := a.execTool(ctx, &fc, sessionID, events)
toolResults <- result
}(toolCall)
}
wg.Wait()
close(toolResults)
return nil
}When Foreman needs both Coder and Researcher, they run in parallel, greatly improving efficiency.
06 User Experience: Seamless Collaborative Interaction
Continuous thinking process
Real‑time execution output
Unified final answer
Intelligent Role Labels
// Add agent role information to StreamEvent
events <- StreamEvent{
Type: "thinking",
Payload: ThinkingEventPayload{
Text: fmt.Sprintf("[%s] %s", agentRole, thinkingText),
},
} [Foreman] 正在分析任务... [Coder] 编写Python代码中... [Researcher] 搜索相关信息中...07 Monitoring and Debugging: Distributed Tracing
Distributed Tracing Integration
ctx, span := tracer.Start(ctx, "Agent.CallCoder",
trace.WithAttributes(
attribute.String("foreman_agent", a.role),
attribute.String("target_agent", "coder"),
attribute.String("task_description", args.Task.Description),
),
)
defer span.End()Performance Dashboard
Execution time per agent
Call chain between agents
Precise bottleneck identification
08 Live Demo: Power of Collaboration
User request: "Write a Go REST API that fetches today’s hot news and generates API docs."
Foreman analyzes the composite task.
Calls Researcher: call_researcher("搜索今天的热门新闻") Researcher fetches news data.
Foreman decides to generate the API.
Calls Coder:
call_coder("用Go语言编写REST API,返回新闻数据,并生成文档")Coder writes, tests, and generates Swagger documentation.
09 Architectural Evolution: Towards an Extensible Ecosystem
Scalable Agent Ecosystem
Tester Agent – dedicated to code testing.
Deployment Agent – handles deployment.
Monitor Agent – responsible for monitoring.
Agent Marketplace Concept
Standardized agent interfaces.
Version management for agents.
Capability descriptions for discoverability.
10 Summary
Intelligent task decomposition – Foreman accurately identifies and assigns tasks.
Specialized agents – each focuses on its domain.
Transparent collaboration – users see a continuous workflow.
Graceful fault tolerance – single failures don’t break the system.
Parallel execution – overall efficiency gains.
Full observability – every step is visible.
Future plans include shared agent memory, dynamic agent creation, and performance‑based agent selection.
Source code is fully open‑source:
GitHub: https://github.com/louis-xie-programmer/easy-agent
Gitee: https://gitee.com/louis_xie/easy-agent
Code Wrench
Focuses on code debugging, performance optimization, and real-world engineering, sharing efficient development tips and pitfall guides. We break down technical challenges in a down-to-earth style, helping you craft handy tools so every line of code becomes a problem‑solving weapon. 🔧💻
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
