agent-gateway 入口治理
请求进入智能体之前要过的检查:鉴权、租户、参数、幂等、审计;之后按配置直连运行时或经事件总线转发。
依据 agent-solution 0.1.3 · main@bf702bc9(2026-11-03)核实
网关站在哪里
企业里的业务系统(销售、客服、门户)不直接调智能体,而是先进 agent-gateway。它接收 A2A JSON-RPC 请求(POST /a2a),做完治理检查,再把请求转给真正的运行时:
| 转发模式 | 走法 |
|---|---|
direct(默认) | 先问注册中心要路由,再直连运行时的 /a2a(HTTP / SSE) |
bus | 经事件总线两跳转发到运行时(下下关讲) |
网关是独立部署的 Spring Boot 服务,跟注册中心、运行时都只通过 HTTP 打交道,没有进程内耦合。
五道检查
| 步骤 | 做什么 | 拒绝时 |
|---|---|---|
| G1 鉴权 | 校验 Authorization: Bearer 令牌 | 401 AUTH_MISSING / AUTH_INVALID |
| G2 租户 | 从凭据取权威租户,丢弃调用方自报的租户头 | 403 TENANT_UNRESOLVED |
| G3 参数 | 校验 JSON-RPC 信封、方法、消息结构,绑定 agentId | VALIDATION_* |
| G4 幂等 | 创建类请求按"租户 + messageId"去重 | 409 冲突 |
| G5 审计 | 通过、拒绝都记一条审计事件 | 审计失败不阻断已通过的请求 |
G1 → G3 由 GovernancePipeline.run 按固定顺序执行,根路径入口和按智能体分路径的入口共用这一份代码,保证两条入口不会走偏。任一步拒绝,先写审计、再把错误抛出去。
鉴权是 fail-closed:没有配置凭据时,所有请求都返回 401。
租户:只认凭据
String authoritative = principal.tenantId();
if (authoritative == null || authoritative.isBlank()) {
throw new GovernanceException(HttpStatus.FORBIDDEN, "TENANT_UNRESOLVED", ...);
}
// 自报的租户被有意丢弃,以凭据为准
return authoritative;
节选自源码,省略了错误消息参数,注释改写为中文。
源码TenantResolver.resolveagent-solution/common/agent-bus/agent-gateway/src/main/java/com/openjiuwen/gateway/governance/tenant/TenantResolver.java:37@bf702bc9这和上一个模块里"租户字段只是标签"呼应:真正可信的租户来自鉴权,不来自请求本身。
幂等:重试不会建出两个任务
网络超时后重发是常态。G4 只对创建类请求(还没有 taskId 的)生效:
| 情况 | 结果 |
|---|---|
| 没带 messageId | 跳过去重 |
| 第一次出现 | 登记为"处理中",继续转发 |
| 同键、同内容、已完成 | 回放第一次的结果,不再转发 |
| 同键、同内容、还在处理 | 409 IDEMPOTENCY_IN_FLIGHT |
| 同键、内容不同 | 409 IDEMPOTENCY_PAYLOAD_MISMATCH |
内容指纹取请求的 params 部分并排序序列化,JSON-RPC 的 id 不算在内,所以客户端重试时换了 id 也能正确回放。创建失败时会释放这个键,允许重试。
当前幂等记录存在单个网关进程的内存里,源码注释明确说多实例部署需要共享存储,不在本版范围内。生产上多副本部署网关时,要自己补这一块。
读响应:别只看状态码
网关有两种错误形态:
- 方法级错误:一律 HTTP 200 + JSON-RPC error 信封,比如参数校验失败
-32602 - 治理错误:根路径入口用非 2xx 状态码 + 统一错误体(401、413、503 等);分路径入口则同样包装成 HTTP 200 + JSON-RPC error
所以接入方判断业务结果,必须解析响应体。
动手:预测网关的处理
小结
- 网关在智能体前面做五道检查:鉴权、租户、参数、幂等、审计,前三步顺序固定。
- 租户只认凭据,自报的租户头直接丢弃。
- 创建类请求按"租户 + messageId"去重:已完成就回放,内容不同就报冲突。
- 判断成功与否要解析响应体,HTTP 200 不等于业务成功。
源码出处
- agent-solution/common/agent-bus/agent-gateway/README.md职责、两种转发模式、错误承载口径
- agent-solution/common/agent-bus/agent-gateway/src/main/java/com/openjiuwen/gateway/governance/GovernancePipeline.java:94
public void run(GovernanceContext contextG1→G3 固定顺序,拒绝先审计再抛出 - agent-solution/common/agent-bus/agent-gateway/src/main/java/com/openjiuwen/gateway/governance/tenant/TenantResolver.java:37
public String resolve(Principal principal只认凭据绑定的租户 - agent-solution/common/agent-bus/agent-gateway/src/main/java/com/openjiuwen/gateway/governance/idempotency/IdempotencyRule.java:49
public Decision check(String tenantId创建类请求去重 - agent-solution/common/agent-bus/agent-gateway/src/main/java/com/openjiuwen/gateway/facade/A2aDispatcher.java:136
idempotencyRule.check回放 / 409 冲突 - agent-solution/common/agent-bus/agent-gateway/src/main/java/com/openjiuwen/gateway/obs/AuditSink.java:13
public interface AuditSink审计出口,不能阻断主流程
链接指向固定提交,仓库后续更新不影响这里的行号。
过关测验
1. 调用方在 X-Tenant-Id 里写了别的租户,网关怎么处理?
2. 创建类请求的幂等键是什么?
3. 网关返回 HTTP 200,是否说明业务成功?