目录一、角色设计误区把 Teammate 当工具调用二、五层边界机制详解2.1 身份层Persona 与 SOUL.md2.2 技能层SKILL.md 与技能分配2.3 工具层TOOL_WHITELIST 能力卡过滤2.4 行为层Rail 钩子机制2.5 权限层owner_scopes 细粒度控制三、三角色 YAML 配置四、角色边界设计模板4.1 设计原则4.2 通用角色边界设计模板4.3 从模板到 JiuwenSwarm 配置的映射五、实操一次客服工作流的完整体验5.1 场景背景5.2 第二步追加需求观察越界响应5.3 第三步跨角色协作验证职责不重叠5.4 效果总结六、写在最后参考资料V2 团队上线后三个角色的表现让人头疼查询员跑去处理退款工单处理员抢着回答用户问题投诉专员在订单查询上花了大量时间。角色的能力明明在配置里写清楚了为什么还是抢活干或互相推诿根本原因是一个普遍的设计误区把 Teammate 当工具调用没有明确职责边界。每个成员继承了主 Agent 的全部能力本质上就是三个全能选手在抢同一个任务。LLM 的帮助性倾向让它主动完成看起来相关的任务而不管这是否在自己的职责范围内。JiuwenSwarm 基于 openJiuwen 框架构建了一套五层角色边界体系从身份到权限层层收窄确保每个 Teammate 只做自己该做的事。本文深入解析这套机制的设计与实现给出可复用的角色边界设计模板、能力隔离配置方案以及查询员、工单处理员、投诉专员三个角色的完整 YAML 配置。运行环境项目配置值操作系统Windows 10 / macOS / LinuxPython3.11 / 3.12 / 3.13模型服务华为云 MaaS / OpenAI 兼容接口 / ModelScope 等通信渠道Web / 飞书 / 钉钉 / 企业微信 / 小艺 / Telegram / Discord / WhatsApp / 个人微信Agent 框架openJiuwen内置一、角色设计误区把 Teammate 当工具调用在讨论具体方案之前先看清楚问题出在哪里。最常见的误区是认为给每个 Teammate 起个名字、写段描述就算角色定义了。配置大概长这样team: predefined_members: - member_name: query_agent display_name: 查询员 persona: 擅长查询订单和物流 - member_name: ticket_handler display_name: 工单处理员 persona: 擅长处理工单 - member_name: complaint_specialist display_name: 投诉专员 persona: 擅长处理投诉看起来没什么问题实际上这只做到了告诉 LLM 它是谁但没有做到限制 LLM 只能做什么。三个成员都继承了主 Agent 的全部技能——搜索、查询、创建工单、修改订单、发送通知——它们在能力上完全等价只是名字不同。这就好比给三个员工发了同样的万能钥匙然后说你是查件的你是处理工单的。员工能不能守规矩全靠自觉而 LLM 恰恰不太擅长自觉。结果就是抢活干查询员发现用户在问退款进度顺手就处理了退款——因为它有这个能力互相推诿工单处理员收到一条模糊的用户消息觉得这应该是查询员的事就不处理了越权操作投诉专员在调查投诉时直接修改了订单状态——因为它继承了修改订单的工具解决思路很明确不能只靠 persona 的自觉还要在工具、技能、行为、权限层面做硬隔离。这就是下面五层边界体系要做的事。二、五层边界机制详解核心机制总览层级机制控制什么源码/文件身份层Persona SOUL.md角色定位、行为准则config.yaml / SOUL.md技能层SKILL.md skills 配置可用技能范围各技能目录 / config.yaml agents 段工具层TOOL_WHITELIST可继承的工具能力卡team/team_runtime_inheritance.py行为层RAIL_WHITELIST Rail 钩子运行时行为约束team/team_runtime_inheritance.py权限层owner_scopes按渠道和用户的细粒度权限config.yaml permissions 段2.1 身份层Persona 与 SOUL.md身份是边界的第一道防线。如果 Teammate 不知道自己是谁、该做什么再精细的工具限制也只是治标。Leader的身份定义在config.yaml中配置team: leader: member_name: team_leader display_name: 调度主管 persona: 你是客户服务团队的调度主管负责接收用户请求、判断问题类型、分配给合适的团队成员。你不直接处理任何用户请求。persona字段直接写入 Leader 的系统提示词。ConfigLoader加载配置时通过_build_leader_spec()方法组装成TeamAgentSpec兼容的字典# config_loader.py _build_leader_spec() def _build_leader_spec(team_raw: dict[str, Any]) - dict[str, Any]: leader_raw team_raw.get(leader, {}) leader_name ( str(leader_raw.get(name, )).strip() or str(leader_raw.get(display_name, )).strip() or TeamLeader ) return { member_name: leader_raw.get(member_name, team_leader), display_name: leader_raw.get(display_name, Team Leader), name: leader_name, persona: leader_raw.get(persona, 天才项目管理专家), }注意persona的默认值天才项目管理专家。即使不配置Leader 也会有一个偏向管理的初始身份而不是全能执行者。Teammate 的身份定义通过predefined_members配置后面三角色 YAML 部分会给出完整示例。SOUL.md是更底层的身份文件定义了全局行为准则。框架提供的中文模板SOUL_ZH.md核心内容# 灵魂 ## 核心原则 - 真正有帮助而不是表演有帮助。省掉废话直接帮。 - 有自己的观点。可以不同意、有偏好。 - 先想办法再问。读文件、看上下文、搜索一下搞不定再问。 - 用能力赢得信任。 - 记住你是个访客。尊重信任。Teammate 创建时继承主 Agent 的SOUL.md全局准则对所有成员生效。两层叠加全局行为准则提供兜底角色 persona 提供定向约束。2.2 技能层SKILL.md 与技能分配身份定义了你是谁技能定义了你会什么。JiuwenSwarm 中每个技能都有一个SKILL.md文件其中allowed_tools字段是最直接的技能边界# advanced-daily-report/SKILL.md --- name: advanced-daily-report allowed_tools: [read_memory, write_memory, bash, read_file, write_file] ---allowed_tools是白名单机制——技能只能使用列表中声明的工具。日报技能有bash执行脚本和文件读写但没有send_message所以它无法直接发送消息到飞书。在团队配置中通过agents.role.skills控制每个角色能访问哪些技能team: agents: leader: skills: [openJiuwen-DeepSearch] teammate: skills: [] # 空 最小继承技能继承的完整流程分两步。第一步是全局技能同步到团队共享目录这一步在团队创建时只执行一次# team_manager.py _copy_global_skills_to_team_shared_dir() staticmethod def _copy_global_skills_to_team_shared_dir(spec: TeamAgentSpec) - None: global_skills_dir get_agent_skills_dir() ws_path str(team_home(spec.team_name) / team-workspace) team_shared_skills_dir Path(ws_path) / skills copied_marker team_shared_skills_dir / .team_skills_copied if copied_marker.exists(): return # 已经同步过跳过 shutil.copytree(global_skills_dir, team_shared_skills_dir, dirs_exist_okTrue) copied_marker.write_text()第二步是成员加入时只复制自己需要的技能到个人目录# team_manager.py copy_member_configured_skills() def copy_member_configured_skills( member_skills_dir: Path, selected_skills: list[str], ) - None: selected_skill_set set(selected_skills) member_skills_dir.mkdir(parentsTrue, exist_okTrue) for skill_dir in global_skills_dir.iterdir(): if not (skill_dir / SKILL.md).is_file(): continue if skill_dir.name not in selected_skill_set: continue # 不在白名单中的技能跳过 dest member_skills_dir / skill_dir.name if not dest.exists(): shutil.copytree(skill_dir, dest)没在名单中的技能成员连 SKILL.md 都看不到。如果成员没有指定具体要哪些技能就继承全部。2.3 工具层TOOL_WHITELIST 能力卡过滤技能层控制技能文件的继承但 Agent 还有一类能力——工具能力卡。这些是注册在主 Agent 身上的工具搜索、通讯、任务等通过能力卡机制注册。team_runtime_inheritance.py定义了工具能力卡的白名单TOOL_WHITELIST frozenset({ # 搜索与网页 free_search, fetch_webpage, paid_search, search_skill, # 多模态 vision, audio, image_ocr, visual_question_answering, generate_image, audio_transcription, audio_question_answering, audio_metadata, video_understanding, image_reading, # 技能管理 install_skill, uninstall_skill, # 任务与待办 task_tool, user_todos, # 个人信息 get_user_location, create_note, search_notes, modify_note, create_calendar_event, search_calendar_event, # 通讯录与媒体 search_contact, search_photo_gallery, upload_photo, search_file, upload_file, # 通讯 call_phone, send_message, search_message, # 闹钟 create_alarm, search_alarms, modify_alarm, delete_alarm, # 平台专属 xiaoyi_collection, xiaoyi_gui_agent, })白名单共 40 个工具。只有在这个白名单中的工具能力卡才会被继承给新成员。白名单本身覆盖面较宽工具层是第一道防线更精细的隔离需要依赖技能层的allowed_tools和权限层的owner_scopes来收窄。继承逻辑# team_runtime_inheritance.py def filter_inheritable_ability_cards(main_agent: Any) - list[ToolCard]: result [] for ability in main_agent.ability_manager.list(): if isinstance(ability, ToolCard): if ability.name in TOOL_WHITELIST: result.append(ability) return result这个设计的关键在于白名单过滤——新增的自定义工具如果不在白名单中就不会自动流向 Teammate。新增能力需要显式授权而不是默认放行。2.4 行为层Rail 钩子机制前三层控制的是静态能力Rail 机制控制的是动态行为——运行时做什么、怎么做。RAIL_WHITELIST frozenset({ RuntimePromptRail, # 运行时上下文注入 ResponsePromptRail, # 响应前处理 JiuClawStreamEventRail, # 流式事件处理 TaskPlanningRail, # 任务规划 SecurityRail, # 安全检查 HeartbeatRail, # 心跳检测 AvatarPromptRail, # 头像提示 FileSystemRail, # 文件系统操作 TeamSkillEvolutionRail, # 团队技能演进管理Leader 专用 TeamSkillCreateRail, # 团队技能创建Leader 专用 SkillEvolutionRail, # 技能自演进Teammate 专用 TeamWorkspaceReportPathRail, # 团队工作空间报告路径 })白名单共 12 个 Rail。关键设计Leader和 Teammate 加载不同的 Rail# team_runtime_inheritance.py build_member_rails() 片段 # Leader 专用团队级别的技能演进管理 if role leader and team_ws_skills_dir: team_skill_rail TeamSkillEvolutionRail(...) rails_list.append(team_skill_rail) # Leader 专用团队级别的技能创建需配置启用 if role leader and team_ws_skills_dir and get_skill_create_enabled(config): create_rail TeamSkillCreateRail(...) rails_list.append(create_rail) # 非 Leader 专用成员级别的技能自演进 if role ! leader and team_ws_skills_dir: evo_rail build_skill_evolution_rail(...) if evo_rail is not None: rails_list.append(evo_rail)各 Rail 的具体职责和加载策略Rail作用加载给谁RuntimePromptRailAgent 启动时注入运行时上下文所有角色ResponsePromptRail响应生成前的 Prompt 后处理所有角色SecurityRail内容安全检查所有角色HeartbeatRail心跳检测维持成员存活所有角色FileSystemRail文件读写操作所有角色TaskPlanningRail任务规划与分解所有角色TeamWorkspaceReportPathRail团队工作空间报告路径所有角色TeamSkillEvolutionRail团队技能演进管理与分发仅 LeaderTeamSkillCreateRail团队技能创建仅 LeaderSkillEvolutionRail技能自演进信号检测与优化仅 Teammate2.5 权限层owner_scopes 细粒度控制前面四层控制的是 Agent 内部的边界owner_scopes控制的是 Agent 外部可触达的范围permissions: owner_scopes: feishu: ou_xxxx: defaults: *: allow tools: bash: *: deny patterns: git status *: allow git log *: allow write: *: deny deny_guidance_message: 该工具未被授权在数字分身模式下使用。权限有三个级别级别含义行为allow允许直接执行不需要确认deny拒绝不执行返回拒绝消息ask询问暂停执行等待用户确认五层边界从内到外形成完整的隔离链——身份限定意图、技能限定能力、工具限定手段、Rail 限定行为、权限限定触达。三、三角色 YAML 配置下面是查询员、工单处理员、投诉专员的完整配置可以直接复制到~/.jiuwenswarm/config/config.yaml的team配置段中使用。team: team_name: customer_service_team lifecycle: persistent teammate_mode: build_mode spawn_mode: inprocess # ── Leader调度主管 ── leader: member_name: dispatcher display_name: 调度主管 persona: 你是客户服务团队的调度主管。 你的职责是接收用户请求判断问题类型分配给合适的团队成员。 你不直接处理任何用户请求。 查询类问题分配给查询员工单相关分配给工单处理员投诉纠纷分配给投诉专员。 遇到无法判断的问题先向用户确认再分配。 # ── 角色分配 ── agents: leader: skills: [openJiuwen-DeepSearch] teammate: skills: [] # ── 三个预定义成员 ── predefined_members: # 角色一查询员 - member_name: query_agent display_name: 查询员 persona: 你是查询员专门负责信息查询。 你的能力范围查询订单状态、查询物流信息、查询账户余额。 你只能访问订单数据库只读和物流数据库只读。 你不负责创建工单、修改订单、发送通知、处理投诉。 收到非查询类的请求时向调度主管反馈不要自行处理。 # 角色二工单处理员 - member_name: ticket_handler display_name: 工单处理员 persona: 你是工单处理员专门负责工单的创建、更新和处理。 你的能力范围创建工单、更新工单状态、查询工单进度、关闭工单。 你可以访问工单数据库读写和订单数据库只读。 你不负责处理投诉、修改订单金额、发送营销通知。 收到非工单类的请求时向调度主管反馈。 # 角色三投诉专员 - member_name: complaint_specialist display_name: 投诉专员 persona: 你是投诉专员专门处理客户投诉和纠纷。 你的能力范围受理投诉、调查投诉原因、制定解决方案、跟进处理结果。 你可以访问客户档案读写、工单数据库读写、订单数据库只读。 你不负责日常查询、工单创建、修改订单金额。 处理投诉时需要理解客户情绪给出专业且有人情味的回复。这个配置的关键设计决策决策理由Leader 只做调度不处理请求避免 Leader 抢 Teammate 的活每个 Teammate 明确写出不负责什么双向锚点防止 LLM 越界三个角色职责互斥查询只读、工单读写、投诉全权不重叠技能继承策略对比策略配置方式适用场景安全等级全量继承teammate: {}快速验证、原型开发低选择性继承teammate: { skills: [skill-a] }生产环境、固定流程中最小继承teammate: { skills: [] } predefined_members高安全要求、敏感操作高客户服务场景建议使用最小继承——每个角色只拥有完成本职工作所需的最小能力集。四、角色边界设计模板4.1 设计原则原则一最小权限每个角色只配置完成任务所必需的最小能力集。不要因为可能用得上就给所有角色所有能力。查询员不需要创建工单的能力投诉专员不需要日常查询的能力。原则二职责互斥两个角色的能力范围不应完全重叠。查询员只读、工单处理员读写工单、投诉专员处理纠纷——互斥的职责边界让 LLM 在角色选择时不会犹豫。原则三persona 要写不做什么只写你擅长什么是不够的。在 persona 中明确写出你不负责 XXX效果比单纯限制工具更好——这是从源头改变行为意图。原则四Leader不执行Leader 的核心价值是调度和监控不是执行。如果 Leader 也参与具体工作它就无法专注于全局协调。原则五越界时反馈而非沉默当 Teammate 收到超出职责范围的指令时应该向 Leader 反馈而不是自行忽略。忽略会导致任务丢失反馈则让 Leader 有机会重新分配。4.2 通用角色边界设计模板以下是一个可直接套用的结构化模板适用于任何多角色团队场景# ═══════════════════════════════════════ # 角色边界设计模板可直接套用 # ═══════════════════════════════════════ role: {{角色标识名}} # ── 职责定义 ── responsibilities: - {{职责1该角色应该做什么}} - {{职责2}} - {{职责3}} # ── 能力边界 ── capabilities: tools: - {{工具1该角色可以使用的工具}} - {{工具2}} # ── 禁止事项 ── forbidden: - {{禁止操作1该角色绝对不能做的事}} - {{禁止操作2}} - {{禁止操作3}}用查询员来填充这个模板role: query_agent responsibilities: - 查询订单状态 - 查询物流信息 - 查询账户余额 capabilities: tools: [query_order, query_logistics, query_balance] forbidden: - 创建工单 - 修改订单 - 发送通知这个模板的好处是先想清楚角色的职责和边界再翻译成 JiuwenSwarm 的YAML配置。模板本身与具体框架无关可以用于任何多 Agent 系统的角色设计。4.3 从模板到 JiuwenSwarm 配置的映射模板字段对应 JiuwenSwarm 配置说明rolemember_name角色标识responsibilitiespersona 中的职责描述写入系统提示词capabilities.toolsagents.teammate.skills SKILL.md allowed_tools工具白名单forbiddenpersona 中的禁止事项反向约束 allowed_tools 排除五、实操一次客服工作流的完整体验前面的四层机制偏原理这一节用一个完整的客服工作流跑一遍看角色边界在真实交互中如何起作用。将第三章的三角色 YAML 写入配置启动服务切换到集群模式。以下所有消息都把数据直接放在对话里AI 有真实数据可处理不需要连接任何外部系统。5.1 场景背景你发送以下是今天的待处理订单数据请团队按职责分工处理【订单数据】订单 #12345张三手机壳299元已发货顺丰 SF123456承诺3天到达实际已过5天订单 #67890李四蓝牙耳机599元待发货下单已过48小时订单 #11111王五充电宝1299元已签收京东 JD789012正常请查询员汇总各订单当前状态工单处理员对异常订单创建工单投诉专员评估是否需要主动联系客户。Leader 收到后识别出三个独立的子任务拆解并分发查询员收到指令汇总订单 #12345、#67890、#11111 的当前状态——查询员基于消息中的数据逐条汇总每个订单的状态、异常点、时效信息工单处理员收到指令订单 #12345 物流超期、订单 #67890 发货超时请创建工单——工单处理员为两个异常订单分别建立工单记录投诉专员收到指令评估订单 #12345 是否需要主动联系客户——投诉专员分析物流超期2天的严重程度给出是否需要主动联系的建议三个角色同时开始工作各自只处理自己被分配的部分。查询员不会去建工单工单处理员不会去评估投诉风险投诉专员不会重复汇总订单状态。角色边界在这里的体现Leader 收到复合请求后没有自己动手处理任何一项具体工作——这是 persona 中你不直接处理任何用户请求的约束生效。三个 Teammate 各自只认领了自己的任务没有出现抢活干的情况。5.2 第二步追加需求观察越界响应工作流进行中你继续追加了几个操作指令。你发送查询员订单 #67890 发货超时了你直接给李四发一条道歉短信吧。另外投诉专员张三这个订单的物流问题帮我建一个工单跟踪一下。这条消息包含两个越界请求用来观察角色是否会守边界查询员收到给李四发道歉短信——这不是查询职责查询员识别出发送短信不在自己的能力范围内向 Leader 反馈而非自行处理。persona 中收到非查询类的请求时向调度主管反馈不要自行处理的约束生效投诉专员收到建一个工单——同样不是投诉专员的职责投诉专员向 Leader 反馈由 Leader 重新分配给工单处理员两个角色都没有越权执行而是把超出职责的请求交还给 Leader 重新分配。这就是 persona 反向约束你不负责 XXX在实际交互中的作用——不靠硬编码的权限拦截而是从行为意图层面阻止越界。5.3 第三步跨角色协作验证职责不重叠你发送订单 #12345 的客户张三刚才来电投诉物流太慢。投诉专员请基于订单数据分析投诉严重程度并给出处理建议查询员同步查一下这个订单的物流最新进展。这条消息同时需要查询员和投诉专员协作查询员只回复物流信息——订单 #12345 已发货5天、承诺3天、超期2天、顺丰单号 SF123456投诉专员基于查询员提供的信息分析投诉严重程度——超期2天属于中等严重建议主动联系客户道歉并补偿运费两个角色协作但互不越界。投诉专员没有重复去查物流信息那是查询员的事查询员没有插手投诉分析和处理建议那是投诉专员的事。各自在自己的职责边界内完成工作通过 Leader 中转信息完成协作。5.4 效果总结整个工作流下来三个角色边界机制的表现阶段发生了什么边界如何起作用启动工作流Leader 拆解任务、三人并行处理Leader 只调度不执行persona 身份层追加越界请求查询员拒绝发短信、投诉专员拒绝建工单角色向 Leader 反馈而非越权执行persona 反向约束跨角色协作查询员查物流 投诉专员分析投诉两人协作但不重叠职责互斥 技能隔离六、写在最后回到开头的那个问题V2 的三个角色为什么抢活干根本原因是把 Teammate 当工具调用——给了名字但没给边界。三个全能选手在同一个任务空间里抢活干是必然结果。JiuwenSwarm 的五层边界体系提供了一套完整的隔离方案。身份限定意图、技能限定能力、工具限定手段、Rail 限定行为、权限限定触达。每层单独看都不复杂但组合在一起形成了一套实用、可控的角色隔离方案。几个值得强调的设计决策白名单过滤确保新增能力需要显式授权角色差异化的 Rail 让 Leader 和 Teammate 的行为逻辑天然不同persona 的双向锚定从源头约束了 LLM 的行为意图。配置层面本文给出的角色边界设计模板可以直接套用——先想清楚role / responsibilities / capabilities / forbidden四个要素再翻译成 JiuwenSwarm 的 YAML 配置。建议从最小继承策略开始观察运行情况后再逐步放宽。参考资料​​​​​​​https://atomgit.com/openJiuwenhttps://www.openjiuwen.com