AgentHub 开发手记(一):用 Go 跑通 ReAct 和第一个真实工具
最近我在用 Go 写一个叫 AgentHub 的项目,想把 AI Agent 从概念变成自己能看懂、能运行的程序。
第一阶段的目标很具体:给程序一个自然语言任务,让模型选择工具,程序执行工具,再把结果交回模型,最后得到回答。最开始用 Echo 验证流程,接着把已有的图片处理服务接进来。
这篇记录的是 2026 年 9 月 10 日的开发过程:项目骨架、图片工具注册,以及随后对忽略规则的修正。
先把模型、工具和循环分开
项目刚开始时,我把主要代码分成了三部分:
| 部分 | 负责什么 |
|---|---|
| 模型客户端 | 把消息发给 GLM,取回模型回复 |
| 工具 | 执行具体操作,比如回显文本、缩放图片 |
| Agent 内核 | 维护消息历史,解析回复,选择工具并控制循环 |
这样分的直接好处是,图片处理的细节可以留在图片工具里。Agent 内核只需要知道工具叫什么、能做什么,以及怎么调用它。
工具接口很小:
1 | type Tool interface { |
Name 用于查找工具,Description 会进入提示词,Execute 才真正执行操作。模型根据描述提出工具调用,Go 程序根据名字查表,再调用对应的 Execute。
这也让我把职责想清楚了:模型输出工具名和参数之后,还需要程序完成解析、分发、执行和结果回传。
ReAct 在代码里是一段有限循环
这一版 AgentHub 使用文本协议。模型需要调用工具时,输出 Action 和 Action Input;能够回答时,输出 Final Answer。
例如,下面是一段用于说明协议的文本,并非某次运行日志:
1 | Action: image_scaling |
内核的执行顺序是:
1 | 准备系统提示词和用户问题 |
这里有一个很容易忽略的地方:第二次调用模型时,要把之前的过程一起发过去。
如果只发原始问题,模型就看不到工具刚刚返回的结果。因此 messages 会不断追加模型回复和工具反馈,让下一轮有依据继续处理。
当前这套文本协议把工具结果作为带有 Observation: 前缀的 user 消息回传,每轮最多处理一个工具请求。它的实现比较直接,适合先跟清楚调用链;后续如果迁移到原生结构化工具调用,还需要调整协议和消息表示。
从 Echo 走到真正的图片处理
最开始用 Echo,是因为它可以把输入原样返回。这样我能先确认工具名称分发和结果回传是否连得起来。
接下来注册 image_scaling,让 Agent 调用本地图片处理服务。用户传入的是这样的任务:
1 | 把 ./picture/window.jpg 缩放到宽度 256 |
模型需要把自然语言转换成工具接受的参数。图片工具再依次完成这些工作:
- 拆出图片路径和目标宽度,检查字段数量及宽度范围。
- 根据文件扩展名检查是否为支持的 JPEG 或 PNG。
- 打开本地文件,把图片和宽度组装为上传请求。
- 请求图片服务的
/resize接口。 - 读取返回的图片字节,保存文件并返回输出路径。
这样,一次自然语言任务就和已有的 HTTP 服务连接起来了。Agent 内核仍然只调用 Execute,上传和保存文件的细节由工具负责。
接口能接住对象,还要看对象有没有准备好
接图片工具时,我也碰到了 Go 里一个很实际的问题:实现了接口,并不意味着这个类型可以直接用零值运行。
Echo 本身不依赖外部配置,用 Echo{} 就能工作。图片工具需要服务地址和 HTTP 客户端,所以要通过构造函数创建:
1 | imageprocessing.NewImageScaling("http://localhost:8080/resize") |
这里还涉及接收者类型。图片工具的方法使用指针接收者,放进工具列表的是 *ImageScaling;Echo 使用值接收者,可以直接放入 Echo{}。
以后再接工具,我会同时看两件事:类型是否满足接口,以及执行时依赖的状态是否已经初始化。
把失败也放进循环里考虑
这版循环除了正常调用,还有几条失败路径:
- 模型请求失败:返回错误,结束本次运行。
- 模型指定了不存在的工具:把错误写成 Observation,交回模型继续处理。
- 工具执行失败:把工具错误交回模型,允许下一轮根据反馈调整。
- 循环次数耗尽:返回“循环耗尽,未能得到最终答案”。
命令行入口将最大循环次数设为 5,整个任务使用 60 秒的超时上下文。这样至少能给一次运行设置明确的次数和时间边界。
不过,5 轮只是停止条件,不能证明任务完成。一次成功的图片实验,也不能说明这些失败路径都已经被充分验证。第一阶段还没有自动化测试,这些都需要后面继续补。
一个和 Agent 无关、但很容易踩的坑
同一天,我还修正了 .gitignore。
原文件的规则带有前导空格,一些说明文字也没有写成注释。修复时把规则顶格书写、给说明行加上 #,并统一了换行格式。
这个问题提醒我,配置文件写得看起来整齐,并不能证明规则实际生效。尤其是 .env 这样的文件,应该用 git check-ignore 检查匹配结果。也要记住,忽略规则不会自动移除已经被 Git 跟踪的文件。
这一阶段留下的问题
到这里,AgentHub 已经能够接收命令行任务,通过模型选择工具,调用图片服务,再根据工具结果生成最终回答。
但当时还有一个明显的问题:运行结束后主要看到的是最终答案,中间到底调用了什么、传了什么参数、哪里慢了,都不够清楚。
如果输出不符合预期,我需要区分是模型回复格式出了问题,还是解析器、工具或外部服务出了问题。
