ARTICLE DETAIL

资讯详情

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

单智能体工具调用型Agentic RAG:本地语料库问答架构实践

单智能体工具调用型Agentic RAG:本地语料库问答架构实践 开头要直接、有信息量像一个做过实际项目的人在复盘。我会以“为什么传统RAG在本地语料库场景下不够用”切入然后展开架构、实操、踩坑、扩展四部分最后用个人体会收尾。输出纯Markdown从二级标题开始不出现任何元说明、字数统计和AI套话。1. 本地语料库的问答为什么传统 RAG 先撞上了天花板先说结论在做这个 Agentic RAG 原型之前我手头已经有一套“标准教科书式”RAG 管线——文档切块、向量化、top_k 召回、拼接提示词、让大模型根据上下文回答。这套东西处理简单事实问答“XX 产品支持哪些协议”效果尚可但一旦面对需要多步推理、条件判断、多文档对照的问题它就像一个只会“查了就说”的自动售货机你把硬币投进去它吐什么你吃什么它不关心你投的是不是对应商品的钱币也不关心货架上还有没有更合适的商品。我最初的目标其实很克制做一个面向有限本地语料库的原型系统语料规模大概几百份内部技术文档总量不超过几万 token 的实际检索面。为什么强调“有限”因为这种场景在工程上很常见——不是互联网规模的知识库不需要分布式向量数据库不需要重型的检索中台但恰恰因为语料少、领域专、答案要求严谨传统 RAG 的缺点会被放大。语料一少召回结果里混入一条看似相关实则无关的内容对大模型答案的误导比大语料场景更明显领域一专用户问法千奇百怪但文档里其实只写了一层意思模型如果不多次“确认”就着急回答很容易靠内部知识脑补。传统 RAG 的根本问题是检索不是模型自己决策出来的而是管线预设的固定动作。无论问题是什么系统都会执行“编码问题—相似度检索—拼接提示”这条流水线。于是出现了几个很典型的现象用户问“你们最近有没有更新过安装流程”系统检索到一堆关于“安装”的片段但文档日期排序、版本变更记录这些信息没有进入召回结果因为相似度检索根本不管时间维度。用户问“A 文档和 B 文档关于同一个接口参数的描述是否有冲突”传统 RAG 只会单独检索最相似的片段不会主动做两轮检索、再把两段话放在一起比较。用户问“这个工具能做什么”这种开放性问题传统 RAG 还是会老老实实去检索然后模型可能只凭内部知识就长篇大论回答检索到的片段反而没用上。这些现象逐渐让我意识到问题的本质不是“检索精度不够高”而是决策权不在模型手里。System 只负责把检索结果灌进去至于什么时候检索、检索几轮、检索回来之后还要不要追加一次验证系统完全不关心。于是我把注意力转向了 Agentic RAG——一种让大模型把“检索”当作可主动调用的工具的架构形态。这个原型的定位也随之明确单智能体工具调用型面向有限本地语料库。标题里的三个限定词不是随便写的它们分别压住了三类复杂度。要理解这个原型得先拆清楚这三个限定词的边界到底在哪里。1.1 单智能体为什么没上多智能体编排在规划架构时我第一个排除的就是多智能体方案。原因很实际本地语料库规模有限业务链路也不复杂多智能体意味着多个角色、多个上下文窗口、多个调度协议这些全是额外的失败点。Google 的 Agent 白皮书里对 Agent 的解读我很认同一个精简的 Agent 系统核心就是“模型 工具 指令”而多智能体协作更像组织架构只有当任务本身确实需要分工制衡时才有必要引入。对于一个几百份文档的检索问答场景分工不可能带来质量收益只会带来调试痛苦。单智能体的另一个好处是行为可追踪。整个决策链路是一条完整的思考流模型看到问题 → 决定是否调用工具 → 观察到工具结果 → 再决定继续检索还是给出回答。这个循环里每一轮发生的事情都可以打印出来排查问题的时候非常直观。多智能体系统里问题可能出在规划器、执行器、验证器之间任何一环追责都不好追。1.2 工具调用型检索从“管线内置”变成了“模型可决策行为”这是 Agentic RAG 和传统 RAG 最核心的分水岭。传统 RAG 里检索是一个外部编排动作Agentic RAG 里检索是暴露给模型的一个工具模型自己判断“我需不需要查”。我测试时还发现另一个有意思的行为差异传统 RAG 面对“你好”这类闲聊也会先去检索一轮再回答而 Agentic RAG 的模型会直接判断“无需调用工具”然后用通用能力回应。单看这一个行为变化就已经把系统整体的可用性往上拉了一大截。在动手写代码之前我把传统 RAG 和 Agentic RAG 的行为差异整理了一下这张表后来成了我向同事解释这个项目最常用的材料维度传统 RAG工具调用型 Agentic RAG检索触发方式管线预设固定执行模型决策可执行可不执行检索轮次通常一轮可多轮观察到不满足后再查对检索结果的态度无条件拼入上下文模型可判断结果是否充分多文档对照需要外部程序实现模型自发多次调用、组织比对闲聊/无关问题开销浪费检索资源零检索开销可解释性只看得见最终拼接提示词完整可见思考与调用链条1.3 有限本地语料库这个限定词反而降低了实现门槛“有限”和“本地”这两个词意味着我不用考虑海量数据的分布式检索不用考虑权限隔离的多租户方案甚至连向量数据库都可以用一个轻量级嵌入式方案解决。整个系统的复杂度上限被压到了一个原型该有的水平本地文件系统 嵌入式向量索引 一个支持工具调用的 LLM API。如果语料是几千万条网页数据那这篇文章的写法会完全不同——先上 ES、再上向量检索集群、还要做缓存和分层召回。但有限本地语料库的目标场景核心矛盾从来不在“存不下、召回不动”而在“模型能不能在正确的时候拿正确的东西”。这个判断直接决定了我后面一整套工具设计思路。2. 单智能体工具调用型架构的组件循环、工具定义与决策边界有了方向我开始审视整个系统的架构构成。一个单智能体工具调用型 Agentic RAG本质上可以理解成一个“会自己决定是否查资料的人”——先听问题再判断自己要不要翻资料、翻什么资料、翻完够不够最后给出答案。这个过程中有三个关键组件缺一不可Agent 循环、工具描述、上下文管理。2.1 整体流程决策→调用→观察→回答的循环我实现的核心循环非常直接几乎可以在任何代码框架里手写出来逻辑如下用户问题进入对话上下文。大模型根据 System Prompt 和当前对话历史输出“本次回复的内容”可能是文本也可能包含工具调用请求。系统判断模型输出中是否有工具调用请求。如果没有说明模型认为已经可以回答直接把模型的文本返回给用户循环结束。如果有系统执行对应的工具函数比如检索文档把执行结果以“工具消息”的形式追加到对话上下文中。带着最新的工具结果重新让大模型生成回复回到第 2 步。这个循环里最值得琢磨的是“工具消息”的组织方式。在我用的 API 里一次工具调用的完整记录通常分成两段第一段是模型发出的“我请求调用 search_documents参数是 {query: 安装流程}”第二段是系统返回的“工具结果 message”。这两段必须一起送回给模型模型才能把“我查了什么”和“查到了什么”关联起来。很多新手经常犯的错就是只回传结果、不回传请求模型看到一条来历不明的工具消息直接懵掉。用 Python 描述这个循环的大致结构如下messages [{role: system, content: SYSTEM_PROMPT}] messages.append({role: user, content: user_query}) for step in range(MAX_STEPS): resp llm.chat(messagesmessages, toolsTOOL_SCHEMAS) msg resp.message if not msg.tool_calls: return msg.content messages.append(msg) # 模型发出的工具调用请求 for tool_call in msg.tool_calls: result execute_tool(tool_call.function.name, tool_call.function.arguments) messages.append({ role: tool, tool_call_id: tool_call.id, content: result })MAX_STEPS 我一般设为 3。原因后面实测部分会说在有限语料场景下超过三轮调用之后边际收益迅速衰减但上下文占用和 token 成本却直线上升。这个值是我反复试出来的不是随手拍的。2.2 工具描述让模型准确理解“查文档”这件事工具调用型架构里模型能不能正确使用工具一半取决于模型本身的能力另一半取决于工具描述写得好不好。我最初犯过一个低级错误把工具描述写得太简略只写了“search_documents(query: str)”。结果模型经常把一句话问题原封不动地塞进 query 里检索出来的东西噪声很大。后来我花了很长时间打磨工具描述把参数含义、使用场景、返回结构都写清楚。这里的关键不是“描述写得越多越好”而是把模型在做决策时需要知道的信息精确传达给它。我给 search_documents 工具写的描述大致是这样的{ type: function, function: { name: search_documents, description: 在本地技术文档库中检索与用户问题相关的文档片段。当用户问题涉及产品功能、参数配置、安装部署、接口调用、故障排查等可能记录在内部文档中的内容时应该调用本工具。如果用户只是闲聊或询问通用知识则不应调用。, parameters: { type: object, properties: { query: { type: string, description: 本次检索的查询词应尽量提取用户问题中的关键技术名词和短语比如产品名、参数名、错误码等不要直接拷贝整句问题。 }, top_k: { type: integer, description: 召回片段数量默认3最大5。普通问题用3需要多文档对比的复杂问题可以用5。, minimum: 1, maximum: 5 } }, required: [query] } } }这里有一个细节值得细说为什么不让模型直接传整句问题我在早期测试中发现对于长句问题模型容易把修饰性的描述也塞进 query导致语义重心偏离。比如用户问“我们生产环境里用 XX 模式部署的时候POD 一直 CrashLoopBackOff 该怎么办”直接把整句作为 query向量检索结果会被“生产环境”“部署”“CrashLoopBackOff”等多个概念拉扯而提取出“CrashLoopBackOff”这种强特征词往往一检索就中。我在工具描述里专门写了“提取关键技术名词”模型大多数时候都能很好地完成这个动作。另外工具返回的内容也不是原始检索片段而是一个格式化后的 JSON包含哪些文档命中、每个片段的内容、来源文件名等信息。这个结构化输出会让模型在回答时自然地知道“我是依据哪份文档说的”而不是把检索到的文字当成自己的内部知识。{ results: [ { doc_name: xx产品部署指南.pdf, snippet: 生产环境建议使用 XX 模式部署若 POD 频繁重启请检查资源配额……, score: 0.86 } ] }2.3 决策边界模型什么时候不该调用工具工具调用型系统最需要调教的其实不是“什么时候调用工具”而是“什么时候不调用”。我写进 System Prompt 里的一条核心规则是如果用户问题与文档库中的信息无关或者文档库中没有相关内容模型应该直接回答不知道而不是为了调用而调用。为什么这条规则如此重要因为我观察到大模型在拥有工具能力之后会出现一种“工具依赖倾向”——不管什么问题先调一下工具再说仿佛不调用就不算负责任。这种行为在 Agentic RAG 里的危害尤其明显检索结果本身就是有噪声的如果把检索到的无关内容当作依据来回答模型会一本正经地把错的东西当作文档内容告诉你比直接承认不知道更容易误导用户。所以在 System Prompt 里我用了一个明确的分层决策逻辑第一层判断“这个问题是否需要借助外部知识库”第二层判断“当前召回结果是否已经充分回答了问题”第三层判断“如果结果不充分是调整思路再检索一轮还是直接告诉用户知识库中没有相关内容”。这套提示词设计让模型的决策行为明显稳健下来后面实测部分我会详细展示效果。3. 从零搭建可运行原型本地索引、工具函数与智能体循环的落地记录架构想清楚之后落地阶段反而没有太多戏剧性。这个原型的整体技术选型是“轻量级为主”本地文件存储文档嵌入式向量库做索引模型 API 使用支持 function calling 的通用大模型。我接下来把每一个环节的关键决策和参数都写出来保持可复现性。3.1 语料准备与切分策略按自然段落还是固定长度我手里的原始语料是 PDF、Markdown 和 HTML 三种格式混合的内部资料。PDF 处理最麻烦需要先用工具抽取文本Markdown 和 HTML 则天然带结构信息。切分策略我纠结了很久最后选择了“基于结构优先长度兜底”的方案。对于 Markdown 和 HTML优先按标题层级切分一个二级标题下的内容作为一个候选片段如果片段太长超过 1000 字再按段落进一步切分如果太短少于 100 字与前一个段落合并。这样做的好处是片段天然具有语义完整性一个片段内部讲的通常是同一个主题检索命中时的信息密度更高。对于 PDF情况更复杂一些因为 PDF 抽取出来的文本经常丢失标题层级结构。我退而求其次采用固定长度加滑动重叠的切法块大小 512 字重叠 64 字。经验是PDF 切分不要追求花哨的语义切分重叠窗口比什么都管用——因为 PDF 文字经常被页眉页脚打断没有重叠窗口一句话会被硬生生切成两半。切分完成后每个片段会带上一个元数据头来源文件名、章节路径如果能提取到、页码。这些元数据会进入向量库也会在检索结果的格式化输出中保留让模型在回答时能提供溯源信息。3.2 embedding 与本地向量检索的选型参数有限本地语料库的场景下向量化方案我最初对比了三种列个表展示区别方案优点缺点适配场景调用云端 embedding API效果好无需本地存模型数据需要出本地环境不适合严格内网非敏感语料的快速原型本地 bge-m3 模型中文效果好完全本地化需要数 GB 内存/显存首次加载慢中文文档占比高的内网场景本地更轻量的 embedding 模型文本向量小模型极轻量CPU 也能跑长文档/复杂语义召回效果略差硬件受限、语料规模极小的场景我最终选了本地 bge-m3 模型维度是 1024距离度量用内积embedding 生成后默认已归一化。索引方面用的是 FAISS 的 IndexFlatIP——这对“有限语料库”来说足够了暴力精确检索在几万片段量级下毫秒级完成根本不需要走 HNSW 之类的近似索引。很多人看到 FAISS 就默认要上 IndexIVFFlat 或 HNSW但在有限本地语料场景里这是没必要的复杂度。精确索引 内积逻辑简单结果还可复现。检索参数上我默认 top_k3score 阈值设为 0.45 左右的动态校准。这里要说一下阈值的作用当检索结果的分数整体低于阈值时工具返回里会带一个“该查询未检索到足够相关的内容请考虑直接回答未知”的提示。这个设计是为了应对模型工具依赖问题——如果检索结果本来就很弱模型就不该继续硬编答案。3.3 工具函数的注册与执行环境有了工具描述 schema下一步是把实际的 Python 函数和 schema 对应起来。我这里维护了一个简单注册表把函数名映射到可执行对象上同时执行结果统一用 JSON 序列化。TOOL_EXECUTORS { search_documents: search_documents, get_document_meta: get_document_meta, } def execute_tool(name: str, arguments: dict): executor TOOL_EXECUTORS.get(name) if not executor: return json.dumps({error: funknown tool: {name}}) try: return json.dumps(executor(**json.loads(arguments)), ensure_asciiFalse) except Exception as exc: return json.dumps({error: str(exc)})这里有一个非常容易被忽略的坑arguments 是模型返回的 JSON 字符串但有些模型的 function calling 输出并不严格合规偶尔会多一个换行、缺一个引号、甚至把数字写成字符串。如果直接做 json.loads一个非法 JSON 就会让整个循环崩溃。稳健的工程实践是先做一层容错解析尝试直接解析失败则用正则抽取 key-value再失败就把原始字符串作为 query 传给工具。这个容错层在真实项目里救了我很多次。我后来还加了一个辅助工具 get_document_meta专门用来查文档的标题、日期、版本等元信息。这个工具是受前面那个“安装流程是否更新过”场景启发——相似度检索只看语义不关心版本和时间但如果模型能先调用检索工具定位候选文档再调用元信息工具核对版本日期答案质量会完全不同。3.4 单智能体主循环五个关键实现细节主循环代码我前面已经给出骨架这里补充五个工程细节细节一历史消息的完整回传。每一轮工具调用产生的消息都必须原样追加进 messages 列表回传给模型。这在前面提过但值得再强调一次工具调用请求和工具结果必须配对出现且顺序不能乱。一旦中间少了一条模型就会像是听到半句话的人后面全部乱套。细节二消息角色的区分。用 OpenAI 风格 API 时工具调用请求消息的角色是 assistant且带 tool_calls 字段工具结果消息的角色是 tool且必须带对应的 tool_call_id。不同厂商的 API 虽然字段名略有差异但这个配对逻辑是通用的。细节三System Prompt 的位置。System Prompt 要在每次请求时都保留放在 messages 列表的头部。我最初写的时候只把 System Prompt 放在第一轮请求里后来发现第二、三轮工具调用时模型开始“忘本”了——它把工具结果当成了用户输入行为变得很奇怪。后来每次循环都把完整的 System Prompt 回传才解决。细节四回答来源的控制。我在 System Prompt 里规定当依据工具检索结果进行回答时必须用“根据《文档名》中的描述”这种形式标注来源如果检索结果不足以支撑回答必须明确告知“本地知识库中未找到相关内容”。这个设计不是为了让回答好看而是为了把“模型从文档中读到的”和“模型自己脑补的”强行区分开。实测中这个约束能显著减少模型把内部知识外挂到文档上的幻觉。细节五匿名化的用户输入。用户问题中可能包含文件路径、内部项目代号等噪音信息如果直接作为 query 检索效果往往不好。我的做法是在进入 Agent 循环前先做一层轻量清洗去除路径前缀、统一大小写、提取技术关键词。这个清洗逻辑不用做得很重只要比“裸奔”好就行。4. 实测记录与失败样本我在测试阶段踩过的五个坑架构和代码都跑通之后真正的考验才开始。我准备了一套测试问题集包含事实型问题、对比型问题、否定型问题和闲聊型问题然后逐条观察 Agent 的行为。这个阶段把 Agentic RAG 的很多“隐藏性格”都暴露出来了我挑了五个最有代表性的坑完整还原当时的排查链路和修复方案。4.1 坑一工具抖动手——模型一言不合就检索第一个诡异的现象把 System Prompt 里的工具使用说明写得过于“详细”之后模型对每个问题都倾向于先调用工具。我测试“你好介绍一下你自己”这种问题模型的第一反应不是回答而是先调用 search_documents 搜一下“介绍自己”。这显然很荒谬。排查链路我先打印了模型完整的思考流程发现模型并不是没有判断能力而是被工具描述里罗列的大量场景吓住了——它看到“涉及产品功能、参数配置……请调用工具”这段描述时把“你在做自我介绍”理解成了“涉及系统功能的介绍”于是决定先查一下。也就是说工具描述里的“功能”这个词产生了歧义。修复方案把工具描述里“可能涉及”的判断标准改成更加严格的措辞“当用户问题明确指向本地文档中记录的具体事实时”同时去掉容易引发歧义的泛指词汇。另外在 System Prompt 里加入一条强调“你是一个自主判断的助手不是自动检索机器人不要在没有必要时使用工具。”修复后闲聊和开放型问题基本不再触发工具。4.2 坑二top_k 和阈值失衡导致的检索噪声把不确定性的问题解决了之后我开始专注调检索质量。最典型的问题是 top_k 设得太大一开始设 5导致工具返回结果里混入两条完全不相关的片段。模型拿到这些噪声之后有时会硬着头皮把不相关的内容也写进回答里从“正确但冗余”变成“错误但自信”。排查链路我对比了同样的问题在 top_k3 和 top_k5 下的行为。发现多出来的两条 low-score 片段不仅没有提供信息增量反而把模型带偏了。后来我用一个置信度过滤规则返回的片段里如果某条片段的分数比最高分低 0.15 以上直接剔除。这个规则比单纯调 top_k 更稳健因为它是按相对质量过滤而不是按绝对数量截断。修复方案top_k 默认 3上限 5score 过滤规则加入工具函数的后处理逻辑里低于相对阈值的片段不进入返回结果。修复之后模型在多文档对比场景下依然能拿到足够的两方证据而简单问题场景下结果干净了很多。4.3 坑三模型把“检索到的内容”和“内部知识”混在一起这是 Agentic RAG 最阴险的问题。模型明明检索到一段文档片段但它回答时会把检索内容和自己的预训练知识混在一起输出导致我分不清哪句话是文档依据哪句话是模型脑补。我测试一个关于“某参数最大支持值”的问题时文档里写的是 100模型回答的结论是 100但后面跟了一句“如果配置更高会导致系统进入保护模式”——这句话文档里根本没有纯粹是模型编的。排查链路问题的根源在于 System Prompt 里的来源约束不够硬。我之前写的是“请结合检索内容回答问题”这个措辞给了模型太多自由裁量空间。模型潜意识里认为“结合”意味着“可以把我知道的东西混进去”。修复方案把约束改成强制性的“回答必须以检索到的文档内容为唯一依据。如果检索结果中没有相关内容明确回答‘知识库中未找到’。禁止在回答中补充检索结果之外的技术细节。”这个措辞直接堵住了模型发挥的路子。修复后的测试里模型明显变得更“老实”文档里没写的东西它会直接说不知道。虽然回答看起来不如以前“丰满”但我做的是知识库问答系统不是写营销文案。4.4 坑四上下文窗口被工具调用历史塞爆这个坑出现在连续追问的场景。用户先问问题 A模型调了一次工具用户接着问问题 B模型又调了几次工具几轮下来完整的历史消息里堆了几大段工具返回的 JSON直接把上下文窗口占了很大一块。小语料库场景还好如果哪次工具返回了一个特别大的检索片段第二轮可能就爆了。排查链路打印每轮请求的 message 序列后我发现前一轮的工具返回结果在后一轮里仍然占据完整上下文造成大量浪费。因为对后一轮问题而言前一轮的检索片段已经不再有参考价值。解决方案是引入“上下文压缩”在进入新一轮之前把上一轮的工具请求和工具结果压缩成一条简短的摘要比如“上一轮已检索关于 XX 的内容结果为简要关键词列表”。修复方案我为消息列表加了一层清洗逻辑——只保留最近两轮的完整工具交互更早的工具交互用摘要替代。这个方案在测试中把长对话场景的 token 消耗降低了大概四成同时模型也没有出现因为历史信息丢失而重复检索的问题。4.5 坑五模型一轮检索之后急着回答不会基于结果追问最后一个坑完全出乎我的意料。模型在拿到第一轮检索结果之后偶尔会检查到结果不充分但它依然直接回答而不是再调用一次工具。我测试“对比 A 文档和 B 文档对一个接口的错误码定义是否一致”时模型第一轮只检索到了 A 文档的内容然后就开始长篇大论地分析“根据已有内容推测 B 文档可能也是这么定义的”——这完全违背了 Agentic RAG 的设计初衷。排查链路这个问题和模型能力有关部分模型对工具调用的连续执行支持得不够好在“已经调用过一次工具”之后倾向于认为“任务已经完成了”。这也和 System Prompt 里没有明确赋予模型“追问权”有关。修复方案在 Prompt 中加入一个明确的执行策略“你可以多次调用同一工具来验证信息是否充分。如果你认为第一轮检索结果不足以支撑回答应异进一步检索后再回答而不是根据现有结果推断。”同时把 MAX_STEPS 从 2 调到 3。这个改动之后模型开始学会在对比型任务中连续发起两三轮检索把 A 文档和 B 文档分别查一遍再回答。修复前后的行为对比如下测试问题修复前行为修复后行为你好请自我介绍一下先检索“自我介绍”再回答直接打招呼XX 参数最大支持值是多少检索一次回答里混入内部知识检索一次只回答文档内容未找到时直接说不知道对比 A/B 文档对错误码定义是否一致只检索到 A 文档推测 B 文档检索 A 文档再检索 B 文档逐条对比闲聊“今天天气怎么样”有时触发检索完全不触发检索5. 从原型到工程化的路MCP 协议与 skills 的演进空间原型跑通之后我也调研了它能不能继续往工程化方向走。一个很自然的演进方向是把工具调用封装成标准协议让新工具的接入不再需要改主循环代码。市面上已经有成熟的方案——MCPModel Context Protocol——它可以理解成“模型工具的 USB-C 接口”。在这个协议下工具不再是写在代码里的函数而是注册在 MCP Server 上的资源。主循环只需要负责统一通信协议不需要关心每个工具的具体实现。5.1 为什么说工具调用是 Agentic 能力的地基回看整个原型会发现最核心的东西不是一个漂亮的 RAG 管线而是“让模型自己决定是否使用工具”这件事。工具调用是 Agent 之所以成为 Agent 的地基——没有工具调用模型只能是一个聪明的文本生成器有了工具调用模型才变成“一个会行动的智能体”。本地知识库检索只是其中一个比较直接的例子同样的机制完全可以扩展到执行 SQL 查询、调用内部 API、触发自动化任务等场景。Agentic RAG 只是工具调用在知识管理领域的一个应用切片。5.2 本地知识库的 MCP 化改造思路如果说我这个原型是最原始的“手写工具调用”那 MCP 化就是它的工程化进阶版。具体做法是写一个 MCP Server把检索工具search_documents、get_document_meta 等注册为标准工具暴露给任何支持 MCP 的客户端。客户端的 Agent 框架负责智能体循环业务方只需要提供 MCP Server 配置就能把本地知识库作为一个工具接入任何 Agent 应用。这让我想到一个类比早期计算机接入外设需要自己写驱动后来有了 USB 标准设备即插即用。MCP 要解决的就是 Agent 工具生态的“即插即用”。在我调研阶段MCP 落地方式已经很清晰写一个 Python MCP Server里面定义好 search_documents 等工具的输入输出 schema启动后通过标准输入输出和客户端通信。然后在一个支持 MCP 的 Agent 框架里配置远程服务器地址框架自动获取工具列表、注册到模型侧、维护工具调用循环。这种情况下我这个原型里手动写的 agent loop 就不再需要了框架帮你做完了。也就是说从“手写工具调用 Agent”到“MCP 化 Agent”的迁移成本核心不在代码量而在工具描述的质量——工具描述写得好放到任何协议里都好用。5.3 skills 与工具的关系把多步调用策略固化下来最近社区里活跃讨论的 skills 概念和工具又是另一层关系。工具是一个独立的功能单元比如“检索文档”而 skills 是一个可复用的操作流程把“用户提问 → 分析问题意图 → 确定要查的文档范围 → 多次调用检索工具 → 交叉比对 → 输出结论”这套多步行为固化成 Agent 的技能。如果把工具比作螺丝刀skills 就是一本怎么拧螺丝的工序卡。对本地知识库场景来说我可以把“多文档一致性核对”定义成一个 skill让 Agent 遇到“对比”“差异”“是否一致”这类意图时自动切换到多轮检索再作答的行为模式。而且以我的观察工具调用型的 RAG 做得好不好往往不取决于工具的多少而取决于模型能不能在合适的时机把合适的 skill 用出来。给 Agent 塞几百个工具不如把关键路径上的检索、比对、溯源这几个动作打磨扎实。有限语料库场景尤其如此——工具不用多够用、精准最重要。5.4 有限本地语料库的后续扩展方向如果这个原型要继续演进出更完善的系统我认为有三个方向最值得做。第一个方向是引入多知识源路由把文档库按业务模块分成多个子库提供一个路由工具判断“这个问题该查哪个子库”。这比把所有文档混在一个大库里检索效果好因为分库之后每个库内语料的主题一致性更高检索噪声更小。第二个方向是知识图谱增强对文档中的实体和关键参数做关系抽取构建一个小规模知识图谱在回答涉及多个实体关系的问题时工具可以同时返回向量检索结果和图谱路径信息。第三个方向是把离线评估跑起来组织一批标准问答对对每次修改做回归测试而不是靠手感。因为 Agentic 系统行为随机性强一次改 Prompt 可能在某些问题上变好在另一些问题上变差没有评估集支撑很难判断改动方向对不对。回看这个原型的整个实践过程我最大的体会有两点。一是“有限本地语料库”这个约束条件其实帮我省的力远大于它限制我的空间不需要重型工程设施所有注意力都可以聚焦在模型决策行为上二是工具调用型 Agentic RAG 的价值不在于让模型“看起来更聪明”而在于把以前藏在管线里的隐式决策替换成模型自己做出的显式决策——这从根本上改变了系统出错的方式也让排错变成了一个可以推理的过程。有一天当你发现一个 Agent 系统偏离预期时不再是一头扎进代码里反复试而是可以坐下来顺着工具调用链一条一条回溯模型看到了什么、决策了什么、为什么这个决策是错的——这种可解释性才是 Agent 架构相对传统管线最值钱的东西。
返回列表