Skip to content

[BUG]: DeepAgent 对 a2a_delegate 的「控制/内容双通道」——内容镜像泄漏与屏蔽不对称 #85

Description

@openjiuwen-collaboration-bot

DeepAgent 对 a2a_delegate 的「控制 / 内容双通道」——内容镜像泄漏与屏蔽不对称

一句话:同样是 a2a_delegate 委派,在 DeepAgent(deep-research)拓扑下,控制通道的中断被 orchestrator
屏蔽(内部驱动远端调用),但 DeepAgent 任务回路额外发布了一条 controller_output 内容镜像,把原始
a2a_delegate__interaction__ 原样透传给客户端;而非 DeepAgent 拓扑(edpa plan-agent)只有控制通道、
被完全屏蔽。屏蔽不对称的根因是 agent 类型(是否走 DeepAgent 任务回路),与并发无关

本文汇总问题现象、多轮代码追踪(双通道、生产者 RemoteA2aInterruptRail、屏蔽根因)与 DeepAgent 原理,
供后续提 issue 使用。

路径前缀:

  • RT/ = agent-runtime-java/(SUT 仓,acceptance 仓只做抓包/断言)
  • EXT/ = third_party/agent-solution/common/agent-runtime-ext-java/agent-service-adapters/agent-service-adapters-agentcore-ext/src/main/java/com/openjiuwen/service/adapters/agentcore/ext/(deepagent 专用扩展模块,被 agent-deep-research 应用引入)
  • CORE/ = RT/third_party/agent-core-java/src/main/java/
  • 行号均为 2026-07-30/31 抓取快照。

1. 问题现象

三个抓包 run,分属三种 A→B 委派拓扑(均经 A2A_STREAM 协议):

run 拓扑(A→B) B / A 的实现 客户端能否检索到 __interaction__ / a2a_delegate
run-20260730-212851 DA-09 deep-research → search-agent(3 路并发)+ verify-agent DeepAgent(agent-deep-research) 既触发 3 次并发远端调用,又收到 controller_output 镜像(a2a_delegate 原样泄漏)
run-20260730-175330 edpa-plan-agent → versatile-adapter plan-agent(versatile-orch-demo-plan-agent) ❌ 完全屏蔽,只看到 Remote agent requires input 代理中断
run-20260730-165126 expense-review-main → expense-review-workflow agent-core WorkflowAgent __interaction__request_user_input 14/14 绑定(原生询问,与 a2a_delegate 无关)

核心疑问(经多轮追踪后已收敛):第 1 行里 a2a_delegate 既被屏蔽(控制通道驱动了 3 次调用)又同时以 __interaction__
原样出现在内容流里;第 2 行里同样的 a2a_delegate 却被完全屏蔽。为什么?


2. 黑盒现象(抓包证据)

2.1 DA-09(deep-research:run-20260730-212851)——未屏蔽 + 3 并发调用

marker 统计(单文件 DA-09-A2A_STREAM-r1.log):

_remote_invocation            0     ← 见 §6.5 解释(不代表没发调用)
"Remote agent requires input" 0
INPUT_REQUIRED                0
statusUpdate                  3     ← 仅终态,非中断代理
artifactUpdate                3873  ← 内容通道主导
__interaction__               4
a2a_delegate                  4
toolName: search-agent ×6, verify-agent ×2

agent-core 应用日志(同毫秒 3 行,注意:这只是日志 for 循环,并非并发执行,见 §6.4):

2026-07-30 21:28:58.511 [react-agent-stream-f7eb6a7e-...] [agent] INFO agent - Executing tool: search-agent with args: {"remoteInput": "DeepSeek V3 API 输入定价 每百万token价格"}
2026-07-30 21:28:58.511 [react-agent-stream-f7eb6a7e-...] [agent] INFO agent - Executing tool: search-agent with args: {"remoteInput": "Qwen-Max API 输入定价 每百万token价格"}
2026-07-30 21:28:58.511 [react-agent-stream-f7eb6a7e-...] [agent] INFO agent - Executing tool: search-agent with args: {"remoteInput": "Doubao-pro API 输入定价 每百万token价格"}

客户端收到的内容镜像(artifactUpdate,索引 828):

{"payload":{"metadata":{"task_id":"deep_agent_task_f7eb6a7e-..._1","stream_kind":"inner_agent"},
 "data":[
   {"data":{"payload":{"id":"call_00_...","value":{"toolCallId":"call_00_...","payloadSchema":{},
     "context":{"_interrupt_kind":"a2a_delegate","agentName":"search-agent"},
     "message":"DeepSeek V3 API 输入定价 每百万token价格","toolName":"search-agent"}},"type":"__interaction__"}},
   {"data":{... call_01 ... "Qwen-Max ..." ...},"type":"__interaction__"},
   {"data":{... call_02 ... "Doubao-pro ..." ...},"type":"__interaction__"}
 ],"type":"task_interaction"},"index":828.0,"type":"controller_output"}

要点:① 走 artifactUpdate(内容通道);② 3 条 a2a_delegate __interaction__ 被打包在一个
controller_output/task_interaction 产物里;③ 整轮以 ANSWER/COMPLETED 收尾,INPUT_REQUIRED=0

2.2 edpa(plan-agent→versatile-adapter:run-20260730-175330)——屏蔽

_remote_invocation            14 文件
INPUT_REQUIRED                26 文件(39 处)
__interaction__               0
a2a_delegate                  0(客户端侧)

statusUpdate(控制通道)里的代理中断:

"statusUpdate":{"status":{"state":"TASK_STATE_INPUT_REQUIRED",
  "message":{"parts":[{"text":"Remote agent requires input"}],
  "metadata":{"_interrupt":{
    "message":"Remote agent requires input",
    "items":[{"toolCallId":"call_00_...","message":"Remote agent requires input","toolName":"versatile-adapter"}]}}}}}

要点:① 走 statusUpdate(控制通道);② _interruptitems[] + toolName=versatile-adapter,
没有 type:"__interaction__"没有 a2a_delegate;③ 该 toolCallId_remote_invocation 一致——
原始 a2a_delegate 已被消费、替换成无标签代理。

2.3 WorkflowAgent 原生询问(run-20260730-165126)——对照

__interaction__ 14 文件、request_user_input 15 文件,同时出现 14/14,无一例外;toolName 38 处全是
request_user_input;_remote_invocation=0。即此处 __interaction__ 来自原生 ask_user(request_user_input
工具),与 a2a_delegate 无关,仅作"客户端本应能收到 __interaction__"的基线对照。


3. 核心结论(TL;DR)

  1. 双通道:同一个 a2a_delegate 事件,在 DeepAgent 里同时走两条通道——
    • 控制通道(TYPE_INTERRUPT):被 orchestrator 捕获 → isCoordinatorInterruptbatchCoordinator
      并发调远端 → resume。被屏蔽(不下发客户端)。
    • 内容通道(TYPE_CHUNK / controller_output):被 orchestrator onNext 直接转发。未屏蔽(原样到客户端)。
  2. DeepAgent = ReActAgent + 任务回路外壳。DeepAgent 本身不推理;它按轮驱动一个内层 ReActAgent,并把它每一轮
    的流额外收集、包装成 controller_output 内容镜像——这条镜像就是 a2a_delegate 泄漏的来源。
  3. 屏蔽不对称的根因是 agent 类型(是否走 DeepAgent 任务回路),与并发无关。edpa plan-agent 非 DeepAgent,只有
    控制通道 → 完全屏蔽;DeepAgent 多发内容镜像 → 部分泄漏。并发只决定镜像里 data:[N] 的 N,不决定镜像是否存在。

对早前结论的纠正:一度认为"DA-09 的 a2a_delegate 不进协调器、纯走内容通道"。逐行追踪后确认:DA-09 的 3 次
HTTP 调用确实是 serve 层 RemoteInvocationBatchCoordinatorA2ARemoteAgentClient 发的
(与 edpa 同一条协调器
路径);_remote_invocation=0 另有原因(§6.5)。真正的图景是"既进协调器(屏蔽、驱动调用)又泄漏内容镜像"。


4. DeepAgent 原理:为什么会有内容镜像

4.1 DeepAgent = ReActAgent + 任务回路外壳

DeepAgent 的内层 agent 字段就是 ReActAgent,构造时直接 new:

  • CORE/com/openjiuwen/harness/deep_agent/DeepAgent.java:79 private final ReActAgent agent;
  • DeepAgent.java:144-146
    this.agent = new ReActAgent(this.card);
    ...
    this.agent.configure(buildReActAgentConfig());

任务回路受开关控制,启用后把 CoreTaskLoopEventExecutor 注册为 DEEP_TASK_TYPE 的执行器,回调 invokeInnerRound:

  • DeepAgent.java:503-504 if (config.isEnableTaskLoop()) ensureTaskLoopRuntime();
  • DeepAgent.java:1062-1070
    taskScheduler = new TaskScheduler(controllerConfig, taskManager, agent.getContextEngine(), ...);
    ...
    taskScheduler.getTaskExecutorRegistry().addTaskExecutor(TaskLoopEventExecutor.DEEP_TASK_TYPE,
        dependencies -> new CoreTaskLoopEventExecutor(dependencies, this, this::invokeInnerRound));

CoreTaskLoopEventExecutor 全仓只被 DeepAgent.java 引用——它是 DeepAgent 任务回路特有的执行器,不是通用机制。

4.2 每轮驱动内层 ReActAgent + 收集流

任务回路每一轮回调 invokeInnerRound,直接跑内层 ReActAgent(DeepAgent.java:1341-1357),分流式/同步两条:

  • 同步 invokeInnerRoundOnce(DeepAgent.java:1368-1370):return agent.invoke(effectiveInputs, session);
  • 流式 invokeInnerRoundStreaming(DeepAgent.java:1380-1401):
    agent.stream(effectiveInputs, innerSession, List.of(StreamMode.OUTPUT))
         .forEachRemaining(chunk -> {
             streamItems.add(chunk);
             if (chunk instanceof OutputSchema outputSchema) {
                 session.writeStream(outputSchema);              // 内层输出直接写进流
             }
         });
    ...
    result.put("stream_chunks", normalizeStreamChunks(streamItems)); // 收集成 stream_chunks

所以前面观察到的所有现象,全部来自这个内层 ReActAgent,DeepAgent 只在外面"按轮驱动 + 收集镜像":

现象 真正产出者
react-agent-stream-<sid> 线程 内层 ReActAgent 流线程(CORE/.../singleagent/agents/ReActAgent.java:813)
Executing tool: search-agent ×3 内层 ReActAgent executeToolCallEntries(ReActAgent.java:475/483/703)
__interaction__(a2a_delegate) 内层 ReActAgent commitInterrupt(ReActAgent.java:919)
controller_output 镜像 DeepAgent 任务回路把收集到的 stream_chunks 交给 CoreTaskLoopEventExecutor 包装

4.3 controller_output 镜像的产生

CoreTaskLoopEventExecutor 读内层结果的 stream_chunks,把每个 __interaction__ 包装成
controller_output/task_interaction 镜像(CORE/.../harness/task_loop/CoreTaskLoopEventExecutor.java):

  • processingChunks(:303-319):读 resultstream_chunks/streamChunks/chunks/inner_stream,逐项处理。
  • toProcessingChunk(:348-376):关键——
    if (item instanceof OutputSchema outputSchema && "__interaction__".equals(outputSchema.getType())) {
        return new ControllerOutputChunk(index,
            new ControllerOutputPayload(TASK_INTERACTION,
                List.of(new DataFrame.JsonDataFrame(
                    Map.of("type", outputSchema.getType(), "payload", outputSchema.getPayload()))),
                Map.of("task_id", taskId, "stream_kind", "inner_agent")),
            false);
    }
  • executeOnce(:162-176):终态 chunk eventType = isInterruptResult(result) ? TASK_INTERACTION : TASK_COMPLETION,
    即中断轮也会产出 task_interaction 镜像(与抓包一致)。

4.4 与 ReActAgent 单独跑的对比

ReActAgent 不产生 controller_output,也不走任务回路(grep ControllerOutput|TaskScheduler|CoreTaskLoopEventExecutor|task_loop
ReActAgent.java 零命中)。它直接把产出 writeStream顶层 OutputSchema(ReActAgent.java:919/1341/1366):
__interaction__ 作为顶层 OutputSchema 写出 → 适配层 normalizeChunk 看到顶层 __interaction__TYPE_INTERRUPT
(控制通道),不会被包成 controller_output

结论:单独跑的 ReActAgent 没有外面那层 DeepAgent 任务回路去收集+包装它的流,所以只有控制通道一条,没有内容镜像。
同一份 ReActAgent 输出,被 DeepAgent 外壳额外镜像成一条 controller_output 内容流——这条流就是 a2a_delegate 泄漏的来源。


5. a2a_delegate 的生产者:RemoteA2aInterruptRail(EXT 模块)

a2a_delegate_interrupt_kind 既不在 agent-core-java,也不在 agent-runtime-java——它们由第三个模块
agent-runtime-ext-java(制品 agent-service-adapters-agentcore-ext,被 agent-deep-research 引入)里的
RemoteA2aInterruptRail 硬编码产生。这就是在 SUT 仓里 grep 不到 a2a_delegate 的原因。

5.1 远端 agent 如何变成"工具"

search-agent/verify-agent 不是有执行体的 Tool,而是一张纯元数据 ToolCard(schema 只有 remoteInput),
执行被 rail 拦截——真正干活的是 rail + serve 层协调器。

注册链路(启动时一次性):

  1. openjiuwen.service.a2a.remote-agentsRT/service/agent-service-app/.../config/A2AProperties.java:51 绑定。
  2. ApplicationReadyEventRT/.../controller/a2a/client/A2AAgentCardDiscovery.java:87-97,142-146 抓取远端 agent card,
    登进 A2ARemoteAgentCardRegistry
  3. EXT/autoconfigure/AgentCoreExtAutoConfiguration.java:21-25RemoteA2aToolInstaller bean。
  4. deep-research 应用选用 handler agentcore-ext:EXT/agentfw/JiuwenCoreAgentExtHandler.java:94-108,其
    streamQuery/query 都在每次 run 前 installBeforeRun()(:110-115)→ remoteToolInstaller.install(getAgent())
  5. EXT/external/RemoteA2aToolInstaller.java:76-111(install):读 registry,每张卡建一个 RemoteA2aToolSpec
    (toolName = remoteAgentName,即 search-agent;toSpec :113-126),全包进一个 RemoteA2aInterruptRail,
    targetAgent.registerRail(rail)(:103)。目标经 DeepAgent.getAgent() 解析(:158-166)。
  6. CORE/.../singleagent/BaseAgent.java:211AgentCallbackManager.registerRail(:117-122)→
    AbilityManager.add(toolCard)(:107-110),把 search-agent/verify-agentToolCard 插进 AbilityManager,
    listToolInfo() 暴露给 LLM。

5.2 a2a_delegate 的字面来源

EXT/external/RemoteA2aInterruptRail.java:44-60(resolveInterrupt),resumeInput == null 时:

InterruptRequest request = InterruptRequest.builder()
    .message(message)                                  // = tool call 的 remoteInput 参数(extractMessage :78-93)
    .context(Map.of(
        "agentName", spec.remoteAgentId(),             // "search-agent"
        "_interrupt_kind", "a2a_delegate"))            // ← 字面来源
    .build();
return interrupt(request);

spec.remoteAgentId() == toolName == "search-agent",与抓包镜像 {context:{_interrupt_kind:"a2a_delegate", agentName:"search-agent"}, toolName:"search-agent"} 完全吻合;payloadSchema:{} 也一致(rail 构造的 request 不设 payload schema)。
resume 时(:46-48)return reject(resumeInput);


6. 完整调用链路(端到端)

6.1 工具执行 → 中断

内层 ReActAgent 拿到 LLM 的 3 个 search-agent tool call:

  • ReActAgent.executeToolCallEntries(:475/483,被 :703 正常轮、:664 resume 轮调用;:450 是零调用方的死代码)
    AbilityManager.execute(:303),顺序逐个跑(:311 for (ToolCall singleToolCall : toolCalls),无并发)。
  • 每个工具:railedExecuteSingleToolCall(:323)→ RailExecutor BEFORE_TOOL_CALL →
    BaseInterruptRail.beforeToolCall(:156-172)命中 search-agentRemoteA2aInterruptRail.resolveInterrupt
    抛出 a2a_delegate InterruptRequest。
  • BaseInterruptRail.applyDecision(:226-258)把它转成 throw new ToolInterruptException(..., toolCall)(:256)。
  • AbilityManager.execute 逐个 catch(:340-346),存为 ToolExecutionEntry(interruptException, null)continue——
    保证本轮所有 tool call 都跑到、各自产生中断
  • ReActAgent.collectToolInterrupts(:939-961)汇总;commitInterrupt(:900-927)把每个中断包成一个
    __interaction__ OutputSchema(:917-919 ToolCallInterruptRequest.fromToolCall),返回
    {result_type:"interrupt", state:[OutputSchema...], interrupt_ids}

6.2 双通道分叉(同一批 __interaction__ ×3 走两条路)

适配层 JiuwenCoreAgentHandler(RT/.../agentcore/agentfw/JiuwenCoreAgentHandler.java)按最外层 type 分流:

  • normalizeChunk(:569-581):顶层 __interaction__toInterruptData(:574,打 "interrupt detected" 日志);
    controller_output → 通用 {type, index, payload} map(:576),内层 __interaction__ 不被拆看
  • mapToQueryChunkType(:609-620):顶层 __interaction__TYPE_INTERRUPT(:613);controller_outputTYPE_CHUNK(:619)。
  • streamQuery(:223-243):TYPE_INTERRUPT 收进 interrupts 列表(:235-236)、不立即转发,流末合并成 1 条
    normalizeInterrupts(:242);其余(TYPE_CHUNK)立即 observer.onNext(:238)。
通道 A:控制(捕获、屏蔽、驱动调用) 通道 B:内容(镜像、透传、客户端可见)
来源 顶层 __interaction__TYPE_INTERRUPT 合并成 {type:__interaction__, items:[3]} DeepAgent 任务回路把 __interaction__ 包成 controller_output/task_interaction data:[3]TYPE_CHUNK
orchestrator onNext :236-238 捕获(不转发) :240 直接转发(不解析内层)
处理 handleInterrupt(:292-324)→ isCoordinatorInterrupt(:533-551)→ batchCoordinator.execute 不进屏蔽判定
结果 3 次并发 HTTP + resume(见 §6.3) 客户端收到那条 controller_output(索引 828)

6.3 通道 A:3 次并发调用 + resume(serve 层)

  • A2AEnabledServeOrchestrator.handleInterrupt(:295-299):batchCoordinator.execute(rawInterrupt, current, observer).get()
  • RemoteInvocationBatchCoordinator.execute(:152-169):parseBatch(:855-891,断言 _interrupt_kind=="a2a_delegate" :873)
    for (Member member : batch.members) submit(...)(:165-167)——3 次紧凑 submit
  • submit(:345-381)→ start(:400-425)→ client.callOutcome(call, …)(:425);client 是 serve 层
    A2ARemoteAgentClient(:241),把真实 A2A JSON-RPC/流式 HTTP 提交到 ioExecutor(ThreadPoolExecutor :112-119,submit :265)。
  • 全部 member 解析完 → streamBatchResolution(:437-457)+ buildBatchResumeRequest(:506-521):结果塞进 metadata
    runtime.remoteToolResults(:517,按 toolCallId 索引)+ runtime.remoteBatchId,构造 resume ServeRequest
  • 回灌:JiuwenCoreAgentHandler.buildInputs(:393-414)读 runtime.remoteToolResults(:402)→ 建 InteractiveInput(:403-407)。
  • 内层 resume:ReActAgent resume 分支(:654-677)检测到 interruptionState,重跑被中断的 tool call(:663-664);
    rail 再触发时 getUserInput(BaseInterruptRail:194-216)按 toolCallId 取回远端答案 → resolveInterrupt 收到
    resumeInput != nullreject(resumeInput)(RemoteA2aInterruptRail:46-48)→ applyDecision(:241-252)
    _skip_tool=true 并把远端答案塞进 toolResult,不再调任何工具实例
  • 全部 tool 完成、无新中断 → collectToolInterrupts 返回 null → commitInterrupt 跳过 → 主循环继续 →
    最终 result_type:"answer"(ReActAgent:722-726),故 INPUT_REQUIRED=0、以 ANSWER 收尾。

6.4 并发的真相:不在 ReActAgent,在 orchestrator 扇出

  • agent-core 内 ReActAgent/AbilityManager 对工具是顺序执行(AbilityManager:311,无 CompletableFuture/线程池/虚线程)。
    同毫秒 3 行 Executing tool: search-agent 只是 executeToolCallEntries(:481-487)在真正执行的日志 for 循环连打 3 行。
  • 真正的 3 路并发在 serve 层:RemoteInvocationBatchCoordinator.execute 紧凑循环 submit 3 个 member →
    A2ARemoteAgentClient.ioExecutor(ThreadPoolExecutor)在各自线程并发发 HTTP,受 maxConcurrency(默认 16,
    A2AProperties:62)限制。

6.5 为什么 _remote_invocation=0 却确实发了调用

_remote_invocation 只在 ChunkMapper.toParts 处理 TYPE_REMOTE_AGENT_OUTPUT chunk 时打
(RT/.../controller/a2a/ChunkMapper.java:40-44)——它标记的是"被转发给客户端的远端产物投影"。DA-09 里 search-agent
的结果被当作 runtime.remoteToolResults 回灌进 agent 续跑了,从没作为内容 chunk 转发给客户端,所以该标记为 0;
但 3 次 HTTP 确实由 serve 层 A2ARemoteAgentClient 发出了。edpa 的 versatile-adapter 因 streaming: true
会向客户端转发远端流式产物,故 _remote_invocation=14


7. 屏蔽不对称根因

屏蔽是"通道作用域"的,只作用于控制通道(TYPE_INTERRUPT);内容通道(TYPE_CHUNK)整体透传,不解析内部
__interaction__/_interrupt_kind
在此之上,屏蔽不对称的真正分界线是 agent 类型(是否走 DeepAgent 任务回路):

  • DeepAgent(deep-research):任务回路(CoreTaskLoopEventExecutor)把内层 ReActAgent 每轮的流额外收集、包装成
    controller_output 内容镜像 → 走内容通道 → 原始 a2a_delegate __interaction__ 原样到客户端。控制通道的中断仍被正常屏蔽、
    驱动远端调用。→ 既屏蔽(控制)又泄漏(内容)
  • 非 DeepAgent(edpa plan-agent):无任务回路外壳,a2a_delegate 只作为顶层 __interaction__ 走控制通道 → 完全屏蔽。→ 只屏蔽

与并发无关:DeepAgent 里单个 a2a_delegate tool call 也会产出 controller_output 镜像(data:[1]);并发只让
data:[N] 的 N 变 3。edpa 即便并发,只要不是 DeepAgent 任务回路,也不会有这条内容镜像。

a2a_delegate 这个 kind 名在两种拓扑下含义其实不同(DeepAgent 是经 RemoteA2aInterruptRail 的远端委派;
其 rail 行为与 edpa 的 versatile-adapter 委派一致,但外壳不同),这也是不对称值得提 issue 的点之一。


8. 影响 / 是否缺陷(取决于契约口径)

口径 A —— "凡委派/输入需求都应以统一信封(如 __interaction__)上报,且不应向客户端泄漏内部委派细节"
则 DeepAgent 路径把 a2a_delegatetask_interaction 镜像泄漏给客户端,是生产者侧(agent-core-java 任务回路 /
agent-runtime-ext-java)的可观测性缺陷
:应在发布 task_interaction/controller_output 产物前,对其中
_interrupt_kind=a2a_delegate 的 item 做脱敏/屏蔽,使 DeepAgent 与非 DeepAgent 对齐。

口径 B —— "a2a_delegate(委派)与 ask_user(原生询问)语义不同,控制通道行为正确"
则当前控制通道屏蔽是对的,但存在可观测性不对称:DeepAgent 泄漏委派镜像、非 DeepAgent 不泄漏。需明确客户端应不应当
看到 a2a_delegate 委派细节:若不应,DeepAgent 侧脱敏;若应,则非 DeepAgent 路径也应补发同样镜像。

口径 C —— 抓包/断言层(acceptance 仓)
同一"输入需求/委派"语义在不同拓扑下形状不一(controller_output 嵌套 __interaction__ vs items[] 代理 vs 原生
request_user_input),断言易误判。建议在 acceptance 侧抽一个跨拓扑统一的判定。


9. 建议方向(供 issue 讨论)

  1. 先定契约:客户端是否应看到 a2a_delegate 的委派/镜像细节?DeepAgent 的 controller_output 内容镜像是否属于
    "应脱敏的内部可观测性"?
  2. 若需脱敏(推荐,口径 A):在 agent-core-java CoreTaskLoopEventExecutor 发布 task_interaction/controller_output
    前,或 DeepAgent.normalizeStreamChunks 收集前,对 _interrupt_kind=a2a_delegate 的 item 做屏蔽,使 DeepAgent 路径
    不再泄漏委派细节。修改点集中在 third_party/agent-core-java + agent-runtime-ext-java
  3. 若需统一上报:在 A2AEnabledServeOrchestrator.buildInterruptQueryResponse / RemoteInvocationBatchCoordinator
    合成代理中断处补 type:"__interaction__",使非 DeepAgent 路径与 DeepAgent/WorkflowAgent 对齐。
  4. acceptance 侧:在 VersatileEventMapping 增加对 items[]/_interrupt/controller_output 嵌套 __interaction__
    形状的 INTERACTION 识别,或抽跨拓扑统一判定,消除断言的拓扑相关歧义。

附录 A:DeepAgent 结构速查

DeepAgent (任务回路: TaskScheduler + CoreTaskLoopEventExecutor,enableTaskLoop 时按轮调度)
  │  每轮回调 invokeInnerRound
  ▼
ReActAgent (this.agent = new ReActAgent(card))  ← 真正的推理 + 工具执行
  │  agent.stream(...) 产出 OutputSchema 流(llm_output / llm_reasoning / __interaction__ / answer)
  ▼
DeepAgent 收集该流 → stream_chunks → CoreTaskLoopEventExecutor 包装成 controller_output 镜像(内容通道)
                同时 __interaction__ 作为顶层中断上浮(控制通道)
  • DeepAgent 还能递归 spawn 子 DeepAgent(instantiateConfiguredSubagent :992-996,
    HarnessFactory.createDeepAgent),每个子 DeepAgent 内层又是一个 ReActAgent。但 DA-09 的 search-agent 是经
    RemoteA2aInterruptRail远端 agent,不是本地子 DeepAgent。

附录 B:关键证据定位

  • DA-09 新 run:target/sit-logs/wire/run-20260730-212851/DA-09-A2A_STREAM-r1.log
    (artifactUpdate + controller_output/task_interaction data:[3],索引 828;_remote_invocation=0)
  • edpa 屏蔽 run:target/sit-logs/wire/run-20260730-175330/balanceThen-A2A_STREAM-r7.log
    (statusUpdate + _interrupt.items[],toolCallId_remote_invocation 一致)
  • WorkflowAgent 对照 run:target/sit-logs/wire/run-20260730-165126/*
    (__interaction__request_user_input 14/14 绑定)
  • 配置:src/test/resources/application-openjiuwen.yml(:124-126 deep-research / :132-136 remote-agents /
    :175-177 edpa-plan-agent)
  • SUT 代码(agent-runtime-java):
    • service/agent-service-app/.../orchestrator/A2AEnabledServeOrchestrator.java
      (onNext 235-241、handleInterrupt 292-324、isCoordinatorInterrupt 533-551、streamBatchResolution 437-457、
      buildBatchResumeRequest 506-521)
    • service/agent-service-app/.../orchestrator/RemoteInvocationBatchCoordinator.java(152-169 / 345-381 / 400-425 / 855-891)
    • service/agent-service-app/.../controller/a2a/client/A2ARemoteAgentClient.java(112-119 / 241 / 265)
    • service/agent-service-app/.../controller/a2a/ChunkMapper.java(40-44,_remote_invocation 触发条件)
    • service/agent-service-adapters/.../agentcore/agentfw/JiuwenCoreAgentHandler.java
      (streamQuery 223-243、normalizeChunk 569-581、mapToQueryChunkType 609-620、buildInputs 393-414)
  • agent-core 代码(third_party/agent-core-java):
    • harness/deep_agent/DeepAgent.java(79 / 144-146 / 503-504 / 1062-1070 / 1341-1401 / 992-996)
    • harness/task_loop/CoreTaskLoopEventExecutor.java(162-176 / 303-319 / 348-376)
    • core/singleagent/agents/ReActAgent.java(475 / 483 / 703 / 813 / 900-927 / 939-961 / 654-677 / 722-726)
    • core/singleagent/AbilityManager.java(107-110 / 303 / 311 / 340-346)
    • core/singleagent/BaseAgent.java(211)、AgentCallbackManager.java(117-122)
    • harness/rails/interrupt/BaseInterruptRail.java(156-172 / 194-216 / 226-258)
  • EXT 模块(third_party/agent-solution/common/agent-runtime-ext-java/.../ext/):
    • external/RemoteA2aInterruptRail.java(44-60,a2a_delegate 字面来源)
    • external/RemoteA2aToolInstaller.java(76-111 / 113-126 / 158-166)
    • agentfw/JiuwenCoreAgentExtHandler.java(94-115)
    • autoconfigure/AgentCoreExtAutoConfiguration.java(21-25)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions