544 lines
26 KiB
Markdown
544 lines
26 KiB
Markdown
# 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<FinishReason>` | `stop_reason` | `StopReason` | 类型替换:`Some(FinishReason::Stop)` → `StopReason::Stop`;无 Option 包裹 |
|
||
| — | — | `id` | `String` | 新增必填字段,使用空字符串 `""` 占位 |
|
||
| — | — | `model` | `String` | 新增必填字段,使用 `"mock"` 或空字符串占位 |
|
||
| — | — | `extra` | `HashMap<String, Value>` | 新增字段,使用 `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 <name>` 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<dyn LlmProvider> = 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::<Vec<_>>();
|
||
eprintln!("AG_LLM_* 环境变量不完整(检测到: {:?}),回退到 MockProvider", found);
|
||
}
|
||
```
|
||
|
||
**工具定义**:
|
||
|
||
| 工具 | 功能 | 关键技术点 |
|
||
|------|------|-----------|
|
||
| `EchoTool` | 回显输入 | 基础工具注册模式 |
|
||
| `CalcTool` | 本地执行四则运算 | 手动解析算术表达式(ponytail:基础 +-*/ 运算无需引入 `rhai` 依赖) |
|
||
| `NoteTool` | 通过 MemoryStore trait 读写笔记 | 直接持有 `Arc<dyn MemoryStore>`,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<dyn MemoryStore>` 引用,在 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 个示例 |
|