150 行代码构建 AI Agent CLI:拆解工具调用的四个核心组件
如果你用过 Claude Code、Codex CLI 或 Cursor 的 Agent 模式,会发现它们的核心能力出奇一致:理解你的自然语言需求,然后调用合适的工具完成操作。这个「理解→调用→响应」循环看似复杂,但其核心逻辑可以用约 150 行代码实现。
Go Micro 团队最近发布了一篇技术博客,拆解了他们在 micro CLI 中实现的 AI Agent 架构。本文在原文基础上做框架无关的扩展分析,让你理解所有 AI 编码 Agent 背后通用的设计模式。
四大核心组件
任何一个工具调用型(Tool-Calling)Agent 都由四个部分组成:
- 工具发现(Tool Discovery)——让 LLM 知道「你能调用什么」
- 模型创建(Model Creation)——连接 LLM 提供商
- 对话记忆(Conversation History)——保持上下文连续性
- 主循环(Main Loop)——处理「输入→推理→执行→响应」的循环
我们逐一拆解。
组件一:工具发现
LLM 需要知道它可以使用哪些功能、每个功能的参数是什么。如果用 Go Micro 框架,服务注册在 Registry 中,每个端点的请求类型和字段元数据可以直接转换为工具列表。
不依赖框架的通用做法是手动定义一个工具清单:
type Tool struct {
Name string
Description string
Parameters map[string]interface{}
}
关键点不在于语言或框架,而在于你给 LLM 的工具描述要足够清晰。以「创建用户」为例:
- Name:
create_user - Description: “创建一个新用户账户,包含姓名和邮箱”
- Parameters:
{"name": "string", "email": "string", "role": "string (optional)"}
如果把服务接口的文档注释(doc comment)直接映射为 LLM 的工具描述,就达到了「代码即文档即工具定义」的效果。
组件二:模型创建
Agent 需要一个 LLM 后端来完成推理。Go Micro 使用统一的 model.New() 接口,支持 Anthropic、OpenAI、Gemini、Groq、Mistral、Together 等多个提供商——切换只需改一个字符串参数。
model, err := model.New("claude-sonnet-4-20250514",
model.WithMaxTokens(4096),
)
这种抽象层的价值在于:你不绑定任何一个提供商。开发时用便宜的模型调试,生产环境切到更强的模型,只需一行改动。
组件三:对话记忆
LLM 本身是无状态的——每次请求都是独立的。Agent 需要手动维护消息列表,把用户输入和模型回复依次追加进去,再在下一轮请求时一起发送。
type History struct {
messages []Message
maxSize int
}
func (h *History) Add(msg Message) {
h.messages = append(h.messages, msg)
if len(h.messages) > h.maxSize {
h.messages = h.messages[1:]
}
}
注意:对话历史过长会导致 Token 消耗暴涨,所以通常有一个大小限制(如保留最近 20 轮对话),或者在接近限制时自动摘要压缩。
组件四:主循环
这是 Agent 的「大脑」,负责协调其余三个组件:
- 读取用户输入
- 将用户输入 + 对话历史 + 工具清单一起发送给 LLM
- 如果 LLM 决定调用某个工具:执行工具调用,把结果追加到对话历史,然后回到步骤 2
- 如果 LLM 直接返回文本回复:输出给用户,追加到对话历史,回到步骤 1
for {
input := readUserInput()
history.Add(UserMessage(input))
for {
response := model.Complete(systemPrompt, tools, history.All())
if response.HasToolCalls() {
for _, call := range response.ToolCalls {
result := executeTool(call.Name, call.Args)
history.Add(ToolResult(call.ID, result))
}
} else {
fmt.Println(response.Text)
history.Add(AssistantMessage(response.Text))
break
}
}
}
这个内层循环是关键——LLM 可能一次调用多个工具,也可能在拿到工具结果后决定再调另一个工具。直到它认为任务完成,才会返回最终文本给用户。
从 150 行到生产级
Go Micro 的核心实现只有约 40 行,加上 CLI 参数处理和提供商配置总共约 220 行。从 150 行原型到生产级 Agent,你还可以添加:
- 确认环节:在调用有副作用的工具前要求用户确认(如「即将删除 3 条记录,继续吗?」)
- 审计日志:将每次工具调用记录到文件或可观测性平台
- 权限控制:限制 Agent 只能看到特定服务或端点
- 持久化记忆:让 Agent 在会话间记住用户的偏好配置
核心启示
Go Micro 的这篇博客揭示了一个被忽视的事实:AI Agent 的工具调用架构本质上是一个简单的消息循环——工具发现提供可调用项,LLM 做决策,执行器执行,记忆保持上下文。框架的价值在于让这些组件之间的衔接更顺畅,但基本模式在任何语言和任何框架中都是一样的。
无论你是用 Go、Python 还是 TypeScript,只要理解了这四个组件的协作关系,就能从零构建一个可用的 AI Agent CLI。
相关链接:Build Your Own AI Agent CLI in 150 Lines — Go Micro Blog
延伸思考:下一个问题是——当 Agent 能调用 100 个工具时,如何让 LLM 在数千 Token 的描述中找到正确的那个?这已经超出了「工具发现」的范畴,进入了工具检索的领域,是另一个值得深入的话题。