基于LangChain与千问大模型构建联网检索Agent:原理、架构与工程实践

发布时间:2026/8/7 6:24:49
基于LangChain与千问大模型构建联网检索Agent:原理、架构与工程实践 1. 项目概述当大模型学会“上网冲浪”最近在折腾AI应用落地的朋友估计都绕不开一个核心痛点大模型的知识“保质期”问题。你问它“今天天气怎么样”它可能会根据训练数据里某个城市的平均气温给你编一个你让它推荐“最近有什么好看的电影”它大概率会给你列出几部经典老片。这背后的原因很简单像千问、GPT这类大语言模型它们的知识库在训练完成后就基本固定了对于实时、动态、非公开的信息它们无能为力。这就是“联网检索Agent”诞生的背景——我们得给这位博学但“宅”在家里的AI助手装上一个能主动“上网冲浪”、查找最新信息并回来跟你聊天的能力。“千问联网检索Agent-生成对话”这个项目本质上就是构建一个智能体Agent它以大语言模型如千问为核心大脑集成了实时信息检索工具。当用户提出一个需要最新信息的问题时这个Agent不会凭空捏造而是会先派出一支“侦察兵”检索工具去互联网上寻找答案然后把找到的可靠信息带回给“大脑”千问模型由大脑消化、理解、整合最后生成一段自然、准确且信息更新的对话回复。这彻底改变了我们与AI的交互模式从单一的问答变成了一个“感知-思考-行动-反馈”的闭环。无论是追踪股市动态、查询科技新闻、获取体育赛事结果还是解答某个软件的最新版特性这个Agent都能胜任让AI真正具备了与时俱进的“生命力”。2. 核心架构与组件选型解析2.1 Agent框架LangChain vs. Semantic Kernel要构建这样一个联网Agent首先得选一个趁手的“脚手架”。目前主流的选择有两个LangChain和Semantic Kernel。LangChain更像是一个为LLM应用量身定制的“瑞士军刀”工具箱。它的设计哲学是“链”Chain将检索、模型调用、记忆、工具使用等环节像乐高积木一样连接起来。对于我们的联网检索场景它的优势非常明显生态丰富社区活跃有大量现成的“工具”Tools集成比如用于网络搜索的SerpAPI、DuckDuckGo Search或是用于读取网页内容的爬虫工具。它的AgentExecutor模块已经封装好了智能体运行的核心逻辑开发者可以快速搭建原型。我选择LangChain作为本项目的基础框架正是看中了它的快速迭代能力和丰富的社区支持能让我把更多精力花在业务逻辑和效果优化上而不是重复造轮子。Semantic KernelSK则更像是微软系技术栈的“战略级”产品它强调“规划”Planner和“插件”Plugins的概念与Azure OpenAI服务深度集成。SK的规划器能自动将用户目标分解成一系列步骤理论上更智能。但对于一个需要高度定制化检索逻辑和结果后处理的场景SK目前的开箱即用体验和文档丰富度略逊于LangChain。如果你的技术栈完全基于微软云SK是一个不错的选择但对于追求灵活性和快速上手的项目LangChain目前是更稳妥的选择。2.2 检索工具从通用搜索到精准抓取检索工具是Agent的“眼睛和手”。我们不能简单地把用户问题扔给Google然后照搬第一个结果那样风险太高信息质量也无法保证。我的方案是采用分层检索策略通用搜索引擎API作为第一道检索入口。我选择了SerpAPIGoogle搜索作为主力。为什么不直接用免费的公共搜索接口因为稳定性、反爬策略和结果的结构化程度。SerpAPI返回的是JSON格式的搜索结果包含标题、链接、摘要甚至“知识图谱”信息这极大方便了后续处理。配置时一个关键参数是num返回结果数量我通常设置为5-8条既保证覆盖面又不会给后续处理带来太大负担。网页内容提取器搜索引擎返回的只是摘要和链接要获取详情还需要深入页面。这里我用了BeautifulSoup和requests库。但直接抓取会遇到各种问题动态加载需要Selenium或Playwright、广告干扰、页面结构复杂。我的经验是优先抓取搜索结果中域名权威性高的页面如维基百科、知名新闻站、官方文档并设置超时如10秒和重试机制。一个重要的技巧是在抓取后使用trafilatura或readability这样的库进行正文提取它能有效过滤导航栏、广告、评论等噪音只保留核心文章内容。备用与垂直检索对于某些特定领域如学术论文用arXiv API、代码库用GitHub API需要集成垂直搜索工具。这构成了Agent的“工具包”LangChain的Agent可以根据问题类型自动选择调用哪个工具。2.3 大模型核心千问的提示工程与上下文管理千问模型在这里扮演“信息整合与对话生成”的角色。它的提示词Prompt设计至关重要直接决定了回复的质量和安全性。我的核心Prompt结构如下你是一个专业的AI助手拥有联网搜索信息的能力。请根据用户的问题和提供的参考信息生成友好、准确、有用的回答。 用户问题{user_question} 参考信息来源于网络检索请批判性使用 {formatted_search_results} 请遵循以下规则 1. 回答必须基于提供的“参考信息”。如果参考信息不足以回答请诚实说明“根据现有信息无法确定”切勿捏造。 2. 如果参考信息中存在矛盾或不同观点可以简要指出并说明哪个来源更权威或更普遍。 3. 严格区分事实陈述和个人观点。如果是事实请注明信息来源如“根据XX新闻报道”。 4. 回答最后可以友好地询问用户是否需要更详细的信息。这里有几个关键点一是强制模型基于检索结果作答这是杜绝“幻觉”的第一道防线二是要求处理矛盾信息这提升了回答的严谨性三是注明来源增强了可信度。{formatted_search_results}部分需要将检索到的多条信息清晰、结构化地拼接起来每条信息最好包含来源网址和核心内容摘要。上下文管理是另一个挑战。千问有上下文长度限制。当检索到的网页内容很长时不能全部塞进去。我的做法是先对抓取的正文进行智能摘要可以用千问自己做一个摘要或者只提取与用户问题相关性最高的段落通过计算嵌入向量相似度确保送入模型的参考信息是精炼且相关的。3. 系统工作流与核心环节实现3.1 端到端流程拆解整个Agent的工作流是一个清晰的管道下图展示了从用户提问到获得回答的完整过程flowchart TD A[用户输入问题] -- B[智能体接收并解析问题] B -- C{是否需要联网检索?} C -- 否 -- D[直接调用千问模型生成回答] C -- 是 -- E[规划并调用搜索引擎工具] E -- F[获取初步摘要与链接] F -- G[并行抓取高相关度网页内容] G -- H[提取并清洗正文] H -- I[智能摘要与关键信息提取] I -- J[整合检索结果 构建提示词] J -- K[调用千问模型 生成最终回答] D -- L[输出最终回答给用户] K -- L下面我们来深入流程中的几个关键环节。3.2 检索触发与查询优化不是所有问题都需要联网。让Agent对每个“你好”都去搜索一下既浪费资源又显得愚蠢。我实现了一个简单的“检索判断器”用一个小型的分类模型或一组关键词规则来判断问题是否需要实时信息。例如包含“最新”、“今天”、“2024年”、“股价”、“比分”等时效性词汇或涉及明显超出模型训练截止日期的事件则触发检索。用户的原生问题可能不适合直接搜索。例如“帮我看看特斯拉最近咋样了”这种口语化表达直接搜索效果差。这里需要做一个“查询优化”。我会用千问模型先将用户问题重写成一个更中立、包含关键实体、适合搜索引擎的查询语句。例如优化为“特斯拉 Tesla 2024年 最新 财报 股价 新闻”。这个步骤能显著提升首次检索的命中率。3.3 信息聚合与可信度评估搜索引擎会返回多条结果我们需要聚合和排序。简单的做法是按搜索排名但这不够。我的策略是结合多种信号来源权威性建立一个简单的域名权重表如.gov,.edu, 知名新闻机构域名给予更高权重。内容新鲜度优先选择发布时间更近的页面。内容相关性使用嵌入模型如text-embedding-ada-002计算用户问题与每条检索摘要/正文的余弦相似度。内容质量快速检查正文长度、是否包含大量广告或无关链接。综合这些分数对检索结果进行重新排序选择Top 3-5条作为最终送入模型的参考信息。对于明显是广告、内容农场或与问题无关的结果果断过滤掉。3.4 生成与校验闭环千问模型根据提示词和参考信息生成回答后工作并未结束。我增加了一个“事实一致性校验”环节。用一个更轻量级的模型或规则检查生成回答中的关键事实陈述如日期、数字、名称、事件是否在提供的参考信息中出现过。如果发现模型“偷偷”加入了未经检索证实的信息则触发警告或要求重新生成。这一步是防止模型“幻觉”的最后一道保险丝虽然增加了少量开销但对于追求高准确率的场景是值得的。4. 实操搭建从零构建一个可运行的Agent4.1 环境准备与依赖安装我们使用Python作为开发语言。首先创建一个干净的虚拟环境然后安装核心依赖。# 创建并激活虚拟环境以conda为例 conda create -n qwen-agent python3.10 conda activate qwen-agent # 安装核心库 pip install langchain langchain-community langchainhub # LangChain核心及社区工具 pip install beautifulsoup4 trafilatura lxml html2text # 网页抓取与内容提取 pip install requests aiohttp # 网络请求 pip install openai # 用于调用千问API需兼容OpenAI格式 pip install sentence-transformers # 用于本地计算文本相似度可选注意trafilatura是一个优秀的网页正文提取库比单纯用BeautifulSoup写规则要稳定得多能处理大多数新闻和文章网站。4.2 配置核心组件与工具假设你已经有了千问模型的API密钥例如通过DashScope平台并且有一个SerpAPI的密钥。import os from langchain.agents import AgentExecutor, create_react_agent from langchain.tools import Tool from langchain_community.utilities import SerpAPIWrapper from langchain_community.document_loaders import WebBaseLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_openai import ChatOpenAI from langchain import hub # 1. 配置模型将千问API适配为OpenAI格式 # 假设你的千问API基址是 https://dashscope.aliyuncs.com/compatible-mode/v1 llm ChatOpenAI( modelqwen-max, # 根据实际模型名调整 openai_api_keyyour_qwen_api_key, openai_api_basehttps://dashscope.aliyuncs.com/compatible-mode/v1, temperature0.1 # 低温度使输出更确定、更基于事实 ) # 2. 定义搜索工具 search SerpAPIWrapper(serpapi_api_keyyour_serpapi_key) search_tool Tool( nameSearch, funcsearch.run, descriptionUseful for when you need to answer questions about current events or recent information. Input should be a clear search query. ) # 3. 定义网页读取与摘要工具 def fetch_and_summarize(url: str, query: str) - str: 抓取网页并提取与查询相关的核心内容 try: # 使用WebBaseLoader加载 loader WebBaseLoader(url) data loader.load() full_text data[0].page_content[:5000] # 限制长度 # 简单实现寻找包含查询关键词的句子 # 更优方案用嵌入模型计算句子与query的相似度取Top K句 import re sentences re.split(r(?[。]), full_text) relevant_sents [s for s in sentences if any(kw in s for kw in query.split())] if len(relevant_sents) 5: summary 。.join(relevant_sents[:5]) 。 elif relevant_sents: summary 。.join(relevant_sents) 。 else: # 如果没有直接匹配返回开头部分 summary full_text[:500] ... return f来源{url}\n内容摘要{summary} except Exception as e: return f抓取网页 {url} 时出错{str(e)} fetch_tool Tool( nameFetchWebpage, funcfetch_and_summarize, descriptionUseful for getting detailed content from a specific webpage URL. Input should be a valid URL and the original user query. ) # 将工具组合成列表 tools [search_tool, fetch_tool]4.3 构建智能体并运行我们使用LangChain的ReAct代理模式它能让模型“思考”一步决定使用哪个工具然后观察结果再思考下一步。# 从LangChain Hub拉取一个优化过的ReAct提示词 prompt hub.pull(hwchase17/react) # 创建ReAct智能体 agent create_react_agent(llm, tools, prompt) # 创建代理执行器 agent_executor AgentExecutor( agentagent, toolstools, verboseTrue, # 开启详细日志方便调试 handle_parsing_errorsTrue, # 处理解析错误 max_iterations5, # 限制最大迭代次数防止死循环 early_stopping_methodgenerate # 提前停止策略 ) # 运行智能体 question 特斯拉2024年第一季度的汽车交付量是多少和去年同期比怎么样 result agent_executor.invoke({input: question}) print(result[output])当你运行这段代码并设置verboseTrue时会在控制台看到类似以下的思考过程Thought: 用户需要特斯拉2024年Q1的交付量数据及同比情况。这是最新的财务数据我需要搜索。 Action: Search Action Input: Tesla 2024 Q1 vehicle deliveries Observation: [SerpAPI返回搜索结果显示特斯拉官网新闻稿或路透社等媒体报道包含具体数字...] Thought: 我找到了关于交付量的文章但需要更详细的同比对比数据。我应该点开最权威的来源如特斯拉投资者关系页面查看详情。 Action: FetchWebpage Action Input: https://ir.tesla.com/press-release/... 特斯拉2024年第一季度交付量 Observation: [抓取到的网页摘要包含交付量、生产量及与去年同期的对比百分比...] Thought: 我现在掌握了准确数据可以回答用户了。 Final Answer: 根据特斯拉官方2024年4月2日发布的新闻稿2024年第一季度特斯拉在全球交付了约38.7万辆汽车... 与2023年第一季度的42.3万辆相比同比下降了约8.5%...这个流程清晰地展示了Agent“思考-行动-观察”的循环直到它认为收集到足够信息来生成最终答案。5. 性能优化与效果提升实战5.1 降低延迟并行化与缓存策略联网检索最大的性能瓶颈是网络I/O。如果串行地搜索、然后一个个抓取网页用户等待时间会很长。我的优化方案是并行检索与抓取使用asyncio和aiohttp库实现异步并发。在得到搜索结果的链接列表后同时发起多个网页抓取请求而不是等一个完成再抓下一个。这通常能将抓取阶段的耗时减少60%以上。结果缓存对于热门或重复性问题如“今天天气”没必要每次都搜索。可以使用redis或diskcache建立一个简单的缓存系统键为用户问题的哈希值值为检索到的结构化信息并设置一个较短的过期时间如10分钟。这能极大提升高频问题的响应速度。模型响应流式输出在千问生成最终答案时启用流式输出Streaming让用户能边生成边看到部分回答提升体验上的“响应速度”。5.2 提升答案质量后处理与润色原始检索到的信息可能是碎片化、带有营销语气或不连贯的。直接扔给模型生成的回答可能生硬。我增加了两个后处理步骤信息结构化与去重在将多条检索结果送入模型前先做一个简单的去重和合并。例如如果两个来源都提到了同一个数据只保留一个并标注“多方确认”。将信息按主题如“交付数据”、“财务表现”、“市场反应”稍作归类让提示词中的{formatted_search_results}部分更有条理。风格一致性控制在最终提示词中加入对回答风格的明确要求。例如“请用口语化、易于理解的方式总结”、“如果涉及数据请尽量使用对比如‘增长了X%’、‘减少了Y万台’”、“避免使用过于专业的金融术语”。这能让生成的对话更自然、更符合用户期待。5.3 处理复杂与多轮对话我们的基础Agent处理的是单轮问题。但真实对话往往是多轮的。例如 用户“苹果公司最新财报怎么样” Agent检索并回答“…营收为…iPhone销售…” 用户“那和华为比呢”这时Agent需要具备“记忆”能力。在LangChain中可以轻松地为AgentExecutor添加一个Memory组件例如ConversationBufferMemory。它会自动将之前的对话历史加入到后续的提示词中。当用户问“那和华为比呢”模型能从记忆里知道“苹果公司”和“最新财报”是上文从而生成一个新的搜索查询“华为 最新季度 财报 2024”进行对比分析。实现多轮对话的关键是确保记忆被正确传递并在每次调用工具时将相关的对话上下文也考虑进去。6. 常见问题、排查与安全考量6.1 典型问题与解决方案速查表问题现象可能原因排查与解决思路Agent陷入循环不停搜索1. 工具描述不清晰模型无法正确选择。2. 搜索未返回有效信息模型反复尝试。3.max_iterations设置过高。1. 检查工具description确保准确描述其功能和输入格式。2. 增加搜索查询的优化步骤或添加“未找到信息时返回特定提示”的逻辑。3. 降低max_iterations如设为3并设置early_stopping_method。回答包含未检索到的信息幻觉1. 提示词约束力不足。2. 模型温度temperature设置过高。3. 检索结果质量太差或为空模型被迫编造。1. 强化提示词使用“必须基于”、“严禁编造”等强指令。2. 将temperature降至0.1或0。3. 增加检索结果的质量过滤和空结果处理逻辑直接回复“未找到”。抓取网页失败或超时1. 网站反爬。2. 网络不稳定。3. 动态加载内容JS渲染。1. 添加请求头User-Agent使用代理IP池需谨慎合法使用。2. 增加重试机制和超时设置。3. 对重要站点考虑使用Selenium或Playwright进行渲染后抓取。回答冗长或重点不突出1. 送入模型的参考信息过于冗长。2. 模型生成参数需调整。1. 对抓取的网页内容进行更严格的摘要只保留核心段落。2. 在提示词中明确要求“简洁”、“分点列出”。调整生成参数max_tokens限制长度。处理速度慢1. 串行执行搜索和抓取。2. 模型响应慢。3. 检索结果过多。1. 实现异步并行抓取见5.1节。2. 考虑使用更快的模型如千问-Lite或启用流式输出改善体验。3. 限制单次检索和抓取的数量如只抓取前3条结果。6.2 安全、合规与成本控制内容安全与过滤互联网信息鱼龙混杂。Agent检索到的内容可能包含虚假信息、偏见或不当内容。必须在两个层面设防一是在检索结果侧集成一个轻量级的内容安全分类器对抓取到的文本进行快速过滤屏蔽明显有害信息二是在模型生成侧利用千问模型自身的安全对齐能力并在系统提示词中强调生成“安全、客观、无害”的内容。尊重版权与Robots协议大规模、频繁抓取网站内容可能涉及法律风险。务必遵守目标网站的robots.txt协议控制抓取频率添加延迟对于明确禁止抓取的网站应避免访问。在商业应用中优先考虑使用官方API或购买数据服务。成本监控这个系统的成本主要来自三块大模型API调用千问、搜索引擎API调用SerpAPI、计算资源。需要仔细监控为Agent的每一步思考、生成设置独立的max_tokens限制对搜索和抓取频率进行限流对于非实时性要求不高的问题可以引导用户使用本地知识库RAG模式避免不必要的搜索开销。构建一个稳定、可靠、高效的联网检索Agent是一个在效果、性能、成本和安全之间不断权衡和调优的过程。从简单的工具调用开始逐步加入查询优化、结果重排序、异步处理、记忆管理和安全过滤你会看到一个越来越智能和实用的对话助手诞生。这个项目最大的乐趣在于你真正地赋予了大模型感知实时世界的能力让它从一本厚重的百科全书变成了一个随时可以请教、永远在线的信息伙伴。