不用写Prompt也能用专家:一套目录复刻WorkBuddy


前面两篇我们聊了关于模型配置、工具管理、权限审批,后来又聊了MCP连接器和Skills机制。到第二篇结束的时候,我们使用agentScope开发的mini-WorkBuddy已经能操作本地文件、调用外部MCP服务、加载技能来工作了。
我们平时用的Agent,大概也就这样了,但是这个对普通用户其实不友好的,开发这个还是用的开发者思维,大多数的用户 根本不关心,你的什么系统提示词,工具,技能这些东西,而且这些东西对用户来说,门槛也挺高的,还需要学习不少的内容。大部分用户想要的是一个能干活的AI工具,给它一个任务,AI能把它做好就行。
那么最近很火的WorkBuddy是怎么来做的呢,我觉得是专家和专家团。用户不需要知道Prompt工程是什么东西,选一个专家,Agent就用那个身份和工作方法来干活。选一个专家团,就把多个专家拉到一起,组件一个团队一起干活。

这篇文章我们就来看看这两块是如何工作的,先搞清楚WorkBuddy怎么设计专家和专家团,再看AgentScope怎么把这两套机制落地。
把专业方法封装成一个专家

WorkBuddy 的专家是什么
我们之前写过一篇关于WorkBuddy专家和专家团的文章,有兴趣的可以去看看那
我们之前在开发Agent的时候,都会有一个新建Agent的操作,让用户手可以手动创建一个自己的Agent来完成特定的任务,增加灵活性,这种Agent我们一般都是让用户自己配置系统提示词,选择工具和技能,我开始认为的这就是垂直类的Agent,也就是我们常说的专家Agent。
每个专家都是一个经过角色化封装的垂直领域AI Agent。专家得知道自己擅长什么,也得知道哪些事做不了。收到任务后,它应该按一套相对稳定的办法做事,不能每次都靠模型临场发挥。交付物也需要提前规划好,可能是一篇文章、一份代码、一份报告,也可能是一张检查单,甚至它还可能带着自己的 Skill、脚本和参考资料。
我们可以把专家拆成五个核心要素:
- •- 人设:专家是谁,有什么专业背景,说话风格什么样
- •- 方法论:这个领域的标准工作流,比如写作专家会按“选题→大纲→初稿→修改→定稿”的路子走
- •- 工具链:绑定了哪些Skill、MCP连接器和内置工具
- •- 工作模板:常见任务的输出格式,比如数据分析师出报告的结构
- •- 沟通风格:输出是简洁的要点还是详细的分析,用不用表格
就拿这个 去 AI 味道写作专家 来说,用户不用自己写一大段 Prompt,只要把文章交给它。它就会按照预先固化的流程,从词汇、句式、逻辑、语气和结构五个维度检查,先评估AI 味浓度,再定向改写,最后给出对比和修改说明。
WorkBuddy调用专家非常简单,在专家页面选择一个专家召唤就行,也可以直接在聊天页面点击 '+' 号 选择一个专家来工作。我们选择专家并使用后,在workbuddy的默认目录下,会把这个专家相关的文档给下载下来,这个有点像skills,这里一个文件夹就是一个专家。

打开一个专家的,我们可以看下具体的目录是什么样的

如上图,专家的文件目录,大概成为以下几个部分,
- •- plugin.json 这个是整个专家的一个说明,名称,描述,有哪些技能等
- •- agents 这个目录下面有一份提示词说明,关于这个专家如何干活,工作流,输出格式,沟通说明等都在这个提示词文件中
- •- avatars 专家头像
- •- skills 这个专家拥有的技能
这个目录结构式Workbuddy专家特有的,我们在设计专家的时候,也是可以参考的,Workbuddy创建专家的时候,会默认使用一个内置技能 expert-manager

这个技能有详细的描述,要如何引导用户来创建一个专家,必要时会不停的和用户对话来确认流程和工作机制,最后生成的目录结构和我们上面描述的专家目录保持一致。
AgentScope 如何复刻专家
事实上,我在开发这个专家的时候,因为有了workbuddy这个专家的参考,首先想到的就是 使用文件夹来管理,一个文件夹对应一个专家。这个真的非常方便,比较容易维护和分享。
最后,我把每个专家做成了独立目录包,在项目的工作目录workspace下面新建了一个experts目录用来存放专家文件夹,例如 去 AI 化写作专家的目录如下。
workspace/experts/ai-detox-writer/├── .workbuddy-plugin/│ └── plugin.json├── agents/│ └── ai-detox-writer.md├── avatars/├── skills/ # 可选│ └── rewrite-helper/│ └── SKILL.md└── README.md
这个分工就很明确了,plugin.json是专家入口,agents/放专家的角色定义,工作流的说明,skills/只属于这个专家的技能,avatars/存放专家头像,README.md给人看的说明。
那plugin.json里到底写了什么?简化后的配置长这样:
{ "name": "ai-detox-writer", "expertType": "agent", "agentName": "ai-detox-writer", "displayName": {"zh": "去AI味道写作专家"}, "profession": {"zh": "去AI化写作专家"}, "displayDescription": {"zh": "识别并去除文章AI生成痕迹,让文本回归自然表达"}, "skills": ["./skills/rewrite-helper"], "runtime": {"accent": "#ec4899"}}挨个说一下这些字段是干嘛的。
- •- name是专家包的唯一标识,内部查找和缓存key都会用到它。
- •- expertType这里是agent,表示单专家。
- •- agentName指向agents/目录下的Markdown文件名,不带.md后缀。
- •- displayName、profession、displayDescription这三个纯粹是给用户看的,显示在专家卡片和详情页上。profession是职业名称,displayDescription是能力描述,让用户一眼就知道这专家能干嘛。
- •- skills声明这个专家自带的Skill目录。加载专家的时候,这些Skill会和用户选择的Skill一起注册进Toolkit。
- •- runtime是我们自己扩展的运行参数,比如accent控制页面上专家卡片的强调色。

不过这些都只是卡片展示用的信息,真正决定专家怎么工作的,是agents/ai-detox-writer.md这个文件:

到这里我们手上只有一堆文件,怎么把它们变成AgentScope能用的Agent?
我们做的事其实很简单,扫描目录->校验字段->拼出system\_prompt->调AgentScope的API。
先看怎么发现和加载专家,项目启动时扫描workspace/experts/目录, 项目启动后,ExpertPackageRepository 会扫描 workspace/experts/ 的直接子目录。
for package_dir in experts_dir.iterdir(): manifest = load_json(package_dir / ".codebuddy-plugin/plugin.json") agent_name = manifest["agentName"] prompt, max_iters = load_agent_markdown( package_dir / "agents" / f"{agent_name}.md" ) skill_dirs = load_skill_dirs(package_dir, manifest) experts[manifest["name"]] = ExpertSpec( id=manifest["name"], handle=agent_name, prompt=prompt, max_iters=max_iters, skill_dirs=skill_dirs, )这里的代码是为了说明流程做的简化。真实实现还会解析多语言展示字段、快捷问法、分类、头像和版本指纹。 用户发起专家对话时,WorkBuddyService._expert_chat() 先选中 ExpertSpec,随后仍走通用 Agent 的创建逻辑。
agent = Agent( name=expert.handle, system_prompt=expert_system_prompt, model=create_model_client(model_config), toolkit=toolkit, context_config=ContextConfig(...), react_config=ReActConfig(max_iters=expert.max_iters),)
我们没有为它新写一个 ExpertAgent 子类,底层还是普通的 AgentScope Agent,只是 system_prompt 换成了专家的工作方法,Toolkit 多加载了专家自带的 Skill,ReAct 轮数改用 maxTurns,缓存和状态也按专家配置隔离开。
这层复用省了很多事,前两篇接好的本地工具、权限审批、MCP、上下文压缩和长期记忆,专家可以直接使用。
这个专家的Skill在plugin.json里声明了路径,加载的时候需要解析出来,和用户选的Skill一起交给Toolkit:
skill_loaders = list(expert.skill_dirs)if selected_skill_dir: skill_loaders.append(selected_skill_dir)toolkit = Toolkit( tools=[Read(), Glob(), Grep(), Write(), Edit(), Bash(cwd=workdir)], skills_or_loaders=skill_loaders, mcps=mcp_clients,)
这样组装完,专家既有 mini-WorkBuddy 内置的文件和命令工具,也有自己包里的 Skill。用户临时选中的 Skill和 MCP 连接器也会一起挂上去。模型看到的就是一个统一的 Toolkit,用的时候不需要管这项能力到底从哪里来。
专家通常会常态化调整,如果直接修改 agents/*.md,但内存里的 Agent 还拿着旧 Prompt,页面显示和真实行为就会对不上。
为了避开旧缓存,加载器会根据 Prompt、max_iters 和 Skill 目录生成一个 revision,并把它放进 Agent 缓存键。
revision = sha256({ "prompt": prompt, "max_iters": max_iters, "skill_dirs": skill_dirs,})[:12]agent_key = f"expert:{expert.id}:{revision}:{model_id}"缓存键还会继续加上连接器和当前手动选中的 Skill。只要 Prompt、轮次、Skill、连接器或模型中的任何一项变了,旧缓存就不会被命中,系统会创建一个新 Agent。

让多个 Agent 围绕一个目标协作

WorkBuddy 的专家团是什么
单个专家解决了找谁做的问题,但有些事情本来就不是一个人能包圆的。开发一个系统,要做架构、写前后端、跑测试,做一份完整调研,也可能同时需要研究、数据分析和风险审查。
专家团就是为这类任务准备的,它是由多个专家组成的团队。workbuddy的设计思路是,团队里有一个主理人负责任务拆解和调度,下面是职责不同的成员,每个人各干各的,最后再把结果聚合输出。
比如一个研发专家团有五个角色。
角色
职责
研发交付总监
需求澄清、任务拆分、进度协调、验收和最终交付
架构师
技术选型、模块划分、数据模型和架构风险
高级后端工程师
API、业务逻辑、数据库和服务端实现
高级前端工程师
页面、交互、组件和前端工程实现
质量保障工程师
测试策略、用例、回归验证和质量报告
这个里面的研发交付总监就是主理人,它需要先把需求问清楚,再让架构师出设计,设计定下来之后前端和后端分头开工,两边都做完以后,QA 再进场。主理人一直盯着任务进展,最后由它向用户交付。

专家团的核心--主理人
主理人就是这个团队的项目负责人,主理人只管调度,不管执行。主理人不代写任何成员的专业产出,成员之间不直接通信,所有信息流经过主理人中转。
它主要管协调,任务超出团队能力就给用户说清楚,这个事情干不了,任务信息不够就先进行询问,如果任务能做,就做拆分分给对应的Agent干活。哪些任务可以并行,哪些必须等前一步,也由它判断。中间如果有Agent失败、产物冲突或者漏了东西,主理人负责补任务和返工。任务产出的结果都整理完成后,它再给用户一份完整结果。
不过,主理人也不能有点小事就把全队叫起来,如果用户的问题很简单,主理人可以直接回答,不用启用团队。
专家、专家团、技能和连接器有什么区别
这四个概念放在一起很容易混淆。
能力
解决的问题
在 mini-WorkBuddy 中发生什么
专家
谁来负责这项专业任务
用固定身份、方法和交付标准创建一个 Agent
专家团
复杂目标由哪些角色协作
主理人动态建队、拆任务、管依赖并汇总交付
技能
一类任务具体应该怎么做
Agent 按需加载流程、知识、模板和脚本
连接器
去哪里读写真实数据
通过 MCP 获得外部系统的工具能力
专家管谁来做,专家团管这些人怎么合作,Skill 告诉 Agent 具体怎么做,连接器让它能去真实系统里干活。
专家团也是一个自包含目录包
WorkBuddy的专家团实现方式和专家 区别不大,也是采用的一个文件夹一个专家团的配置,在新建专家团的时候 使用的是同一个 技能 expert-manager 来辅助用户创建专家团。
它们主要是通过 plugin.json 中的 expertType 来区分
支持两种专家类型:
- •- Agent 型(expertType: "agent"):单个 AI 专家
- •- Team 型(expertType: "team"):多角色协作团队
专家团在 agents目录下 会有多个提示词,一个提示词就是一个专家干活的指令,还有就是avatar目录下有多少个专家就有多少个头像
我们把专家团放在 workspace/teams/ 下。一个研发专家团结构如下。
workspace/teams/rd-expert-team/├── .workbuddy-plugin/│ └── plugin.json├── agents/│ ├── rd-expert-team-team-lead.md│ ├── rd-expert-team-architect.md│ ├── rd-expert-team-backend.md│ ├── rd-expert-team-frontend.md│ └── rd-expert-team-qa.md├── avatars/├── settings.json└── README.md
专家团的 plugin.json 与单专家大体相同,关键区别有下面几个。
{ "name": "rd-expert-team", "expertType": "team", "agentName": "rd-expert-team-team-lead", "teamInfo": { "leadAgent": "rd-expert-team-team-lead", "memberAgents": [ "rd-expert-team-architect", "rd-expert-team-backend", "rd-expert-team-frontend", "rd-expert-team-qa" ] }, "members": [ {"id": "rd-expert-team-team-lead", "role": "lead"}, {"id": "rd-expert-team-architect", "role": "member"} ], "runtime": { "workflows": ["需求分析与架构设计", "开发实施与质量保障", "汇总交付"] }}expertType 变成了 team,teamInfo 显式声明主理人和成员列表,members 保存每个角色的名字、职业和头像,runtime.workflows 给出团队的高层流程。
settings.json.agent 还必须与 teamInfo.leadAgent 一致。加载时,主理人和每位成员的 Markdown 都会被校验,再组装成一个 TeamSpec。
这里有个设计我考虑了很久,专家团包内保存成员的完整 Agent Markdown,不依赖外部专家引用,只有这样,团队包才能独立安装和分发。换一台机器没有安装某个外部专家,团队仍然是完整的。
AgentScope Agent Service 如何实现专家团
做专家时,一个 Agent 就够了,到专家团这里,只是多创建几个 Agent 肯定不够,团队还得有任务、依赖、会话、消息和共享工作区,AgentScope Agent Service 正好把这几件事都包了。
Agent Service 已经提供了团队运行需要的核心对象:
- •- Agent:主理人和成员
- •- Session:每个 Agent 独立的对话和状态
- •- Team:团队及成员关系
- •- Task:任务、负责人、依赖和状态
- •- Message Bus:主理人和成员之间传递事件
- •- Workspace Manager:为团队分配工作目录
主理人还可以使用一组团队工具:
- •- TeamCreate:创建团队
- •- TaskCreate:创建结构化任务
- •- TaskUpdate:设置负责人、依赖和任务状态
- •- AgentCreate:根据模板创建成员 Agent
- •- TeamSay:在团队中发送消息
- •- TeamDelete:任务结束后清理团队
完整流程大致如下:
注册成员
主理人调用 AgentCreate 时,需要告诉 AgentScope 要创建哪一类成员。因此,项目会把团队包中的所有成员转换成 SubAgentTemplate。
SubAgentTemplate( type=member.id, description=f"{member.name}:{member.summary}", system_prompt_template=( "你是 {member_name},隶属于 {team_name}," "主理人是 {leader_name}。\n" f"{member.prompt}\n\n" "只完成职责范围内的专业工作;" "禁止联系其他成员;" "所有结论、风险和产物路径必须用 TeamSay 回传。" ), react_config=ReActConfig(max_iters=member.max_iters),)type 是成员类型的唯一标识。主理人在 AgentCreate 里的 name 和 subagent_type 都必须使用这个 Agent ID,不能传中文名字,也不能临时编一个标识。
description 是给主理人看的能力说明。system_prompt_template 则把团队名称、主理人、本轮职责和成员自己的 Prompt 合并起来。不得跨职责,成员之间禁止直连。必须通过 TeamSay 回传也会被统一追加进成员 Prompt。
专家包在进程启动后仍然可能新建。因此每次团队对话开始前,refresh_subagent_templates() 都会重新生成并更新模板注册表,新安装的专家团不需要重启进程就能调度。
主理人的编排
NativeTeamRuntime 会为当前工作区、聊天 Session 和专家团生成稳定的主理人 Agent ID 与原生 Session ID,再把团队包中的主理人 Prompt、可调度成员、能力关键词和高层工作流程组合成最终的 system_prompt。
这个 Prompt 要求主理人遵守下面这套流程。
这里没有用 Python 写死成员和顺序,专家团只给出了SOP和业务边界,具体让谁干活、拆成几个任务,都由主理人根据本轮目标决定。
一项完整研发任务可以这样拆。
任务1 架构设计 owner=architect depends_on=[]任务2 后端开发 owner=backend depends_on=[任务1]任务3 前端开发 owner=frontend depends_on=[任务1]任务4 质量验收 owner=qa depends_on=[任务2, 任务3]
任务1完成后,任务2和任务3之间没有直接依赖,主理人可以连续创建前端和后端成员,让他们分别在独立 Session 中执行。等两者都完成以后,QA 任务的前置条件才满足。
这里的前端和后端是两个不同的 Session 和执行流的 Worker,可以同时推进。它们并没有再同一个 Agent 里轮流切换角色。
成员之间如何通信
当前的成员模板禁止 Worker 直接联系其他成员。所有信息都必须走下面的路径。
成员 A ── TeamSay ──> 主理人 ── 完整结果中转 ──> 成员 B
这么做虽然会多一次中转,但是主理人却能始终知道当前发生了什么,它可以等并行任务都回传后做冲突检查,也可以在进入下一阶段前补齐上下文。两个成员私下改了方案,最终交付人却毫不知情,这类问题也就不存在了。
主理人是唯一信息枢纽,也是唯一对用户负责的 Agent。
如何显示子Agent的工作记录
多个 Agent 在后台工作,用户端却什么都看不见,不知道当前Agent正在做什么,这个问题很容易被漏掉。
主理人和每个 Worker 都有自己的工作 Session,如果前端只订阅主理人的事件流,当成员连续工作几十秒时,页面上看起来就像卡住了。
为了让页面始终有反馈,项目加了一个 WorkerEventProjector,它会检查 Worker Session 的 team_id,找到团队主理人的 Session,再把 Worker 的原生 AgentScope 事件包装成自定义事件,发到主理人的事件通道。
if session_record.team_id: team = await storage.get_team(user_id, session_record.team_id) if team and team.session_id != session_record.id: await projection.publish( team.session_id, "workbuddy.team.worker_event", { "agent_id": agent_record.id, "session_id": session_record.id, "name": agent_record.data.name, "event": event.model_dump(mode="json"), }, )NativeTeamRuntime 再把这些事件翻译成前端已经认识的消息格式。
- •- team_member_event 表示成员开始工作或产生了一个 Agent 事件。
- •- team_member_delta 表示成员正在增量输出文本。
- •- team_member_done 表示成员本轮完成。
- •- team_tasks 带回当前任务看板和状态。
这样前端页面不用同时管理多条 AgentScope 连接,也能在同一个聊天页面里显示主理人建了哪些任务、调了哪些成员、谁正在执行、谁已经完成。
专家团怎样跑完一条异步任务
普通 Agent 收到 REPLY\_END,这一轮对话通常就结束了,专家团不能这样处理,因为主理人的回复结束时,成员可能才刚开始工作。
主理人调用 AgentCreate 后,Worker 会进入各自的 Session 执行任务。主理人不必一直占着当前回复等待,可以先结束这一轮消息,这里的 REPLY\_END 只说明主理人暂时说完了,不能表明整个团队已经完成任务。
成员完成工作后,会通过 TeamSay 把结果发给主理人,AgentScope 的 dispatcher 收到消息,再次唤醒主理人的 Session。主理人拿到结果后,可能继续派发下一阶段,也可能要求成员返工,或者整理现有产出交付给用户,一次完整任务往往会经历多轮这样的唤醒。
流式运行时收到主理人的 REPLY\_END 后,不会马上关闭连接,它会先等待 AgentScope 保存最新状态,读取任务看板,再检查这个 Session 是否仍然绑定着 Team。
只要绑定关系还在,就说明团队仍有工作需要处理,运行时就会继续监听事件,等待 Worker 回传或 dispatcher 再次唤醒主理人,等主理人完成交付并调用 TeamDelete,Team 被清理,这一轮专家团对话才真正结束。
专家和专家团应该怎么选
做完之后,我们再来看看专家和专家团怎么选择
维度
专家
专家团
核心问题
一个专业角色如何稳定交付
多个角色如何围绕同一目标协作
运行时
AgentScope Agent
AgentScope Agent Service / Agent Team
上下文
一个 Agent Session
主理人和多个 Worker Session
任务拆分
在专家 ReAct 内完成
主理人创建结构化任务和依赖
执行方式
单 Agent 执行
有依赖的串行,无依赖的可并行
信息交换
无需跨 Agent 通信
Worker 用 TeamSay 回传,主理人中转
基础设施
普通 Agent 运行环境
额外需要 Storage、Message Bus 和 Workspace Manager
成本
较低
多次模型调用、更长运行时间和更复杂的异常处理
适用场景
写作、分析、评审、翻译
软件交付、复杂调研、内容项目、多角色审查
我们目前的判断方法比较简单,如果一位专家能够从头做到交付,就交给专家。任务里确实存在多种专业责任,拆开后又有清楚的输入、输出和依赖,才用专家团。
AgentScope 恰好提供了两层对应的能力,普通 Agent 跑单专家,Agent Service 撑起专家团。