梳理搜索增强 Agent 的四大失败模式(结果过期、 paraphrase 漂移、无来源标识、工具形态不匹配)并给出检索契约、查询构造、引用强制和验证通道的具体解法。
所有人起步时都是把搜索结果一股脑塞进上下文窗口,然后听天由命。实践中会碰到四种典型失败模式:
结果过期。 排第一的搜索结果是一年前的,模型毫不知情。没有时间戳、没有新鲜度窗口、不会重新检索。
** paraphrase drift(改写漂移)。** 模型读了一遍来源,用自己的话重新组织,在重新表述的过程中悄悄丢掉或扭曲了关键的那个数字。答案"有搜索依据",但仍然是错的。
上下文污染。 塞进去十五段摘要后,模型偏偏盯上了那段看起来最合理、却与其他段落相矛盾的段落。更多上下文反而让情况更糟,而不是更好。
输出无法验证。 回复里没有 URL、没有引用片段、没有方式让人类——或者第二轮 agent 核查——去核实任何一个声明。
Grounding 失败,十次有九次出在检索层,而不是生成层。先把契约修好。
第一个决策是 agent 实际接收到的是什么。原始抓取的 HTML 不是 grounding 材料,结构化的结果才是:
{
"query": "30-year fixed mortgage rate",
"freshness": "7d",
"k": 5,
"results": [
{
"title": "Mortgage rates today",
"url": "https://example.com/mortgage-rates",
"snippet": "The average 30-year fixed rate is 6.1% as of Aug 10.",
"published": "2026-08-10",
"score": 0.93
}
]
}
每个字段都有它存在的理由:published 让模型能够推理新鲜度,score 给它一个判断冲突的弱信号,url 是引用锚点,而 snippet 才是模型被允许直接引用的内容。如果你的搜索工具返回不了这个形状,用一个适配器包装它——你的 prompt 工程会感谢你。
Agent 内部的问题不是搜索查询。"把 Postgres 备份到 S3 最好的方式是什么?"是一句对话;而搜索层想要的是 "postgres backup to s3 best practices"、"postgres pg_dump to s3"、"postgres backup tools comparison"。
有效的模式是:把 agent 的问题拆成 2-3 个具体的子查询,并行执行,然后按来源质量而非顺序合并。对于任何易变的事物——价格、版本、分数、可用性——加上时间范围限定。对于一个每周都会变化的事实,一个没有新鲜度限定的查询就是一次多此一举的幻觉。
这一条能解决大部分幻觉,而且零成本。不要让模型把检索到的事实 paraphrasing(改写)进答案却不给那条具体声明附上引用。两个机制让它生效:
把 snippet 在上下文中加上引号,让模型的输出锚定在确切措辞上,而不是对页面的模糊记忆。
在输出格式中要求每个声明都有引用——先给答案,然后标注 [source: url]——对于模型无法关联到已检索 snippet 的声明直接丢弃。
听起来很严苛,但这就是"跟你争论的 agent"和"展示推理过程的 agent"之间的区别。下面的验证环节之所以能成立,正是因为这个格式存在。
第二轮审查能捕获第一轮没能发现的问题。把起草的声明与已检索的 snippet 逐条核对:
数字精确匹配。对于价格、版本、日期:snippet 里真的这么说了吗?
跨来源一致性。对于易变事实,要求两个独立结果一致,不一致时让 agent 明确说明。
新鲜度复查。如果答案依赖于"最新",重新检索而不是信任第一次拉取的结果。
诚实失败。允许的输出只有"已确认"和"无法确认"两种。"无法确认"是合法的答案。
这把 grounding 从对上下文窗口的一次侥幸尝试变成了一个循环——检索→起草→验证→失败时重新检索——而不是单程票。
以上每一步都假设搜索步骤是一次可靠的、结构化调用。但在实践中这正是 grounding 管道最容易挂掉的地方:爬虫换个页面布局就坏了、浏览器自动化撞上 rate limit、API key 周五过期。
这也是我停止手写爬虫的时刻。Grounding 循环需要一个结构化的搜索能力(JSON 输出,不是 HTML)、能从 agent 自己的 runtime 触达、可以一键安装——形状跟一个设计良好的检索微服务一样,但没有 REST 那套繁琐的东西。这正是 agent 原生工具在 Pilot Protocol 上的样子——一个面向 AI agent 的开源覆盖网络。它在应用商店里提供了一个有 grounding 的 Web Search 应用——发现、安装、调用:
pilotctl appstore install io.pilot.cosift
pilotctl appstore call io.pilot.cosift cosift.search '{"q":"latest stable postgres release","k":"5"}'
JSON 进、JSON 出。重量级的搜索后端跑在别处;agent 拿到的是一个本地类型化适配器,接口稳定,安装时自动启动。同一个应用可以被网络上 243k+ 的 agent 发现——这是"下个季度还会有人维护"的不错指标。而且它直接嵌入上面的检索契约,不需要一行胶水代码。
我仍然自己写契约、查询拆解和验证环节——那些是任何工具都替代不了你做判断的地方。但检索步骤从一个需要自己维护的爬虫变成一条命令安装,这才是 grounding 循环能交付而不是慢慢烂掉的区别所在。
核心要点:
把 agent 的问题拆解为范围明确的子查询。
通过稳定、类型化的搜索工具检索结构化结果。
用带引号的 snippet 起草,每个声明附上引用。
对照来源验证每条声明;失败时重新检索。
用引用回答——或者承认无法确认。
Grounding 是一条管道,不是一句 prompt。把检索契约做好、让每个事实都紧绑来源、先验证再回答、让搜索步骤成为一个稳定类型化的调用而不是爬虫。你的 agent 仍然会犯错——但它会带着来源犯错,这是一个可调试的问题,而不是信任问题。
想试试工具链这边?整个网络一条命令安装:
curl -fsSL https://pilotprotocol.network/install.sh | sh
然后 pilotctl appstore catalogue 查看有什么可用——包括上面的有 grounding 搜索应用。如果你在生产环境里已经跑通了一个 grounding 循环,我很想知道你的检索契约长什么样。