Skip to content

feat(runtime): Runtime 层重构与工具并发执行优化#289

Merged
phantom5099 merged 26 commits into
1024XEngineer:mainfrom
phantom5099:main
Apr 14, 2026
Merged

feat(runtime): Runtime 层重构与工具并发执行优化#289
phantom5099 merged 26 commits into
1024XEngineer:mainfrom
phantom5099:main

Conversation

@phantom5099

@phantom5099 phantom5099 commented Apr 14, 2026

Copy link
Copy Markdown
Collaborator

变更说明

本次 PR 主要针对 internal/runtime 层进行了深度的架构重构与能力升级。核心改动包括:将原有的工具串行执行机制升级为并发执行以大幅提升运行效率,引入独立的控制面(Control Plane)用以清晰管理执行阶段与停止决策,并新增了基于“进展追踪”的死循环(防卡死)拦截机制。

#279

改动内容

  1. 并发工具调用支持 (Parallel Tool Execution)

    • 重构 internal/runtime/toolexec.go,将 executeAssistantToolCalls 的实现方式由串行遍历迭代,改为基于 goroutine + sync.WaitGroup 的高并发执行模式。
    • 使用 sync.Mutex 保护执行过程中的最早异常抛出点,保证并发状态下的稳定退出。
  2. 架构解耦与控制面 (Control Plane) 剥离

    • 新增 internal/runtime/controlplane 模块,明确定义了执行阶段 Phase(如 PhasePlanPhaseExecutePhaseVerify)。
    • 引入了规范的停机决策中心(decider.go)与专门的停机原因枚举(StopReason),将整个运行的最终中止原因严格划分为互斥的四类:success(正常结束)、max_loops(轮数超限)、error(运行时异常)、canceled(上下文取消),避免了以往原因模糊的问题。
    • 将原臃肿的 runtime.go 按照职责拆分出 run_lifecycle.go (运行生命周期) 和 session_scheduler.go (包含全新的细粒度会话锁管理与目录解析),并移除了冗余的 errors.go
  3. 执行监控与死循环拦截 (Progress Monitoring)

    • 在控制面引入了运行进展评估机制(progress.go),通过追踪每次循环后工具结果所贡献的“进展证据”(Evidence)进行分数评价。
    • 在主闭环加入断路器:若模型在交互中陷入泥潭,连续 3 轮未产生实质性工作进展(尝试调用工具却没有工具成功执行,streak >= 3),将触发 ErrNoProgressStreakLimit 强制终止任务。
  4. 事件派发流改造 (Event Emitter) 与标准事件结构

    • 抽离出专门的事件控制逻辑到 event_emitter.go
    • 升级了底层的 RuntimeEvent 结构(Envelope),现在的事件包默认附带了 Timestamp(时间戳)、Turn(当前轮次)、Phase(所处阶段)以及 PayloadVersion 等更丰富的强类型元数据。
    • 增加了全新事件,如 EventPhaseChangedEventProgressEvaluated 以及 EventStopReasonDecided,且优化了部分现有事件命名(如 EventCompactDone 改为 EventCompactApplied)。
  5. 全方位测试覆盖保证

    • 新增 runtime_progress_test.gorun_termination_test.go 测试模块;重构扩充 runtime_test.go。实现了并发错误回调、阶段流转状态机、死锁终止机制等所有新特性与边界的充分覆盖。

改动原因

  • 执行效率受限:过去遇到模型在单论输出中请求使用多个毫无依赖关联的工具(比如并行查询 3 个文件)时只能逐一执行,造成无谓的等待延迟,严重影响最终交互速度。
  • 系统健壮性与防呆:如果模型由于未预见情况“卡壳”而重复发送无用的或完全一样的命令,原系统只能坐视其消耗完最大重试轮次设置,平白无故浪费 API Token 与时间。
  • 架构扩展瓶颈:此前的核心 Runtime 层承担了太多工作,随着上层功能复杂化变得臃肿杂乱。没有显式“阶段”管理也导致 UI 状态展示相对受限。

预期收益

  • 响应速度跨越式提升:得益于工具层面的全面并发能力,那些伴随多文件查询/多并发网络请求的任务完成耗时将被显著压缩。
  • 更强的 Token 与时间保护:快速阻断(3次无进展即拦截)极大程度上避免了模型“胡言乱语”陷入自我死锁带来的算力与成本消耗。
  • 更好的代码可维护与可观测性:解耦后不仅为未来更复杂的多智能体编排打好了地基,而且事件流自带更丰富的上下文元数据,有助于前端构建出更加精准细腻的 UI 动画。

@codecov

codecov Bot commented Apr 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

fennoai[bot]

This comment was marked as outdated.

fennoai[bot]

This comment was marked as outdated.

fennoai[bot]

This comment was marked as outdated.

fennoai[bot]

This comment was marked as outdated.

fennoai[bot]

This comment was marked as outdated.

@phantom5099

Copy link
Copy Markdown
Collaborator Author

/simplify

@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Completed

Implemented fixes for the unresolved review items that were worth addressing, then ran a code-simplifier sub-agent pass and kept the resulting simplification.

What I changed

  • Fixed terminal event reliability in Run lifecycle:
    • adjusted defer order so termination emission runs before cancel()/finishRun
    • added fallback stop-event emit context (background + timeout) to reduce stop_reason_decided drop risk under canceled context/backpressure
  • Fixed run envelope consistency:
    • changed agent_done emission to emitRunScoped(...) so turn/phase metadata is preserved
  • Hardened parallel tool execution:
    • added bounded concurrency (defaultToolParallelism)
    • added per-tool-name serialization within a turn to avoid same-tool concurrent races
  • Serialized concurrent permission-ask flow:
    • added runtime-level mutex in awaitPermissionDecision(...) to prevent overlapping permission_requested events
  • Clarified concurrency contract:
    • documented in tools.Manager that Execute must be concurrency-safe

Tests added/updated

  • internal/runtime/run_termination_test.go
    • TestEmitRunTerminationUsesFallbackContextWhenCanceled
  • internal/runtime/runtime_test.go
    • TestParallelToolCallsRespectConcurrencyLimit
    • TestParallelToolCallsSerializeSameToolName
    • TestAgentDoneEventCarriesRunScopedEnvelope
  • internal/runtime/permission_test.go
    • TestAwaitPermissionDecisionSerializesConcurrentAskRequests

Validation

  • gofmt -w on touched files
  • go test ./internal/runtime/...
  • go test ./...

Commit and push

Not done

  • No additional refactors beyond the requested unresolved-comment fixes and simplification pass.

View job run

xgopilot and others added 2 commits April 14, 2026 10:17
Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
fix(runtime): harden terminal event and tool parallelism
@phantom5099

Copy link
Copy Markdown
Collaborator Author

/review -codex

fennoai[bot]

This comment was marked as outdated.

@phantom5099

Copy link
Copy Markdown
Collaborator Author

/simplify

@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

完成情况

已按你的要求完成:先读取历史评论并处理值得修复的未解决 review 项,再执行了一轮 code-simplifier 简化并整合。

已修复项

  • 修复 progress 语义不一致:ApplyProgressEvidence 在存在证据时重置 NoProgressStreak,与“连续无进展才触发拦截”一致。
  • 修复权限审批全局串行导致的阻塞:awaitPermissionDecision 改为按 run 维度串行(带 ref-count 回收),避免跨 run / 跨 session 的 head-of-line blocking。
  • 修复并发工具执行测试桩 data race:为 stubToolManager 增加互斥保护,Execute 使用局部快照避免竞态读写。
  • 同步文档事件清单:docs/runtime-provider-event-flow.md 补充 phase_changedprogress_evaluated

新增/调整测试

  • internal/runtime/permission_test.go
    • 新增 TestAwaitPermissionDecisionDoesNotSerializeAcrossRuns
  • internal/runtime/runtime_progress_test.go
    • 新增 TestProgressEvidenceResetsNoProgressStreak
  • internal/runtime/controlplane/progress_test.go
    • 调整 non-dup 证据用例为“重置 streak”语义

简化(code-simplifier pass)

  • 精简了 permission 锁释放路径(sync.Once + 更清晰 defer 顺序)。
  • 精简了 ApplyProgressEvidence 分支结构,去掉重复返回路径。

验证

  • go test -race ./internal/runtime -run TestParallelToolCallsPhaseMigration -count=1
  • go test ./internal/runtime/...
  • go test ./...

提交与推送

未执行

  • 未做与上述 review/simplify 无关的额外重构。

View job run

…OL blocking

- reset no-progress streak when evidence is present
- scope permission ask serialization per run instead of global lock
- remove test data race in stubToolManager under parallel tool execution
- sync runtime event docs with emitted phase/progress events

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
@phantom5099

Copy link
Copy Markdown
Collaborator Author

/review -codex

fennoai[bot]

This comment was marked as outdated.

fix(runtime): resolve simplify follow-up issues
@phantom5099

Copy link
Copy Markdown
Collaborator Author

/review -codex

fennoai[bot]

This comment was marked as outdated.

fennoai[bot]

This comment was marked as outdated.

fennoai[bot]

This comment was marked as outdated.

fennoai[bot]

This comment was marked as outdated.

fennoai[bot]

This comment was marked as outdated.

@phantom5099

Copy link
Copy Markdown
Collaborator Author

/simplify

@phantom5099

Copy link
Copy Markdown
Collaborator Author

/code -codex 继续修编译不过问题

@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

处理结果

已继续修复当前分支“编译不过”问题,根因是 max loop 分支返回了新建字符串错误,导致 errors.Is(err, ErrMaxLoopReached) 断言失效。

本次修改

  1. 命中最大轮数时返回 ErrMaxLoopReached 哨兵错误。
  2. 若已保存 continuation checkpoint,返回 fmt.Errorf("%w after saving continuation checkpoint", ErrMaxLoopReached),保留上下文文案并支持 errors.Is
  3. EventError 同步使用修正后的错误文本。

验证

  • go test ./internal/runtime/...:通过
  • go test ./...:通过

提交与推送

未执行

  • 未做与本次编译/测试失败修复无关的重构。

View job run

xgopilot and others added 2 commits April 14, 2026 13:27
Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
fix(runtime): preserve ErrMaxLoopReached sentinel on loop-limit exit
@phantom5099

Copy link
Copy Markdown
Collaborator Author

你这个问题可以拆成一句话: runtime 的“并发调用工具”是批量调度策略tools 的“并发执行”是单次 Execute 的线程安全语义

具体区别(按代码位点)

  1. 并发决策权归属不同
  • runtime 决定一轮里开多少并发、怎么派发、何时停:

    • 并发上限:internal/runtime/runtime.go:23 (defaultToolParallelism = 4)
    • worker 池与任务派发:internal/runtime/toolexec.go:26-66
    • 同名工具串行锁:internal/runtime/toolexec.go:179-189
    • 首错取消+停止后续派发:internal/runtime/toolexec.go:58-63, 220-224
  • tools 不决定“这一轮怎么并发调度”,只暴露 Execute 能力。

  1. 关注对象不同
  • runtime 面向的是一批 ToolCalls(同一轮 assistant 返回),做批处理编排。
  • tools 面向的是单个 ToolCallInput,只定义这一次调用的执行协议:internal/tools/manager.go:24-26
  1. 错误语义不同
  • runtime 有批处理级语义:记录首错、取消执行上下文、阻断后续派发(fail-fast)。
  • tools 只有单调用语义:返回该调用的 ToolResult + error,不负责整轮停止策略(例如权限 ask/deny 只是这一调用的结果,见 internal/runtime/permission.go:71-160 调用链)。
  1. 状态与事件责任不同
  • runtime 负责把工具结果回灌会话、发事件、推进 phase/progress:internal/runtime/run.go:146-182
  • tools 负责权限检查、sandbox、executor 调用等执行细节:internal/tools/manager.go:184-240
  1. “工具并发执行”在 tools 层的真实含义
  • 这里的“并发执行”不是说 tools 自己做 batch scheduler,而是:当 runtime 并发调用 Execute 时,tools.Manager/Executor 必须是并发安全的(接口注释已明确,internal/tools/manager.go:24)。

等价类比:

  • runtime = 调度器(类似 goroutine pool + cancel 策略)
  • tools = 线程安全任务处理函数(单任务语义)

View job run

@xgopilot 直接把runtime的并发执行逻辑下沉到工具层有问题吗?是否干扰tool职责?或是当前runtime的并发是否影响runtime职责

@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

结论

不建议把 runtime 里的“本轮批量并发调度”整体下沉到 tools 层。这样会混淆职责边界,并削弱当前控制面能力。

你这次 PR 里的 runtime 并发实现本身不越权,属于 runtime 该负责的“回合编排”。

为什么不建议下沉

  1. 调度语义属于 runtime,不属于单次工具执行
  • runtime 现在处理的是一整批 assistant.ToolCalls 的编排:并发度、派发顺序、fail-fast、取消传播(internal/runtime/toolexec.go:26-67)。
  • tools.Manager 接口是单调用语义 Execute(ctx, ToolCallInput),没有 batch 调度契约(internal/tools/manager.go:21-26)。
  1. run 级状态与事件在 runtime,tools 拿不到完整上下文
  • phase 切换、进展评估、终止判定都在 runtime 主循环(internal/runtime/run.go:145-180)。
  • 事件信封也由 runtime 统一带 turn/phase/timestampinternal/runtime/event_emitter.go:25-43)。
  • 如果下沉到 tools,必须把这些 run 语义再反向传入 tools,会造成反向依赖和跨层污染。
  1. 权限审批是 runtime 与 UI 的协作协议
  • awaitPermissionDecision 会发 permission_requested 并等待 UI 决策(internal/runtime/permission.go:162-199)。
  • 这是“运行时编排协议”,不是“工具执行细节”。下沉后 tools 将被迫感知 UI 协议。

当前实现是否影响 runtime 职责

不影响,且是合理增强:

  • 并发上限受控(defaultToolParallelisminternal/runtime/runtime.go:23)。
  • 同名工具单轮串行锁(internal/runtime/toolexec.go:179-189)。
  • 首错取消与停止派发(internal/runtime/toolexec.go:58-63, 220-224)。

这些都属于 runtime 的“执行策略层”,不是工具层的“能力实现层”。

建议的分层准则(最小可行)

  • 保持:runtime 负责 batch 调度策略(并发度、排序、去重、取消、停机)。
  • 保持:tools 负责单次 Execute 的并发安全和副作用边界(接口注释已明确,internal/tools/manager.go:24)。
  • 若要增强:可让 tools 暴露“能力元数据”(如是否可并发/建议并发上限),但最终调度决策仍由 runtime 统一执行。

本次未执行

仅做架构分析与结论回复,未修改代码。

View job run

@phantom5099

Copy link
Copy Markdown
Collaborator Author

修正方案

你这个点是对的:当前并发执行只做了“同名工具串行”,没有做“同名+同参数”重复调用检查,导致同一轮可能重复执行完全相同的调用。

建议先做一个最小闭环方案(先不改架构):

  1. runtime 增加统一参数指纹函数
  • 落点:internal/runtime/toolexec.go(可抽到新文件)

  • 指纹规则:fingerprint = normalize(toolName) + "\x1f" + canonicalArgs

  • canonicalArgs

    • 优先将 call.Arguments 反序列化 JSON 后再序列化(规范化 key 顺序)
    • 解析失败时回退 strings.TrimSpace(rawArgs),避免因非标准 JSON 崩掉
  1. 在派发前做“同轮去重检查”
  • 落点:internal/runtime/toolexec.go:58

  • orderedCalls 进入 worker 前先扫一遍:

    • seen[fingerprint] = firstCallID
    • 命中重复时不进入执行队列,直接写回一个 tool error 结果(包含被重复的 call_id
  • 这样不会重复触发外部副作用(写文件/网络请求),且消息流仍完整。

  1. 保持进展评估语义一致
  • 落点:internal/runtime/run.go:137
  • 当前 hasProgress 是按工具结果是否非 error 判断;重复调用被标成 error 后会自然计入“无进展”,与防卡死策略一致。
  1. 建议补测(必须)
  • internal/runtime/runtime_test.go:4426 附近新增:

    • 同轮两次相同 name+args 仅执行一次(验证 toolManager.Execute 调用次数)
    • 相同 name、不同 args 仍全部执行
    • 参数 JSON 键顺序不同但语义相同时应判重(如 {"a":1,"b":2} vs {"b":2,"a":1}
    • 非法 JSON 参数走回退比较,不应 panic

取舍说明

  • 推荐“跳过重复并写回错误结果”,不建议直接终止整轮 Run。
  • 原因:终止会放大影响面;跳过重复可保留主链路稳定,并把问题显式反馈给模型,促使下一轮修正参数。

本次未执行

  • 仅给出修正方案,未修改代码、未运行测试。

View job run

@xgopilot 执行方案,不允许直接在这个PR中提交

@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

执行结果

已按你给的最小闭环方案在本地完成实现与验证,且未在当前 PR 分支提交/推送

本地已实现改动

  • internal/runtime/toolexec.go

  • 新增同轮去重预处理:按 normalize(toolName) + "\x1f" + canonicalArgs 生成指纹。

  • canonicalArgs 规则:优先 JSON 反序列化后再序列化;解析失败回退 strings.TrimSpace(rawArgs)

  • 命中重复时不进入执行队列,直接回写 tool error(包含首个 call_id 与重复 call_id),并发出 EventToolResult

  • 非重复调用继续原并发执行路径;hasProgress 语义保持不变(重复调用作为 error 结果自然计入无进展)。

  • internal/runtime/runtime_internal_helpers_test.go

  • 新增 4 个测试:

  1. 同轮相同 name+args 仅执行一次。
  2. 同名不同参数全部执行。
  3. JSON 键顺序不同但语义相同会判重。
  4. 非法 JSON 参数走回退比较且不 panic。

验证结果

  • go test ./internal/runtime/... 通过
  • go test ./... 通过

未执行

  • 按你的要求,未在该 PR 中提交任何 commit / push。

View job run

@phantom5099

Copy link
Copy Markdown
Collaborator Author

结论

不建议把 runtime 里的“本轮批量并发调度”整体下沉到 tools 层。这样会混淆职责边界,并削弱当前控制面能力。

你这次 PR 里的 runtime 并发实现本身不越权,属于 runtime 该负责的“回合编排”。

为什么不建议下沉

  1. 调度语义属于 runtime,不属于单次工具执行
  • runtime 现在处理的是一整批 assistant.ToolCalls 的编排:并发度、派发顺序、fail-fast、取消传播(internal/runtime/toolexec.go:26-67)。
  • tools.Manager 接口是单调用语义 Execute(ctx, ToolCallInput),没有 batch 调度契约(internal/tools/manager.go:21-26)。
  1. run 级状态与事件在 runtime,tools 拿不到完整上下文
  • phase 切换、进展评估、终止判定都在 runtime 主循环(internal/runtime/run.go:145-180)。
  • 事件信封也由 runtime 统一带 turn/phase/timestampinternal/runtime/event_emitter.go:25-43)。
  • 如果下沉到 tools,必须把这些 run 语义再反向传入 tools,会造成反向依赖和跨层污染。
  1. 权限审批是 runtime 与 UI 的协作协议
  • awaitPermissionDecision 会发 permission_requested 并等待 UI 决策(internal/runtime/permission.go:162-199)。
  • 这是“运行时编排协议”,不是“工具执行细节”。下沉后 tools 将被迫感知 UI 协议。

当前实现是否影响 runtime 职责

不影响,且是合理增强:

  • 并发上限受控(defaultToolParallelisminternal/runtime/runtime.go:23)。
  • 同名工具单轮串行锁(internal/runtime/toolexec.go:179-189)。
  • 首错取消与停止派发(internal/runtime/toolexec.go:58-63, 220-224)。

这些都属于 runtime 的“执行策略层”,不是工具层的“能力实现层”。

建议的分层准则(最小可行)

  • 保持:runtime 负责 batch 调度策略(并发度、排序、去重、取消、停机)。
  • 保持:tools 负责单次 Execute 的并发安全和副作用边界(接口注释已明确,internal/tools/manager.go:24)。
  • 若要增强:可让 tools 暴露“能力元数据”(如是否可并发/建议并发上限),但最终调度决策仍由 runtime 统一执行。

本次未执行

仅做架构分析与结论回复,未修改代码。

View job run

@xgopilot 如果直接改成runtime不限制,tool层一次最多只能用固定个数的工具呢

@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

结论

不建议改成“runtime 不限并发、tools 层统一固定并发上限”这一种单层限流。更稳妥的是:runtime 保留回合级并发治理,tools 只做执行层保护(可选二级限流),两层协同。

原因

  1. runtime 负责的是“批调度语义”,不是单次执行。
    当前批调度包含首错取消、停止派发、同名工具串行、回合内顺序重排,集中在 internal/runtime/toolexec.go。这属于 run 编排,不应下沉。

  2. tools.Manager 接口是单调用语义。
    internal/tools/manager.go 只有 Execute(ctx, ToolCallInput),没有 batch 维度上下文(turn/phase/stop 决策)。把总并发上限放在这里会让工具层被迫承接运行时语义。

  3. 若 runtime 不限并发,会产生“前置失控”。
    即使 tools 层有固定 worker 池,runtime 仍可能在单轮创建大量 goroutine 与等待队列,带来内存/调度开销、取消传播滞后、事件背压放大风险。

  4. 权限与事件是 runtime/UI 协议。
    permission_requested/permission_resolvedturn/phase envelope 在 runtime 发射(internal/runtime/permission.gointernal/runtime/event_emitter.go)。若总调度下沉,边界会变脏。

建议方案(最小改造)

  1. 保持 runtime 的并发上限(现有 defaultToolParallelism)。
  2. tools 层可新增“执行保护阀”(全局 semaphore),作为第二道保险,但不替代 runtime 调度。
  3. 若要更细粒度,增加“工具能力元数据”(例如 max_parallelism/serialize_by_name),由 runtime 读取后决策,tools 仅执行。

直接回答你的问题

“runtime 不限制,tool 层固定并发”可以跑,但会弱化分层、放大运行时开销和治理盲区,不建议作为主方案。建议采用“runtime 主调度 + tools 防护阀”的双层模型。

本次执行

  • 已完成:架构分析与方案建议回复。
  • 未执行:未做任何代码修改。

View job run

@1024XEngineer 1024XEngineer deleted a comment from fennoai Bot Apr 14, 2026
@phantom5099

Copy link
Copy Markdown
Collaborator Author

结论

不建议改成“runtime 不限并发、tools 层统一固定并发上限”这一种单层限流。更稳妥的是:runtime 保留回合级并发治理,tools 只做执行层保护(可选二级限流),两层协同。

原因

  1. runtime 负责的是“批调度语义”,不是单次执行。
    当前批调度包含首错取消、停止派发、同名工具串行、回合内顺序重排,集中在 internal/runtime/toolexec.go。这属于 run 编排,不应下沉。
  2. tools.Manager 接口是单调用语义。
    internal/tools/manager.go 只有 Execute(ctx, ToolCallInput),没有 batch 维度上下文(turn/phase/stop 决策)。把总并发上限放在这里会让工具层被迫承接运行时语义。
  3. 若 runtime 不限并发,会产生“前置失控”。
    即使 tools 层有固定 worker 池,runtime 仍可能在单轮创建大量 goroutine 与等待队列,带来内存/调度开销、取消传播滞后、事件背压放大风险。
  4. 权限与事件是 runtime/UI 协议。
    permission_requested/permission_resolvedturn/phase envelope 在 runtime 发射(internal/runtime/permission.gointernal/runtime/event_emitter.go)。若总调度下沉,边界会变脏。

建议方案(最小改造)

  1. 保持 runtime 的并发上限(现有 defaultToolParallelism)。
  2. tools 层可新增“执行保护阀”(全局 semaphore),作为第二道保险,但不替代 runtime 调度。
  3. 若要更细粒度,增加“工具能力元数据”(例如 max_parallelism/serialize_by_name),由 runtime 读取后决策,tools 仅执行。

直接回答你的问题

“runtime 不限制,tool 层固定并发”可以跑,但会弱化分层、放大运行时开销和治理盲区,不建议作为主方案。建议采用“runtime 主调度 + tools 防护阀”的双层模型。

本次执行

  • 已完成:架构分析与方案建议回复。
  • 未执行:未做任何代码修改。

View job run

@xgopilot 我还是不够明白,假设tool工具层固定只能并发执行3个工具,那么在runtime固定并发执行4个同名工具有什么意义?直接把runtime并发执行删了不就行了

@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

直接回答

你这个例子里(tools 固定并发 3,runtime 一轮派发 4 个且同名),吞吐上基本没有意义。因为当前实现里同名工具在 runtime 已被强制串行,等效并发是 1,不会跑到 4。

为什么是这样

  • runtime 先按工具名建锁:internal/runtime/toolexec.gobuildToolExecutionLocks + executeOneToolCalltoolLock.Lock()
  • 同名调用即使进入多个 worker,也会在同一把锁前排队,所以不会并发执行。
  • defaultToolParallelism=4 主要影响的是“不同工具名”的并发上限,不是同名工具。

那 runtime 并发还能不能删

不建议直接删。原因不是“这个同名场景加速”,而是职责和治理:

  • runtime 负责回合级调度语义(首错取消、停止派发、同名串行、轮询重排、事件/phase 语义)。
  • tools.Manager 当前接口是单调用 Execute(ctx, input),没有 turn/batch 语义。
  • 若 runtime 不控并发,仍会先产生大量调度与等待(即使 tools 有池子),回合治理会变弱且边界变脏。

可落地结论

  • 你说的这个具体 case:runtime=4 对“4 个同名工具”确实不带来性能收益。
  • 但 runtime 并发治理仍应保留,用于“跨工具名”的回合级调度与运行时保护。
  • 如果后续在 tools 层也加固定并发上限,建议两层协同:runtime 做语义调度,tools 做执行保护阀(而不是互相替代)。

本次执行

  • 已完成:基于当前 PR 代码路径核对并给出结论。
  • 未执行:未做任何代码修改。

View job run

@phantom5099

Copy link
Copy Markdown
Collaborator Author

修正方案

你这个点是对的:当前并发执行只做了“同名工具串行”,没有做“同名+同参数”重复调用检查,导致同一轮可能重复执行完全相同的调用。

建议先做一个最小闭环方案(先不改架构):

  1. runtime 增加统一参数指纹函数
  • 落点:internal/runtime/toolexec.go(可抽到新文件)

  • 指纹规则:fingerprint = normalize(toolName) + "\x1f" + canonicalArgs

  • canonicalArgs

    • 优先将 call.Arguments 反序列化 JSON 后再序列化(规范化 key 顺序)
    • 解析失败时回退 strings.TrimSpace(rawArgs),避免因非标准 JSON 崩掉
  1. 在派发前做“同轮去重检查”
  • 落点:internal/runtime/toolexec.go:58

  • orderedCalls 进入 worker 前先扫一遍:

    • seen[fingerprint] = firstCallID
    • 命中重复时不进入执行队列,直接写回一个 tool error 结果(包含被重复的 call_id
  • 这样不会重复触发外部副作用(写文件/网络请求),且消息流仍完整。

  1. 保持进展评估语义一致
  • 落点:internal/runtime/run.go:137
  • 当前 hasProgress 是按工具结果是否非 error 判断;重复调用被标成 error 后会自然计入“无进展”,与防卡死策略一致。
  1. 建议补测(必须)
  • internal/runtime/runtime_test.go:4426 附近新增:

    • 同轮两次相同 name+args 仅执行一次(验证 toolManager.Execute 调用次数)
    • 相同 name、不同 args 仍全部执行
    • 参数 JSON 键顺序不同但语义相同时应判重(如 {"a":1,"b":2} vs {"b":2,"a":1}
    • 非法 JSON 参数走回退比较,不应 panic

取舍说明

  • 推荐“跳过重复并写回错误结果”,不建议直接终止整轮 Run。
  • 原因:终止会放大影响面;跳过重复可保留主链路稳定,并把问题显式反馈给模型,促使下一轮修正参数。

本次未执行

  • 仅给出修正方案,未修改代码、未运行测试。

View job run

/code -codex

# Conflicts:
#	internal/runtime/run.go
#	internal/runtime/runtime_test.go
# Conflicts:
#	internal/runtime/run.go
#	internal/runtime/runtime_test.go
@phantom5099

Copy link
Copy Markdown
Collaborator Author

/code 给我解决冲突

@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

处理结果

已完成冲突消解,并把与 origin/main 的冲突语义对齐到最新主干行为(重点是去除 max_loops/loop_limit 路径冲突)。

本次修改

  • internal/runtime/run.go
  • internal/runtime/runtime.go
  • internal/runtime/runtime_internal_helpers_test.go
  • internal/runtime/runtime_progress_test.go
  • internal/runtime/runtime_test.go
  • docs/context-compact.md
  • docs/runtime-provider-event-flow.md
  • docs/guides/configuration.md
  • docs/config-management-detail-design.md

核心处理点:

  • Run 主循环对齐主干:移除内部 max_loops 终止与 loop_limit checkpoint 分支。
  • 保留当前 PR 的控制面/事件信封/并发执行改造能力,不跨层回退。
  • 调整冲突测试用例:改为“同一 run 内可持续工具轮次并正常收敛”语义;修复 progress 测试对旧 ErrMaxLoopReached 的依赖。
  • 文档同步移除 loop_limit 与配置里的 max_loops 描述,保留 compact_applied 与 reactive 降级重试说明。

验证

  • go test ./internal/runtime/...
  • go test ./...
  • 三方合并检查:git merge-tree $(git merge-base HEAD origin/main) HEAD origin/main<<<<<<< 冲突标记 ✅

提交与推送

未执行

  • 未对本次冲突之外的架构/功能做额外重构。

View job run

xgopilot and others added 2 commits April 14, 2026 15:14
Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
fix(runtime): resolve merge conflicts with main
@phantom5099

Copy link
Copy Markdown
Collaborator Author

/code 重修,接收没有最大循环数(8)

@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

任务清单

  • 同步上下文:读取仓库规范与历史评论
  • 明确本次触发评论的具体诉求
  • 复查与“最大循环数(8)”相关实现与测试
  • 输出审查结论并更新评论

View job run

# Conflicts:
#	docs/context-compact.md
#	internal/runtime/run.go
#	internal/runtime/runtime.go
#	internal/runtime/runtime_internal_helpers_test.go
@phantom5099

Copy link
Copy Markdown
Collaborator Author

/code 修复报错,并提高测试覆盖率直到100%

@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

完成情况

已按你的指令完成“修复报错 + 提升覆盖率”。

本次修改

验证结果

  • go test ./...:通过。
  • 覆盖率:
    • 仓库整体 total86.8%(当前仓库基线)。
    • 本 PR 改动的 Go 新增行未覆盖数为 0(即改动行覆盖达到 100%)

提交与推送

未完成

  • 未将“整个仓库总体覆盖率”提升到 100%(当前为 86.8%);本次已把本 PR 改动行覆盖补齐到 100%。

View job run

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: phantom5099 <245659304+phantom5099@users.noreply.github.com>
@fennoai

fennoai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

任务进度

  • 建立并维护 Todo 清单
  • 阅读仓库约束(含 CLAUDE.md / AGENTS.md)并收集上下文
  • 定位触发评论“修复报错”对应的具体错误
  • 实施修复并补充/更新测试
  • 运行必要验证(至少 go test ./...
  • 提交并推送到当前 PR 分支
  • 更新最终结果说明

View job run

test: fix runtime compile regression and improve patch coverage
@phantom5099 phantom5099 merged commit 6855d42 into 1024XEngineer:main Apr 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants