Skip to content

feat(agent): 按 PR #162 架构契约迭代 agent 模块(事件驱动 + 网关解耦) #178

@Lwwww1

Description

@Lwwww1

背景

PR #162 补齐了 docs/bytemind-architecture/* 的架构与接口契约,但当前 internal/agent 仍以 Runner 为中心、同步串行执行。
需要把 agent 从“可用实现”迭代到“契约一致、可扩展、可观测”的主编排层。

现状差距

  1. agent 入口仍是同步 RunPrompt,缺少统一 TurnEvent 流。
  2. agentsession/context/provider/tools/runtime/policy/storage 的边界未完全网关化,耦合偏高。
  3. 工具权限评估未形成 agent 层显式前置决策链,审计事件不完整(尤其是 permission/task 关键事件)。
  4. runtime 任务能力仍是占位实现,子代理编排(尤其异步归并/worktree)无法落地。
  5. prompt_too_long 仅有预算前压缩,缺少“触发后 reactive compact + retry”闭环。

目标

  • 对齐 PR 架构设计 #162agent 契约的最小可用版本(MVP)。
  • 保持现有 CLI/TUI 行为不回退,采用“适配层 + 分阶段迁移”。

迭代计划

  • Phase 0(契约对齐):统一 policy 优先级口径(hard_deny > explicit_deny > risk_rule > explicit_allow > mode_default > fallback_ask)并固化到 agent 依赖链。
  • Phase 1(入口升级):新增 Engine.HandleTurn(ctx, req) (<-chan TurnEvent, error),Runner 作为兼容适配器调用新 Engine。
  • Phase 1(网关解耦):补齐 SessionGateway/ContextGateway/ModelGateway/PolicyGateway/ToolGateway/RuntimeGateway/StorageGateway 最小接口与默认实现。
  • Phase 2(执行链路):实现 start/delta/tool_use/tool_result/complete/error 事件流;工具调用前必须经过 PolicyGateway
  • Phase 2(agent 编排闭环):接入 ContextGateway 的 reactive compact + retry(一次),压缩规则由 context 模块负责。
  • Phase 3(子代理最小版):先落地同步子代理(Spawn + Wait),异步归并与 worktree 隔离单独 issue。
  • Phase 3(审计与恢复):补齐关键审计事件(permission_decision、tool_execute_start/result、task_state_changed)并可回放校验。

验收标准

  1. go test ./internal/agent -vgo test ./internal/runtime -v 通过。
  2. 新增主闭环契约测试:事件顺序、终态一致性、异常分支可重现。
  3. 新旧入口兼容:CLI/TUI 的现有调用方式不需要改动即可运行。
  4. 工具权限决策与审计日志可关联到 session_id/task_id/trace_id
  5. 子代理 MVP 仅支持同步模式,明确不包含后台归并/worktree。

非目标

  • 本 issue 不一次性实现完整五层权限细分。
  • 本 issue 不实现异步子代理归并与 worktree 隔离。
  • 本 issue 不做 provider 大重构。

模块架构设计(Agent 详细版)

1. 目标与边界

  • agent 负责“单回合编排”与“跨模块协调”,不直接内嵌 context/policy/runtime/storage 的实现细节。
  • Engine 成为唯一回合执行入口;Runner 仅做兼容适配,不再承载主编排逻辑。
  • TurnEvent 是回合执行唯一对外事件契约;CLI/TUI/审计统一消费该事件流。
  • 保持现有 RunPrompt/RunPromptWithInput 调用方式不变,先兼容后迁移。

2. 分层结构(建议落地到 internal/agent

  1. engine(编排层)
  • 提供 HandleTurn(ctx, req) (<-chan TurnEvent, error)
  • 负责回合状态机、事件发射、错误收敛、重试策略。
  1. gateway(边界层)
  • 定义并注入 Session/Context/Model/Policy/Tool/Runtime/Storage 七类网关接口。
  • 屏蔽底层模块差异,确保 engine 只依赖接口。
  1. runner_adapter(兼容层)
  • Runner 内部调用 Engine,把 TurnEvent 转为现有输出与旧 Event
  • 在迁移完成前维持旧行为与测试用例稳定。
  1. event_codec(事件层)
  • 统一 TurnEvent schema、序列号分配、终态判定与校验。

3. 对外契约(MVP)

建议新增(或在 issue 内冻结)如下最小契约:

// 新入口(MVP)
type Engine interface {
    HandleTurn(ctx context.Context, req TurnRequest) (<-chan TurnEvent, error)
}

type TurnRequest struct {
    SessionID   core.SessionID
    UserMessage llm.Message
    Mode        plan.AgentMode
    Workspace   string
    Assets      map[llm.AssetID]llm.ImageAsset
    TraceID     core.TraceID // 空值时由 engine 生成
}

type TurnEvent struct {
    EventID    core.EventID
    SessionID  core.SessionID
    TaskID     core.TaskID
    TraceID    core.TraceID
    TurnID     string
    Sequence   uint64
    Type       string // start|delta|tool_use|tool_result|complete|error
    Timestamp  time.Time
    Payload    map[string]any
    ErrorCode  string
    Retryable  bool
}

4. 七类 Gateway 职责与最小方法

  1. SessionGateway
  • 职责:加载/持久化 session,维护会话消息 append 原子性。
  • 最小方法:Load(...)Save(...)AppendMessages(...)
  1. ContextGateway
  • 职责:系统提示组装、上下文预算评估、compact 执行与回放保真。
  • 最小方法:BuildTurnMessages(...)EstimateTokens(...)Compact(...)
  • 约束:继续遵守 prompt 组装顺序:default -> mode -> runtime block -> active skill -> AGENTS.md
  1. ModelGateway
  • 职责:封装 Create/Stream 调用与模型能力裁剪(tool use、parts 兼容)。
  • 最小方法:Chat(...)ChatStream(...)
  1. PolicyGateway
  • 职责:在工具执行前输出显式 allow/deny/ask 决策与理由。
  • 最小方法:DecideTool(ctx, in) (Decision, error)
  • 约束:决策优先级固定为:
    hard_deny > explicit_deny > risk_rule > explicit_allow > mode_default > fallback_ask
  1. ToolGateway
  • 职责:执行工具、返回结构化结果,屏蔽 registry/executor 细节。
  • 最小方法:Execute(ctx, call, execCtx)
  • 约束:不得绕过 PolicyGateway 直接执行。
  1. RuntimeGateway
  • 职责:任务/子代理编排入口(MVP 先同步)。
  • 最小方法:Submit(...)Wait(...)Cancel(...)Stream(...)
  • 约束:子代理先支持 Spawn + Wait;异步归并/worktree 隔离后续 issue。
  1. StorageGateway
  • 职责:审计与提示历史写入,保证 event->audit 映射一致。
  • 最小方法:AppendAudit(...)AppendPromptHistory(...)

5. 回合状态机与事件顺序

回合状态机(engine 内部唯一真源):

  • initialized -> preparing -> model_calling -> (tool_authorizing -> tool_executing -> model_calling)* -> completing -> completed
  • 异常路径:任意中间态可转 failed

事件顺序约束(单个 TurnID 内严格单调):

  1. start
  2. delta(可 0..N)
  3. tool_use(可 0..N)
  4. tool_result(与 tool_use 成对,允许 error 结果)
  5. completeerror(二选一终态)

补充约束:

  • 每个事件必须带 session_id + trace_id + sequence
  • 若存在 runtime 任务,补 task_id 并透传到审计事件。
  • complete/error 发出后 channel 关闭。

6. 权限前置决策链(工具调用前)

对每个 tool call 执行以下流程:

  1. 归一化工具名与参数(空名直接 deny)。
  2. PolicyGateway.DecideTool 返回 {decision, reason_code, risk_level}
  3. 发射 permission_decision 审计事件。
  4. allow 才进入 tool_use -> tool_resultdeny/ask 直接回写结构化 tool_result(error)并继续回合。

说明:

  • ask 在 CLI/TUI 可映射交互确认;本 issue MVP 可先按配置降级为 deny 或 allow(需文档固定)。
  • 决策结果必须可重放(相同输入得到同类决策)。

7. prompt_too_long 的 reactive compact + retry(一次)

触发点:ModelGateway.Chat/ChatStream 返回 context too long(含本地预算与 provider 错误)。

处理流程:

  1. 首次命中 prompt_too_long 时,发 error(code=prompt_too_long_pre_retry)内部标记,不对外终止。
  2. 调用 ContextGateway.Compact(reason=reactive_prompt_too_long, keep_latest_user=true)
  3. 重新构建消息并重试模型调用一次。
  4. 若仍失败,发终态 error(code=prompt_too_long),结束回合。

一致性约束:

  • 只允许一次 reactive retry,避免无限循环。
  • 压缩后必须保留当前回合用户输入与必要 tool_use/tool_result 配对语义。

8. Runtime/子代理集成(MVP)

  • engine 不直接管理子代理线程,统一通过 RuntimeGateway 触发。
  • 同步子代理路径:
    1. Spawn 返回 task_id
    2. 发射 task_state_changed(pending/running) 审计事件
    3. Wait 收敛结果并映射为 tool_resulterror
  • 父回合取消时调用 RuntimeGateway.Cancel(task_id),并发射 task_state_changed(killed)

9. 审计模型与可回放要求

最小审计动作集合:

  • permission_decision
  • tool_execute_start
  • tool_execute_result
  • task_state_changed
  • turn_complete / turn_error

关联键要求:

  • 必带:session_id, trace_id
  • 有任务时必带:task_id
  • 同回合内建议记录:turn_id, sequence

10. Runner 兼容适配设计

  • Runner.RunPrompt* 保留签名。
  • Runner 内部流程改为:
    1. 组装 TurnRequest
    2. 调用 Engine.HandleTurn
    3. 消费 TurnEvent 并映射旧 Eventrun_started/assistant_delta/tool_call_started/...
    4. 保持当前 stdout 输出行为与测试快照不变

迁移完成判定:

  • runner.go 仅剩参数组装与适配逻辑,不再承担核心状态机。

11. 并发与故障收敛策略

  • 单回合事件流由一个主 goroutine 串行发射,保证序号严格递增。
  • 工具执行可并发扩展,但 MVP 先保持串行(行为兼容优先)。
  • 任一不可恢复错误统一落为 error 终态事件并关闭流,禁止悬挂 channel。

12. 与阶段计划的映射

  • Phase 0:冻结 PolicyGateway 优先级与 reason_code 字典。
  • Phase 1:落地 Engine + RunnerAdapter + Gateway 接口骨架
  • Phase 2:打通 TurnEvent 主链路与 reactive compact + retry
  • Phase 3:接入同步子代理 + 审计回放校验。

13. 测试矩阵(新增建议)

  1. 事件流契约
  • 顺序:start -> delta* -> tool_use* -> tool_result* -> complete|error
  • 终态唯一性与 channel close。
  1. 权限链路
  • 覆盖 hard_deny/explicit_deny/risk_rule/explicit_allow/mode_default/fallback_ask 六级优先级。
  • deny/ask 不执行工具,且有 permission_decision 审计。
  1. reactive compact
  • 首次 prompt too long -> compact -> retry 成功。
  • 二次失败 -> prompt_too_long 终态错误。
  1. 兼容层
  • Runner 入口输出与关键 Event 序列不回归。
  1. 子代理 MVP
  • Spawn + Wait 正常闭环。
  • 父回合取消能传播到 runtime 任务。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions