如果你最近在做 Agent 类的应用,大概率见过同一个尴尬场景:让模型去查一份文档,它语气肯定地回了一段根本不存在的内容。我做了一个给 Agent 用的网站,把关键流程和数据结构拆成 skills 放进去,核心目标就一句话——让 AI 更可靠。这篇文章不绕弯子,直接讲清楚我为什么做这个网站、skills 是怎么设计的、落地过程中踩过的坑,以及什么样的场景适合直接照搬这套思路。
这个站点本质上是一个很薄的信息与工具层:人能打开页面看文档,Agent 能通过结构化接口取数据、取技能说明。除了页面,我更做了一整套“技能包”,让模型知道自己在什么情况下该做什么、输入长什么样、输出该返回成什么。做之前我总觉得 Agent 不靠谱是模型的锅,做完之后才意识到,很多时候是我们没有给模型一个“不犯错”的抓手。
这篇文章适合三类人看:正在做 Agent 应用的开发者、想让提示词工程更工程化的 AI 爱好者,以及被模型幻觉折腾到头疼的技术产品经理。下面内容全部来自真实实践,能直接搬走的部分不少。
1. 为什么要给 Agent 单独做一个网站
1.1 Agent 的不可靠来自三个地方
大语言模型从原理上就不是一个可靠的知识库,而是一台擅长模式补全的机器。它训练时的数据有截止日期,也不可能连接到你业务里的真实配置和个人数据。这种先天条件决定了,Agent 一旦需要处理精确信息,就很容易“一本正经地胡说八道”。
我复盘了之前做的几个自动化脚本,问题基本集中在三处。第一是事实性幻觉,模型会从上下文里拼凑一个看似合理但完全不存在的参数值;第二是知识过时,同一个问题隔一个月问,结果可能天差地别;第三是输出格式不稳定,明明要求返回 JSON,它时不时给你夹带一段 Markdown 解释。
这些问题的解法有一个共同思路:不要指望模型“记住”,而是给它一个随时能查、随时能调用的体外大脑。换成人话就是,员工记不住公司所有流程没关系,公司只要有一套随叫随到的作业指导书就行。我的这个网站,承担的就是“作业指导书”的角色。
1.2 网站比“多写提示词”可靠在哪
有人会问,为什么不把所有信息直接塞进 system prompt?我也是从这条路试过来的,效果只能说短效。提示词越长,模型越容易抓不住重点,而且每次改业务规则都要重新发一次上下文,Token 成本也直线上升。更麻烦的是,提示词没有版本管理,时间一长根本不知道哪句话在起作用。
也有人建议上向量数据库,把文档切块存进去搞语义检索。向量检索适合“找相似”,不适合“给准确答案”。你问它某个字段的枚举值有哪些,它可能召回八段相关文本,但没有一段包含全部枚举值。这种不确定性对 Agent 来说是致命的,因为它不知道该信哪句。
网站方案的优势非常明显。一是结构化接口,API 返回什么就是什么,字段一一对应,不靠模型猜测。二是可管理性,每个技能有版本、有测试、有变更记录,更新线上就是发布一次代码。三是人机同读,页面既能被解析也能被直接浏览,人工 review 或者调试的时候非常直观。
2. 网站整体设计与 skills 结构
2.1 给 Agent 用的页面不该只是“人看的文档”
最开始我犯过一个错,把网站做成了一堆好看的文档页面,排版精美、配图齐全,结果 Agent 根本不知道怎么用,因为它需要的是确定的机器可读内容。后来我把站点拆成了两层:表层是给人看的说明页,里层是给 Agent 用的结构化数据接口。
整个网站以“能力点”为单位组织,每个能力点对应一个技能。比如“数据校验”是一个技能,“订单状态查询”是另一个技能。每个技能在网站上有一个专属页面,页面里用 JSON Schema 描述输入输出,同时挂着一份 YAML 格式的技能定义文件。模型如果走 HTTP 工具访问,它会先读取技能总列表,再根据任务选择对应技能,最后按 schema 提交参数。
这里的顺序很重要:先让模型“看到有什么”,再让它“选要做什么”,最后才是“具体怎么做”。如果一上来就把几十个技能的完整说明全部堆给它,模型会在选择阶段就迷路。设计成列表加详情的两层结构之后,整个调用链路清晰了很多。
2.2 定义 skills:让能力变成可复用资产
skills 是整个项目的核心资产,我把每个技能都写成一个 YAML 文件,统一放在站点的一个目录下,构建时自动生成页面和接口。YAML 的好处是方便人读、容易做 diff,后续走代码审核和版本回滚都很自然。
一个技能文件至少要包含这几个字段:name、description、version、input_schema、instruction、examples、test_cases。其中最容易写错的是description,它决定了模型什么时候会想起调用这个技能,写得太长模型没耐心看,写得太泛又会在多个技能间混淆。我用了一个经验:描述控制在两句话内,第一句说做什么,第二句说什么时候用。
instruction是技能内部的操作指引,目的是约束模型的执行过程,而不是让它自由发挥。例如“只校验不修改”“错误必须逐条列出”“如果输入缺少字段,直接返回错误而不是猜测”等规则,都放在这一块。配上examples和test_cases之后,每个技能就成了一组可回归测试的代码资产,而不只是一段提示文本。
name:>tools: - name: site_skill_runner url: https://agent-tool.your-domain/api/run method: POST headers: Content-Type: application/json body: skill_name: "{skill_name}" arguments: "{arguments}"接入完成后,模型遇到新任务时,会先读技能列表,再决定调用哪个技能。这一步是整个体系的临门一脚,但也是最容易翻车的一步,因为技能描述质量不行,模型很容易在列表阶段就选错。
3.2 一个技能跑通全链路:数据校验示例
拿“数据校验”这个技能来演示全链路。用户在对话里发来一段常见 JSON 数据,说“帮我看看哪里有问题”。原本的应对方式?是我们百度在 short 答案中回指令。其实是先直接处理。
现在改了链路之后,先让模型识别这是数据校验任务,从技能列表里选中>{ "skill_name": "data-validator", "arguments": { "records": [ { "id": 1, "price": "abc" }, { "id": 2, "price": 19.9 } ], "rules": { "price": "number" } } }
站点返回:
{ "report": { "valid": false, "errors": [ { "field": "price", "type": "type_mismatch", "reason": "第 1 条记录的 price 是字符串,期望 number" } ] } }模型拿到这份报告后,只需要把它转述给用户即可,不需要自己判断“价格字段应该是什么类型”。这类看似简单的场景,恰恰是最容易出幻觉的地方。没有这套外部网站时,模型极有可能按自己的理解补一个“应该是数字”的结论,如果上下文里有另一条旧数据,它甚至会给出冲突结果。现在幻觉被前置校验挡住了,模型只需要做翻译,不需要做判断。
调试技能时我会看三样东西:调用日志里的完整请求、站点返回的状态码和错误体、以及 Agent 最终输出给用户的话。只要这三层连起来看,就能快速定位问题是出在技能选择、参数构造,还是执行逻辑。我习惯给每个技能配一个本地测试脚本,改完 YAML 或执行逻辑先跑一遍用例,再放到真实环境里跑一轮。
4. 常见问题与排查实录
4.1 高频故障:现象、原因与解法
| 故障现象 | 直接原因 | 排查与处理 |
|---|---|---|
| Agent 返回的内容明显与实际数据不符 | 技能选择错误或未调用,模型自己生成了答案 | 检查技能 description 是否准确,日志里是否有调用记录 |
| 调用技能后请求超时 | 第三方 API 响应过慢或数据量过大 | 拆分技能粒度,增加缓存,限制单次返回体大小 |
| 返回结果不是预期 JSON | 执行逻辑抛错,被模型临场发挥修复 | 统一异常捕获,接口层保证永远返回标准结构 |
| 模型频繁选错技能 | 描述过于含糊或多个技能重叠 | 精简描述,明确“什么时候用”,合并重叠技能 |
| 更新技能后 Agent 仍用旧逻辑 | 客户端或站点侧缓存未清除 | 显式在响应体里带 version 字段,并在日志标记版本 |
这份表格里的问题基本我都遇到过。其中超时最折磨人,根源是我在某个技能里一次性把一个月的数据全部返回,一个响应体就有几千行。后来我把数据改成按天分页,模型需要更多时间就分多次调用,响应体控制在几十 KB 以内,超时问题基本消失。
4.2 调试技巧与我的避坑经验
第一个经验是:description的价值大于instruction。模型在做工具选择时,首先看的就是技能名称和描述,如果描述写得像一篇小作文,模型反而不知道在哪个场景触发它。我后来把所有技能描述都压缩成两句话,并在第二句固定加上触发条件,命中率立刻提高不少。
第二个经验是:一定要给技能配测试用例。最初我没写测试,改了一个技能的校验规则,结果另一个依赖它的场景直接出问题。现在每次变更技能,我都会先跑一遍test_cases,确保老功能没有被破坏。这个过程和传统软件开发做回归测试没有区别,技能就是代码,不能只当文本看。
第三个经验是:不要指望一个技能解决所有问题。技能设计得越细,模型越容易找准边界。宁可多设计几个小技能,也不要把十个功能揉进一个技能。揉进去之后,参数 Schema 会变得非常复杂,模型构造参数时就会开始犯错,最后还是要拆开。
还有一个容易被忽略的点是页面与接口的同步。最初我手写了详情页,又单独维护 YAML 文件,内容经常对不上。后来改成“YAML 为唯一数据源,页面和接口都从 YAML 渲染生成”,这类问题才彻底消失。给 Agent 用的站点,一定要让所有机器可读信息收敛到一处。
5. 边界与后续:这套方案适合谁,还能怎么长
5.1 别把它当成万能药
这套方案的适用边界很明显,它适合场景相对固定、信息可以结构化、调用链路可重复的 Agent 任务,比如内部工具查询、数据清洗、订单状态同步、报表生成。在这些场景里,模型的核心工作是“理解意图 + 构造参数 + 翻译结果”,可靠性可以做得非常高。
但它不适合纯开放式创新任务。你让 Agent 写一篇有风格的文章,或者做一个头脑风暴,就不需要这种强约束网站,加了反而限制发挥。同样,如果你的 Agent 每次任务都在变,技能列表会无限膨胀,维护成本很快失控。我现在的原则是,一个技能如果一个月内被调用的次数屈指可数,那就先下架,等有需要再恢复。
5.2 可继续延伸的三个方向
第一个方向是多模型适配。当前技能定义看起来有点偏特定模型的口味,但实际上只要是支持工具调用的模型,都能接同一套接口。把技能描述和 Schema 设计得模型无关之后,团队内部不用换代码就能切换模型,这点我觉得挺值得继续打磨。
第二个方向是调用数据反哺。站点日志里记录了每个技能的调用次数、成功率、平均耗时,这些数据可以直接反馈到技能优化里。某个技能成功率长期偏低,大概率是 description 或 schema 有问题,按数据去改,比拍脑袋改提示词有效得多。
第三个方向是技能共享。现在每个团队都在重复造类似的工具,如果能形成一套标准的“技能包”协议,把定义、测试和部署方式统一起来,团队之间就能直接复用。这不仅是效率问题,也是 Agent 生态走向工程化的必经一步。
最后再聊点个人体会。整个项目做下来,编码的时间占比其实不高,反而是在技能描述和测试用例上花了大头。一个特别微小的细节:某个技能描述一开始写得太长,模型几乎从不会调用;我改成两句话之后,命中率一下子从两成提高到八成。这类差异很难从原理上推出来,只能靠实际日志一点点调出来。
如果你也想让自己的 Agent 变得更可靠,我的建议是从两个最痛的高频技能开始,先跑通一个闭环,再逐步扩展。比起一次性把所有流程都塞进去,小而稳的起步往往能带来更持久的效果。