mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-05-13 03:36:40 +08:00
better doc
This commit is contained in:
@@ -1,14 +1,12 @@
|
||||
# s04: Subagents
|
||||
|
||||
> サブエージェントは新しいメッセージリストで実行され、親とファイルシステムを共有し、要約のみを返す -- 親のコンテキストをクリーンに保つ。
|
||||
`s01 > s02 > s03 > [ s04 ] s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12`
|
||||
|
||||
> *"Process isolation = context isolation"* -- サブエージェントごとに新しいmessages[]。
|
||||
|
||||
## 問題
|
||||
|
||||
エージェントが作業するにつれ、メッセージ配列は膨張する。すべてのツール呼び出し、ファイル読み取り、bash出力が蓄積されていく。20-30回のツール呼び出しの後、コンテキストウィンドウは無関係な履歴で溢れる。ちょっとした質問に答えるために500行のファイルを読むと、永久に500行がコンテキストに追加される。
|
||||
|
||||
これは探索的タスクで特に深刻だ。「このプロジェクトはどのテストフレームワークを使っているか」という質問には5つのファイルを読む必要があるかもしれないが、親エージェントには5つのファイルの内容すべては不要だ -- 「pytest with conftest.py configuration」という回答だけが必要なのだ。
|
||||
|
||||
このコースでの実用的な解決策は fresh `messages[]` 分離だ: `messages=[]`で子エージェントを生成する。子は探索し、ファイルを読み、コマンドを実行する。終了時には最終的なテキストレスポンスだけが親に返される。子のメッセージ履歴全体は破棄される。
|
||||
エージェントが作業するにつれ、messages配列は膨張し続ける。すべてのファイル読み取り、すべてのbash出力がコンテキストに永久に残る。「このプロジェクトはどのテストフレームワークを使っているか」という質問は5つのファイルを読む必要があるかもしれないが、親に必要なのは「pytest」という答えだけだ。
|
||||
|
||||
## 解決策
|
||||
|
||||
@@ -17,19 +15,18 @@ Parent agent Subagent
|
||||
+------------------+ +------------------+
|
||||
| messages=[...] | | messages=[] | <-- fresh
|
||||
| | dispatch | |
|
||||
| tool: task | ---------->| while tool_use: |
|
||||
| prompt="..." | | call tools |
|
||||
| | summary | append results |
|
||||
| result = "..." | <--------- | return last text |
|
||||
| tool: task | ----------> | while tool_use: |
|
||||
| prompt="..." | | call tools |
|
||||
| | summary | append results |
|
||||
| result = "..." | <---------- | return last text |
|
||||
+------------------+ +------------------+
|
||||
|
|
||||
Parent context stays clean.
|
||||
Subagent context is discarded.
|
||||
|
||||
Parent context stays clean. Subagent context is discarded.
|
||||
```
|
||||
|
||||
## 仕組み
|
||||
|
||||
1. 親エージェントにサブエージェント生成をトリガーする`task`ツールが追加される。子は`task`を除くすべての基本ツールを取得する(再帰的な生成は不可)。
|
||||
1. 親に`task`ツールを追加する。子は`task`を除くすべての基本ツールを取得する(再帰的な生成は不可)。
|
||||
|
||||
```python
|
||||
PARENT_TOOLS = CHILD_TOOLS + [
|
||||
@@ -37,62 +34,18 @@ PARENT_TOOLS = CHILD_TOOLS + [
|
||||
"description": "Spawn a subagent with fresh context.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"prompt": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
},
|
||||
"properties": {"prompt": {"type": "string"}},
|
||||
"required": ["prompt"],
|
||||
}},
|
||||
]
|
||||
```
|
||||
|
||||
2. サブエージェントは委譲されたプロンプトのみを含む新しいメッセージリストで開始する。ファイルシステムは共有される。
|
||||
2. サブエージェントは`messages=[]`で開始し、自身のループを実行する。最終テキストだけが親に返る。
|
||||
|
||||
```python
|
||||
def run_subagent(prompt: str) -> str:
|
||||
sub_messages = [{"role": "user", "content": prompt}]
|
||||
for _ in range(30): # safety limit
|
||||
response = client.messages.create(
|
||||
model=MODEL, system=SUBAGENT_SYSTEM,
|
||||
messages=sub_messages,
|
||||
tools=CHILD_TOOLS, max_tokens=8000,
|
||||
)
|
||||
sub_messages.append({
|
||||
"role": "assistant", "content": response.content
|
||||
})
|
||||
if response.stop_reason != "tool_use":
|
||||
break
|
||||
# execute tools, append results...
|
||||
```
|
||||
|
||||
3. 最終テキストのみが親に返される。子の30回以上のツール呼び出し履歴は破棄される。
|
||||
|
||||
```python
|
||||
return "".join(
|
||||
b.text for b in response.content if hasattr(b, "text")
|
||||
) or "(no summary)"
|
||||
```
|
||||
|
||||
4. 親はこの要約を通常のtool_resultとして受け取る。
|
||||
|
||||
```python
|
||||
if block.name == "task":
|
||||
output = run_subagent(block.input["prompt"])
|
||||
results.append({
|
||||
"type": "tool_result",
|
||||
"tool_use_id": block.id,
|
||||
"content": str(output),
|
||||
})
|
||||
```
|
||||
|
||||
## 主要コード
|
||||
|
||||
サブエージェント関数(`agents/s04_subagent.py` 110-128行目):
|
||||
|
||||
```python
|
||||
def run_subagent(prompt: str) -> str:
|
||||
sub_messages = [{"role": "user", "content": prompt}]
|
||||
for _ in range(30):
|
||||
response = client.messages.create(
|
||||
model=MODEL, system=SUBAGENT_SYSTEM,
|
||||
messages=sub_messages,
|
||||
@@ -116,6 +69,8 @@ def run_subagent(prompt: str) -> str:
|
||||
) or "(no summary)"
|
||||
```
|
||||
|
||||
子のメッセージ履歴全体(30回以上のツール呼び出し)は破棄される。親は1段落の要約を通常の`tool_result`として受け取る。
|
||||
|
||||
## s03からの変更点
|
||||
|
||||
| Component | Before (s03) | After (s04) |
|
||||
@@ -125,10 +80,6 @@ def run_subagent(prompt: str) -> str:
|
||||
| Subagent | None | `run_subagent()` function |
|
||||
| Return value | N/A | Summary text only |
|
||||
|
||||
## 設計原理
|
||||
|
||||
このセッションでは、fresh `messages[]` 分離はコンテキスト分離を近似する実用手段だ。新しい`messages[]`により、サブエージェントは親の会話履歴を持たずに開始する。トレードオフは通信オーバーヘッドで、結果を親へ圧縮して返すため詳細が失われる。これはメッセージ履歴の分離戦略であり、OSのプロセス分離そのものではない。サブエージェントの深さ制限(再帰スポーン不可)は無制限のリソース消費を防ぎ、最大反復回数は暴走した子処理の終了を保証する。
|
||||
|
||||
## 試してみる
|
||||
|
||||
```sh
|
||||
@@ -136,8 +87,6 @@ cd learn-claude-code
|
||||
python agents/s04_subagent.py
|
||||
```
|
||||
|
||||
試せるプロンプト例:
|
||||
|
||||
1. `Use a subtask to find what testing framework this project uses`
|
||||
2. `Delegate: read all .py files and summarize what each one does`
|
||||
3. `Use a task to create a new module, then verify it from here`
|
||||
|
||||
Reference in New Issue
Block a user