给个人站加一个 AI 助手:把知识库和 17 个工具接进对话里

2 tok/s 的本地模型跑不动自主 Agent,所以做成工具路由器:模型只选工具,确定性计算交给代码。这篇记录 17 个工具、SSE 流式、五个坑(哈希被编造、工具名混淆、重复调用、嵌套 JSON、nginx 60 秒掐断)和线上实测。

站里有 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 的 md5md55d41402abc4b2a76b9719d911017c59212s
站里有 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 环境变量里

六、说实话:它的局限

  1. 慢。50 字回答约 25 秒,知识库类回答 1~2 分钟。它是"问一个问题等一个答案",不是聊天。
  2. 会笨。小模型组织语言的能力有限,即使有工具结果也可能说得含糊——所以来源卡片和"以原文为准"的提示必须放在页面上。
  3. 配额小。30 次/天是故意的,因为它和站点抢同一个物理核。

但它的价值也很清楚:把"确定性边界"划出来之后,2 tok/s 的模型也能用。确定性计算交给代码,协议校验交给代码,超时和配额交给网关,模型只做它擅长的那点事——选工具、组织一句话。

七、代码在哪

后端 internal/agent/(llm.go 客户端 / tools.go 注册表 / agent.go 编排),HTTP 层是 SSE + 配额 + 并发闸门,前端一个页面加一个组件,nginx 单独一段 location。连前端带后端不到 1000 行。

设计细节和踩坑的完整版写在知识库的「AI Agent 实战」教程里(第 33~35 章),这里就不重复了。


如果你也想给自己的站加一个,建议先问自己一个问题:你的模型有多快? 答案会直接决定你是做"自主 Agent"还是"工具路由器"——这两个东西的复杂度差一个数量级。

返回博客列表