2026年6月6日 1 分钟阅读

150 行代码构建 AI Agent CLI:拆解工具调用的四个核心组件

tinyash 0 条评论

如果你用过 Claude Code、Codex CLI 或 Cursor 的 Agent 模式,会发现它们的核心能力出奇一致:理解你的自然语言需求,然后调用合适的工具完成操作。这个「理解→调用→响应」循环看似复杂,但其核心逻辑可以用约 150 行代码实现。

Go Micro 团队最近发布了一篇技术博客,拆解了他们在 micro CLI 中实现的 AI Agent 架构。本文在原文基础上做框架无关的扩展分析,让你理解所有 AI 编码 Agent 背后通用的设计模式。

四大核心组件

任何一个工具调用型(Tool-Calling)Agent 都由四个部分组成:

  1. 工具发现(Tool Discovery)——让 LLM 知道「你能调用什么」
  2. 模型创建(Model Creation)——连接 LLM 提供商
  3. 对话记忆(Conversation History)——保持上下文连续性
  4. 主循环(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 的「大脑」,负责协调其余三个组件:

  1. 读取用户输入
  2. 将用户输入 + 对话历史 + 工具清单一起发送给 LLM
  3. 如果 LLM 决定调用某个工具:执行工具调用,把结果追加到对话历史,然后回到步骤 2
  4. 如果 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 的描述中找到正确的那个?这已经超出了「工具发现」的范畴,进入了工具检索的领域,是另一个值得深入的话题。

发表评论

你的邮箱地址不会被公开,带 * 的为必填项。