Subagent —— 进程隔离就是上下文隔离

模型和人一样,在一个拥挤的房间里没法思考。有时候你需要一个安静的小房间。


上下文是 Agent 的 RAM

先说个常识。

你写代码的时候,如果浏览器开着 50 个标签页、微信在闪、Slack 在响、IDE 里有 20 个打开的文件……你能写好代码吗?

不能。你的"上下文"被塞爆了。

Agent 也一样。模型的上下文窗口就是它的"工作记忆"。你把之前的文件内容、工具结果、对话历史全都塞在一起:

messages = [
    "帮我看看这个项目" (5K tokens)
    → 读了 10 个文件 (30K tokens)
    → 跑了 3 个测试 (10K tokens)
    → 修改了 2 个文件 (8K tokens)
    → "现在帮我写一个新功能"
    → 又读了 15 个文件 (50K tokens)
    → ...
]

100K tokens 往后,模型的推理能力明显下降。它会"忘记"对话早期做的决定,开始前后矛盾,甚至重复做已经做完的事。

s01-s03 的问题:所有工作在同一个上下文里完成,上下文越来越脏。


s04 的解法:Subagent = 给模型一个干净的房间

s04 做的事情其实很简单——模型可以 spawn 一个子代理,给它一个独立的任务,然后等它返回结果。 子代理运行在自己的上下文里,父代理的上下文不受影响。

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
        # 执行工具...
    # 只有最终文本摘要返回给父代理
    return "".join(b.text for b in response.content if hasattr(b, "text")) or "(no summary)"

注意那句注释:# fresh context。子代理的 sub_messages 从零开始。它不知道父代理之前聊了什么,不知道之前的文件内容,不知道之前的决策。它只知道你告诉它的任务。

执行过程是一个独立的 agent loop,和父代理的循环完全一样:

子代理循环:
    sub_messages = [{user: prompt}]    ← 只有任务描述
    while True:
        response = LLM(sub_messages)    ← 独立调用
        sub_messages.append(response)
        if stop:
            break
        execute tools
        sub_messages.append(results)
    return summary_text                  ← 只返回摘要

父代理拿到摘要后,子代理的上下文直接被丢弃——那些对话、工具结果、中间状态,全部消失了。

这就像是:你让一个实习生去查个资料,他查完后回来告诉你结论。你不会把他查资料时看的每一页书都塞到自己的脑子里。你只需要结论。


为什么 Subagent 比单上下文好?

做个对比:

单上下文(s01-s03 模式):

main context: [读代码A(5K)] [读代码B(3K)] [改文件A(2K)] [跑测试(8K)]
              [读代码C(4K)] [读代码D(6K)] [改文件B(3K)] [跑测试(10K)]
              [读代码E(12K)] [思考新功能(2K)] [读数据库schema(8K)]
              → 63K tokens, 模型开始犯傻

子代理模式(s04):

main context: [读代码A(5K)] [读代码B(3K)] [改文件A(2K)]
              [subagent结果: "数据库schema已分析完毕"]
              [读代码E(12K)] [思考新功能(2K)]
              → 干净的24K tokens, 模型清醒得很

subagent context: [读代码C(4K)] [读代码D(6K)] [读数据库schema(8K)]
              → 用完即焚,18K tokens不污染主上下文

这是"分工"和"一锅烩"的区别。

作者在 s04 的文件头里写了一句关键注释:

“Process isolation gives context isolation for free.”
(进程隔离自动带来了上下文隔离。)

对,这不是什么新发明。这就是操作系统的进程隔离思想——每个进程有自己的地址空间。Subagent 就是进程,messages 列表就是它的地址空间。


安全措施:子代理的权限限制

注意一个细节——子代理的工具集比父代理少:

# 父代理有 task 工具(可以 spawn 子代理)
PARENT_TOOLS = CHILD_TOOLS + [
    {"name": "task", ...}
]

# 子代理没有 task 工具 → 不能递归 spawn
CHILD_TOOLS = [
    {"name": "bash", ...},
    {"name": "read_file", ...},
    {"name": "write_file", ...},
    {"name": "edit_file", ...},
]

子代理不能 spawn 子代理。没有递归创建。

这是有意的安全设计。否则你可能会看到:

Agent spawns Subagent A
    → Subagent A spawns Subagent B
        → Subagent B spawns Subagent C
            → Subagent C spawns Subagent D
                → ...

无限递归炸掉你的 API 账单和线程池。s04 用一个简单的"子代理没有 task 工具"就杜绝了这个问题。


s09 的进化:从一次性 Subagent 到持久化 Teammate

Subagent 有一个明显的局限:它是一次性的。

spawn → work → return summary → destroyed

它没有身份。没有跨调用的记忆。没有名字,没有角色,没有"我就是上次那个干过这事的 Agent"。

s09 解决了这个问题——引入了 Teammate(队友) 系统。

Teammate lifecycle:
  spawn → WORKING → IDLE → WORKING → ... → SHUTDOWN

每个 Teammate 是持久的,有自己的名字和角色:

def spawn(self, name: str, role: str, prompt: str) -> str:
    member = {"name": name, "role": role, "status": "working"}
    self.config["members"].append(member)
    thread = threading.Thread(target=self._teammate_loop, args=(name, role, prompt))
    thread.start()
    return f"Spawned '{name}' (role: {role})"

每个 Teammate 运行在自己的线程里,有自己的 agent loop:

def _teammate_loop(self, name: str, role: str, prompt: str):
    sys_prompt = f"You are '{name}', role: {role}, at {WORKDIR}."
    messages = [{"role": "user", "content": prompt}]
    for _ in range(50):
        inbox = BUS.read_inbox(name)     # 检查邮箱
        for msg in inbox:
            messages.append({"role": "user", "content": json.dumps(msg)})
        response = client.messages.create(
            model=MODEL, system=sys_prompt, messages=messages, ...
        )
        # 执行工具、追加结果...
    self._set_status(name, "idle")

关键的区别在哪?

Subagent(s04):父代理显式调用 task 工具,等结果 → 类似"函数调用"

Teammate(s09):team lead spawn 一个队友,队友自己运行,通过邮箱通信 → 类似"微服务"

通信方式也变了——从"返回值"变成了JSONL 邮箱

class MessageBus:
    def send(self, sender: str, to: str, content: str, msg_type: str = "message"):
        msg = {"type": msg_type, "from": sender, "content": content, "timestamp": time.time()}
        inbox_path = self.dir / f"{to}.jsonl"
        with open(inbox_path, "a") as f:
            f.write(json.dumps(msg) + "\n")
        return f"Sent {msg_type} to {to}"

    def read_inbox(self, name: str) -> list:
        inbox_path = self.dir / f"{name}.jsonl"
        messages = [json.loads(line) for line in ...]
        inbox_path.write_text("")  # drain
        return messages

想想这个设计的精妙之处:

  1. 每个队友有独立邮箱 —— alice.jsonl, bob.jsonl
  2. 邮箱是文件 —— 没有任何中间件,不需要 Redis/RabbitMQ
  3. 读取即清空(drain)—— 每条消息只被处理一次
  4. append-only —— 没有锁竞争,并发安全
  5. JSONL 格式 —— 每行一个 JSON,可以 grep,可以审计

5 种消息类型,定义了队友间可能发生的所有交互:

message                  → 普通文本消息
broadcast                → 群发
shutdown_request         → 请求关闭
shutdown_response        → 同意/拒绝关闭
plan_approval_response   → 批准/拒绝计划

这些消息类型在 s10 变成了完整的状态机协议(Shutdown Protocol + Plan Approval Protocol)。


从 Subagent 到 Teammate:一条清晰的抽象路径

回头看,从 s04 到 s09 的进化路径非常清晰:

s04 Subagent:     spawn → work → return → die
                  类似一个独立的函数调用

s09 Teammate:     spawn → work → idle → work → ... → shutdown
                  类似一个微服务进程

s10 Protocols:    队友之间可以走协议:shutdown、plan_approval
                  请求 + request_id + 响应 = 异步 RPC

s11 Autonomous:   队友空闲时自动扫描任务板,认领任务
                  "我不需要被告诉做什么,我自己找活干"

每一步都在增加 Agent 的自主性持久性,但同时 Harness 的复杂度几乎没变——还是那个 while 循环,还是那个 tool dispatch map,只是多加了几行工具注册。


这个进化说明了什么?

这个 evolution 说明了一个深刻的工程道理:

AI Agent 的扩展不是通过修改 Agent 本身,而是通过给 Agent 更好的组织架构。

你没法让模型一下子变聪明 10 倍(你又不能改权重)。但你可以给模型配合伙人(Subagent),可以给模型建团队(Teammate),可以给团队设计协作协议(Protocol),可以让团队自发地找活干(Autonomous)。

每加一层组织能力,Agent 系统能处理的任务复杂度就上一个台阶。

这就是 Harness Engineering 的终极形态:你不是在写一个 Agent,你是在设计一个组织。


下集预告

s04-s09 给了 Agent 队友和团队。但还有一个基础问题没解决:上下文总会满的。

即使你有 Subagent 来分担压力,主上下文的 messages 还是会随着时间增长。s06 的三层压缩策略解决了这个问题——让 Agent 可以永远工作下去。

但在此之前,s05 先插了一课:技能加载。一个关于"不要什么都往 system prompt 里塞"的教训。

下一篇:技能加载 —— 别什么都往 System Prompt 里塞

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐