Files
agcore/design/pdd/27-step3-phase26-ci-verification.md
T
徐涛 28ca43ccb2 chore(docs): 将设计文档从 docs 移至 design 目录
将 note、pdd、prd、roadmap 四类文档分别归入 `design/` 下对应子目录中,并新增 `.gitkeep` 占位文件
2026-07-23 05:45:53 +08:00

24 KiB
Raw Blame History

AG Core v0.3.2 Step 3Phase 2627)— 验证固化 + 文档更新实施方案

1. 背景与目标

背景agcore v0.3.2 Step 1Phase 2025)已交付 —— Cargo features 拆分基础设施改造全部完成,所有模块 #[cfg] 门控注入完毕,依赖全部 optional 化,default = ["full"] 保持向后兼容。当前项目处于已改造完成但未经 CI 固化、无文档指引的状态。

当前状态快照

  • v0.3.0 → v0.3.2 Step 168 个源文件,23,765 行
  • 16 个 features10 模块级 + 5 provider + 1 工具)+ 4 个快捷组合
  • cargo test --features "full"427 passed
  • 7 种 feature 组合的 cargo test --lib 已全部通过(full / light / chat / chat+mcp / multi / multi+mcp / clippy),无需修复 cfg 遗漏
  • cargo test(不带 --lib)会因 18 个 example 缺少 required-features 而失败
  • 项目无 CI/CD 配置

目标:通过 Step 3 将 features 体系验证固化到 CI 中,消除编译死代码警告,完成文档指引,使 v0.3.2 达到可发布状态。

预期效果

  • 每次提交自动验证 7 种 feature 组合的编译 + 测试(零 warning)
  • 18 个 example 各自标注准确的 required-features,外树用户可一键运行
  • cargo clippy --all-features -- -D warnings 零告警
  • README 含完整 feature 表 + Cargo.toml 配置示例 + 升级指南,新用户 5 分钟内可选定组合
  • roadmap 同步更新

2. 需求分析

2.1 功能需求

# 需求 说明 对应工作
F1 LlmProvider trait 不应依赖具体 provider feature trait 自身不依赖 reqwest 或任何 provider 实现,纯 Mock 场景也应可用 工作 0
F2 每个 example 通过 cargo run --example xxx 正确编译 18 个 example 各有精确的最小 features 声明 工作 1
F3 CI 自动验证 7 种特征组合的编译与测试 push / PR 触发 工作 2
F4 所有 feature 组合下 0 个编译器警告 消除 dead_code 等警告 工作 3
F5 README 提供完整的 feature 选择指引 表格 + 场景推荐 + Cargo.toml 示例 工作 5Phase 27
F6 example 文件顶部标注所需 features 用户可一键复制运行命令 工作 5Phase 27
F7 roadmap 状态同步 总入口 + v0.3.2 子文档 工作 5Phase 27

2.2 非功能需求

# 需求 指标 对应工作
N1 向后兼容 default = ["full"] 行为与 v0.3.0 一致,427 tests passed 全部
N2 CI 时效 全矩阵 ≤ 10 分钟 工作 2
N3 最少侵入 不改动功能逻辑,仅 cfg / 配置 / 文档变更 全部

2.3 推演概要

需求拆解:从当前编译验证结果出发,发现三类待解决问题:

  1. 架构归属问题——LlmProvider trait 定义在 provider 模块门控下,语义上应归属 llm 基础设施。同时其返回类型 ProviderCapabilities / ProviderFeatures 也必须一并移出
  2. example 可编译性问题——18 个 example 无 required-features,多组合下 cargo test 失败
  3. 代码质量问题——session.rsbundle() 方法在 chat 组合下 dead_code
  4. 工程缺失——无 CI、README 无 features 说明、roadmap 未同步

边界识别

  • 工作 0 仅移动 trait 及关联类型定义,不改变公开 API 签名
  • 工作 1 的 required-features 是最小集合,不添加冗余 feature
  • 工作 2 的 CI 仅验证编译 + 单元测试,不包含集成测试
  • 工作 5 的文档更新不涉及新的功能描述

非目标声明

  • 不新增 feature 组合(保持现有的 4 个快捷组合不变)
  • 不重构 ProviderConfig / ProviderType / create_provider() 等 provider 模块创建逻辑(仅移出 trait 和元数据结构体)
  • 不集成集成测试(CI 仅验证 --lib 单元测试 + example 编译验证)
  • 不改动 Cargo.toml[dependencies] 声明
  • 不改变 default = ["full"] 的默认行为

2.4 需求映射矩阵

功能需求 非功能需求 对应工作 验收项
F1 + N1 + N3 工作 0 A1, A2, A9
F2 工作 1 A10
F3 N2 工作 2 A5, A11
F4 工作 3 A4
F5 N1 工作 5 A6
F6 工作 5 A7
F7 工作 5 A8
N3 全部 A1
R2 缓解 全部 A10

3. 方案设计

3.1 总体架构调整

工作 0 — 架构修正(LlmProvider trait 及关联类型归属调整)

当前:
  src/llm/provider.rs     #[cfg(any(feature = "provider-openai", ...))]
    ├─ pub trait LlmProvider { ... }
    ├─ pub struct ProviderCapabilities { ... }
    ├─ pub struct ProviderFeatures { ... }
    └─ pub fn capabilities(&self) -> ProviderCapabilities;

目标:
  src/llm/provider_trait.rs   #[cfg(feature = "llm")]
    ├─ pub trait LlmProvider { ... }
    ├─ pub struct ProviderCapabilities { ... }
    ├─ pub struct ProviderFeatures { ... }
    └─ pub fn capabilities(&self) -> ProviderCapabilities;
  src/llm/provider.rs         #[cfg(any(feature = "provider-openai", ...))]
    └─ 各 provider 实现 + ProviderConfig / ProviderType / create_provider()
  src/llm.rs
    └─ pub use provider_trait::{LlmProvider, ProviderCapabilities, ProviderFeatures};

影响文件(6 个源文件 + 1 个新建):
  - src/llm.rs                    — 添加 mod provider_trait 声明 + pub use 重导出
  - src/llm/provider_trait.rs     — 新文件,trait + 关联类型定义移入
  - src/llm/provider.rs           — 移出 trait + 关联类型
  - src/llm/provider/openai.rs    — use super:: → use crate::llm::
  - src/llm/provider/anthropic.rs — 同上
  - src/llm/provider/ollama.rs    — 同上
  - src/llm/provider/openai_compat.rs — 同上(两个 import 合并)

3.2 各子项设计方案

工作 0 — LlmProvider trait 归属修正

设计方案

  1. src/llm/ 下新建 provider_trait.rs,门控为 #[cfg(feature = "llm")]
  2. src/llm/provider.rs 中提取以下定义到新文件:
    • pub trait LlmProvider(含关联方法 chat / chat_stream / capabilities
    • pub struct ProviderCapabilities(含字段 features: ProviderFeatures
    • pub struct ProviderFeatures(含 8 个功能开关字段)
  3. src/llm.rs 中声明 mod provider_trait;,并 pub use provider_trait::{LlmProvider, ProviderCapabilities, ProviderFeatures};
  4. src/llm/provider.rs 移除上述定义,保留 ProviderConfig / ProviderType / create_provider() 等运行时代码
  5. 更新所有 import 路径(详见下方清单)

Import 路径调整清单

现有写法 → 目标写法

# 文件 现有 import 目标 import
1 agent/builder.rs:16 use crate::llm::provider::LlmProvider; use crate::llm::LlmProvider;
2 agent/builder.rs:135test use crate::llm::provider::{LlmProvider, ProviderCapabilities, ProviderFeatures}; use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures};
3 agent/session.rs:35 use crate::llm::provider::LlmProvider; use crate::llm::LlmProvider;
4 agent/runtime.rs:21 use crate::llm::provider::LlmProvider; use crate::llm::LlmProvider;
5 llm/mock.rs:46 use crate::llm::provider::{LlmProvider, ProviderCapabilities, ProviderFeatures}; use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures};
6 llm/cycle.rs:22 use crate::llm::provider::LlmProvider; use crate::llm::LlmProvider;
7 llm/cycle.rs:940,1401test use crate::llm::provider::{ProviderCapabilities, ProviderFeatures}; use crate::llm::{ProviderCapabilities, ProviderFeatures};
8 llm/provider/openai.rs:24 use super::{LlmProvider, ProviderCapabilities, ProviderFeatures}; use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures};
9 llm/provider/anthropic.rs:21 use super::{LlmProvider, ProviderCapabilities, ProviderFeatures}; use crate::llm::{LlmProvider, ProviderCapabilities, ProviderFeatures};
10 llm/provider/ollama.rs:14 use super::{LlmProvider, ProviderCapabilities}; use crate::llm::{LlmProvider, ProviderCapabilities};
11 llm/provider/openai_compat.rs:18,21 use super::ProviderCapabilities; + use crate::llm::provider::LlmProvider; use crate::llm::{LlmProvider, ProviderCapabilities};(合并为一行)
12 llm/provider/registry.rs:6 use crate::llm::provider::{LlmProvider, ProviderConfig, ProviderType, create_provider}; use crate::llm::LlmProvider; + use crate::llm::provider::{ProviderConfig, ProviderType, create_provider};(拆分)

示例文件 import 调整

# 文件 现有 import 目标 import
13 examples/end_to_end.rs:21 use agcore::llm::provider::{create_provider, LlmProvider, ProviderConfig, ProviderType}; use agcore::llm::LlmProvider; + use agcore::llm::provider::{create_provider, ProviderConfig, ProviderType};
14 examples/context_slot_demo.rs:18 use agcore::llm::provider::LlmProvider; use agcore::llm::LlmProvider;
15 examples/streaming_events_demo.rs:19 use agcore::llm::provider::LlmProvider; use agcore::llm::LlmProvider;
16 examples/quick_start.rs:9 use agcore::llm::provider::LlmProvider; use agcore::llm::LlmProvider;

Breaking Change 声明

工作 0 是非兼容性变更,现有用户可能通过以下路径引用 LlmProvider

旧路径(v0.3.0v0.3.2 Step 1 新路径(v0.3.2 Step 3 后)
agcore::llm::provider::LlmProvider agcore::llm::LlmProvider
agcore::llm::provider::ProviderCapabilities agcore::llm::ProviderCapabilities
agcore::llm::provider::ProviderFeatures agcore::llm::ProviderFeatures

向后兼容方案(可选):在 src/llm/provider.rs 中添加 #[cfg(feature = "llm")] 门控的类型别名,让老路径仍然可用:

#[cfg(feature = "llm")]
pub use super::provider_trait::LlmProvider;
#[cfg(feature = "llm")]
pub use super::provider_trait::ProviderCapabilities;
#[cfg(feature = "llm")]
pub use super::provider_trait::ProviderFeatures;

推荐:用户应迁移到新路径 agcore::llm::LlmProviderprovider 模块仅保留 ProviderConfig / ProviderType / create_provider() 等创建逻辑。

验证

  • cargo test --features "full" 仍 427 passed
  • cargo test --no-default-features --features "llm,llm-types" --lib 编译通过(无需任何 provider feature
  • 所有 12 个内部文件 + 4 个示例文件的 import 路径正确

工作 1 — examples required-features 标注

设计方案:在 Cargo.toml 中为每个 example 添加 [[example]] + required-features,精确到最小 features 集合。

上下文 slot 示例(context_slot_demo):
  工作 0 后仅需 ["agent"](修正前需 ["agent", "provider-openai"]
  因为 agent 的测试无需真实 providermock 即可

推理不变的 examplesimple_visit):
  真正调用 LLM,需要 ["llm", "provider-openai", "tracing-init"]

完整映射关系见实施计划 §4.2。

工作 2 — CI 配置

设计方案GitHub Actions 矩阵策略,7 个并行测试 job + 1 clippy + 1 format + 1 example 验证。

Job Command 作用域
full RUSTFLAGS="-D warnings" cargo test --features "full" --lib 全量回归,零警告
light RUSTFLAGS="-D warnings" cargo test --no-default-features --features "light" --lib 生产常用,零警告
chat RUSTFLAGS="-D warnings" cargo test --no-default-features --features "chat,provider-openai" --lib 纯对话,零警告
chat+mcp RUSTFLAGS="-D warnings" cargo test --no-default-features --features "chat,provider-openai,tools-mcp" --lib 对话 + 工具,零警告
multi RUSTFLAGS="-D warnings" cargo test --no-default-features --features "multi,provider-openai" --lib 多 Agent,零警告
multi+mcp RUSTFLAGS="-D warnings" cargo test --no-default-features --features "multi,provider-openai,tools-mcp" --lib 多 Agent + 工具,零警告
clippy cargo clippy --all-features --lib -- -D warnings lint 检查
format cargo fmt --checkstable toolchain 格式检查
examples cargo test --features "full"(不加 --lib,编译并运行所有 example example 编译验证

关键决策

  • 矩阵中统一使用 --lib 而非 --all-targets。理由:examples 的编译由 required-features 独立管理,若混入矩阵会因 feature 组合不匹配导致 example 编译失败,干扰模块测试结果验证。
  • 使用 RUSTFLAGS="-D warnings" 将警告升级为编译错误,确保 F40 编译器警告)被矩阵中所有 6 个测试 job 强制执行。
  • format job 使用 stable toolchaincargo fmt --check 不需要 nightly)。
  • 独立 examples job 使用 cargo test --features "full"(不加 --lib),验证所有 example 在完整 features 下编译并运行通过。

工作 3 — bundle() 死代码警告修复

设计方案:在 src/agent/session.rs:154pub(crate) fn bundle() 方法上添加 #[cfg(feature = "engine")] 条件编译。

背景:bundle() 仅被 engine/session_manager.rs:219 调用,当启用 chat 组合(agent 但非 engine)时产生 dead_code 警告。

验证cargo test --no-default-features --features "chat,provider-openai" --lib 0 warnings。

工作 4(可选)— 编译时间基线

记录但不沉淀到代码或 CI 中,仅供性能参考:

time cargo build --features "full"
time cargo build --no-default-features --features "light"

注:此工作在 v0.3.2 发布前为手动执行,不纳入 CI 或验收标准。若后续版本需要编译时间回归检测,可将其提升为正式工作项。

工作 5 — 文档更新

三处并行更新:

README.md 新增 features 表格

  • 4 个快捷组合 + 推荐使用场景 + Cargo.toml 配置示例
  • 下游用户可快速选择并复制配置

升级指南章节(README.md 新增)

  • 针对工作 0 的 Breaking Change 提供迁移说明
  • 列出旧路径 → 新路径的对照表
  • 提供向后兼容的重导出方案说明
  • 示例:agcore::llm::provider::LlmProvideragcore::llm::LlmProvider
  • 提醒用户更新 use 声明

example 文件顶部注释

  • 每个 example 第一行格式:// Required features: cargo run --example xxx --features "..."
  • 对应工作 1 的 required-features 声明

roadmap 状态同步

  • docs/roadmap.md:补充 Phase 26-27 完成状态 + v0.3.2 链接
  • docs/roadmap-v0.3.2.mdPhase 26/27 状态从 改为 + 完成日期

3.3 ADR 记录

ADR-1LlmProvider trait 及关联类型归属 llm 模块

字段 内容
问题 LlmProvider trait 定义在 provider 模块门控 any(provider-openai, provider-anthropic, ...) 下,纯 Mock 场景被迫引入至少一个 provider feature。其返回类型 ProviderCapabilities / ProviderFeatures 同样被困在 provider 门控中
决策 将 trait 定义 + ProviderCapabilities / ProviderFeatures 一并提取到 src/llm/provider_trait.rs,归属 #[cfg(feature = "llm")]
备选方案 保持不动,在 mock provider 上添加 cfg 绕过 — 否决,因为 provider 模块整体门控错误
理由 trait 本身是个接口定义,不依赖 reqwest 或任何 provider 实现细节;ProviderCapabilities / ProviderFeatures 是 trait 方法的返回类型,必须与 trait 同门控
影响 修改 6 个源文件 + 1 个新建文件 + 4 个示例文件(详见 §3.2 import 调整清单)
状态 已采纳

ADR-2CI 使用 nightly toolchain

字段 内容
问题 项目已使用 edition 2024,是否降级到 2021 以使用 stable Rust
决策 测试和 clippy 使用 nightlyedition 2024 目前要求 nightly);format 使用 stable
备选方案 降级 edition 到 2021 — 否决,已迁移至 edition 2024 且编译通过
理由 edtion 2024 是主动选择的方向,降级是倒退且涉及大量语法变更;cargo fmt --check 无需 nightly
影响 CI 依赖 actions-rust-lang/setup-rust-toolchain@v1format job 指定 toolchain: stable
状态 已采纳

ADR-3CI 矩阵使用 --lib 而非 --all-targets

字段 内容
问题 cargo test --all-targets 会编译所有 example,与矩阵中自选的 feature 组合可能冲突
决策 矩阵测试使用 --libexamples 由独立 jobcargo test --features "full" 不加 --lib)验证
备选方案 在矩阵中也传入 --all-targets — 否决,example 编译失败会干扰模块测试验证
理由 分离关注点:矩阵验证模块级编译 + 零警告,独立 job 验证 example 编译
状态 已采纳

4. 实施计划

4.1 任务拆解与优先级

优先级 工作 编号 规模 依赖
P0 LlmProvider trait 归属修正 工作 0 ~30 行(含关联类型移动 + import 调整)
P0 bundle() 门控修复 工作 3 1 行
P0 examples required-features 工作 1 ~50 行 工作 0context_slot_demo / quick_start 最小 features 从 agent+provider-openai 降为 agent
P0 CI 配置 工作 2 ~80 行
P1 文档更新 工作 5 ~150 行 全部
P2 编译时间基线 工作 4 手动 全部

4.2 各 example 的 required-features 清单

example 文件名 required-features 运行环境备注
prompt_composer ["prompt"]
custom_tool ["tools"]
conversation_memory_demo ["memory"]
knowledge_graph_demo ["memory"]
knowledge_search_demo ["memory"]
agent_session_demo ["agent"]
task_agent_demo ["agent"]
context_slot_demo ["agent"] 工作 0 后无需 provider
quick_start ["agent"] 工作 0 后无需 provider
simple_visit ["llm", "provider-openai", "tracing-init"] 需要 API key
streaming_events_demo ["llm", "provider-openai"] 需要 API key
agent_switch_demo ["engine"]
bridge_keys_demo ["engine"]
dispatch_stream_demo ["engine"]
engine_demo ["engine"]
sub_agent_dispatch_demo ["engine"]
document_demo ["memory", "tracing-init"] 需 sqlite 依赖(memory-sqlite feature 可选)
end_to_end ["agent", "memory-sqlite", "provider-openai"] 需要 API key + sqlite 依赖

验证策略:每个 example 除 --lib 验证外,还需单独运行以下命令确认 required-features 精确性:

cargo test --no-default-features --features "<features>" --example <name>

4.3 Commit 策略

每个工作独立 commit,按依赖顺序排列:

顺序 Scope Type 描述 依赖
1 core refactor 将 LlmProvider trait 及关联类型移出 provider 模块归属 llm
2 agent fix 为 session.rs bundle() 方法添加 engine feature 门控
3 examples chore 为 18 个 example 添加 required-features 声明 工作 1context_slot 等受益于工作 0 的轻量 features)
4 ci chore 创建 CI 测试矩阵配置
5 docs docs 更新 README feature 表 + 升级指南 + 示例注释 + roadmap 状态 全部

4.4 参考实现:CI 配置

name: CI
on: [push, pull_request]
env:
  RUSTFLAGS: "-D warnings"
jobs:
  test-matrix:
    strategy:
      matrix:
        features:
          - "full"
          - "light"
          - "chat,provider-openai"
          - "chat,provider-openai,tools-mcp"
          - "multi,provider-openai"
          - "multi,provider-openai,tools-mcp"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: nightly
      - run: cargo test --no-default-features --features "${{ matrix.features }}" --lib
  clippy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: nightly
      - run: cargo clippy --all-features --lib -- -D warnings
  format:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable
      - run: cargo fmt --check
  examples:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: nightly
      - run: cargo test --features "full"

5. 风险评估

风险 概率 影响 缓解措施 对应验收项
工作 0 重构后公开 API 被意外改变 重构前后分别跑 cargo test --features "full" 确认测试数一致(427),且 cargo doc 无差异 A1
required-features 标注不准确导致 example 运行时缺少 trait 实现 每个 example 在 --lib 验证后,再单独跑 cargo test --example xxx --no-default-features --features "对应features" 确认 A10
CI 首次在 GitHub runner 上因环境差异(OS、Toolchain 版本)失败 非阻塞问题,修复后重新推送即可;本地已在 macOS 验证 7 种组合 A5
edition 2024 在 GitHub runner 的特定 nightly 版本上不稳定 可在 Cargo.toml 中加 rust-version = "1.85" 下限约束 A5
bundle() 门控修复后 engine 组合下方法不可见 极低 cargo test --features "engine,provider-openai" 编译通过即可验证 A4

6. 验收标准

# 验收项 验证方式 对应工作
A1 cargo test --features "full" 仍 427 passed cargo test -F full -q 工作 0
A2 cargo test --no-default-features --features "llm,llm-types" --lib 编译通过 无需任何 provider feature 即完成编译 工作 0
A3 7 种组合下 cargo test --no-default-features --features "组合" --lib -q 全部通过 逐一验证 工作 1example features 正确不会干扰 --lib
A4 6 个矩阵组合(full / light / chat / chat+mcp / multi / multi+mcp)下 RUSTFLAGS="-D warnings" cargo test --lib 0 warnings 所有组合均无编译器警告 工作 3
A5 .github/workflows/ci.yml 文件存在,结构包含 9 个 job6 测试 + 1 clippy + 1 format + 1 examples 文件检查 工作 2
A6 README.md 包含 features 表格 + 使用场景 + Cargo.toml 配置示例 + 升级指南 review 通过 工作 5
A7 所有 18 个 example 文件首行含 // Required features: cargo run --example xxx --features "..." 注释 review 通过 工作 5
A8 docs/roadmap.mddocs/roadmap-v0.3.2.md 中 Phase 26/27 状态标记为 review 通过 工作 5
A9 工作 0 后 cargo doc --no-deps --features "llm,llm-types" 可生成 LlmProvider / ProviderCapabilities / ProviderFeatures 的 API 文档(无需任何 provider feature review 通过 工作 0
A10 每个 example 单独验证:cargo test --no-default-features --features "<对应features>" --example <name> 编译通过 逐一验证 18 个 example 工作 1
A11 全矩阵 CI6 测试 + clippy + format + examples)从 checkout 到完成 ≤ 10 分钟 实测计时 工作 2