Files
agcore/docs/7-agent-runtime.md
T

46 KiB
Raw Blame History

Agent Runtime 方案设计

设计日期:2026-06-09 状态:待实施 关联文档:

  • docs/note-agent-runtime-design.md — 设计决策记录(接口签名、文件清单、决策依据)
  • docs/note-agent-harness-references.md — 参考项目调研(OpenClaw / Hermes / OpenHuman / OpenHarness
  • docs/6-memory-system.md — Phase 3 方案
  • docs/5-tool-system.md — Phase 2 方案
  • docs/roadmap.md — 项目总 Roadmap

1. 背景与目标

1.1 背景

AG Core 已完成 Phase 0LLM 调用周期)、Phase 1(提示词工程)、Phase 2(工具系统)、Phase 3(记忆系统)共 4 个 phase 的交付。LlmCycle::submit_with_tools() 已在 Phase 2 末实现"LLM 决策 → 工具执行 → 回传结果"的单次循环;ConversationMemory / KnowledgeStore / MemoryRetriever 在 Phase 3 提供了完整的记忆抽象。

当前缺一个整合层:把 Phase 0-3 的能力"装配"起来,对上层应用暴露"智能体"的概念。

1.2 目标

Phase 4 整体目标是提供一个薄胶水层 + 一组 trait 抽象,让上层应用可以基于 AG Core 构建多轮对话、任务规划等智能体行为。为控制 scope、降低交付风险,拆分为三个子阶段实施:

子阶段 定位 交付物
Phase 4a(核心胶水层) 最小可用 Agent Runtime Agent + AgentSession + submit_turn + RuntimeBundle / AgentBuilder / AgentError + Plan/Step 纯数据 + hooks 扩展
Phase 4b(任务执行) 自主任务规划与执行 TaskAgent + PlanParser trait + JsonPlanParser + OnPlanStepComplete hook
Phase 4c(会话级记忆) 跨 context 信息桥接 SessionMemory(基于 MemoryStore+ AgentSession 接入 + builder 支持

每个子阶段独立交付,Phase 4a 完成后上层即可接入;Phase 4b/4c 无相互依赖,可并行或按需延后。

Phase 4a 具体包括:

  • Agent trait — 智能体的"角色"抽象(不绑定 session
  • AgentSession struct — 智能体的"会话"实例(绑定 session_id + 状态)
  • RuntimeBundle — 显式依赖注入容器,集中管理 provider/registry/hook 等依赖
  • AgentBuilder — 链式构造入口
  • AgentError — 统一错误类型,聚合 LlmError / ToolError / MemoryError
  • Plan / Step / StepStatus — 任务规划纯数据结构(不做解析逻辑)

Phase 4b 追加:

  • TaskAgent trait — 任务型智能体的"规划/执行"抽象
  • PlanParser trait + JsonPlanParser — Plan 解析接口与参考实现

Phase 4c 追加:

  • SessionMemory — 会话级记忆,用于 context 间的信息桥接(基于 MemoryStore 后端)

1.3 设计原则

Phase 4 严格遵循以下原则,所有范围决策都基于这些原则推导:

原则 含义 推导
最小范围 AG Core 是 lib crate,不是产品;不实现业务循环 只暴露 trait + 最小 reference impl
薄胶水层 不在 L1 重写已经做好的能力 复用 LlmCycle::submit_with_tools 等已有 API
依赖注入 所有运行时依赖显式打包传递 采用 OpenHarness RuntimeBundle 模式
实体/会话分离 同一角色可被多 session 复用 Agent + AgentSession 两层模型
记忆弱引用 记忆是"被动能力",不内嵌循环 memory_store: Option<Arc<dyn MemoryStore>> 弱引用
业务可注入 Plan 拆解是业务能力,不在 core 库实现 暴露 PlanParser trait,上层注入
会话级记忆 session 内共享、context 间桥接,不是持久层也不是对话历史 SessionMemory 基于 MemoryStore,按 session_id 命名空间隔离
借鉴不照搬 4 个参考项目均非 Rust 实现 只取架构模式,不抄实现细节

1.4 与已完成的 Phase 关系

Phase 0  (L0/L1) ──  LlmProvider / LlmCycle / Hook / Stream / Compact
Phase 1  (L2)     ──  PromptTemplate / PromptComposer
Phase 2  (L1)     ──  ToolRegistry / BaseTool / PermissionChecker / McpClient
Phase 3  (L2)     ──  MemoryStore / ConversationMemory / KnowledgeStore / MemoryRetriever
                        ↑
                        │ 复用
                        │
Phase 4a (L1→L2) ──  Agent trait + AgentSession + submit_turn + RuntimeBundle + Plan/Step 纯数据(胶水层)
Phase 4b (L2)     ──  TaskAgent + PlanParser + JsonPlanParser(任务执行)
Phase 4c (L2)     ──  SessionMemory(会话级记忆)
                        ↓
应用层  (L4)       ──  上层 crate / 二进制 / Gateway(不在 Phase 4 范围)

详细架构对照见 docs/note-agent-harness-references.md §3-5。

2. 需求分析

2.1 功能需求

ID 需求 优先级 归属 说明
F1 Agent trait 抽象 P0 4a 角色定义:name / system_prompt / 工具集
F2 AgentSession 会话实例 P0 4a 绑定 session_id、bundle、turn_index、cost_so_far
F3 submit_turn() 最小 reference impl P0 4a 组装 LlmCycle → submit → 累计 cost~30 行
F6 Plan / Step / StepStatus 数据结构 P0 4a 含 Pending / Running / Completed / Failed / Skipped 状态机
F8 RuntimeBundle 依赖注入容器 P0 4a 聚合 provider/registry/hook/config(不含 session_memory_backend
F9 AgentBuilder 链式构造 P0 4a 构建 RuntimeBundle,retriever 存在时自动注册为 tool
F10 AgentError 统一错误类型 P0 4a 聚合 LlmError / ToolError / MemoryError,含 is_recoverable()
F11a Hook 事件扩展:OnTurnStart / OnTurnEnd + turn_index 字段 P0 4a llm/hooks.rs 中追加 2 个事件 + 1 个字段
F12a 烟雾测试 3-4 个(Phase 4a P0 4a trait 可装配 / RuntimeBundle 可构造 / submit_turn 跑通 mock / Plan 数据结构
F13 lib.rs 导出 pub mod agent; P0 4a 一行
F14 方案文档(本文件)+ 决策记录 P0 已完成
F4 TaskAgent::run(goal) 自主式入口 P0 4b 内部用 LLM 拆 Plan,再调用 execute_plan
F5 TaskAgent::execute_plan(plan) 外部驱动式入口 P0 4b 用户预定义 Plan,逐步执行
F7 PlanParser trait + JsonPlanParser 参考实现 P0 4b 注入式,上层可替换
F11b Hook 事件扩展:OnPlanStepComplete + plan_step_index 字段 P0 4b llm/hooks.rs 中追加 1 个事件 + 1 个字段
F12b 烟雾测试 2-3 个(Phase 4b P0 4b TaskAgent + PlanParser 跑通 mock
F15a Roadmap 状态翻转(Phase 4a P0 4a 实施完成后做
F15b Roadmap 状态翻转(Phase 4b P0 4b 实施完成后做
F16 SessionMemory 会话级记忆 P0 4c 基于 MemoryStorecontext 间信息桥接
F17 RuntimeBundle / Builder 扩展 session_memory_backend P0 4c 追加字段 + setter 方法
F18 AgentSession 接入 SessionMemory P0 4c 替换内联 HashMap,接入完整 SessionMemory
F12c 烟雾测试 2-3 个(Phase 4c P0 4c SessionMemory set/get/snapshot
F15c Roadmap 状态翻转(Phase 4c P0 4c 实施完成后做

2.2 非功能需求

ID 需求 说明
NF1 不引入新外部依赖 仅使用 Phase 0-3 已有的 async-trait / serde / thiserror / tokio
NF2 错误体系完善 AgentError 聚合下层错误,含 is_recoverable() 分类
NF3 线程安全 所有公开类型满足 Send + Sync
NF4 异步优先 涉及 IO 的 API 全部 async
NF5 模块化 各组件独立可替换,遵循"trait 抽象 + 轻量默认实现"惯例
NF6 文档注释 所有公开 API 必须有 /// 文档注释
NF7 builder 模式 复杂配置走 builder 链式构造
NF8 显式依赖 不引入模块级全局状态,所有依赖通过参数或 bundle 注入
NF9 不破坏现有 API Phase 0-3 的公开 API 一字不改;hooks.rs 扩展为"追加变体 + 追加字段"(兼容)
NF10 最小测试覆盖 核心 trait 至少 1 个烟雾测试;submit_turn 至少 1 个 mock 测试;不强求集成测试

3. 方案设计

3.1 总体架构

┌──────────────────────────────────────────────────────────────────────┐
│                            应用层(不在 Phase 4 范围)                 │
│   ┌────────────┐   ┌────────────┐   ┌────────────┐   ┌────────────┐    │
│   │ CLI Agent  │   │ Feishu Bot │   │ Web Service│   │  TUI App   │   │
│   └─────┬──────┘   └─────┬──────┘   └─────┬──────┘   └─────┬──────┘    │
└─────────┼────────────────┼────────────────┼────────────────┼───────────┘
          │                │                │                │
          └────────────────┴────────────────┴────────────────┘
                                    │
                                    ▼
┌──────────────────────────────────────────────────────────────────────┐
│                     Agent RuntimePhase 4                          │
│                                                                       │
│   ┌────────────────┐         ┌──────────────────┐                     │
│   │  Agent trait   │ 1 ──── * │  AgentSession    │                     │
│   │  (角色)        │         │  (会话实例)       │                     │
│   └────────────────┘         └──────┬───────────┘                     │
│                                      │ Arc<...>                       │
│                                      ▼                                │
│                             ┌──────────────────┐                      │
│                             │  RuntimeBundle   │                      │
│                             │  - provider      │                      │
│                             │  - tool_registry │                      │
│                             │  - hook_executor │                      │
│                             │  - memory_store? │ ◄─ 弱引用            │
│                             │  - retriever?    │ ◄─ 弱引用            │
│                             │  - config        │                      │
│                             └──────┬───────────┘                      │
│                                    │ new() 时若 retriever 存在        │
│                                    ▼                                  │
│                             ┌──────────────────┐                      │
│                             │ "retrieve" tool  │ ◄─ 自动注册           │
│                             └──────────────────┘                      │
│                                                                       │
│   ┌────────────────┐   ┌──────────────────┐   ┌──────────────────┐    │
│   │ TaskAgent trait│   │ Plan/Step/Status │   │ PlanParser trait │    │
│   │  run()         │   │  状态机          │   │ JsonPlanParser   │    │
│   │  execute_plan()│   │                  │   │ (参考实现 ~20行) │    │
│   └────────────────┘   └──────────────────┘   └──────────────────┘    │
│                                                                       │
│   ┌────────────────┐   ┌──────────────────┐                          │
│   │  AgentError    │   │  AgentBuilder    │                          │
│   │  (聚合)        │   │  (链式构造)       │                          │
│   └────────────────┘   └──────────────────┘                          │
└──────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼ 复用
┌──────────────────────────────────────────────────────────────────────┐
│                  LLM / Tool / Prompt / MemoryPhase 0-3            │
│   LlmCycle / ProviderRegistry / ToolRegistry / PermissionChecker /   │
│   HookExecutor / StreamEvents / CompactConfig /                      │
│   PromptTemplate / PromptComposer /                                  │
│   MemoryStore / ConversationMemory / KnowledgeStore / MemoryRetriever│
└──────────────────────────────────────────────────────────────────────┘

3.2 接口设计

详细接口签名见 docs/note-agent-runtime-design.md §4,本节说明设计意图。

3.2.1 Agent trait

pub trait Agent: Send + Sync {
    fn name(&self) -> &str;
    fn system_prompt(&self) -> Option<&str>;
    /// 列出该 Agent 想要暴露给 LLM 的工具定义。
    /// 默认实现:从 RuntimeBundle.tool_registry 取全部(最常用)。
    /// 子 trait 可覆盖做白名单/过滤。
    fn tool_definitions(&self, bundle: &RuntimeBundle) -> Vec<ToolDefinition>;
}

设计意图

  • name / system_prompt 是 LLM 调用必需的元数据
  • tool_definitions 默认从 bundle 全量取,Agent 可以在不修改 bundle 的情况下做工具白名单——这与 Hermes 的"Skill 暴露"机制对齐
  • 不在 trait 里强制 submit_turn——submit_turnAgentSession 的方法,不应绑死在角色定义上

3.2.2 RuntimeBundle

pub struct RuntimeBundle {
    pub provider: Arc<dyn LlmProvider>,
    pub tool_registry: Arc<ToolRegistry>,
    pub hook_executor: Arc<HookExecutor>,
    pub memory_store: Option<Arc<dyn MemoryStore>>,   // 弱引用
    pub retriever: Option<Arc<MemoryRetriever>>,       // 弱引用
    pub session_memory_backend: Option<Arc<dyn MemoryStore>>, // SessionMemory 后端(选填)
    pub config: AgentConfig,
}

设计意图

  • 所有运行时依赖显式打包OpenHarness 风格)
  • memory_store / retriever 均为 Option——上层应用不传也能跑(无记忆模式)
  • retriever 存在时,RuntimeBundle::new() 内部自动注册一个名为 "retrieve" 的 tool(具体实现:在 ToolRegistry 里加一个 RetrieveTool 包装),让 LLM 在对话中主动调用检索能力
  • session_memory_backendSessionMemory 的持久后端。传入时 SessionMemory 使用该后端(支持跨进程共享);不传时 AgentSession 内部自动创建 InMemoryStore 作为进程级隔离的后端
  • config 集中管理所有可调参数(max_turns、max_tool_turns、session_ttl、compact_config

3.2.3 AgentSession 与最小 reference impl

pub struct AgentSession {
    pub session_id: String,
    pub agent: Arc<dyn Agent>,
    bundle: Arc<RuntimeBundle>,
    turn_index: u32,
    cost_so_far: CostTracker,
    session_memory: SessionMemory,
}

impl AgentSession {
    /// 最小 reference impl(约 30 行):
    /// 1. 触发 OnTurnStart hook
    /// 2. 组装 LlmCycle(注入 system_prompt + messages 历史 + tool definitions
    /// 3. submit_with_tools() 跑单轮对话
    /// 4. 累计 cost
    /// 5. 触发 OnTurnEnd hook
    /// 6. turn_index += 1
    /// 7. 返回 ChatResponse
    /// 不做 memory 回写(由上层独立 task 处理)
    pub async fn submit_turn(
        &mut self,
        user_input: impl Into<String>,
    ) -> Result<ChatResponse, AgentError>;
}

设计意图

  • agent: Arc<dyn Agent> 而非 agent_name: String——submit_turn 从 agent 获取 system_prompt()tool_definitions(),同时为 v0.2+ 的"热切换 agent"预留:替换 self.agent 即可切换角色
  • session_memory 是进程内共享的会话级记忆,context 间通过它桥接信息(详见 §3.2.8)
  • "最小 reference impl" 只演示最常见的对话场景
  • 业务循环(多轮策略、错误重试、记忆回写时机)由上层应用或具体的 TaskAgent 实现决定
  • submit_turn 不持有 ConversationMemory——上层应用可独立 new 一个 ConversationMemory,在合适的时机(如 OnTurnEnd hook)调 add_message

3.2.4 TaskAgent + Plan / Step

pub struct Plan {
    pub id: String,
    pub goal: String,
    pub steps: Vec<Step>,
}

pub struct Step {
    pub index: usize,
    pub description: String,
    pub status: StepStatus,
}

pub enum StepStatus {
    Pending,
    Running,
    Completed(ChatResponse),
    Failed(AgentError),
    Skipped,
}

设计意图

  • StepStatus 用 enum 而非简单 bool,便于上层 UI 展示和统计
  • 状态机转换:Pending → Running → (Completed | Failed | Skipped),单向不可回退(重试由上层新建 Plan)
  • Plan / Step 故意保持简单——不引入 dependencies / parallel_group 等高级字段(v0.3+ 再考虑)

3.2.5 PlanParser trait + JsonPlanParser 参考实现

#[async_trait]
pub trait PlanParser: Send + Sync {
    async fn parse(&self, raw: &str, goal: &str) -> Result<Plan, AgentError>;
}

pub struct JsonPlanParser;
#[async_trait]
impl PlanParser for JsonPlanParser {
    /// 期望 LLM 输出形如:
    /// {"steps": [{"description": "..."}, ...]}
    /// 的 JSON 文本。
    /// 解析失败返回 AgentError::PlanParse。
    async fn parse(&self, raw: &str, goal: &str) -> Result<Plan, AgentError> { /* ... */ }
}

设计意图

  • 注入式:上层应用可以注入自己的 PlanParser(如基于 XML / YAML / 自定义 DSL
  • JsonPlanParser参考实现,不是默认实现——上层必须显式选择
  • JsonPlanParser 大约 20 行:serde_json::from_str 解析 + 字段映射

3.2.6 AgentError

pub enum AgentError {
    Llm(LlmError),
    Tool(ToolError),
    Memory(MemoryError),
    PlanParse(String),
    HookBlocked(String),
    LimitExceeded(String),
    Config(String),
    Other(String),
}

设计意图

  • 聚合而非包装下层错误(避免 Box<dyn Error> 丢失类型)
  • PlanParse / HookBlocked / LimitExceeded / Config 是 Agent 层特有的错误类型
  • is_recoverable() 根据变体类型判定(如 Memory(_) 可恢复、PlanParse(_) 不可恢复)

3.2.7 AgentConfig + AgentBuilder

pub struct AgentConfig {
    pub max_turns: u32,
    pub max_tool_turns: u32,
    pub session_ttl: Option<Duration>,
    pub compact_config: Option<CompactConfig>,
}

pub struct AgentBuilder { /* ... */ }
impl AgentBuilder {
    pub fn new() -> Self;
    pub fn provider(self, p: Arc<dyn LlmProvider>) -> Self;
    pub fn tool_registry(self, r: Arc<ToolRegistry>) -> Self;
    pub fn hook_executor(self, h: Arc<HookExecutor>) -> Self;
    pub fn memory_store(self, m: Arc<dyn MemoryStore>) -> Self;     // 选填
    pub fn retriever(self, r: Arc<MemoryRetriever>) -> Self;        // 选填
    pub fn session_memory_backend(self, s: Arc<dyn MemoryStore>) -> Self; // 选填
    pub fn config(self, c: AgentConfig) -> Self;
    pub fn build(self) -> Result<RuntimeBundle, AgentError>;
}

设计意图

  • AgentBuilder唯一RuntimeBundle 构造入口
  • 必填字段在 build() 时校验(provider / tool_registry / hook_executor 不可缺)
  • memory_store / retriever / session_memory_backend 选填
  • session_memory_backend 不传时,AgentSession 内部用 InMemoryStore 兜底(进程级隔离)

3.2.8 SessionMemory — 会话级记忆

pub struct SessionMemory {
    store: Arc<dyn MemoryStore>,
    namespace: String,
}

impl SessionMemory {
    /// 创建新的 session 级记忆实例。
    /// store:后端存储(可跨进程共享的 MemoryStore 实现)。
    /// namespace:按 session_id 隔离,防止跨 session 泄漏。
    pub fn new(store: Arc<dyn MemoryStore>, namespace: &str) -> Self;

    /// 写入一条 key-value 条目。
    pub async fn set(&self, key: &str, value: &str) -> Result<(), AgentError>;

    /// 读取指定 key 的值。
    pub async fn get(&self, key: &str) -> Result<Option<String>, AgentError>;

    /// 返回所有条目的格式化快照,适合注入 system prompt。
    /// 格式:
    ///   <session-context>
    ///   key1: value1
    ///   key2: value2
    ///   </session-context>
    pub async fn snapshot(&self) -> Result<String, AgentError>;

    /// 删除指定 key。
    pub async fn remove(&self, key: &str) -> Result<(), AgentError>;

    /// 清空当前 namespace 下所有条目。
    pub async fn clear(&self) -> Result<(), AgentError>;
}

设计意图

  • SessionMemory会话级记忆,不是持久层(MemoryStore)也不是对话历史(ConversationMemory)——它的定位是 session 内各 context 之间的信息桥接
  • 复用 Phase 3 MemoryStore trait:不引入新的存储后端机制。单进程场景用 InMemoryStore(零序列化开销),跨进程场景换 Redis / SQLite 等实现即可
  • namespace 隔离:每个 session 一个独立命名空间("_session_{session_id}"),避免跨 session 意外泄漏
  • snapshot() 格式化为标记文本:专为注入 system prompt 设计,LLM 可以自然理解 <session-context> 标签中的内容
  • 所有方法为 async:因为后端可能是跨进程的(Redis / DB),虽然 InMemoryStore 本身是同步操作
  • 不引入自己的错误类型:错误通过 AgentError::Memory 传播(复用已有变体)

三层记忆体系关系

持久层(Phase 3  MemoryStore / KnowledgeStore     ── 跨 session 持久,长期知识
会话层(新增)     SessionMemory                      ── 单 session 内共享,context 桥接
对话层(Phase 3  ConversationMemory                 ── 单 context 内消息历史

典型使用模式v0.2+ context 切换场景):

context_a (build agent)
  → 在对话中决定某个关键结论值得记下来
  → 调用 session_memory.set("design_decision", "用 PostgreSQL")
  → 继续对话

创建 context_b (plan agent)
  → system_prompt 末尾追加 session_memory.snapshot()
  → LLM 看到 "<session-context>\ndesign_decision: 用 PostgreSQL\n</session-context>"
  → 无需看 context_a 的 50 轮完整历史,但知道关键上下文

3.3 状态机

3.3.1 StepStatus 状态转换图

                  ┌─────────────┐
                  │   Pending   │  ◄── 初始状态
                  └──────┬──────┘
                         │ execute_plan() 进入
                         ▼
                  ┌─────────────┐
                  │   Running   │  ◄── 触发 OnPlanStepCompletestatus=Running
                  └──────┬──────┘
                         │
        ┌────────────────┼────────────────┐
        │                │                │
        ▼                ▼                ▼
   ┌─────────┐      ┌──────────┐    ┌──────────┐
   │Completed│      │  Failed  │    │ Skipped  │
   └─────────┘      └──────────┘    └──────────┘
   触发 OnPlanStepCompletestatus=Completed
                    触发 OnPlanStepCompletestatus=Failed
                                  触发 OnPlanStepCompletestatus=Skipped

设计约束

  • 状态转换单向Pending → Running → 终态),不回退
  • 终态(Completed / Failed / Skipped)触发 OnPlanStepComplete hook
  • 重试由上层应用新建 Plan 实现(不在 TaskAgent 内做自动重试)

3.3.2 Session 状态

AgentSession 的状态机比 Step 简单:

创建 (new) ──► turn_index=0 ──► submit_turn() ──► turn_index+=1 ──► ... ──► 销毁

turn_index 累加,cost_so_far 累加,无显式状态枚举(避免过度设计)。

3.4 Hook 扩展设计

src/llm/hooks.rs 中追加 3 个事件 + 2 个上下文字段:

pub enum HookEvent {
    // ... 现有 4 个:PreRequest / PostRequest / OnRetry / OnError ...

    // 新增 3 个(Phase 4):
    OnTurnStart,
    OnTurnEnd,
    OnPlanStepComplete,
}

pub struct HookContext {
    // ... 现有字段 ...

    // 新增 2 个(Phase 4):
    pub turn_index: Option<u32>,        // OnTurnStart / OnTurnEnd 用
    pub plan_step_index: Option<usize>, // OnPlanStepComplete 用
}

设计意图

  • 不破坏现有 hook 兼容性:3 个新事件是 enum 追加,2 个新字段是 Option<T> 默认 None
  • 上层应用可通过监听 OnTurnEnd 实现"独立 task 回写 ConversationMemory"——呼应"记忆在独立 task 处理"原则
  • OnPlanStepComplete 提供"步骤级别"的可观测性,与 Hermes 的"任务进度回调"对齐

3.5 错误体系

AgentError 与下层错误的关系:

┌──────────────────┐
│   AgentError     │
├──────────────────┤
│  Llm(LlmError)   │──► 透传 Phase 0 错误,含 is_recoverable()
│  Tool(ToolError) │──► 透传 Phase 2 错误,含 is_recoverable()
│  Memory(MemoryError)│─► 透传 Phase 3 错误
│  PlanParse(String)  │─► Agent 层特有
│  HookBlocked(String)│─► Agent 层特有
│  LimitExceeded(String)│► Agent 层特有
│  Config(String)   │──► Agent 层特有
│  Other(String)    │──► 兜底
└──────────────────┘
        │
        ▼
  is_recoverable(): 聚合判定
  - Llm/Memory 可恢复(重试)
  - PlanParse / Config 不可恢复(需人工介入)
  - Tool / HookBlocked / LimitExceeded 按内层错误判定

自动 From 转换:通过 #[from] 宏实现 From<LlmError> / From<ToolError> / From<MemoryError>,让 submit_turn 内部可以用 ? 运算符直接传播。

3.6 与 Phase 0-3 模块的集成

Phase 4 组件 调用的下层 API 调用位置
AgentSession::submit_turn LlmCycle::new + with_system_prompt + with_hook_executor + with_compact_config + with_messages + submit_with_tools session.rs
AgentSession::submit_turn CostTracker::add(累计 cost session.rs
RuntimeBundle::new ToolRegistry::register(注册 retrieve tool runtime.rs
TaskAgent::execute_plan AgentSession::submit_turn(每步调一次) task.rs
JsonPlanParser::parse serde_json::from_str task.rs
AgentError::from LlmError / ToolError / MemoryError error.rs
HookContext 扩展 HookEvent::OnTurnStart/End/OnPlanStepComplete llm/hooks.rs
SessionMemory::set/get/snapshot MemoryStore::save/load/search session_memory.rs

不调用的下层 API(明确边界):

  • ConversationMemory(由上层独立 task 管理)
  • KnowledgeStore(由上层独立 task 管理)
  • McpClient(已由 ToolRegistry 包装)
  • StreamEvents::submit_streamv1 暂不暴露流式 submit_turnv0.2 再说)
  • 多 context 切换管理(v0.2+ 实现,Phase 4 只预留 SessionMemory 桥接通道)
  • "session_memory_set" 等 session memory tool 自动注册(v0.2+ 可选)

4. 实施计划

Phase 4 拆分为三个独立子阶段:Phase 4a(核心胶水层)Phase 4b(任务执行)Phase 4c(会话级记忆)。每个子阶段独立交付、独立验证,4b 与 4c 无相互依赖。

4.1 文件清单

新增文件(9 个)

src/agent.rs                       # [4a] 模块根 + pub use 重导出
src/agent/agent.rs                 # [4a] Agent trait
src/agent/runtime.rs               # [4a] RuntimeBundle + AgentConfig(不含 session_memory_backend
src/agent/session.rs               # [4a] AgentSessionsubmit_turn + 内联 session_data HashMap
src/agent/task.rs                  # [4a] Plan / Step / StepStatus 纯数据 / [4b] TaskAgent + PlanParser + JsonPlanParser
src/agent/builder.rs               # [4a] AgentBuilder(不含 session_memory_backend
src/agent/error.rs                 # [4a] AgentError(不含 PlanParse 变体)/ [4b] 补充 PlanParse 变体
src/agent/session_memory.rs        # [4c] SessionMemory(基于 MemoryStore

修改文件(2 个)

src/lib.rs                         # [4a] + pub mod agent;
src/llm/hooks.rs                   # [4a] + 2 事件(OnTurnStart/OnTurnEnd+ 1 字段(turn_index
                                   # [4b] + 1 事件(OnPlanStepComplete+ 1 字段(plan_step_index

关联文档(已完成)

docs/note-agent-harness-references.md  # ✅ 已存在
docs/note-agent-runtime-design.md      # ✅ 已存在(与本文件配套)
docs/7-agent-runtime.md                # ✅ 本文件

4.2 Phase 4a — 核心胶水层(最小 Agent Runtime

范围Agent trait + AgentSession + submit_turn(内联 HashMap session_data+ RuntimeBundle/AgentBuilder + AgentError + Plan/Step 纯数据 + hooks 扩展(OnTurnStart/OnTurnEnd

任务拆解

顺序 任务 涉及文件 验证
a1 修改 llm/hooks.rs 追加 OnTurnStart / OnTurnEnd + turn_index 字段 src/llm/hooks.rs cargo build 通过;Phase 0 测试不挂
a2 新建 agent/error.rs 定义 AgentError(不含 PlanParse 变体) src/agent/error.rs cargo build 通过
a3 新建 agent/agent.rs 定义 Agent trait src/agent/agent.rs cargo build 通过
a4 新建 agent/runtime.rs 定义 RuntimeBundle + AgentConfig(不含 session_memory_backend src/agent/runtime.rs cargo build 通过
a5 新建 agent/builder.rs 定义 AgentBuilder(不含 session_memory_backend 方法) src/agent/builder.rs cargo build 通过
a6 新建 agent/session.rs 定义 AgentSession + submit_turn(内联 HashMap<String,String> 做 session_data,不引 MemoryStore src/agent/session.rs cargo build 通过
a7 新建 agent/task.rs 定义 Plan / Step / StepStatus 纯数据结构(不含 TaskAgent trait,不含 PlanParser src/agent/task.rs cargo build 通过
a8 新建 src/agent.rs 模块根 + pub use 重导出 + 修改 lib.rs src/agent.rs + src/lib.rs cargo build 通过
a9 编写烟雾测试 3-4 个(Agent trait 可装配 / RuntimeBundle 可构造 / submit_turn 跑通 mock / Plan 数据结构) src/agent/*.rs 内联 cargo test 通过
a10 完整 cargo test 跑全量回归 + roadmap.md 状态更新 所有已有测试不挂

依赖关系

hooks扩展 (a1) ──┐
                 ├──► agent/error.rs (a2) ──► agent/agent.rs (a3)
                 │                                │
                 │                                ▼
                 │                      agent/runtime.rs (a4)
                 │                                │
                 │                                ▼
                 │                      agent/builder.rs (a5)
                 │                                │
                 │                                ▼
                 │                      agent/session.rs (a6)
                 │                                │
                 │                                ▼
                 │                      agent/task.rs (a7) [纯数据]
                 │                                │
                 └──────────────────► src/agent.rs + lib.rs (a8)
                                                  │
                                                  ▼
                                            cargo test (a9 → a10)

4.3 Phase 4b — 任务执行

范围TaskAgent trait + PlanParser trait + JsonPlanParser 参考实现 + OnPlanStepComplete hook + AgentError PlanParse 变体

前置条件Phase 4a 已完成并交付。

任务拆解

顺序 任务 涉及文件 验证
b1 修改 llm/hooks.rs 追加 OnPlanStepComplete + plan_step_index 字段 src/llm/hooks.rs cargo build 通过;Phase 0 + 4a 测试不挂
b2 agent/error.rs 追加 PlanParse 变体 src/agent/error.rs cargo build 通过
b3 agent/task.rs 追加 TaskAgent trait + PlanParser trait + JsonPlanParser 参考实现 src/agent/task.rs cargo build 通过
b4 更新 agent.rs 模块根重导出(如有新增公开类型) src/agent.rs cargo build 通过
b5 编写烟雾测试 2-3 个(TaskAgent mock 执行 / JsonPlanParser 解析 / PlanParse 错误) src/agent/task.rs 内联 cargo test 通过
b6 完整 cargo test 跑全量回归 + roadmap.md 状态更新 所有已有测试不挂

依赖关系

hooks扩展 (b1) ──┐
                 ├──► error.rs 追加 (b2) ──► task.rs 追加 (b3)
                                              │
                                              ▼
                                        agent.rs 更新 (b4)
                                              │
                                              ▼
                                          cargo test (b5 → b6)

4.4 Phase 4c — 会话级记忆

范围SessionMemory struct(基于 MemoryStore+ RuntimeBundle/Build 扩展 session_memory_backend + AgentSession 接入(替换内联 HashMap

前置条件:Phase 4a 已完成并交付(可在 4b 之前、之后或并行实施)。

任务拆解

顺序 任务 涉及文件 验证
c1 新建 agent/session_memory.rs 定义 SessionMemory(基于 MemoryStorenamespace 隔离) src/agent/session_memory.rs cargo build 通过
c2 agent/runtime.rs 追加 session_memory_backend 字段到 RuntimeBundle src/agent/runtime.rs cargo build 通过
c3 agent/builder.rs 追加 .session_memory_backend() 方法 src/agent/builder.rs cargo build 通过
c4 agent/session.rs 替换内联 HashMap 为完整 SessionMemory + 更新模块根重导出 src/agent/session.rs + src/agent.rs cargo build 通过
c5 编写烟雾测试 2-3 个(SessionMemory set/get/snapshot 基于 InMemoryStore src/agent/session_memory.rs 内联 cargo test 通过
c6 完整 cargo test 跑全量回归 + roadmap.md 状态更新 所有已有测试不挂

依赖关系

session_memory.rs (c1) ──► runtime.rs 追加 (c2) ──► builder.rs 追加 (c3)
                                                          │
                                                          ▼
                                                    session.rs 修改 (c4)
                                                          │
                                                          ▼
                                                      cargo test (c5 → c6)

4.5 预估工作量(按子阶段)

子阶段 文件 行数 说明
Phase 4a hooks 扩展(2 事件 + 1 字段) ~10 追加变体 + 字段 + 文档
agent/error.rs ~40 AgentError 枚举 + From + is_recoverable
agent/agent.rs ~30 Agent trait + docs
agent/runtime.rs ~60 RuntimeBundle + AgentConfig
agent/builder.rs ~60 链式构造 + build 校验
agent/session.rs ~100 AgentSession + submit_turn + 内联 HashMap
agent/task.rs(纯数据) ~40 Plan / Step / StepStatus
src/agent.rs + lib.rs ~20 模块根 + 导出
烟雾测试 ~80 3-4 个测试
小计 ~440 核心胶水层
Phase 4b hooks 扩展(1 事件 + 1 字段) ~5 OnPlanStepComplete + plan_step_index
error.rs 追加 PlanParse ~5 1 个变体
task.rs 追加(TaskAgent + PlanParser + JsonPlanParser ~130 trait + 参考实现 + docs
烟雾测试 ~60 2-3 个测试
小计 ~200 任务执行
Phase 4c session_memory.rs ~40 5 个方法 + docs
runtime.rs / builder.rs / session.rs 修改 ~35 追加字段 + setter + 替换 HashMap
烟雾测试 ~40 2-3 个测试
小计 ~115 会话级记忆
合计 ~755 与原始预估 ~800 基本持平

5. 风险评估

5.1 抽象化边界(核心风险)

风险描述Phase 4 容易"过度抽象"——参考了 OpenHarness / Hermes 后,倾向于把它们的核心能力都搬到 Rust core 库里。

缓解措施

  • 严格遵循 §1.3 的 7 条设计原则
  • 每次添加新 trait / struct 前,先问"这属于 core 库职责吗?"
  • 业务能力(Plan 拆解、多 Agent 协同、技能加载)一律走 trait 注入或 v0.2+ 延后

5.2 对 Phase 0-3 的侵入风险

风险描述:为实现 Phase 4 需修改 src/llm/hooks.rs,可能破坏 Phase 0 的现有测试。

缓解措施

  • 只追加 enum 变体和 Option<T> 字段(NF9
  • 顺序:先跑 cargo test 确认 Phase 0 测试不挂,再开始 Phase 4
  • 详细回归验证:实施完毕后跑全量 cargo test

5.3 参考项目语言差异

风险描述OpenClaw / Hermes / OpenHarness 均为 Python/TypeScriptOpenHuman 虽是 Rust + Tauri 但定位是桌面应用。直接照搬接口形状可能导致 Rust 借用检查问题、async 复杂度增加。

缓解措施

  • §1.3 明确"借鉴不照搬"
  • 反模式列表(见 docs/note-agent-harness-references.md §6)作为排除项
  • 接口设计优先考虑 Rust 惯例(Arc<dyn Trait> / async fn / Result<T, E>

5.4 trait 设计的稳定性风险

风险描述Phase 4 是 v0.1 的第一个"复杂 trait 集合",如果 trait 形状不稳定,v0.2+ 添加新能力时会 breaking。

缓解措施

  • §3.2 的所有 trait / struct 在 docs/note-agent-runtime-design.md §4 已固化草案
  • 实施时如需调整,应先更新决策记录再改代码
  • 预留扩展点:Agent::tool_definitions 的默认实现可被子 trait 覆盖

5.5 实施进度风险

风险描述:拆为 3 个子阶段后每个阶段任务量降低(4a 约 440 行、4b 约 200 行、4c 约 115 行),但阶段间衔接(4b/4c 对 4a 的依赖)可能产生等待。

缓解措施

  • 每个子阶段独立验证,完成即交付,不阻塞后续阶段
  • 4b 和 4c 无相互依赖,可并行开工
  • 烟雾测试只验证"能跑通"不验证"业务正确"——避免陷入业务循环的细节
  • 必要时先做 MockProvider(Phase 0 已有模式),不依赖真实 LLM

6. 验收标准

6.1 通用代码验收(每个子阶段必须满足)

  • cargo build --release 0 错误 0 警告(clippy
  • cargo test 所有已有测试 + 本阶段新增测试全部通过
  • cargo doc --no-deps 所有公开 API 有 /// 文档注释
  • src/llm/hooks.rs 仅追加(不修改现有变体或字段)

6.2 Phase 4a 验收

6.2a 代码验收

  • 新增代码 ~440 行(含测试 + 文档注释),与 §4.5 预估一致
  • src/lib.rs 新增一行 pub mod agent;
  • 新增文件:agent.rs / agent/agent.rs / agent/runtime.rs / agent/builder.rs / agent/session.rs / agent/task.rs / agent/error.rs(共 7 个文件,不含 agent/builder.rs 之外的 builder 则 7 个)

6.2b 接口验收

  • Agent trait 包含 name / system_prompt / tool_definitions 三个方法
  • RuntimeBundle 包含 5 个字段:provider / tool_registry / hook_executor / memory_store? / retriever? / config(不含 session_memory_backend
  • AgentBuilder 提供 5 个 setter(不含 session_memory_backend+ build() 校验
  • AgentSessionArc<dyn Agent> 而非 agent_name: String
  • AgentSession::submit_turn 实现约 30 行,含 OnTurnStart/End hook 触发
  • AgentSession 用内联 HashMap<String, String> 做 session_data(不引 MemoryStore
  • Plan / Step / StepStatus 纯数据结构存在,状态机正确
  • AgentError 聚合 6 个变体:Llm / Tool / Memory / HookBlocked / LimitExceeded / Config / Other(不含 PlanParse
  • AgentError::is_recoverable() 对各变体返回正确分类
  • HookEvent 新增 2 个变体:OnTurnStart / OnTurnEnd
  • HookContext 新增 1 个 Option 字段:turn_index

6.2c 测试验收

  • 测试 1Agent trait 可实现 + RuntimeBundle 可构造(builder 链式调用)
  • 测试 2AgentSession::submit_turn 跑通 mock providerPhase 0 MockProvider 模式)
  • 测试 3Plan / Step / StepStatus 状态机转换正确
  • 测试 4(可选)session_data set/get 基本读写

6.2d 行为验收

  • AgentSession::submit_turn 不持有 ConversationMemorygrep 验证无 use crate::memory::ConversationMemory
  • AgentSessionArc<dyn Agent>,可从 agent 获取 system_prompt() / tool_definitions()
  • RuntimeBundle::newretrieverSome 时自动注册 "retrieve" tool
  • AgentBuilder::build 在必填字段缺失时返回 AgentError::Config(而非 panic

6.3 Phase 4b 验收

6.3a 代码验收

  • 追加代码 ~200 行(增量,在 Phase 4a 基础上),与 §4.5 预估一致
  • src/llm/hooks.rs 追加 OnPlanStepComplete + plan_step_index(不修改 Phase 4a 新增内容)

6.3b 接口验收

  • TaskAgent trait 提供双入口 run(goal) + execute_plan(plan)
  • PlanParser trait 可注入,JsonPlanParser 参考实现基于 serde_json~20 行)
  • AgentError 追加 PlanParse 变体(共 7 个变体)
  • HookEvent 追加 1 个变体:OnPlanStepComplete
  • HookContext 追加 1 个 Option 字段:plan_step_index

6.3c 测试验收

  • 测试 1TaskAgent::execute_plan 跑通 mock provider
  • 测试 2JsonPlanParser::parse 能解析合法 JSON,失败时返回 AgentError::PlanParse
  • 测试 3(可选)OnPlanStepComplete hook 触发正确

6.4 Phase 4c 验收

6.4a 代码验收

  • 追加代码 ~115 行(增量,在 Phase 4a 基础上),与 §4.5 预估一致
  • 新增文件:agent/session_memory.rs

6.4b 接口验收

  • SessionMemory 包含 5 个方法(set / get / snapshot / remove / clear),基于 MemoryStore 实现
  • SessionMemory::snapshot 返回 <session-context> 标签包裹的格式化文本
  • RuntimeBundle 追加 session_memory_backend: Option<Arc<dyn MemoryStore>> 字段
  • AgentBuilder 追加 .session_memory_backend() setter
  • AgentSession 替换内联 HashMap 为完整 SessionMemory,含 session_memory: SessionMemory 字段
  • SessionMemorysession_memory_backend 未传入时自动使用 InMemoryStore 兜底

6.4c 测试验收

  • 测试 1SessionMemory set / get / snapshot 基本读写(基于 InMemoryStore
  • 测试 2session_data 内联 HashMap ↔ SessionMemory 替换后 submit_turn 行为不变

6.5 文档验收

  • docs/7-agent-runtime.md(本文件)完整,6 段式结构齐备
  • docs/note-agent-runtime-design.md 与本文件互相引用一致
  • docs/note-agent-harness-references.md 与本文件互相引用一致
  • docs/roadmap.md 各子阶段状态按阶段翻转

6.6 风险验收

  • 5.1 抽象化边界:交付物列表中不包含 Multi-Agent / Skills / TUI / Gateway 等应用层能力
  • 5.2 Phase 0-3 侵入:git diff 显示 src/llm/hooks.rs 仅追加
  • 5.3 语言差异:trait 形状符合 Rust 惯例(无 Python 风格的复杂继承)
  • 5.4 trait 稳定性:决策记录与最终代码一致
  • 5.5 实施进度:每个子阶段实际工作量与 §4.5 预估偏差 < 30%

7. 一句话总结

Phase 4 = 3 个子阶段:4a(核心胶水层:Agent + AgentSession + submit_turn + RuntimeBundle + Plan/Step 纯数据,~440 行)→ 4b(任务执行:TaskAgent + PlanParser/JsonPlanParser~200 行)→ 4c(会话级记忆:SessionMemory + 接入,~115 行),合计 ~755 行,分步交付、逐段验证,把 Phase 0-3 已有能力"装配"成"智能体"的概念。