已合并
GH杭研现场问题修改 #349
GH杭研现场问题修改 #349
已合并
guoyangsen创建于 6月29日
1 个文件变更+38-7
@@ -30,6 +30,9 @@ from a2a.server.agent_execution import AgentExecutor, RequestContext
30from a2a.server.context import ServerCallContext30from a2a.server.context import ServerCallContext
31from a2a.server.events import EventQueue31from a2a.server.events import EventQueue
32from a2a.types.a2a_pb2 import (32from a2a.types.a2a_pb2 import (
33+ AgentCapabilities,
34+ AgentCard,
35+ AgentInterface,
33 CancelTaskRequest,36 CancelTaskRequest,
34 GetTaskRequest,37 GetTaskRequest,
35 Message,38 Message,
@@ -49,6 +52,7 @@ from a2a.types.a2a_pb2 import (
49 TASK_STATE_INPUT_REQUIRED,52 TASK_STATE_INPUT_REQUIRED,
50 TASK_STATE_WORKING,53 TASK_STATE_WORKING,
51)54)
55+from a2a.utils.constants import PROTOCOL_VERSION_1_0, TransportProtocol
52from a2a.utils.errors import TaskNotFoundError, UnsupportedOperationError56from a2a.utils.errors import TaskNotFoundError, UnsupportedOperationError
53from google.protobuf.json_format import MessageToDict, ParseDict57from google.protobuf.json_format import MessageToDict, ParseDict
54from google.protobuf.struct_pb2 import Struct, Value58from google.protobuf.struct_pb2 import Struct, Value
@@ -357,15 +361,38 @@ class Executor(AgentExecutor):
357 """是否具备派发能力:注入了单 client(兜底)或有 client_factory(按 url 构造)。"""361 """是否具备派发能力:注入了单 client(兜底)或有 client_factory(按 url 构造)。"""
358 return self._sub_agent_client is not None or self._client_factory is not None362 return self._sub_agent_client is not None or self._client_factory is not None
359 363 
364+ @staticmethod
365+ def _build_sub_agent_card(url: str) -> AgentCard:
366+ """用主侧已知可达地址 url(yaml 配置的 spec.url)自建子 Agent 最小 Card。
367+ 
368+ 不信任子 Agent Card 自报的 netloc——容器内
369+ ``host:8090`` 会让 RPC 回环到主侧自身(见部署排障)。改为以 yaml 配置的
370+ 可达地址为权威 RPC 目标,对齐 VA client(``_build_va_card`` + ``create``)。
371+ 
372+ 子 Agent A2A RPC 端点挂在 ``/a2a/``,补尾斜杠避免 ``POST /a2a`` 触发 307
373+ """
374+ rpc_url = url.rstrip("/") + "/"
375+ card = AgentCard(name="SubDPA", description="子 Agent A2A 端点", version="1.0.0")
376+ card.supported_interfaces.append(
377+ AgentInterface(
378+ protocol_binding=TransportProtocol.JSONRPC,
379+ url=rpc_url,
380+ protocol_version=PROTOCOL_VERSION_1_0,
381+ )
382+ )
383+ card.capabilities.CopyFrom(AgentCapabilities(streaming=True))
384+ return card
385+ 
360 async def _get_sub_agent_client(self, url: str) -> Optional[Client]:386 async def _get_sub_agent_client(self, url: str) -> Optional[Client]:
361 """解析某 url 对应的子 Agent A2A client。387 """解析某 url 对应的子 Agent A2A client。
362 388 
363 - 注入了 ``sub_agent_client``(单测/单部署兜底)→ 直接复用,忽略 url 差异;389 - 注入了 ``sub_agent_client``(单测/单部署兜底)→ 直接复用,忽略 url 差异;
364- - 否则用 ``client_factory`` 按 url ``create_from_url`` 并缓存(每 url 一个);390+ - 否则用 ``client_factory`` 按 yaml 配置的 url 自建 Card 后 ``create`` 并缓存(每 url 一个);
365 - url 为空或无 factory → 返回 None(由调用方将该实体降级 failed)。391 - url 为空或无 factory → 返回 None(由调用方将该实体降级 failed)。
366 392 
367- ``create_from_url`` 可能抛(子 Agent 不可达 / 卡片拉取失败);不在此吞掉,393+ 直接用 yaml 中的 url 作为 RPC 目标,不再 ``create_from_url`` 卡片
368- ``_run_sub_agent`` try/except 捕获 → 仅该实体 failed,不连坐其他394+ (子 Agent Card 自报容器内地址会导致 RPC 回环到主侧)建连/发送失败由
395+ ``_run_sub_agent`` 的 try/except 捕获 → 仅该实体 failed,不连坐其他。
369 """396 """
370 if self._sub_agent_client is not None:397 if self._sub_agent_client is not None:
371 return self._sub_agent_client398 return self._sub_agent_client
@@ -373,9 +400,10 @@ class Executor(AgentExecutor):
373 return None400 return None
374 client = self._sub_agent_clients.get(url)401 client = self._sub_agent_clients.get(url)
375 if client is None:402 if client is None:
376- client = await self._client_factory.create_from_url(url)403+ card = self._build_sub_agent_card(url)
404+ client = self._client_factory.create(card)
377 self._sub_agent_clients[url] = client405 self._sub_agent_clients[url] = client
378- logger.info(f"[Executor] sub_agent client 构造并缓存:url={url}")406+ logger.info(f"[Executor] sub_agent client 构造并缓存(RPC 直连 yaml 地址):url={url}")
379 return client407 return client
380 408 
381 async def cancel_task(self, conv_id: str) -> None:409 async def cancel_task(self, conv_id: str) -> None:
@@ -1224,7 +1252,7 @@ class Executor(AgentExecutor):
1224 "trace_id": session_ctx.get("trace_id", ""), # 链路可观测性1252 "trace_id": session_ctx.get("trace_id", ""), # 链路可观测性
1225 "agent_id": getattr(get_settings(), "dpa_agent_id", "") or "", # 子 Agent 自身 ID1253 "agent_id": getattr(get_settings(), "dpa_agent_id", "") or "", # 子 Agent 自身 ID
1226 "params": session_ctx.get("params", {}), # 继承父请求 URL query(如 type/workspace_id),供子 Agent 工作流调用 VA 时透传1254 "params": session_ctx.get("params", {}), # 继承父请求 URL query(如 type/workspace_id),供子 Agent 工作流调用 VA 时透传
1227- "body": {}, # 子 Agent 自行构造 VA 请求体,初始为空1255+ "body": session_ctx.get("body", {}), # 继承父请求体:子 Agent VA 时仅覆盖 query/intent,其余业务字段沿用父请求
1228 },1256 },
1229 ex=_TTL,1257 ex=_TTL,
1230 )1258 )
@@ -1362,7 +1390,9 @@ class Executor(AgentExecutor):
1362 ) -> SubAgentResult:1390 ) -> SubAgentResult:
1363 """驱动单个子 Agent:node_start → 盖章/透传 report → node_end,并捕获 child_task_id。"""1391 """驱动单个子 Agent:node_start → 盖章/透传 report → node_end,并捕获 child_task_id。"""
1364 conv_id = turn_ctx.conv_id1392 conv_id = turn_ctx.conv_id
1365- sub_conv_id = f"{conv_id}:sub:{spec.entity_id}"1393+ # 分隔符用 '-' 而非 ':':子 Agent 会把该 conv_id 透传给 VA 作 conversationId,
1394+ # 工作流平台校验 conversationId 必须匹配 ^[a-zA-Z0-9_-]+$(冒号会被拒,返回 500)。
1395+ sub_conv_id = f"{conv_id}-sub-{spec.entity_id}"
1366 await self._emit_sub_task(1396 await self._emit_sub_task(
1367 turn_ctx, child_path, "agent",1397 turn_ctx, child_path, "agent",
1368 {"event": "node_start", "entity_name": spec.entity_name},1398 {"event": "node_start", "entity_name": spec.entity_name},
@@ -1518,6 +1548,7 @@ class Executor(AgentExecutor):
1518 "headers": parent_cached.get("headers", {}), # VA 鉴权 token1548 "headers": parent_cached.get("headers", {}), # VA 鉴权 token
1519 "params": parent_cached.get("params", {}), # 前端 URL query(如 type/workspace_id)继承给子 Agent 的 VA 调用1549 "params": parent_cached.get("params", {}), # 前端 URL query(如 type/workspace_id)继承给子 Agent 的 VA 调用
1520 "trace_id": parent_cached.get("trace_id", ""), # 链路追踪1550 "trace_id": parent_cached.get("trace_id", ""), # 链路追踪
1551+ "body": parent_cached.get("body", {}), # 下传父请求体:子 Agent 仅覆盖 query/intent,其余业务字段继承(工作流 code 组件依赖)
1521 "sub_task_path": list(child_path), # 下传绝对路径:子节点继续盖章 / 深度门控1552 "sub_task_path": list(child_path), # 下传绝对路径:子节点继续盖章 / 深度门控
1522 }1553 }
1523 request = self._build_sub_agent_message(spec.query, sub_conv_id, session_context)1554 request = self._build_sub_agent_message(spec.query, sub_conv_id, session_context)