将 note、pdd、prd、roadmap 四类文档分别归入 `design/` 下对应子目录中,并新增 `.gitkeep` 占位文件
13 KiB
v0.1 发布实施计划
状态:待实施 关联文档:
docs/roadmap.md、docs/8-examples-plan.md、docs/10c-phase2-llm-cycle-simplify.md
背景与目标
AG Core 已完成 Phase 0-4c 全部 7 个 Phase 的核心功能,以及 Provider IR 重构(新类型系统 + Anthropic/DeepSeek/Qwen Provider)+ LlmCycle 简化(IR 消息类型切换 + 桥接层移除)。
当前基线:
cargo build— ✅ 通过(5 个 warning)cargo test— ❌ 编译失败(4 个 error:session.rs/cycle.rs 测试模块中ContentBlock/ProviderCapabilities/ProviderFeatures导入缺失,均为 Provider IR 重构后未同步的回归)- LlmCycle 简化合并前全量测试为 177 通过,合并后尚未跑通过过全量测试
v0.1 发布的阻塞项不是"功能不足",而是"已有功能不能被用户快速看见和使用"。本计划聚焦于:
- 扫清技术债 — composer.rs 迁移、锁修复、clippy 清零
- 提供可离线运行的示例 — 让用户 5 分钟内上手
- 补充面向用户的文档 — README、MockProvider、错误消息友好化
- 完成发布前准备 — CI 验证、Roadmap 同步、版本标记
总体时间线
预计 8-10 个工作日(2 周),分为两条并行线:
Week 1 ────┬── 测试修复 + 技术债扫清
│ ├ T0 测试编译回归修复(~0.5d)← ⚠️ 任何改动前的前置步骤
│ ├ T1.1 composer.rs IR 迁移(~1d)
│ ├ T1.2 knowledge.rs 锁修复(~0.5d)
│ ├ T1.3 旧类型废弃标记(~0.5d)
│ └ T1.4 clippy 清零(~0.5d)
│
├── 示例 + 开发者体验
│ ├ T2.0 MockProvider 公开化(~0.5d)
│ ├ T2.1 4个🥇示例(~2-3d,可并行)
│ └ T3.1 README 初稿(~1d,并行)
│
Week 2 ────┬── 示例继续
│ ├ T2.2 3个🥈示例(~2d)
│ └ T3.2 错误消息 review(~0.5d)
│
└── 发布准备
├ T3.3 README 定稿(~1d)
├ T4.1 CI 示例验证(~0.5d)
├ T4.2 Roadmap 更新(~0.5d)
├ T4.3 CHANGELOG 初始化(~0.5d)
└ T4.4 v0.1 tag(~0.5d)
实施步骤
Phase A — 测试修复 + 技术债扫清(Week 1 前半)
重要:Task A0 是后续所有任务的前提——不修复测试编译,所有 Task 的验收条件(
cargo test全通过)都不可执行。
Task A0:修复测试编译回归(~0.5d)
问题:Provider IR 重构后,ContentBlock/ProviderCapabilities/ProviderFeatures 被移到新位置,但 2 个测试模块的导入未同步,导致 cargo test 编译失败。
涉及文件:
src/agent/session.rs— 测试模块缺少ContentBlock导入src/llm/cycle.rs— 测试模块缺少ProviderCapabilities/ProviderFeatures导入
修复方案:在相应测试模块内补全 use 语句(每处加一行即可)。
额外收益:修复后 convert.rs 中 2 处 irrefutable if let 警告也一并修正(改为 let),减少 clippy 基数。
前置依赖:无
验收条件:
cargo test全量编译通过cargo test全量运行通过
Task A1:composer.rs IR 迁移(~1d)
前置依赖:Task A0(否则无法通过 cargo test 验证)
涉及文件:
src/prompt/composer.rs— 主要改动src/prompt/mod.rs— 类型重导出
改动范围:约 60 处 OpenaiChatMessage → Message,ContentField/OpenaiContentPart → ContentBlock
具体清单:
PromptComposer内部messages字段类型Vec<OpenaiChatMessage>→Vec<Message>- 所有
.system_text()/.user_text()/.assistant_text()/.developer_text()/.tool_result()构造方法 — 新Message已有同名方法,直接替换调用 - 所有
xx_content()/xx_contents()方法 —ContentBlock替代ContentField/OpenaiContentPart set_message_name()— 新Message是扁平 enum,无name字段,移除或忽略build()返回类型Vec<OpenaiChatMessage>→Vec<Message>validate_messages()消息校验函数 —OpenaiChatMessage::Tool { .. }→Message::ToolResult { .. },tool_calls从Assistant变体的ContentBlock::ToolUse中提取
测试:内联测试中 OpenaiChatMessage::Tool { .. } pattern match 需同步更新
验收条件:
cargo build无错误cargo test中 prompt 模块测试全通过- 无新增 clippy 警告
Task A2:knowledge.rs 锁修复(~0.5d)
涉及文件:src/memory/knowledge.rs
问题:std::sync::Mutex guard 在 search() 方法中跨 .await 持有,可能在高并发下阻塞 tokio 工作线程
修复方案:std::sync::Mutex<Vec<PageIndexEntry>> → tokio::sync::Mutex<Vec<PageIndexEntry>>
影响范围:5 处 .lock().unwrap() 调用(rebuild_index、add_page、delete_page、search、get_index)
验收条件:
cargo build无错误cargo test中 memory 模块测试全通过- clippy 不再报
await_holding_lock警告
Task A3:旧类型公开 API 废弃标记(~0.5d)
涉及文件:src/llm/types/mod.rs
改动:
ChatResponsestruct 加#[deprecated(since = "0.1.0", note = "请改用 MessageResponse")]ToolDefinition类型别名加#[deprecated](如果仍公开)- 保留结构体定义(OpenAI
chat_inner()内部转换层仍然在用),不删除
验收条件:
cargo build产生废弃警告(期望行为)但不产生编译错误- 外部调用方能看到有用提示
Task A4:clippy 警告清零(~0.5d)
当前剩余 8 个:
| # | 警告类型 | 文件 | 修复方式 |
|---|---|---|---|
| 1 | irrefutable if let |
— | 改为直接 let |
| 2 | with_plan_step_index 未用 |
src/llm/hooks.rs |
加 #[allow(dead_code)] |
| 3-5 | 字段未读(api_key, stop_sequence, next_block_index) | anthropic.rs, response_v2.rs |
加 #[allow(dead_code)] |
| 6 | 手动前缀剥离 | — | 用 strip_prefix() |
| 7 | MutexGuard 跨 await | knowledge.rs | Task A2 修复后自动消失 |
| 8 | if 相同分支 |
retriever.rs | 合并条件 |
验收条件:cargo clippy --lib -p agcore 0 警告
Phase B — 示例实现(Week 1 后半 ~ Week 2 前半)
Task B0:MockProvider 公开化(~0.5d)
涉及文件:
src/llm/mock.rs— 新建,公开MockProviderstructsrc/llm/mod.rs— 加pub mod mock;src/agent/session.rs— 测试中MockProvider改为引用crate::llm::mock::MockProvider
MockProvider 接口:
pub struct MockProvider { .. }
impl MockProvider {
pub fn new(responses: Vec<MessageResponse>) -> Self;
pub fn chat(&self, request: MessageRequest) -> Result<MessageResponse, LlmError>;
pub fn chat_stream(&self, request: MessageRequest)
-> Result<Pin<Box<dyn Stream<Item = Result<StreamEvent, LlmError>> + Send>>, LlmError>;
}
决策:同时实现 chat_stream,从 MessageResponse 拆解为 StreamEvent::ContentBlockStart + ContentBlockDelta * N + MessageComplete 序列,使流式示例也能不依赖 API key。
验收条件:
- 公开
MockProvider可被外部 crate 引用 cargo test中所有 session 测试通过agent_session_demo.rs示例可引用MockProvider并运行
Task B1:🥇 示例 × 4(~2-3d,可并行)
4 个示例相互无依赖,按技术债消除进度安排:
| # | 示例 | 代码量 | 前置依赖 | 说明 |
|---|---|---|---|---|
| B1a | agent_session_demo.rs |
~100 行 | Task B0 | AgentBuilder → AgentSession → submit_turn → SessionMemory 完整链路 |
| B1b | custom_tool.rs |
~80 行 | 无 | 实现模拟工具 → ToolRegistry 注册 → invoke/invoke_all → PermissionChecker |
| B1c | prompt_composer.rs |
~60 行 | Task A1 | 模板变量插值 → PromptComposer 构建消息链 → 断言验证,纯离线 |
| B1d | task_agent_demo.rs |
~70 行 | 无 | 构造 JSON → JsonPlanParser → Plan → Step 状态机 → Hook 事件 |
每个示例的详细设计见 docs/8-examples-plan.md,按该文档直接实现。
验收条件:
cargo run --example prompt_composer→ 成功退出cargo run --example custom_tool→ 成功退出cargo run --example agent_session_demo→ 成功退出cargo run --example task_agent_demo→ 成功退出
Task B2:🥈 示例 × 3(~2d)
| # | 示例 | 代码量 | 前置依赖 |
|---|---|---|---|
| B2a | conversation_memory_demo.rs |
~70 行 | 无 |
| B2b | knowledge_search_demo.rs |
~60 行 | 无 |
| B2c | streaming_events_demo.rs |
~80 行 | Task B0(MockProvider 需支持 chat_stream) |
验收条件:额外 3 个示例均可 cargo run 成功退出
Phase C — 开发者体验 + 文档(Week 2 后半,与 Phase B 后段并行)
Task C1:README 完整版(~1.5d)
涉及文件:README.md
内容结构:
# AG Core
## 这是什么? ← 一句话定位
## 快速上手 ← cargo add + MockProvider 示例
## 核心模块 ← 7 个模块一句话说明
## 架构关系图 ← ASCII 或 Mermaid
## 模块依赖关系 ← 依赖关系图 + 说明
## 环境变量 ← 当前支持的环境变量表
## 参考项目 ← OpenClaw / Hermes / OpenHuman / OpenHarness
## 许可证 ← MIT / Apache-2.0
要求:快速上手代码段在提交前必须真实编译通过。
Task C2:错误消息友好化 review(~0.5d)
涉及文件:
src/agent/error.rs— AgentError 消息src/llm/error.rs— LlmError 消息src/tools/error.rs— ToolError 消息src/memory/error.rs— MemoryError 消息src/prompt/error.rs— PromptError 消息
检查标准:
- 用户能理解"哪里错了"
- 有可操作的建议("请检查 API key"、"请配置环境变量 LLM_API_KEY")
- 无英文残留的工程师视角消息
Phase D — 发布准备(Week 2 末)
Task D1:CI 示例验证集成(~0.5d)
- 确认
cargo build无新增警告 - 确认
cargo test全部通过(退出码 0) - 确认
cargo test --examples全部通过 - 确认
cargo clippy --lib -p agcore0 警告
Task D2:Roadmap 更新(~0.5d)
更新 docs/roadmap.md:
- Phase 2 状态更新为 ✅ 全部交付物已完成(Provider IR 重构 + LlmCycle 简化)
- 测试计数更新为 D1 确认的最终实际数值
- 移除 clippy 警告计数
- 添加 v0.1 发布里程碑
Task D3:依赖裁剪(~0.5d,非阻塞,可跳过)
tokio = { features = ["full"] } → 按需 feature(rt、macros、sync、time、net)
理由:减少编译时间 + 减少安全面。时间不够可延后到 v0.2。
Task D4:CHANGELOG 初始化(~0.5d)
涉及文件:CHANGELOG.md
内容要求:
- 版本号:
v0.1.0 - 发布日期:标记当天
- 变更摘要:Phase 0-4c(LLM 调用周期、提示词工程、工具系统、记忆系统、Agent 运行时、任务执行、会话级记忆)+ Provider IR 重构(统一类型系统 + Anthropic/DeepSeek/Qwen 适配)+ LlmCycle 简化
- 格式参考 Keep a Changelog 规范
验收条件:
CHANGELOG.md文件存在,内容与当前版本一致- 文件随 tag commit 一起提交
Task D5:版本标记(~0.5d)
git tag v0.1.0 && git push --tags
并行机会
| 并行组 | 包含任务 | 说明 |
|---|---|---|
| 组 0 | A0 | 单独执行,后续所有 Task 的前提 |
| 组 1 | A1 + A2 + A3 | A0 完成后可并行修 |
| 组 2 | B1a + B1b + B1d | 三个示例无前置依赖(B1a 需等 B0,B1c 需等 A1) |
| 组 3 | C1 + C2 + D2 | README / 错误消息 / Roadmap 更新,纯文档工作 |
| 组 4 | D1 + D3 | CI 验证 + 依赖裁剪,可并行 |
风险与应对
| 风险 | 影响 | 概率 | 应对 |
|---|---|---|---|
| A0 修复后仍有未发现的测试回归 | 🔴 后续 Task 验收不可信 | 低 | cargo test 全量通过后才启动 A1 |
| composer.rs 迁移发现未预见的 API 依赖 | 🔴 A1 延期 | 低 | 保持每次 commit 可编译;分 2 次提交(先私有不影响构建,再改返回类型) |
| MockProvider stream 实现比预期复杂 | 🟡 B2c 延期 | 中 | 先简化实现(只支持基本 text delta),复杂 case 留给 v0.2 |
| 示例与库 API 不同步 | 🟡 持续风险 | 中 | 所有示例纳入 cargo test --examples 作为 CI gate |
clippy #[allow(dead_code)] 积累过多 |
🟢 低 | 高 | v0.2 发布前专门 review 一次允许列表 |
| tokio feature 裁剪导致编译失败 | 🟡 D3 延期 | 低 | 裁剪前确认现有 feature 覆盖所有 tokio:: 调用点 |
验收标准
- 代码质量:
cargo build0 错误,cargo clippy --lib -p agcore0 警告 - 测试覆盖:
cargo test全量编译通过且全部运行通过,cargo test --examples全通过 - 示例可用:4 个🥇 + 3 个🥈共 7 个示例均可离线
cargo run成功退出 - 文档完整:README 包含快速上手 + 架构概览,错误消息全部友好化
- 版本标记:
git tag v0.1.0 - Roadmap 同步:
docs/roadmap.md测试计数和状态与代码一致