1. 联网问答 Agent 到底在解决什么问题企业里做 AI 助手的人大概都经历过这个尴尬场面内部知识库问答跑得好好的员工问“我们公司年假怎么算”助手对答如流结果有人随口问一句“今天行业里有什么新政策”助手立刻开始胡编或者干脆说“我的知识截止到某年某月”。这种体验落差就是纯离线知识库方案的天花板。联网问答 Agent要干的事情很明确让企业的 AI 助手在回答问题时能够实时去外部信息源取数据把“最新”“当前”“今天”这类时效性问题的答案拉回来再结合企业内部知识一起组织成回答。它不是一个单纯的搜索框也不是一个纯 RAG 系统而是检索决策 实时抓取 内容融合 引用溯源的组合体。适合看这篇内容的人有三类一是正在给企业搭内部 AI 助手的开发者二是负责知识管理或客服系统选型的产品同学三是想搞清楚“联网”这件事在工程上到底难在哪的技术负责人。我会从整体设计思路讲到具体实现细节把踩过的坑和能直接抄的配置都摆出来。2. 整体架构设计与方案选型思路2.1 为什么不能简单接一个搜索 API 就完事很多人第一反应是联网嘛调个搜索接口把结果拼到 prompt 里不就行了。我一开始也这么想实测下来问题一大堆。第一个问题是触发时机。如果每轮对话都去联网成本和延迟都受不了而且很多问题根本不需要联网比如“帮我总结一下这份内部文档”。所以必须有一个检索决策层判断当前问题是否需要外部信息。这个判断本身可以用一个小模型或者规则引擎来做核心是识别问题里的时效性信号词比如“最新”“今天”“最近”“当前价格”“刚刚发布”等。第二个问题是结果质量。搜索接口返回的往往是标题加摘要直接喂给大模型模型容易断章取义。所以需要正文抓取和清洗环节把网页正文提取出来去掉导航栏、广告、页脚这些噪音。第三个问题是可信度。企业场景下AI 助手说错话是要担责的。所以联网拿到的信息必须带来源引用让用户能点回去核对。这一点在内部合规审查时特别重要。2.2 分层架构拆解我把整个系统拆成五层从下往上说。接入层负责接收用户问题做初步的意图分类。这一层可以用规则加小模型成本低、响应快。意图分类的结果决定后续走哪条路纯内部知识、纯联网、还是两者混合。决策层是核心大脑判断是否需要联网、需要联网几次、每次查什么。这里有个经验不要一次性把用户原问题直接丢给搜索而是要先做查询改写。比如用户问“最近新能源车补贴有啥变化”直接搜这句话效果一般改写成“新能源车补贴政策 最新”会好很多。检索层负责实际的外部信息获取包括搜索接口调用、结果排序、正文抓取。这一层要处理超时、重试、限流这些工程问题。融合层把外部信息和内部知识库的检索结果合并做去重和排序再交给大模型生成回答。这里的关键是给不同来源打权重内部知识库的权重通常高于外部网页因为内部信息更权威。输出层负责生成最终回答并附上引用来源。引用格式要统一最好能直接定位到原文段落。2.3 方案选型的关键取舍在检索方案上我试过两种路线。一种是全文抓取把搜索结果页的正文全部拉下来另一种是摘要优先只用搜索接口返回的摘要按需再抓正文。实测下来摘要优先在响应速度上优势明显平均能快 40% 左右但信息完整度差一些。我的建议是首轮用摘要快速判断相关性命中后再抓正文这样兼顾速度和深度。在模型选择上决策层用 7B 级别的小模型就够了生成层才需要大模型。这样整体成本能压下来不少。如果企业有自己的推理集群决策层甚至可以退化成规则引擎加关键词匹配效果也不会差太多。3. 核心细节解析与实操要点3.1 检索决策的触发逻辑怎么设计决策层的输入是用户问题和对话历史输出是一个布尔值加查询列表。我用的判断逻辑分三步。第一步时效性信号检测。维护一个信号词表包含“最新”“今天”“最近”“当前”“刚刚”“实时”“现在”等。命中任意一个标记为“可能需联网”。第二步内部知识覆盖度检查。拿用户问题去内部知识库做一次快速检索如果相似度高于阈值我设的是 0.85说明内部知识足够回答可以不联网。这一步能省掉大量不必要的联网请求。第三步问题类型判断。有些问题天然需要外部信息比如“竞品最近有什么动作”“行业标准有没有更新”。这类问题即使没有时效词也应该联网。我维护了一个问题类型分类器用少量标注数据微调了一个小模型准确率能到 90% 以上。注意决策层不要做得太复杂否则延迟会拖垮整个链路。我的经验是决策层总耗时控制在 200ms 以内超过这个数用户就能感觉到卡顿。3.2 查询改写的几个实用技巧查询改写直接决定检索质量。我总结了几个在实操中特别管用的技巧。去掉口语化表达。用户问“那个啥最近有啥新出的规定没”改写成“最新规定 发布”比原句效果好得多。补充上下文。多轮对话里用户可能说“那它呢”这时候要把上一轮的主语补进来。我用的方法是从对话历史里提取最近提到的实体拼到查询里。控制查询长度。查询太长搜索引擎会稀释关键词权重。我一般把查询控制在 15 个字以内超过就做关键词提取。多查询并行。对于复杂问题拆成 2 到 3 个子查询并行检索再合并结果。比如“最近新能源车补贴和购置税有啥变化”拆成“新能源车补贴 最新”和“购置税 新能源车 最新”两个查询。3.3 正文抓取与内容清洗搜索接口返回的摘要往往不够用需要抓正文。这一步的坑最多。反爬处理。很多网站对频繁请求有封禁策略。我的做法是控制单域名请求频率加随机延迟并且准备多个请求头轮换。这里不展开具体技术细节核心思路是模拟正常用户行为。正文提取。我试过几种方案最后用的是基于 DOM 密度的提取算法。简单说就是计算每个节点的文本密度密度最高的区域大概率是正文。这个方案对新闻类、博客类页面效果好对论坛类页面需要额外处理。内容去噪。提取出来的正文里往往混着“相关阅读”“广告”“评论区”这些内容。我用规则加小模型的方式过滤规则负责明显噪音小模型负责边界情况。编码处理。中文网页编码五花八门UTF-8、GBK、GB2312 都有。我统一用 chardet 检测编码再转成 UTF-8。这一步不做后面全是乱码。3.4 多源信息的融合与排序外部信息和内部知识库的结果要合并这里有个排序问题。我的排序公式是最终得分 来源权重 × 相关性得分 × 时效性衰减来源权重上内部知识库设 1.0权威外部站点设 0.8一般网页设 0.5。相关性得分用向量相似度算。时效性衰减按发布时间算越新越高超过 30 天的内容衰减到 0.3。去重也很关键。不同来源可能报道同一件事我用的方法是标题相似度加正文指纹双重判断。标题相似度用编辑距离正文指纹用 SimHash。两个都超过阈值才判定为重复保留来源权重高的那条。4. 实操过程与核心环节实现4.1 环境准备与依赖安装先列一下我用的技术栈都是常见组件没有冷门依赖。语言Python 3.10向量库Milvus 或 Qdrant二选一嵌入模型BGE-M3中文效果好大模型本地部署或 API 调用均可搜索接口按企业实际情况选择合规的搜索服务网页解析BeautifulSoup lxml编码检测chardet安装依赖的命令如下pip install beautifulsoup4 lxml chardet requests pip install pymilvus sentence-transformers如果用的是 Qdrant把 pymilvus 换成 qdrant-client 即可。4.2 决策层代码实现决策层的核心是一个函数输入问题和对话历史输出是否需要联网以及查询列表。import re TIME_SIGNALS [最新, 今天, 最近, 当前, 刚刚, 实时, 现在, 近期] def need_web_search(question, history, internal_score): # 第一步时效性信号检测 has_time_signal any(sig in question for sig in TIME_SIGNALS) # 第二步内部知识覆盖度检查 if internal_score 0.85: return False, [] # 第三步问题类型判断 external_types [竞品, 行业, 政策, 标准, 新闻] is_external_type any(t in question for t in external_types) if has_time_signal or is_external_type: queries rewrite_query(question, history) return True, queries return False, [] def rewrite_query(question, history): # 去掉口语化表达 question re.sub(r那个啥|就是|帮我看看, , question) # 补充上下文实体 if len(question) 6 and history: last_entity extract_entity(history[-1]) question last_entity question # 控制长度 if len(question) 15: question extract_keywords(question) return [question]这段代码里internal_score是内部知识库检索的最高相似度extract_entity和extract_keywords可以用简单的规则或者小模型实现。4.3 检索与抓取流程检索流程我写成了一个独立的类方便复用和测试。import requests from bs4 import BeautifulSoup import chardet import time import random class WebRetriever: def __init__(self, search_api, timeout5): self.search_api search_api self.timeout timeout self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) } def search(self, query, top_k5): results self.search_api(query, top_k) return results def fetch_content(self, url): try: time.sleep(random.uniform(0.5, 1.5)) resp requests.get(url, headersself.headers, timeoutself.timeout) encoding chardet.detect(resp.content)[encoding] or utf-8 html resp.content.decode(encoding, errorsignore) return self.extract_main_content(html) except Exception as e: return def extract_main_content(self, html): soup BeautifulSoup(html, lxml) # 基于文本密度提取正文 paragraphs soup.find_all(p) content \n.join([p.get_text().strip() for p in paragraphs if len(p.get_text().strip()) 20]) return contentextract_main_content这里用的是简化版实际生产环境可以加更复杂的密度计算。time.sleep那行是控制请求频率的别去掉否则容易被封。4.4 融合与生成融合层的逻辑是把内部检索结果和外部抓取结果合并排序后拼成 prompt。def merge_results(internal_results, web_results): merged [] for r in internal_results: merged.append({ content: r[content], source: internal, weight: 1.0, score: r[score] }) for r in web_results: merged.append({ content: r[content], source: r[url], weight: 0.8 if is_authoritative(r[url]) else 0.5, score: r[relevance] }) # 去重 merged deduplicate(merged) # 排序 merged.sort(keylambda x: x[weight] * x[score], reverseTrue) return merged[:5]生成阶段的 prompt 模板我建议这样写你是一个企业 AI 助手。请基于以下资料回答问题并标注引用来源。 如果资料中没有相关信息请明确说明不要编造。 资料 {context} 问题{question} 回答要求 1. 优先使用内部资料 2. 外部资料需标注来源 3. 不确定的信息要说明这个模板的关键是明确要求标注来源和禁止编造实测能显著降低幻觉率。5. 常见问题与排查技巧实录5.1 联网请求超时怎么办超时是最高频的问题。我的处理策略是分级超时加降级。搜索接口超时设 3 秒正文抓取设 5 秒。超时后不阻塞主流程直接用已有结果生成回答并在回答里说明“部分信息获取超时”。如果超时率持续偏高要检查是不是请求频率太高被限流了。我遇到过单域名每秒请求超过 5 次就被封的情况后来加了随机延迟就稳定了。5.2 抓回来的内容全是乱码九成是编码问题。先用 chardet 检测检测不准的话可以尝试用resp.apparent_encoding。还有一种情况是网页用了动态渲染requests 拿到的 HTML 里没有正文这种就需要换方案或者直接放弃这个源。5.3 模型还是胡编怎么办即使给了资料模型有时还是会编。我的经验是在 prompt 里加负面示例。比如明确写“不要回答资料中没有的内容即使你知道答案”。另外生成后加一道事实校验把回答里的关键实体和资料做比对对不上的打回重生成。5.4 常见问题速查表问题现象可能原因排查方向解决建议联网请求全部超时网络策略限制或接口不可用检查网络连通性和接口状态配置备用接口加降级逻辑抓取内容为空页面动态渲染或反爬查看返回 HTML 是否含正文换静态源或调整请求头回答引用来源错误融合排序有误检查来源权重和去重逻辑调整权重参数加强去重决策层误判信号词表不全收集误判案例补充信号词微调分类器响应延迟高串行请求太多看各环节耗时改并行请求加缓存5.5 几个独家避坑技巧缓存一定要做。相同或相似的查询在短时间内可能重复出现我加了一层查询缓存命中率能到 30% 左右直接省掉这部分请求。来源白名单。企业场景下不是所有网站都适合抓。我维护了一个白名单只从权威站点抓取既保证质量又降低合规风险。日志要详细。每次联网请求的查询、耗时、结果数量、来源都要记下来。出问题时这些日志就是排查依据。我吃过亏一开始日志太简略出了问题根本不知道是哪一步卡的。灰度上线。联网功能不要一次性全量放开先在小范围试用观察一周再逐步扩大。我见过直接全量上线导致成本失控的案例。6. 效果评估与持续优化6.1 怎么衡量联网问答的效果评估不能只看“回答对不对”要拆成几个维度。时效性准确率看回答里的信息是不是最新的引用准确率看标注的来源是不是真的支持回答决策准确率看该联网的时候联网了、不该联网的时候没联网响应延迟看 P95 延迟有没有超标。我一般用人工抽样的方式评估每周抽 100 条标注这四个维度。自动化评估可以用大模型做裁判但准确率有限只能作为参考。6.2 持续优化的方向信号词表要持续更新。网络热词变化快今天没有的信号词明天可能就出现了。我每月 review 一次误判案例补充信号词。来源权重动态调整。哪些来源被引用得多、被用户点击得多权重就调高。这个可以做成自动化的。查询改写模型迭代。积累足够的查询改写对之后可以微调一个小模型比规则方案效果好。成本监控。联网请求是有成本的要设预算上限超了就降级。我设的是单次对话联网请求不超过 3 次超过就只用已有结果。7. 我个人在实际操作中的几点体会做联网问答 Agent 这一年多最大的感受是工程细节决定成败。模型能力固然重要但真正拉开差距的是决策逻辑、抓取质量、融合排序这些看起来不起眼的地方。我踩过最大的坑是早期太依赖模型判断决策层直接用大模型做结果延迟高、成本高效果还不稳定。后来换成规则加小模型的组合反而更稳。这让我意识到不是所有环节都需要大模型合适的工具用在合适的地方才是正道。另一个体会是引用溯源不能省。企业用户对 AI 的信任是慢慢建立的一次胡编就可能毁掉所有信任。带引用的回答即使不完美用户也能自己判断接受度高很多。最后分享一个小技巧给联网结果加时间戳。回答里明确写“根据某年某月某日的信息”用户一眼就知道时效性比含糊其辞强得多。这个细节很小但用户反馈里提到它的频率出乎意料地高。