你可能已经跑通了单轮检索问答,也调通了工具调用,但把两者拼成一个能上线的系统时就开始卡:检索结果塞不进上下文、Agent 循环偶尔不收敛、接口一超时就整条链路挂掉、出了问题翻日志也看不出是哪一步慢。这篇不讲协议兼容和框架原理,只给从零搭建时的目录结构、参数清单、降级顺序和评测办法,让你能一步步落地并知道哪一步不适用。
很多人已经跑通过单轮检索问答,也单独调通过工具调用,但把两者拼成一个能上线的系统时就卡住了:检索回来的片段塞不进上下文、Agent 的循环偶尔不收口、接口一超时整条链路就挂、出问题翻日志看不出慢在哪一步。这篇不讲协议兼容和框架原理,只给从零搭建时的目录分层、参数口径、降级顺序和评测办法。
阅读方式建议按顺序走:先定骨架再调参数,然后想清楚坏了先砍哪一步,最后补日志和评测。如果你的场景没有自有资料、或者必须逐条人工复核,下面有些做法并不适用,最后一节会说明边界。
先定骨架:一次请求要经过哪几层
把一次请求拆成入口层、编排层、检索层、模型层、工具层五段,每段只干一件事。这样做的价值不在于架构好看,而是排错时能快速定位是哪一段的锅,替换实现时也不用动其他层。
- 入口层
- 做鉴权、限流、请求 id 生成,不碰业务逻辑
- 编排层
- 决定这一轮是检索还是调工具,管循环和收口
- 检索层
- 输入查询、输出带来源标记的片段
- 模型层
- 统一封装调用、重试、模型选择
- 工具层
- 外部函数的注册、参数校验与超时
- 检索和工具做成统一接口:输入是查询或参数,输出是带来源标记的片段,Agent 决定查库还是调函数时走同一套流程
- 会话状态单独放一层,不要在检索函数里顺手改历史,否则并发时同一会话会互相踩
- 小团队不必一上来就上向量库集群,先用文件加索引把链路跑通,量上来再换方案,具体选型去 /stacks/vector-dbs 对照
- 编排层别写死顺序,把「先检索还是先调工具」做成可配置项,方便后续按场景切换
| 层 | 只负责什么 | 不该做什么 |
|---|---|---|
| 入口层 | 鉴权限流、生成请求 id | 拼 prompt |
| 编排层 | 循环控制、决定下一步动作 | 直接发 HTTP 请求 |
| 检索层 | 召回并打分片段 | 改会话历史 |
| 模型层 | 调用与重试 | 判断业务是否成功 |
| 工具层 | 参数校验、真实执行 | 替模型写结论 |
参数怎么定:超时、并发、检索条数
超时分三档:连接超时、单次模型调用超时、整条请求超时。整条请求的超时要明显大于前面各档之和,否则前面的设置形同虚设,整条链路仍然会在中途被掐断。
- 先量延迟基线用固定的几组问题跑一遍,记录每段耗时。到 /tools/speed-test 量自己的延迟基线,别拿别人的数字套自己环境。
- 再定检索条数检索条数先设小再往上调。召回太多会挤掉对话历史,还拖慢首字时间,收益经常是负的。
- 最后定循环上限Agent 循环设步数上限加工具白名单,到点强制收口输出当前结论,不要让它自己判断该不该停。
判断标准很简单:如果加并发之后错误率明显上升而总吞吐没涨,说明瓶颈在上游限速而不是你的编排逻辑,这时候该做的是排队和退避策略,而不是继续加线程。
降级顺序:哪一步坏了先砍哪一步
降级要有固定顺序,推荐先降检索条数,再关掉非必要工具,再换更便宜的模型,最后才是返回兜底话术。一上来就整条失败,用户体验和排错成本都更差。
- 检索超时不要直接让模型硬答,改成返回检索暂不可用并保留追问入口,能避免编造来源
- 模型失败要区分可重试和不可重试:网络类可退避重试,参数类重试多次也没用,只会放大限流
- 多模型路由的降级链只留两级,链太长时单次请求耗时和排错成本都会翻倍
- 每级降级都要打点,否则你根本不知道线上到底经常降在哪一级
哪些模型能互相顶班,不是看名字像不像,要看能力对照和是否支持同样的输入形态,去 /models 看能力对照再定。另外,降级策略本身也要测:手动把检索层关掉跑一遍,看链路是不是按预期给出可用的兜底回答。
日志和评测:上线后靠什么定位问题
日志不是记全就行,关键是能回答慢在哪、错在哪、答错是检索偏了还是模型偏了。所以字段要围绕这三点设计。
| 记什么 | 用来回答什么 | 常见漏记点 |
|---|---|---|
| 请求 id | 把一次请求的所有日志串起来 | 只在入口生成,没往下传 |
| 检索命中条数与片段 id | 检索层是否召回对了 | 只记条数不记 id |
| 工具调用序列 | Agent 走了哪条路径 | 不记参数和返回值 |
| 各段耗时 | 慢在哪一步 | 只有总耗时 |
| 最终模型名 | 是不是被降级换过 | 降级换模型后仍记原模型 |
- 攒失败样本把失败样本单独存一份,不要混在通用日志里,攒够一批再回放,比对着线上日志逐条猜快得多。
- 固定回归集上线前准备一组固定问题,改检索策略或换模型后重跑,看哪些问题从能答变成答错。
- 看引用对不对别只看答得像不像,还要看有没有引用到正确片段,引用错了说明问题出在检索层而不是模型层。
什么情况下这套做法不适用
上面这套偏向有自有资料、要求可控可排查的场景。碰到下面几类情况,需要先调整前提,否则按这套搭出来只会越跑越累。
- 资料更新极频繁、要求秒级一致的场景,先解决数据同步再谈检索,不然检索层永远在给过期内容做补救
- 对答案有强合规要求、必须逐条人工复核的场景,Agent 自动收口就不合适,要留人工确认环节
- 纯闲聊或没有自有资料的场景,检索层是纯负担,直接调模型更省事也更省成本
- 请求量很小、单次延迟不敏感的内部工具,过度设计降级和日志体系收益有限,可以先简化
常见问题
RAG 和 Agent 一定要一起上吗?先做哪个?
不一定。如果你的问题都是「查资料再回答」,先把检索做稳就够用;只有当需要模型自己决定查几次、要不要换工具时再引入 Agent。建议顺序是先跑通检索链路并用固定问题集验证,再加工具调用和循环控制,否则出问题时你分不清是检索差还是编排乱。
上线后回答变慢,一般先查哪一段?
按耗时占比从大到小排:先看检索层是不是召回条数被调大了,再看模型调用是不是换到了更慢的一档,最后看 Agent 是不是多跑了几轮循环。这三段加起来占了绝大部分延迟,逐段打点记耗时比整体计时有用得多。
检索结果和模型回答对不上,怎么定位?
先把该次请求命中的片段原文和最终回答一起打出来做对照。如果片段里根本没有答案,是检索层的问题;如果片段里有但回答没引用,是拼装或提示词层面的问题。分清楚这两类再改,能省掉大量来回试错。