
1. 单体 Agent 为什么撑不住Sub-Agents 解耦的真实场景1.1 单体 Agent 的三大痛点上下文污染、提示词爆炸、权限粒度失控先摆一个我反复踩过的坑。最早做某企业内部知识问答系统时我图省事只放一个大模型 Agent把文档检索、业务查询、工单生成、权限核验所有能力全塞进同一个系统提示词里。效果怎么形容呢就像让一个全科医生同时干导诊、挂号、开药、写病历的活病人一多必然手忙脚乱。单体 Agent 的失效是有规律的不是随机乱来而是集中在三处。第一是上下文污染。系统提示词里既要交代你是客服助理又要描述调用售后工单接口还得规定回答格式必须是 markdown 表格结果模型在长上下文中经常把指令优先级搞混。最典型的表现是用户问了一句与知识库无关的闲聊它却开始认真调用工具把检索结果硬凑进回答。原因就是提示词内部的注意力被多条相互独立的任务线撕扯模型无法稳定判断当前该激活哪条指令。第二是提示词爆炸。功能每加一个提示词就得往上叠一段很快就到了几千字的量级。表层看是字数问题本质是模型对早期指令的遗忘率上升。我在实测中发现超过 3000 字之后指令靠前位置的内容被遵循的概率明显下降尤其是禁止输出中文必须分三步回答这类约束性指令经常被淹没。这种场景下你改提示词是治标不治本今天摁住一个错误明天又冒出来一个。第三是权限粒度完全失控。单体 Agent 拿到了一个身份那它可能同时拥有查询接口权限、写入权限、文件读取权限。调一次对话你根本不知道模型在哪个环节调了哪个敏感工具想要限制它不碰某些数据只能靠提示词里写不要调用 XX 接口。但提示词是软约束拦不住偶然性操作。这个我后面会讲权限必须靠架构层面硬隔离不能靠语言去劝。所以 Sub-Agents 这套思路本质上不是多开几个模型实例那么简单而是做一次边界重构把一个大而全的 Agent 拆成多个小而专的子代理每个子代理只负责一个职责域只持有完成该职责所需的最少工具和权限再通过一个调度层决定任务路由到谁手里。用生活化的类比单体 Agent 是夫妻店老板什么都干Sub-Agents 团队是开公司前台管接待、财务管报账、技术管开发各人守各人的摊子。沟通成本变高了但整体容错率和可维护性上了一个大台阶。我做这个改造项目的第一周就把系统中一半以上的提示词冲突问题和越权调用问题消掉了。1.2 按什么标准拆分职责边界比功能堆叠更重要拆分子代理之前先要回答一个问题按什么维度切我试过三种切法各有适用场景但有一个共同原则——必须是职责边界清晰可描述的而不是功能堆叠式的硬切。第一种切法是按领域切。比如一个客户服务系统拆成售前咨询子代理售后处理子代理投诉升级子代理。这样每个子代理的知识范围、工具集、语气风格都很聚焦适合业务域天然分明的场景。第二种切法是按流程阶段切。针对一次复杂任务比如调研→方案→执行→复盘拆成调研子代理、方案子代理、执行子代理、复盘子代理。每个阶段的子代理只需要理解上一阶段的输出和本阶段的目标不需要了解全链路。这种切法适合任务流程固化、上下游衔接清晰的系统我那个知识库项目最终选的就是这种。第三种切法是按行为类型切。读、写、审、算各分一类。比如只读检索子代理只挂检索工具没有任何写接口内容更新子代理才有写入和确认权限。这种切法适合对数据安全要求高的场景在后面权限设计部分我会重点展开。我当时给系统定的拆分策略是流程为主、行为为辅主链路按流程切成调研、方案、执行三个子代理每个子代理内部再按行为类型限制工具集。调研子代理只能检索方案子代理能读能生成执行子代理必须经过审批才能写回知识库。一个重要的经验是每个子代理一定要有唯一的职责描述和明确的不做什么列表。比如执行子代理的系统提示词里除了你负责……还要写明你不负责数据格式校验、不负责生成新文档模板这些任务请返回给调度层。原因很简单——子代理不是人它不会主动说这个我不擅长。你不写清楚边界它就会硬着头皮乱做结果比拒绝执行更糟。这个点上我吃亏很多次。最初某调研子代理被问到一个售后赔偿问题它不认识该领域的知识块居然从产品调研材料里硬找出一段相关文字当成答案输出给用户了。问题就出在角色定义里只写了负责调研分析没有写不涉及售后的赔偿规则。加了一句领域边界声明之后类似误答率立刻降了一大截。2. 角色、权限与可控性别让子代理变成脱缰的野马2.1 角色定义文件怎么设计字段拆解与示例子代理拆分思路定了之后落地的第一步就是角色定义。我在项目里把每个子代理的身份配置化存成一个结构化的角色定义文件而不是把身份描述直接硬编码在调用逻辑里。这样做的好处是后续加角色、改提示词、调权限都不需要动代码改配置重启即可运营人员也能参与维护。我常用的角色定义字段如下逐个说下填写要点字段作用填写要点name角色唯一标识用英文小写加短横线比如research-agent方便路由识别display_name角色显示名中文用于日志和前端展示description角色功能简述一句话说清是干什么的调度层靠它做任务路由匹配system_prompt角色行为规则写清目标、边界、输出格式、禁止事项allowed_tools允许调用的工具清单数组形式按工具名精确匹配allowed_files允许访问的数据目录目录级限制细粒度到文件夹approval_required是否需要人工审批布尔值高风险操作场景设为 truemax_iterations最大执行轮数防止子代理陷入死循环一般设 3 到 5其中最关键的是description和system_prompt。description决定调度层能不能正确地把任务分发给这个角色写得太泛比如负责各种分析工作调度层就会频繁把无关任务派过来角色不断触发边界拒绝影响整体效率。system_prompt决定这个角色在执行任务时的行为质量写得太粗模型就容易自由发挥。我贴一个实际用过的调研子代理角色定义片段结构上可以做参考{ name: research-agent, display_name: 调研分析子代理, description: 负责从知识库和外部数据源中检索信息、汇总分析输出结构化调研结论不涉及任何内容写入操作, system_prompt: 你是一个调研分析助手。你的唯一目标是根据用户问题在授权知识库范围内检索相关资料并输出结构化调研结论。\n你必须遵循以下规则\n1. 只回答与授权知识库内容相关的问题对超出范围的问题直接回复『超出我的职责范围请转接其他子代理处理』\n2. 回答必须以 markdown 格式输出包含『核心结论』『依据来源』『关联问题』三个小节\n3. 严禁杜撰数据所有结论必须引用检索到的原始内容\n4. 如果检索结果不足以支撑结论必须明确说明信息不足而不是猜测, allowed_tools: [kb_search, web_search, doc_preview], allowed_files: [/kb/tech_docs, /kb/product_docs], approval_required: false, max_iterations: 4 }这里有个容易忽略的细节system_prompt里我明确写了一条注意力约束——只回答授权知识库范围内的问题。很多人写提示词只写正向目标比如你负责调研分析不写负向边界。模型在遇到超范围问题时往往会为了完成用户请求而无意识地扩大检索面甚至直接依据自身预训练知识回答。你把拒绝路径写清楚等于提前给了模型一条安全的逃生通道它不必硬着头皮编答案系统的整体可信度会明显提升。2.2 权限最小化的三条硬规则角色定义里权限是最不能凑合的部分。我总结了三条硬规则项目里每条都是踩过坑之后才定的。规则一工具级授权不要角色级授权。不要让整个角色拥有某类工具的全部能力而是精确到单个工具。比如同样是文档处理工具文档预览和文档编辑是两个不同的工具调研子代理只挂预览权限不挂编辑权限。这样即使模型被诱导去修改文档工具层直接返回权限不足物理上做不到越权。规则二目录级数据隔离。知识库里的数据一定分目录管理比如产品文档目录、技术文档目录、售后工单目录。每个子代理的allowed_files只挂自己职责相关的目录。这一点非常重要——数据隔离不只是安全需求也是质量保障。如果检索范围过宽子代理可能检索到跨域内容导致回答时混入不相关信息专业感尽失。规则三高风险操作强制审批。凡是涉及写入、删除、修改的操作不管子代理多智能都要在流程上设置一道人审。我在执行子代理上把approval_required设为 true当它要更新知识库某条记录时会先生成一条待审批操作指令由管理人员确认后才真正落地。这么做虽然多了一步人工操作但换来的是内容质量的兜底和对异常行为的拦截。权限这部分我建议在设计初期就做不要等系统跑起来再补。因为权限做晚了你要处理的是已经被污染的数据、已经被越权操作的痕迹返工成本极高。我那个知识库系统早期就是绕过了权限设计直接让主代理挂全量工具后来发现它在一个对话里连续调用了搜索、文件预览和写入接口而且写入的内容根本没人看过。从那以后权限规则一条不敢少。2.3 角色的拒绝路线设计不做什么比做什么更重要前面提到了领域边界声明这里单独展开讲。我在实际观察中发现Sub-Agents 系统里最容易出的问题不是该做的没做而是不该做的硬做。大模型有很强的讨好倾向用户问什么它都倾向于回答哪怕信息不在授权范围内它也会尝试从现有材料里找相似内容凑一个答案。这在单体 Agent 里可能只是一次错误回答在子代理体系里却会引发连锁反应——下游子代理拿到错误输入继续加工最后输出的方案偏差越来越大。所以我在每个子代理的角色定义里都加了一个拒绝路线段落内容包括明确不处理的任务类型列表比如不处理赔偿规则问题不处理合同审核问题给出统一的拒绝话术模板该问题超出我的职责范围请转接 XX 子代理处理规定拒绝后必须触发一个route_request工具调用把任务重新交回调度层这个设计等于给每个子代理装了刹车。模型不是万能的它需要知道自己的边界在哪里也需要知道当自己处理不了时该怎么优雅退出而不是硬撑。我还给调度层加了一条规则如果一个子代理连续三次返回拒绝就把用户请求标记为未匹配到合适处理角色转人工处理。这是防止子代理互相踢皮球的兜底机制。有个阶段我遇到过一次离奇现象——用户问的是产品价格结果被调度层分给了调研子代理调研子代理拒绝后又被调度层分回给它自己来回试了几次才超时。后来我检查调度逻辑发现是根据 keywords 标签做路由而价格这个词在调研子代理的标签里也有就误配了。修复方式是给每个角色增加负向标签比如调研子代理的负向标签明确排除价格、售后、合同命中负向标签的任务直接跳过该角色。这个小改动让路由准确率提高了不少。3. Skills 能力封装让子代理从会说话变成能干活3.1 技能包的结构菜谱式的可下发能力角色定义解决了子代理是什么身份、能碰哪些资源的问题但还没解决它能执行什么具体操作流程的问题。这时候就需要 Skills 能力封装上场。我理解的 Skills本质是一种菜谱式的结构化指令集。就像你给一个厨师的不仅是一句做一道红烧肉而是把这道菜的食材清单、步骤、火候、关键技巧都写成菜谱厨师照着做就能稳定出品。技能包也是这样定义好每个操作流程的触发条件、执行步骤、输入输出要求和版本子代理在运行时按需加载、按步骤执行。一个技能包的标准结构我这样定义{ skill_name: doc_structure_tagger, skill_version: 1.2.0, description: 对上传的文档进行结构化拆解识别章节、段落和小节生成标记大纲, when_to_use: 当用户要求解析文档、生成目录、建立知识入口时调用, dependencies: [doc_parser, kb_metadata_writer], steps: [ 读取文档全文识别一级标题、二级标题、段落边界, 为每个章节生成编号和摘要不超过50字, 将结构化结果存入知识库元数据字段, 输出大纲 markdown 内容供用户预览 ], expected_output: 包含章节编号、标题、摘要、原文位置的 markdown 大纲, permission_required: [kb_metadata_writer] }这里最关键的字段是description和when_to_use。它们决定了子代理在什么时机主动想起使用这个技能包。我实测下来description写得越具体、越贴近触发场景技能包被调用的准确率越高。比如当用户要求解析文档、生成目录、建立知识入口时调用这个描述就比文档处理技能好得多前者让模型在遇到相关任务时能建立明确映射。3.2 技能怎么拆按工序、数据形态、复用频率三维度技能包的拆分逻辑我用了三个维度来评估。按工序拆分是把一个复杂工作流程拆成若干不可再分的动作单元。比如建立知识库新文档这个任务拆成文档解析文本清洗段落切分向量入库四个技能步骤排好调用顺序组织成一个流程型技能。这样做的好处是每个步骤都可以单独测试、单独优化某一步出问题时不至于整个流程重来。按数据形态拆分是针对不同的输入数据类型封装不同技能。知识库系统里最常见的是两种数据长文本文档和短问答数据。长文本文档需要走切分、摘要、段落标注的流程短问答数据走的是清洗、去重、入库的流程。两种数据形态的技术细节差异很大放在一个技能里会让步骤互相干扰拆开反而清爽。按复用频率拆分是把高频操作抽成独立技能包低频复杂操作放到流程型技能里。比如关键词检索相似度召回属于高频操作做成轻量级技能后多个子代理可以复用而月度知识库质量报告生成属于低频操作做成一个包含多步骤的流程型技能只在特定任务被触发时才调用。你可以想象一下假如把高频和低频技能硬塞在一起每次子代理干活都要加载一个大而全的技能集合指令空间的噪声就会变大模型反而容易在步骤选择上出错。拆分的核心原则仍然是最小化加载按需组合。3.3 技能包与角色怎么绑定权责匹配的平衡点技能包拆好之后要和角色做绑定。绑定逻辑很简单一个角色能调用哪些技能包决定了它的能力边界技能包能调用哪些工具决定了它的行为边界。两者叠加才是真正的权责匹配。我在配置里给角色增加了一个skills字段挂载技能包白名单。比如调研子代理挂kb_search_skill、doc_summarize_skill不挂任何写入类技能包执行子代理挂doc_structure_tagger、kb_content_update_skill但每个写入类技能包的permission_required都标注了需审批。这里要强调一点技能包本身也应该声明它需要的权限而不是默认继承角色的全部权限。我用的是一个独立权限声明字段执行时由权限中心统一校验。表面上是多写了一段配置实际是把谁在什么条件下能用什么能力全程留痕后续出问题时查日志能精确追溯到某次技能调用。技能包还有版本管理的问题。我在项目里为每个技能包维护了版本号更新技能逻辑时不改技能名而是新增版本子代理在服务注册中心获取到的是最新可用版本。这样做有一个明显好处当某个技能新版本表现不佳时可以快速回退到旧版本不影响线上系统。有一次我给文档解析技能升级引入了新的段落识别算法结果对表格类型文档的解析准确率反而下降了就是因为版本管理做得好几分钟内就回退了。关于技能包的开发我还有一个建议先做最小可用版本再滚动迭代。不要一开始就追求技能包能覆盖所有边界情况。技能包的价值在于能稳定复用而不在于能应对一切。先让它服务好 80% 的场景剩下的 20% 在日志反馈中逐步补齐。我第一版技能包只有四类检索、摘要、切分、入库。上线后根据检索不到内容的案例逐步加了同义词扩展关键实体提取两个技能系统才真正跑顺。4. V1 知识库跑通全流程实录从零到可用的必经之路4.1 最小闭环文本接入到向量检索的五步链路角色、权限、技能包这些神经系统搭好之后知识库本身才是系统的血液。所谓 V1 知识库我不追求一上来就做多复杂的功能而是先把从原始文档到可检索向量的全链路跑通保证能进、能存、能查、能答四件事同时成立。我的 V1 知识库最小闭环分五步第一步文本采集与清洗。把原始文档从各个来源汇聚到一个统一的原始文件目录。采集阶段重点做编码标准化和格式归一化比如把 PDF 抽取出的文本里的乱码字符清理掉把表格转成 markdown 格式。这一阶段不做深度处理只保证数据干净可读。第二步段落切分。这是知识库质量的第一道关键工序。切分太粗每段包含多个主题检索时召回的内容不聚焦切分太细单个段落语义不完整模型无法理解上下文。我实测下来按标题层级 段落边界混合切分是 V1 阶段性价比最高的方案。先按一级标题切分再在每个章节里按自然段落拆分段落超过一定长度时按固定窗口切。常用的窗口大小是 300 到 500 字叠加上下文窗口 50 到 100 字避免语义断裂。第三步向量化。把切好的文本块通过嵌入模型转换成向量向量。V1 阶段选嵌入模型的核心指标不是精度最高而是稳定开源可部署、维度适中、中文效果可接受。我选了一款 768 维的中文嵌入模型在当时的测试集上检索命中率足够满足内部使用且单机就能跑不需要复杂的推理服务。这里提醒一句不要盲目追求高维模型向量维度高意味着存储和检索成本成倍上升V1 阶段够用就好。第四步向量索引构建。我用的是常见的近似最近邻索引类型核心参数是 metric 类型和列表数。metric 用的余弦相似度适配语义检索场景列表数按数据量设的10 万条以内设置为数据量的十分之一左右检索性能和召回率相对均衡。索引构建完成后要自测一遍随机抽 20 条知识库内容拿对应的常见问题去检索看能不能在 Top 5 内召回正确段落。第五步检索应答接入。把向量召回的段落结果拼接成上下文传给子代理模型生成回答。这里有个细节传入模型的不是所有召回的原始段落而是经过相关性粗排后的 Top 3 到 Top 5 段。上下文风险随输入长度上升得很快控制好传入量是稳定回答质量的关键。4.2 检索参数调优chunk、overlap、top_k、score 的实测经验V1 知识库跑通不是终点检索质量直接决定了问答效果。我把自己在调参过程中积累的几个硬经验写下来完全可以作为参考起点。关于chunk_size段落大小。300 到 500 字这个区间是我反复测试后锁定的。段落太短少于 100 字经常出现单个检索结果缺少关键背景子代理基于片段生成的回答容易断章取义段落太长超过 800 字一个检索结果里包含多个主题回答时不知道该引用哪个部分输出内容显得散。建议测试时以 100 字为步长做一组对照实验用同一组测试问题看回答完整度。关于overlap重叠窗口。它解决的是段落边界处语义断裂的问题。设 50 到 100 字比较合适。重叠过大会导致相邻段落高度相似检索召回时出现大量重复内容白白占满上下文重叠过小则边界处的语义信息丢失长文档中跨段描述的关键概念容易被切断。我测试的一篇技术文档中某个概念在段落末尾被引入、下段开头展开描述如果没有 overlap检索只能召回后半段回答明显缺少概念定义。关于top_k召回数量。V1 阶段我建议 Top 3 到 Top 5不要贪多。尤其是把检索结果直接喂给模型时召回段落太多会引入无关信息模型的注意力被分散回答质量反而下降。把 Top 1 的准确率和 Top 5 的覆盖率分开展示你会发现 Top 5 覆盖率明显高但真正对回答有价值的信息往往集中在 Top 2 到 Top 3。所以调 top_k 的核心目标不完全是召回的都对而是排在前面的足够对。关于 score 阈值。这是防止低相关段落混入回答的最后一道闸门。我建议记录一下正常回答的相似度分数分布然后以比最低分再低 0.05 的值作为拒绝阈值。比如我系统里正常的有效召回分数基本落在 0.62 到 0.88 区间我就把阈值设为 0.60低于这个分数宁可告诉用户知识库中未找到相关内容也不要硬答。这是一个很实用的兜底策略大幅减少了胡编乱造的情况。4.3 检索失效排查实录一次查不到问题的定位过程写这部分是为了提醒你知识库系统的很多故障不是模型能力问题而是链路中某个环节悄悄坏了。我分享一次实测中的排查经历整个思路可以直接复用。现象是某个子代理在回答一个常见问题时连续多次返回未找到相关内容。但知识库里明明有这条内容而且之前测试是能检索到的。我的排查第一步先确认向量检索环节是否正常。直接调用检索接口用问题文本做一次检索看返回结果。结果发现 Top 5 里根本没有目标段落。问题定位到了检索环节。第二步检查目标段落是否在向量索引中。我查询了知识库的元数据发现目标文档在索引构建时被标记为解析失败整个段落块都没有进入向量库。问题根源是新的 PDF 文档格式不被当时的文档解析器支持解析失败后也没有触发告警知识就这样静默丢失了。修复方案有两个层面短期是重跑一遍文档解析流程用备用解析器把这个 PDF 转换成功再重新切分、向量化、入库长期是给知识库的采集链路加一个解析成功率监控任何文档解析失败都会生成一条告警而不是默默跳过。这个案例让我养成了一个习惯检索不到结果的时候永远先怀疑数据链路而不是立刻调模型提示词或参数。因为数据链路的问题往往不易察觉而提示词调整只是让模型换个姿势回答根本解决不了索引里没有的数据。5. 常见问题速查表与避坑技巧我把自己和周围团队在 Sub-Agents 与知识库项目中遇到的典型问题整理成一张速查表方便你直接对照使用问题现象可能原因处理策略子代理总是把超出范围的任务硬做完角色定义中缺少负向边界声明在 system_prompt 中补充不做什么清单并给出拒绝话术模板多个子代理互相踢皮球任务无法结束调度层路由规则冲突角色标签重叠为角色增加负向标签路由时先排除负向标签匹配的候选角色检索能召回但模型回答还是胡编score 阈值未设置低相关段落混入上下文设置相似度拒绝阈值低于阈值时明确回答未找到相关内容新增文档后检索不到新内容文档解析失败或向量化流程未触发检查采集链路日志确认解析、切分、向量化、入库每步都执行成功角色调用技能包的频率很低技能的 description 写得太宽泛重写技能描述补充具体触发场景和用户意图关键词知识更新后旧内容仍然被大量召回向量索引未重建或旧向量未失效建立索引版本管理数据更新后触发增量索引构建避坑方面最想强调三件事。第一子代理数量不是越多越好。我见过有人把系统拆到十几个子代理结果调度层自己先懵了一个简单请求被路由遍历好几个子代理才找到合适的。V1 阶段我建议不超过五个子代理流程链路清晰即可。随着任务类型被真实流量验证后再逐步拆分。第二技能包和角色定义要分开维护。有人把技能包内容直接写进角色定义的 system_prompt 里看起来省事实则让角色定义越来越臃肿而且技能要复用给其他角色时只能复制粘贴改一处要同步好几处。分开维护后技能包像插件一样角色按需加载更新只需改技能包本身所有挂载它的角色自动生效。第三日志一定要结构化。Sub-Agents 系统的排查难度远高于单体 Agent因为一次用户请求可能跨多个子代理、多次技能调用、多次工具调用日志不结构化的话根本追不出来是哪个环节出了问题。我在项目里的日志格式固定为请求 ID、角色 ID、技能 ID、工具名、输入摘要、输出摘要、耗时、状态码。每个环节写一条结构化日志排查时按请求 ID 串联整条链路十分钟内基本能定位问题。这个习惯救过我很多次。写在最后一套跑通后的真实体感这套系统从设计到 V1 跑通前后迭代了大几周。最深的体会是Sub-Agents、角色权限、技能包这些东西如果只是停留在概念讨论层面永远感受不到它们解决的是什么问题。真正把知识库接入、让子代理在真实请求下跑起来之后你才会看到原来单体 Agent 时代那些时好时坏的表现其实是系统性缺陷的必然结果。现在这套系统里调研、方案、执行三个子代理各司其职技能包按需加载权限硬隔离知识库检索在绝大多数常见问题上能稳定命中。偶尔还会出现一些边界情况比如用户用方言提问导致检索不到、文档解析遇到奇葩格式等但这些已经不会让我慌张了——结构化的日志、版本化的技能包、可回退的索引让人有足够的工具去定位和修复。做这类项目我的建议一直是先跑通再跑好。V1 知识库不需要完美的切分策略、不需要十个技能包、不需要复杂的权限矩阵只需要一条完整、可追踪、可回退的链路。当你把这条链路握在手里后面每一处优化都是在已有的稳定地基上填砖加瓦如果一开始就追求大而全反而容易在一个不完整的地基上反复返工。