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

27 KiB
Raw Blame History

AG Core v0.3.2 Step 1Phase 20)— Cargo Features 基础设施改造实施方案

1. 背景与目标

背景agcore 是一个 Rust 编写的智能体核心工具箱,目前约 23,718 行、66 个源文件。v0.3.0 发布后,所有模块在编译时全量捆绑,下游用户无法按需选择模块,即使只使用 LLM 对话也需要编译 sqlite / MCP / agent 引擎等全部依赖。

目标:通过 Cargo features 拆分让下游按需选择模块。Step 1 是基础设施变更 —— Cargo.toml features 定义 + 依赖 optional 化 + 必要的子模块 cfg 门控,编译通过后打 checkpoint。

预期效果

  • default = ["full"] → v0.3.0 用户零迁移成本
  • 最小组合(document)零重型外部依赖(仅依赖始终编译的轻量依赖:serde/serde_json/thiserror/async-trait/tracing
  • 纯对话组合(chat + provider-openai)仅需 ~10 个依赖,不含 sqlite / MCP / engine

2. 需求分析

2.1 约束条件

# 约束 说明
1 default = ["full"] 保持向后兼容,v0.3.0 用户零迁移成本
2 document feature 零重型外部依赖(仅依赖始终编译的轻量依赖:serde/serde_json/thiserror/async-trait/tracing 纯 std + 始终编译的轻量依赖(serde/serde_json/thiserror/async-trait/tracing
3 tokio 从 ["full"] 拆细 已验证全库无 net/fs/signal 使用,拆为 ["rt", "sync", "time", "macros", "process", "io-util"]
4 重型依赖全部 optional tokio、reqwest、rusqlite、tracing-subscriber、tokio-stream、futures、futures-util、futures-core、bytes、async-stream、tokio-util、time
5 始终编译的轻量依赖 serde、serde_json、thiserror、async-trait、tracing

2.2 关键决策

# 决策 理由
1 tools feature 必须 imply tokio src/tools/registry.rs 使用 tokio::time::timeout
2 pub mod llm 门控条件为 any(feature = "llm-types", feature = "llm") prompt → llm-types 路径需要 llm 模块编译,但只需 types 子模块
3 测试 dev-dependencies 加 tokio = { version = "1", features = ["rt", "macros"] } 现有 #[tokio::test] 需要 tokio runtime
4 快捷组合名保持原名(chat/multi/light 文档中说明各组合包含的 feature 约束
5 init_tracing() 函数整体用 #[cfg(feature = "tracing-init")] 包裹 避免 use tracing_subscriber 出现在未启用 feature 时编译失败

3. 方案设计

3.1 Features 定义(完整 Cargo.toml [features] 草案)

[features]
default = ["full"]

# === 模块级 features ===
document = []
llm-types = []
prompt = ["llm-types"]
llm = ["llm-types", "tokio", "async-stream", "futures-core", "tokio-stream"]
tools = ["llm-types", "futures", "tokio-util", "tokio"]
tools-mcp = ["tools", "reqwest"]
# memory 模块依赖 llmconversation/vector_store 使用 compact/embedding)、tokioknowledge.rs 使用 Mutex)、timetypes.rs 使用 OffsetDateTime
memory = ["document", "llm", "tokio", "time"]
memory-sqlite = ["memory", "rusqlite", "time"]
agent = ["llm", "tools", "memory", "futures-util"]
engine = ["agent"]

# === Provider features ===
# Provider features — openai/anthropic 额外依赖 bytes(流式解析)和 futures-utilStream 组合)
provider-openai = ["llm", "reqwest", "bytes", "futures-util"]
provider-anthropic = ["llm", "reqwest", "bytes", "futures-util"]
# deepseek/qwen 使用 openai_compat 适配层,不需要 bytes 和 futures-util
provider-deepseek = ["llm", "reqwest"]
provider-qwen = ["llm", "reqwest"]
provider-ollama = ["llm", "reqwest"]

# === 工具 features ===
tracing-init = ["tracing-subscriber"]

# === 快捷组合 ===
full = [
    "document", "llm-types", "prompt", "llm",
    "tools", "tools-mcp",
    "memory", "memory-sqlite",
    "agent", "engine",
    "provider-openai", "provider-anthropic", "provider-deepseek",
    "provider-qwen", "provider-ollama",
    "tracing-init",
]
light = ["llm", "provider-openai", "tools", "tools-mcp", "memory", "agent", "engine", "prompt", "document"]
chat = ["agent", "provider-openai"]
multi = ["engine", "provider-openai"]

features 依赖图(简略)

document (零外部依赖)
  └── memory (+llm, +tokio, +time) ─── memory-sqlite (+rusqlite, +time)
                      
llm-types (零依赖)
  ├── prompt
  └── llm (+tokio, +async-stream, +futures-core, +tokio-stream)
        ├── tools (+futures, +tokio-util) ─── tools-mcp (+reqwest)
        ├── provider-openai / provider-anthropic (+reqwest, +bytes, +futures-util)
        ├── provider-deepseek / provider-qwen / provider-ollama (+reqwest)
        └── agent (+tools, +memory, +futures-util) ─── engine

3.2 依赖 optional 化方案

始终编译(5 个,不参与门控)

serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
async-trait = "0.1"
tracing = "0.1"

12 个依赖加 optional = true

依赖 原声明 新声明
tokio { version = "1", features = ["full"] } { version = "1", features = ["rt", "sync", "time", "macros", "process", "io-util"], optional = true }
reqwest { version = "0.12", features = ["json", "stream"] } { version = "0.12", features = ["json", "stream"], optional = true }
rusqlite { version = "0.32", features = ["bundled"] } { version = "0.32", features = ["bundled"], optional = true }
tracing-subscriber { version = "0.3", features = ["env-filter"] } { version = "0.3", features = ["env-filter"], optional = true }
tokio-stream { version = "0.1" } { version = "0.1", optional = true }
futures { version = "0.3" } { version = "0.3", optional = true }
futures-util { version = "0.3" } { version = "0.3", optional = true }
futures-core { version = "0.3" } { version = "0.3", optional = true }
bytes { version = "1" } { version = "1", optional = true }
async-stream { version = "0.3" } { version = "0.3", optional = true }
tokio-util { version = "0.7", features = ["rt", "sync"] } { version = "0.7", features = ["rt", "sync"], optional = true }
time { version = "0.3", features = ["serde", "parsing", "formatting", "macros"] } { version = "0.3", features = ["serde", "parsing", "formatting", "macros"], optional = true }

dev-dependencies 新增

[dev-dependencies]
tokio = { version = "1", features = ["rt", "macros"] }

3.3 源文件改动清单

共涉及 8 个文件(预估 ~100 行改动):Cargo.tomlsrc/lib.rssrc/llm.rssrc/llm/cycle.rssrc/tools.rssrc/memory.rssrc/memory/store.rssrc/agent/session.rs


文件 1Cargo.toml

改动 1.1 — 新增 [features] 表(约 45 行,插入在 [package] 之后、[dependencies] 之前)

+ [features]
+ default = ["full"]
+ 
+ # === 模块级 features ===
+ document = []
+ llm-types = []
+ prompt = ["llm-types"]
+ llm = ["llm-types", "tokio", "async-stream", "futures-core", "tokio-stream"]
+ tools = ["llm-types", "futures", "tokio-util", "tokio"]
+ tools-mcp = ["tools", "reqwest"]
+ # memory 模块依赖 llmconversation/vector_store 使用 compact/embedding)、tokioknowledge.rs 使用 Mutex)、timetypes.rs 使用 OffsetDateTime
+ memory = ["document", "llm", "tokio", "time"]
+ memory-sqlite = ["memory", "rusqlite", "time"]
+ agent = ["llm", "tools", "memory", "futures-util"]
+ engine = ["agent"]
+ 
+ # === Provider features ===
+ # Provider features — openai/anthropic 额外依赖 bytes(流式解析)和 futures-utilStream 组合)
+ provider-openai = ["llm", "reqwest", "bytes", "futures-util"]
+ provider-anthropic = ["llm", "reqwest", "bytes", "futures-util"]
+ # deepseek/qwen 使用 openai_compat 适配层,不需要 bytes 和 futures-util
+ provider-deepseek = ["llm", "reqwest"]
+ provider-qwen = ["llm", "reqwest"]
+ provider-ollama = ["llm", "reqwest"]
+ 
+ # === 工具 features ===
+ tracing-init = ["tracing-subscriber"]
+ 
+ # === 快捷组合 ===
+ full = [
+     "document", "llm-types", "prompt", "llm",
+     "tools", "tools-mcp",
+     "memory", "memory-sqlite",
+     "agent", "engine",
+     "provider-openai", "provider-anthropic", "provider-deepseek",
+     "provider-qwen", "provider-ollama",
+     "tracing-init",
+ ]
+ light = ["llm", "provider-openai", "tools", "tools-mcp", "memory", "agent", "engine", "prompt", "document"]
+ chat = ["agent", "provider-openai"]
+ multi = ["engine", "provider-openai"]

改动 1.2 — tokio 依赖声明修改

- tokio = { version = "1", features = ["full"] }
+ tokio = { version = "1", features = ["rt", "sync", "time", "macros", "process", "io-util"], optional = true }

改动 1.3 — 11 个重型依赖逐行加 optional = true

- reqwest = { version = "0.12", features = ["json", "stream"] }
+ reqwest = { version = "0.12", features = ["json", "stream"], optional = true }

- rusqlite = { version = "0.32", features = ["bundled"] }
+ rusqlite = { version = "0.32", features = ["bundled"], optional = true }

- tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+ tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true }

- tokio-stream = "0.1"
+ tokio-stream = { version = "0.1", optional = true }

- futures = "0.3"
+ futures = { version = "0.3", optional = true }

- futures-util = "0.3"
+ futures-util = { version = "0.3", optional = true }

- futures-core = "0.3"
+ futures-core = { version = "0.3", optional = true }

- bytes = "1"
+ bytes = { version = "1", optional = true }

- async-stream = "0.3"
+ async-stream = { version = "0.3", optional = true }

- tokio-util = { version = "0.7", features = ["rt"] }
+ tokio-util = { version = "0.7", features = ["rt", "sync"], optional = true }

- time = { version = "0.3", features = ["serde", "parsing", "formatting", "macros"] }
+ time = { version = "0.3", features = ["serde", "parsing", "formatting", "macros"], optional = true }

改动 1.4[dev-dependencies] 新增 tokio

+ [dev-dependencies]
+ tokio = { version = "1", features = ["rt", "macros"] }

说明:如果原 Cargo.toml 已有 [dev-dependencies] 则追加该行;若无则新增整个 section。


文件 2src/lib.rs(当前约 26 行 → 改动后约 40 行)

当前内容(参考)

//! agcore —— 智能体(Agent)核心工具箱。

pub mod llm;
pub mod document;
pub mod prompt;
pub mod tools;
pub mod memory;
pub mod agent;
pub mod engine;

pub use document::Document;

use tracing_subscriber::{EnvFilter, fmt, prelude::*};
static INIT: std::sync::Once = std::sync::Once::new();
pub fn init_tracing() {
    INIT.call_once(|| {
        let filter = EnvFilter::try_from_default_env()
            .unwrap_or_else(|_| EnvFilter::new("agcore=info"));
        tracing_subscriber::registry()
            .with(fmt::layer())
            .with(filter)
            .init();
    });
}

改动后内容

  //! agcore —— 智能体(Agent)核心工具箱。

- pub mod llm;
+ #[cfg(any(feature = "llm-types", feature = "llm"))]
+ pub mod llm;
- pub mod document;
+ #[cfg(feature = "document")]
+ pub mod document;
- pub mod prompt;
+ #[cfg(feature = "prompt")]
+ pub mod prompt;
- pub mod tools;
+ #[cfg(feature = "tools")]
+ pub mod tools;
- pub mod memory;
+ #[cfg(feature = "memory")]
+ pub mod memory;
- pub mod agent;
+ #[cfg(feature = "agent")]
+ pub mod agent;
- pub mod engine;
+ #[cfg(feature = "engine")]
+ pub mod engine;

- pub use document::Document;
+ #[cfg(feature = "document")]
+ pub use document::Document;

- use tracing_subscriber::{EnvFilter, fmt, prelude::*};
- static INIT: std::sync::Once = std::sync::Once::new();
- pub fn init_tracing() {
-     INIT.call_once(|| {
-         let filter = EnvFilter::try_from_default_env()
-             .unwrap_or_else(|_| EnvFilter::new("agcore=info"));
-         tracing_subscriber::registry()
-             .with(fmt::layer())
-             .with(filter)
-             .init();
-     });
- }
+ #[cfg(feature = "tracing-init")]
+ use tracing_subscriber::{EnvFilter, fmt, prelude::*};
+ 
+ #[cfg(feature = "tracing-init")]
+ static INIT: std::sync::Once = std::sync::Once::new();
+ 
+ #[cfg(feature = "tracing-init")]
+ pub fn init_tracing() {
+     INIT.call_once(|| {
+         let filter = EnvFilter::try_from_default_env()
+             .unwrap_or_else(|_| EnvFilter::new("agcore=info"));
+         tracing_subscriber::registry()
+             .with(fmt::layer())
+             .with(filter)
+             .init();
+     });
+ }

文件 3src/llm.rs(当前约 12 行 → 改动后约 24 行)

改动说明:为每个子模块声明加 feature 门控。types 子模块在 llm-typesllm 任一 feature 启用时编译(llm imply llm-types,但 prompt 也 depend on llm-types);其余子模块(compact/convert/cycle 等)仅在 llm feature 启用时编译。

  //! LLM 调用周期 —— 大模型基础调用周期控制。

- pub mod types;
+ #[cfg(feature = "llm-types")]
+ pub mod types;
- pub mod compact;
+ #[cfg(feature = "llm")]
+ pub mod compact;
- pub mod convert;
+ #[cfg(feature = "llm")]
+ pub mod convert;
- pub mod cycle;
+ #[cfg(feature = "llm")]
+ pub mod cycle;
- pub mod embedding;
+ #[cfg(feature = "llm")]
+ pub mod embedding;
- pub mod error;
+ #[cfg(feature = "llm")]
+ pub mod error;
- pub mod hooks;
+ #[cfg(feature = "llm")]
+ pub mod hooks;
- pub mod mock;
+ #[cfg(feature = "llm")]
+ pub mod mock;
- pub mod provider;
+ // provider 模块依赖 reqwest(通过 reqwest::Client),仅在任一 provider feature 启用时编译
+ #[cfg(any(feature = "provider-openai", feature = "provider-anthropic", feature = "provider-deepseek", feature = "provider-qwen", feature = "provider-ollama"))]
+ pub mod provider;
- pub mod stream;
+ #[cfg(feature = "llm")]
+ pub mod stream;

文件 3bsrc/llm/cycle.rs(新增文件,约 35 行)

改动说明cycle.rs 中使用 crate::tools::ToolRegistry(第 29 行),依赖 tools feature。工具相关字段和方法需加 #[cfg(feature = "tools")] 门控。

⚠️ 这是 Phase 22.7 原计划的门控变更,因为编译阻塞提前到 Step 1 执行。

  //! Cycle —— 多轮对话与工具调用编排。

  use async_trait::async_trait;
  use futures::StreamExt;  // 来自 llm→tokio imply 链
+ #[cfg(feature = "tools")]
  use crate::tools::ToolRegistry;

  // ... struct / enum 定义 ...

  // ===== CycleConfig — 工具相关字段加 cfg 门控 =====
  pub struct CycleConfig {
      pub max_retries: usize,
      pub max_history: usize,
+     #[cfg(feature = "tools")]
      pub max_tool_turns: usize,
+     #[cfg(feature = "tools")]
      pub tool_timeout_secs: u64,
      // ... 其他字段 ...
  }

  // ===== Cycle — 方法加 cfg 门控 =====
  impl Cycle {
      /// 仅在有 tools feature 时才有工具调用相关方法
+     #[cfg(feature = "tools")]
      pub async fn submit_with_tools(&self, ...) -> Result<...> {
          // ...
      }

+     /// submit_with_tools_stream 方法同样需要 tools 门控,
+     /// 因参数包含 Arc<ToolRegistry> 而与 submit_with_tools 同理。
+     #[cfg(feature = "tools")]
+     pub async fn submit_with_tools_stream(
+         &self, ...  // 方法签名中包含 Arc<ToolRegistry> 参数
+     ) -> Result<...> {
+         // ...
+     }

+     #[cfg(feature = "tools")]
      async fn run_tool_loop(&self, ...) -> Result<...> {
          // ...
      }
  }

+ // ===== 顶层函数 — 同样依赖 ToolRegistry =====
+ /// run_tool_loop 函数(顶层函数,非 LlmCycle 方法)同样依赖 Arc<ToolRegistry>
+ /// 参数包含 Arc<ToolRegistry>,需 #[cfg(feature = "tools")]。
+ #[cfg(feature = "tools")]
+ pub async fn run_tool_loop(
+     // ... 函数签名中包含 Arc<ToolRegistry> 参数
+ ) -> Result<...> {
+     // ...
+ }

  **说明**`Cycle` 本身的 struct 定义、`submit()` 基础方法、`ResponseStream` 等不依赖 tools 的部分保持无门控,仅在 `llm` feature 下编译即可。

---

#### 文件 4`src/tools.rs`(当前约 13 行 → 改动后约 15 行)

**改动说明**:`mcp` 子模块及对应的 `pub use` 仅在 `tools-mcp` feature 启用时编译。其余子模块(base/error/permission/registry)始终在 `tools` feature 下编译。

```diff
  //! 工具系统 —— 工具抽象、注册、调用、权限控制与 MCP 集成。

  pub mod base;
  pub mod error;
- pub mod mcp;
+ #[cfg(feature = "tools-mcp")]
+ pub mod mcp;
  pub mod permission;
  pub mod registry;

  pub use base::{BaseTool, ToolContext, ToolRef};
  pub use error::ToolError;
- pub use mcp::{McpClient, McpTransport};
+ #[cfg(feature = "tools-mcp")]
+ pub use mcp::{McpClient, McpTransport};
  pub use permission::{Permission, PermissionChecker, PermissionConfig};
  pub use registry::{ToolInvocation, ToolRegistry};

文件 5src/memory/store.rs(当前约 62 行 → 改动后约 64 行)

改动说明sqlite_store 子模块及其 pub use 仅在 memory-sqlite feature 启用时编译。

  //! MemoryStore 抽象接口与默认实现。

  use async_trait::async_trait;
  use crate::memory::error::MemoryError;
  use crate::memory::types::{MemoryFilter, MemoryItem};

  pub mod in_memory;
- pub mod sqlite_store;
+ #[cfg(feature = "memory-sqlite")]
+ pub mod sqlite_store;

  pub use in_memory::InMemoryStore;
- pub use sqlite_store::SqliteStore;
+ #[cfg(feature = "memory-sqlite")]
+ pub use sqlite_store::SqliteStore;

说明MemoryStore trait、EvictionConfigEvictionPolicy 等定义保持不变,不需要 cfg 门控。


文件 6src/memory.rs(当前约 32 行 → 改动后约 34 行)

改动说明SqliteStore 的重新导出仅在 memory-sqlite feature 启用时编译。其余子模块声明和 pub use 保持不变(memory feature 门控由 src/lib.rs 负责)。

  //! 记忆系统 —— 对话消息管理、知识页面存储与关键词检索。

  // 所有子模块声明保持不变:
  // pub mod conversation;
  // pub mod error;
  // pub mod graph;
  // pub mod knowledge;
  // pub mod retriever;
  // pub mod store;
  // ...
  
  // 高频类型
  pub use conversation::{ConversationMemory, ConversationMemoryConfig};
  pub use error::MemoryError;
  pub use graph::{GraphEntity, GraphRelation, InMemoryGraph, KnowledgeGraph, RelationDirection, ScoredEntity};
  pub use knowledge::KnowledgeStore;
  pub use retriever::MemoryRetriever;
  pub use store::{InMemoryStore, MemoryStore};
+ #[cfg(feature = "memory-sqlite")]
+ pub use store::SqliteStore;
  // 其余 pub use 保持不变...

文件 7src/agent/session.rs(新增文件,约 30 行)

改动说明session.rs 中引用了 crate::engine::*SessionMemoryEntry、SessionSnapshot、EngineError),而 agent feature 不含 engineengine = ["agent"] 是反向依赖)。需要对 engine 相关导入和方法加门控。

⚠️ 阻塞 B3src/agent/session.rs:28-29 无条件引用 crate::engine::*,在 agent feature 下编译时因缺少 engine 而失败。

  //! Session —— Agent 会话管理。

  use async_trait::async_trait;
  use crate::llm::types::LLMRequest;
  use crate::memory::MemoryStore;
+ #[cfg(feature = "engine")]
  use crate::engine::snapshot::{SessionMemoryEntry, SessionSnapshot};
+ #[cfg(feature = "engine")]
  use crate::engine::EngineError;

  // ===== AgentSession — pending_memory_restore 字段 =====
+ /// AgentSession 结构体中的 pending_memory_restore 字段类型来自 engine 模块,
+ /// 需要条件编译。
  pub struct AgentSession {
+     // ... 其他字段 ...
+ 
+     #[cfg(feature = "engine")]
+     pending_memory_restore: Option<HashMap<String, SessionMemoryEntry>>,
+     // ... 其他字段 ...
+ }

  impl Session {
      /// to_snapshot / from_snapshot / restore_memory 仅在 engine feature 下可用
+     #[cfg(feature = "engine")]
      pub fn to_snapshot(&self) -> SessionSnapshot {
          // ...
      }

+     #[cfg(feature = "engine")]
      pub fn from_snapshot(snap: SessionSnapshot) -> Result<Self, EngineError> {
          // ...
      }

+     #[cfg(feature = "engine")]
      async fn restore_memory(&mut self, entries: Vec<SessionMemoryEntry>) -> Result<(), MemoryError> {
          // ...
      }
  }

说明Session 结构体本身以及不依赖 engine 的方法(如 new()add_message()get_history())保持无门控,仅在 agent feature 下编译即可。


4. 实施步骤

3 个 commit 粒度执行,每个 commit 后编译验证。

Commit 1Cargo.toml features 定义 + 依赖 optional 化

涉及文件:仅 Cargo.toml

操作清单

  1. [package] 之后、[dependencies] 之前插入 [features] 表(16 个 features + 4 个快捷组合,约 45 行)
  2. tokio features 从 ["full"] 改为 ["rt", "sync", "time", "macros", "process", "io-util"] 并加 optional = true
  3. reqwest / rusqlite / tracing-subscriber / tokio-stream / futures / futures-util / futures-core / bytes / async-stream / tokio-util / time 共 11 个依赖加 optional = true
  4. [dependencies] 之后新增 [dev-dependencies]tokio = { version = "1", features = ["rt", "macros"] }

验证

cargo build --no-default-features           # 不依赖任何 optional crate,应通过
cargo build -F document                      # 零外部依赖,应通过

Commit 2cfg 门控(pub mod + pub use + init_tracing

涉及文件src/lib.rssrc/llm.rssrc/llm/cycle.rssrc/tools.rssrc/memory/store.rssrc/memory.rssrc/agent/session.rs

操作清单

按文件逐一执行:

  1. src/lib.rs — 7 个 pub mod#[cfg(feature = "...")]Document pub use 加 cfg、init_tracing 整体用 #[cfg(feature = "tracing-init")] 包裹
  2. src/llm.rs — 10 个子模块按 llm-types / llm / provider 分类门控
  3. src/llm/cycle.rsToolRegistry 导入加 #[cfg(feature = "tools")],工具字段和方法加相同门控
  4. src/tools.rspub mod mcppub use mcp::*#[cfg(feature = "tools-mcp")]
  5. src/memory/store.rspub mod sqlite_storepub use sqlite_store::SqliteStore#[cfg(feature = "memory-sqlite")]
  6. src/memory.rspub use store::SqliteStore#[cfg(feature = "memory-sqlite")]
  7. src/agent/session.rs — engine 相关导入加 #[cfg(feature = "engine")]to_snapshot/from_snapshot/restore_memory 加相同门控

验证

cargo build -F "full"                        # 全量回归
cargo build -F "prompt"                      # 验证 llm::types imply 路径
cargo build -F "tools"                       # 验证 tokio imply 路径
cargo build -F "memory"                      # 验证记忆模块不含 sqlite
cargo build -F "chat,provider-openai"        # 纯对话组合
cargo build -F "chat,provider-openai,tools-mcp"  # 带 MCP 对话

Commit 3Checkpoint 全量验证

操作清单

  1. 完整的验证矩阵执行(见第 5 节)
  2. cargo test -F "full" 确认 427 passed

验证

cargo test -F "full"
cargo build -F "light"
cargo build -F "multi"

5. 验证标准

编译验证矩阵

命令 验证目标 预期结果
cargo build --no-default-features 空 crate 编译通过(无模块)
cargo build -F "full" 全量回归 编译通过,与 v0.3.0 语义一致
cargo test -F "full" 测试回归 427 passed
cargo build -F "document" 文档模块独立 编译通过,零外部依赖
cargo build -F "prompt" 提示词独立 编译通过,llm::types imply 路径正确
cargo build -F "tools" 工具独立 编译通过,tokio imply 路径正确
cargo build -F "memory" 记忆模块独立编译 不含 sqlite(依赖 B1/B2 修复)
cargo build -F "memory-sqlite" 含 SQLite 的记忆模块 含 rusqlite
cargo build -F "agent" Agent 独立编译 含 llm+tools+memory,不含 engine(依赖 B1/B3 修复)
cargo build -F "engine" Engine 独立编译 imply agent → llm+tools+memory
cargo build -F "chat,provider-openai" 纯对话组合 编译通过,不含 MCP、sqlite
cargo build -F "chat,provider-openai,tools-mcp" 带 MCP 对话 编译通过,含 reqwest 无 sqlite
cargo build -F "light" 生产常用组合 编译通过
cargo build -F "multi" 多 provider 组合 编译通过

验证操作指令

每次编译验证后执行(验证编译产物不含意外符号):

# 确认空 crate 确实没有模块符号
cargo build --no-default-features 2>&1 && echo "OK"

# 确认 document 零外部依赖(无 reqwest/rusqlite 等符号)
cargo build -F "document" 2>&1 && echo "OK"

# 全量构建 + 测试
cargo build -F "full" 2>&1 && cargo test -F "full" 2>&1 | tail -5

验证通过条件

  • 所有 14 条编译验证命令返回 exit code 0
  • cargo test -F "full" 输出 427 passed(与 v0.3.0 基线一致,不要求测试数精确匹配,但必须全部通过且数量合理)
  • unused import / unused variable / dead code warning(由 #[cfg] 引起的新 warning 需逐一修复)

6. 风险评估

风险 等级 缓解措施
tokio features 拆细遗漏:某些代码路径用到 net/fs/signal 已通过 SA(静态分析)验证全库无相关使用
#[tokio::test] 编译失败:测试代码无 tokio runtime [dev-dependencies] 添加 tokio = { version = "1", features = ["rt", "macros"] }
下游 transitive tokio features 缩小:依赖 agcore 的 crate 之前通过 agcore 间接获得 full tokio,现在范围缩小 Phase 27 README 发布说明中明确告知迁移方案;下游如需完整 tokio 需自行添加
imply 链未闭合:某个 feature 依赖了未 imply 的 feature 7 种特征组合全部逐条构建验证;features 定义中有交叉引用的全部显式列出
unused cfg warning:某些 #[cfg] 标记导致编译 warning 每个 commit 后检查编译器输出,发现后立即修复
测试依赖循环dev-deps 与普通 deps 版本冲突 dev-deps 的 tokio 版本与主依赖保持一致 (version = "1",由 cargo 自动选择兼容版本)
memory 跨模块 imply 链 memory 模块依赖 llmconversation/vector_store)、tokioknowledge)、timetypes),imply 链必须完整传递
跨模块引用未门控 provider.rs 依赖 reqwest、cycle.rs 依赖 ToolRegistry、session.rs 依赖 engineStep 1 必须添加 cfg 门控