ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Chatbot联网搜索的Agent化重构:从搜索框到智能体编排

Chatbot联网搜索的Agent化重构:从搜索框到智能体编排 这几天一直在重构手头这个Chatbot的联网搜索链路客户的需求说简单也简单说复杂也复杂——他们原话是“别让用户点了搜索按钮还得自己一个个翻链接你直接告诉答案链接拿来当凭证就行。”这句话翻译过来其实就是Chatbot联网搜索正在经历的范式转移从“给用户一个搜索框”变成“让Agent自己用搜索”。这个需求我相信不少团队都接过看着不难真做进去才发现搜索框和Agent之间的距离远比想象中大。这篇文章我想用一次实际重构的经历把Chatbot联网搜索演进过程中最核心的技术变化、踩坑细节和选型逻辑完整拆开。适合正在做聊天机器人、智能客服、知识问答类产品的开发者也适合准备给自己的Agent加上实时信息能力的读者参考。1. 搜索框模式的三重困境为什么Chatbot联网搜索非要走向Agent先说清楚一个问题传统Chatbot早就支持“联网搜索”了无非是用户输入关键词系统去搜索引擎抓前几条结果拼到上下文里再让大模型基于这些内容回答。这套模式很多产品都在用但用起来就会发现三个硬伤这也是所有Agent化改造的原始动机。1.1 搜索框的三个硬伤单轮、单意图、单关键词第一是单轮困境。用户说“帮我查一下最新的旗舰手机”模型搜索完回答了一堆用户接着问“那它的续航呢”——这里面的“它”指代谁、和上一轮的搜索结果是什么关系传统搜索框完全无解。因为它根本没有“对话状态”这个概念每一轮搜索都是重新开始上下文里的历史记录也只是堆在一起没有结构化地告诉搜索模块该用什么查询词。第二是单意图困境。用户说“对比下这三款手机的拍照和续航”传统搜索框只能把它当成一个整体关键词丢给搜索引擎搜出来的结果要么是营销号横向对比要么是单款产品的评测信息匹配度非常差。实际上这个问题至少包含三个独立子任务查A的拍照、查B的拍照、查C的拍照各查各的续航最后才能做比较。Agent的本质就是能把一个复合任务拆成多个原子搜索动作再重组。第三是单关键词困境。大模型和搜索引擎的“理解方式”天然不同。搜索引擎喜欢关键词堆叠大模型擅长语义表达。用户对Chatbot说“那个新出的折叠屏外屏挺小的那款大概多少钱”这句话给搜索引擎直接用效果惨不忍睹但Agent可以先把这句话改写成一个规范的查询词比如“2024 小折叠屏手机 外屏 小 价格”再拿去搜索召回质量完全不一样。1.2 一个对比场景传统链路与Agent链路的高下立判我拿实际测试过的场景举个例子。用户在对话框输入“帮我看看深圳最近三天适不适合露营顺便告诉我露营要带什么。”传统搜索链路的行为是把整句话作为query传出去搜索引擎返回的是一堆“深圳露营攻略”“深圳天气”类的混合结果。模型拿到的上下文里天气和装备信息交织在一起回答要么顾此失彼要么两边都没答透。更麻烦的是如果用户追问“那番禺呢”系统完全不知道要把“番禺”替代掉“深圳”并重新发起天气查询。Agent化的链路则是这样运转的意图识别发现两个子任务——查天气、查露营装备清单。第一个子任务进入一个专门的天气查询规划把“深圳最近三天是否适合露营”改写为“深圳 未来三天 天气预报”。第二个子任务单独搜索查询词是“露营 必备装备 清单”。两路搜索结果分别进入对应的上下文槽位。模型整合后输出一个结构化回答并附上每条信息的来源链接。同样一次对话前者像让一个实习生拿着整段话去搜索引擎复制粘贴后者像让一个熟手拆解任务、逐个攻破、最后汇总。这不是体验细节的差异是架构层面的代差。2. Agent架构下的联网搜索从ReAct循环到工具编排的范式转移既然传统搜索框模式撑不住复杂需求那Agent化的搜索到底是什么样的架构这块我得从实际代码层面讲清楚不用“智能体”之类的玄学词汇就说它底层到底怎么转的。2.1 核心范式ReAct循环如何接管搜索决策业界最普遍的Agent搜索范式是ReAct——Reasoning and Acting。理解它不需要看论文记住一句话大模型不再只是“读结果的人”而是变成了“指挥搜索的人”。循环过程大致是模型接收用户目标和当前上下文。模型“思考”Reasoning下一步该做什么可能是改写查询词可能是不需要搜索直接回答可能是补充一个查询参数。模型调用工具Acting也就是执行一次真实的搜索API请求。观察返回结果再次进入思考决定是继续搜索还是整理答案。这个循环最关键的地方在于“搜索”从一个固定模块变成了模型手里的一个“可调用工具”。我在实际项目中用的伪代码差不多长这样def agent_search_loop(user_message, history): messages build_conversation(history [user_message]) while True: response llm.chat( messagesmessages, tools[search_tool_definition], # 搜索作为一个工具暴露给模型 tool_choiceauto ) if response.tool_calls: # 模型决定调用搜索工具 for call in response.tool_calls: query rewrite_with_context(call.arguments, history) results search_api(query) messages.append({ role: tool, tool_call_id: call.id, content: format_results(results) }) continue else: # 模型认为信息已经足够输出最终回答 return response.content这里面的rewrite_with_context和format_results是两个关键函数后面会单独展开。先说这个循环带来的两个质变。第一搜索的次数和方向由模型动态决定。简单问题可能一次搜索都不用复杂问题可能连续搜三四次、每次还换不同的查询词。这打破了传统链路“一次请求一次搜索”的僵硬结构。第二搜索结果作为工具输出参与轮次博弈。模型能看到“我搜了什么、搜到什么”然后决定“还差什么、要不要换个词再搜”。很多第一次做Agent的人会疑惑为什么模型反复搜索同一个问题这不是bug是它判断第一次的结果不够好正在自我修正。2.2 工具注册搜索API如何变成模型的“手和脚”ReAct循环的前提是模型会调用搜索工具这就要求我们把搜索能力注册成符合模型认知的“工具描述”。这里面有个特别容易被忽视的细节工具描述写不好模型就不会用。我在项目里最初的工具描述写得很简单大意是“搜索互联网并返回结果列表”。结果模型动不动就反复调用、传参也充满问题或者干脆不调用直接瞎编。后来参考大厂开源Agent的最佳实践把工具描述改成了这样{ type: function, function: { name: web_search, description: Perform a web search for the given query. Use this when the user asks about real-time information, recent events, statistics, prices, or any facts that may be beyond your training data. The query should be a concise search engine query, not a natural language question., parameters: { type: object, properties: { query: { type: string, description: The search query, 3-8 keywords separated by spaces. Do NOT include conversational filler words. }, recency: { type: string, enum: [day, week, month, year, no_limit], description: Restrict results to content published within this period. Use day for breaking news and very time-sensitive topics. } }, required: [query] } } }这里有个细节值得展开为什么description里要强调“query是关键词而不是自然语言问句”因为大模型默认倾向于写完整句子把它塞给搜索引擎很多搜索引擎的召回效果会退化三成以上。我做过对照测试同样的“深圳 露营 天气”和“深圳最近三天适合露营吗”前者的有效信源明显更多。让模型知道“工具需要什么格式”比提高模型本身的搜索能力更立竿见影。2.3 为什么搜索不能只搜一次多轮检索的触发逻辑很多人第一次看到Agent搜索会问用户问个问题搜一次不就完了为什么还要支持多轮检索答案是真实用户的问题往往信息不完整一次搜索的结果往往不足以支撑回答。举个我测试中遇到的实际案例。用户问“MacBook Air M4值得买吗”第一次搜索返回了配置参数和媒体评测但模型发现这些内容大多来自发布当月的报道而用户当前的时间点已经过去四个月价格和口碑可能发生了变化。于是模型主动修改查询词加上“2024年11月”和“用户评价”发起第二次搜索拿到更实时的用户反馈数据后才开始作答。还有一类触发多轮搜索的情况是“信息互斥”。用户问“A和B哪个更适合编程”第一次搜索A第二次搜索B模型需要分别收集信息后做对比。如果指望一次搜索结果里同时覆盖两者的详细对比召回大概率不理想因为搜索引擎优化的强相关内容会淹没弱相关内容。所以在Agent的搜索循环里我强烈建议不要用硬编码限制最大搜索次数比如“最多搜两次”这种看似省成本的做法。更好的做法是设置一个动态上限3到5次并给模型清晰的判断标准“当且仅当已有信息不足以回答用户问题时才继续搜索。”否则模型会在成本意识的驱动下过度保守或反之疯狂检索。关于工具调用的边界还有一个容易翻车的点搜索结果会包含大量噪声模型在ReAct循环中看到的是格式化之后的摘要不是原始网页。这是后续所有优化工作的基石——模型不需要读整篇文章它只需要足够判断“这条结果有没有用”。3. 联网搜索Agent的关键技术细节查询改写、结果解析与记忆管理架构选型定下来之后真正耗时间的反而是那些看起来不起眼的“工程小细节”。搜索Agent好不好用八成的差距在细节里。这一节我把实际项目里反复调优过的几个技术点拆开讲每一个都踩过坑。3.1 查询改写多轮对话的临门一脚前面提到的rewrite_with_context是整个搜索Agent里性价比最高的优化点。它的作用是把用户当前这句口语化的、带着指代和省略的话改写成适合搜索引擎的规范查询词。我维护过一个内部的经验清单查询改写最常见的几个规则消解指代把“它的价格”“那家店”中的指代词替换成前文明确提到的实体。补全省略用户说“华为那个新平板怎么样”但前文是iPhone需要明确补全为“华为 新平板 评测”。识别隐含条件用户说“想换手机预算五千左右”查询词里要体现“5000元档”否则搜索引擎会返回旗舰机报价。保留最新性凡是产品选型、政策变化、开放时间这类时效敏感问题查询词要带上相对时间如“2025年”否则模型容易被旧信息带偏。中英文混合处理很多技术类问题中英混合查询效果更好比如“Java virtual thread 性能 对比”比纯中文“Java虚拟线程性能对比”召回质量更高。在执行层面查询改写有两种做法。一种是单独调用一次大模型专用改写另一种是直接在ReAct循环的工具参数里让模型自己生成query。我实测下来后者的效果和前者相当还省了一次模型调用所以推荐优先用后者——只要工具描述里写清楚“query应该是简洁关键词”就行。单独做改写函数适用于搜索引擎API对查询词格式特别敏感的场景这个后面表格里会说。再多说一句改写后的查询词不要直接抛出去先做一次屏蔽词过滤和长度限制。有段时间我们的日志里经常出现模型把用户整段评论复制进query的情况搜索引擎返回了一堆无关内容。后来加了一条规则query超过30个字就强制截断或重写问题好了很多。3.2 搜索API选型对比别只看召回率联网搜索Agent的地基是搜索API这层选错了后面全白搭。我对比过市面上主流的几家搜索API综合考虑成本、返回结构、速率限制之后给团队的结论是这样一张表搜索API返回结构时效性速率限制成本定位适用场景Tavily SearchJSON结构化自带内容摘要、相关性评分较好中免费层可用按量计费中等Agent工具调用专用内置检索质量优化Brave Search标准JSON含网页结果/新闻/视频较好较高相对亲民通用场景控制成本优先Bing Web Search API标准JSON生态成熟稳定高中等偏贵需要全球区域覆盖和大并发场景SerpApi聚合Google结果JSON结构化好中较贵需要特定搜索引擎结果、爬取类需求向量库关键词混合检索自建取决于数据源自控低但需运维垂直领域、存量文档检索这个表不是说Tavily一定最好而是它设计之初就是冲着Agent工具调用去的返回结果里已经带了snippet摘要和相关性分数接入ReAct循环时几乎不用二次处理。Brave Search的优势是免费额度相对阔绰适合预算敏感的初创团队跑通demo。Bing API的稳定性在大流量场景下最可靠但价格也最敏感。有一个坑我必须多说一次永远不要直接拿基础搜索API的raw response塞给模型。搜索引擎返回的原始结果是给人类看的里面有大量页面的导航信息、站点广告标识、重复片段。直接拼进上下文既浪费token还干扰模型判断。中间一定要加一层“结果清洗”把title、snippet、url、发布日期、站点域名抽出来必要时做内容去重。3.3 结果解析与上下文组装决定回答质量的隐形环节format_results就是上面说的结果清洗和组装。这个过程做得好不好直接决定大模型最终回答的质量。我的经验是遵循几个原则第一每条结果控制在120个token以内。搜索引擎返回的snippet经常从正文中间截断直接塞进去会产生大量不完整句子。更好的做法是截取title snippet前80个字符再手动拼上发布站点和日期让模型快速判断来源可信度。第二给结果编号并让回答引用编号。这是我测试后效果最好的做法。格式化的时候给每条结果分配一个索引编号模型回答时直接说“根据[1]的信息参考[3]的评测……”。这样既提高了回答的严谨性也方便后续做引用溯源——用户点进来源链接时Agent知道对应用户可能想核实哪段内容。第三按“与当前问题的相关性”排序而不是按搜索引擎返回的顺序。搜索引擎天然会把权威站点排前面但Agent单轮问答里最相关的可能是垂直论坛里的一条真实体验帖。我做过实验对20个搜索query把最相关的结果人工调整到第一位模型回答的准确率主观评分提升了约15%。当然这个排序不一定要用重排序模型简单用关键词重合度也能解决一大半。第四对旧结果做标注。搜索结果里会混入几年前的旧文章模型有时会把过时信息当成现状回答。我在格式化时会把超过时间的文章显式标注“该结果发布于两年前请注意时效性”模型在看到风险提示后更倾向于重新搜索或保守表达。3.4 记忆层搜索上下文与历史对话的取舍Agent搜索区别于搜索框模式的另一个核心是记忆管理。注意这里的“记忆”不是指大模型的参数记忆而是对话历史中哪些信息需要被带入搜索决策。我在实际项目里维护了一条分层记忆策略短期记忆最近3轮对话必须完整保留用户的指代和偏好大多隐藏在这里。中期记忆当前任务栈保留当前问题相关的中间搜索结果。比如用户在对比三款手机每款手机的搜索结果都保留对应的摘要方便模型跨结果对比。长期记忆用户画像非必要不参与搜索只在用户明确要求个性化推荐时注入。比如用户之前聊过“预算五千以内”搜索手机时候选列表会优先过滤出这个价格区间。这里最大的教训是“不要试图保留所有搜索结果”。很多团队为了追求“记忆好”把每一轮搜索的完整结果都堆在上下文里结果上下文长度爆炸模型反而丢失了重点。我的做法是在下一轮搜索动作开始前只保留上一轮搜索结果中被模型实际引用的那几条通过引用编号追踪其余全部丢弃。这样既省token又保持上下文干净。记忆层的实现最粗糙的版本就是维护一个历史列表按上面策略截断后重新拼进messages进阶版本可以用embedding做记忆检索只把最相关的几段历史片段注入当前搜索决策。后者对长对话场景帮助很大但初版项目不用一上来就上先把截断逻辑做对再说。4. 实测中的拦路虎上下文污染、幻觉、并发与成本的那些坑架构搭好、细节调优之后项目真正上线前还有四个拦路虎。每一个我都见过团队在群里哀嚎“模型疯了”“接口又炸了”这里把排查思路和最终解法完整记录下来。4.1 上下文污染搜索结果如何“吃掉”对话能力这是我把搜索Agent从一个demo级产品推向可用级时遇到的最大障碍——搜索结果在上下文里堆积导致模型忘记对话本身的指令。现象很典型对话超过五轮后模型开始答非所问或者复述搜索结果而不是回答问题。排查日志发现Agent在多轮搜索后上下文被大量格式化结果占据原始用户问题反而被淹没在最前面的历史里模型的注意力分散了。我的排查思路分成三步把每一轮搜索后输入给模型的messages dump出来按token占比统计。结果发现搜索结果占了超过70%的上下文窗口。检查模型是否需要全局看到所有搜索结果。结论是不需要——模型每一轮需要关注的只是“当前这个查询词的结果”和“最终引用列表”。实施压缩策略每轮搜索后把上一轮的原始结果替换为“上一轮搜索的结论摘要”由模型自己生成30到50字总结连同本轮新结果一起输入。这个压缩操作上线后对话质量立刻回升。中间还有个细节压缩摘要由谁生成我用的是“让模型对每轮结果先做一次轻量总结”虽然多了一次推理调用但效果远好于让主循环直接读原始结果。因为搜索Agent本质是“先搜再想”搜索过程中每轮都保持一个小步总结最终回答时才有充分的结构化素材。4.2 幻觉与信源冲突搜索到了正确信息模型仍然瞎说第二个大坑是幻觉治理。搜索API返回了正确的信息但模型生成回答时仍然会把自己的“训练记忆”混进去。最典型的场景是用户问“2024年诺贝尔物理学奖是谁”搜索结果已经明确给出了获奖者模型却先按训练数据里的旧获奖者开场搜索正确信息夹在中间前后矛盾。这类问题的根治思路不是换个大模型而是在Prompt层面对模型做硬约束。我最终在系统提示词里加了三条规则效果立竿见影“回答基于本次对话中的搜索结果。当搜索结果与内部知识冲突时优先采用搜索结果。”“引用信息必须给出对应来源编号。不要引用未在搜索结果中出现的事实。”“当搜索结果包含多个矛盾表述时如实呈现差异并说明来源发布时间而不是强行归纳。”这看起来是提示词工程实际是信源治理。另外还有一个相关的小技巧对每个搜索结果标注发布时间和站点可信度级别。模型看到“某个人博客”和“官方渠道”的结果在回答时会更自然地偏向可信来源。当然站点可信度分级需要人工维护一份白名单和黑名单我的原则是宁缺毋滥只维护白名单。4.3 扛并发Agent搜索比普通搜索接口难伺候十倍“AI Agent怎么扛并发”这个话题最近讨论度很高我实打实运维过搜索Agent后完全理解为什么它难。常规搜索接口扛并发只需要解决吞吐量Agent搜索的并发问题在于单次Agent任务可能触发多次搜索API调用而任务本身又互相穿插导致搜索API的并发峰值不可预测。我踩过的具体坑线上突然涌入50个用户提问每个用户平均要触发3~5次搜索调用瞬间后端收到200多个搜索API请求。当时用的搜索API单key速率限制是每分钟100次直接被限流大量任务超时重试重试又进一步叠加压力雪崩效应。最终方案分三层请求队列 滑动窗口限流用一个内存队列为每个API key维护滑动窗口超过速率限制的请求排队等待而不是直接失败。这样高峰期的请求会平缓吸收而不是被API拒绝。结果缓存相同或相似的查询词在短时间内直接命中缓存。实测中让搜索API调用量下降了约40%。这里的缓存key不只是查询词还包含时间窗口因为很多问题时效性敏感“昨天”的搜索结果和“上周”的不该复用。任务级超时与降级单个Agent任务的搜索总时长超过阈值比如30秒强制回收不再发起新搜索直接用已有部分结果回答。这个措施防止个别复杂任务霸占整个进程。奉劝一句Agent上线前一定要做并发压测而且压测场景必须是“多个Agent任务同时多轮搜索”不是简单地对搜索API做单请求压测。后者的结果对线上完全没参考价值。4.4 成本与速率一个容易被忽略的软性指标成本问题放在最后是因为很多团队demo阶段根本关注不到。但一旦搜索Agent上线曝露真实流量账单数字很容易让人心梗。这里分享几个我在成本控制上验证过的有效动作搜索结果数量动态化简单问题返回5条即可复杂对比类问题才返回8到10条。不要固定10条多出来的低相关结果只会烧token。结果内容按需截断模型回答问题前只需要snippet不需要全文。全文爬取只在需要深入对比时触发。对模型输出长度做上限约束搜索类问答的答案默认控制在300字以内用户主动要求详细才输出长文。多数短答案对用户友好且成本下降非常明显。缓存策略分级热门查询缓存30分钟一般查询缓存10分钟含有时间限定词的查询不缓存。粗糙缓存全开虽然能大幅降本但副作用是用户问“今天天气”得到“昨天”的错误答案得不偿失。这个表是我整理的不同策略组合的成本对比不要当成绝对真理但当参考基准是够的策略单次任务平均搜索次数平均token消耗单次任务成本估算无优化4.16200基准结果动态数量3.85100约-18%缓存命中40%2.53400约-45%输出长度约束2.42900约-53%组合下来单次搜索Agent任务的成本可以压到无优化版本的一半左右同时回答质量没有明显下降。这组数据说明一件事搜索Agent的成本大头不在模型生成而在“搜索多少次上下文塞了多少垃圾”。5. 从单Agent到多Agent搜索能力未来的形态演进写到这里前面讲的都是单个Agent自己具备搜索能力。但顺着演进趋势往下走联网搜索的下一个阶段其实是多Agent分工协作——这也是为什么“多Agent”“Agent框架”“Agent Harness”这些词最近频繁出现在讨论区。5.1 搜索Agent的两种形态直连技能与子Agent我观察到的实践路径一共有两条。第一条是搜索作为技能Skill在Agent Harness框架里注册成一个标准工具任何Agent都能调用。第二条是搜索独立为一个子Agent由主Agent通过任务分配机制把搜索任务派给它它完成搜索并返回结构化结论而不是原始链接列表。两条路径对架构的影响差异很大。技能模式适合信息获取简单、且主Agent本身已经够聪明的场景子Agent模式适合需要深度检索、多源对比、甚至专项追踪的任务。举个例子用户问“对比A和B两个产品的用户口碑”搜索子Agent可以先做第一轮搜索识别出五个主要讨论渠道再对每个渠道发起第二轮定向检索最后返回一份综合口碑摘要。技能模式一次只做一件事做不了这种“先侦察后深挖”的复杂动作。我目前团队的项目就处在从技能模式往子Agent模式迁移的阶段核心推动力是主Agent的上下文再大也装不下多路搜索的中间过程与其让主Agent在重复搜索中消耗注意力不如把搜索过程完全下沉到子Agent内部主Agent只接收提炼后的结论。5.2 把搜索能力封装成Skill接口标准化的一次实践在迁移过程中我们参考社区讨论较多的“Agent Skills”理念把搜索能力做成了标准化的Skill包。所谓Skill就是一段完整的“技能声明”包含能力说明、参数定义、触发条件、上下文协议。我们的搜索Skill包的大致结构是输入用户问题、当前对话上下文、搜索范围偏好。执行内部按任务复杂度选择“单轮搜索”还是“多轮搜索”策略复杂任务自动启动子Agent模式。输出提炼后的关键信息列表带来源编号和相关度评分。边界明确“哪些问题不该搜索”——比如用户问数学推导、纯逻辑推理搜索反而引入噪声这种情况Skill应当返回“无需搜索”标记。把搜索封装成Skill的主要好处是可组合性。同一个搜索Skill既能被天气查询Agent调用也能被产品调研Agent调用调用方只需按协议传参不需要知道内部搜索细节。这大大降低了多Agent体系的耦合成度。5.3 Harness把工具、记忆与权限统一收口最后聊聊“Agent Harness”这个词。我在具体项目里的理解是Harness就是一个收口层把Agent运行时的工具调用、记忆管理、权限控制、上下文组装这些事情统一接管让业务开发只关注提示词和工具定义。为什么要这样设计因为搜索Agent一旦多轮化、多Agent化面临的就不只是“调搜索API”这一件事还涉及哪些搜索动作是允许的、搜索结果要在哪个生命周期内被清理、不同Agent之间的搜索上下文如何隔离、敏感查询词要不要拦截。这些如果没有统一口径散落在每个Agent实现里一定会出现权限混乱和上下文串联事故。我处理过的一个线上事故至今记忆犹新两个子Agent共享了同一个搜索上下文缓存一个Agent在搜索“某竞品负面新闻”另一个Agent的对话里莫名被注入了相关内容整个回答都歪了。这就是没有Harness收口导致的上下文隔离失效。后来把搜索上下文加上AgentID维度的隔离问题才彻底消失。所以我的判断是搜索能力从“工具”演进到“Skill”再到“子Agent”底层基础设施必然走向Harness化。单纯搜索的能力差异会越来越小最终比拼的是谁的Agent运行环境更能管好工具调用的秩序、记忆的边界和权限的约束。这个方向的技术演进远没有停止。比如用Rust这类高性能语言做Agent运行时来原生扛并发比如把搜索记忆做成向量索引支持跨Agent共享都是我在关注和下个版本准备试水的方向。但眼下这个“从搜索框到Agent”的阶段核心的技术功课还是那些查询改写、结果治理、上下文管理、并发与成本控制。把这些做扎实你的搜索Agent就已经超过大多数demo级的同类产品了。
返回列表