ag2-network-quickstart

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

AG2 Network — Quickstart

AG2 Network — 快速入门

The network turns a collection of
Agent
instances into a coordinated multi-agent system. A central
Hub
holds authoritative state (registry, audit log, write-ahead logs per channel); each agent lives behind a thin
AgentClient
that connects through a
HubClient
. Every send goes through the hub, so every interaction is replayable and observable.
The network is opt-in. Bare
Agent
continues to work standalone — the network only activates when you import from
ag2.network
.
该网络可将多个
Agent
实例转化为协同工作的多Agent系统。中央
Hub
存储权威状态(注册表、审计日志、每个通道的预写日志);每个Agent都由一个轻量的
AgentClient
封装,通过
HubClient
连接到Hub。所有消息发送都经过Hub,因此每一次交互都可重放和监控。
该网络为可选启用。独立的
Agent
可继续单独运行——只有当你从
ag2.network
导入相关模块时,网络功能才会激活。

When to use

使用场景

Load this skill whenever the user wants two or more agents to interact. Concrete trigger phrases:
  • "Have two agents talk to each other"
  • "Set up a multi-agent system / multi-agent chat / agent network"
  • "Agents that can call each other"
  • "Replace the classic
    GroupChat
    /
    ConversableAgent.handoffs
    "
  • "Add a registry / audit trail / shared inbox for my agents"
Not a network task: if the user is asking one agent to spawn its own sub-tasks (recursive task fan-out, parallel sub-task execution), use
ag2-subagent-delegation
instead. The network is for distinct, registered agents collaborating.
当用户需要让两个或更多Agent进行交互时,加载此技能。具体触发场景:
  • "让两个Agent互相对话"
  • "搭建多Agent系统/多Agent聊天/Agent网络"
  • "可互相调用的Agent"
  • "替换传统的
    GroupChat
    /
    ConversableAgent.handoffs
    "
  • "为我的Agent添加注册表/审计追踪/共享收件箱"
**不属于网络任务的场景:**如果用户要求单个Agent生成自己的子任务(递归任务分发、并行子任务执行),请使用
ag2-subagent-delegation
。本网络适用于独立注册的Agent之间的协作。

Mental model

心智模型

                        ┌────────────────┐
                        │      Hub       │  ←── audit log, registry, channels,
                        │ ── adapters ── │       WAL, sweepers, expectation
                        │ ── channels ── │       evaluators
                        │ ── audit log ──│
                        └────────┬───────┘
                                 │  in-process duplex (LocalLink)
              ┌──────────────────┼──────────────────┐
              ▼                  ▼                  ▼
        ┌──────────┐       ┌──────────┐       ┌──────────┐
        │AgentClient│      │AgentClient│      │AgentClient│
        │  alice    │      │   bob     │      │   carol   │
        │  Agent    │      │  Agent    │      │  Agent    │
        └──────────┘       └──────────┘       └──────────┘
ConceptLives inPurpose
Hub
One per networkAuthoritative state — registry, audit log, channel table, WAL, sweepers
Passport
Hub registryStable identity (
name
, hub-stamped
agent_id
, optional
owner
,
model
)
Resume
Hub registryCapability claims plus hub-mutated
observed
track record
HubClient
One per processPer-process broker; manages registration
AgentClient
One per registered AgentWraps the Agent; sends envelopes, runs the notify handler
Envelope
The wire formatHub-stamped record of one event in one channel
Channel
Created by
agent_client.open(...)
A bounded multi-party exchange governed by an adapter
The hub assigns the
agent_id
at registration. Use it (
alice.agent_id
) for routing rather than the human-readable name.
                        ┌────────────────┐
                        │      Hub       │  ←── 审计日志、注册表、通道、
                        │ ── adapters ── │       WAL、清理器、预期
                        │ ── channels ── │       评估器
                        │ ── audit log ──│
                        └────────┬───────┘
                                 │  进程内双向通信(LocalLink)
              ┌──────────────────┼──────────────────┐
              ▼                  ▼                  ▼
        ┌──────────┐       ┌──────────┐       ┌──────────┐
        │AgentClient│      │AgentClient│      │AgentClient│
        │  alice    │      │   bob     │      │   carol   │
        │  Agent    │      │  Agent    │      │  Agent    │
        └──────────┘       └──────────┘       └──────────┘
概念所属位置用途
Hub
每个网络一个存储权威状态——注册表、审计日志、通道表、WAL、清理器
Passport
Hub注册表稳定身份标识(
name
、Hub标记的
agent_id
、可选的
owner
、
model
)
Resume
Hub注册表能力声明加上Hub更新的
observed
行为记录
HubClient
每个进程一个进程级代理;管理注册流程
AgentClient
每个已注册Agent一个封装Agent;发送消息包,运行通知处理器
Envelope
有线格式Hub标记的单个通道内的事件记录
Channel
通过
agent_client.open(...)
创建
由适配器管控的多方交互边界
Hub会在注册时分配
agent_id
。请使用该ID(如
alice.agent_id
)进行路由,而非人类可读的名称。

The smallest possible network

最简网络示例

A single-process hub, two agents, a
consulting
channel (strict 1Q1R, auto-closes after the reply):
python
import asyncio

from ag2 import Agent
from ag2.config import AnthropicConfig
from ag2.knowledge import MemoryKnowledgeStore
from ag2.network import (
    EV_CHANNEL_CLOSED,
    EV_TEXT,
    Hub,
    HubClient,
    LocalLink,
    Passport,
    Resume,
)


async def main() -> None:
    config = AnthropicConfig(model="claude-sonnet-4-6")

    # 1. Boot the hub. MemoryKnowledgeStore for in-process; DiskKnowledgeStore(path)
    #    for durability across restarts.
    hub = await Hub.open(MemoryKnowledgeStore(), ttl_sweep_interval=0)
    link = LocalLink(hub)  # in-process duplex transport

    # 2. One HubClient per process boundary; here we use two for clarity.
    alice_hc = HubClient(link, hub=hub)
    bob_hc = HubClient(link, hub=hub)

    # 3. Register each Agent. The hub stamps the agent_id and returns an AgentClient.
    #    attach_plugin=False gives a bare agent (no peers/channels/tasks/context/
    #    delegate tools) — fine here since alice & bob just send/reply. The `say`
    #    tool is offered per-turn by the adapter, not the plugin (see below).
    alice = await alice_hc.register(
        Agent("alice", prompt="Ask one focused question and stop.", config=config),
        Passport(name="alice"),
        Resume(),
        attach_plugin=False,
    )
    bob = await bob_hc.register(
        Agent("bob", prompt="Answer in one short sentence.", config=config),
        Passport(name="bob"),
        Resume(),
        attach_plugin=False,
    )

    # 4. Open a consulting channel. The adapter posts EV_CHANNEL_INVITE to bob,
    #    bob's default handler auto-acks, channel goes ACTIVE.
    channel = await alice.open(type="consulting", target="bob")
    await channel.send(
        "What's the single most important property of a distributed system?",
        audience=[bob.agent_id],
    )

    # 5. Bob's default handler probes can_send, runs Agent.ask on the inbound
    #    text, sends the reply. ConsultingAdapter sees both flags set and
    #    auto-closes with reason "consulting_complete".
    close_env = await alice.wait_for_channel_event(
        channel_id=channel.channel_id,
        predicate=lambda e: e.event_type == EV_CHANNEL_CLOSED,
        timeout=60.0,
    )
    print(f"closed: {close_env.event_data.get('reason')!r}")

    # 6. Replay the conversation from the hub's WAL.
    wal = await hub.read_wal(channel.channel_id)
    for env in wal:
        if env.event_type == EV_TEXT:
            speaker = "alice" if env.sender_id == alice.agent_id else "bob"
            print(f"{speaker}: {env.event_data['text']}")

    await alice_hc.close()
    await bob_hc.close()
    await hub.close()


asyncio.run(main())
That's the entire mandatory surface. Everything else below is variation.
单进程Hub、两个Agent、一个
consulting
通道(严格一问一答,回复后自动关闭):
python
import asyncio

from ag2 import Agent
from ag2.config import AnthropicConfig
from ag2.knowledge import MemoryKnowledgeStore
from ag2.network import (
    EV_CHANNEL_CLOSED,
    EV_TEXT,
    Hub,
    HubClient,
    LocalLink,
    Passport,
    Resume,
)


async def main() -> None:
    config = AnthropicConfig(model="claude-sonnet-4-6")

    # 1. 启动Hub。使用MemoryKnowledgeStore实现进程内存储;如需跨重启持久化,使用DiskKnowledgeStore(path)
    hub = await Hub.open(MemoryKnowledgeStore(), ttl_sweep_interval=0)
    link = LocalLink(hub)  # 进程内双向传输

    # 2. 每个进程边界对应一个HubClient;此处为清晰起见使用两个
    alice_hc = HubClient(link, hub=hub)
    bob_hc = HubClient(link, hub=hub)

    # 3. 注册每个Agent。Hub会标记agent_id并返回AgentClient。
    #    attach_plugin=False表示使用裸Agent(无节点/通道/任务/上下文/
    #    委托工具)——此处适用,因为alice和bob仅需发送/回复消息。`say`
    #    工具由适配器按轮次提供,而非插件(见下文)。
    alice = await alice_hc.register(
        Agent("alice", prompt="提出一个明确的问题后停止。", config=config),
        Passport(name="alice"),
        Resume(),
        attach_plugin=False,
    )
    bob = await bob_hc.register(
        Agent("bob", prompt="用一句话简短回答。", config=config),
        Passport(name="bob"),
        Resume(),
        attach_plugin=False,
    )

    # 4. 打开一个consulting通道。适配器会向bob发送EV_CHANNEL_INVITE事件,
    #    bob的默认处理器会自动确认,通道进入ACTIVE状态。
    channel = await alice.open(type="consulting", target="bob")
    await channel.send(
        "分布式系统最重要的单一特性是什么?",
        audience=[bob.agent_id],
    )

    # 5. Bob的默认处理器会检查can_send,对入站文本运行Agent.ask,发送回复。
    #    ConsultingAdapter检测到两个标记都已设置,会自动关闭通道,原因是"consulting_complete"。
    close_env = await alice.wait_for_channel_event(
        channel_id=channel.channel_id,
        predicate=lambda e: e.event_type == EV_CHANNEL_CLOSED,
        timeout=60.0,
    )
    print(f"closed: {close_env.event_data.get('reason')!r}")

    # 6. 从Hub的WAL中重放对话内容。
    wal = await hub.read_wal(channel.channel_id)
    for env in wal:
        if env.event_type == EV_TEXT:
            speaker = "alice" if env.sender_id == alice.agent_id else "bob"
            print(f"{speaker}: {env.event_data['text']}")

    await alice_hc.close()
    await bob_hc.close()
    await hub.close()


asyncio.run(main())
以上是所有必备内容。以下所有内容均为扩展变体。

Picking a channel adapter

选择通道适配器

Every channel is governed by an adapter that defines participants, turn order, and termination. Four adapters ship; pick one when you call
agent_client.open(type=..., target=...)
.
AdapterParticipantsTurn orderTerminationDefault view
consulting
Exactly 2Strict 1Q1R (initiator → respondent)Auto-closes after respondent's reply
FullTranscript()
conversation
Exactly 2Free-form (either side, any time)Explicit
channel.close()
or TTL
WindowedSummary(recent_n=10)
discussion
2+Round-robin via
ordering="round_robin"
Explicit close or TTL
WindowedSummary(recent_n=N*2)
workflow
2+Declarative
TransitionGraph
Graph terminates (
TerminateTarget
/
max_turns
)
WindowedSummary(recent_n=N*2)
This skill covers
consulting
and
conversation
(the 2-party adapters).
For the others:
  • N-party round-robin → load
    ag2-network-discussion
    .
  • Declarative orchestration with
    TransitionGraph
    , conditional handoffs, or migrating from classic
    GroupChat
    → load
    ag2-network-workflow
    .
每个通道都由一个适配器管控,适配器定义了参与者、轮次顺序和终止规则。内置四种适配器;调用
agent_client.open(type=..., target=...)
时选择其一。
适配器参与者数量轮次顺序终止方式默认视图
consulting
恰好2个严格一问一答(发起方 → 回应方)回应方回复后自动关闭
FullTranscript()
conversation
恰好2个自由形式(任意一方可随时发言)显式调用
channel.close()
或超时
WindowedSummary(recent_n=10)
discussion
2个及以上通过
ordering="round_robin"
实现轮询
显式关闭或超时
WindowedSummary(recent_n=N*2)
workflow
2个及以上声明式
TransitionGraph
图终止(
TerminateTarget
/
max_turns
)
WindowedSummary(recent_n=N*2)
**本技能涵盖
consulting
和
conversation
(双方适配器)。**其他适配器请参考:
  • 多方轮询 → 加载**
    ag2-network-discussion
    **。
  • 使用
    TransitionGraph
    进行声明式编排、条件切换,或从传统
    GroupChat
    迁移 → 加载**
    ag2-network-workflow
    **。

Plugin tools vs. adapter tools —
attach_plugin
and
say

插件工具 vs 适配器工具 —
attach_plugin
和
say

HubClient.register(...)
attaches
NetworkPlugin
by default. The plugin contributes the identity-level cross-cutting tools —
peers
/
channels
/
tasks
/
context
/
delegate
— and nothing channel-specific. Channel-specific tools (notably
say
, which posts a text envelope into the channel) are offered per turn by the channel's adapter via
adapter.tools_for(...)
, regardless of
attach_plugin
:
AdapterOffers
say
?
consulting
only to the participant whose turn it is in the 1Q1R
conversation
always (no turn order)
discussion
only to
expected_next_speaker
workflow
never — routing is your
@tool
handoff functions
So
attach_plugin=False
no longer means "no
say
" — it means "no
delegate
/
peers
/
channels
/
tasks
/
context
". Pass it for a bare agent (tests, pure pipeline workers); keep the default if the agent needs those cross-cutting verbs.
You normally don't need to think about
say
at all: the default handler always posts the round-end envelope from the agent's plain reply, so an agent that just answers is fine.
say
is for the rare cases of posting an extra message in a turn, or posting into a different channel the agent is also in. (Edge case to avoid: an agent on a
consulting
channel that calls
say(...)
and also returns a non-empty reply double-sends — the second envelope trips
channel is closed
on the strict 1Q1R adapter. If you don't want the agent calling
say
, just don't prompt it to.) Full detail:
ag2-network-tools-and-views
→ "Adapter-owned tools (
tools_for
)".
HubClient.register(...)
默认会附加
NetworkPlugin
。该插件提供身份级跨域工具——
peers
/
channels
/
tasks
/
context
/
delegate
——不包含通道特定工具。通道特定工具(尤其是
say
,用于向通道中发送文本消息包)由通道的适配器按轮次通过
adapter.tools_for(...)
提供
,与
attach_plugin
无关:
适配器是否提供
say
?
consulting
仅轮到发言的参与者可用
conversation
始终可用(无轮次限制)
discussion
仅
expected_next_speaker
可用
workflow
不可用——路由由你的
@tool
切换函数控制
因此
attach_plugin=False
不再意味着"无
say
工具"——而是"无
delegate
/
peers
/
channels
/
tasks
/
context
工具"。对于裸Agent(测试、纯流水线工作者)可设置该参数;如果Agent需要这些跨域工具,请保持默认值。
通常你无需关注
say
工具:默认处理器总会在轮次结束时发送Agent纯回复对应的消息包,因此仅需回复的Agent可正常工作。
say
工具适用于少数场景:在一轮中发送额外消息,或向Agent所在的其他通道发送消息。(需避免的边缘情况:
consulting
通道中的Agent同时调用
say(...)
并返回非空回复会导致重复发送——第二个消息包会触发严格一问一答适配器的"channel is closed"错误。如果不希望Agent调用
say
,只需在提示词中明确说明即可。)详细内容:
ag2-network-tools-and-views
→ "适配器专属工具(
tools_for
)"。

consulting
— strict 1Q1R

consulting
— 严格一问一答

The example above. Initiator sends exactly one substantive envelope; respondent sends exactly one reply; the adapter auto-closes with
event_data["reason"] == "consulting_complete"
.
State on the hub side:
python
@dataclass(slots=True)
class ConsultingState:
    initiator_sent: bool = False
    respondent_replied: bool = False
ConsultingAdapter.validate_send
rejects:
  • Out-of-order sends (respondent speaking before initiator's first envelope).
  • Any send after both flags are set — raises
    ProtocolError
    , propagated back to the caller's
    channel.send(...)
    .
Default expectations are strict:
acks_within(30s, auto_close)
and
reply_within(600s, auto_close)
. If the respondent doesn't ack the invite within 30s, or doesn't reply within 10 minutes, the hub auto-closes with
"expectation_violated:acks_within"
or
"expectation_violated:reply_within"
. Tune these via
ag2-network-governance
.
如上述示例。发起方发送恰好一条实质性消息包;回应方发送恰好一条回复;适配器自动关闭通道,原因是
event_data["reason"] == "consulting_complete"
。
Hub端的状态:
python
@dataclass(slots=True)
class ConsultingState:
    initiator_sent: bool = False
    respondent_replied: bool = False
ConsultingAdapter.validate_send
会拒绝以下情况:
  • 顺序错误的发送(发起方发送第一条消息包前,回应方发言)。
  • 两个标记都已设置后的任何发送——会抛出
    ProtocolError
    ,并传播回调用者的
    channel.send(...)
    。
默认预期规则严格:
acks_within(30s, auto_close)
和
reply_within(600s, auto_close)
。如果回应方未在30秒内确认邀请,或未在10分钟内回复,Hub会自动关闭通道,原因是
"expectation_violated:acks_within"
或
"expectation_violated:reply_within"
。可通过
ag2-network-governance
调整这些规则。

conversation
— free-form 2-party

conversation
— 自由双方对话

Either side sends at any time, any order. The adapter never auto-closes; you halt via
channel.close()
or rely on TTL.
python
channel = await alice.open(type="conversation", target="bob")
await channel.send("Hi bob, what's a good first ML concept to learn?")
Both default handlers run
Agent.ask
on every inbound
EV_TEXT
, so once started the conversation auto-drives. Two halt patterns:
  1. Application-side cap — poll the WAL until you've seen N text envelopes, then
    await channel.close()
    .
  2. Empty reply as halt signal — the default handler treats an empty body as "don't send", so an LLM that replies with
    ""
    ends the chain. Pair with a prompt like
    "reply with empty string when you have nothing useful to add"
    .
ConversationAdapter.validate_send
only checks "is the sender a participant?" — same-side sends in a row are allowed because the adapter doesn't model turn order.
Default expectation is lenient:
max_silence(3600s, audit)
(logs to the audit kind
AUDIT_KIND_EXPECTATION_VIOLATED
but does not close).
Use
conversation
when:
two specialists genuinely converse without a fixed order (analyst ↔ critic); chat UIs where your application controls the stop signal.
Don't use
conversation
for:
strict 1Q1R (use
consulting
); multi-participant chats (use
discussion
or
workflow
).
任意一方可随时发送消息,无顺序限制。适配器从不自动关闭;你需通过
channel.close()
或依赖超时来终止对话。
python
channel = await alice.open(type="conversation", target="bob")
await channel.send("嗨Bob,机器学习的第一个核心概念应该学什么?")
两个默认处理器都会对每个入站
EV_TEXT
事件运行
Agent.ask
,因此对话启动后会自动进行。两种终止模式:
  1. 应用端限制——轮询WAL直到看到N条文本消息包,然后调用
    await channel.close()
    。
  2. 空回复作为终止信号——默认处理器会将空内容视为"不发送",因此返回
    ""
    的LLM会终止对话链。可搭配提示词如
    "当你没有有用内容可补充时,回复空字符串"
    。
ConversationAdapter.validate_send
仅检查"发送者是否为参与者?"——允许连续发送,因为适配器不维护轮次顺序。
默认预期规则宽松:
max_silence(3600s, audit)
(记录到审计类型
AUDIT_KIND_EXPECTATION_VIOLATED
但不关闭通道)。
**适用场景:**两个专家无需固定顺序的真实对话(分析师 ↔ 评论员);由应用控制停止信号的聊天UI。
**不适用场景:**严格一问一答(使用
consulting
);多参与者聊天(使用
discussion
或
workflow
)。

A non-LLM participant —
HumanClient

非LLM参与者 —
HumanClient

Not every participant is an
Agent
.
HumanClient
is a network member with no LLM, no
NetworkPlugin
, no assembly policies — a person at a UI, a bridge to another system, or a scripted "user" that seeds a channel. Register it with
register_human
(not
register
— that path now rejects
kind="human"
and points you here):
python
from ag2.network import HumanClient, Passport

user_hc = HubClient(link, hub=hub)
user = await user_hc.register_human(Passport(name="user", kind="human"))   # resume=, rule=, auto_ack_invites= optional
It implements the same
NetworkClient
surface as
AgentClient
for outbound —
user.open(type=..., target=...)
,
user.send(channel_id, text, audience=...)
,
user.post_envelope(env)
(the escape hatch for adapter-shaped envelopes like a workflow
EV_PACKET
) — and gives you two ways to consume inbound envelopes:
  • Push —
    user.on_envelope(callback)
    ; the coroutine fires once per inbound envelope. Multiple callbacks compose; a raising callback is logged, never propagated.
  • Pull —
    await user.next_envelope(predicate=..., timeout=...)
    blocks for the next match;
    async for env in user.envelopes(): ...
    streams everything until
    user.disconnect()
    .
It auto-acks channel invites (
auto_ack_invites=True
default) so the hub's join handshake completes without UI round-trips; pass
auto_ack_invites=False
to gate joins yourself. Discover humans via
hub.list_agents(kind="human")
.
Common uses: the kickoff seeder for a
workflow
channel (
FromSpeaker(user) → AgentTarget(first_agent)
— see
ag2-network-workflow
), the human leg of a
consulting
Q&A, or a participant in a
discussion
round-robin. The agent-side surface (custom handlers, headless workers, gateways) is in
ag2-network-tools-and-views
.
并非所有参与者都是
Agent
。
HumanClient
是网络成员,无LLM、无
NetworkPlugin
、无组装策略——可代表UI端的用户、与其他系统的桥接、或为通道提供初始内容的脚本化"用户"。使用
register_human
注册(而非
register
——该路径现在会拒绝
kind="human"
并引导你使用此方法):
python
from ag2.network import HumanClient, Passport

user_hc = HubClient(link, hub=hub)
user = await user_hc.register_human(Passport(name="user", kind="human"))   # resume=, rule=, auto_ack_invites= 为可选参数
它实现了与
AgentClient
相同的
NetworkClient
出站接口——
user.open(type=..., target=...)
、
user.send(channel_id, text, audience=...)
、
user.post_envelope(env)
(用于发送适配器格式的消息包,如工作流
EV_PACKET
)——并提供两种方式处理入站消息包:
  • 推送——
    user.on_envelope(callback)
    ;每个入站消息包触发一次协程。多个回调可组合;抛出异常的回调会被记录,但不会传播。
  • 拉取——
    await user.next_envelope(predicate=..., timeout=...)
    会阻塞直到匹配到下一个消息包;
    async for env in user.envelopes(): ...
    会流式传输所有消息包直到
    user.disconnect()
    。
它会自动确认通道邀请(默认
auto_ack_invites=True
),因此Hub的加入握手无需UI往返;可传入
auto_ack_invites=False
来自行管控加入流程。通过
hub.list_agents(kind="human")
发现人类参与者。
常见用途:
workflow
通道的启动种子(
FromSpeaker(user) → AgentTarget(first_agent)
——见
ag2-network-workflow
)、
consulting
问答中的人类端、或
discussion
轮询中的参与者。Agent端相关内容(自定义处理器、无头工作者、网关)请参考
ag2-network-tools-and-views
。

Identity —
Passport
and
Resume

身份标识 —
Passport
和
Resume

Three dataclasses describe an agent on the network. Tenant supplies most fields; the hub stamps the rest.
python
from ag2.network import Passport, Resume, ResumeExample

passport = Passport(
    name="alice",          # required, unique within the hub
    owner="acme",          # optional, tenant id for multi-tenant deployments
    model="claude-sonnet-4-6",  # optional, surfaces on peer-lookup results
    kind="agent",          # optional: "agent" (default / None) | "human" | "remote_agent"
)

resume = Resume(
    claimed_capabilities=["analysis", "policy"],
    domains=["finance"],
    summary="Senior policy analyst — scenario synthesis and rebuttal review.",
    examples=[ResumeExample(title="Q3 risk brief", note="…")],
)
The hub stamps
Passport.agent_id
and
Passport.created_at
at registration.
Passport.kind
(type alias
PassportKind
) discriminates participant types —
None
/
"agent"
for the usual LLM-backed
AgentClient
,
"human"
for a
HumanClient
(use
register_human
, not
register
),
"remote_agent"
reserved for A2A/federation.
hub.list_agents(kind=...)
filters by it. The
Resume.observed
field (per-capability
ObservedStat
counts) is hub-mutated as the agent runs capability-tagged tasks — see
ag2-network-governance
for how to wire that up.
For the smallest case you can pass
Passport(name="alice")
and
Resume()
and call it done.
三个数据类描述网络中的Agent身份。租户提供大部分字段;Hub标记其余字段。
python
from ag2.network import Passport, Resume, ResumeExample

passport = Passport(
    name="alice",          # 必填,Hub内唯一
    owner="acme",          # 可选,多租户部署中的租户ID
    model="claude-sonnet-4-6",  # 可选,在节点查询结果中显示
    kind="agent",          # 可选: "agent"(默认/None)| "human" | "remote_agent"
)

resume = Resume(
    claimed_capabilities=["analysis", "policy"],
    domains=["finance"],
    summary="高级政策分析师——场景综合与反驳审查。",
    examples=[ResumeExample(title="Q3风险简报", note="…")],
)
Hub会在注册时标记
Passport.agent_id
和
Passport.created_at
。
Passport.kind
(类型别名
PassportKind
)用于区分参与者类型——
None
/
"agent"
对应常规LLM驱动的
AgentClient
,
"human"
对应
HumanClient
(使用
register_human
而非
register
),
"remote_agent"
预留用于A2A/联邦场景。
hub.list_agents(kind=...)
可按类型筛选。
Resume.observed
字段(每个能力对应的
ObservedStat
计数)会在Agent运行带有能力标签的任务时由Hub更新——请参考
ag2-network-governance
了解配置方法。
最简场景下,你只需传入
Passport(name="alice")
和
Resume()
即可。

Channel lifecycle

通道生命周期

agent_client.open(type=..., target=...)
    │
    ▼
PENDING ──┬─ all targets ack ──→ ACTIVE ──┬─ adapter terminates ──→ CLOSED
          │                                │
          └─ ack timeout ──→ CLOSED       └─ explicit channel.close() ──→ CLOSING ──→ CLOSED
                  (invite_timeout)               or TTL expired
The default
invite_ack_timeout
on
Hub.open(...)
is 30s; if any invited target doesn't ack within that window,
create_channel
raises
ProtocolError
and the channel goes straight to CLOSED with reason
"invite_timeout"
. (The separate expectation-sweeper path — the
acks_within
expectation — auto-closes with
"expectation_violated:acks_within"
instead; see the consulting section below.)
agent_client.open(type=..., target=...)
    │
    ▼
PENDING ──┬─ 所有目标确认 ──→ ACTIVE ──┬─ 适配器终止 ──→ CLOSED
          │                                │
          └─ 确认超时 ──→ CLOSED       └─ 显式调用channel.close() ──→ CLOSING ──→ CLOSED
                  (invite_timeout)               或超时过期
Hub.open(...)
的默认
invite_ack_timeout
为30秒;如果任何被邀请的目标未在该窗口内确认,
create_channel
会抛出
ProtocolError
,通道直接进入CLOSED状态,原因是
"invite_timeout"
。(独立的预期清理器路径——
acks_within
预期——会自动关闭通道,原因是
"expectation_violated:acks_within"
;见下文consulting部分。)

Sending an envelope

发送消息包

python
await channel.send(text, audience=[bob.agent_id])
audience
controls who sees the envelope (default: all participants). The hub stamps
Envelope.envelope_id
,
sender_id
,
created_at
, and writes it to the WAL.
For custom event types, send raw envelopes via
agent_client.send_envelope(envelope)
. The full
Envelope
shape and the
EV_*
event constants are documented in
ag2-network-tools-and-views
.
python
await channel.send(text, audience=[bob.agent_id])
audience
控制谁能看到消息包(默认:所有参与者)。Hub会标记
Envelope.envelope_id
、
sender_id
、
created_at
,并将其写入WAL。
如需发送自定义事件类型,可通过
agent_client.send_envelope(envelope)
发送原始消息包。完整的
Envelope
结构和
EV_*
事件常量请参考
ag2-network-tools-and-views
文档。

The five channel-close routes

五种通道关闭路径

Every channel terminates with an
EV_CHANNEL_CLOSED
envelope. Five routes lead there; pick by who decides:
PatternWho decidesBest for
channel.close(reason=...)
Your orchestration codeCustom caps (turn count, time, predicate)
Agent-side tool (
ChannelInject
)
The LLM"Agent decides we're done"
Adapter sentinel (subclass)The frameworkContent-based stop ("TERMINATE" keyword)
Workflow
TerminateTarget
A declarative graphMulti-step orchestrations
TTL / expectationsThe hub's sweepersTime- or expectation-based safety nets
Adapter compatibility:
Auto-closeApp closeAgent toolSentinelWorkflow graph
consulting
Yes (after reply)Yes (early bail)YesSubclassn/a
conversation
NeverYes (typical)YesCanonicaln/a
discussion
NeverYes (typical)YesSubclassn/a
workflow
Yes (graph)Yes (override)Yes (via
ToolCalled
)
n/aCanonical
每个通道都会以
EV_CHANNEL_CLOSED
消息包终止。共有五种关闭路径,选择依据是谁做出终止决定:
模式决策方最佳适用场景
channel.close(reason=...)
你的编排代码自定义限制(轮次计数、时间、谓词)
Agent端工具(
ChannelInject
)
LLM"Agent决定对话结束"
适配器哨兵(子类)框架基于内容的停止("TERMINATE"关键字)
Workflow
TerminateTarget
声明式图多步骤编排
TTL / 预期规则Hub的清理器基于时间或预期的安全机制
适配器兼容性:
自动关闭应用端关闭Agent工具哨兵Workflow图
consulting
是(回复后)是(提前终止)是子类不适用
conversation
否是(典型场景)是标准实现不适用
discussion
否是(典型场景)是子类不适用
workflow
是(图终止)是(覆盖)是(通过
ToolCalled
)
不适用标准实现

Pattern: agent-side close tool

模式:Agent端关闭工具

The LLM decides when to stop. Inject the active
Channel
into the tool:
python
from ag2.network.client.inject import ChannelInject


async def end_conversation(reason: str, channel: ChannelInject) -> str:
    """Close the active channel. The reason flows on EV_CHANNEL_CLOSED."""
    if channel is None:
        return "no active channel"
    await channel.close(reason=f"agent_close:{reason}")
    return f"closed: {reason}"


alice_agent.tool(end_conversation)
bob_agent.tool(end_conversation)
The default handler stamps the active
Channel
into
context.dependencies
before each LLM turn, so
ChannelInject
resolves automatically. Outside a network turn it resolves to
None
— the guard above keeps the tool safe in non-network contexts.
Prefer the tool-call pattern over an adapter sentinel ("TERMINATE" keyword) — tool calls are typed, traceable on the WAL, multilingual, and resist prompt injection.
由LLM决定何时停止。将当前
Channel
注入工具:
python
from ag2.network.client.inject import ChannelInject


async def end_conversation(reason: str, channel: ChannelInject) -> str:
    """关闭当前活动通道。原因会随EV_CHANNEL_CLOSED事件传递。"""
    if channel is None:
        return "无活动通道"
    await channel.close(reason=f"agent_close:{reason}")
    return f"已关闭: {reason}"


alice_agent.tool(end_conversation)
bob_agent.tool(end_conversation)
默认处理器会在每次LLM轮次前将当前
Channel
标记到
context.dependencies
中,因此
ChannelInject
会自动解析。在非网络轮次中,它会解析为
None
——上述防护措施可确保工具在非网络环境中安全运行。
优先选择工具调用模式而非适配器哨兵("TERMINATE"关键字)——工具调用是类型化的、可在WAL中追踪的、多语言兼容的,且能抵御提示注入攻击。

Watching for close

监听关闭事件

All five routes produce the same
EV_CHANNEL_CLOSED
envelope, so observers only need one predicate:
python
close_env = await alice.wait_for_channel_event(
    channel_id=channel.channel_id,
    predicate=lambda e: e.event_type == EV_CHANNEL_CLOSED,
    timeout=180.0,
)
print(f"reason: {close_env.event_data.get('reason')!r}")
ChannelMetadata.close_reason
is also stored on the channel record —
await hub.get_channel(channel_id)
returns the reason without re-reading the WAL.
所有五种路径都会生成相同的
EV_CHANNEL_CLOSED
消息包,因此观察者只需一个谓词:
python
close_env = await alice.wait_for_channel_event(
    channel_id=channel.channel_id,
    predicate=lambda e: e.event_type == EV_CHANNEL_CLOSED,
    timeout=180.0,
)
print(f"原因: {close_env.event_data.get('reason')!r}")
ChannelMetadata.close_reason
也会存储在通道记录中——
await hub.get_channel(channel_id)
可直接获取原因,无需重新读取WAL。

Replay and inspection

重放与检查

The hub's WAL is the single source of truth for "what happened in this channel":
python
wal = await hub.read_wal(channel.channel_id)
for env in wal:
    print(env.created_at, env.event_type, env.sender_id, env.event_data)
Every envelope is hub-stamped (
envelope_id
,
created_at
,
sender_id
,
audience
,
event_type
,
event_data
). The WAL is keyed by channel id; reading it works for any channel this process can see.
For the audit log (hub-level events: agent registered, channel created/closed, expectation violated), see
ag2-network-governance
.
Hub的WAL是"该通道中发生了什么"的唯一可信来源:
python
wal = await hub.read_wal(channel.channel_id)
for env in wal:
    print(env.created_at, env.event_type, env.sender_id, env.event_data)
每个消息包都由Hub标记(
envelope_id
、
created_at
、
sender_id
、
audience
、
event_type
、
event_data
)。WAL按通道ID存储;只要进程有权限,即可读取任意通道的WAL。
关于审计日志(Hub级事件:Agent注册、通道创建/关闭、预期违规),请参考
ag2-network-governance
。

Closing down

关闭流程

python
await alice_hc.close()
await bob_hc.close()
await hub.close()
Always pair
Hub.open(...)
with
hub.close()
(typically in
try/finally
).
hub.close()
cancels the sweeper tasks, closes the underlying store, drains pending I/O.
HubClient.close()
cancels the link's listening task and unsubscribes the clients.
python
await alice_hc.close()
await bob_hc.close()
await hub.close()
请始终将
Hub.open(...)
与
hub.close()
配对使用(通常放在
try/finally
中)。
hub.close()
会取消清理器任务、关闭底层存储、排空待处理I/O。
HubClient.close()
会取消链接的监听任务并注销客户端。

What to load next

后续加载推荐

User goalSkill
N-party round-robin / fixed turn order
ag2-network-discussion
Declarative orchestration /
TransitionGraph
/ GroupChat migration
ag2-network-workflow
Rate limits, access policy, expectations, audit, capability tracking, swappable arbiter / hub listeners
ag2-network-governance
Adapter-owned tools (
say
/
tools_for
), plugin tools (
delegate
/
peers
/ …), custom handlers,
HumanClient
internals, views, peer discovery
ag2-network-tools-and-views
用户目标技能
多方轮询 / 固定轮次顺序
ag2-network-discussion
声明式编排 /
TransitionGraph
/ GroupChat迁移
ag2-network-workflow
速率限制、访问策略、预期规则、审计、能力追踪、可替换仲裁器/Hub监听器
ag2-network-governance
适配器专属工具(
say
/
tools_for
)、插件工具(
delegate
/
peers
/…)、自定义处理器、
HumanClient
内部实现、视图、节点发现
ag2-network-tools-and-views

Quick reference — imports

快速参考 — 导入

python
from ag2.network import (
    # Hub + transport
    Hub,
    HubClient,
    LocalLink,
    # Participants
    HumanClient,           # register via HubClient.register_human(...)
    # Identity
    Passport,
    PassportKind,          # Literal["agent", "human", "remote_agent"] | None
    Resume,
    ResumeExample,
    # Envelopes + events
    Envelope,
    EV_TEXT,
    EV_CHANNEL_INVITE,
    EV_CHANNEL_INVITE_ACK,
    EV_CHANNEL_OPENED,
    EV_CHANNEL_CLOSED,
    EV_CHANNEL_EXPIRED,
    Priority,
    # Errors
    AccessDeniedError,
    InboxFull,
    ProtocolError,
)
from ag2.knowledge import MemoryKnowledgeStore  # or DiskKnowledgeStore
python
from ag2.network import (
    # Hub + 传输
    Hub,
    HubClient,
    LocalLink,
    # 参与者
    HumanClient,           # 通过HubClient.register_human(...)注册
    # 身份标识
    Passport,
    PassportKind,          # Literal["agent", "human", "remote_agent"] | None
    Resume,
    ResumeExample,
    # 消息包 + 事件
    Envelope,
    EV_TEXT,
    EV_CHANNEL_INVITE,
    EV_CHANNEL_INVITE_ACK,
    EV_CHANNEL_OPENED,
    EV_CHANNEL_CLOSED,
    EV_CHANNEL_EXPIRED,
    Priority,
    # 错误
    AccessDeniedError,
    InboxFull,
    ProtocolError,
)
from ag2.knowledge import MemoryKnowledgeStore  # 或DiskKnowledgeStore