AgentHub 开发手记(三):让回答有依据,补上检索、用量统计与参数校验
前两个阶段,AgentHub 已经能通过模型选择工具,并记录模型和工具的调用过程。接下来,我想让它处理一个更接近业务的问题:根据商店提供的资料回答售后咨询。
这件事把几个问题放到了一起:回答依据从哪里来,每轮请求到底带了多少内容,以及模型给出的工具参数应该怎样检查。
这篇记录的是截至 2026 年 10 月 9 日完成的知识检索、模型用量统计和独立图片参数校验。这里的演示商店政策都是练习用的虚构资料。
先做一条能跟清楚的检索链路
我先把演示商店的退货、保修和运费政策写进 Markdown 文档,用二级标题划分片段,再增加 knowledge_search 工具。
工具接受空格分隔的关键词,比如:
1 | 耳机 拆封 退货 |
当前执行过程是:
- 检查上下文是否已取消,拒绝空关键词输入。
- 读取知识文档,按二级标题拆分片段。
- 将关键词和片段转换为小写,做子串匹配。
- 一个片段只要命中任意关键词,就加入结果。
- 返回片段内容,并附上来源文件名。
因此,工具返回给模型的是资料片段,模型再根据这些片段生成回答。
返回格式会包含这样的来源标识:
1 | 来源:after_sales.md |
把来源一起回传,可以帮助回答保留依据。不过,模型最终是否引用正确、是否严格遵守资料,仍然需要检查;工具提供资料不能自动保证答案正确。
无资料,也应该是一个明确结果
检索工具里有两类需要分开的情况。
如果文件读取失败、文档没有预期的二级标题,或者输入关键词为空,那是执行或输入错误,会返回 error。
如果文档正常读取,只是没有片段命中,则返回:
未找到相关资料。当前知识库无法提供这个问题的答案。
工具描述也要求模型在资料不足时说明信息不足。这样,模型下一轮能知道发生的是“正常检索但没有结果”,而不是把它误当成工具运行失败。
这只能提供明确反馈,不能保证模型每次都遵守。后续仍需要用固定问题检查有资料、无资料和资料冲突时的回答表现。
关键词匹配的不足已经能看见
当前规则是“任意关键词命中就返回片段”。它简单,但也容易带回过多内容。
例如,耳机 拆封 退货 这组词,在当前演示文档里会命中七天退货、耳机保修和退货运费三个片段。模型要处理的资料比直接回答拆封退货问题所需的更多。
接下来可以考虑统计关键词命中数、排序并只返回前 K 个片段。但这一版还没有评分、排序和 Top-K,也没有接入向量检索。
先保留这条简单链路,可以把“用户问题 → 检索参数 → 资料片段 → 最终回答”逐步检查清楚,再决定检索能力应该怎样升级。
用量信息要表达“有没有取得”
随着循环继续,历史消息和工具结果会被送进下一轮模型请求。为了观察这一过程,我把模型客户端的返回值从正文字符串改成了 Response:
1 | type Response struct { |
Usage 使用指针,是为了区分接口返回了用量数据,还是根本没有返回。
没有取得用量时,控制台会显示“未取得”。如果只得到部分调用的用量,任务结束时会说明累计数字只包含已取得的部分。
这样可以避免把缺失数据打印成零,让一个看起来很精确的数字造成误解。
目前还分别记录模型调用次数和取得用量的调用次数,并通过延迟执行的结束回调汇总。因此提前返回或循环耗尽时,也会输出本次运行已经收集到的统计。
字符数、Token 和费用分别看
模型调用前,程序会记录消息条数和消息正文的 rune 数;调用后,如果接口提供了 usage,再记录输入、输出和总 Token 数。
正文字符数可以帮助观察输入是否不断增长,但它不等于 Token 数,也不包含模型接口所有潜在的协议开销。Token 应以接口实际返回的数据为准。
同样,用量统计还不等于费用统计。这一版没有接入价格表、预算控制或自动压缩。我现在能够观察的是每轮输入变化、调用耗时,以及已取得的 Token 用量。
这些数据会帮助后续判断:检索结果是否太多,是否反复带入了不必要的历史,以及一次任务大概经历了多少次模型调用。
独立参数校验先把规则写清楚
这一阶段还增加了 internal/tool/validation 包,用 JSON Schema 校验图片缩放参数。
输入样例是:
1 | {"path": "picture/window.jpg", "width": 256} |
当前规则要求根值为对象,必须包含字符串 path 和整数 width,宽度范围为 10 到 4096。
对应测试覆盖了边界值、缺失字段、类型错误和 JSON 格式错误。例如:
| 输入情况 | 当前结果 |
|---|---|
| 宽度为 10 或 4096 | 通过 |
| 宽度为 9 或 4097 | 规则校验失败 |
宽度为字符串 "128" | 规则校验失败 |
宽度为 128.0 | 通过,数值没有小数部分 |
宽度为 128.5 | 规则校验失败 |
| 缺少必需字段 | 规则校验失败 |
| 空路径或额外字段 | 当前规则允许 |
| JSON 损坏或连续包含两个值 | JSON 解析失败 |
“空路径允许”和“额外字段允许”也是现有规则的真实行为。测试把它们明确记录下来,方便以后决定是否收紧规则。
这个校验器不会检查路径对应的文件是否存在、是否允许访问,也不会执行图片处理。数据满足 schema,只说明它满足当前定义的结构和取值规则。
当前校验尚未进入工具执行链路
这一点需要单独说清楚:独立校验器已经有实现和测试,但还没有接入 ImageScaling.Execute 或 Agent 循环。
当前图片工具仍然接收“路径 宽度”的文本参数,并在自己的 Execute 里做原有检查。Agent 循环也仍然直接调用工具,没有完成执行前的权限判断。
因此,目前还不能说“所有工具已经统一使用 JSON 参数”或“Agent 已有完整权限系统”。把当前的接口和测试单独做好,是后续接入执行链路的准备。
这一阶段完成的内容
到这里,AgentHub 有了三个工具:Echo、图片缩放和知识检索。模型与工具调用有轨迹,模型返回值能携带可选用量,图片参数也有独立的规则校验。
