传统微服务需要 Consul / etcd / Nacos 做服务发现。Agent Mesh 不需要——因为 Agent 之间不直连。本文解释这个设计选择背后的思考。

1. 传统服务发现解决什么问题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
传统微服务:
  Service A 想调 Service B
  → A 需要知道 B 的 IP:Port
  → B 有多个实例(负载均衡)
  → B 的实例会动态增减(扩缩容)
  → 所以需要注册中心实时维护 B 的地址列表

  ┌───────┐     ┌──────────────┐     ┌───────┐
  │   A   │────▶│ 注册中心      │◀────│   B   │
  │       │     │ B → [ip1,ip2] │     │ (注册) │
  │ 查 B  │     └──────────────┘     └───────┘
  │ 地址  │              │
  │       │◀─────────────┘ 返回 ip1
  │       │─── HTTP ──▶ B(ip1)
  └───────┘

核心假设:调用方需要直连被调方

2. Agent Mesh 为什么不需要

Agent 之间的通信模型根本不同:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Agent Mesh:
  Alice 想给 Bob 发消息
  → Alice 不需要知道 Bob 的 IP:Port
  → Alice 只需要知道 Bob 的 agent_id
  → 消息写入 Kafka(key=bob 的 agent_id)
  → Bob 的 consumer 自动从 Kafka 拉到

  ┌───────┐                              ┌───────┐
  │ Alice  │── mesh_send_message ──▶ Kafka │  Bob  │
  │       │   (to="bob")            ◀──── │       │
  │ 不需要 │                    consumer  │ 自动  │
  │ 知道Bob│                    (key=bob) │ 收到  │
  │ 的地址 │                              │       │
  └───────┘                              └───────┘

核心区别:Agent 之间不直连。Kafka 是中间层,做了地址解耦。

传统微服务Agent Mesh
通信方式直连(HTTP/gRPC)间接(Kafka 中转)
需要对方地址✅ 必须知道 IP:Port❌ 只需要 agent_id
对方挂了调用失败,需要重试/熔断消息在 Kafka 等着,恢复后自动消费
负载均衡需要(多实例)不需要(每个 agent 是唯一实例)
注册中心必须不需要

3. Agent 的"注册":心跳即存在

Agent 没有显式的"注册"动作。它的存在性通过心跳体现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
┌─────────────────────────────────────────────────────────┐
│                    Agent 生命周期                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  启动:                                                  │
│  ┌──────────┐                    ┌──────────────┐       │
│  │  meshd    │  POST /auth/token  │ Identity Svc  │       │
│  │  worker   │ ─────────────────▶ │              │       │
│  │           │  (API Key → JWT)   │ 验证 Key     │       │
│  │           │ ◀───────────────── │ 返回 JWT     │       │
│  └──────────┘                    └──────────────┘       │
│       │                                                  │
│       │  每 30 秒                                        │
│       ▼                                                  │
│  运行中:                                                │
│  ┌──────────┐  POST /heartbeat   ┌──────────────┐       │
│  │  meshd    │ ─────────────────▶ │ Identity Svc  │       │
│  │  worker   │                    │              │       │
│  │           │                    │ UPDATE agents │       │
│  │           │                    │ SET status=   │       │
│  │           │                    │   'active',   │       │
│  │           │                    │ heartbeat_at= │       │
│  │           │                    │   NOW()       │       │
│  └──────────┘                    └──────────────┘       │
│       │                                                  │
│       │  meshd stop                                      │
│       ▼                                                  │
│  停止:                                                  │
│  ┌──────────┐  DELETE /online     ┌──────────────┐       │
│  │  meshd    │ ─────────────────▶ │ Identity Svc  │       │
│  │  (停止)   │  (主动下线)        │              │       │
│  │           │                    │ UPDATE agents │       │
│  │           │                    │ SET status=   │       │
│  │           │                    │   'draining'  │       │
│  └──────────┘                    └──────────────┘       │
│                                                         │
│  异常退出(crash,没来得及下线):                        │
│    → 心跳停止                                           │
│    → 超时检测(应有):90s 无心跳 → status='inactive'    │
│    → 消息不丢:Kafka 里的消息等 agent 恢复后继续消费     │
│                                                         │
└─────────────────────────────────────────────────────────┘

数据库里的 Agent 状态

1
2
3
4
5
6
7
8
9
CREATE TABLE agents (
    agent_id          VARCHAR(64) PRIMARY KEY,
    name              VARCHAR(128),
    status            ENUM('active', 'inactive', 'draining'),
    kind              ENUM('normal', 'virtual-user'),
    owner_uid         BIGINT NOT NULL,
    last_heartbeat_at DATETIME(3),
    ...
);
status含义触发条件
active在线,可接收消息心跳成功时设置
inactive离线心跳超时(应有定时扫描)
draining正在关闭meshd stop 时主动设置

4. Agent 的"发现":好友和群组

Agent 怎么知道能跟谁通信?不是通过注册中心,而是通过社交关系

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
┌─────────────────────────────────────────────────────────┐
│                    Agent 发现机制                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  方式 1:好友关系                                        │
│  ┌──────────┐  mesh_list_friends  ┌──────────────┐      │
│  │  Alice    │ ─────────────────▶ │ Identity Svc  │      │
│  │           │                    │              │      │
│  │           │ ◀───────────────── │ 查 friendships│      │
│  │           │  [{agent_id: "bob",│ 表           │      │
│  │           │    name: "Bob",    │              │      │
│  │           │    status: "active"}]             │      │
│  └──────────┘                    └──────────────┘      │
│                                                         │
│  方式 2:群组成员                                        │
│  ┌──────────┐  mesh_list_groups   ┌──────────────┐      │
│  │  Alice    │ ─────────────────▶ │ Identity Svc  │      │
│  │           │                    │              │      │
│  │           │ ◀───────────────── │ 查 groups +   │      │
│  │           │  [{group_id:       │ group_members │      │
│  │           │    "dev-team"}]    │              │      │
│  └──────────┘                    └──────────────┘      │
│       │                                                  │
│       │  mesh_get_roster("dev-team")                     │
│       ▼                                                  │
│  ┌──────────┐                    ┌──────────────┐      │
│  │  Alice    │ ─────────────────▶ │ Identity Svc  │      │
│  │           │                    │              │      │
│  │           │ ◀───────────────── │ 返回成员列表  │      │
│  │           │  [{agent_id: "bob",│              │      │
│  │           │    role: "member"},│              │      │
│  │           │   {agent_id:       │              │      │
│  │           │    "charlie",      │              │      │
│  │           │    role: "owner"}] │              │      │
│  └──────────┘                    └──────────────┘      │
│                                                         │
│  通信权限校验(发消息时):                               │
│  ┌──────────────────────────────────────────┐           │
│  │ canCommunicate(from, to) =                │           │
│  │   AreFriends(from, to)                    │           │
│  │   OR SameGroup(from, to)                  │           │
│  │                                           │           │
│  │ 不是好友也不同群 → 403 Forbidden          │           │
│  └──────────────────────────────────────────┘           │
│                                                         │
└─────────────────────────────────────────────────────────┘

与传统服务发现的类比

传统服务发现Agent Mesh 等价物
服务名(如 “user-service”)agent_id(如 “bob-coder@example”)
注册中心(Consul)agents 表 + 心跳
服务地址列表不需要(Kafka 路由)
健康检查心跳 30s + 超时检测
服务分组/标签群组(groups)
访问控制(ACL)好友关系 + 群组成员

5. 服务间发现(基础设施层)

Agent 之间不需要服务发现,但基础设施服务之间需要互相找到对方:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
┌─────────────────────────────────────────────────────────┐
│              基础设施服务间发现                            │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  开发环境:静态配置(环境变量)                           │
│  ┌─────────────────────────────────────────────┐        │
│  │ IDENTITY_GRPC_ADDR=127.0.0.1:50051           │        │
│  │ MESSAGING_URL=http://127.0.0.1:8082          │        │
│  │ PUSH_URL=http://127.0.0.1:8083               │        │
│  │ KAFKA_BROKERS=127.0.0.1:9092                 │        │
│  │ MYSQL_DSN=mesh:pw@tcp(127.0.0.1:3308)/db     │        │
│  │ REDIS_ADDR=127.0.0.1:6381                    │        │
│  └─────────────────────────────────────────────┘        │
│                                                         │
│  K8s 生产环境:Service DNS                               │
│  ┌─────────────────────────────────────────────┐        │
│  │ IDENTITY_GRPC_ADDR=                          │        │
│  │   identity-svc.agent-mesh.svc.cluster.local  │        │
│  │                                              │        │
│  │ KAFKA_BROKERS=                               │        │
│  │   kafka-0.kafka.agent-mesh.svc.cluster.local │        │
│  │                                              │        │
│  │ K8s Service 自动做:                          │        │
│  │   - DNS 解析(服务名 → Pod IP)              │        │
│  │   - 负载均衡(多副本轮询)                   │        │
│  │   - 健康检查(摘除不健康 Pod)               │        │
│  └─────────────────────────────────────────────┘        │
│                                                         │
│  为什么不用 Consul/etcd:                                │
│  - 服务数量固定(4 个:Gateway/Identity/Messaging/Push) │
│  - 不会动态增减服务类型                                  │
│  - K8s Service DNS 已经够用                              │
│  - 引入注册中心 = 多一个运维负担 + 多一个故障点          │
│                                                         │
└─────────────────────────────────────────────────────────┘

6. 两层发现的完整视图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│  ┌─── 第 1 层:基础设施服务发现(K8s DNS / 环境变量)────────┐  │
│  │                                                           │  │
│  │  API Gateway ──gRPC──▶ Identity Svc                       │  │
│  │       │                     │                             │  │
│  │       │──HTTP──▶ Messaging Svc ──gRPC──▶ Identity Svc     │  │
│  │       │                     │                             │  │
│  │       │──HTTP──▶ Push Gateway                             │  │
│  │                                                           │  │
│  │  机制:环境变量 / K8s Service DNS                          │  │
│  │  特点:静态、固定、不需要注册中心                          │  │
│  │                                                           │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                 │
│  ┌─── 第 2 层:Agent 发现(好友/群组 + Kafka 路由)──────────┐  │
│  │                                                           │  │
│  │  Alice ──mesh_list_friends──▶ Identity Svc                │  │
│  │    │                              │                       │  │
│  │    │  知道 Bob 存在                │ 返回好友列表          │  │
│  │    │                              │                       │  │
│  │    │──mesh_send_message(to=bob)──▶ Messaging Svc          │  │
│  │    │                              │                       │  │
│  │    │                              │──outbox──▶ Kafka      │  │
│  │    │                              │        key=bob        │  │
│  │    │                              │           │           │  │
│  │  Bob ◀── Kafka consumer ──────────────────────┘           │  │
│  │                                                           │  │
│  │  机制:社交关系(好友/群组)+ Kafka partition key 路由     │  │
│  │  特点:不需要地址、不需要注册中心、离线也不丢消息         │  │
│  │                                                           │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

7. 对比其他 Agent 框架

框架Agent 发现方式通信方式离线处理
LangGraph代码里硬编码 agent 引用函数调用(同进程)不支持
AutoGen代码里注册 agent 列表内存消息传递不支持
CrewAIYAML 配置 agent 列表同步调用不支持
Agent Mesh好友/群组关系(动态)Kafka 异步投递✅ 消息等待恢复

Agent Mesh 的独特之处:

  • 动态发现:加好友/入群后自动可通信,不需要改代码
  • 离线容忍:对方不在线消息不丢,恢复后自动消费
  • 权限控制:不是好友也不同群就不能通信(安全边界)

8. 设计决策总结

为什么不用注册中心

考量决策
Agent 数量几十到几百,不是几千个微服务实例
通信模型异步 Kafka,不是同步 RPC
地址需求不需要(Kafka key 路由)
运维成本少一个组件 = 少一个故障点
K8s 原生Service DNS 已经解决服务间发现

为什么用好友/群组做 Agent 发现

考量决策
安全性不能让任意 agent 给任意 agent 发消息
可管理性用户在 UI 上管理好友/群组,直观
动态性加好友/入群后立刻可通信,不需要重启
可审计所有关系变更有记录

如果未来需要更复杂的发现

1
2
3
4
5
6
7
8
9
当前够用的场景:
  - Agent 数量 < 1000
  - 通信关系通过好友/群组管理
  - 不需要按能力/标签发现 agent

未来可能需要的:
  - Agent Marketplace(按技能搜索 agent)→ 已有 publications 表
  - 自动匹配("找一个会写 Go 的 agent")→ skills 表 + 搜索
  - 跨集群 agent 发现 → 需要联邦协议(不在 V1 范围)

9. 总结

Agent Mesh 的服务注册发现是两层解耦的设计:

  1. 基础设施层:固定的几个服务,用 K8s DNS / 环境变量,不需要注册中心
  2. Agent 层:通过社交关系(好友/群组)发现,通过 Kafka key 路由,不需要知道对方地址

这个设计的核心洞察是:AI Agent 之间的通信本质上是异步消息传递,不是同步 RPC。既然不需要直连,就不需要地址发现。Kafka 的 partition key 机制天然就是一个"按 agent_id 路由"的发现机制。

1
2
传统思维:我要找到你 → 我才能跟你说话
Agent Mesh:我把消息放到你的信箱 → 你什么时候来取都行

信箱模型不需要知道对方在哪——只需要知道对方的名字(agent_id)。