1. 为什么要把智能体拆成角色而不是做一个万能大脑做AI智能体项目组里最常听到的一句话就是“能不能让这个Agent自己搞定所有事”。最开始谁都这么想真做两个星期就会发现任务越复杂单个Agent越容易顾此失彼。这个项目的核心思路是用“Sub-Agents角色分工”替代单体智能体硬扛再配合“Skills能力封装”、权限粒度控制和V1知识库同步跑通把整个工作流拆成可管理、可调试、可局部优化的模块。如果你正在做企业内部助手、工单分类系统、文档问答机器人或者准备把自己的工具集挂到智能体上这篇内容会很有参考价值。我全程拿模拟项目X当例子它是某企业内部的一个“工单知识助手”负责回答产品使用、故障排查和运维操作相关问题。1.1 单体智能体的现实瓶颈上下文膨胀、工具串台、提示词失序最朴素的做法是把所有提示词、所有工具、所有知识库塞进同一个Agent。它的优点是架构简单缺点却会随着功能叠加像滚雪球一样变大。先说上下文膨胀。模型能同时看到的Token是有限的你把产品FAQ、运维手册、工单模板、部门流程全部塞进系统提示模型真正回答用户问题时有用信息反而被无关内容稀释了。我在模拟项目X里试过提示词超过8000字之后不仅每次请求的费用明显上涨回答的跑题频率也开始变高经常出现“用户问A模型先解释B”的情况。第二个问题是工具串台。一旦工具数量超过十几个模型选工具的准确率会肉眼可见地下降。我见过一个非常典型的场景用户问“离线安装包怎么下载”本应触发“安装包信息查询”工具模型却调用了“工单状态查询”因为这两个工具的描述里都含有“包”和“查询”字样。这种问题靠改提示词很难根除因为工具越多描述相似度就越高模型的选择空间也越大。第三个问题是提示词失序。单体Agent里往往要塞入多个业务规则比如“先理解意图再查知识库再调用工具最后给出答案”。这些规则以并列形式写进提示词后模型在执行时经常抓不住优先级。更麻烦的是一旦某个环节改需求你必须小心翼翼地在长提示词里找对应的句子改完还会担心影响其他分支。这类问题的本质是职责没有隔离。打个比方让同一个人既当客服、又当运维、还管账一个人再勤快也难免出错如果能分成不同岗位每个岗位只看自己那套流程整体表现反而会更一致、更可控。1.2 角色分工带来的三个直接收益角色分工说白了就是把一个复杂的“万能Agent”拆成多个“专能Sub-Agents”每个只负责一个相对收敛的任务域。做这件事的直接收益有三个。第一上下文聚焦。检索角色只需要加载检索提示词和结果拼装规则不需要知道外部API怎么调用执行角色只需要关注工具入参和返回值不需要理解知识库怎么分块。每个角色看到的上下文小模型回答的准确度自然更好。我们在模拟项目X里把知识库检索独立成一个角色之后单轮检索的准确率大概提升了十来个点原因很简单它不再被“工单状态”“部门人员”这些无关内容干扰了。第二权限能被真实隔离。如果所有能力都在一个Agent里一旦系统提示被诱导输出或者用户要求执行越权操作你几乎无法从机制上挡住。拆成角色以后每个角色挂独立的权限策略要么放行要么拒绝要么转人工审批边界非常清晰。这里说的权限隔离不是心理上的安全而是调度层的强制校验角色A没有声明的资源连请求都不允许发出去。第三可测试性大幅提升。主Agent的行为很难做回归测试因为影响因素太多你没法保证每次结果都可复现。子代理可以单独构造测试集、单独跑指标。比如只测“检索角色”的召回率只测“分类角色”的准确率出了问题定位非常快。这一点在团队协作时尤其重要同事之间可以各认领一个子代理版本合并时冲突也少因为每个角色的配置是独立文件。1.3 角色拆分的前提判断不是所有项目都该拆角色拆分不是免费的这一点必须说清楚。它增加了编排复杂度你要处理子代理之间的调用关系、超时、重试、上下文传递。如果项目只有三五个简单功能硬拆反而增加成本。我的判断标准很直接当单个Agent的提示词超过4000字或者工具数量超过8个或者你已经开始在提示词里写“如果A场景则…如果B场景则…”这种分支时就说明到了该拆角色的时候。反过来如果所有任务一句话就能讲清楚拆角色纯属自己折腾自己。另外还有一条反直觉的提醒角色不是拆得越细越好。我在项目里见过一种极端做法把“读取用户输入、分析情绪、判断意图、生成回答”都拆成独立角色结果一个简单问答需要五六次智能体间通信时延高得没法用。合适的粒度是“一个角色能独立完成一个可交付的中间结果”比如“从知识库召回候选片段”“把候选片段整理成结构化答复”“调用工单系统更新状态”。满足这个标准的角色才有独立存在的意义。2. 角色、权限与技能包三者的边界到底怎么划角色拆分听起来容易落地的难点在于角色、权限、技能包这三个概念经常被混在一起。这里先给我的理解角色代表“你是什么身份”权限代表“你能碰什么”技能包代表“你能干什么”。三者是引用关系不是互相包含的关系。角色配置文件里声明自己拥有哪些技能包和权限但技能包和权限本身是独立维护的这样换一个人设时不需要重写工具调用逻辑。2.1 角色定义职责模板而不是人格设定很多初学者会把角色写成一套人格设定比如“你是一个乐于助人的客服专家回答要热情”这其实对效果提升很有限。真正有用的信息是职责边界和输入输出协议。角色定义文件建议用YAML维护既方便人看也方便程序解析。我们内部的一个角色配置长这样role: kb_retriever description: 负责在V1知识库中检索素材并按照固定模板输出检索结果 permissions: - kb:read:public - kb:read:internal_v1 skills: - kb_search - result_formatter max_iterations: 5 timeout_seconds: 30字段含义不复杂。description告诉模型它存在的意义permissions声明这个角色可以读哪些知识库skills声明它拥有哪些技能包max_iterations和时间限制用于防止子代理陷入无限循环。注意角色文件里不包含工具调用细节工具细节全部收敛到技能包里这样角色和技能包才能分别迭代。我在跑通模拟项目X时就是先把角色定义好再去配技能包顺序反过来会导致角色文件越来越臃肿最后变成另一个单体Agent。角色数量一般控制在三到七个。太多会导致通信链路爆炸太少又回退成单体。常用的组织方式是“三层结构”入口角色负责理解用户意图和分配任务专业角色负责具体执行兜底角色负责处理异常或转人工。这个结构可以覆盖绝大多数企业内部助手场景也算是我实践下来比较省心的一种模板。2.2 权限模型最小权限在智能体里的落地方式权限建模要做到三件事能覆盖资源、能跨角色隔离、能支持动态断点。我推荐把权限拆成“动作 资源”二元组。动作包括read、write、execute、approve资源包括知识库路径、工具ID、外部端点。然后对每个动作定义三种模式allow、deny、require_human。默认情况全部deny再逐个放行。示例权限策略{ version: 1, policies: [ { role: kb_retriever, effect: allow, action: [read], resource: [kb://v1/*] }, { role: ticket_operator, effect: require_human, action: [write], resource: [ticket://external_system/status] } ] }这段策略表达两个意思检索角色可以读V1知识库下所有内容工单操作角色要写外部工单系统的状态必须转人工确认。实际跑下来这个require_human模式救过我很多次。原因很简单LLM执行链一旦长起来它可能凭自己的理解去改状态哪怕用户没有明确要求加一步人工确认成本很低但能防止不可逆操作。权限判断别放在提示词里靠模型自觉要放在调度器拦截层用确定性代码判断。模型可以向调度器提出行动意图但能不能执行必须由代码决定。权限审计也很重要。每个子代理的每次权限命中或拒绝都要打日志至少记录角色、动作、资源、结果、耗时。一旦线上出现“某角色越权”的投诉没有日志你只能靠猜。我建议把审计日志和链路追踪的trace_id绑定这样能从一次用户请求的视角看到权限在哪个子代理那里被拦住排查效率会高很多。2.3 技能包把工具调用变成可复用的肌肉记忆技能包是最容易被低估的概念。它的本质是“工具调用模板 触发条件 参数映射 输出协议 重试策略”。如果只是把工具函数列表扔给模型每个角色都要重新学习怎么用还会用出不同风格。技能包把这些经验固化下来让任何人都能复用。一个知识库检索技能包可以长这样skill_name: kb_search description: 对V1知识库做混合检索返回带来源的片段列表 trigger_conditions: - user_intent_contains: [怎么安装, 报错, 配置, 升级] parameters: query: { type: string, required: true } top_k: { type: integer, default: 5 } score_cutoff: { type: number, default: 0.65 } output_format: - source - content - score retry: max_retries: 2 backoff: exponential test_examples: - query: 离线安装包放到哪 expected_outputs_field: source这里的trigger_conditions不是硬过滤而是帮助调度器判断“这个技能包该不该被激活”。parameters那一层负责校验模型生成的参数防止query为空字符串或top_k为负数。output_format必须严格固定这样下游角色能稳定解析。我在技能包里还加了test_examples给自动化测试使用省去反复手测的时间。技能包相当于“一个动作的肌肉记忆”——第一次教一次之后每次调用都按同一套标准执行。封装技能包时有一个容易忽略的点要把“重试策略”写进去。模型调用工具经常遇到临时故障比如外部服务超时、返回格式异常。如果没有重试一次失败就直接把整个流程拍死如果无脑重试又会拖慢响应。推荐的逻辑是有限次数的指数退避第一次失败等1秒再试第二次等2秒最多三次。这样既提升了容错率又不会无限占用资源。3. V1知识库跑通的完整过程实录标题里最后四个字“跑通”才是重头戏。V1知识库不追求数据量庞大追求的是链路完整文档进库、向量检索、角色召回、结果呈现全线通畅。下面按时间顺序记录做法你可以把它当成一份可直接参考的实操记录。3.1 从零搭建V1知识库的流程先明确数据范围。模拟项目X选了两类文档产品FAQ和运维操作手册大概几百个页面总量不大但内容很杂。第一步不是急着向量化而是清洗。原文档里有大量目录、页眉页脚、重复的表格说明这些东西如果直接丢进知识库检索时会被当成有效答案返回后果很尴尬。用户问“license过期怎么办”模型可能召回一条“关于本文档”之类的杂讯。清洗规则可以总结成几条删除纯目录页内容识别页码和页眉并剔除合并被分页符拆断的句子去除表格中无意义的空单元格。这些规则不复杂但在后续检索质量的提升上非常明显。清洗完成后进入分块环节。分块直接影响召回率这部分我做了两组对比固定长度分块512字符加64字符重叠以及按语义章节分块。前者实现简单但会把一个完整问答拆成两半后者更贴合内容结构但需要维护章节边界。V1阶段我先用了固定长度分块原因很现实可靠性更高换文档源时不用重新标章节。之后在同一个知识库上用语义分块做离线评测发现召回率确实好一点但差距不大所以V1保留了更简单的那套。索引结构要支持混合检索。向量检索负责语义相似关键词检索负责精确匹配。这一点为什么必须做因为“KB-12345”这种工单编号向量检索往往匹配到“KB”开头的另一条记录关键词检索却能精确锁定。V1知识库实际上维护了两个索引查询时并行召回再合并去重。这一步绕不开它是后期回答准确率的重要保障。3.2 把角色、技能包与知识库链路串起来知识库建好后剩下的工作就是把角色、技能包、知识库对接进主流程。链路是这样的用户提问 → 路由层根据意图选择子代理 → 检索子代理调kb_search技能包 → 知识库返回片段 → 摘要子代理整理答案 → 必要时执行子代理调用外部操作并申请人工审批。实际代码里不需要太复杂的调度逻辑用配置驱动路由就能跑起来route_rules: - pattern: 安装|部署|升级|离线包 target_role: kb_retriever fallback_role: ticket_operator - pattern: 报错|异常|无法启动 target_role: kb_retriever focus_skill: kb_search这条配置表达的是当用户问题包含安装类关键词先把请求交给kb_retriever角色如果知识库找不到满意结果再转给工单操作角色考虑是否需要人工介入。这里有一个细节值得强调路由判断用关键词正则只是V1阶段为了方便不要迷信它后面可以换意图分类模型但V1的关键是“能跑通”。正则的好处是透明每一步是谁决定的、为什么这样路由都清清楚楚出问题能马上定位到规则。链路串起来之后记得做一次端到端的真实提问测试。我选的标准问题是“部署包下载后校验失败怎么办”完整流程应该是路由层命中“部署”和“失败”关键词把任务交给kb_retriever检索角色调用kb_search技能包在V1知识库中召回“部署包校验失败的可能原因及对策”片段摘要角色把片段整理成带步骤的答案最后主流程把答案连同来源一起返回给用户。如果这一步跑通紧接着验证反例用户问“今天中午食堂有什么菜”系统应该不会去查运维手册而是返回“该问题超出知识库范围”或转人工。3.3 参数选择与效果评估V1知识库的检索参数是整个链路中最影响回答质量的变量我把它当成调优抓手。常用参数就三个top_k控制返回候选片段数score_cutoff控制保留的最低阈值max_context_len控制最终喂给模型的上下文上限。我们在一个包含50条标准问题的测试集上做了评估数据大致如下参数组合召回命中率回答准确率平均时延top_k3, 无阈值62%55%1.2stop_k5, 阈值0.578%66%1.5stop_k5, 阈值0.6574%71%1.5stop_k8, 阈值0.6576%70%1.9s可以明显看出一个规律top_k不是越大越好超过5以后收益递减时延还上升。score_cutoff提高了回答准确率但也会漏掉一些“表述不相似但意思相关”的片段。V1阶段我选了top_k5、阈值0.65这组理由是它对回答准确率提升最明显同时漏召回还在可接受范围内。评估时要注意分开看“召回命中率”和“回答准确率”前者衡量知识库检索得好不好后者衡量整条链路最终效果两个都提升才算真正跑通。4. 踩坑记录与问题排查速查表跑通之后我以为万事大吉结果联调第一天就连续踩了三个坑。这里如实记录下来希望能帮你省掉几顿加班饭。4.1 三个典型故障角色串味、权限误伤、知识库“看起来没生效”第一个坑是角色串味。测试时我发现检索子代理的回答里居然出现“我可以帮你更新工单状态”这种话它的职责根本不该包含这个。排查后发现原因路由层在拼接子代理提示词时把全局系统提示放在后面后文内容会对模型产生更强烈的行为影响角色设定被覆盖了。解决方法很简单——调整拼接顺序角色定义放最后并且增加强约束“你只能使用本角色挂载的技能包其他工具一律不存在。”第二个坑是权限误伤。开通权限时我按照最小权限原则把所有写操作默认拒绝结果执行子代理在调用一个只读API时也被拦了。原因在于这个API本身需要读取内部配置文件而配置文件被归类成write资源范围。这类问题本质上还是资源分类太粗。解决方法是把资源配置细化到“端点和方法”级别GET请求一律算readPOST/PUT/DELETE算write。权限拦得再严也不能把正常只读操作误伤掉。第三个坑最隐蔽知识库“看起来没生效”。模型回答时完全没引用知识库内容。我查了日志检索技能包明明返回了三条片段但摘要子代理生成答案时只用了模板里的一句话硬把知识库片段截断了。问题出在result_formatter技能包它把检索片段压缩成了30字以内的摘要模型拿到的信息严重缩水。后来我把输出模板改成“保留至少300字原始片段附带来源”回答终于开始引用知识库。这个坑提醒我知识库生效不等于检索生效还要确保输出格式给下游提供足够的信息量。4.2 常见问题排查速查表把项目中出现过的问题整理成一张速查表方便对照排查现象可能原因解决办法子代理回答超出本角色范围全局提示词覆盖角色定义调整提示词拼接顺序角色定义置后只读操作被权限拦截资源分类过粗细化到端点和方法级别知识库有数据但回答不用技能包输出模板截断保留原始片段和来源信息子代理循环调用不退出缺少max_iterations限制角色配置加最大迭代次数多个技能包同时命中参数冲突缺少触发条件优先级给技能包增加priority字段查工单编号匹配到无关记录向量检索无法精确匹配编号增加关键词索引做二次过滤速查表里最后一条我想再展开一下。工单编号这类强标识符知识库最好单独建一个“ID映射索引”把编号、标题、状态做成关系表查询时先在ID映射索引里精确匹配没命中再走向量检索。V1版本一开始没做这张表用户反馈“查编号不准”之后才补上。补完之后这类问题的响应时延没有增加准确率却高了一大截。排查这类问题我习惯遵循一个顺序先看路由日志确认子代理选对了没有再看子代理的输入输出确认技能包有没有被正确调用最后看技能包内部的参数和结果确认是否是知识库召回问题。按这个顺序查绝大多数问题都能在十几分钟内定位。切忌一上来就改提示词、调阈值那样只会让问题更模糊。5. 后续扩展方向标题里写的是V1意味着这条路还有很长的后续。这里我把下一步要做的两件事也列出来当作方向参考。5.1 技能包的版本管理与灰度发布技能包一旦进入稳定期就不能每次改完直接全量上线。我的计划是给每个技能包增加version字段路由时指定可用版本范围类似“kb_search1.x”。改技能包时先在隔离环境里用标准测试集回归通过后再让一小部分流量走新版本对比回答准确率和异常率。这听起来像常见的灰度发布但它对智能体项目尤其重要因为技能包改动会影响所有挂载该技能包的角色。V1阶段我们就吃了没做版本管理的亏——改了一个检索参数另一个角色的回答风格直接变了定位花了半天。如果当初有版本隔离这个改动完全可以回滚而不用连累其他角色。5.2 知识库的增量更新与反馈闭环V1知识库是离线批量导入的文档更新后知识库不会自动同步。下一步要做增量更新监听文档源变化对有改动的文件重新清洗、分块、向量化同时保留旧版本作为回滚。另一个方向是反馈闭环允许用户对答案点“有帮助/没帮助”把“没帮助”的问答对沉淀到标注池定期人工审核后再决定是否补充知识库或调整检索参数。这个闭环价值非常大因为知识库的最终质量不是靠一次性清洗决定的而是靠长期反馈驱动的。这次跑通V1项目后我最大的体会是角色、权限、技能包这些概念单独拿出来都不难理解难的是在真实项目里把依赖关系理顺。V1跑通的意义不在于效果有多惊艳而在于我们有了一个可以重复实验的基线后续任何优化都能在这个基线上对比出真实收益。如果你也在做类似的Sub-Agents项目我建议先把“跑通一个最小闭环”当作第一目标不要一上来就让多个角色、多个知识库全量上线。先把一个角色、一个技能包、一小块知识库打通再慢慢往外扩。这条路我实测走下来是最省心的一条也希望它能让你少走一些弯路。