站里有 1682 篇教程和六十多个在线工具,但入口一直是"关键词搜索"和"手动点"。这次我给它接了个 AI 助手:能直接问"Go 的 goroutine 调度在哪篇教程里",也能让它顺手算个 md5。这篇记录从设计到上线的完整过程,包括五个把我坑到的细节。
一、先泼一盆冷水:这台机器只有 2 tok/s
助手跑在一台 2 核 VPS 上(实际是 1 个物理核 + 超线程),本地 1.5B 模型的生成速度是 2 token/s。
这个数字直接否掉了"自主 Agent"的方案。规划、反思、多轮 ReAct 那套东西,一次问答要跑 5~8 轮模型调用,用户得等五六分钟;而且小模型的 JSON 遵循能力本来就弱,轮次越多、失败率越接近 1。
所以走的是工具路由器:模型只负责"选哪个工具",真正干活的是我写好的 Go 函数。
问题 → 决策轮(只输出一个 JSON,160 token)→ 执行工具(毫秒级)→ 最多 2 轮
→ 成文轮(流式,把工具结果写成自然语言)
决策轮不生成自然语言,这是省时间的关键:160 token 的 JSON,2 tok/s 也就 20 多秒。
二、能干什么
17 个工具,分三类:
| 类 | 工具 |
|---|---|
| 知识库 | kb_search(检索)、kb_article(读正文)、kb_toc(列教程目录) |
| 站点 | tool_list(工具清单)、blog_latest(最近博客) |
| 计算 | md5/sha1/sha256、base64、url 编解码、uuid、json_format、text_stats、timestamp |
回答里会附来源卡片,点进去直接看原文——小模型说的话不一定对,能核对原文很重要。
三、五个坑
坑 1:模型会编造确定性结果。 让它算 hello 的 md5,工具调用完全正确,返回 5d41402abc4b2a76b9719d911017c592;然后让它"基于结果回答",它输出 9e10...——不是抄错,是重新算了一个错的。
解法是短路:哈希、编码、UUID、时间戳这类工具的结果由代码直接返回,根本不进入模型成文环节。顺带把这类问答从 100 秒降到 12 秒。
坑 2:工具名混淆。 我一开始只给了一个通用入口 tool_run(tool, input),实测模型输出的是 {"tool":"sha1","args":{...}}——它分不清"工具名"和"参数里的工具名"。改成把每个可执行工具注册成一等工具,少一层嵌套,成功率立刻上来。
坑 3:同一个工具反复调用。 第二轮又把 kb_toc 调了一遍,结果一模一样,白烧 20 秒。加了「工具名+参数」去重,重复调用只提醒它收尾,不再执行。
坑 4:协议漂移。 让它用一句话回答,它给的是 {"final":"{\"error\":\"工具 'sha1' 不存在\"}"}——final 里嵌了另一个 JSON。现在的规则是:final 必须是自然语言,JSON 形态一律丢弃并走兜底。
坑 5:nginx 把慢回答掐断。 /api 默认 proxy_read_timeout 60s,而一次回答要 100 秒,决策轮和成文之间的空档就超时了,前端只显示"网络错误",服务端日志里一切正常。给 /api/v1/agent/ 单独开了一段:读超时 300s、proxy_buffering off、gzip off,再让后端补一个 X-Accel-Buffering: no。
四、实测
| 提问 | 工具调用 | 结果 | 耗时 |
|---|---|---|---|
| 帮我算一下 hello 的 md5 | md5 | 5d41402abc4b2a76b9719d911017c592 | 12s |
| 站里有 Go 相关的教程吗 | kb_toc | 正确列出 Go/GoFrame 教程 | 60s |
| 有哪些 Docker 教程 | tool_list + kb_toc | 正确列出教程与面试八股 | 104s |
页面是 SSE 流式:先看到 meta,再看到"🔧 kb_search(...)"的工具标签,然后回答一个字一个字冒出来。等待的焦虑感比等一个转圈圈低得多——这可能是我这次做的最重要的产品决定。
五、保护那唯一一个核
- 每 IP 每天 30 次,超了返回 429 并提示明天再来
- 全局并发 1,排队最多 30 秒(这台机器只能同时算一个)
- 用户断开就取消生成(监听
Request.Context()),别白烧 CPU - 模型只监听
127.0.0.1,API Key 只在 supervisor 环境变量里
六、说实话:它的局限
- 慢。50 字回答约 25 秒,知识库类回答 1~2 分钟。它是"问一个问题等一个答案",不是聊天。
- 会笨。小模型组织语言的能力有限,即使有工具结果也可能说得含糊——所以来源卡片和"以原文为准"的提示必须放在页面上。
- 配额小。30 次/天是故意的,因为它和站点抢同一个物理核。
但它的价值也很清楚:把"确定性边界"划出来之后,2 tok/s 的模型也能用。确定性计算交给代码,协议校验交给代码,超时和配额交给网关,模型只做它擅长的那点事——选工具、组织一句话。
七、代码在哪
后端 internal/agent/(llm.go 客户端 / tools.go 注册表 / agent.go 编排),HTTP 层是 SSE + 配额 + 并发闸门,前端一个页面加一个组件,nginx 单独一段 location。连前端带后端不到 1000 行。
设计细节和踩坑的完整版写在知识库的「AI Agent 实战」教程里(第 33~35 章),这里就不重复了。
如果你也想给自己的站加一个,建议先问自己一个问题:你的模型有多快? 答案会直接决定你是做"自主 Agent"还是"工具路由器"——这两个东西的复杂度差一个数量级。