@oyasmi/pipiclaw 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +308 -209
- package/dist/agent/channel-runner.d.ts +3 -2
- package/dist/agent/channel-runner.js +22 -14
- package/dist/{command-extension.d.ts → agent/command-extension.d.ts} +0 -1
- package/dist/{command-extension.js → agent/command-extension.js} +1 -2
- package/dist/{commands.d.ts → agent/commands.d.ts} +0 -1
- package/dist/{commands.js → agent/commands.js} +0 -1
- package/dist/agent/index.d.ts +0 -1
- package/dist/agent/index.js +0 -1
- package/dist/agent/progress-formatter.d.ts +0 -1
- package/dist/agent/progress-formatter.js +0 -1
- package/dist/{prompt-builder.d.ts → agent/prompt-builder.d.ts} +1 -2
- package/dist/{prompt-builder.js → agent/prompt-builder.js} +0 -1
- package/dist/agent/run-queue.d.ts +0 -1
- package/dist/agent/run-queue.js +0 -1
- package/dist/agent/runner-factory.d.ts +0 -1
- package/dist/agent/runner-factory.js +0 -1
- package/dist/agent/session-events.d.ts +0 -1
- package/dist/agent/session-events.js +0 -1
- package/dist/agent/session-resource-gate.d.ts +10 -0
- package/dist/agent/session-resource-gate.js +44 -0
- package/dist/agent/type-guards.d.ts +0 -1
- package/dist/agent/type-guards.js +1 -2
- package/dist/agent/types.d.ts +1 -2
- package/dist/agent/types.js +0 -1
- package/dist/{config-loader.d.ts → agent/workspace-resources.d.ts} +3 -10
- package/dist/{config-loader.js → agent/workspace-resources.js} +3 -19
- package/dist/index.d.ts +9 -9
- package/dist/index.js +9 -9
- package/dist/log.d.ts +0 -1
- package/dist/log.js +0 -1
- package/dist/main.d.ts +0 -1
- package/dist/main.js +0 -1
- package/dist/memory/bootstrap.d.ts +0 -1
- package/dist/memory/bootstrap.js +0 -1
- package/dist/memory/candidates.d.ts +0 -1
- package/dist/memory/candidates.js +0 -1
- package/dist/memory/chinese-words.d.ts +0 -1
- package/dist/memory/chinese-words.js +64 -1
- package/dist/memory/consolidation.d.ts +0 -1
- package/dist/memory/consolidation.js +2 -3
- package/dist/memory/files.d.ts +0 -1
- package/dist/memory/files.js +0 -1
- package/dist/memory/lifecycle.d.ts +1 -2
- package/dist/memory/lifecycle.js +0 -1
- package/dist/memory/recall.d.ts +1 -1
- package/dist/memory/recall.js +77 -11
- package/dist/memory/session.d.ts +0 -1
- package/dist/memory/session.js +2 -3
- package/dist/{sidecar-worker.d.ts → memory/sidecar-worker.d.ts} +0 -1
- package/dist/{sidecar-worker.js → memory/sidecar-worker.js} +1 -2
- package/dist/models/api-keys.d.ts +7 -0
- package/dist/models/api-keys.js +16 -0
- package/dist/{model-utils.d.ts → models/utils.d.ts} +1 -2
- package/dist/{model-utils.js → models/utils.js} +0 -1
- package/dist/paths.d.ts +0 -1
- package/dist/paths.js +0 -1
- package/dist/runtime/bootstrap.d.ts +0 -1
- package/dist/runtime/bootstrap.js +2 -3
- package/dist/runtime/delivery.d.ts +0 -1
- package/dist/runtime/delivery.js +0 -1
- package/dist/runtime/dingtalk.d.ts +0 -1
- package/dist/runtime/dingtalk.js +1 -2
- package/dist/runtime/events.d.ts +0 -1
- package/dist/runtime/events.js +0 -1
- package/dist/runtime/store.d.ts +0 -1
- package/dist/runtime/store.js +0 -1
- package/dist/sandbox.d.ts +0 -1
- package/dist/sandbox.js +1 -2
- package/dist/{context.d.ts → settings.d.ts} +1 -2
- package/dist/{context.js → settings.js} +1 -2
- package/dist/{llm-json.d.ts → shared/llm-json.d.ts} +0 -1
- package/dist/{llm-json.js → shared/llm-json.js} +0 -1
- package/dist/shared/markdown-sections.d.ts +0 -1
- package/dist/shared/markdown-sections.js +0 -1
- package/dist/{shell-escape.d.ts → shared/shell-escape.d.ts} +0 -1
- package/dist/{shell-escape.js → shared/shell-escape.js} +0 -1
- package/dist/shared/text-utils.d.ts +0 -1
- package/dist/shared/text-utils.js +0 -1
- package/dist/shared/type-guards.d.ts +0 -1
- package/dist/shared/type-guards.js +0 -1
- package/dist/shared/types.d.ts +0 -1
- package/dist/shared/types.js +0 -1
- package/dist/subagents/discovery.d.ts +0 -1
- package/dist/subagents/discovery.js +1 -2
- package/dist/subagents/tool.d.ts +1 -2
- package/dist/subagents/tool.js +1 -2
- package/dist/tools/attach.d.ts +0 -1
- package/dist/tools/attach.js +0 -1
- package/dist/tools/bash.d.ts +0 -1
- package/dist/tools/bash.js +0 -1
- package/dist/tools/edit.d.ts +0 -1
- package/dist/tools/edit.js +1 -2
- package/dist/tools/index.d.ts +1 -2
- package/dist/tools/index.js +0 -1
- package/dist/tools/read.d.ts +0 -1
- package/dist/tools/read.js +1 -2
- package/dist/tools/truncate.d.ts +0 -1
- package/dist/tools/truncate.js +0 -1
- package/dist/tools/write-content.d.ts +0 -1
- package/dist/tools/write-content.js +1 -2
- package/dist/tools/write.d.ts +0 -1
- package/dist/tools/write.js +0 -1
- package/package.json +9 -3
- package/CHANGELOG.md +0 -47
- package/dist/agent/channel-runner.d.ts.map +0 -1
- package/dist/agent/channel-runner.js.map +0 -1
- package/dist/agent/index.d.ts.map +0 -1
- package/dist/agent/index.js.map +0 -1
- package/dist/agent/progress-formatter.d.ts.map +0 -1
- package/dist/agent/progress-formatter.js.map +0 -1
- package/dist/agent/run-queue.d.ts.map +0 -1
- package/dist/agent/run-queue.js.map +0 -1
- package/dist/agent/runner-factory.d.ts.map +0 -1
- package/dist/agent/runner-factory.js.map +0 -1
- package/dist/agent/session-events.d.ts.map +0 -1
- package/dist/agent/session-events.js.map +0 -1
- package/dist/agent/type-guards.d.ts.map +0 -1
- package/dist/agent/type-guards.js.map +0 -1
- package/dist/agent/types.d.ts.map +0 -1
- package/dist/agent/types.js.map +0 -1
- package/dist/agent.d.ts +0 -3
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -2
- package/dist/agent.js.map +0 -1
- package/dist/command-extension.d.ts.map +0 -1
- package/dist/command-extension.js.map +0 -1
- package/dist/commands.d.ts.map +0 -1
- package/dist/commands.js.map +0 -1
- package/dist/config-loader.d.ts.map +0 -1
- package/dist/config-loader.js.map +0 -1
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/llm-json.d.ts.map +0 -1
- package/dist/llm-json.js.map +0 -1
- package/dist/log.d.ts.map +0 -1
- package/dist/log.js.map +0 -1
- package/dist/main.d.ts.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/memory/bootstrap.d.ts.map +0 -1
- package/dist/memory/bootstrap.js.map +0 -1
- package/dist/memory/candidates.d.ts.map +0 -1
- package/dist/memory/candidates.js.map +0 -1
- package/dist/memory/chinese-words.d.ts.map +0 -1
- package/dist/memory/chinese-words.js.map +0 -1
- package/dist/memory/consolidation.d.ts.map +0 -1
- package/dist/memory/consolidation.js.map +0 -1
- package/dist/memory/files.d.ts.map +0 -1
- package/dist/memory/files.js.map +0 -1
- package/dist/memory/lifecycle.d.ts.map +0 -1
- package/dist/memory/lifecycle.js.map +0 -1
- package/dist/memory/recall.d.ts.map +0 -1
- package/dist/memory/recall.js.map +0 -1
- package/dist/memory/session.d.ts.map +0 -1
- package/dist/memory/session.js.map +0 -1
- package/dist/model-utils.d.ts.map +0 -1
- package/dist/model-utils.js.map +0 -1
- package/dist/paths.d.ts.map +0 -1
- package/dist/paths.js.map +0 -1
- package/dist/prompt-builder.d.ts.map +0 -1
- package/dist/prompt-builder.js.map +0 -1
- package/dist/runtime/bootstrap.d.ts.map +0 -1
- package/dist/runtime/bootstrap.js.map +0 -1
- package/dist/runtime/delivery.d.ts.map +0 -1
- package/dist/runtime/delivery.js.map +0 -1
- package/dist/runtime/dingtalk.d.ts.map +0 -1
- package/dist/runtime/dingtalk.js.map +0 -1
- package/dist/runtime/events.d.ts.map +0 -1
- package/dist/runtime/events.js.map +0 -1
- package/dist/runtime/store.d.ts.map +0 -1
- package/dist/runtime/store.js.map +0 -1
- package/dist/sandbox.d.ts.map +0 -1
- package/dist/sandbox.js.map +0 -1
- package/dist/shared/markdown-sections.d.ts.map +0 -1
- package/dist/shared/markdown-sections.js.map +0 -1
- package/dist/shared/text-utils.d.ts.map +0 -1
- package/dist/shared/text-utils.js.map +0 -1
- package/dist/shared/type-guards.d.ts.map +0 -1
- package/dist/shared/type-guards.js.map +0 -1
- package/dist/shared/types.d.ts.map +0 -1
- package/dist/shared/types.js.map +0 -1
- package/dist/shell-escape.d.ts.map +0 -1
- package/dist/shell-escape.js.map +0 -1
- package/dist/sidecar-worker.d.ts.map +0 -1
- package/dist/sidecar-worker.js.map +0 -1
- package/dist/subagents/discovery.d.ts.map +0 -1
- package/dist/subagents/discovery.js.map +0 -1
- package/dist/subagents/tool.d.ts.map +0 -1
- package/dist/subagents/tool.js.map +0 -1
- package/dist/tools/attach.d.ts.map +0 -1
- package/dist/tools/attach.js.map +0 -1
- package/dist/tools/bash.d.ts.map +0 -1
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/edit.d.ts.map +0 -1
- package/dist/tools/edit.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/read.d.ts.map +0 -1
- package/dist/tools/read.js.map +0 -1
- package/dist/tools/truncate.d.ts.map +0 -1
- package/dist/tools/truncate.js.map +0 -1
- package/dist/tools/write-content.d.ts.map +0 -1
- package/dist/tools/write-content.js.map +0 -1
- package/dist/tools/write.d.ts.map +0 -1
- package/dist/tools/write.js.map +0 -1
- package/docs/memory-audit.md +0 -330
- package/docs/memory-optimization-round2.md +0 -319
- package/docs/specs/001-implement-memory/memory-rfc.md +0 -297
- package/docs/specs/002-subagent/pi-subagent-analyse.txt +0 -190
- package/docs/specs/002-subagent/pi-subagent-design.txt +0 -266
- package/docs/specs/002-subagent/pi-subagent-phase1-plan.txt +0 -529
- package/docs/specs/003-improve-memory/design.md +0 -537
- package/docs/specs/003-improve-memory/interfaces-and-tests.md +0 -473
- package/docs/specs/003-improve-memory/spec.md +0 -357
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
多 Agent / Sub-Agent 模式分析
|
|
2
|
-
|
|
3
|
-
一、pi-coding-agent 当前支持到什么程度?
|
|
4
|
-
|
|
5
|
-
当前已有完整但处于 example 状态的 sub-agent 实现:
|
|
6
|
-
|
|
7
|
-
1. 进程级隔离的 SubAgent 扩展 (packages/coding-agent/examples/extensions/subagent/)
|
|
8
|
-
|
|
9
|
-
这是一个完整的 Extension,支持三种执行模式:
|
|
10
|
-
|
|
11
|
-
┌──────────┬────────────────────────────────────────────────┬────────────────────────────────┐
|
|
12
|
-
│ 模式 │ 语法 │ 描述 │
|
|
13
|
-
├──────────┼────────────────────────────────────────────────┼────────────────────────────────┤
|
|
14
|
-
│ Single │ { agent: "name", task: "..." } │ 单个子代理执行单个任务 │
|
|
15
|
-
├──────────┼────────────────────────────────────────────────┼────────────────────────────────┤
|
|
16
|
-
│ Parallel │ { tasks: [{agent, task}, ...] } │ 最多 8 个任务,并发度 4 │
|
|
17
|
-
├──────────┼────────────────────────────────────────────────┼────────────────────────────────┤
|
|
18
|
-
│ Chain │ { chain: [{agent, task: "...{previous}..."}] } │ 链式执行,前一个输出注入后一个 │
|
|
19
|
-
└──────────┴────────────────────────────────────────────────┴────────────────────────────────┘
|
|
20
|
-
|
|
21
|
-
实现方式是 spawn 独立 pi 子进程(--mode json --no-session),通过 NDJSON
|
|
22
|
-
事件流收集结果。每个子代理有完全独立的 context window。
|
|
23
|
-
|
|
24
|
-
2. Agent 发现系统 (agents.ts)
|
|
25
|
-
|
|
26
|
-
- 从 ~/.pi/agents/*.md(用户级)和 .pi/agents/*.md(项目级)加载 Agent 定义
|
|
27
|
-
- 每个 Agent 是一个带 YAML frontmatter 的 Markdown 文件,定义
|
|
28
|
-
name、description、tools、model、systemPrompt
|
|
29
|
-
- 项目级 Agent 覆盖同名的用户级 Agent
|
|
30
|
-
|
|
31
|
-
3. 轻量 Worker Agent 模式 (pipiclaw/src/memory-consolidation.ts:208-243)
|
|
32
|
-
|
|
33
|
-
pipiclaw 已经在用一种极简 sub-agent 模式:直接在进程内 new Agent() 创建一个无工具、纯文本生成的
|
|
34
|
-
worker,用于内存整理。这是最低开销的 sub-agent 形态。
|
|
35
|
-
|
|
36
|
-
4. 底层基础设施
|
|
37
|
-
|
|
38
|
-
- Agent 类完全支持独立实例化,构造参数灵活(tools、model、systemPrompt、thinkingLevel 等均可定制)
|
|
39
|
-
- AgentSession 的 Extension 系统提供 60+ 事件钩子,可以拦截/增强工具调用
|
|
40
|
-
- registerTool() API 可以在运行时注入新工具(包括"调用子代理"这个工具)
|
|
41
|
-
|
|
42
|
-
总结:核心能力已就绪,但 subagent 扩展还在 examples/ 目录,不是标准内置功能。
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
二、Multi-Agent vs Sub-Agent 的区别
|
|
46
|
-
|
|
47
|
-
这是两种根本不同的协作范式:
|
|
48
|
-
|
|
49
|
-
Sub-Agent(主从模式) Multi-Agent(对等协作模式)
|
|
50
|
-
┌───────────┐ ┌──────────┐ ┌──────────┐
|
|
51
|
-
│ 主 Agent │ │ Agent A │◄──►│ Agent B │
|
|
52
|
-
│ │ │ (编码) │ │ (测试) │
|
|
53
|
-
│ ┌──────┐ │ └──────────┘ └──────────┘
|
|
54
|
-
│ │子Agent│ │ ▲ ▲
|
|
55
|
-
│ │(工具) │ │ │ │
|
|
56
|
-
│ └──────┘ │ ┌──────────┐ │
|
|
57
|
-
└───────────┘ │ 协调层 │◄────────┘
|
|
58
|
-
└──────────┘
|
|
59
|
-
|
|
60
|
-
┌──────────┬──────────────────────────────────────────┬────────────────────────────────────────┐
|
|
61
|
-
│ 维度 │ Sub-Agent │ Multi-Agent │
|
|
62
|
-
├──────────┼──────────────────────────────────────────┼────────────────────────────────────────┤
|
|
63
|
-
│ 关系 │ 主从/调用方-被调用方 │ 对等/协作 │
|
|
64
|
-
├──────────┼──────────────────────────────────────────┼────────────────────────────────────────┤
|
|
65
|
-
│ 发起方 │ 主 Agent 通过 tool call 发起 │ 协调层编排,或 Agent 互相触发 │
|
|
66
|
-
├──────────┼──────────────────────────────────────────┼────────────────────────────────────────┤
|
|
67
|
-
│ 上下文 │ 子 Agent 只看到主 Agent 传入的 task 描述 │ 每个 Agent 有自己的完整上下文 │
|
|
68
|
-
├──────────┼──────────────────────────────────────────┼────────────────────────────────────────┤
|
|
69
|
-
│ 生命周期 │ 短暂——完成任务即销毁 │ 长期运行,可持续对话 │
|
|
70
|
-
├──────────┼──────────────────────────────────────────┼────────────────────────────────────────┤
|
|
71
|
-
│ 结果流向 │ 子 → 主(返回文本结果) │ 双向或多向(共享状态/消息传递) │
|
|
72
|
-
├──────────┼──────────────────────────────────────────┼────────────────────────────────────────┤
|
|
73
|
-
│ 决策权 │ 主 Agent 决定何时调用、给什么任务 │ 各 Agent 可自主决策 │
|
|
74
|
-
├──────────┼──────────────────────────────────────────┼────────────────────────────────────────┤
|
|
75
|
-
│ 复杂度 │ 低(现有 tool 框架直接支持) │ 高(需要消息总线、共享状态、冲突解决) │
|
|
76
|
-
├──────────┼──────────────────────────────────────────┼────────────────────────────────────────┤
|
|
77
|
-
│ 典型场景 │ "帮我搜索这个"、"审查这段代码" │ "一个写代码、一个写测试、一个做Review" │
|
|
78
|
-
└──────────┴──────────────────────────────────────────┴────────────────────────────────────────┘
|
|
79
|
-
|
|
80
|
-
对 pipiclaw 的实际建议:先做 Sub-Agent,这是投入产出比最高的路径。 Multi-Agent
|
|
81
|
-
的协调成本和调试复杂度目前不适合 DingTalk bot 场景。
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
三、在 pipiclaw 中如何设计和规划
|
|
85
|
-
|
|
86
|
-
Phase 1:内置 Sub-Agent 工具(短期,投入小)
|
|
87
|
-
|
|
88
|
-
将 examples/extensions/subagent/ 的核心能力集成到 pipiclaw 中,但做适配:
|
|
89
|
-
|
|
90
|
-
pipiclaw 适配要点:
|
|
91
|
-
1. 不能 spawn `pi` CLI 子进程(pipiclaw 无 CLI)
|
|
92
|
-
→ 改为进程内 new Agent() + 注入精简 tool 集
|
|
93
|
-
2. Agent 发现:复用 .pi/agents/*.md 机制
|
|
94
|
-
→ 或从 AGENTS.md 中解析 agent 定义
|
|
95
|
-
3. 输出回传:子 Agent 结果直接返回给主 Agent 的 tool result
|
|
96
|
-
4. 资源限制:子 Agent 的 token 消耗需要计入总量
|
|
97
|
-
|
|
98
|
-
核心实现路径:
|
|
99
|
-
|
|
100
|
-
// pipiclaw 的 sub-agent tool 骨架
|
|
101
|
-
function createSubAgentTool(parentConfig: ChannelConfig): AgentTool {
|
|
102
|
-
return {
|
|
103
|
-
name: "subagent",
|
|
104
|
-
execute: async (toolCallId, { agent, task }, signal) => {
|
|
105
|
-
const agentConfig = discoverAgents(workDir, "both")
|
|
106
|
-
.agents.find(a => a.name === agent);
|
|
107
|
-
|
|
108
|
-
// 进程内创建,比 spawn 更轻量
|
|
109
|
-
const worker = new Agent({
|
|
110
|
-
initialState: {
|
|
111
|
-
systemPrompt: agentConfig.systemPrompt,
|
|
112
|
-
model: agentConfig.model ?? parentModel,
|
|
113
|
-
tools: resolveTools(agentConfig.tools), // bash, read, write 等
|
|
114
|
-
},
|
|
115
|
-
getApiKey,
|
|
116
|
-
beforeToolCall, // 可复用主 Agent 的安全拦截
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
await worker.prompt(task);
|
|
120
|
-
await worker.waitForIdle();
|
|
121
|
-
return extractResult(worker);
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
Phase 2:Parallel Sub-Agent 支持(中期)
|
|
127
|
-
|
|
128
|
-
当 LLM 判断任务可分解时,允许并行派发多个 sub-agent:
|
|
129
|
-
|
|
130
|
-
- 实现 parallel 模式(复用 examples 中的 mapWithConcurrencyLimit)
|
|
131
|
-
- 添加 chain 模式用于流水线式任务
|
|
132
|
-
|
|
133
|
-
Phase 3:Agent 自主决策触发(长期,实验性)
|
|
134
|
-
|
|
135
|
-
让主 Agent 学会在适当场景自动使用 sub-agent,而不是用户手动指定:
|
|
136
|
-
|
|
137
|
-
- 在 SOUL.md 中定义策略(如"代码修改量超过 3 个文件时,自动启用 sub-agent 做代码审查")
|
|
138
|
-
- 或者通过 Extension 监听 tool call 事件,在特定条件下自动触发
|
|
139
|
-
|
|
140
|
-
---
|
|
141
|
-
四、目标效果
|
|
142
|
-
|
|
143
|
-
┌──────────────┬─────────────────────────────────────────┬────────────────────────────────────┐
|
|
144
|
-
│ 场景 │ 无 Sub-Agent │ 有 Sub-Agent │
|
|
145
|
-
├──────────────┼─────────────────────────────────────────┼────────────────────────────────────┤
|
|
146
|
-
│ 复杂代码任务 │ 主 Agent 在单一 context │ 拆分子任务,每个子 Agent │
|
|
147
|
-
│ │ 中处理所有文件,容易超长截断 │ 聚焦具体文件/模块 │
|
|
148
|
-
├──────────────┼─────────────────────────────────────────┼────────────────────────────────────┤
|
|
149
|
-
│ 代码审查 │ 改完即结束 │ 主 Agent 改代码 → sub-agent 自动 │
|
|
150
|
-
│ │ │ review → 反馈改进 │
|
|
151
|
-
├──────────────┼─────────────────────────────────────────┼────────────────────────────────────┤
|
|
152
|
-
│ 信息搜集 │ 串行搜索,效率低 │ 并行派 3 个 sub-agent │
|
|
153
|
-
│ │ │ 同时搜不同方向 │
|
|
154
|
-
├──────────────┼─────────────────────────────────────────┼────────────────────────────────────┤
|
|
155
|
-
│ 测试 │ 手动让 Agent 跑测试 │ 代码写完后 sub-agent │
|
|
156
|
-
│ │ │ 自动运行测试并汇总 │
|
|
157
|
-
├──────────────┼─────────────────────────────────────────┼────────────────────────────────────┤
|
|
158
|
-
│ 长对话场景 │ context window 膨胀,质量下降 │ 重计算子任务卸载到独立 context 的 │
|
|
159
|
-
│ │ │ sub-agent │
|
|
160
|
-
└──────────────┴─────────────────────────────────────────┴────────────────────────────────────┘
|
|
161
|
-
|
|
162
|
-
核心价值:context window 分治。 DingTalk bot
|
|
163
|
-
场景中,用户可能在一个频道里提出复杂的多步骤需求,sub-agent 让主 Agent
|
|
164
|
-
可以将重计算子任务卸载,保持主 context 的精简和高质量。
|
|
165
|
-
|
|
166
|
-
---
|
|
167
|
-
五、怎样决定是否启用 Sub-Agent
|
|
168
|
-
|
|
169
|
-
不应该让用户手动决定——由 Agent 自主判断,但需要给出决策框架:
|
|
170
|
-
|
|
171
|
-
适合启用 sub-agent 的条件(可写入 SOUL.md 作为 Agent 指令):
|
|
172
|
-
|
|
173
|
-
1. 任务可分解性:当前任务可以拆分成独立子问题(如"搜索 A 并搜索 B"→ parallel)
|
|
174
|
-
2. context 压力:主 context 已接近上限,或需要处理大量代码文件
|
|
175
|
-
3. 专业化需求:任务需要不同"人格"(如编码 Agent vs 审查 Agent)
|
|
176
|
-
4. 耗时操作:长时间运行的 bash 命令或大范围文件搜索
|
|
177
|
-
5. 质量保证:写代码后希望有独立视角的审查
|
|
178
|
-
|
|
179
|
-
不适合使用的场景:
|
|
180
|
-
|
|
181
|
-
1. 简单问答:用户问一个简单问题,没有调用开销的必要
|
|
182
|
-
2. 上下文密集:子任务严重依赖当前对话上下文,传递成本高
|
|
183
|
-
3. 交互式任务:需要频繁和用户确认的任务(sub-agent 是批处理模式)
|
|
184
|
-
4. 成本敏感:每个 sub-agent 都有额外的 token 开销
|
|
185
|
-
|
|
186
|
-
实现建议:在 pipiclaw 中,初期可以不做自动决策,而是:
|
|
187
|
-
- 在 AGENTS.md 中定义可用的 Agent 列表和各自能力
|
|
188
|
-
- 在 SOUL.md 中告诉主 Agent "当遇到 X 场景时,可以使用 subagent 工具"
|
|
189
|
-
- 让 LLM 基于指令自主判断——这本身就是 LLM 擅长的决策
|
|
190
|
-
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
Sub-Agent 集成方案
|
|
2
|
-
|
|
3
|
-
整体思路
|
|
4
|
-
|
|
5
|
-
现有 examples/extensions/subagent/ 是通过 spawn pi CLI 子进程 实现隔离的,但 pipiclaw 是长驻
|
|
6
|
-
DingTalk bot 进程,没有 CLI 入口。所以核心改造是:用进程内 new Agent() 替代 subprocess
|
|
7
|
-
spawn,复用同一个 Executor(sandbox)实例。
|
|
8
|
-
|
|
9
|
-
现有 subagent (pi-coding-agent CLI):
|
|
10
|
-
主 Agent → spawn("pi", ["--mode json"]) → 子进程 Agent → stdout NDJSON → 解析
|
|
11
|
-
|
|
12
|
-
pipiclaw 方案:
|
|
13
|
-
主 Agent → tool call "subagent" → new Agent({tools, systemPrompt}) → 事件订阅 → 结果收集
|
|
14
|
-
|
|
15
|
-
需要做的事情(共 5 件)
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
一、新建 src/tools/subagent.ts — Sub-Agent 工具实现
|
|
19
|
-
|
|
20
|
-
这是核心,作为一个 AgentTool 注册给主 Agent,LLM 通过 tool call 触发。
|
|
21
|
-
|
|
22
|
-
关键设计:
|
|
23
|
-
|
|
24
|
-
// src/tools/subagent.ts 骨架
|
|
25
|
-
|
|
26
|
-
interface SubAgentToolOptions {
|
|
27
|
-
executor: Executor; // 复用父 Agent 的 sandbox
|
|
28
|
-
getModel: () => Model<Api>; // 当前活跃模型
|
|
29
|
-
resolveApiKey: (model: Model<Api>) => Promise<string>;
|
|
30
|
-
workspaceDir: string; // Agent 定义文件搜索根
|
|
31
|
-
channelDir: string; // 当前频道目录
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// 工具 schema — 简化版,只支持 single 模式(Phase 1)
|
|
35
|
-
const subagentSchema = Type.Object({
|
|
36
|
-
label: Type.String({ description: "Brief description of what this subagent task does" }),
|
|
37
|
-
agent: Type.String({ description: "Name of the agent to invoke (from .pi/agents/)" }),
|
|
38
|
-
task: Type.String({ description: "Task description for the subagent" }),
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
执行流程:
|
|
42
|
-
|
|
43
|
-
1. 发现 Agent — 从 {workspaceDir}/.pi/agents/ 和 {channelDir}/agents/ 加载 .md 文件,解析
|
|
44
|
-
frontmatter 得到 AgentConfig
|
|
45
|
-
2. 构造工具集 — 根据 AgentConfig.tools 字段(如 "bash,read"),从已有的
|
|
46
|
-
createReadTool/createBashTool/... 中按名过滤
|
|
47
|
-
3. 创建 Agent 实例 — new Agent({initialState: {systemPrompt, model, tools}, ...})
|
|
48
|
-
4. 订阅事件收集结果 — 通过 agent.subscribe() 监听 message_end 事件,累积输出
|
|
49
|
-
5. 等待完成 — await agent.waitForIdle(),提取最终文本输出返回给主 Agent
|
|
50
|
-
6. 传递 abort 信号 — 父 Agent 被取消时,子 Agent 也跟着取消
|
|
51
|
-
|
|
52
|
-
与现有 memory-consolidation 中 runWorkerPrompt 的区别:
|
|
53
|
-
|
|
54
|
-
┌──────────┬────────────────────────────┬─────────────────────────────────┐
|
|
55
|
-
│ 维度 │ runWorkerPrompt │ subagent tool │
|
|
56
|
-
├──────────┼────────────────────────────┼─────────────────────────────────┤
|
|
57
|
-
│ tools │ [](纯文本生成) │ 按配置注入 bash/read/edit/write │
|
|
58
|
-
├──────────┼────────────────────────────┼─────────────────────────────────┤
|
|
59
|
-
│ 触发方 │ 系统自动(compaction 时) │ LLM 自主决策(tool call) │
|
|
60
|
-
├──────────┼────────────────────────────┼─────────────────────────────────┤
|
|
61
|
-
│ 输出 │ 内部消费(更新 MEMORY.md) │ 返回给主 Agent 作为 tool result │
|
|
62
|
-
├──────────┼────────────────────────────┼─────────────────────────────────┤
|
|
63
|
-
│ 生命周期 │ fire-and-forget │ 可被 abort │
|
|
64
|
-
└──────────┴────────────────────────────┴─────────────────────────────────┘
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
二、新建 src/agents.ts — Agent 发现与加载
|
|
68
|
-
|
|
69
|
-
从 examples/extensions/subagent/agents.ts 提取核心逻辑,适配 pipiclaw 的目录结构:
|
|
70
|
-
|
|
71
|
-
搜索路径(优先级从低到高):
|
|
72
|
-
~/.pi/agents/*.md → 用户全局 Agent 定义
|
|
73
|
-
{workspaceDir}/.pi/agents/*.md → 项目级 Agent 定义(覆盖同名)
|
|
74
|
-
|
|
75
|
-
这个文件基本可以直接复用 examples/extensions/subagent/agents.ts 的 discoverAgents() +
|
|
76
|
-
loadAgentsFromDir() + frontmatter 解析逻辑,只需要:
|
|
77
|
-
- 去掉对 getAgentDir() 的依赖(pipiclaw 有自己的目录约定)
|
|
78
|
-
- 把 findNearestProjectAgentsDir 简化为直接用已知的 workspaceDir
|
|
79
|
-
|
|
80
|
-
Agent 定义文件格式(与现有一致):
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
name: reviewer
|
|
84
|
-
description: Reviews code changes for quality and correctness
|
|
85
|
-
model: claude-sonnet-4-20250514
|
|
86
|
-
tools: bash,read
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
You are a code reviewer. Given a task, review the relevant code...
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
三、修改 src/tools/index.ts — 工具注册集成
|
|
93
|
-
|
|
94
|
-
当前 createPipiclawTools() 返回固定的 4 个工具。需要改为:
|
|
95
|
-
|
|
96
|
-
// 改造前
|
|
97
|
-
export function createPipiclawTools(executor: Executor): AgentTool<any>[] {
|
|
98
|
-
return [createReadTool(executor), createBashTool(executor), createEditTool(executor),
|
|
99
|
-
createWriteTool(executor)];
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 改造后
|
|
103
|
-
export function createPipiclawTools(executor: Executor): AgentTool<any>[] {
|
|
104
|
-
return [createReadTool(executor), createBashTool(executor), createEditTool(executor),
|
|
105
|
-
createWriteTool(executor)];
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 新增:按名称过滤工具子集(供 sub-agent 使用)
|
|
109
|
-
export function filterToolsByName(allTools: AgentTool<any>[], names: string[]): AgentTool<any>[] {
|
|
110
|
-
const nameSet = new Set(names);
|
|
111
|
-
return allTools.filter(t => nameSet.has(t.name));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// 新增:创建包含 subagent 的完整工具集(供主 Agent 使用)
|
|
115
|
-
export function createPipiclawToolsWithSubAgent(
|
|
116
|
-
executor: Executor,
|
|
117
|
-
subagentOptions: SubAgentToolOptions,
|
|
118
|
-
): AgentTool<any>[] {
|
|
119
|
-
const baseTools = createPipiclawTools(executor);
|
|
120
|
-
const subagentTool = createSubAgentTool(subagentOptions, baseTools);
|
|
121
|
-
return [...baseTools, subagentTool];
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
关键设计决策:sub-agent 不能递归调用 subagent 工具。 createSubAgentTool 内部只传入 baseTools(4
|
|
125
|
-
个基础工具),不包含 subagent 自身,天然防止递归。
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
四、修改 src/agent.ts (ChannelRunner) — 接入主流程
|
|
129
|
-
|
|
130
|
-
在 ChannelRunner 构造器中,将 subagent 工具加入主 Agent 的工具集:
|
|
131
|
-
|
|
132
|
-
// agent.ts 中现有的工具创建:
|
|
133
|
-
// const tools = createPipiclawTools(executor);
|
|
134
|
-
|
|
135
|
-
// 改为:
|
|
136
|
-
const baseTools = createPipiclawTools(executor);
|
|
137
|
-
const subagentTool = createSubAgentTool({
|
|
138
|
-
executor,
|
|
139
|
-
getModel: () => this.activeModel,
|
|
140
|
-
resolveApiKey: (model) => getApiKeyForModel(this.modelRegistry, model),
|
|
141
|
-
workspaceDir: this.workspaceDir,
|
|
142
|
-
channelDir,
|
|
143
|
-
}, baseTools);
|
|
144
|
-
const tools = [...baseTools, subagentTool];
|
|
145
|
-
|
|
146
|
-
变更范围极小 — 只改工具数组的组装方式,Agent 构造、AgentSession 构造、Extension 注册等全部不变。
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
五、修改 src/prompt-builder.ts — 告诉 LLM 如何使用 sub-agent
|
|
150
|
-
|
|
151
|
-
在 buildAppendSystemPrompt() 中新增一个 section,告知主 Agent sub-agent 的存在和使用策略:
|
|
152
|
-
|
|
153
|
-
## Sub-Agents
|
|
154
|
-
|
|
155
|
-
You have a `subagent` tool that delegates tasks to specialized agents.
|
|
156
|
-
Available agents are discovered from `.pi/agents/*.md` files.
|
|
157
|
-
|
|
158
|
-
Use sub-agents when:
|
|
159
|
-
- The task can be decomposed into independent sub-problems
|
|
160
|
-
- You need a fresh context window for a heavy computation
|
|
161
|
-
- A specialized agent (reviewer, researcher) would produce better results
|
|
162
|
-
- The current context is getting long and you want to offload work
|
|
163
|
-
|
|
164
|
-
Do NOT use sub-agents for:
|
|
165
|
-
- Simple questions or short tasks
|
|
166
|
-
- Tasks that heavily depend on current conversation context
|
|
167
|
-
- Interactive tasks requiring user confirmation
|
|
168
|
-
|
|
169
|
-
Each sub-agent runs with an isolated context — it cannot see your conversation history.
|
|
170
|
-
You must provide sufficient context in the `task` parameter.
|
|
171
|
-
|
|
172
|
-
同时,如果 workspace 下存在 .pi/agents/ 目录,可以在 prompt 中列出可用 Agent 名称和描述,帮助 LLM
|
|
173
|
-
决策。
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
数据流总览
|
|
177
|
-
|
|
178
|
-
用户 (DingTalk)
|
|
179
|
-
│
|
|
180
|
-
▼
|
|
181
|
-
主 Agent (ChannelRunner)
|
|
182
|
-
│ tools: [read, bash, edit, write, subagent]
|
|
183
|
-
│
|
|
184
|
-
│ LLM 决定调用 subagent({agent: "reviewer", task: "Review PR #42"})
|
|
185
|
-
│
|
|
186
|
-
▼
|
|
187
|
-
subagent tool execute()
|
|
188
|
-
│
|
|
189
|
-
├─ discoverAgents(workspaceDir) → 找到 "reviewer" 配置
|
|
190
|
-
│
|
|
191
|
-
├─ filterToolsByName(baseTools, ["bash", "read"]) → 子工具集
|
|
192
|
-
│
|
|
193
|
-
├─ new Agent({
|
|
194
|
-
│ systemPrompt: reviewer.systemPrompt,
|
|
195
|
-
│ model: reviewer.model ?? parentModel,
|
|
196
|
-
│ tools: filteredTools,
|
|
197
|
-
│ thinkingLevel: "off",
|
|
198
|
-
│ })
|
|
199
|
-
│
|
|
200
|
-
├─ agent.subscribe(event => { 收集 message_end 事件 })
|
|
201
|
-
│
|
|
202
|
-
├─ await agent.prompt(task)
|
|
203
|
-
├─ await agent.waitForIdle()
|
|
204
|
-
│
|
|
205
|
-
├─ 提取最终文本输出
|
|
206
|
-
│
|
|
207
|
-
└─ return { content: [{type: "text", text: output}] }
|
|
208
|
-
│
|
|
209
|
-
▼
|
|
210
|
-
主 Agent 收到 tool result,继续推理
|
|
211
|
-
|
|
212
|
-
---
|
|
213
|
-
需要注意的设计要点
|
|
214
|
-
|
|
215
|
-
1. 工具实例共享安全性
|
|
216
|
-
|
|
217
|
-
sub-agent 和主 Agent 共享同一个 Executor 实例。这是安全的,因为:
|
|
218
|
-
- Executor.exec() 是无状态的(每次 spawn 新进程)
|
|
219
|
-
- Docker sandbox 本身提供了文件系统隔离
|
|
220
|
-
- Host 模式下两者操作相同的工作目录,这是预期行为
|
|
221
|
-
|
|
222
|
-
2. API Key 与 Model 解析
|
|
223
|
-
|
|
224
|
-
sub-agent 可以指定不同的 model(如主 Agent 用 opus,子 Agent 用 sonnet 降低成本)。API key
|
|
225
|
-
通过同一个 ModelRegistry 解析,不需要额外配置。
|
|
226
|
-
|
|
227
|
-
3. Token 消耗可见性
|
|
228
|
-
|
|
229
|
-
sub-agent 的 token 消耗不会自动体现在主 Agent 的 usage 统计中。可以在 tool result 的 details
|
|
230
|
-
中附带 usage 信息,并在 /session 命令中展示。这个是增强项,不阻塞 Phase 1。
|
|
231
|
-
|
|
232
|
-
4. 超时与取消
|
|
233
|
-
|
|
234
|
-
- 主 Agent 的 AbortSignal 通过 tool execute 的 signal 参数传入
|
|
235
|
-
- sub-agent 创建时用同一个 signal,或创建子 AbortController 链接到父 signal
|
|
236
|
-
- DingTalk 用户发新消息中断时,整条链路都能正确取消
|
|
237
|
-
|
|
238
|
-
5. Phase 2 扩展路径
|
|
239
|
-
|
|
240
|
-
Phase 1 只做 single 模式。后续扩展:
|
|
241
|
-
- Parallel 模式:schema 加 tasks 数组,复用 mapWithConcurrencyLimit 模式,并发创建多个 Agent
|
|
242
|
-
- Chain 模式:schema 加 chain 数组,顺序执行,{previous} 占位符替换
|
|
243
|
-
- Streaming 更新:通过 onUpdate 回调实时推送子 Agent 进度到 DingTalk 卡片
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
文件变更清单
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
┌───────────────────────┬──────┬────────────────────────────────────────────────┐
|
|
250
|
-
│ 文件 │ 操作 │ 说明 │
|
|
251
|
-
├───────────────────────┼──────┼────────────────────────────────────────────────┤
|
|
252
|
-
│ src/tools/subagent.ts │ 新建 │ Sub-agent 工具核心实现 │
|
|
253
|
-
├───────────────────────┼──────┼────────────────────────────────────────────────┤
|
|
254
|
-
│ src/agents.ts │ 新建 │ Agent 发现与加载(从 examples 提取) │
|
|
255
|
-
├───────────────────────┼──────┼────────────────────────────────────────────────┤
|
|
256
|
-
│ src/tools/index.ts │ 修改 │ 新增 filterToolsByName,导出 subagent 相关函数 │
|
|
257
|
-
├───────────────────────┼──────┼────────────────────────────────────────────────┤
|
|
258
|
-
│ src/agent.ts │ 修改 │ ChannelRunner 中组装工具时加入 subagent │
|
|
259
|
-
├───────────────────────┼──────┼────────────────────────────────────────────────┤
|
|
260
|
-
│ src/prompt-builder.ts │ 修改 │ 新增 sub-agent 使用指南 section │
|
|
261
|
-
└───────────────────────┴──────┴────────────────────────────────────────────────┘
|
|
262
|
-
|
|
263
|
-
不需要改动的部分: AgentSession 构造、Extension
|
|
264
|
-
系统、MemoryLifecycle、DingTalkBot、DeliveryController、Store、Sandbox — 全部保持不变。
|
|
265
|
-
|
|
266
|
-
这个方案的核心优势是侵入性极低:本质上就是多注册了一个 tool,其余架构完全不动。
|