使用Claude实现异步多智能体编排

本教程基于Anthropic Python SDK和asyncio,深入剖析Claude Opus 4.8多智能体编排的两种核心模式:固定N智能体团队和异步子智能体。通过构建消息中心(Hub)和基础代理循环,实现智能体间的异步消息传递与动态生成。教程提供完整可运行的代码,展示工具调用顺序、消息流和状态管理,并指导开发者替换领域工具以扩展应用。适合有编程基础、希望掌握AI工程实践的开发者,帮助理解多智能体系统的架构设计与实现细节。

技术背景

随着大语言模型能力的增强,单一模型在处理复杂任务时面临上下文长度限制、任务分解困难等问题。多智能体编排通过将任务分配给多个专业化的智能体,实现并行处理、分工协作,从而提升效率和效果。Anthropic的Claude模型支持工具调用,结合异步编程框架asyncio,可以构建灵活的多智能体系统。本教程旨在展示两种基础编排模式,为开发者提供可复用的架构模板,解决实际应用中任务分解、消息传递和生命周期管理的问题。

核心原理

核心原理是基于消息传递的异步协作。每个智能体拥有独立的收件箱(inbox)和事件(event),通过Hub进行消息路由。智能体通过工具调用send_message发送消息,通过wait_for_message阻塞等待消息。基础代理循环(run_agent)处理模型响应,将工具结果和收件箱消息附加到下一次请求中,实现消息内联到达,避免轮询。异步子智能体模式通过create_subagents工具动态生成子任务,主代理可查询状态、收集结果并终止子代理,实现动态编排。

架构分析

系统由Hub(消息中心)、run_agent(代理循环)、工具集(BASE_TOOLS、SLEEP、SUBAGENT_TOOLS)和调度器(extra_dispatch)组成。Hub管理智能体注册、消息投递、状态跟踪和名称生成。run_agent是通用执行器,处理模型调用、工具分发和消息注入。固定团队模式中,所有智能体预先注册,并行运行,通过消息协作。异步子代理模式中,主代理动态创建子代理,子代理独立运行,通过工具与主代理交互。架构设计强调解耦:消息传递与领域逻辑分离,工具可插拔,便于扩展。

完整实战教程

本教程展示了Claude Opus 4.8系统卡中多智能体结果背后的两种多智能体编排模式的“形态”:固定N智能体团队和异步子智能体。这里没有领域任务——仅包含消息传递和子智能体机制,以便您能清楚地看到哪些工具被触发以及触发顺序,然后可以自行添加工具和任务。

所有内容均基于公开的Anthropic Python SDK和asyncio运行,使用任意API密钥,通常在三十秒内完成。

设置

%pip install -qU anthropic
import asyncio
import itertools
from collections import Counter, defaultdict

import anthropic

MODEL = "claude-opus-4-8"  # swap for the newest Claude model available to you
client = anthropic.AsyncAnthropic()  # reads ANTHROPIC_API_KEY from env

共享构建模块

消息中心

每个智能体都有一个收件箱列表和一个用于阻塞等待的 asyncio.Event。drain 清空收件箱并返回结构化消息;render 将它们格式化为文本,以便附加到工具结果中。

class Hub:
    def __init__(self):
        self.inbox: dict[str, list[dict]] = defaultdict(list)
        self.event: dict[str, asyncio.Event] = defaultdict(asyncio.Event)
        self.status: dict[str, str] = {}
        self._ids = itertools.count(1)

    def register(self, name: str):
        self.status[name] = "active"
        _ = self.inbox[name], self.event[name]

    def new_name(self, prefix="helper") -> str:
        n = f"{prefix}{next(self._ids)}"
        self.register(n)
        return n

    def post(self, sender: str, recipients: list[str], content: str) -> list[str]:
        delivered = []
        for rid in recipients:
            if rid in self.status:
                self.inbox[rid].append({"from": sender, "content": content})
                self.event[rid].set()
                delivered.append(rid)
        return delivered

    def drain(self, name: str) -> list[dict]:
        msgs, self.inbox[name] = self.inbox[name], []
        self.event[name] = asyncio.Event()
        return msgs

    @staticmethod
    def render(msgs: list[dict]) -> str:
        if not msgs:
            return ""
        body = "\n".join(
            f'<agent-message from="{m["from"]}">\n{m["content"]}\n</agent-message>' for m in msgs
        )
        return f"\n\n[Messages received while you were working:]\n{body}"

消息工具

两种模式中的每个智能体都会获得这两个工具。

SEND_MESSAGE = {
    "name": "send_message",
    "description": (
        "Send a message to one or more other agents. It will appear appended to their next tool "
        "result. This is the ONLY way to reach other agents — plain text in your turn goes nowhere."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "recipient_ids": {"type": "array", "items": {"type": "string"}, "minItems": 1},
            "content": {"type": "string"},
        },
        "required": ["recipient_ids", "content"],
    },
}
WAIT_FOR_MESSAGE = {
    "name": "wait_for_message",
    "description": (
        "Block until another agent messages you. Note: messages also arrive automatically appended "
        "to the result of ANY other tool call, so only use this when you have nothing else to do."
    ),
    "input_schema": {"type": "object", "properties": {}},
}
BASE_TOOLS = [SEND_MESSAGE, WAIT_FOR_MESSAGE]

基础代理循环

一个函数运行任何代理:一个标准的工具使用循环,通过 hub 调度 send_message / wait_for_message,通过 extra_dispatch 路由任何额外的工具,并将排空的收件箱附加到最后一个工具结果——因此代理从不轮询;消息内联到达。每次工具调用和收件箱投递都会实时打印,以便您观察编排过程;最后会打印摘要跟踪。

TRACE: dict[str, list[str]] = defaultdict(list)


def _snip(s, n=60):
    s = str(s).replace("\n", " ")
    return s if len(s) <= n else s[:n] + "…"


async def run_agent(
    hub: Hub,
    name: str,
    system: str,
    first_user_turn: str,
    tools: list = None,
    extra_dispatch=None,
    max_turns: int = 20,
) -> str:
    tools = tools or BASE_TOOLS
    extra_dispatch = extra_dispatch or {}
    messages = [{"role": "user", "content": first_user_turn}]

    try:
        for _ in range(max_turns):
            resp = await client.messages.create(
                model=MODEL,
                max_tokens=2048,
                system=system,
                tools=tools,
                messages=messages,
            )
            messages.append({"role": "assistant", "content": resp.content})

            if resp.stop_reason == "end_turn":
                hub.status[name] = "done"
                return "".join(getattr(b, "text", "") for b in resp.content)
            if resp.stop_reason != "tool_use":
                raise RuntimeError(f"unexpected stop_reason: {resp.stop_reason}")

            results = []
            for block in resp.content:
                if block.type != "tool_use":
                    continue
                TRACE[name].append(block.name)
                if block.name == "send_message":
                    rids = block.input["recipient_ids"]
                    delivered = hub.post(name, rids, block.input["content"])
                    unknown = [r for r in rids if r not in delivered]
                    out = f"delivered to {delivered}" + (f"; unknown: {unknown}" if unknown else "")
                elif block.name == "wait_for_message":
                    hub.status[name] = "idling"
                    try:
                        await asyncio.wait_for(hub.event[name].wait(), timeout=60)
                        out = "woke: new messages"
                    except TimeoutError:
                        out = "woke: 60s timeout"
                    hub.status[name] = "active"
                elif block.name in extra_dispatch:
                    out = await extra_dispatch[block.name](block)
                else:
                    out = f"error: no dispatch for {block.name}"
                print(f"  [{name}] {block.name}({_snip(block.input)}) → {_snip(out)}")
                results.append({"type": "tool_result", "tool_use_id": block.id, "content": out})

            inbox = hub.drain(name)
            for m in inbox:
                print(f"  [{name}] ← received from {m['from']}: {_snip(m['content'])}")
            if results:
                results[-1]["content"] += hub.render(inbox)  # ← the key line
            messages.append({"role": "user", "content": results})

        hub.status[name] = "done"
        return f"[{name} hit max_turns={max_turns}]"
    except Exception:
        hub.status[name] = "crashed"
        raise


def print_trace():
    for agent in sorted(TRACE):
        counts = Counter(TRACE[agent])
        print(f"  {agent}: " + ", ".join(f"{n}×{t}" for t, n in counts.most_common()))
    TRACE.clear()

第一部分 · 固定N智能体团队

三个智能体——一个主导和两个辅助——每个都被赋予一行角色描述。它们所做的只是通过send_message互相介绍自己;主导写一句总结并结束运行。没有领域工具:这纯粹是消息传递路径。

TEAM_SYSTEM = "You are {name}, one of 3 agents working together (peers: {peers})."

TASKS = {
    "lead": "You are the lead. Introduce yourself to the others. Once everyone has introduced "
    "themselves, finish with a one-sentence summary of the team.",
    "helper1": "You are a backend engineer named Ada. Introduce yourself to the others, then wait "
    "for their replies.",
    "helper2": "You are a designer named Bo. Introduce yourself to the others, then wait for their "
    "replies.",
}


async def run_team() -> str:
    hub = Hub()
    names = list(TASKS)
    for n in names:
        hub.register(n)

    helper_tasks = [
        asyncio.create_task(
            run_agent(
                hub,
                n,
                system=TEAM_SYSTEM.format(name=n, peers=[p for p in names if p != n]),
                first_user_turn=TASKS[n],
            )
        )
        for n in names[1:]
    ]
    try:
        return await run_agent(
            hub,
            "lead",
            system=TEAM_SYSTEM.format(name="lead", peers=names[1:]),
            first_user_turn=TASKS["lead"],
        )
    finally:
        for t in helper_tasks:
            t.cancel()
        await asyncio.gather(*helper_tasks, return_exceptions=True)
answer = await run_team()
print(f"\n[lead final answer]\n{answer}\n\nTool-call summary:")
print_trace()

现在给主子代理工具和一个简单的客户端 sleep(seconds) 工具。主代理生成三个辅助代理;每个辅助代理休眠 *N* 秒,通过 send_message 向主代理发送“完成”消息,然后 wait_for_message 等待进一步指令。生成调用立即返回;主代理在它们运行时检查 get_status,通过 wait_for_message 收集它们的报告,然后 kill_subagents 终止所有三个以解散它们。不涉及领域逻辑——仅完整的生成/状态/收集/终止路径。

SLEEP = {
    "name": "sleep",
    "description": "Sleep for the given number of seconds, then return.",
    "input_schema": {
        "type": "object",
        "properties": {"seconds": {"type": "integer", "minimum": 0, "maximum": 10}},
        "required": ["seconds"],
    },
}
SUBAGENT_TOOLS = [
    {
        "name": "create_subagents",
        "description": "Spawn helper subagents. Returns immediately — helpers run concurrently in the "
        "background in parallel with you. Each gets base_instruction, optionally + "
        "per_subagent_instructions[i].",
        "input_schema": {
            "type": "object",
            "properties": {
                "base_instruction": {"type": "string"},
                "per_subagent_instructions": {
                    "type": "array",
                    "items": {"type": "string"},
                    "maxItems": 10,
                },
            },
            "required": ["base_instruction"],
        },
    },
    {
        "name": "get_status",
        "description": "Status of every helper (active / idling / done / crashed).",
        "input_schema": {"type": "object", "properties": {}},
    },
    {
        "name": "kill_subagents",
        "description": "Cancel running helpers you no longer need.",
        "input_schema": {
            "type": "object",
            "properties": {
                "subagent_ids": {"type": "array", "items": {"type": "string"}, "minItems": 1}
            },
            "required": ["subagent_ids"],
        },
    },
]


async def dispatch_sleep(block) -> str:
    s = int(block.input["seconds"])
    await asyncio.sleep(s)
    return f"slept {s}s"
async def run_spawn_lead() -> str:
    hub = Hub()
    hub.register("lead")
    helpers: dict[str, asyncio.Task] = {}

    async def _create(block):
        base = block.input["base_instruction"]
        per = block.input.get("per_subagent_instructions") or [""]
        spawned = []
        for suffix in per:
            h = hub.new_name()
            helpers[h] = asyncio.create_task(
                run_agent(
                    hub,
                    h,
                    system=f"You are {h}, a helper.",
                    first_user_turn=f"{base}\n\n{suffix}".strip(),
                    tools=[SLEEP, *BASE_TOOLS],
                    extra_dispatch={"sleep": dispatch_sleep},
                )
            )
            spawned.append(h)
        return f"spawned: {', '.join(spawned)}"

    async def _status(block):
        return "\n".join(f"{n}: {s}" for n, s in hub.status.items() if n != "lead") or "(none)"

    async def _kill(block):
        killed_ids, to_await = [], []
        for sid in block.input["subagent_ids"]:
            if sid in helpers and not helpers[sid].done():
                helpers[sid].cancel()
                hub.status[sid] = "done"
                to_await.append(helpers.pop(sid))
                killed_ids.append(sid)
        await asyncio.gather(*to_await, return_exceptions=True)
        return f"cancelled: {', '.join(killed_ids)}" if killed_ids else "no matching active helpers"

    try:
        return await run_agent(
            hub,
            "lead",
            system="You are the lead.",
            first_user_turn=(
                "Spawn three helper agents. Instruct each to sleep a different number of seconds "
                "(1, 2, 3), report back to you 'done, slept Ns', then wait for your further "
                "instructions. After that, check that they're running, collect all three reports, "
                "then dismiss all three helpers. Finish with a one-line summary."
            ),
            tools=[*SUBAGENT_TOOLS, *BASE_TOOLS],
            extra_dispatch={
                "create_subagents": _create,
                "get_status": _status,
                "kill_subagents": _kill,
            },
        )
    finally:
        for t in helpers.values():
            t.cancel()
        await asyncio.gather(*helpers.values(), return_exceptions=True)
answer = await run_spawn_lead()
print(f"\n[lead final answer]\n{answer}\n\nTool-call summary:")
print_trace()

后续步骤

替换为你自己的领域工具——将它们添加到 tools 中,并在 extra_dispatch 中添加一个处理器。上面的 Hub、run_agent 以及两个团队运行器就是你所需要的全部;其余部分是你的任务和你的工具。

有关完整的工具定义参考,请参阅工具使用指南。

常见问题

常见错误包括:1) 忘记设置ANTHROPIC_API_KEY环境变量,导致认证失败;2) 模型名称错误,应使用可用的最新模型;3) 工具调用未在extra_dispatch中注册,导致'no dispatch'错误;4) 子代理未正确取消,导致资源泄漏;5) 消息未送达,可能因为收件人未注册或名称拼写错误。排查方法:检查环境变量、模型名称、工具定义和调度器映射;使用print_trace查看工具调用序列;确保所有智能体已注册。生产环境优化:使用连接池、超时控制、错误重试、日志记录和监控;考虑使用消息队列(如Redis)替代内存Hub以支持分布式部署。

企业应用扩展

开发者可以替换领域工具,将自定义工具添加到tools列表,并在extra_dispatch中提供处理函数。例如,添加网络搜索、数据库查询或文件操作工具。可以扩展Hub支持持久化消息、优先级队列或广播。也可以修改run_agent以支持更复杂的停止条件、多轮对话或人类审批。此外,可以结合LangChain或CrewAI等框架,但本教程提供了底层实现,便于深入理解。

总结

本教程基于Anthropic Python SDK和asyncio,深入剖析Claude Opus 4.8多智能体编排的两种核心模式:固定N智能体团队和异步子智能体。通过构建消息中心(Hub)和基础代理循环,实现智能体间的异步消息传递与动态生成。教程提供完整可运行的代码,展示工具调用顺序、消息流和状态管理,并指导开发者替换领域工具以扩展应用。适合有编程基础、希望掌握AI工程实践的开发者,帮助理解多智能体系统的架构设计与实现细节。

内容来源与验证

继续系统学习

站内还有更多完整实战教程

加入会员,解锁全部教程、完整代码与持续更新内容。

加入会员
查看全部AI 智能体教程