RAG与智能体:从AI原型到生产级应用的工程化实践
1. 项目概述从“玩具”到“工具”的AI工程化之路最近和不少做AI应用的朋友聊天发现一个挺有意思的现象大家用大模型API搞个Demo做个聊天机器人或者文本总结工具速度都很快效果乍一看也挺唬人。但一旦想把东西拿给真实用户用或者想把它嵌入到自己的核心业务流里问题就全冒出来了。比如你让模型帮你分析一份最新的行业报告它可能给你编造几个根本不存在的数据你想让它根据公司内部知识库回答客户问题它要么说“我不知道”要么就开始一本正经地胡说八道。这感觉就像你买了一台号称“全能”的工程机械结果发现它既不会挖坑也不会吊装离了说明书就寸步难行只能当个摆设。这背后的核心矛盾其实就是当前大模型能力与真实世界需求之间的“最后一公里”问题。大模型尤其是那些千亿、万亿参数的基础模型本质是一个基于海量互联网文本训练出来的“概率预测大师”。它擅长续写、概括、翻译这些模式相对固定的任务但对于需要精确、实时、私有化知识的场景就显得力不从心了。它不知道你公司上周刚更新的产品定价表也读不懂你数据库里那些结构复杂的客户记录更无法主动去调用一个外部API来查询今天的天气或股票价格。于是两个概念在AI工程化的实践中被推到了前台RAG检索增强生成和智能体Agent。这俩不是什么遥不可及的学术概念而是我们这些一线开发者用来给大模型“打补丁”、“装手脚”的实用工具箱。简单来说RAG解决的是“知识”问题让模型能说“正确”的话智能体解决的是“行动”问题让模型能做“有用”的事。这篇文章我就结合自己趟过的坑和做过的项目来拆解一下为什么在今天的AI应用开发中你几乎绕不开这两项技术以及它们是如何协同工作把一个“聊天玩具”变成真正可用的“生产力工具”的。2. 核心困境拆解大模型的“无知”与“无能”在深入RAG和智能体之前我们必须先搞清楚我们到底在解决什么问题。把大模型直接当“万能大脑”来用通常会撞上以下几堵南墙。2.1 知识幻觉与事实性谬误这是最头疼的问题没有之一。你问模型“我们公司旗舰产品Arix Pro的最大续航是多少” 尽管你的产品文档里白纸黑字写着“18小时”但模型可能会基于它在训练数据里看到的其他笔记本信息自信地回答“根据公开信息大约是12小时”。这种现象被称为“幻觉”Hallucination。模型并不是在“撒谎”它只是在基于概率生成最“流畅”和“合理”的文本而这个文本可能与事实相去甚远。在金融、法律、医疗等对准确性要求极高的领域这种幻觉是致命的。我曾参与一个金融合规审核的项目初期直接调用模型分析交易条款它偶尔会“创造”出一些不存在的监管条例编号差点引发误判。这让我们意识到不能让模型在“未知”或“私有”领域自由发挥。它需要一个可靠的知识来源作为依据。2.2 知识陈旧与缺乏实时性大模型的训练数据是有截止日期的。比如GPT-4的知识截止到2023年4月。这意味着它不知道这之后发生的任何事件、发布的新产品、变更的法律法规。你无法用它来查询今天的股价、分析刚出炉的财报、或者解答关于某个昨天才爆出新闻的科技事件。对于需要实时响应的应用如客服、新闻摘要、市场分析这是一个硬伤。模型需要一种机制能动态地获取并理解最新的信息。2.3 无法访问私有与结构化数据企业的核心价值往往沉淀在内部CRM里的客户记录、Confluence里的项目文档、数据库中的订单信息、GitLab里的代码库。这些数据要么是私有的从未出现在公开互联网上要么是高度结构化的如JSON、SQL表与模型训练时见到的自然文本相差甚远。模型对此一无所知就像一个被关在图书馆门外的人空有理解能力却无书可读。2.4 缺乏执行与交互能力模型本质上是一个“思考者”和“表达者”而不是一个“行动者”。它可以写出一段完美的代码但不能自己点击“运行”按钮它可以描述如何发送一封邮件但不能真的调用SMTP接口它可以分析是否需要查询数据库但不能自己建立连接并执行SQL。在很多自动化流程中我们需要AI不仅能“想”和“说”还要能“做”。这就需要为模型赋予调用工具Tools、执行任务Tasks的能力。注意很多人容易混淆“自动化脚本”和“智能体”。一个简单的、按固定流程运行的脚本不是智能体。智能体的核心在于“决策”——它能够根据模型的推理动态地决定下一步调用哪个工具、传入什么参数、如何处理返回结果。这个决策环路是智能体价值的关键。3. RAG详解为模型构建“外部记忆体”面对“无知”的困境最直观的想法就是给模型“喂资料”。RAG就是一套系统化的“喂资料”方法论。它的核心思想不是去改变模型本身那需要昂贵的微调而是在模型之外构建一个动态的、可检索的“知识库”在生成答案前先把相关的参考资料找出来和问题一起交给模型。这样模型就能基于你提供的“上下文”来生成答案大幅提高准确性和可控性。3.1 RAG的核心工作流程一个典型的RAG系统可以分为“索引”和“检索-生成”两个主要阶段。阶段一索引Indexing这个阶段是离线的目的是把你的原始知识文档、网页、数据库表等处理成便于快速检索的格式。加载与切分首先你需要把各种格式的文档PDF、Word、Markdown、HTML等加载进来并切割成大小合适的“块”Chunks。这里有个关键技巧切分不是简单按字数。好的切分要兼顾语义完整性。比如按段落切分比按固定500字切分更好对于Markdown可以按标题层级切分。使用像LangChain的RecursiveCharacterTextSplitter或Semantic Splitter能获得更好的效果。向量化这是RAG的“魔法”步骤。使用一个嵌入模型Embedding Model将每一个文本块转换成一个高维度的向量比如1536维。这个向量就像是这段文本的“数学指纹”语义相近的文本其向量在空间中的距离也会很近。常用的嵌入模型有OpenAI的text-embedding-3系列、开源的BGE、Voyage等。存储将这些向量以及对应的原始文本块存储到一个专门的“向量数据库”中。常见的向量数据库有Pinecone、Weaviate、Qdrant、Milvus以及PGVectorPostgreSQL的扩展。选择哪个取决于你的规模、性能要求和运维能力。阶段二检索与生成Retrieval Generation这个阶段是在线响应用户查询时发生的。问题向量化当用户提出一个问题Query系统使用同样的嵌入模型将这个问题也转化为一个向量。相似性检索在向量数据库中寻找与“问题向量”最相似的若干个“文本块向量”。这个过程通常使用余弦相似度或点积来计算。系统会返回相似度最高的前k个文本块例如top-5。上下文构建与提示将这k个检索到的文本块作为“参考上下文”与用户的原始问题一起构造成一个详细的提示Prompt发送给大语言模型。一个经典的提示模板可能是“请基于以下上下文信息回答问题。如果上下文信息不足以回答问题请直接说‘根据提供的信息我无法回答这个问题’。上下文{检索到的文本}。问题{用户问题}”。生成最终答案大语言模型基于这个富含上下文的提示生成最终答案。由于答案的“素材”来源于你提供的可靠文档其产生幻觉的概率大大降低。3.2 RAG实战中的关键技巧与避坑指南搭建一个能跑的RAG原型很简单但要让它在生产环境稳定、准确、高效需要注意大量细节。技巧一分块策略是成败的第一步切忌无脑固定长度分块512个字符的块可能会把一个完整的概念拦腰截断。对于技术文档可以按章节/子章节切分对于对话记录按对话轮次切分。重叠Overlap是必要的在切分时让相邻的文本块有少量重叠比如100个字符。这能确保一些关键信息如出现在段落末尾的定义不会因为恰好被切在边界而丢失提高了检索的召回率。实操心得我通常会先用不同的分块策略按段落、按标题、固定长度重叠对一小部分数据做索引然后用一组标准问题去测试检索质量选择效果最好的策略。没有银弹必须结合数据特性实验。技巧二检索不是“一锤子买卖”简单相似性检索的局限如果用户问“苹果公司最新财报怎么样”而你的知识库里只有一篇题为“Apple Q4 2023 Financial Results”的PDF由于“苹果”和“Apple”的语义虽然一样但向量表示可能因语言不同而有差异可能导致检索失败。引入查询重写与扩展在检索前可以先让大模型对原始查询进行重写或扩展。例如将“苹果财报”重写为“Apple financial report earnings Q4 2023”。这能显著提升检索的鲁棒性。LangChain中的MultiQueryRetriever就是这个思路的自动化实现。混合检索Hybrid Search除了向量检索还可以结合传统的关键词检索如BM25。关键词检索在精确匹配术语、缩写、产品型号等方面有优势。将两者的结果进行加权融合如 Reciprocal Rank Fusion能兼顾语义相似性和字面匹配度效果通常比单一方法好。技巧三提示工程是质量的“放大器”检索到了对的文档但如果提示没写好模型依然可能忽略上下文或生成糟糕的答案。明确指令在提示中强烈要求模型“必须且仅能”依据提供的上下文回答。可以设置“引用”格式要求模型在答案中标注出处来自哪个文档块这既方便用户溯源也便于我们后期评估RAG链路的质量。处理“未找到”场景一定要在提示中告诉模型如果上下文不相关或不足应该如何回应。一个友好的“我暂时没有找到相关信息您可以尝试……”比一个胡编乱造的答案体验好得多。上下文排序与过滤检索到的top-k个块其相关性可能差异很大。可以在送入最终提示前用一个更轻量的模型或规则对它们进行重排序或过滤只保留最相关的几个避免无关信息干扰模型也节省了令牌Token消耗。注意RAG不是微调的替代品而是互补。对于需要模型深度理解特定领域术语、风格或推理模式的任务如生成特定格式的法律文书微调Fine-tuning可能更有效。RAG更擅长处理需要大量、动态、事实性知识支撑的问答场景。在实际项目中我经常看到“RAG 轻量微调”的组合拳用RAG解决知识问题用微调让模型更“懂行”。4. 智能体详解为模型安装“手脚”与“调度中心”解决了“知识”问题我们来看“行动”问题。智能体Agent的本质是赋予大语言模型使用工具Tools、进行规划Planning、并持续执行Execution的能力。你可以把它想象成一个项目的“高级主管”它理解老板用户的最终目标“做一份竞品分析报告”知道自己手下可用工具有哪些人搜索工具、文档分析工具、图表生成工具然后自己制定步骤先搜索最新信息再总结各自优缺点最后生成报告草稿并一步步指挥协调直到任务完成。4.1 智能体的核心架构推理-行动循环智能体的工作遵循一个经典的“感知-思考-行动”循环在LLM语境下常被称为ReActReasoning Acting框架。观察智能体接收到用户的目标或当前任务状态。思考大语言模型作为“大脑”分析当前状况。它需要决定任务完成了吗如果没完成下一步该做什么应该调用哪个工具调用时需要什么参数行动根据思考的结果智能体调用相应的工具如执行一段代码、调用一个API、查询数据库并获取工具执行的结果。再观察将工具执行的结果成功或失败以及返回的数据作为新的观察反馈给“大脑”。循环模型基于新的观察再次进行思考决定下一步行动如此循环直至任务完成或无法继续。4.2 智能体中的关键角色工具Tools工具是智能体能力的延伸。一个工具本质上是一个函数它有明确的名称、描述、参数格式。智能体通过提示词学习这些工具的功能。常见的工具有网络搜索如SerpAPI或DuckDuckGo Search让智能体能获取实时信息。代码执行如Python REPL让智能体能进行数学计算、数据处理。文件操作读写本地文件。API调用连接企业内部或第三方服务如发送邮件、查询数据库、操作CRM。专属工具你为特定业务编写的任何函数比如“查询本月销售数据”、“为客户生成保单号”。定义工具的技巧名称和描述要清晰模型的“思考”依赖于你对工具的描述。search_web就不如search_web_for_latest_news明确。描述要写清楚“这个工具是干什么的”以及“在什么情况下使用它”例如“使用此工具在维基百科上搜索关于历史人物或事件的摘要信息。”处理好复杂参数如果工具参数是一个复杂对象最好在描述中给出清晰的JSON结构示例帮助模型正确格式化请求。4.3 主流智能体框架与平台选择现在有很多优秀的框架和平台能帮助我们构建智能体降低开发门槛。LangChain / LangGraph这是目前生态最丰富、最灵活的框架之一。LangChain提供了构建智能体所需的所有基础组件工具、记忆、链。而LangGraph更进一步允许你以“图”的形式可视化地定义智能体的工作流特别适合构建有复杂状态转移和分支逻辑的多步骤智能体。它给了开发者极大的控制权但学习曲线相对陡峭。AutoGen由微软推出专注于多智能体协作。你可以创建多个角色化的智能体如程序员、测试员、产品经理让它们通过对话来协作解决复杂任务。这在需要多角度评审或分工的场景下非常强大比如协同代码开发、方案辩论等。Dify / Coze这类属于低代码/无代码AI应用平台。它们提供了可视化的界面让你可以通过拖拽组件的方式快速组装包含RAG、智能体、工作流在内的复杂应用。Dify的“工作流”画布和Coze的“Bot”创建界面都非常直观。它们的优势在于极快的原型开发和部署速度特别适合产品经理、业务人员或需要快速验证想法的开发者。但深度定制能力可能不如纯代码框架。如何选择如果你是研究者或需要极度定制复杂逻辑的工程师LangGraph是你的不二之选。如果你想探索多智能体社会的交互与协作AutoGen提供了绝佳的试验场。如果你的目标是快速将AI能力转化为可用的业务应用且团队中不一定有资深AI工程师Dify或Coze这类平台能让你在几小时内就看到成果。我个人在为企业做内部效率工具PoC时经常先用Dify快速搭出可演示的版本验证价值后再考虑用代码重构。4.4 智能体开发中的常见陷阱与调试心得智能体开发听起来很酷但调试起来可能让人抓狂因为它涉及非确定性的LLM推理。陷阱一智能体陷入“死循环”或“无效行动”这是最常见的问题。智能体可能反复调用同一个工具而不推进或者在几个无关工具间来回切换。对策设置明确的停止条件Stop Condition和最大迭代次数。在提示词中清晰地告诉模型“如果你认为已经获得了足够的信息来回答问题或者连续三次尝试都无法取得进展请直接输出最终答案。” 在代码层面务必设置循环上限比如10次防止无限循环消耗资源。调试技巧开启智能体的详细日志观察它的“思考”过程。看看它在每一步到底是如何解析观察、决定行动的。很多时候问题出在工具的描述不够清晰或者上一步工具返回的结果格式让模型产生了误解。陷阱二工具调用参数错误模型可能会误解你的要求给工具传入错误类型或格式的参数。对策强化工具描述中的示例。对于复杂参数使用JSON Schema进行严格定义。一些高级框架支持“工具调用”Function Calling模式模型会输出结构化的调用请求这比从自然语言文本中解析参数要可靠得多。此外可以在工具函数内部增加健壮的类型检查和错误处理当参数错误时返回清晰的错误信息帮助模型进行修正。陷阱三处理开放域任务的不可控性你告诉智能体“帮我策划一个周末旅行”这个目标非常开放。智能体可能会调用搜索工具查找“旅行”然后开始预订机票和酒店如果它有这些工具权限这显然存在风险和成本。对策为智能体设定明确的边界和权限。在提示词开头就定义它的角色和限制例如“你是一个旅行信息咨询助手只能提供信息查询和方案建议不能执行任何实际的预订、支付或更改数据的操作。” 同时在工具层面进行权限控制对于高风险工具如写数据库、发邮件需要额外的确认机制或根本不对智能体开放。实操心得开发智能体时我习惯采用“由简入繁”的策略。先实现一个只有一个核心工具的简单智能体确保它能稳定运行。然后逐步添加更多工具和更复杂的推理逻辑。每加一个新功能都用一组测试用例去验证观察智能体的决策是否符合预期。把智能体想象成一个需要“训练”和“引导”的新员工清晰的指令提示词和规范的流程工具设计至关重要。5. RAG与智能体的融合构建下一代AI应用单独使用RAG或智能体已经能解决很多问题但当我们把两者结合起来时就能构建出真正强大、自主的AI应用。我称之为“有记忆、会思考、能行动”的智能系统。5.1 融合的典型模式模式一智能体驱动RAG在这种模式下智能体作为总控RAG作为它手下的一个“专家工具”。场景用户问“对比一下我们产品A和竞争对手产品B在能耗方面的表现并给出建议。”流程智能体“思考”要回答这个问题我需要两份资料我们产品A的规格书以及竞争对手产品B的公开评测或官网数据。智能体“行动”调用“内部知识库查询工具”即RAG系统传入查询“产品A 规格书 能耗”。获取结果。智能体“行动”调用“网络搜索工具”传入查询“产品B 评测 能耗 2024”。获取结果。智能体“思考”现在我有了两份资料我需要分析对比并生成建议。智能体“行动”调用“文本分析与报告生成工具”可以是另一个LLM调用传入两份资料和对比指令生成最终答案。这里RAG负责解决获取内部精确知识的任务而智能体负责规划整个任务流并协调RAG和搜索引擎等多个工具。模式二RAG增强智能体在这种模式下智能体在每一步决策时都能获得来自RAG系统的背景知识支持使其决策更精准。场景一个用于处理IT运维工单的智能体。流程用户提交工单“服务器X的磁盘空间报警了。”智能体在决定采取什么行动前先通过RAG查询“服务器X的历史运维记录”、“磁盘清理标准操作流程SOP”、“与该服务器相关的应用清单”。RAG返回相关文档历史记录显示该服务器上周刚清理过日志SOP指出先检查/var/log目录应用清单显示上面跑着核心数据库。智能体“思考”根据SOP我应该先建议检查日志目录。但历史记录显示刚清理过可能问题复发。考虑到是核心数据库服务器操作需谨慎。智能体“行动”生成回复“根据SOP建议先检查/var/log目录占用情况可使用du -sh /var/log/*命令。请注意该服务器上周已进行日志清理且运行核心数据库建议在业务低峰期操作并先确认数据库日志配置。是否需要我为您生成更详细的检查脚本”在这个例子里RAG在智能体决策的“思考”环节提供了关键的背景信息使得智能体的建议不再是泛泛而谈而是具有针对性和历史上下文。5.2 融合架构的设计考量当你决定融合两者时架构设计需要仔细权衡。记忆管理智能体有自己的工作记忆Working Memory来存储多轮对话和中间结果而RAG拥有长期的知识记忆。需要设计好两者之间的信息交换通道。例如智能体可以将本轮对话的摘要或关键结论作为新的文档存入RAG知识库实现知识的积累。流程编排复杂的任务可能涉及多次RAG检索和多个工具调用。使用像LangGraph这样的框架可以清晰地编排这些步骤定义条件分支例如如果RAG检索结果为空则转向网络搜索。评估与监控融合系统更复杂评估点也更多。需要监控RAG的检索相关性、智能体的工具调用成功率、任务完成度、最终答案的用户满意度等。建立一套评估体系对迭代优化至关重要。6. 从概念到落地构建你的第一个AI智能助理理论说了这么多我们来看一个具体的、可落地的例子构建一个“技术文档智能问答助理”。它既能回答关于你公司技术产品的具体问题用RAG又能根据问题帮你生成代码片段或执行简单的系统诊断命令用智能体。6.1 技术栈选择与环境准备为了平衡灵活性和开发效率我们选择以下技术栈后端框架LangChainLangGraph。它提供了我们所需的所有基础模块且LangGraph能很好地描述智能体的工作流。向量数据库Chroma。它轻量、易用适合原型和中小规模项目无需单独部署服务。嵌入模型OpenAI text-embedding-3-small。在效果和成本间取得良好平衡API调用方便。大语言模型OpenAI GPT-4o。作为智能体的“大脑”其推理和指令跟随能力较强。前端简单的Gradio界面用于快速演示。首先安装必要的Python包pip install langchain langchain-openai langchain-chroma langgraph gradio6.2 核心模块实现第一步构建RAG索引模块我们创建一个类来处理文档的加载、切分和索引。from langchain_community.document_loaders import DirectoryLoader, TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma import os class KnowledgeBaseIndexer: def __init__(self, persist_directory./chroma_db): self.embeddings OpenAIEmbeddings(modeltext-embedding-3-small) self.persist_dir persist_directory self.text_splitter RecursiveCharacterTextSplitter( chunk_size1000, chunk_overlap200, separators[\n\n, \n, 。, , , , , , ] ) def index_documents(self, docs_directory): 加载指定目录下的所有文本文档并创建索引 loader DirectoryLoader(docs_directory, glob**/*.txt, loader_clsTextLoader) documents loader.load() print(f已加载 {len(documents)} 个文档) # 切分文档 splits self.text_splitter.split_documents(documents) print(f切分为 {len(splits)} 个文本块) # 创建向量存储 vectordb Chroma.from_documents( documentssplits, embeddingself.embeddings, persist_directoryself.persist_dir ) vectordb.persist() print(f索引已创建并保存至 {self.persist_dir}) return vectordb def get_retriever(self, k4): 获取检索器 vectordb Chroma(persist_directoryself.persist_dir, embedding_functionself.embeddings) return vectordb.as_retriever(search_kwargs{k: k})第二步定义智能体可用的工具我们定义三个工具RAG检索工具、代码执行工具、系统命令工具需谨慎使用。from langchain.tools import tool from langchain.utilities import BashProcess import subprocess # 工具1: RAG检索工具 tool def search_knowledge_base(query: str) - str: 当用户询问关于产品、API、配置等内部技术文档问题时使用此工具从知识库中查找相关信息。 # 这里需要接入第一步创建的检索器 # 假设我们有一个全局的 retriever 对象 docs retriever.invoke(query) context \n\n.join([doc.page_content for doc in docs]) return f从知识库中检索到以下相关信息\n{context} if context else 知识库中未找到相关信息。 # 工具2: 安全的代码执行工具仅限Python tool def execute_python_code(code_snippet: str) - str: 当用户请求生成或验证Python代码片段时使用此工具在安全沙箱中执行代码并返回结果。输入必须为纯Python代码字符串。 try: # 使用exec在局部作用域中执行避免污染全局环境 local_vars {} exec(code_snippet, {}, local_vars) # 尝试获取一个显式的结果如果没有则说明代码可能已打印输出 # 更健壮的做法是重定向stdout这里为简化示例 output str(local_vars.get(result, 代码执行完成无返回值请检查是否有打印输出。)) return f代码执行成功。输出{output} except Exception as e: return f代码执行出错{str(e)} # 工具3: 受限的系统命令工具示例生产环境需极度小心 bash BashProcess() tool def run_safe_system_command(command: str) - str: 当用户需要检查系统状态如磁盘空间、进程且命令安全时使用此工具。仅允许执行白名单内的命令。 SAFE_COMMANDS [df -h, uptime, whoami, date] if command.strip() not in SAFE_COMMANDS: return 错误该命令不在允许的安全命令列表中。 try: result bash.run(command) return result except Exception as e: return f命令执行失败{str(e)}第三步使用LangGraph构建智能体工作流我们设计一个简单的图智能体先尝试用RAG回答问题如果RAG结果不理想或用户要求执行操作则调用相应工具。from langgraph.graph import StateGraph, END from langchain_openai import ChatOpenAI from typing import TypedDict, Annotated import operator # 定义状态结构 class AgentState(TypedDict): question: str rag_answer: str tool_calls: list final_answer: str # 初始化模型和工具 llm ChatOpenAI(modelgpt-4o, temperature0) tools [search_knowledge_base, execute_python_code, run_safe_system_command] llm_with_tools llm.bind_tools(tools) # 1. 路由节点决定使用RAG还是调用工具 def router(state: AgentState): question state[question] # 简单的启发式路由如果问题包含“如何实现”、“代码”、“执行”、“命令”等倾向于调用工具 tool_keywords [代码, 执行, 命令, 运行, 写一个, 如何实现, debug] if any(kw in question for kw in tool_keywords): return call_tools else: return use_rag # 2. RAG处理节点 def rag_node(state: AgentState): docs retriever.invoke(state[question]) context \n\n.join([doc.page_content for doc in docs]) prompt f基于以下上下文信息回答问题。如果信息不足请如实告知。 上下文 {context} 问题 {state[question]} 答案 response llm.invoke(prompt) return {rag_answer: response.content} # 3. 工具调用节点 def tool_node(state: AgentState): messages [(user, state[question])] # 让模型决定调用哪个工具 ai_msg llm_with_tools.invoke(messages) tool_calls ai_msg.tool_calls if not tool_calls: return {final_answer: 我无法处理这个请求因为它需要我执行不支持的特定操作。} results [] for tool_call in tool_calls: tool_name tool_call[name] tool_to_call next(t for t in tools if t.name tool_name) result tool_to_call.invoke(tool_call[args]) results.append(f{tool_name} 结果{result}) # 让模型根据工具结果总结最终答案 summary_prompt f用户问题{state[question]}\n工具执行结果{ .join(results)}\n请根据以上结果给出清晰完整的最终回答。 final_msg llm.invoke(summary_prompt) return {tool_calls: tool_calls, final_answer: final_msg.content} # 4. 构建图 workflow StateGraph(AgentState) workflow.add_node(rag_node, rag_node) workflow.add_node(tool_node, tool_node) workflow.set_conditional_entry_point( router, { use_rag: rag_node, call_tools: tool_node, } ) workflow.add_edge(rag_node, END) workflow.add_edge(tool_node, END) agent workflow.compile()第四步集成与测试最后我们将索引器、智能体和一个简单的Web界面集成起来。import gradio as gr # 初始化 indexer KnowledgeBaseIndexer() # 假设文档已放在 ./docs 目录下首次运行需要创建索引 # vectordb indexer.index_documents(./docs) retriever indexer.get_retriever(k4) def chat_with_agent(message, history): 处理用户消息 result agent.invoke({question: message}) final_answer result.get(final_answer) or result.get(rag_answer) return final_answer # 创建Gradio界面 demo gr.ChatInterface( fnchat_with_agent, title技术文档智能助理, description可以回答技术文档问题也能执行简单的代码和系统命令安全限制内。 ) if __name__ __main__: demo.launch()6.3 部署与优化注意事项安全性是重中之重上述示例中的run_safe_system_command工具极度简化绝对不要在生产环境中直接开放Bash命令执行。必须建立严格的命令白名单、参数校验、权限控制和沙箱环境。RAG检索质量定期评估和优化你的分块策略、嵌入模型和检索器。可以引入重排序模型来提升top-k结果的精度。智能体稳定性为智能体的推理循环设置超时和最大步数限制避免成本失控或死循环。记录完整的交互日志便于分析和调试。成本控制RAG的嵌入调用和智能体的多次LLM调用都会产生费用。可以通过缓存常见的检索结果、对智能体的思考步骤进行适当限制比如在简单问题上不使用ReAct来优化成本。这个项目示例展示了如何将RAG作为知识来源将智能体作为决策和执行中枢构建出一个能理解私有文档、又能进行简单操作的实用AI助手。你可以在此基础上扩展更多的工具如连接Jira、查询数据库、优化路由逻辑、并为其添加记忆功能使其能处理更复杂的多轮对话任务。