在workbuddy提示词组装时,我们可以看到有个 {{ PluginAgentPrompt }} 槽位,收集器会往里填专家人格。这个槽位是整条链路里最特殊的一块——其它变量都来自系统和会话状态,只有它,内容是由第三方开发者写的。所以这篇把专家这件事单独拆开。
在 WorkBuddy 里点一个专家,表面上是换了个名字和头像。实际发生的事要多一些:会话模板切分支,人格正文被塞进固定槽位,后续轮次还会收到提醒「现在是谁在说话」。如果选的是专家团,背后还会多出一组团队工具和一套关机流程。
本文的顺序照着这条链路走:专家以什么形式存在(一个插件目录)→ 怎么创建和校验 → 人格怎么进会话 → 没选专家时系统怎么推荐 → 多个专家组队时靠什么协作,以及规范层和运行时层打架的地方。
左边是货架上的专家包,右边是专家团的工作方式
专家是一个装得进目录的插件包#
先看专家以什么形式存在。用户安装的专家落在 ~/.workbuddy/plugins/marketplaces/my-experts/plugins/<name>/。目录结构大致是:
<name>/
├── .codebuddy-plugin/
│ ├── plugin.json ← 专家元数据(expertType: agent | team)
│ └── marketplace.json ← 市场注册信息
├── agents/
│ └── <agent>.md ← 人格正文(frontmatter + Markdown)
├── skills/ ← 可选技能
├── bin/ ← 可选脚本
├── hooks/ ← 可选钩子
└── avatars/ ← 头像
配置目录可以用 WORKBUDDY_CONFIG_DIR 或 CODEBUDDY_CONFIG_DIR 改,默认是 ~/.workbuddy。
把专家做成目录,有三个直接结果。复制目录就能迁移,版本管理可以直接用 Git;内置的 expert-manager 技能能按流程生成专家包;技能、脚本、钩子复用插件体系,不用为专家另造一套加载机制。
plugin.json 的字段分三组:
| 组 | 字段 | 作用 |
|---|---|---|
| 基础 | name、version、description、author | 插件身份与版本 |
| 类型 | expertType、agentName、teamInfo | 单专家还是专家团,主 Agent 是谁 |
| 展示 | displayName、profession、displayDescription、avatar、categoryId、tags、quickPrompts | 市场卡片、推荐卡片、默认引导语 |
expertType 只有两个值:"agent" 和 "team"。规范要求按实际结构填,单角色不能为了看起来更强标成团队。
agents/*.md 的 frontmatter 很窄:
---
name: stock-researcher
description: 负责基本面与财务分析的个股研究员
displayName:
en: "Stock Researcher"
zh: "个股研究员"
profession:
en: "Equity Research Analyst"
zh: "股票研究员"
maxTurns: 50
---
没有 tools 字段。规范写得很直白:工具权限由系统统一分配,开发者不能也不该在 frontmatter 里声明工具。
这条约束不只是风格问题。专家包如果能自带工具白名单,一个第三方专家就可以借人格扩张权限。把工具收归运行时,专家只回答「我是谁、我怎么做」,能做什么由会话模式和审批策略决定。
maxTurns 是少数留在包里的运行参数。普通专家常见 50,复杂专家或团队主理人建议 150 到 200。它管单次任务的回合上限,不管能力边界。
四个脚本把专家变成可交付物#
目录格式有了,普通用户怎么从零做出一个合格的专家包?WorkBuddy 把创建过程拆成四步,全在 expert-manager 技能里:
| 脚本 | 作用 |
|---|---|
init_expert.py | 初始化目录骨架 |
validate_expert.py | 校验 plugin.json、Agent Markdown、引用资源 |
register_expert.py | 写入 marketplace.json,注册到 my-experts |
package_expert.py | 打包成可分发的插件 |
校验这一步最容易被跳过,也最有用。没有它,专家包常见的毛病是 name 和文件名对不上、quickPrompts 不是 3 条、displayDescription 太长、头像路径不存在、团队 settings.json 指向错误的主理人。这些都不致命,但会让市场卡片一片空白。校验脚本把它们变成硬失败。
注册也不是可有可无。专家放进目录不会自动生效,还要写进 marketplace.json。市场索引和插件目录是两份状态,安装、卸载、更新都得维护一致性。
人格进入会话的四步#
包就绪、用户在 UI 里选中之后,就回到了上一篇说的那个槽位。会话带上 expertId,后面发生四件事:
- 模板切到
expert-*分支; ExpertPromptSlotCollector读取该专家的agents/*.md,剥掉 frontmatter,填入{{ PluginAgentPrompt }};- 正文前加一段 Role Override,明确「这是当前会话的权威角色定义,与更早的自我描述冲突时以这里为准」;
- 如果会话中途切换或取消专家,再注入
<expert_selection state="active|cancelled">。
第 3 步和第 4 步值得展开说。
会话里可能同时存在用户身份、个性化偏好、长期记忆、专家人格,它们都在描述「你是谁」。Role Override 的作用是给这些来源排个序:冲突时以专家定义为准。没有这一句,模型会按出现顺序或语义强度自行取舍,人格漂移多半从这里开始。
提醒处理的是另一件事:模板只在会话开始时渲染一次。用户在第二轮从「股票研究员」切到「法律顾问」,只改模板影响不了已经建立的上下文。<expert_selection state="cancelled"> 的措辞非常强硬:立即停止旧角色、忽略此前注入的专家指令、恢复默认行为。这是为了让「人格切换」成为一条模型无法忽略的对话事件。
人格正文有 20,000 字符上限。超出部分会被截断,提醒里会给出完整文件的绝对路径,并要求模型直接读该路径,不要用 Glob/find 重新扫描插件目录。省的是两类成本:上下文 token,以及模型在目录里乱翻的工具调用。
推荐不是写死在 UI 里#
人格注入解决的是「选了之后怎么生效」。但还有一个前置问题:用户还没选专家时,模型怎么知道该推荐哪几个?
专家推荐由 recommend-experts 技能驱动,三步:
- 确认当前会话没有已选专家;
- 调
search_plugins,type设为expert,把用户意图放进userIntent; - 调一次
suggest_plugin_install,用pluginId1..3渲染推荐卡片。
约束列出来很琐碎,但每一条都对应一类事故:
| 约束 | 防的事故 |
|---|---|
| 一次最多推荐 3 个候选 | 卡片轰炸 |
每个 ID 必须来自本次 search_plugins 返回 | 推荐幻觉,编造不存在的插件 |
suggest_plugin_install 每次响应只能调一次 | 连续弹窗 |
| 用户跳过、超时或取消后不重复推荐同一批 | 反复骚扰 |
不允许用 Web 搜索找插件,search_plugins 是唯一来源 | 来源不可控 |
| 不允许静默启用或替换当前专家 | 模型自作主张换人 |
市场先验放在 curated-experts.json:version: 1,60 个候选,类型只有 expert 和 expert_team,priority 从 5 到 300。SeniorDeveloper 是 300,WeChatMiniProgramDeveloper 是 295,EquityResearchExpert 是 290。
这份列表的定位是推荐先验,不是目录。它让常见任务有一个高召回、低延迟的起点,最终候选还要经过市场搜索确认。好处是质量可控,坏处是它会随版本漂移,需要人维护。
专家团:主理人、成员和一份 SOP#
单个专家的问题到此说清了。expertType 还有另一个值:"team",多个专家组成一个团队干活。这时候光有人格不够——谁拆任务、谁先说话、产出怎么汇总,都需要一套组织规则。
组织模型很明确:
- 一个主理人,负责拆解、分派、汇总、对用户沟通;
- 若干成员,各自有独立人格、职责和输出规范;
- 一套 SOP,按阶段推进,常见的是需求澄清、方案、执行、评审、交付;
- 一个
settings.json,把agent指向主理人的 Agent 名。
{
"agent": "trading-team-lead"
}
这个值要和 plugin.json 的 agentName、主理人 Markdown 的 name 三处一致。规范还要求主理人文件名不能叫通用的 team-lead.md,必须带团队前缀。多个团队同时安装时,通用名会冲突,排障也分不清是谁。
团队规范里有四条协作铁律:任务开始时由主理人亲自 TeamCreate,不能委派成员创建;按 SOP 阶段把成员拉进来,下发独立任务;成员产出回传主理人,由主理人汇总、转交下一阶段;专业产出以对应成员的输出为准,主理人只做编排和汇编。
还有五条红线:不能跳过 TeamCreate 自己模拟成员发言,不能代写成员产出,不能跳阶段,不能让成员互相直连,不能 spawn 主理人自己。
这些规则的目标很明确:防止假团队。模型很容易在一个回合里自问自答,伪装成多个角色讨论,最后给出一份看起来多方评审、实际只有一个视角的结论。强制 TeamCreate → Agent spawn → SendMessage 回传,是为了让每个角色留下独立会话、工具调用和产出记录。
团队是一组带协议的工具#
上面那套 SOP 靠什么落地?答案是:团队能力靠一组工具支撑,不靠 Prompt 模拟:
| 工具 | 作用 |
|---|---|
TeamCreate | 创建团队与任务目录 |
TeamDelete | 删除团队;仍有活跃成员时会拒绝 |
Agent | 派生 teammate / subagent |
SendMessage | 给具名成员发消息、广播、发关机请求 |
TaskOutput / TaskStop / TaskList | 后台任务查询、停止、列表 |
TaskCreate / TaskGet / TaskUpdate | 共享任务队列 |
从工具描述里能读出几条硬约束。
成员的普通文本输出对主理人和其他成员不可见,必须用 SendMessage。这是多 Agent 系统里最常见的误解:以为「我说了」等于「对方收到了」。
主理人的固定通信名是 team-lead。框架通知、关机请求、成员回传都发到这个邮箱;成员名用于任务归属和点对点消息。
关机有协议。TeamDelete 在还有活跃成员时会失败,必须先用 SendMessage 发 shutdown_request,等成员回 shutdown_response,再删除。会话直接结束时,团队目录会在关闭时自动清理。
后台 Agent 的完成状态必须查。Agent(run_in_background: true) 返回 { taskId, id, name, agentId },完成与否要通过 TaskOutput 获取,不能因为「没返回状态」就判断完成。
某些形态下团队工具会被禁用。在 Agent Home / colleague job 形态下,TeamCreate 和 SendMessage 不可用,改用频道 @ 提及。同一套运行时,在不同产品形态下会做能力裁剪。
规范和运行时打架的地方#
专家团规范要求所有跨成员信息流经主理人中转,成员不得直连。但运行时注入给成员的 team-sys-prompt 写着:如果你的工作和另一个成员重叠,直接给他发消息,不要什么都走 team-lead。
规范层:成员之间不得直连,所有信息流经主理人中转
运行时:鼓励成员在依赖重叠时直接通信
这不是谁对谁错。规范层要的是可审计、可复现的 SOP;运行时想减少主理人瓶颈。实际效果取决于模型更听哪一层。
对做多 Agent 系统的人来说,这件事的提醒很直接:写在文档里的协作规则,和注入到上下文里的协作规则,得一起设计、一起测试。只改一边,模型就会收到矛盾指令。
派生一个子 Agent,继承了什么#
团队跑起来之后还有一层:成员本身也能派生子 Agent。新会话会继承一批元数据:
subagent.meta.rootRequestId =
subagent.meta.rootRequestId ?? mainSession.meta?.rootRequestId;
subagent.meta.parentConversationId =
subagent.meta.parentConversationId ?? mainSession.id;
subagent.meta.subagentDepth =
subagent.meta.subagentDepth ?? nextSubagentDepth(mainSession);
subagent.meta.subagentSpawnBudget = getOrCreateSpawnBudget(mainSession);
| 字段 | 作用 |
|---|---|
rootRequestId | 把整棵 Agent 树的调用归到同一次用户请求,便于追踪与计费 |
parentConversationId | 建立父子会话关系,支持回放与审计 |
subagentDepth | 防止无限递归 |
subagentSpawnBudget | 限制一次请求内能派生多少子 Agent |
并发预算有三个默认值,都能用环境变量覆盖:
最大并发子 Agent:20 CODEBUDDY_CODE_MAX_CONCURRENT_SUBAGENTS
每会话最大子 Agent:200 CODEBUDDY_CODE_MAX_SUBAGENTS_PER_SESSION
最大嵌套深度:5
20 个并发意味着一个 30 人的大团队不能同时开工,只能分批;200 个每会话意味着长任务可以反复派生,但不能无限扩张;深度 5 意味着主理人、成员、成员的子 Agent 最多五层,再深会被拒绝。
团队模式还有两个特殊处理。sandboxScopedAgentView 让成员拿到按职责裁剪过的视图,工具面、沙箱范围、可见资源都可能不同;leader 模式可以 fork 父会话的 active 历史,有些场景下子 Agent 需要继承主理人已经积累的上下文,而不是从零开始。
成本,以及几个容易忽略的坑#
成本不是线性的。每个成员都是独立会话,有自己的系统提示词、工具描述和上下文。一个 5 人团队做一件事,token 成本可能是单 Agent 的数倍。maxTurns、并发上限、后台任务都是成本阀门。
协调开销会吃掉并行收益。主理人拆任务、成员回传、主理人汇总,每一步都是串行等待。任务粒度太细,协调成本高于执行成本;粒度太粗,成员之间又容易冲突。
结果不确定。多 Agent 的输出依赖消息到达顺序、成员是否按时回传、主理人怎么汇总。同样的输入,两次运行可能得到不同质量的结果。这是分布式协作的固有属性,得靠任务状态和产物校验兜底。
规范容易漂移。「成员能否直连」就是一个例子。规范、Prompt、工具描述三处都在表达协作规则,任何一处更新而另外两处没跟上,模型就会收到矛盾指令。
权限边界要单独验证。专家包不能声明 tools,这是很好的起点;但专家包可以带 skills、bin、hooks,这些仍然有执行能力。安装第三方专家时,真正的风险不在人格文本,在它附带的脚本和钩子。
最后#
专家团的可靠性来自工具和协议,不在提示词。TeamCreate、SendMessage、shutdown_request、任务队列,这些让团队能被审计、被停止、被清理;提示词只负责让角色说对话。
权限是另一条主线。专家只描述「我是谁、怎么做」,工具由运行时按模式和审批策略注入——这个分工挡住了「装一个人格就拿到一套权限」的捷径。但专家包仍然可以带 skills、bin、hooks,安装第三方专家时,真正该看的不是人格文本,是它附带的脚本和钩子。
文中的目录结构、字段、并发预算按 WorkBuddy 5.8.4 整理;常量名和字段名保持原样,代码为便于阅读做过格式化。版本更新后默认值可能变化,设计方式比具体数字更稳定。