上一篇讲了 Skill——把工作流固化成可复用知识。但 Skill 解决的是”怎么做”,不是”谁来做”。当任务变复杂,单 agent 会撞到 context window 上限、工具调用串行瓶颈、子任务互相踩文件这些问题。
这就是多 agent 系统要解的题。
用户视角:怎么让多 agent 干活
先讲最实际的——你(用户)怎么触发多 agent。
方式一:直接说,让 agent 自己决定
最简单的方式:你只管描述任务,agent 自己判断要不要 spawn 子 agent。
你:帮我重构 src/auth/ 目录,同时更新测试和文档。agent 看到”同时”,会自动 spawn 多个子 agent 并行干活。你不需要关心内部怎么调度。
方式二:显式要求并行
如果你想强调并行,直接说:
你:并行调查 src/auth/、src/api/、src/utils/ 三个目录的结构,然后汇总。agent 会 spawn 三个 explore agent 并行调查。
八种内置 Agent
omp 内置了 8 种 agent,每种有明确的分工:
| Agent | 干什么 | 能改文件吗 | 用什么模型 |
|---|---|---|---|
task | 通用任务 | ✅ 能 | pi/task |
explore | 快速探索代码 | ❌ 工具级只读 | pi/smol |
plan | 架构规划 | ⚠️ 带 bash,靠 prompt 约束 | pi/slow |
designer | UI/UX 设计 | ✅ 能 | pi/designer |
reviewer | 代码审查 | ⚠️ 带 bash,靠 prompt 约束 | pi/slow |
librarian | 查文档/库 | ⚠️ 带 bash,靠 prompt 约束 | pi/smol |
tester | 写测试 | ✅ 能 | pi/task |
sonic | 快速小任务 | ✅ 能 | pi/smol |
关键区别:只有 explore 是工具层面硬约束只读——它的工具列表里根本没有 edit、write、bash,物理上无法修改文件。
plan、reviewer、librarian 虽然带 bash,但靠 prompt 约束”不主动改文件”。如果你对安全性要求很高,用 explore 最保险。
什么时候用哪个?
- 调查代码结构 →
explore(快,只读,不会误改文件) - 写代码/改文件 →
task(通用,什么都能干) - 写测试 →
tester(专门干这个) - 查第三方库怎么用 →
librarian - 审查代码质量 →
reviewer - 设计 UI →
designer - 简单快速任务 →
sonic(用小模型,省 token)
实战场景
场景一:大范围重构
需求:把项目里所有 var 改成 const/let。
你:把 src/ 目录下所有 .ts 文件里的 var 声明改成 const 或 let,同时更新相关测试。agent 会:
- spawn
exploreagent 调查哪些文件用了var - spawn 多个
taskagent 并行修改不同子目录 - spawn
testeragent 更新测试
场景二:新功能开发
需求:给博客加一个评论系统。
你:给博客加评论系统,需要:1. 后端 API(src/api/comments.ts)2. 前端组件(src/components/Comments.astro)3. 数据库 schema(prisma/schema.prisma)4. 测试
并行开发,最后集成。agent 会 spawn 多个 task agent 并行开发各部分。
场景三:代码审查
需求:审查最近的改动。
你:审查 src/auth/ 目录的代码质量,找出潜在问题。agent 会 spawn reviewer agent,它只读代码、报告问题,不会修改任何文件。
场景四:文档研究
需求:查一个第三方库怎么用。
你:查一下 zod 库的 discriminated unions 怎么用,给几个例子。agent 会 spawn librarian agent 去查文档、搜示例。
场景五:多视角审稿
需求:审查一篇博客文章,从不同角度找问题。
这是多 agent 的高级用法——让多个 agent 从不同视角评估同一份内容,通过分歧暴露盲点。
审稿 Prompt(可直接复制使用)
请 spawn 4 个 reviewer agent,从不同角度审阅这篇文章。
文章路径:src/content/posts/xxx.md
tasks: - id: editor role: 专业编辑 assignment: | 你是资深编辑,专注于: - 文章结构是否清晰(引言→正文→结论) - 段落过渡是否自然 - 语言是否简洁,有没有冗余表达 输出:评分(1-10) + 3个优点 + 3个改进点 + 具体建议
- id: reader role: 目标读者 assignment: | 你是这篇文章的目标读者,专注于: - 读完能否理解核心概念 - 能否照着文章实际操作 - 哪些地方看不懂需要更多解释 输出:评分(1-10) + 3个优点 + 3个改进点 + 具体建议
- id: tech role: 技术审稿人 assignment: | 你是技术专家,专注于: - 技术概念是否准确 - 代码示例是否正确、可运行 - 术语使用是否一致 输出:评分(1-10) + 3个优点 + 3个改进点 + 具体建议
- id: seo role: SEO 专家 assignment: | 你专注于内容传播,专注于: - 标题是否吸引人 - 是否适合搜索引擎和社交分享 - 段落长度是否利于快速浏览 输出:评分(1-10) + 3个优点 + 3个改进点 + 具体建议
最后汇总:1. 一致认为的问题(必须改)2. 有分歧的问题(需要判断)3. 优先级排序这个模式的核心价值
不是让一个 agent 做所有判断,而是让多个 agent 从不同角度看,通过分歧暴露盲点。
- 一致的问题 = 真问题,必须改
- 有分歧的问题 = 审美/风格选择,需要人工判断
例如:
- 编辑说”太啰嗦”,读者说”刚好” → 可能是风格差异
- 技术审稿人说”概念不准确”,SEO 专家说”关键词很好” → 技术准确性优先
- 读者说”看不懂”,编辑说”结构清晰” → 需要补充解释
迭代改进
收到审稿结果后,可以让 omp 继续:
你:根据审稿结果,修改文章。优先处理一致认为的问题, 然后处理有分歧但你判断应该改的地方。这就是多 agent 审稿的完整工作流:
- 运行多视角审稿 prompt
- 查看一致问题和分歧点
- 让 omp 根据结果修改
- 如果需要,再跑一轮审稿验证改进效果
什么时候用多 agent
这是最重要的问题。多 agent 不是万能的,用错场景反而更慢。
✅ 适合多 agent 的场景
1. 独立子任务,无文件依赖
✅ 重构 auth.ts + 更新测试 + 更新文档 → 三个 agent 各改各的文件,互不干扰2. 大范围搜索
✅ "找出所有用了旧 API 的地方" → spawn 5 个 explore agent,每个搜一个子目录3. 探索 + 执行分离
✅ "先调查代码库结构,然后实现功能" → explore agent 调查(只读,快速),task agent 执行(可写)4. 多视角评估
✅ "从安全、性能、可维护性三个角度审查这段代码" → spawn 3 个 reviewer agent,不同角色,找分歧❌ 不适合多 agent 的场景
1. 强依赖的串行任务
❌ "先改接口,再改实现,最后改测试" → 后一步依赖前一步,无法并行2. 小任务
❌ "把这个变量名从 x 改成 count" → spawn agent 的开销比直接改还慢3. 共享状态的任务
❌ "同时改 package.json 的依赖版本" → 两个 agent 改同一个文件,必然冲突内部机制:agent 怎么调度子 agent
如果你好奇内部怎么工作,这里是简化的流程:
用户说"并行做 A、B、C" ↓主 agent 决定 spawn 子 agent ↓调用 task 工具,传入: - agent: "task"(或 "explore" 等) - assignment: 具体任务描述 ↓omp 创建子 agent 进程 ↓子 agent 独立执行,完成后 yield 结果 ↓主 agent 收到结果,汇总Batch 调用:一次 spawn 多个
普通调用一次 spawn 一个 agent:
task({ agent: "task", assignment: "重构 auth.ts" })Batch 调用一次 spawn 多个,共享 context:
task({ agent: "task", context: "目标:重构 auth 模块,使用 JWT 替代 session。约束:保持 API 兼容。", tasks: [ { id: "refactor", role: "Auth 重构专家", assignment: "重构 src/auth.ts" }, { id: "tests", role: "测试工程师", assignment: "更新 auth 相关测试" }, { id: "docs", role: "技术文档", assignment: "更新 API 文档" }, ],})context 是共享背景——所有子 agent 都能看到。每个 task 的 assignment 是具体指令。
隔离执行:避免文件冲突
多个 agent 同时改文件会冲突。omp 的解决方案是 isolation worktree——每个 agent 在独立的工作区里工作,改完再合并。
注意:isolated: true 需要先在 settings 里启用隔离模式:
{ "task.isolation.mode": "auto" // 默认是 "none",不启用隔离}可选的后端:auto、apfs、btrfs、zfs、reflink、overlayfs、projfs。
启用后,可以指定合并策略:
- patch 模式(默认):生成 patch 文件,主 agent 决定是否应用
- branch 模式:创建 git 分支,cherry-pick 到主分支
task({ agent: "task", assignment: "重构 auth.ts", isolated: true })IRC:Agent 间通信
多 agent 运行时,它们之间可以发消息:
# 主 agent 给子 agent 发消息irc({ op: "send", to: "AuthRefactor", message: "注意保持旧 API 兼容" });
# 子 agent 互相通信irc({ op: "send", to: "TestWriter", message: "auth.ts 改完了,接口变了" });
# 查看有哪些 agent 可以联系irc({ op: "list" });什么时候用 IRC?当子 agent 之间有依赖关系时。比如 agent A 改完了接口,需要通知 agent B 更新测试。
自定义 Agent
内置 agent 够用,但有时你需要专门的 agent。
在项目里创建 .omp/agents/my-agent.md:
---name: my-agentdescription: 专门处理数据库迁移的 agenttools: read, grep, glob, write, edit, bashspawns: "explore,task"---
你是数据库迁移专家。
## 规则- 先分析现有 schema- 生成 migration 文件- 更新 ORM 类型定义- 跑测试验证
## 注意事项- 不要破坏现有数据- 生成回滚脚本然后就能用了:
你:用 my-agent 给 users 表加一个 avatar 字段。Agent 发现优先级
同名 agent 的优先级:
- 项目
.omp/agents/(最高) - 用户
~/.omp/agent/agents/ - 内置 agent(最低)
你可以在项目里覆盖内置 agent 的行为。
性能陷阱
1. 过度并行
同时 spawn 10 个 agent 看起来很快,但:
- API rate limit 可能更低
- 10 个 agent 同时读同一个大文件,重复消耗 token
- 并发上限是 32,但实际瓶颈往往在 API
2. Context 膨胀
每个子 agent 的输出都会回到主 agent 的 context。如果子 agent 输出很长,主 agent 的 context 会快速膨胀。
omp 的限制:每个子 agent 最大输出 500KB / 5000 行。超出会被截断。
3. 隔离开销
Worktree 隔离有成本:创建 worktree、克隆文件、合并结果。对于小任务,这个开销可能比任务本身还大。
源码视角:关键设计
如果你对实现细节感兴趣,这里是几个关键设计:
工具权限硬约束
export const READ_ONLY_TOOL_NAMES: ReadonlySet<string> = new Set([ "read", "grep", "glob", "web_search", "ast_grep", "yield", "irc", "ask", "job", "todo", // ...]);
export function isReadOnlyAgent(agent: AgentDefinition): boolean { return !!agent.tools?.length && agent.tools.every(tool => READ_ONLY_TOOL_NAMES.has(tool));}explore agent 的 tools 列表只有 read, grep, glob, web_search——全是只读工具。它根本拿不到 edit 工具。
而 plan、reviewer、librarian 的 tools 列表里有 bash,所以 isReadOnlyAgent() 对它们返回 false。
Spawn 策略
// packages/coding-agent/src/task/spawn-policy.ts(简化)export function resolveSpawnPolicy(parentSpawns): ResolvedSpawnPolicy { if (parentSpawns === "*") { return { enabled: true, defaultAgent: "task", allowedAgents: null }; } const allowedAgents = parentSpawns.split(",").map(s => s.trim()); if (allowedAgents.length === 0) { return { enabled: false }; } return { enabled: true, defaultAgent: allowedAgents[0], allowedAgents };}agent 定义里的 spawns 字段控制谁能 spawn 谁:
*= 不限制explore,reviewer= 只能 spawn 这两个,且默认用explore""= 禁止 spawn
并发控制
export async function mapWithConcurrencyLimit<T, R>( items: T[], concurrency: number, fn: (item: T, index: number, signal: AbortSignal) => Promise<R>, signal?: AbortSignal,): Promise<ParallelResult<R>> { const limit = Math.max(1, Math.min(concurrency, items.length)); // worker pool 模式,最多 limit 个并发 // 一个 worker 抛错,立刻取消其他 worker}默认并发上限 32。
下一步
多 agent 解决了”谁来做”的问题,但还有一个更深层的问题:agent 怎么记住之前学过什么?
下一篇讲 omp 的记忆系统——MnemoPi。它不只是”历史对话保存”,而是一个基于向量检索的知识库,让 agent 能从过去的经验里学习。
本文是「omp 编程工具使用探讨」专栏第 6 篇。文中的使用示例来自实际项目经验,源码片段来自 oh-my-pi 仓库。