# Phase 8 — MVP 集成出口实现方案 - **文档编号**:15 - **标题**:Phase 8 — MVP 集成出口实现方案 - **日期**:2026-07-05 - **状态**:已定稿 - **涉及模块**:全局(llm/types、agent、tools、memory、prompt、examples) - **关联文档**:roadmap.md(§Phase 8)、14-phase7-sqlite-store.md --- ## 1. 背景与目标 Phase 5-7 已交付 P0 功能闭环:ProviderConfig `from_env()`(Phase 5)、ToolDef IR 正式化(Phase 6)、SqliteStore 持久化(Phase 7)。当前 200 个测试全绿、clippy 0 警告,但缺乏一个"可被人依赖"的集成出口。 Phase 8 的目标是完成 API 稳定性扫尾 + Quick Start 示例 + 端到端示例,产出 **v0.2.0-rc.1** 标签。三个 Step 分别对应三类用户群体: | Step | 受众 | 交付物 | |------|------|--------| | **8.1** | 存量升级者(v0.1 → v0.2) | API 稳定性扫尾 + CHANGELOG | | **8.2** | 新用户评估者("30 秒决定要不要用") | Quick Start 示例 | | **8.3** | 技术决策者("这框架能跑真实场景吗") | 端到端集成示例 | --- ## 2. 当前状态 | 度量 | 数值 | |------|------| | `cargo test --all-targets` | ✅ 200 passed / 0 failed | | `cargo clippy --all-targets -- -D warnings` | ✅ 0 警告 | | 已存在 `#[non_exhaustive]` 枚举 | 4 个(StopReason / FinishReason / EvictionPolicy / ProviderType) | | 已存在 `#[deprecated]` 项 | 3 个(ChatResponse / ToolDefinition / task_agent_demo 中旧类型使用) | | 已有示例 | 8 个 | | `StepStatus::Completed` 使用类型 | `ChatResponse`(已 `#[deprecated]`) | ### 2.1 关键技术债 ``` // agent/task.rs —— StepStatus 当前使用已废弃类型 #[allow(deprecated)] pub enum StepStatus { Completed(ChatResponse), // ← ChatResponse 已在 0.1.0 标记 #[deprecated] ... } ``` `task_agent_demo.rs` 中同时使用了 `ChatResponse` / `OpenaiChatMessage` / `FinishReason` 三个废弃类型,入口处有 `#![allow(deprecated)]`。 --- ## 3. 实施方案 ### 3.1 Step 8.1 — API 稳定性扫尾 拆为 4 个增量 commit: | Commit | 内容 | 涉及文件 | |--------|------|---------| | **commit 1** | 14 个公开枚举追加 `#[non_exhaustive]` | 各枚举定义文件(详见 §3.1.1) | | **commit 2** | `StepStatus::Completed(ChatResponse)` → `Completed(MessageResponse)` + `task_agent_demo.rs` 清理全部 3 个废弃类型(`ChatResponse` / `OpenaiChatMessage` / `FinishReason`),移除 `#![allow(deprecated)]` | `src/agent/task.rs`、`examples/task_agent_demo.rs` | | **commit 3** | CHANGELOG v0.2 条目 + Cargo.toml version → `0.2.0-rc.1` + README 更新 | `CHANGELOG.md`、`Cargo.toml`、`README.md` | | **commit 4** | 验证:`cargo test + clippy + cargo doc` 零告警 | 无代码改动 | #### 3.1.1 `#[non_exhaustive]` 追加清单(14 个枚举) 按优先级分级: | 优先级 | 枚举 | 模块路径 | 理由 | |--------|------|---------|------| | **P0 核心** | `Message` | `llm/types/message.rs` | 核心 IR 类型,未来可能新增变体(MultiModal 扩展) | | | `ContentBlock` | `llm/types/message.rs` | 同上 | | | `ContentBlockType` | `llm/types/message.rs` | 同上 | | | `StreamEvent` | `llm/types/response_v2.rs` | 流式事件集,Provider 扩展可能新增事件 | | | `HookEvent` | `llm/hooks.rs` | 生命周期钩子,框架扩展需要新增事件点 | | **P0 Error** | `AgentError` | `agent/error.rs` | 顶层错误,下游 match 需保护 | | | `LlmError` | `llm/error.rs` | LLM 调用错误 | | | `ToolError` | `tools/error.rs` | 工具系统错误 | | | `MemoryError` | `memory/error.rs` | 记忆系统错误 | | | `PromptError` | `prompt/error.rs` | 提示词工程错误 | | **P1 其他** | `MemoryStrategy` | `memory/conversation.rs` | 对话策略,未来可扩展(如 Summarize) | | | `StepStatus` | `agent/task.rs` | 步骤状态机,可扩展(如 Cancelled) | | | `ToolChoice` | `llm/types/request.rs` | Provider 工具选择策略 | | | `ResponseFormat` | `llm/types/shared.rs` | 响应格式枚举 | **明确不加的**: | 类别 | 枚举 | 原因 | |------|------|------| | 内部 wire-format | `OpenaiChatMessage` / `OpenaiTool` / `OpenaiToolCall` / `ContentField` / `OpenaiContentPart` / `LegacyStreamEvent` | 内部转换层,不构成公共 API 契约 | | 语义稳定 | `Role` / `ServiceTier` / `Modality` / `ImageDetail` / `AudioFormat` / `StopSequence` | 语义已收敛,协议层无新增变体预期 | | 使用面窄 | `TemplateValue` / `Permission` / `McpTransport` / `ContentBlockBuilder` / `ExtraError` | 内部实现细节或使用频率极低,下游不直接 match | > **`#[non_exhaustive]` 的不可逆性**:一旦 v0.2.0-rc.1 发布,以下游代码可能依赖 `_ =>` 通配分支。在 v0.3+ 中移除 `#[non_exhaustive]` 将构成 semver breaking change(新增变体不再触发编译警告,下游 match 可能遗漏新变体),因此当前追加的标记应视为永久 API 契约。 #### 3.1.2 StepStatus 迁移细节 ```rust // 变更前 #[allow(deprecated)] pub enum StepStatus { Completed(ChatResponse), // ChatResponse 已 #[deprecated] ... } // 变更后 #[non_exhaustive] pub enum StepStatus { Completed(MessageResponse), ... } ``` **字段映射差异**:`MessageResponse` 不是 `ChatResponse` 的简单改名——两者结构不同,迁移需要做字段适配: | ChatResponse 字段 | 类型 | MessageResponse 字段 | 类型 | 映射方式 | |-------------------|------|---------------------|------|---------| | `message` | `OpenaiChatMessage` | `message` | `Message` | 类型替换:`OpenaiChatMessage::assistant_text(t)` → `Message::Assistant { content: vec![ContentBlock::Text { text: t.into() }] }` | | `usage` | `Usage` | `usage` | `Usage` | ✅ 同类型,直接迁移 | | `stop_reason` | `Option` | `stop_reason` | `StopReason` | 类型替换:`Some(FinishReason::Stop)` → `StopReason::Stop`;无 Option 包裹 | | — | — | `id` | `String` | 新增必填字段,使用空字符串 `""` 占位 | | — | — | `model` | `String` | 新增必填字段,使用 `"mock"` 或空字符串占位 | | — | — | `extra` | `HashMap` | 新增字段,使用 `HashMap::new()` 占位 | **迁移示例**(`task_agent_demo.rs` 的构造代码): ```rust // 旧代码(3 个废弃类型) StepStatus::Completed(ChatResponse { message: OpenaiChatMessage::assistant_text("天气:晴,22°C"), usage: Usage::from_input_output(10, 5), stop_reason: Some(FinishReason::Stop), }) // 新代码(纯 MessageResponse) StepStatus::Completed(MessageResponse { id: String::new(), model: "mock".into(), message: Message::assistant("天气:晴,22°C"), usage: Usage::from_input_output(10, 5), stop_reason: StopReason::Stop, extra: HashMap::new(), }) ``` 涉及文件: - `src/agent/task.rs`:枚举定义 + `#[allow(deprecated)]` 移除 + `#[non_exhaustive]` 追加 - `examples/task_agent_demo.rs`:`ChatResponse{...}` → `MessageResponse{...}` 构造替换,同时替换 `OpenaiChatMessage` / `FinishReason` 引用,移除 `#![allow(deprecated)]` ### 3.2 Step 8.2 — Quick Start 示例 | 属性 | 值 | |------|-----| | 文件 | `examples/quick_start.rs` | | 规模 | ~36 行 | | Provider | `MockProvider`(FIFO 单响应队列) | | 工具 | `EchoTool`(回传 `"收到: {input}"`,完整 JSON Schema 参数声明) | | 执行 | `submit_turn("你好")` → 验证输出包含 `"收到"` | | 验证 | `cargo run --example quick_start` exit 0 | 设计要点: - 展示四层抽象:Agent trait / BaseTool 自定义 / AgentBuilder 装配 / AgentSession 执行 - 无外部依赖、无 API key、零配置 ### 3.3 Step 8.3 — 端到端示例 | 属性 | 值 | |------|-----| | 文件 | `examples/end_to_end.rs` | | 规模 | ~160 行(**最小可行边界**:3 工具 + 3 轮 + 持久化验证,防止实施中进一步膨胀) | | Provider | 自动检测 `AG_LLM_*` → `from_env()`,fallback 到 `MockProvider` | | 工具组合 | EchoTool(回显)+ CalcTool(四则运算,本地执行)+ NoteTool(笔记,通过 MemoryStore trait 操作 SessionMemory) | | 持久化 | `tempfile::TempDir` + `SqliteStore`,drop 后重建连接验证数据不丢 | | 对话 | 3 轮:计算 → 记笔记 → 回忆 | | 验证 | `cargo run --example end_to_end` exit 0(无需任何外部配置) | **真实 Provider 切换**:示例在文件顶部注释中说明 "设置 `AG_LLM_BASE_URL` / `AG_LLM_API_KEY` / `AG_LLM_MODEL` 环境变量即可使用真实 LLM Provider(支持 OpenAI / Ollama 等);未设置时自动降级为 MockProvider,零配置可运行。" **`from_env()` 部分环境变量策略**:`from_env()` 要求完整的三件套(`{prefix}_BASE_URL` + `{prefix}_API_KEY` + `{prefix}_MODEL`)。当环境变量部分设置时,示例**整体降级到 MockProvider**——不在"半配置"状态下尝试部分初始化。日志输出形如 `"AG_LLM_* 环境变量不完整(检测到: {found_vars}),回退到 MockProvider"`。 架构亮点: ``` ┌─────────────────────────┐ │ AgentSession │ │ (submit_turn × 3) │ └────┬──────┬──────┬──────┘ │ │ │ ┌────┘ │ └──────┐ ▼ ▼ ▼ ┌──────────┐ ┌────────┐ ┌──────────┐ │ EchoTool │ │CalcTool│ │ NoteTool │ │ (回显) │ │(四则) │ │ (记忆) │ └──────────┘ └────────┘ └────┬─────┘ │ ┌──────▼──────┐ │ SessionMemory│ │ (MemoryStore)│ └──────┬──────┘ │ ┌──────▼──────┐ │ SqliteStore │ │ (temp dir) │ └─────────────┘ ``` NoteTool 展示 `MemoryStore` trait 解耦能力:不绑定 SqliteStore,上层 `AgentSession` 通过 `SessionMemory` 操作,底层可互换。 --- ## 4. 否决项记录 | 否决方案 | 否决原因 | |---------|---------| | `#[non_exhaustive]` 仅加 5 个核心类型 | 全面覆盖 Error enums 为零运行时成本,对下游更友好。Error 枚举是下游 match 最密集的地方,漏标会在 v0.3 引入 breakage | | StepStatus::Completed 留到 v0.3 再修 | rc.1 前清理 deprecated 类型污染最划算——越晚 migration cost 越高,且当前仅 1 个示例 + 1 个测试引用 | | Quick Start 纯文本路线(不展示自定义工具) | 含 EchoTool 展示核心差异化,仅多 5 行代码但传递了"可以自定义工具"的关键信息 | | 端到端仅 Echo + Calc(无 NoteTool) | NoteTool 展示 MemoryStore trait 解耦能力是架构亮点,跳过后新用户无法理解 memory 如何集成到 Agent 流程 | | 持久化仅注释说明不实际运行(方案 Y) | 进程内实操验证(create → drop → reopen → assert)比注释更有说服力,增加约 15 行代码 | --- ## 5. 关键假设 1. **MockProvider FIFO 队列满足 auto-tool-loop 消费顺序**:MockProvider 的 `pop()` 按预设顺序弹出。当 LLM 返回多个 tool call 时队列消费顺序与预设一致,无需额外同步 2. **StepStatus 切换需做字段适配**:`ChatResponse`(3 字段) 到 `MessageResponse`(6 字段) 存在字段类型差异(`message` 类型不同、`stop_reason` 类型 + Option 有无不同、`id`/`model`/`extra` 为新增必填字段),消费者需按字段映射表提供占位值。但消费者仅 1 个(`task_agent_demo.rs`)+ 1 个内联测试,手动适配工作量极小。`StepStatus` 的 `is_terminal()` / `is_pending()` 行为不受影响 3. **所有 10 个示例零外部配置 exit 0**:已有 8 个示例已验证,新增 2 个(quick_start + end_to_end)均使用 MockProvider fallback,无需 API key 4. **`#[non_exhaustive]` × 14 不触发额外 clippy warning**:当前无代码对以上枚举做 exhaustive match(不含 `_`),追加 `#[non_exhaustive]` 是纯安全标记 --- ## 6. 实施顺序与验证标准 ### 6.1 提交顺序 ``` Step 8.1 (4 commits) → commit 1: #[non_exhaustive] × 14 → commit 2: StepStatus 修复(Completed(ChatResponse) → Completed(MessageResponse)) → commit 3: CHANGELOG v0.2 + Cargo.toml version 0.2.0-rc.1 + README 更新 → commit 4: 验证(test / clippy / doc 零告警) Step 8.2 → commit 5: examples/quick_start.rs(~36 行) Step 8.3 → commit 6: examples/end_to_end.rs(~160 行) 最终验证 → cargo test --all-targets → cargo clippy --all-targets -- -D warnings → cargo doc --no-deps → git tag v0.2.0-rc.1 ``` ### 6.2 验收标准 | 指标 | 要求 | |------|------| | `cargo test --all-targets` | 全绿 | | `cargo clippy --all-targets -- -D warnings` | 0 警告 | | `cargo doc --no-deps` | 0 warning | | 所有 10 个示例 | `cargo run --example ` exit 0 | | Cargo.toml version | `0.2.0-rc.1` | | CHANGELOG | v0.2 条目完整(Added / Changed / Deprecated / Fixed / Removed 各节) | | README | 示例列表 + 版本号更新 | | git tag | `v0.2.0-rc.1` | --- ## 7. 参考来源 - **roadmap.md** — Phase 8 原始定义(Step 8.1/8.2/8.3)、依赖关系(Phase 5/6/7 → Phase 8) - **`src/agent/task.rs`** — `StepStatus` 当前实现,`Completed(ChatResponse)` 类型 - **`src/llm/types/message.rs`** — `Message` / `ContentBlock` / `ContentBlockType` 枚举定义 - **`src/llm/types/response_v2.rs`** — `StreamEvent` / `StopReason` 枚举定义(StopReason 已有 `#[non_exhaustive]`) - **`src/llm/types/shared.rs`** — `ResponseFormat` / `Role` / `FinishReason` 等枚举(FinishReason 已有 `#[non_exhaustive]`) - **`src/llm/types/request.rs`** — `ToolChoice` 枚举定义 - **`src/llm/hooks.rs`** — `HookEvent` 枚举定义 - **`src/llm/error.rs`** — `LlmError` 枚举定义 - **`src/agent/error.rs`** — `AgentError` 枚举定义 - **`src/tools/error.rs`** — `ToolError` 枚举定义 - **`src/memory/error.rs`** — `MemoryError` 枚举定义 - **`src/memory/conversation.rs`** — `MemoryStrategy` 枚举定义 - **`src/prompt/error.rs`** — `PromptError` 枚举定义 - **`examples/task_agent_demo.rs`** — 当前使用 `#[allow(deprecated)]` + `ChatResponse` 的示例 --- ## 8. 实施计划 ### 8.1 实施步骤 #### Step 8.1 — API 稳定性扫尾 拆为 4 个增量 commit,依次提交。 ##### commit 1: #[non_exhaustive] × 14 | 属性 | 值 | |------|-----| | 涉及文件 | 14 个枚举定义所在文件(见下方清单) | | 前置依赖 | 无 | | 预估工作量 | S(<1h) | | 风险等级 | 低 | 在每个目标枚举定义处的 `pub enum` 之前加一行 `#[non_exhaustive]`,纯文本属性追加,无逻辑变更。 | 目标枚举 | 文件路径 | 行号附近 | |---------|---------|---------| | `Message` | `src/llm/types/message.rs` | `pub enum Message` (L22) | | `ContentBlock` | `src/llm/types/message.rs` | `pub enum ContentBlock` (L99) | | `ContentBlockType` | `src/llm/types/message.rs` | `pub enum ContentBlockType` (L134) | | `StreamEvent` | `src/llm/types/response_v2.rs` | `pub enum StreamEvent` (L167) | | `HookEvent` | `src/llm/hooks.rs` | `pub enum HookEvent` (L9) | | `AgentError` | `src/agent/error.rs` | `pub enum AgentError` (L20) | | `LlmError` | `src/llm/error.rs` | `pub enum LlmError` (L10) | | `ToolError` | `src/tools/error.rs` | `pub enum ToolError` (L6) | | `MemoryError` | `src/memory/error.rs` | `pub enum MemoryError` (L8) | | `PromptError` | `src/prompt/error.rs` | `pub enum PromptError` (L3) | | `MemoryStrategy` | `src/memory/conversation.rs` | `pub enum MemoryStrategy` (L14) | | `StepStatus` | `src/agent/task.rs` | `pub enum StepStatus` (L59) | | `ToolChoice` | `src/llm/types/request.rs` | `pub enum ToolChoice` (L14) | | `ResponseFormat` | `src/llm/types/shared.rs` | `pub enum ResponseFormat` (L70) | > **注意**:`StepStatus` 在 commit 2 中会同时被修改(variant 类型替换 + 移除 `#[allow(deprecated)]`)。commit 1 仅追加 `#[non_exhaustive]` 属性,commit 2 再处理变体变更和清理。 **验收条件**:`cargo build --all-targets` 通过 ##### commit 2: StepStatus 修复 + 废弃类型清理 | 属性 | 值 | |------|-----| | 涉及文件 | `src/agent/task.rs`,`examples/task_agent_demo.rs` | | 前置依赖 | commit 1(StepStatus 先标记 `#[non_exhaustive]`,此处改 variant 时一并保留,无实际冲突) | | 预估工作量 | S(<1h,约 20 行改动) | | 风险等级 | 低 | 两步操作: 1. **`src/agent/task.rs`**(L59-L71): - `StepStatus::Completed(ChatResponse)` → `Completed(MessageResponse)` - 移除 `#[allow(deprecated)]`(第 13、59 行两处) 2. **`examples/task_agent_demo.rs`**: - 替换 3 个废弃类型:`ChatResponse` → `MessageResponse`,`OpenaiChatMessage::assistant_text(t)` → `Message::assistant(t)`,`FinishReason::Stop` → `StopReason::Stop` - 补充 `id: String::new()`,`model: "mock".into()`,`extra: HashMap::new()` 占位字段 - 移除 `#![allow(deprecated)]`(第 26 行) - 移除 `use` 中的 `ChatResponse`、`OpenaiChatMessage`、`FinishReason` - 添加 `use std::collections::HashMap`,`use agcore::llm::types::{Message, MessageResponse, StopReason}`(注意:`Message::assistant_text(t)` 不存在,需使用 `Message::assistant(t)`) 字段映射参见 §3.1.2 的字段映射表和迁移示例。 **验收条件**:`cargo build --all-targets` 通过,零 deprecated warning ##### commit 3: CHANGELOG + 版本号 + README | 属性 | 值 | |------|-----| | 涉及文件 | `CHANGELOG.md`,`Cargo.toml`,`README.md` | | 前置依赖 | commit 1+2(CHANGELOG 需记录实际变更) | | 预估工作量 | S(<1h) | | 风险等级 | 低 | 1. **`CHANGELOG.md`**:新增 `[0.2.0-rc.1]` 条目,包含: - **Added**:SqliteStore 持久化 / OllamaProvider / ProviderConfig::from_env / ToolDef IR / Quick Start 和 end_to_end 示例 - **Changed**:MessageRequest.tools 切换 ToolDef / StepStatus::Completed 类型替换 - **Deprecated**:ChatResponse / with_system_prompt() / with_client() - **Non-exhaustive**:14 个枚举标记清单 2. **`Cargo.toml`**:第 3 行 `version = "0.1.0"` → `version = "0.2.0-rc.1"` 3. **`README.md`**:更新示例列表从 7 个改为 10 个(含新增 2 个),版本号同步 **验收条件**:人工 review CHANGELOG + `git diff` 确认版本号 ##### commit 4: 验证 | 属性 | 值 | |------|-----| | 涉及文件 | 无代码改动 | | 前置依赖 | commit 3 | | 预估工作量 | S(<1h,主要等待编译) | | 风险等级 | 低 | 运行三条命令: ```bash cargo test --all-targets cargo clippy --all-targets -- -D warnings cargo doc --no-deps 2>&1 | grep "^warning:" && echo "WARNINGS FOUND" || echo "0 warnings" ``` **验收条件**:前两条 0 错误,第三条输出 `0 warnings` #### Step 8.2 — Quick Start 示例 ##### commit 5: examples/quick_start.rs | 属性 | 值 | |------|-----| | 涉及文件 | `examples/quick_start.rs` | | 前置依赖 | 无(可从 Phase 7 独立创建) | | 预估工作量 | S(<1h) | | 风险等级 | 低 | 新文件 `examples/quick_start.rs`,~36 行,结构如下: ``` 1- 6 use 块(agcore 类型 + Arrow/std 类型) 7- 8 struct Greeter + impl Agent(name / system_prompt) 9-14 struct EchoTool + #[async_trait] impl BaseTool(完整 JSON Schema 带 text 参数) 15-20 fn mock_response() -> MessageResponse 辅助函数(构造纯文本响应) 21-33 #[tokio::main] async fn main(): - ToolRegistry::new() + register EchoTool - MockProvider 预设 1 条 mock_response - AgentBuilder::new() + provider + tool_registry + hook_executor → build - AgentSession::new + submit_turn("你好") - println!("{}", response.text()) ``` **设计约束**: - EchoTool 的 `parameters()` 返回完整 JSON Schema:`{"type":"object","properties":{"text":{"type":"string"}},"required":["text"]}` - 无外部依赖、无 API key、零配置 - 展示四层抽象:Agent trait / BaseTool 自定义 / AgentBuilder 装配 / AgentSession 执行 **验收条件**:`cargo run --example quick_start` exit 0,输出包含 `"收到"` #### Step 8.3 — 端到端示例 ##### commit 6: examples/end_to_end.rs | 属性 | 值 | |------|-----| | 涉及文件 | `examples/end_to_end.rs` | | 前置依赖 | commit 5(示例编写模式已建立);SqliteStore(Phase 7 已完成) | | 预估工作量 | M(1-4h) | | 风险等级 | 中 | 新文件 `examples/end_to_end.rs`,~160 行,最小可行边界(3 工具 + 3 轮 + 持久化验证)。 **Provider 初始化策略**: ``` if env::var("AG_LLM_BASE_URL").is_ok() && env::var("AG_LLM_API_KEY").is_ok() { // 使用真实 Provider(AG_LLM_MODEL 非必填,from_env 内部会处理默认值) let provider: Arc = Arc::from(create_provider( ProviderType::OpenaiChat, ProviderConfig::from_env("AG_LLM").unwrap() )?); } else { // MockProvider fallback,预设 4 条响应序列 let found = ["AG_LLM_BASE_URL", "AG_LLM_API_KEY"].iter() .filter(|k| env::var(k).is_ok()).collect::>(); eprintln!("AG_LLM_* 环境变量不完整(检测到: {:?}),回退到 MockProvider", found); } ``` **工具定义**: | 工具 | 功能 | 关键技术点 | |------|------|-----------| | `EchoTool` | 回显输入 | 基础工具注册模式 | | `CalcTool` | 本地执行四则运算 | 手动解析算术表达式(ponytail:基础 +-*/ 运算无需引入 `rhai` 依赖) | | `NoteTool` | 通过 MemoryStore trait 读写笔记 | 直接持有 `Arc`,key 前缀 `"note:"`;save 用 `MemoryStore::save(MemoryItem { id: "note:{key}", content, .. })`,query 用 `MemoryStore::list(MemoryFilter { prefix: Some("note:"), .. })` | **持久化验证**: ```rust let dir = tempfile::TempDir::new()?; let db_path = dir.path().join("agcore.db"); let backend = Arc::new(SqliteStore::open(&db_path)?); // ... 构建 RuntimeBundle + AgentSession,写入数据 ... drop(bundle); // 释放所有对 backend 的 Arc 引用 drop(session); // 此时 backend 无活跃引用,SQLite 连接自动关闭 let backend2 = Arc::new(SqliteStore::open(&db_path)?); // 重建连接 // assert 数据仍在 ``` **输出示范**: ``` === agcore 端到端演示 === 🔄 Provider: MockProvider (离线回退模式) 💾 SqliteStore: /tmp/agcore_XXXXX/agcore.db 🔧 注册工具: echo, calc, note 第 1 轮 用户: 帮我算 25 * 4 → 调用 calc(...) → 100 → 回答: 25 * 4 = 100 第 2 轮 用户: 记下来:结果是 100 → 调用 note(save, ...) → 回答: 已记录 第 3 轮 用户: 我刚才算了什么? → 调用 note(query) → 回答: 您刚才的计算结果是 100 📊 用量: prompt=XX, completion=XX === 持久化验证 === ✓ 跨连接数据存活验证通过 ✓ 端到端演示完成 ``` **设计约束**: - 文件顶部注释说明 `AG_LLM_*` 环境变量切换真实 Provider - 零外部配置可运行(Mock fallback) - 最小可行边界:3 工具 + 3 轮 + 持久化验证,不膨胀 **验收条件**:`cargo run --example end_to_end` exit 0(零外部配置) ### 8.2 并行机会 commit 1 和 commit 5 可以并行执行(零文件重叠)。commit 5 也可与 commit 2 并行。commit 6 实质上也仅依赖「代码库状态稳定」而非某个具体 commit。 | 并行组 | commit A | commit B | 前提 | |--------|---------|---------|------| | 1 | commit 1(#[non_exhaustive]) | commit 5(Quick Start) | 零文件重叠 | | 2 | commit 2(StepStatus 修复) | commit 5(Quick Start) | 零文件重叠 | | 3 | commit 5(Quick Start) | commit 6(端到端) | 零文件重叠,但存在知识依赖——commit 6 需参考 commit 5 的 `MessageResponse` 构造、`MockProvider` 用法、`AgentBuilder` 装配模式。推荐 commit 5 先行或实施前同步这些模式 | ### 8.3 风险与应对 | 风险 | 影响 | 可能性 | 应对 | |------|------|--------|------| | MockProvider 响应序列与 tool-loop 消费顺序不匹配 | commit 6 端到端示例不通过 | 中 | 按 §5 假设 1:设计响应队列时确保每条 Mock 响应的 `stop_reason` 与 ToolUse/Stop 匹配。出现不匹配时改用完整 `MessageResponse` 构造显式控制 | | NoteTool 与 AgentSession 的数据传递路径需要扩展现有 API | commit 6 需要修改 `session.rs` | 低 | ponytail 方案:NoteTool 直接持有 `Arc` 引用,在 execute 时直接操作 `MemoryStore::save/get`,绕过 AgentSession 的 session_memory 封装 | | `#[non_exhaustive]` 在某个 enum 上导致 crate 内 match 编译失败 | commit 1 不通过 | 低 | 实施前先运行 `rg "match.*(Message|ContentBlock|ContentBlockType|StreamEvent|HookEvent|AgentError|LlmError|ToolError|MemoryError|PromptError|MemoryStrategy|StepStatus|ToolChoice|ResponseFormat)" src/ --include="*.rs"` 快速扫描 exhaustive match。若某 enum 编译失败,回退该 enum 上的 `#[non_exhaustive]` 属性,标注原因 | ### 8.4 测试策略 | commit | 测试 | 方式 | |--------|------|------| | commit 1 | 编译测试 | `cargo build --all-targets` | | commit 2 | 编译 + 单测 + 无 deprecated warning | `cargo build --all-targets && cargo test` | | commit 3 | 人工 review | `git diff` | | commit 4 | 全量自动化 | `cargo test + clippy + doc` | | commit 5 | 示例运行 | `cargo run --example quick_start` | | commit 6 | 示例运行 | `cargo run --example end_to_end` | | 最终 | 全量回归 | 全部三项 + 所有 10 个示例 |