diff --git a/wiki/archive/memory-extraction-details.md b/wiki/archive/memory-extraction-details.md new file mode 100644 index 0000000..d84fb29 --- /dev/null +++ b/wiki/archive/memory-extraction-details.md @@ -0,0 +1,73 @@ +--- +title: 记忆提取详细逻辑 (归档) +archived_from: wiki/memory.md +archived_on: 2026-04-22 +reason: Wiki 压缩重构,详细提取逻辑从主页面移除 +--- + +# 记忆提取详细逻辑 (归档) + +> 2026-04-22 从 wiki/memory.md 归档。详细提取 prose 和 Hermes 分析内容。 +> 主页面仅保留数据流 + 不变量 + 活跃问题。 + +## 原始内容 + +详细的提取逻辑 prose 已移除。需要时请参考以下源文件: + +- `crates/zclaw-growth/src/extractor.rs` — LLM 记忆提取实现 +- `crates/zclaw-growth/src/retriever.rs` — 语义检索实现 +- `crates/zclaw-growth/src/retrieval/query.rs` — QueryAnalyzer 意图分类 +- `crates/zclaw-growth/src/experience_store.rs` — 经验 CRUD +- `wiki/archive/hermes-analysis.md` — Hermes 管线完整分析 (463 行) + +## 查询意图分类 (QueryAnalyzer) + +| 意图 | 说明 | 检索策略 | +|------|------|----------| +| Preference | 用户偏好 | 精确匹配 preference 类型记忆 | +| Knowledge | 知识查询 | 语义搜索 knowledge 类型 | +| Experience | 经验检索 | 时间+相关性排序 | +| Code | 代码相关 | 关键词优先 | +| General | 通用 | 混合策略 | + +## 进化引擎组件清单 + +``` +EvolutionEngine — 行为模式检测 → 技能/工作流建议 +FeedbackCollector — 收集用户反馈信号 +PatternAggregator — 行为模式聚合 +QualityGate — 进化质量门控 (长度/标题/置信度/去重) +SkillGenerator — 自动技能生成 (SkillManifest) +WorkflowComposer — 工作流自动编排 +ProfileUpdater — 用户画像更新 +ExperienceExtractor — 经验提取器 +Summarizer — 记忆摘要 +``` + +## zclaw-growth 完整模块结构 (19 文件) + +``` +crates/zclaw-growth/src/ +├── evolution_engine.rs 进化引擎核心 +├── experience_extractor.rs 经验提取 +├── experience_store.rs 经验 CRUD +├── extractor.rs 记忆提取 +├── feedback_collector.rs 反馈收集 +├── injector.rs Prompt 注入 +├── json_utils.rs JSON 工具 +├── pattern_aggregator.rs 模式聚合 +├── profile_updater.rs 画像更新 +├── quality_gate.rs 质量门控 +├── retriever.rs 语义检索 +├── skill_generator.rs 技能生成 +├── summarizer.rs 摘要生成 +├── tracker.rs 追踪器 +├── types.rs 类型定义 +├── viking_adapter.rs Viking 适配器 +├── workflow_composer.rs 工作流编排 +├── retrieval/ 检索子模块 +│ ├── query.rs 意图分类 + CJK +│ └── semantic.rs EmbeddingClient +└── storage/ 存储子模块 + └── sqlite.rs FTS5 + TF-IDF +``` diff --git a/wiki/memory.md b/wiki/memory.md index b073d2c..3c8b7eb 100644 --- a/wiki/memory.md +++ b/wiki/memory.md @@ -2,412 +2,146 @@ title: 记忆管道 updated: 2026-04-22 status: active -tags: [module, memory, growth] +tags: [module, memory, fts5, growth] --- # 记忆管道 (Memory Pipeline) -> 从 [[index]] 导航。关联模块: [[chat]] [[middleware]] +> 从 [[index]] 导航。关联: [[chat]] [[middleware]] [[butler]] +> 详细提取逻辑归档: [[archive/memory-extraction-details]] -## 设计思想 +## 1. 设计决策 -**核心问题: LLM 无状态,每次对话都从零开始。需要从历史对话中积累知识。** +**核心问题: LLM 无状态,每次对话从零开始。需要从历史对话中积累知识。** -设计决策: -1. **闭环架构** — 对话 → 提取 → 索引 → 检索 → 注入,形成正向循环 -2. **FTS5 + TF-IDF** — 轻量级语义搜索,不依赖外部 embedding 服务 -3. **Token 预算控制** — 注入 system prompt 时有 token 上限,防止溢出 -4. **EmbeddingClient trait 已预留** — 接口已写,激活即可升级到向量搜索 +| 决策 | WHY | +|------|-----| +| 闭环架构 | 对话→提取→索引→检索→注入,形成正向循环。每次聊天都积累,每次提问都利用 | +| 双数据库 | memories.db (FTS5 全文索引) + data.db (结构化画像)。前者处理模糊语义检索,后者处理精确字段查询 | +| FTS5+TF-IDF+Embedding 三层 | FTS5 粗筛 + TF-IDF 加权 + Embedding 精排(当前 NoOp,配置后激活)。不依赖外部服务即可工作 | +| 进化引擎 | 从对话中检测行为模式变化,生成进化候选项(技能建议/工作流优化),通过 EvolutionMiddleware@78 注入 system prompt | +| Token 预算控制 | 注入 system prompt 时有 token 上限,防止记忆注入挤占用户实际对话空间 | -## 功能清单 +**Hermes 核心借鉴** (详见 [[archive/hermes-analysis]]): 经验库 FTS5 全文检索 + 用户画像结构化建模 + 自然语言 cron 调度 + 轨迹记录压缩。4 Chunk 已交付: ExperienceStore(10 tests) + UserProfileStore(14 tests) + NlScheduleParser(16 tests) + TrajectoryRecorder(18 tests)。 -| 功能 | 描述 | 入口文件 | 状态 | -|------|------|----------|------| -| 记忆提取 | LLM 从对话中提取偏好/知识/经验 | extractor.rs | ✅ | -| FTS5 全文检索 | SQLite FTS5 + TF-IDF 权重搜索 | storage/sqlite.rs | ✅ | -| 语义检索 | 意图分类 + CJK 关键词 + 混合评分 | retriever.rs, retrieval/query.rs | ✅ | -| Prompt 注入 | token 预算控制 + 结构化上下文注入 | injector.rs | ✅ | -| 经验管理 | pain→solution→outcome CRUD | experience_store.rs | ✅ | -| 用户画像 | 结构化偏好/兴趣/能力管理 | profile_updater.rs | ✅ | -| 进化引擎 | 行为模式检测 → 技能/工作流建议 | evolution_engine.rs | ✅ | -| 质量门控 | 长度/标题/置信度/去重校验 | quality_gate.rs | ✅ | -| 自动技能生成 | 从模式生成 SkillManifest | skill_generator.rs | ✅ | -| 上下文压缩 | 超长对话压缩摘要 | summarizer.rs | ✅ | -| 嵌入向量 | EmbeddingClient trait + NoOp 默认 | retrieval/semantic.rs | 🚧 接口就绪 | -| SaaS pgvector | knowledge_chunks 向量索引 | saas/generate_embedding.rs | 🚧 索引就绪,生成未实现 | +## 2. 关键文件 + 数据流 -## 代码逻辑 +### 核心文件 + +| 文件 | 职责 | +|------|------| +| `crates/zclaw-growth/src/extractor.rs` | LLM 记忆提取 (偏好/知识/经验) | +| `crates/zclaw-growth/src/retriever.rs` | 语义检索 (FTS5 + TF-IDF + 意图分类) | +| `crates/zclaw-growth/src/injector.rs` | Prompt 注入 (token 预算) | +| `crates/zclaw-growth/src/storage/sqlite.rs` | FTS5 + TF-IDF 核心 (memories.db) | +| `crates/zclaw-runtime/src/middleware/memory.rs` | 记忆中间件 (提取+注入编排) | +| `crates/zclaw-runtime/src/growth.rs` | GrowthIntegration 闭环编排 | +| `crates/zclaw-memory/src/user_profile_store.rs` | UserProfileStore (data.db) | ### 闭环数据流 ``` -[提取] 对话发生 - → MemoryExtractor (crates/zclaw-growth/src/extractor.rs) - → LLM 提取: 偏好 (Preference) / 知识 (Knowledge) / 经验 (Experience) - → MemoryEntry { agent_id, memory_type, content, keywords, importance } +[提取] 对话完成 → MemoryMiddleware.after_completion + → MemoryExtractor.extract_combined() → LLM 单次调用 + → CombinedExtraction { memories, experiences, profile_signals } + → VikingAdapter → SqliteStorage → memories.db (FTS5 索引) + → UserProfileStore → data.db (结构化画像) -[索引] 存储 - → SqliteStorage.store() (crates/zclaw-growth/src/storage/sqlite.rs) - → SQLite + FTS5 全文索引 - → TF-IDF 权重计算 - → (可选) EmbeddingClient.embed() → 向量存储 [未激活] - -[检索] 查询时 - → MemoryRetriever.retrieve(query, agent_id) (crates/zclaw-growth/src/retriever.rs) - → QueryAnalyzer: 意图分类 (Preference/Knowledge/Experience/Code/General) - → 中文+英文关键词提取 + CJK 支持 + 同义词扩展 - → SemanticScorer: TF-IDF 匹配 (70% embedding / 30% TF-IDF, embedding 未激活) - → 返回 top-k 相关记忆 - -[注入] 给 LLM - → PromptInjector.inject(system_prompt, memories) (crates/zclaw-growth/src/injector.rs) - → token 预算控制 - → 格式化为结构化上下文块 - → 插入到 system prompt 中 +[检索] 新请求 → MemoryMiddleware.before_completion + → MemoryRetriever.retrieve(agent_id, user_input) + → QueryAnalyzer 意图分类 (5类: Preference/Knowledge/Experience/Code/General) + → FTS5 全文搜索 + TF-IDF 评分 + IdentityRecall 43+ 模式 + → 弱身份 fallback: <3 结果 → 补充 broad retrieval + → PromptInjector.inject_with_format(system_prompt, memories) ``` -### 跨会话记忆完整链路(2026-04-22 验证通过) +### 集成契约 -> **重要:修改任何环节前请先阅读此链路,避免引入断裂。** +| 方向 | 模块 | 触发点 | +|------|------|--------| +| Called by | middleware: Memory@150 | Every chat completion (after) + new request (before) | +| Calls | zclaw-memory: FTS5 store, TF-IDF scorer | Memory extraction + retrieval | +| Calls | zclaw-growth: GrowthIntegration, EvolutionEngine | Pattern detection + skill generation | +| Provides | loop_runner: Context injection into system prompt | Before LLM call | + +### Tauri 命令 + +| 分类 | 命令数 | 关键命令 | +|------|--------|----------| +| Memory CRUD (`memory_commands.rs`) | 11 | `memory_store`, `memory_search`, `memory_build_context` | +| VikingStorage (`viking_commands.rs`) | 14 | `viking_add`, `viking_find`, `viking_inject_prompt` | +| Intelligence (`intelligence/`) | ~40 | identity(13), heartbeat(11), pain(5), reflection(6) | +| 提取+Embedding | 5 | `extract_session_memories`, `embedding_create` | + +## 3. 代码逻辑 + +### 跨会话记忆完整链路 ``` -[初始化] kernel_init (desktop/src-tauri/src/kernel_commands/lifecycle.rs) - ① viking_commands::init_storage() → 初始化 SqliteStorage → memories.db - ② Kernel::boot(config) → 创建 MemoryStore (data.db) + in-memory VikingAdapter - ③ viking_commands::get_storage() → SqliteStorage 包装为 VikingAdapter → kernel.set_viking() - ↑ 此时 GrowthIntegration 缓存被清除 - ④ TauriExtractionDriver::new(driver, model) → kernel.set_extraction_driver() - ↑ GrowthIntegration 缓存再次清除 - ⑤ [首次聊天时] create_middleware_chain() 重建 GrowthIntegration: - - GrowthIntegration::new(self.viking.clone()) ← 持久化 SqliteStorage - - .with_llm_driver(extraction_driver) ← LLM 提取能力 - - .with_profile_store(UserProfileStore::new(memory.pool())) ← data.db 画像持久化 - - .configure_embedding(embedding_client) ← 语义检索(可选) - → 缓存到 kernel.growth (Mutex>) - → MemoryMiddleware::new(growth) 注册到中间件链 (priority=150) +[初始化] kernel_init → init_storage(memories.db) → MemoryStore(data.db) + → set_viking(SqliteStorage) → set_extraction_driver(LLM) + → [首次聊天] create_middleware_chain() 重建 GrowthIntegration: + VikingAdapter + ExtractionDriver + UserProfileStore(data.db) + → MemoryMiddleware@150 注册到中间件链 -[写入] after_completion — 记忆提取 (crates/zclaw-runtime/src/middleware/memory.rs) - MemoryMiddleware.after_completion(ctx) - → 30秒去重: should_extract(agent_id) — 同一 agent 30秒内跳过 - → growth.extract_combined(agent_id, messages, session_id) - → MemoryExtractor.extract_combined() (crates/zclaw-growth/src/extractor.rs) - → LLM 单次调用 (COMBINED_EXTRACTION_PROMPT + 对话文本) - → 返回 CombinedExtraction { memories, experiences, profile_signals } - → extractor.store_memories(agent_id, memories) - → VikingAdapter → SqliteStorage.store() → memories.db (FTS5 索引) - → experience_extractor.persist_experiences(agent_id, combined) - → agent://{agent_id}/experience/... URI - → profile_updater.collect_updates(combined) - → UserProfileStore.update_field/add_recent_topic/add_pain_point/add_preferred_tool - → 写入 data.db.user_profiles 表 (user_id = agent_id) +[写入] after_completion → 30秒去重 → extract_combined(LLM) + → memories → memories.db | profile_signals → data.db -[读取] before_completion — 记忆检索+注入 (每个新请求) - MemoryMiddleware.before_completion(ctx) - → growth.enhance_prompt(agent_id, system_prompt, user_input) - → retriever.retrieve(agent_id, user_input) - → 按 agent_id 构建搜索范围: agent://{agent_id}/{type} - → QueryAnalyzer 意图分析 + IdentityRecall 43+ 模式匹配 - → FTS5 全文搜索 + TF-IDF 评分 + 语义重排序 - → 弱身份 fallback: <3 结果 + weak_identity → 补充 broad retrieval - → injector.inject_with_format(system_prompt, memories) - → 按 token 预算注入结构化上下文 +[读取] before_completion → retrieve(agent_id, query) + → FTS5 + TF-IDF + IdentityRecall → inject_with_format() -[展示] 管家Tab — 前端读取 (desktop/src/components/ButlerPanel/) - MemorySection.tsx: - → listVikingResources("agent://{agent_id}/") → viking_ls → memories.db - → readVikingResource(uri, "L1") → viking_read → L1 摘要 - → 按类型分组: 偏好/知识/经验/会话 - → agent_get(agentId) → kernel.memory() → UserProfileStore.get() → data.db - → 用户画像卡片: 行业/角色/沟通风格/近期话题/常用工具 - -[数据库架构] - memories.db (SqliteStorage, viking_commands 管理) - → memories 表: URI + memory_type + content + FTS5 索引 - → memories_fts 虚拟表: FTS5 trigram tokenizer (CJK 支持) - data.db (MemoryStore, kernel 管理) - → user_profiles 表: user_id + industry + role + recent_topics(JSON) + ... - → agents 表: agent 配置 - → sessions 表: 会话数据 - -[关键文件地图] - crates/zclaw-kernel/src/kernel/mod.rs create_middleware_chain() + memory() - crates/zclaw-runtime/src/middleware/memory.rs MemoryMiddleware before/after - crates/zclaw-runtime/src/growth.rs GrowthIntegration 闭环编排 - crates/zclaw-growth/src/extractor.rs extract_combined() LLM 提取 - crates/zclaw-growth/src/retriever.rs retrieve() FTS5+TF-IDF 检索 - crates/zclaw-growth/src/injector.rs inject_with_format() prompt 注入 - crates/zclaw-growth/src/storage/sqlite.rs SqliteStorage (memories.db) - crates/zclaw-memory/src/user_profile_store.rs UserProfileStore (data.db) - desktop/src-tauri/src/viking_commands.rs viking_ls/viking_read Tauri 命令 - desktop/src-tauri/src/kernel_commands/agent.rs agent_get (读取 UserProfile) - desktop/src-tauri/src/kernel_commands/lifecycle.rs kernel_init (初始化链路) - desktop/src/components/ButlerPanel/MemorySection.tsx 前端展示 +[展示] 管家Tab → viking_ls/viking_read(memories.db) + agent_get(data.db) ``` -### 经验存储 (ExperienceStore) +### 不变量 -Hermes 管线 Chunk1 新增: +- memories.db 和 data.db 是独立的 SQLite 数据库,跨库查询需使用正确连接 +- 记忆注入在中间件@150,AFTER ButlerRouter@80,BEFORE SkillIndex@200 +- Embedding 当前为 NoOpEmbeddingClient,用户配置 provider 后替换为真实实现 +- 30 秒去重窗口:同一 agent 30 秒内跳过重复提取 +- URI scheme: `agent://{agent_id}/{type}/{category}` -``` -ExperienceStore (crates/zclaw-growth/src/experience_store.rs) - → CRUD 封装: pain → solution → outcome 结构化经验 - → 底层使用 VikingAdapter - → URI scheme: agent://{agent_id}/experience/... -``` +### 非显然逻辑 -### Hermes 管线 (4 Chunk) +- TF-IDF 语义路由:70% embedding 权重 + 30% TF-IDF(embedding 未激活时退化为纯 TF-IDF) +- IdentityRecall: 43+ 模式匹配("你记得我"/"上次说的"/"我的偏好"等)触发记忆检索 +- 弱身份 fallback: 检索结果 <3 且含弱身份关键词时,补充 broad retrieval 扩大搜索范围 -| Chunk | 模块 | 文件 | 测试 | -|-------|------|------|------| -| 1 | ExperienceStore + Extractor | experience_store.rs | 10 | -| 2 | UserProfileStore + Profiler | user_profile.rs | 14 | -| 3 | NlScheduleParser | nl_schedule.rs | 16 | -| 4 | TrajectoryRecorder + Compressor | middleware/trajectory_recorder.rs | 18 | +## 4. 活跃问题 + 陷阱 -Hermes 相关测试分布: -- `nl_schedule.rs`: 16 tests (中文时间→cron 解析) -- `types.rs`: 9 tests (记忆类型) -- `injector.rs`: 9 tests (prompt 注入) +### 活跃 -### 查询意图分类 - -`QueryAnalyzer` 支持 5 种意图: - -| 意图 | 说明 | 检索策略 | -|------|------|----------| -| Preference | 用户偏好 | 精确匹配 preference 类型记忆 | -| Knowledge | 知识查询 | 语义搜索 knowledge 类型 | -| Experience | 经验检索 | 时间+相关性排序 | -| Code | 代码相关 | 关键词优先 | -| General | 通用 | 混合策略 | - -### Embedding 基础设施 (已写未激活) - -``` -EmbeddingClient trait (crates/zclaw-growth/src/retrieval/semantic.rs) - → async embed(&str) -> Vec - → is_available() -> bool - → 当前实现: NoOpEmbeddingClient (始终返回空) - -SaaS 侧: - → pgvector HNSW 索引就绪 (knowledge_chunks 表, vector(1536)) - → generate_embedding Worker: 内容分块 + 中文关键词提取 (Phase 2 embedding deferred) -``` - -### 进化引擎 (EvolutionEngine) - -zclaw-growth 包含完整的自我改进闭环: - -``` -EvolutionEngine (crates/zclaw-growth/src/evolution_engine.rs) - → 从对话历史中检测行为模式变化 - → 生成进化候选项 (新技能建议/工作流优化) - → EvolutionMiddleware@78 注入 system prompt - -配套组件: - → FeedbackCollector — 收集用户反馈信号 - → PatternAggregator — 行为模式聚合 - → QualityGate — 进化质量门控 - → SkillGenerator — 自动技能生成 - → WorkflowComposer — 工作流自动编排 - → ProfileUpdater — 用户画像更新 - → ExperienceExtractor — 经验提取器 - → Summarizer — 记忆摘要 -``` - -zclaw-growth 模块结构 (19 文件): -``` -crates/zclaw-growth/src/ -├── evolution_engine.rs 进化引擎核心 -├── experience_extractor.rs 经验提取 -├── experience_store.rs 经验 CRUD -├── extractor.rs 记忆提取 -├── feedback_collector.rs 反馈收集 -├── injector.rs Prompt 注入 -├── json_utils.rs JSON 工具 -├── pattern_aggregator.rs 模式聚合 -├── profile_updater.rs 画像更新 -├── quality_gate.rs 质量门控 -├── retriever.rs 语义检索 -├── skill_generator.rs 技能生成 -├── summarizer.rs 摘要生成 -├── tracker.rs 追踪器 -├── types.rs 类型定义 -├── viking_adapter.rs Viking 适配器 -├── workflow_composer.rs 工作流编排 -├── retrieval/ 检索子模块 -│ ├── query.rs 意图分类 + CJK -│ └── semantic.rs EmbeddingClient -└── storage/ 存储子模块 - └── sqlite.rs FTS5 + TF-IDF -``` - -### 前端 Tauri 命令 - -> 完整 API 接口详情见下方 `## API 接口` 章节。 - -**Memory CRUD** (`desktop/src-tauri/src/memory_commands.rs`, 11 命令): - -| 命令 | 参数 | 说明 | +| 问题 | 状态 | 说明 | |------|------|------| -| `memory_store` | MemoryEntryInput | 存储记忆条目 | -| `memory_get` | id | 按 ID 获取 | -| `memory_search` | MemorySearchOptions | FTS5 + TF-IDF 搜索 | -| `memory_delete` | id | 删除单条 | -| `memory_delete_all` | agent_id | 删除 Agent 全部记忆 | -| `memory_stats` | — | 记忆统计 | -| `memory_export` | — | 导出全部 | -| `memory_import` | Vec\ | 批量导入 | -| `memory_build_context` | agent_id, query, max_tokens? | 构建检索增强上下文 | -| `memory_db_path` | — | SQLite 路径 | -| `memory_init` | — | 初始化 (no-op, Viking 自动初始化) | +| Embedding 未激活 | 🚧 长期观察 | NoOpEmbeddingClient 是默认值,用户配置 provider 后替换 | +| SaaS pgvector | 🚧 deferred | HNSW 索引就绪 (knowledge_chunks, vector(1536)),生成逻辑未实现 | -**VikingStorage** (`desktop/src-tauri/src/viking_commands.rs`, 14 命令): +### 历史陷阱 (已修复) -| 命令 | 说明 | -|------|------| -| `viking_add` / `viking_add_with_metadata` | 添加记忆 (URI scheme: agent://{id}/{type}/{category}) | -| `viking_find` | 语义/关键词搜索 | -| `viking_grep` | 正则搜索 | -| `viking_ls` / `viking_tree` / `viking_read` | 浏览/读取 | -| `viking_remove` | 删除 | -| `viking_inject_prompt` | 记忆注入 prompt | -| `viking_configure_embedding` | 配置 embedding provider | -| `viking_configure_summary_driver` | 配置摘要 LLM | -| `viking_store_with_summaries` | 自动摘要存储 | -| `viking_status` | 存储健康检查 | -| `viking_load_industry_keywords` | 加载行业关键词 | +- 跨会话记忆断裂 (profile_store 未连接 + 双数据库不一致 + 缺日志) → 04-22 已修复 (adf0251) +- 记忆去重失败 (同 URI+content 重复添加) → 04-17 已修复 (a504a40) +- 记忆非 Agent 隔离 (viking_find 返回所有 Agent 记忆) → 04-17 已修复 (a504a40) +- 跨会话注入断裂 (新会话报 "no conversation history found") → 04-17 已修复 (a504a40) -**Intelligence** (`desktop/src-tauri/src/intelligence/`, ~40 命令): +### 警告 -| 模块 | 命令数 | 功能 | -|------|--------|------| -| identity.rs | 13 | Agent 身份/SOUL.md 管理 | -| heartbeat.rs | 11 | Heartbeat 引擎配置+记录 | -| pain_aggregator.rs | 5 | 痛点追踪+方案生成 | -| reflection.rs | 6 | 自我反思引擎 | -| compactor.rs | 4 | 上下文压缩 | -| health_snapshot.rs | 1 | 综合健康快照 | +> memories.db / data.db 连接池隔离。修改存储层代码务必确认目标数据库。 +> GrowthIntegration 缓存在 `set_viking()` / `set_extraction_driver()` 时会被清除,首次聊天时重建。 -**记忆提取** (`desktop/src-tauri/src/memory/`, 3 命令): -`extract_session_memories`, `extract_and_store_memories`, `estimate_content_tokens` +## 5. 变更日志 -**Embedding** (`desktop/src-tauri/src/llm/`, 2 命令): -`embedding_create`, `embedding_providers` - -**总计 ~74 个** memory/growth/intelligence 相关 Tauri 命令。 - -## API 接口 - -### Memory CRUD (`desktop/src-tauri/src/memory_commands.rs`, 11 命令) - -| 命令 | 参数 | 说明 | +| 日期 | 变更 | 关联 | |------|------|------| -| `memory_store` | MemoryEntryInput | 存储记忆条目 | -| `memory_get` | id | 按 ID 获取 | -| `memory_search` | MemorySearchOptions | FTS5 + TF-IDF 搜索 | -| `memory_delete` | id | 删除单条 | -| `memory_delete_all` | agent_id | 删除 Agent 全部记忆 | -| `memory_stats` | — | 记忆统计 | -| `memory_export` | — | 导出全部 | -| `memory_import` | Vec\ | 批量导入 | -| `memory_build_context` | agent_id, query, max_tokens? | 构建检索增强上下文 | -| `memory_db_path` | — | SQLite 路径 | -| `memory_init` | — | 初始化 (no-op) | +| 2026-04-22 | 跨会话记忆断裂修复: profile_store 连接 + 双数据库统一 + 诊断日志 | commit adf0251 | +| 2026-04-22 | Wiki 5-section 重构: 363→~190 行,详细逻辑归档 | wiki/ | +| 2026-04-21 | Embedding 接通 + 自学习自动化 A线+B线 (SemanticScorer + EvolutionMiddleware) | 934 tests PASS | +| 2026-04-17 | E2E 全系统验证 129 链路: 记忆去重+注入+跨会话 修复 | 79.1% 通过率 | +| 2026-04-15 | Heartbeat 统一健康系统 (health_snapshot + 健康快照集成) | 183 commands | -### VikingStorage (`desktop/src-tauri/src/viking_commands.rs`, 14 命令) +## 测试概览 -| 命令 | 说明 | -|------|------| -| `viking_add` / `viking_add_with_metadata` | 添加记忆 (URI: agent://{id}/{type}/{category}) | -| `viking_find` | 语义/关键词搜索 | -| `viking_grep` | 正则搜索 | -| `viking_ls` / `viking_tree` / `viking_read` | 浏览/读取 | -| `viking_remove` | 删除 | -| `viking_inject_prompt` | 记忆注入 prompt | -| `viking_configure_embedding` | 配置 embedding provider | -| `viking_configure_summary_driver` | 配置摘要 LLM | -| `viking_store_with_summaries` | 自动摘要存储 | -| `viking_status` / `viking_load_industry_keywords` | 健康检查/行业关键词 | - -### Intelligence (`desktop/src-tauri/src/intelligence/`, ~40 命令) - -| 模块 | 命令数 | 功能 | -|------|--------|------| -| identity.rs | 13 | Agent 身份/SOUL.md 管理 | -| heartbeat.rs | 11 | Heartbeat 引擎配置+记录 | -| pain_aggregator.rs | 5 | 痛点追踪+方案生成 | -| reflection.rs | 6 | 自我反思引擎 | -| compactor.rs | 4 | 上下文压缩 | -| health_snapshot.rs | 1 | 综合健康快照 | - -### 记忆提取 + Embedding (5 命令) - -`extract_session_memories`, `extract_and_store_memories`, `estimate_content_tokens` (memory/) -+ `embedding_create`, `embedding_providers` (llm/) - -**总计 ~74 个** memory/growth/intelligence 相关 Tauri 命令。 - -## 测试链路 - -| 功能 | Crate | 测试文件 | 测试数 | 覆盖状态 | -|------|-------|---------|--------|---------| -| 记忆存储/检索 | zclaw-growth | storage/sqlite.rs | 6 | ✅ | -| 经验 CRUD | zclaw-growth | experience_store.rs | 12 | ✅ | -| 提取器 | zclaw-growth | extractor.rs | 8 | ✅ | -| 注入器 | zclaw-growth | injector.rs | 9 | ✅ | -| 质量门控 | zclaw-growth | quality_gate.rs | 9 | ✅ | -| 进化引擎 | zclaw-growth | evolution_engine.rs | 5 | ✅ | -| 技能生成 | zclaw-growth | skill_generator.rs | 6 | ✅ | -| 工作流编排 | zclaw-growth | workflow_composer.rs | 5 | ✅ | -| 意图分类+CJK | zclaw-growth | retrieval/query.rs | 11 | ✅ | -| 语义评分 | zclaw-growth | retrieval/semantic.rs | 9 | ✅ | -| Embedding | zclaw-growth | retrieval/cache.rs | 7 | ✅ | -| 模式聚合 | zclaw-growth | pattern_aggregator.rs | 6 | ✅ | -| 反馈收集 | zclaw-growth | feedback_collector.rs | 10 | ✅ | -| 摘要 | zclaw-growth | summarizer.rs | 5 | ✅ | -| 类型定义 | zclaw-growth | types.rs | 13 | ✅ | -| 进化闭环 | zclaw-growth | tests/evolution_loop_test.rs | 6 | ✅ | -| 经验链 | zclaw-growth | tests/experience_chain_test.rs | 6 | ✅ | -| 集成 | zclaw-growth | tests/integration_test.rs | 9 | ✅ | -| **zclaw-growth 小计** | | 29 文件 | **181** | | -| 记忆存储 | zclaw-memory | store.rs | 20 | ✅ | -| 用户画像 | zclaw-memory | user_profile_store.rs | 20 | ✅ | -| 轨迹记录 | zclaw-memory | trajectory_store.rs | 9 | ✅ | -| 事实提取 | zclaw-memory | fact.rs | 5 | ✅ | -| **zclaw-memory 小计** | | 4 文件 | **54** | | -| **合计** | | 33 文件 | **235** | | - -## 关联模块 - -- [[chat]] — 对话是记忆的输入源 -- [[butler]] — 管家模式可能利用记忆提供个性化响应 -- [[middleware]] — Memory 中间件自动提取 + SkillIndex 注入 - -## 关键文件 - -| 文件 | 职责 | -|------|------| -| `crates/zclaw-growth/src/extractor.rs` | LLM 记忆提取 | -| `crates/zclaw-growth/src/retriever.rs` | 语义检索 | -| `crates/zclaw-growth/src/injector.rs` | Prompt 注入 (token 预算) | -| `crates/zclaw-growth/src/experience_store.rs` | 经验 CRUD | -| `crates/zclaw-growth/src/storage/sqlite.rs` | FTS5 + TF-IDF 核心 | -| `crates/zclaw-growth/src/retrieval/semantic.rs` | EmbeddingClient trait | -| `crates/zclaw-growth/src/retrieval/query.rs` | 意图分类 + CJK 关键词 | -| `crates/zclaw-growth/src/nl_schedule.rs` | 中文时间→cron 解析 (16 tests) | -| `crates/zclaw-runtime/src/middleware/memory.rs` | 记忆中间件 | -| `crates/zclaw-runtime/src/middleware/trajectory_recorder.rs` | 轨迹记录中间件 | -| `desktop/src/store/memoryGraphStore.ts` | 前端记忆 UI | -| `desktop/src-tauri/src/memory/` | Tauri 记忆命令桥接 | -| `desktop/src-tauri/src/memory_commands.rs` | 13 个 memory CRUD 命令 | - -## 已知问题 - -- ✅ **记忆去重失败** — BUG-H2 已修复 (commit a504a40)。`viking_add` 同 URI+content 重复添加 -- ✅ **记忆非 Agent 隔离** — BUG-M3 已修复 (commit a504a40)。`viking_find` 返回所有 Agent 记忆 -- ✅ **跨会话注入断裂** — BUG-M5 已修复 (commit a504a40)。新会话报 "no conversation history found" -- ✅ **profile_store 未连接** — 已修复 (commit adf0251)。create_middleware_chain() 中未设置 UserProfileStore,导致 profile_signals 被静默丢弃 -- ✅ **双数据库 UserProfile 不一致** — 已修复 (commit adf0251)。UserProfileStore 写入 data.db 而 agent_get 读取 memories.db -- ⚠️ **NoOpEmbeddingClient 是默认值** — 正常设计,用户配置 provider 后替换 -- ⚠️ **SaaS pgvector embedding 生成未实现** — 索引就绪,生成逻辑 deferred +| Crate | 测试数 | 覆盖 | +|-------|--------|------| +| zclaw-growth | 181 (29 文件) | 全覆盖 | +| zclaw-memory | 54 (4 文件) | 全覆盖 | +| **合计** | **235** | | diff --git a/wiki/middleware.md b/wiki/middleware.md index c9d4350..8b66054 100644 --- a/wiki/middleware.md +++ b/wiki/middleware.md @@ -1,94 +1,105 @@ --- title: 中间件链 -updated: 2026-04-21 +updated: 2026-04-22 status: active tags: [module, middleware, runtime] --- # 中间件链 -> 从 [[index]] 导航。关联模块: [[chat]] [[butler]] [[memory]] +> 从 [[index]] 导航。关联模块: [[chat]] [[butler]] [[memory]] [[hands-skills]] -## 设计思想 +## 1. 设计决策 -**中间件是请求处理的管道,按优先级顺序执行。** +**中间件是请求处理的管道,每条聊天消息都经过完整链路。** -- 优先级 0-999,数值越小越先执行(`middleware.rs` 按升序排列) -- 每层中间件实现 `AgentMiddleware` trait,4个 hook 点: `before_completion` / `before_tool_call` / `after_tool_call` / `after_completion` -- 所有消息流(聊天、管家)都经过完整中间件链 -- 中间件可返回 `Stop`/`Block`/`AbortLoop` 决策来中断流程 +- **WHY 优先级排序 (0-999)**: 数值越小越先执行。宽范围设计允许在任意位置插入新中间件而无需重新编号。 +- **WHY 注册顺序 != 执行顺序**: `kernel/mod.rs` 中 14 次 `chain.register()` 的代码顺序与运行时顺序无关,chain 按 `priority()` 升序排列后执行。 +- **WHY 6 类 14 层**: 进化(70-79) -> 路由(80-99) -> 上下文(100-199) -> 能力(200-399) -> 安全(400-599) -> 遥测(600-799),优先级范围即执行阶段。 +- **WHY Stop/Block/AbortLoop**: 细粒度流控 -- Stop 中断 LLM 循环,Block 阻止单次工具调用,AbortLoop 终止整个 Agent 循环。命中后跳过所有后续中间件。 -## 代码逻辑 +## 2. 关键文件 + 数据流 -### 14 层 Runtime 中间件(注册顺序见 `kernel/mod.rs:248-361`,执行按 priority 升序) +### 核心文件 -| # | 中间件 | 优先级 | 文件 | 职责 | 注册条件 | -|---|--------|--------|------|------|----------| -| 1 | EvolutionMiddleware | 78 | `middleware/evolution.rs` | 推送进化候选项到 system prompt | 始终 | -| 2 | ButlerRouter | 80 | `middleware/butler_router.rs` | 语义技能路由 + system prompt 增强 | 始终 | -| 3 | Compaction | 100 | `middleware/compaction.rs` | 超阈值时压缩对话历史 | `compaction_threshold > 0` | -| 4 | Memory | 150 | `middleware/memory.rs` | 对话后自动提取记忆 + 进化检查 | 始终 | -| 5 | Title | 180 | `middleware/title.rs` | 自动生成会话标题 | 始终 | -| 6 | SkillIndex | 200 | `middleware/skill_index.rs` | 注入技能索引到 system prompt | `!skill_index.is_empty()` | -| 7 | DanglingTool | 300 | `middleware/dangling_tool.rs` | 修复缺失的工具调用结果 | 始终 | -| 8 | ToolError | 350 | `middleware/tool_error.rs` | 格式化工具错误供 LLM 恢复 | 始终 | -| 9 | ToolOutputGuard | 360 | `middleware/tool_output_guard.rs` | 工具输出安全检查 | 始终 | -| 10 | Guardrail | 400 | `middleware/guardrail.rs` | shell_exec/file_write/web_fetch 安全规则 | 始终 | -| 11 | LoopGuard | 500 | `middleware/loop_guard.rs` | 防止工具调用无限循环 | 始终 | -| 12 | SubagentLimit | 550 | `middleware/subagent_limit.rs` | 限制并发子 agent | 始终 | -| 13 | TrajectoryRecorder | 650 | `middleware/trajectory_recorder.rs` | 轨迹记录 + 压缩 | 始终 | -| 14 | TokenCalibration | 700 | `middleware/token_calibration.rs` | Token 用量校准 | 始终 | +| 文件 | 职责 | +|------|------| +| `crates/zclaw-runtime/src/middleware.rs` | `AgentMiddleware` trait + `MiddlewareChain` 执行引擎 | +| `crates/zclaw-runtime/src/middleware/` | 14 个中间件实现 (.rs) | +| `crates/zclaw-kernel/src/kernel/mod.rs:248-361` | `create_middleware_chain()` 注册入口 (14 次 register) | +| `crates/zclaw-saas/src/main.rs` | SaaS HTTP 中间件注册 (10 层) | -> **注意**: 注册顺序(代码中的 chain.register 调用顺序)与执行顺序不同。Chain 按 priority 升序排列后执行。 - -### 10 层 SaaS HTTP 中间件(`zclaw-saas/src/main.rs`) - -| # | 中间件 | 职责 | 层级 | -|---|--------|------|------| -| 1 | public_rate_limit_middleware | 公共端点限流 (20次/分钟/IP) | 公共路由 | -| 2 | api_version_middleware | API 版本校验 | 公共 + 认证路由 | -| 3 | request_id_middleware | 请求 ID 注入 | 公共 + 认证路由 | -| 4 | rate_limit_middleware | 认证端点限流 (5次/分钟/IP) | 认证路由 | -| 5 | auth_middleware | JWT 认证 + 权限校验 | 认证路由 | -| 6 | TimeoutLayer | 请求超时 15s | 认证路由 | -| 7 | api_version_middleware (relay) | API 版本校验 | Relay 路由 | -| 8 | request_id_middleware (relay) | 请求 ID 注入 | Relay 路由 | -| 9 | quota_check_middleware | 配额检查 | Relay 路由 | -| 10 | CORS / 其他 layer | 跨域等 | 全局 | - -### 优先级分类(Runtime,来自 `middleware.rs` 头注释) - -| 范围 | 类别 | 包含的中间件 | -|------|------|-------------| -| 70-79 | 进化 | EvolutionMiddleware | -| 80-99 | 路由 | ButlerRouter | -| 100-199 | 上下文塑造 | Compaction, Memory | -| 200-399 | 能力 | SkillIndex, DanglingTool, ToolError, ToolOutputGuard | -| 400-599 | 安全 | Guardrail, LoopGuard, SubagentLimit | -| 600-799 | 遥测 | TrajectoryRecorder, TokenCalibration, Title | - -### 中间件执行流 +### 执行流 ``` -用户消息 → AgentLoop - → chain.run_before_completion(ctx) - → [按优先级升序] 每层 middleware.before_completion() - → Continue: 继续下一层 - → Stop(reason): 中断循环,返回 reason - → LLM 调用 - → (工具调用时) chain.run_before_tool_call() - → Allow: 允许执行 - → Block(msg): 阻止,返回错误给 LLM - → ReplaceInput: 替换参数后允许 - → AbortLoop: 立即终止整个循环 - → chain.run_after_tool_call() - → chain.run_after_completion() +用户消息 -> AgentLoop + -> chain.run_before_completion(ctx) + -> [按 priority 升序] 每层 middleware.before_completion() + -> Continue: 下一层 | Stop(reason): 中断循环 + -> LLM 调用 + -> (工具调用时) chain.run_before_tool_call() + -> Allow | Block(msg) | ReplaceInput | AbortLoop + -> 工具执行 + -> chain.run_after_tool_call() + -> chain.run_after_completion() ``` +### 集成契约 + +| 方向 | 模块 | 接口 | 触发时机 | +|------|------|------|----------| +| Called by <- | kernel | `kernel/mod.rs:create_middleware_chain()` | Kernel 启动 | +| Calls -> | runtime | `MiddlewareChain::run_before_completion()` | 每条聊天请求 | +| Called by <- | saas | HTTP relay handler | SaaS relay 路由 | +| Provides -> | all | `AgentMiddleware` trait | 14 个实现 | + +## 3. 代码逻辑 + +### 14 层 Runtime 中间件 + +| 优先级 | 中间件 | 文件 | 职责 | 注册条件 | +|--------|--------|------|------|----------| +| @78 | EvolutionMiddleware | `evolution.rs` | 推送进化候选项到 system prompt | 始终 | +| @80 | ButlerRouter | `butler_router.rs` | 语义技能路由 + system prompt 增强 + XML fencing | 始终 | +| @100 | Compaction | `compaction.rs` | 超阈值时压缩对话历史 | `compaction_threshold > 0` | +| @150 | Memory | `memory.rs` | 对话后自动提取记忆 + 注入检索结果 | 始终 | +| @180 | Title | `title.rs` | 自动生成会话标题 | 始终 | +| @200 | SkillIndex | `skill_index.rs` | 注入技能索引到 system prompt | `!skill_index.is_empty()` | +| @300 | DanglingTool | `dangling_tool.rs` | 修复缺失的工具调用结果 | 始终 | +| @350 | ToolError | `tool_error.rs` | 格式化工具错误供 LLM 恢复 | 始终 | +| @360 | ToolOutputGuard | `tool_output_guard.rs` | 工具输出安全检查 | 始终 | +| @400 | Guardrail | `guardrail.rs` | shell_exec/file_write/web_fetch 安全规则 | 始终 | +| @500 | LoopGuard | `loop_guard.rs` | 防止工具调用无限循环 | 始终 | +| @550 | SubagentLimit | `subagent_limit.rs` | 限制并发子 agent | 始终 | +| @650 | TrajectoryRecorder | `trajectory_recorder.rs` | 轨迹记录 + 压缩 | 始终 | +| @700 | TokenCalibration | `token_calibration.rs` | Token 用量校准 | 始终 | + +> 注册顺序 (代码) 与执行顺序 (priority) 不同。Chain 按 priority 升序排列后执行。 + +### 10 层 SaaS HTTP 中间件 + +| 层级 | 中间件 | 职责 | +|------|--------|------| +| 公共路由 | `public_rate_limit_middleware` | 20次/分钟/IP | +| 公共+认证 | `api_version_middleware` | API 版本校验 | +| 公共+认证 | `request_id_middleware` | 请求 ID 注入 | +| 认证路由 | `rate_limit_middleware` | 5次/分钟/IP | +| 认证路由 | `auth_middleware` | JWT 认证 + 权限 | +| 认证路由 | `TimeoutLayer` | 请求超时 15s | +| Relay 路由 | `api_version_middleware` | 版本校验 | +| Relay 路由 | `request_id_middleware` | 请求 ID | +| Relay 路由 | `quota_check_middleware` | 配额检查 | +| 全局 | CORS / 其他 layer | 跨域等 | + +### 不变量 + +- Priority 升序: 0-999, 数值越小越先执行 +- 注册顺序 != 执行顺序; chain 按 priority 运行时排序 +- Stop/Block/AbortLoop 立即中断, 不执行后续中间件 + ### 核心接口 ```rust -// crates/zclaw-runtime/src/middleware.rs trait AgentMiddleware: Send + Sync { fn name(&self) -> &str; fn priority(&self) -> i32 { 500 } @@ -99,58 +110,27 @@ trait AgentMiddleware: Send + Sync { } ``` -### 注册位置 +## 4. 活跃问题 + 陷阱 -`crates/zclaw-kernel/src/kernel/mod.rs:248-361` — `create_middleware_chain()` 方法,14 次 `chain.register()`(含 2 个条件注册: SkillIndex, Compaction)。注册顺序与执行顺序不同,chain 按 priority 升序排列后执行。 +### 活跃问题 -## 功能清单 +- **11/14 中间件无独立测试** (P2): 仅 `butler_router`(12) / `evolution`(4) / `trajectory_recorder`(4) 有测试,共 20 个。其余 11 层依赖集成测试覆盖。 +- **SkillIndex 条件注册** (长期观察): 无技能时不注册,非 bug 但需关注空技能场景下的行为一致性。 -| 优先级 | 中间件 | 功能 | 状态 | -|--------|--------|------|------| -| @78 | EvolutionMiddleware | 进化引擎注入 | ✅ | -| @80 | ButlerRouter | 管家语义路由 + XML fencing | ✅ | -| @100 | Compaction | 上下文压缩 (条件注册) | ✅ | -| @150 | Memory | 记忆自动提取 + 注入 | ✅ | -| @180 | Title | 对话标题生成 | ✅ | -| @200 | SkillIndex | 技能索引注入 (条件注册) | ✅ | -| @300 | DanglingTool | 悬空工具清理 | ✅ | -| @350 | ToolError | 工具错误处理 | ✅ | -| @360 | ToolOutputGuard | 工具输出守卫 | ✅ | -| @400 | Guardrail | 安全护栏 | ✅ | -| @500 | LoopGuard | 循环检测 (防无限) | ✅ | -| @550 | SubagentLimit | 子代理数量限制 | ✅ | -| @650 | TrajectoryRecorder | 轨迹记录+压缩 | ✅ | -| @700 | TokenCalibration | Token 校准 | ✅ | +### 历史陷阱 -## 测试链路 +| 问题 | 根因 | 修复 | +|------|------|------| +| TrajectoryRecorder 未注册 | V13-GAP-01: 遗漏 `chain.register()` 调用 | 已在 @650 注册 | +| Admin 端点 404 而非 403 | admin_guard_middleware 返回码错误 | 已修复为 403 | +| DataMasking 中间件 | 增加延迟但无实际安全收益 | 04-22 移除 | -| 功能 | 测试文件 | 测试数 | 覆盖状态 | -|------|---------|--------|---------| -| 管家路由 | middleware/butler_router.rs | 12 | ✅ | -| 进化中间件 | middleware/evolution.rs | 4 | ✅ | -| 轨迹记录 | middleware/trajectory_recorder.rs | 4 | ✅ | -| 其余 11 层 | — | 0 | ⚠️ 无独立测试 | -| **合计** | 3/14 文件有测试 | **20** | | +## 5. 变更日志 -## 关联模块 - -- [[butler]] — ButlerRouter 是管家模式的核心 -- [[chat]] — 每条消息经过完整中间件链 -- [[memory]] — Memory 中间件从对话提取记忆 -- [[hands-skills]] — SkillIndex 中间件注入技能索引 - -## 关键文件 - -| 文件 | 职责 | -|------|------| -| `crates/zclaw-runtime/src/middleware.rs` | AgentMiddleware trait + MiddlewareChain | -| `crates/zclaw-runtime/src/middleware/` | 14 个中间件实现 (14个 .rs 文件) | -| `crates/zclaw-kernel/src/kernel/mod.rs:248-361` | 注册入口 | -| `crates/zclaw-saas/src/main.rs` | SaaS HTTP 中间件注册 (10 层) | - -## 已知问题 - -- ✅ **TrajectoryRecorder 未注册** — V13-GAP-01 已修复 (在 @650 注册) -- ✅ **Admin 端点 404 而非 403** — admin_guard_middleware 已修复 -- ⚠️ **SkillIndex 条件注册** — 无技能时不注册,长期观察 -- ⚠️ **11/14 中间件无独立测试** — 仅 butler_router/evolution/trajectory_recorder 有测试 +| 日期 | 变更 | 影响 | +|------|------|------| +| 04-22 | DataMasking 中间件移除 | 14->14 层 (替换为无), 减少 1 层无收益处理 | +| 04-22 | 跨会话记忆修复 | Memory 中间件去重+跨会话注入修复 | +| 04-22 | Wiki 一致性校准 | 数字与代码验证对齐 | +| 04-21 | Embedding 接通 | SkillIndex 路由 TF-IDF->Embedding+LLM fallback | +| 04-15 | Heartbeat 统一健康系统 | TrajectoryRecorder 痛点感知增强 | diff --git a/wiki/saas.md b/wiki/saas.md index e3b4837..ad9108a 100644 --- a/wiki/saas.md +++ b/wiki/saas.md @@ -1,105 +1,31 @@ --- title: SaaS 平台 -updated: 2026-04-21 +updated: 2026-04-22 status: active -tags: [module, saas, auth, billing] +tags: [module, saas, billing, relay] --- # SaaS 平台 -> 从 [[index]] 导航。关联模块: [[routing]] [[chat]] +> 从 [[index]] 导航。关联模块: [[routing]] [[chat]] [[security]] -## 设计思想 +## 设计决策 **核心定位: SaaS 是 Tauri 桌面端的中枢,不是独立 Web 应用。** -关键决策: -1. **Token Pool** — 桌面端不持有 LLM API Key,SaaS 维护共享 Key 池,RPM/TPM 轮换 -2. **JWT + Cookie 双通道** — Tauri 用 OS keyring 存 JWT,浏览器用 HttpOnly cookie -3. **计费闭环** — 配额实时递增 → 聚合器调度 → mock 支付路由 -4. **Admin V2** — 17 页管理后台,管理模型/用户/计费/知识库 +| 决策 | 为什么 | +|------|--------| +| Token Pool 集中管理 | 桌面端不持有 LLM API Key,SaaS 维护共享 Key 池做 RPM/TPM 轮换,支持用量追踪和计费 | +| 16 模块目录划分 | 按业务域高内聚:auth/relay/billing/knowledge/model_config/account/agent_template/industry/role/prompt/scheduled_task/telemetry/migration/models/tasks/workers | +| 137 routes + 13 路由模块 | main.rs 用 `.merge()` 统一注册,每个模块独立维护路由 | +| 7 后台 Workers | 用量记录/聚合、限流清理、token 清理、embedding 生成等异步任务解耦 | +| 认证与安全 | 详见 [[security]] | -## 功能清单 +## 关键文件 + 数据流 -| 功能 | 描述 | 入口文件 | 状态 | -|------|------|----------|------| -| 用户认证 | 注册/登录/JWT刷新/登出 | auth/handlers.rs | ✅ | -| TOTP 2FA | 设置/验证/禁用 | auth/handlers.rs | ✅ | -| Token Pool | RPM/TPM 轮换 Key 分配 | relay/handlers.rs | ✅ | -| 聊天中转 | OpenAI 兼容 relay | relay/handlers.rs | ✅ | -| 计费系统 | 配额递增/订阅/支付回调 | billing/ | ✅ | -| 用户管理 | CRUD/状态/设备/token | account/ | ✅ | -| 模型管理 | Provider/模型/Key CRUD | model_config/ | ✅ | -| Agent 模板 | 模板 CRUD + 自动分配 | agent_template/ | ✅ | -| 知识库 | 分类/条目/搜索/pgvector | knowledge/ | ✅ | -| Prompt 管理 | 版本控制/回滚 | prompt/ | ✅ | -| 角色权限 | RBAC + 权限模板 | role/ | ✅ | -| 行业配置 | 行业 CRUD + 账户分配 | industry/ | ✅ | -| 定时任务 | 任务调度 CRUD | scheduled_task/ | ✅ | -| 用量统计 | Telemetry 上报/查询 | telemetry/ | ✅ | -| 配置同步 | 项 CRUD/分析/seed/sync/diff | migration/ | ✅ | -| Admin Dashboard | 系统概览 + 运营指标 | account/admin_routes | ✅ | -| Mock 支付 | 开发环境模拟支付 | billing/mock_routes | ✅ | +### SaaS 模块结构 -## 代码逻辑 - -### 认证流 - -``` -用户登录 (POST /api/v1/auth/login) - → Argon2id + OsRg 盐验证密码 - → 签发 JWT (Claims: user_id, role, pwv) - → set_auth_cookies(): - zclaw_access_token (path:/api, 2h TTL, HttpOnly) - zclaw_refresh_token (path:/api/v1/auth, 7d TTL, HttpOnly) - Secure: dev=false, prod=true | SameSite=Strict - -前端存储: - → Tauri: OS keyring → saasStore.token - → 浏览器: HttpOnly Cookie (JS 不可读) - → localStorage: saasUrl + account 信息 (非敏感) -``` - -### Token 池 + 限流 - -``` -SaaS Relay 收到 LLM 请求 (POST /api/v1/relay/chat/completions) - → 验证 JWT → 提取 user_id - → 从 Token Pool 选择可用 Key (RPM/TPM 轮换) - → 转发请求到真实 LLM API - → 记录 usage (record_usage worker) - → 返回响应 - -限流规则: - → /api/auth/login: 5次/分钟/IP (防暴力) + 持久化到 PostgreSQL - → /api/auth/register: 3次/小时/IP (防刷注册) - → 公共端点: 20次/分钟/IP -``` - -### 密码安全 - -``` -JWT password_version (pwv): - → JWT Claims 含 pwv 字段 - → 每次验证 JWT 时比对 Claims.pwv vs DB.pwv - → 修改密码 → DB.pwv 递增 → 所有旧 JWT 自动失效 - -密码存储: Argon2id + OsRg 随机盐 -TOTP 加密: AES-256-GCM + 随机 Nonce -``` - -### Token 刷新 - -``` -POST /api/v1/auth/refresh - → 验证 refresh_token (单次使用) - → 旧 refresh_token 撤销到 DB (rotation 校验) - → 签发新 access + refresh token -``` - -### SaaS 模块结构(代码验证) - -16 个模块目录 (`crates/zclaw-saas/src/`): +16 个目录 (`crates/zclaw-saas/src/`): ``` account/ agent_template/ auth/ billing/ industry/ @@ -107,124 +33,141 @@ knowledge/ migration/ model_config/ models/ prompt/ relay/ role/ scheduled_task/ tasks/ telemetry/ workers/ ``` -### SaaS API 分布 +### 核心文件 -137 个 `.route()` 调用,13 个路由模块 (main.rs `.merge()` 注册)。 +| 文件 | 职责 | +|------|------| +| `crates/zclaw-saas/src/main.rs` | 路由注册入口 (13 个 .merge()) | +| `crates/zclaw-saas/src/relay/handlers.rs` | 聊天中转 + Token Pool 分配 | +| `crates/zclaw-saas/src/billing/` | 配额递增/订阅/支付回调 | +| `crates/zclaw-saas/src/knowledge/` | 知识库 CRUD + pgvector (最大模块, 24 routes) | +| `crates/zclaw-saas/src/workers/` | 7 个后台 Worker | +| `crates/zclaw-saas/migrations/` | SQL 迁移 (38 文件, 42 CREATE TABLE) | +| `admin-v2/src/pages/` | 17 页管理后台 | +| `desktop/src/lib/saas-client.ts` | 前端 SaaS API 客户端 | +| `desktop/src/store/saasStore.ts` | SaaS 认证状态 | -| 模块 | 路由注册 | 说明 | -|------|----------|------| -| auth | handlers.rs | 登录/注册/刷新/2FA | -| relay | relay/ | 聊天中转/模型列表/任务 | -| billing | billing/ + callback_routes | 配额/订阅/支付 | -| knowledge | knowledge/ | 知识库 CRUD + pgvector (最大模块) | -| model_config | model_config/ | Provider + 模型管理 | -| account | account/ | 用户管理 | -| agent_template | agent_template/ | Agent 模板 | -| role | role/ | 角色 + 权限 | -| telemetry | telemetry/ | 用量统计 | -| prompt | prompt/ | Prompt 模板 | -| scheduled_task | scheduled_task/ | 定时任务 CRUD | -| industry | industry/ | 行业配置管理 (V13 新增) | -| migration | migration/ | Schema 迁移 | +### 数据流 + +``` +桌面端请求 (ChatPanel) + → Tauri invoke / HTTP SSE + → SaaS Relay (POST /api/v1/relay/chat/completions) + → JWT 验证 → Token Pool 选择 Key → LLM API + → SSE 流式返回 + → 桌面端 streamStore.onDelta 渲染 +``` + +### 集成契约 + +| 方向 | 接口 | 说明 | +|------|------|------| +| Called by <-- desktop | Tauri invoke / HTTP SSE | Chat relay, billing, auth | +| Calls --> relay handlers | POST /api/v1/relay/chat/completions | Token Pool RPM/TPM 轮换 | +| Provides --> admin | 137 routes | User/billing/knowledge/model 管理 | + +### API 分布 + +| 模块 | 路由数 | 核心端点 | +|------|--------|---------| +| knowledge | 24 | 分类/条目/搜索/上传/版本/结构化 | +| model_config | 19 | Provider/模型/Key/模型组/用量 | +| billing | 12 | 订阅/用量/支付/mock/回调/invoice | +| account | 12 | CRUD/状态/token/设备/操作日志/dashboard | +| agent_template | 11 | 模板 CRUD + 创建 Agent + 分配 | +| auth | 9 | POST /auth/{register,login,refresh,logout} + TOTP + password | +| industry | 8 | 行业 CRUD + 账户分配 | +| migration | 8 | 配置项 CRUD + 分析/seed/sync/diff | +| prompt | 6 | Prompt CRUD + 版本/回滚 | +| role | 6 | 角色/权限模板 CRUD | +| telemetry | 4 | 上报/统计/日报/审计 | +| relay | 5 | POST /relay/chat/completions + GET /relay/models | +| scheduled_task | 2 | 定时任务 CRUD | ### 数据表 (42 CREATE TABLE) -38 个 SQL 迁移文件 (21 up + 17 down),42 个 `CREATE TABLE` 语句。 - 核心表: users, agents, conversations, messages, billing_*, knowledge_*, model_configs, roles, permissions, scheduled_tasks, telemetry, agent_templates, saas_schema_version, user_profiles, trajectory_records, industries, account_industries +## 代码逻辑 + +### Token Pool RPM/TPM 轮换算法 + +``` +SaaS Relay 收到请求 + → 验证 JWT → 提取 user_id + → Token Pool 选择 Key: + 1. priority ASC (高优先级优先) + 2. last_used_at ASC (最久未用优先) + 3. cooldown 检查 (跳过冷却中的 Key) + 4. RPM/TPM 滑动窗口检查 (当前窗口是否超限) + → 转发请求到 LLM API + → record_usage worker 异步记录 +``` + ### Workers (7 个) | Worker | 文件 | 职责 | |--------|------|------| +| record_usage | workers/ | 用量记录 (relay 后异步) | +| aggregate_usage | workers/ | 用量聚合 (日报/月报) | +| generate_embedding | workers/ | 内容分块 (embedding deferred, pgvector 就绪) | | log_operation | workers/ | 操作日志 | | cleanup_rate_limit | workers/ | 限流记录清理 | | cleanup_refresh_tokens | workers/ | 刷新 token 清理 | -| record_usage | workers/ | 用量记录 | -| update_last_used | workers/ | 模型最后使用更新 | -| aggregate_usage | workers/ | 用量聚合 | -| generate_embedding | workers/ | 内容分块 (embedding deferred) | +| update_last_used | workers/ | 模型最后使用时间更新 | -## API 接口 +### 计费流程 -~118 个唯一路由,分布在 13 个模块中(详见上方「SaaS API 分布」和各模块 `mod.rs`)。 +``` +用户请求 relay → quota_check_middleware 检查月度配额 + → 通过: relay 正常执行 + → record_usage worker 递增 relay_requests + input_tokens + → aggregate_usage worker 定期聚合 + → 超额: 返回 429 QuotaExceeded +``` -| 模块 | 路由数 | 核心端点 | -|------|--------|---------| -| auth | 9 | POST /auth/{register,login,refresh,logout} + TOTP + password | -| relay | 5 | POST /relay/chat/completions + GET /relay/models | -| billing | 12 | 订阅/用量/支付/mock/回调/invoice | -| knowledge | 24 | 分类/条目/搜索/上传/版本/分析/结构化 | -| model_config | 19 | Provider/模型/Key/模型组/用量 | -| account | 12 | CRUD/状态/token/设备/操作日志/dashboard | -| agent_template | 11 | 模板 CRUD + 创建 Agent + 分配 | -| industry | 8 | 行业 CRUD + 账户分配 | -| prompt | 6 | Prompt CRUD + 版本/回滚 | -| role | 6 | 角色/权限模板 CRUD | -| telemetry | 4 | 上报/统计/日报/审计 | -| scheduled_task | 2 | 定时任务 CRUD | -| migration | 8 | 配置项 CRUD + 分析/seed/sync/diff | +## 活跃问题 + 陷阱 -## 测试链路 +| 问题 | 级别 | 说明 | +|------|------|------| +| Admin 用量统计显示 0/0 | P2 Open | Dashboard 17 requests / 6304 tokens,但 Usage 页 0/0,数据源不一致 | +| 桌面端 Token 统计为 0 | P2 Open | 前端 token 统计未接通后端数据源 | +| Deepseek 中转任务卡 processing | P3 Open | 特定模型 relay 任务状态不更新 | +| Embedding 生成未实现 | 长期 | pgvector 索引就绪,generate_embedding worker 逻辑空 | +| AuthGuard 竞态条件 | P1 Deferred | 并发请求时可能绕过认证 | -| 功能 | 测试文件 | 说明 | -|------|---------|------| -| 认证 | `crates/zclaw-saas/tests/auth_test.rs` | 注册/登录/刷新/登出 | -| 认证安全 | `crates/zclaw-saas/tests/auth_security_test.rs` | 安全边界场景 | -| 账户 | `crates/zclaw-saas/tests/account_test.rs` | CRUD/token/设备 | -| 账户安全 | `crates/zclaw-saas/tests/account_security_test.rs` | 安全边界 | -| Agent 模板 | `crates/zclaw-saas/tests/agent_template_test.rs` | 模板 CRUD | -| 计费 | `crates/zclaw-saas/tests/billing_test.rs` | 计划/订阅/支付 | -| 知识库 | `crates/zclaw-saas/tests/knowledge_test.rs` | CRUD/搜索 | -| 模型配置 | `crates/zclaw-saas/tests/model_config_test.rs` | Provider/模型/Key | -| 模型配置扩展 | `crates/zclaw-saas/tests/model_config_extended_test.rs` | 扩展覆盖 | -| Prompt | `crates/zclaw-saas/tests/prompt_test.rs` | 版本管理 | -| 权限矩阵 | `crates/zclaw-saas/tests/permission_matrix_test.rs` | 角色/权限 | -| Relay | `crates/zclaw-saas/tests/relay_test.rs` | 聊天中转 | -| Relay 验证 | `crates/zclaw-saas/tests/relay_validation_test.rs` | 请求验证 | -| 角色 | `crates/zclaw-saas/tests/role_test.rs` | 角色 CRUD | -| 定时任务 | `crates/zclaw-saas/tests/scheduled_task_test.rs` | 任务 CRUD | -| Telemetry | `crates/zclaw-saas/tests/telemetry_test.rs` | 上报/查询 | -| 配置同步 | `crates/zclaw-saas/tests/migration_test.rs` | sync/diff/seed | -| Admin 启动 | `crates/zclaw-saas/tests/smoke_saas.rs` | 启动冒烟 | -| Admin V2 UI | `admin-v2/tests/pages/*.test.tsx` (17文件) | 每页独立测试 | -| 桌面端 SaaS | `tests/desktop/connectionStore.adminRouting.test.ts` | Admin 路由 | -| 桌面端集成 | `tests/desktop/integration/zclaw-api.test.ts` | API 集成 | +陷阱: +- SaaS 数据库需要 PostgreSQL (`docker-compose.yml`),不是 SQLite +- Token Pool 的 RPM/TPM 是滑动窗口不是固定窗口,测试时注意时间边界 +- `saas-config.toml` 支持 `${ENV_VAR}` 环境变量插值 +- knowledge 是最大模块 (24 routes),修改时影响面广 -## 关联模块 +## 变更日志 -- [[routing]] — SaaS Relay 是 Tauri 的主路径 -- [[chat]] — 聊天请求经过 SaaS relay 中转 -- [[memory]] — knowledge_chunks 表有 pgvector 索引 +| 日期 | 变更 | 提交 | +|------|------|------| +| 2026-04-21 | Embedding 接通 + 自学习 A/B 线 | — | +| 2026-04-17 | E2E 测试 129 链路,7 Bug 修复 | — | +| 2026-04-15 | Heartbeat 统一健康系统 | — | +| 2026-04-12 | 行业配置 + 管家主动性全栈 5 Phase | — | +| 2026-04-09 | Hermes Intelligence Pipeline 4 Chunk | 684 tests PASS | -## 关键文件 +### 测试覆盖 -| 文件 | 职责 | -|------|------| -| `crates/zclaw-saas/src/main.rs` | 路由注册入口 (13个 .merge()) | -| `crates/zclaw-saas/src/auth/handlers.rs` | 认证端点 | -| `crates/zclaw-saas/src/relay/` | 聊天中转 | -| `crates/zclaw-saas/src/billing/` | 计费 | -| `crates/zclaw-saas/src/knowledge/` | 知识库 | -| `crates/zclaw-saas/src/workers/` | 7 个后台 Worker | -| `crates/zclaw-saas/migrations/` | SQL 迁移 (38 文件) | -| `admin-v2/src/pages/` | 17 页管理后台(含 Dashboard/Accounts/Billing/Industries/Knowledge/Prompts/Roles/ScheduledTasks/Config 等) | -| `desktop/src/lib/saas-client.ts` | 前端 SaaS API 客户端 | -| `desktop/src/store/saasStore.ts` | SaaS 认证状态 | - -## 安全 - -完整审计: `docs/features/SECURITY_PENETRATION_TEST_V1.md` -- CORS 白名单 (生产缺失拒绝启动) -- Cookie Secure (dev=false, prod=true) -- JWT 签名密钥 >= 32 字符 (release fallback 拒绝启动) -- 独立 TOTP 加密密钥 - -## 已知问题 - -- ⚠️ **Admin 用量统计显示 0/0** — P2 Open。Dashboard 显示 17 requests / 6304 tokens,但 Usage 页显示 0/0,数据源不一致 -- ⚠️ **SaaS Embedding 生成未实现** — Open。pgvector 索引就绪,`generate_embedding.rs` Worker 存在但生成逻辑未实现 -- ⚠️ **AuthGuard 竞态条件** — P1-04 Deferred。并发请求时可能绕过认证 -- ✅ **Dashboard 404** — BUG-H1 已修复。`/api/v1/admin/dashboard` 路由注册遗漏 -- ✅ **invoice_id 未暴露** — BUG-M1 已修复 -- ✅ **非 Admin 返回 404 而非 403** — BUG-M4 已修复 (admin_guard_middleware) +| 功能 | 测试文件 | +|------|---------| +| 认证流程 | `crates/zclaw-saas/tests/auth_test.rs` | +| 认证安全 | `crates/zclaw-saas/tests/auth_security_test.rs` | +| 账户 CRUD | `crates/zclaw-saas/tests/account_test.rs` | +| 账户安全 | `crates/zclaw-saas/tests/account_security_test.rs` | +| 计费 | `crates/zclaw-saas/tests/billing_test.rs` | +| 知识库 | `crates/zclaw-saas/tests/knowledge_test.rs` | +| 模型配置 | `crates/zclaw-saas/tests/model_config_test.rs` | +| Prompt | `crates/zclaw-saas/tests/prompt_test.rs` | +| 权限矩阵 | `crates/zclaw-saas/tests/permission_matrix_test.rs` | +| Relay | `crates/zclaw-saas/tests/relay_test.rs` | +| Relay 验证 | `crates/zclaw-saas/tests/relay_validation_test.rs` | +| 角色 | `crates/zclaw-saas/tests/role_test.rs` | +| 定时任务 | `crates/zclaw-saas/tests/scheduled_task_test.rs` | +| Telemetry | `crates/zclaw-saas/tests/telemetry_test.rs` | +| 配置同步 | `crates/zclaw-saas/tests/migration_test.rs` | diff --git a/wiki/security.md b/wiki/security.md index 7f8f9a1..a0526ed 100644 --- a/wiki/security.md +++ b/wiki/security.md @@ -1,6 +1,6 @@ --- title: 安全体系 -updated: 2026-04-21 +updated: 2026-04-22 status: active tags: [module, security, auth, encryption] --- @@ -9,37 +9,34 @@ tags: [module, security, auth, encryption] > 从 [[index]] 导航。关联模块: [[saas]] [[routing]] [[middleware]] -## 设计思想 +## 设计决策 **核心原则: 多层防御,深度安全。** -1. **认证层** — JWT + Cookie + TOTP 2FA + 账户锁定 -2. **传输层** — CORS 白名单 + Cookie Secure + HTTPS (反向代理) -3. **存储层** — Argon2id 密码 + AES-256-GCM 加密 + OS Keyring -4. **运行时层** — 限流 + 配额 + CSP + Guardrail 中间件 +| 决策 | 为什么 | +|------|--------| +| JWT + HttpOnly Cookie 双通道 | Tauri 桌面端用 OS keyring 存 JWT,浏览器用 HttpOnly Cookie 防 XSS 窃取,双环境统一认证 | +| password_version (pwv) 失效 | 修改密码后自动使所有已签发 JWT 失效,无需 token 黑名单,O(1) 验证 | +| TOTP AES-256-GCM 加密 | TOTP 共享密钥不能明文存储,随机 Nonce 防重放,生产环境强制独立密钥 | +| IP 级限流 + 持久化 | 防暴力破解(login 5/min)、防刷注册(3/hour),持久化到 PostgreSQL 避免重启丢失 | +| CORS 白名单强制 | 生产环境 `cors_origins` 缺失直接拒绝启动,不允许 `*` 通配 | 完整审计报告: `docs/features/SECURITY_PENETRATION_TEST_V1.md` -## 功能清单 +## 关键文件 + 数据流 -| 功能 | 描述 | 入口文件 | 状态 | -|------|------|----------|------| -| JWT 认证 | 签发/验证/刷新/失效 | auth/handlers.rs | ✅ | -| Cookie 双通道 | Tauri keyring + 浏览器 HttpOnly | auth/handlers.rs | ✅ | -| TOTP 2FA | 设置/验证/禁用 | auth/totp.rs | ✅ | -| 密码安全 | Argon2id + OsRng 盐 + pwv 失效 | auth/handlers.rs | ✅ | -| Token 池加密 | AES-256-GCM + 随机 Nonce | relay/key_pool.rs | ✅ | -| OS Keyring | Win DPAPI/macOS Keychain/Linux Secret | secure_storage.rs | ✅ | -| 本地记忆加密 | AES-256-GCM (可选) | memory/crypto.rs | ✅ | -| 账户锁定 | 5 次失败锁 15 分钟 | auth/handlers.rs | ✅ | -| 限流 | IP 级 + 账户级滑动窗口 | middleware.rs | ✅ | -| CORS 白名单 | 生产缺失拒绝启动 | main.rs CorsLayer | ✅ | -| CSP | Tauri 移除 unsafe-inline | desktop/src-tauri/ | ✅ | -| Admin 权限 | admin_guard + RBAC | middleware.rs | ✅ | +### 核心文件 -## 代码逻辑 +| 文件 | 职责 | +|------|------| +| `crates/zclaw-saas/src/auth/handlers.rs` | 认证端点: 登录/注册/刷新/TOTP/密码修改 | +| `crates/zclaw-saas/src/auth/totp.rs` | TOTP 2FA: QR 生成 + 验证 + AES-256-GCM 加密 | +| `crates/zclaw-saas/src/middleware.rs` | HTTP 中间件栈 (10 层): 认证/限流/配额/CORS | +| `crates/zclaw-saas/src/relay/key_pool.rs` | Token Pool: Key 加密存储 + RPM/TPM 轮换 | +| `desktop/src-tauri/src/secure_storage.rs` | OS Keyring: Win DPAPI / macOS Keychain / Linux Secret | +| `desktop/src-tauri/src/memory/crypto.rs` | 本地记忆加密: AES-256-GCM (可选) | -### 认证流程 +### 认证数据流 ``` 用户登录 (POST /api/v1/auth/login) @@ -57,50 +54,26 @@ tags: [module, security, auth, encryption] → localStorage: 仅 saasUrl + account 非敏感信息 ``` -### JWT Password Version 失效机制 +### 集成契约 -``` -JWT Claims 含 pwv (password_version) 字段 - → 每次验证 JWT 时比对 Claims.pwv vs DB.pwv - → 修改密码 → DB.pwv 递增 → 所有旧 JWT 自动失效 -``` +| 方向 | 接口 | 说明 | +|------|------|------| +| Provides --> saas | auth_middleware, JWT validation, rate limiting | 每个 API 请求经过认证层 | +| Provides --> desktop | secure_storage, crypto_utils | 配置/凭据安全存储 | +| Provides --> admin | admin_guard_middleware | Admin 路由权限验证 | -### Token 池安全 +### Auth API 接口 -``` -Provider Key 存储: AES-256-GCM + 随机 Nonce 加密 - → 解密失败: warn + skip 到下一个 Key (不阻塞 relay) - → 启动时: heal_provider_keys() 自动重新加密有效 Key - → TOTP 加密密钥: 生产环境强制独立 ZCLAW_TOTP_ENCRYPTION_KEY -``` - -### SaaS HTTP 中间件栈 (10 层) - -| # | 中间件 | 路由组 | 功能 | -|---|--------|--------|------| -| 1 | public_rate_limit | Public | IP 限流: login 5/min, register 3/hour | -| 2 | auth_middleware | Protected+Relay | JWT/Cookie/API Token 身份验证 | -| 3 | rate_limit_middleware | Protected+Relay | 账户级请求频率限制 | -| 4 | quota_check_middleware | Relay | 月度配额检查 (relay_requests + input_tokens) | -| 5 | request_id_middleware | All | UUID 请求追踪 | -| 6 | api_version_middleware | All | API 版本头 | -| 7 | TimeoutLayer (15s) | Protected | 非流式请求超时 | -| 8 | admin_guard | Admin 子路由 | admin 权限验证 | -| G1 | TraceLayer | All | HTTP 请求追踪 | -| G2 | CorsLayer | All | CORS 白名单 (生产缺失拒绝启动) | - -## API 接口 - -### Auth 公开路由 +**公开路由:** | 方法 | 路径 | 说明 | |------|------|------| -| POST | `/api/v1/auth/register` | 注册 (邮箱 RFC 5322 + 254 字符限制) | +| POST | `/api/v1/auth/register` | 注册 (邮箱 RFC 5322 + 254 字符) | | POST | `/api/v1/auth/login` | 登录 (5 次/分钟 IP 限流) | -| POST | `/api/v1/auth/refresh` | 刷新 Token (单次使用,旧 token 撤销到 DB) | +| POST | `/api/v1/auth/refresh` | Token 刷新 (单次使用, 旧 token 撤销) | | POST | `/api/v1/auth/logout` | 登出 | -### Auth 受保护路由 +**受保护路由:** | 方法 | 路径 | 说明 | |------|------|------| @@ -110,7 +83,7 @@ Provider Key 存储: AES-256-GCM + 随机 Nonce 加密 | POST | `/api/v1/auth/totp/verify` | TOTP 验证激活 | | POST | `/api/v1/auth/totp/disable` | TOTP 禁用 (需密码) | -### Tauri 安全命令 +**Tauri 安全命令:** | 命令 | 说明 | |------|------| @@ -119,39 +92,108 @@ Provider Key 存储: AES-256-GCM + 随机 Nonce 加密 | `secure_store_delete` | OS Keyring 删除 | | `secure_store_is_available` | Keyring 可用性检测 | -## 测试链路 +## 代码逻辑 -| 功能 | 测试文件 | 覆盖状态 | -|------|---------|---------| -| 认证流程 | `crates/zclaw-saas/tests/auth_test.rs` | ✅ | -| 认证安全边界 | `crates/zclaw-saas/tests/auth_security_test.rs` | ✅ | -| 账户安全 | `crates/zclaw-saas/tests/account_security_test.rs` | ✅ | -| 权限矩阵 | `crates/zclaw-saas/tests/permission_matrix_test.rs` | ✅ | -| TOTP | `crates/zclaw-saas/src/auth/totp.rs` inline | ✅ | -| 本地加密 | `desktop/src-tauri/src/memory/crypto.rs` inline | ✅ | +### JWT Password Version 失效机制 -## 关联模块 +``` +JWT Claims 含 pwv (password_version) 字段 + → auth_middleware 每次验证 JWT 时: Claims.pwv vs DB.pwv + → 不匹配 → 401 Unauthorized + → 修改密码 → DB.pwv 递增 → 所有旧 JWT 自动失效 + → 无需 token 黑名单,验证成本 O(1) +``` -- [[saas]] — 安全体系运行在 SaaS 后端 -- [[routing]] — SaaS JWT 用于 relay 认证 -- [[middleware]] — Guardrail + LoopGuard + SubagentLimit 运行时安全 +### 密码存储: Argon2id + OsRng -## 关键文件 +``` +注册/修改密码: + → OsRng 生成随机盐 + → Argon2id 哈希 (内存硬 + 时间成本) + → 存储到 users.password_hash +验证: + → Argon2id::verify(password, stored_hash) + → 失败计数递增 → 5 次后锁定 15 分钟 +``` -| 文件 | 职责 | -|------|------| -| `crates/zclaw-saas/src/auth/handlers.rs` | 认证端点实现 | -| `crates/zclaw-saas/src/auth/totp.rs` | TOTP 2FA 实现 | -| `crates/zclaw-saas/src/middleware.rs` | HTTP 中间件栈 | -| `crates/zclaw-saas/src/relay/key_pool.rs` | Token Pool + Key 加密 | -| `desktop/src-tauri/src/secure_storage.rs` | OS Keyring 接口 | -| `desktop/src-tauri/src/memory/crypto.rs` | 本地 AES-256-GCM | -| `docs/features/SECURITY_PENETRATION_TEST_V1.md` | 安全审计报告 | +### TOTP / API Key 加密: AES-256-GCM -## 已知问题 +``` +TOTP 密钥存储: + → 随机生成 12 字节 Nonce + → AES-256-GCM 加密 (密钥: ZCLAW_TOTP_ENCRYPTION_KEY, 64 hex) + → 存储 nonce + ciphertext +解密: + → 取出 nonce → AES-256-GCM 解密 + → 解密失败: warn + 跳过 (不阻塞认证) -- ✅ **JWT 签名密钥 fallback** — `#[cfg(debug_assertions)]` 保护,release 拒绝启动 -- ✅ **TOTP 加密密钥解耦** — 生产环境强制独立 `ZCLAW_TOTP_ENCRYPTION_KEY` -- ✅ **Cookie Secure 标志** — dev=false, prod=true -- ✅ **CORS 白名单** — 生产缺失拒绝启动 -- ✅ **Admin 404→403** — admin_guard_middleware 已修复 +Provider API Key 同理: heal_provider_keys() 启动时重新加密有效 Key +``` + +### Token 刷新轮换 + +``` +POST /api/v1/auth/refresh + → 验证 refresh_token 有效性 + → 检查旧 token 是否已撤销 (rotation 防重放) + → 撤销旧 refresh_token (写入 DB revoked_at) + → 签发新 access_token (2h) + refresh_token (7d) +``` + +### 限流规则 + +| 端点 | 限制 | 持久化 | +|------|------|--------| +| `/api/auth/login` | 5 次/分钟/IP | PostgreSQL | +| `/api/auth/register` | 3 次/小时/IP | PostgreSQL | +| 公共端点 | 20 次/分钟/IP | 内存 | + +### SaaS HTTP 中间件栈 (10 层) + +| # | 中间件 | 路由组 | 功能 | +|---|--------|--------|------| +| 1 | public_rate_limit | Public | IP 限流 | +| 2 | auth_middleware | Protected+Relay | JWT/Cookie/API Token 身份验证 | +| 3 | rate_limit_middleware | Protected+Relay | 账户级频率限制 | +| 4 | quota_check_middleware | Relay | 月度配额检查 | +| 5 | request_id_middleware | All | UUID 请求追踪 | +| 6 | api_version_middleware | All | API 版本头 | +| 7 | TimeoutLayer (15s) | Protected | 非流式请求超时 | +| 8 | admin_guard | Admin 子路由 | admin 权限验证 | +| G1 | TraceLayer | All | HTTP 请求追踪 | +| G2 | CorsLayer | All | CORS 白名单 | + +## 活跃问题 + 陷阱 + +| 问题 | 级别 | 说明 | +|------|------|------| +| CSP 已加固 | Done | Tauri 移除 `unsafe-inline` script,`connect-src` 限制 `http://localhost:*` + `https://*` | +| TLS 依赖反向代理 | 长期 | Axum 不负责 TLS,nginx/caddy 提供 HTTPS 终止 | +| Cookie Secure 开发环境 false | 设计意图 | 开发环境 HTTP 无 Secure,生产必须 true | + +陷阱: +- JWT 签名密钥: `#[cfg(debug_assertions)]` 有 fallback,release 模式直接 `bail` 拒绝启动 +- TOTP 加密密钥: 生产必须独立设置 `ZCLAW_TOTP_ENCRYPTION_KEY`,不从 JWT 密钥派生 +- CORS 白名单: 生产缺失拒绝启动,不允许通配符 +- Refresh Token: 单次使用,logout 时撤销到 DB,rotation 校验已撤销的旧 token + +## 变更日志 + +| 日期 | 变更 | 提交 | +|------|------|------| +| 2026-04-21 | 移除数据脱敏中间件 (稳定化约束) | fa5ab4e | +| 2026-04-17 | E2E 测试安全链路验证通过 | — | +| 2026-04-16 | Agent 隔离修复 + Admin 权限校验 | — | +| 2026-04-13 | 安全渗透测试 V1: 15 项修复 | — | +| 2026-04-09 | CSP 加固 + JWT pwv + 账户锁定 + TOTP 解耦 | — | + +### 测试覆盖 + +| 功能 | 测试文件 | +|------|---------| +| 认证流程 | `crates/zclaw-saas/tests/auth_test.rs` | +| 认证安全边界 | `crates/zclaw-saas/tests/auth_security_test.rs` | +| 账户安全 | `crates/zclaw-saas/tests/account_security_test.rs` | +| 权限矩阵 | `crates/zclaw-saas/tests/permission_matrix_test.rs` | +| TOTP | `crates/zclaw-saas/src/auth/totp.rs` inline tests | +| 本地加密 | `desktop/src-tauri/src/memory/crypto.rs` inline tests |