Files
zclaw_openfang/docs/superpowers/specs/2026-03-26-agent-growth-design.md
iven b7f3d94950
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
fix(presentation): 修复 presentation 模块类型错误和语法问题
- 创建 types.ts 定义完整的类型系统
- 重写 DocumentRenderer.tsx 修复语法错误
- 重写 QuizRenderer.tsx 修复语法错误
- 重写 PresentationContainer.tsx 添加类型守卫
- 重写 TypeSwitcher.tsx 修复类型引用
- 更新 index.ts 移除不存在的 ChartRenderer 导出

审计结果:
- 类型检查: 通过
- 单元测试: 222 passed
- 构建: 成功
2026-03-26 17:19:28 +08:00

758 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ZCLAW Agent 成长功能设计规格
> **版本**: 1.0
> **日期**: 2026-03-26
> **状态**: 已批准
> **作者**: Claude + 用户协作设计
---
## 一、概述
### 1.1 背景
ZCLAW 当前的学习系统存在**前后端分离问题**
- 前端有完整的学习逻辑 (`active-learning.ts`, `memory-extractor.ts`)
- 但这些学习结果存储在 localStorage/IndexedDB
- 后端执行系统 (Rust) 无法获取这些学习结果
- 导致 Agent 无法真正"成长"
### 1.2 目标
设计并实现完整的 Agent 成长功能,让 Agent 像个人管家一样:
- **记住偏好**:用户的沟通风格、回复格式、语言偏好等
- **积累知识**:从对话中学习用户相关事实、领域知识、经验教训
- **掌握技能**:记录技能/Hand 的使用模式,优化执行效率
### 1.3 需求决策
| 维度 | 决策 | 理由 |
|------|------|------|
| 成长维度 | 偏好 + 知识 + 技能(全部) | 完整的管家式成长体验 |
| 整合策略 | 完全后端化Rust 重写 | 避免前后端数据隔离问题 |
| 存储架构 | OpenViking 作为完整记忆层 | 利用现有的 L0/L1/L2 分层 + 语义搜索 |
| 学习触发 | 对话后自动 + 用户显式触发 | 平衡自动化和可控性 |
| 行为影响 | 智能检索 + Token 预算控制 | 解决长期使用后数据量过大的问题 |
---
## 二、系统架构
### 2.1 整体架构图
```
┌─────────────────────────────────────────────────────────────────┐
│ ZCLAW Agent 成长系统 │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ zclaw-growth (新 Crate) │ │
│ │ ────────────────────────────────────────────────────── │ │
│ │ • MemoryExtractor - 从对话中提取偏好/知识/经验 │ │
│ │ • MemoryRetriever - 语义检索相关记忆 │ │
│ │ • PromptInjector - 动态构建 system_prompt │ │
│ │ • GrowthTracker - 追踪成长指标和演化 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ OpenViking (记忆层) │ │
│ │ ────────────────────────────────────────────────────── │ │
│ │ URI 结构: │ │
│ │ • agent://{id}/preferences/{category} - 用户偏好 │ │
│ │ • agent://{id}/knowledge/{domain} - 知识积累 │ │
│ │ • agent://{id}/experience/{skill} - 技能经验 │ │
│ │ • agent://{id}/sessions/{sid} - 对话历史 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ zclaw-runtime (修改) │ │
│ │ ────────────────────────────────────────────────────── │ │
│ │ AgentLoop 集成: │ │
│ │ 1. 对话前 → MemoryRetriever 检索相关记忆 │ │
│ │ 2. 构建请求 → PromptInjector 注入记忆 │ │
│ │ 3. 对话后 → MemoryExtractor 提取新记忆 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 2.2 数据流
```
用户输入
┌─────────────────────────────────────────┐
│ 1. 记忆检索 │
│ • 用当前输入查询 OpenViking │
│ • 召回 Top-5 相关记忆 │
│ • Token 预算控制 (500 tokens) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 2. Prompt 构建 │
│ system_prompt = base + │
│ "## 用户偏好\n" + preferences + │
│ "## 相关知识\n" + knowledge │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 3. LLM 对话 │
│ • 正常的 AgentLoop 执行 │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 4. 记忆提取 (对话后) │
│ • 分析对话内容 │
│ • 提取偏好/知识/经验 │
│ • 写入 OpenViking (L0/L1/L2) │
└─────────────────────────────────────────┘
```
### 2.3 OpenViking URI 结构
```
agent://{agent_id}/
├── preferences/
│ ├── communication-style # 沟通风格偏好
│ ├── response-format # 回复格式偏好
│ ├── language-preference # 语言偏好
│ └── topic-interests # 主题兴趣
├── knowledge/
│ ├── user-facts # 用户相关事实
│ ├── domain-knowledge # 领域知识
│ └── lessons-learned # 经验教训
├── experience/
│ ├── skill-{id} # 技能使用经验
│ └── hand-{id} # Hand 使用经验
└── sessions/
└── {session_id}/ # 对话历史
├── raw # 原始对话 (L0)
├── summary # 摘要 (L1)
└── keywords # 关键词 (L2)
```
---
## 三、详细设计
### 3.1 新 Crate 结构
```
crates/zclaw-growth/
├── Cargo.toml
├── src/
│ ├── lib.rs # 入口和公共 API
│ ├── extractor.rs # 记忆提取器
│ ├── retriever.rs # 记忆检索器
│ ├── injector.rs # Prompt 注入器
│ ├── tracker.rs # 成长追踪器
│ ├── types.rs # 类型定义
│ └── viking_adapter.rs # OpenViking 适配器
```
### 3.2 核心类型定义
```rust
// types.rs
/// 记忆类型
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MemoryType {
Preference, // 偏好
Knowledge, // 知识
Experience, // 经验
Session, // 对话
}
/// 记忆条目
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryEntry {
pub uri: String,
pub memory_type: MemoryType,
pub content: String,
pub keywords: Vec<String>,
pub importance: u8, // 1-10
pub access_count: u32,
pub created_at: DateTime<Utc>,
pub last_accessed: DateTime<Utc>,
}
/// 提取的记忆
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtractedMemory {
pub memory_type: MemoryType,
pub category: String,
pub content: String,
pub confidence: f32, // 提取置信度 0.0-1.0
pub source_session: SessionId,
}
/// 检索配置
#[derive(Debug, Clone)]
pub struct RetrievalConfig {
pub max_tokens: usize, // 总 Token 预算,默认 500
pub preference_budget: usize, // 偏好 Token 预算,默认 200
pub knowledge_budget: usize, // 知识 Token 预算,默认 200
pub experience_budget: usize, // 经验 Token 预算,默认 100
pub min_similarity: f32, // 最小相似度阈值,默认 0.7
pub max_results: usize, // 最大返回数量,默认 10
}
impl Default for RetrievalConfig {
fn default() -> Self {
Self {
max_tokens: 500,
preference_budget: 200,
knowledge_budget: 200,
experience_budget: 100,
min_similarity: 0.7,
max_results: 10,
}
}
}
/// 检索结果
#[derive(Debug, Clone, Default)]
pub struct RetrievalResult {
pub preferences: Vec<MemoryEntry>,
pub knowledge: Vec<MemoryEntry>,
pub experience: Vec<MemoryEntry>,
pub total_tokens: usize,
}
/// 提取配置
#[derive(Debug, Clone)]
pub struct ExtractionConfig {
pub extract_preferences: bool, // 是否提取偏好,默认 true
pub extract_knowledge: bool, // 是否提取知识,默认 true
pub extract_experience: bool, // 是否提取经验,默认 true
pub min_confidence: f32, // 最小置信度阈值,默认 0.6
}
impl Default for ExtractionConfig {
fn default() -> Self {
Self {
extract_preferences: true,
extract_knowledge: true,
extract_experience: true,
min_confidence: 0.6,
}
}
}
```
### 3.3 MemoryExtractor 接口
```rust
// extractor.rs
/// 记忆提取器 - 从对话中提取有价值的记忆
pub struct MemoryExtractor {
llm_driver: Arc<dyn LlmDriver>,
}
impl MemoryExtractor {
pub fn new(llm_driver: Arc<dyn LlmDriver>) -> Self {
Self { llm_driver }
}
/// 从对话中提取记忆
pub async fn extract(
&self,
messages: &[Message],
config: &ExtractionConfig,
) -> Result<Vec<ExtractedMemory>> {
let mut results = Vec::new();
if config.extract_preferences {
let prefs = self.extract_preferences(messages).await?;
results.extend(prefs);
}
if config.extract_knowledge {
let knowledge = self.extract_knowledge(messages).await?;
results.extend(knowledge);
}
if config.extract_experience {
let experience = self.extract_experience(messages).await?;
results.extend(experience);
}
// 过滤低置信度结果
results.retain(|m| m.confidence >= config.min_confidence);
Ok(results)
}
/// 提取偏好
async fn extract_preferences(
&self,
messages: &[Message],
) -> Result<Vec<ExtractedMemory>> {
// 使用 LLM 分析对话,提取用户偏好
// 例如:用户喜欢简洁的回复、用户偏好中文等
// ...
}
/// 提取知识
async fn extract_knowledge(
&self,
messages: &[Message],
) -> Result<Vec<ExtractedMemory>> {
// 使用 LLM 分析对话,提取有价值的事实和知识
// 例如:用户是程序员、用户在做一个 Rust 项目等
// ...
}
/// 提取经验
async fn extract_experience(
&self,
messages: &[Message],
) -> Result<Vec<ExtractedMemory>> {
// 分析对话中的技能/工具使用,提取经验教训
// 例如:某个技能执行失败、某个工具效果很好等
// ...
}
}
```
### 3.4 MemoryRetriever 接口
```rust
// retriever.rs
/// 记忆检索器 - 从 OpenViking 检索相关记忆
pub struct MemoryRetriever {
viking: Arc<VikingAdapter>,
}
impl MemoryRetriever {
pub fn new(viking: Arc<VikingAdapter>) -> Self {
Self { viking }
}
/// 检索与当前输入相关的记忆
pub async fn retrieve(
&self,
agent_id: &AgentId,
query: &str,
config: &RetrievalConfig,
) -> Result<RetrievalResult> {
// 1. 检索偏好
let preferences = self.retrieve_by_type(
agent_id,
MemoryType::Preference,
query,
config.max_results,
).await?;
// 2. 检索知识
let knowledge = self.retrieve_by_type(
agent_id,
MemoryType::Knowledge,
query,
config.max_results,
).await?;
// 3. 检索经验
let experience = self.retrieve_by_type(
agent_id,
MemoryType::Experience,
query,
config.max_results / 2,
).await?;
// 4. 计算 Token 使用
let total_tokens = self.estimate_tokens(&preferences, &knowledge, &experience);
Ok(RetrievalResult {
preferences,
knowledge,
experience,
total_tokens,
})
}
/// 按类型检索
async fn retrieve_by_type(
&self,
agent_id: &AgentId,
memory_type: MemoryType,
query: &str,
limit: usize,
) -> Result<Vec<MemoryEntry>> {
let scope = format!("agent://{}/{}", agent_id, memory_type_to_scope(&memory_type));
let results = self.viking.find(query, FindOptions {
scope: Some(scope),
limit: Some(limit),
level: Some("L1"), // 使用摘要级别
}).await?;
// 转换为 MemoryEntry
// ...
}
fn estimate_tokens(
&self,
preferences: &[MemoryEntry],
knowledge: &[MemoryEntry],
experience: &[MemoryEntry],
) -> usize {
// 简单估算:约 4 字符 = 1 token
let total_chars: usize = preferences.iter()
.chain(knowledge.iter())
.chain(experience.iter())
.map(|m| m.content.len())
.sum();
total_chars / 4
}
}
fn memory_type_to_scope(ty: &MemoryType) -> &'static str {
match ty {
MemoryType::Preference => "preferences",
MemoryType::Knowledge => "knowledge",
MemoryType::Experience => "experience",
MemoryType::Session => "sessions",
}
}
```
### 3.5 PromptInjector 接口
```rust
// injector.rs
/// Prompt 注入器 - 将记忆动态注入 system_prompt
pub struct PromptInjector {
config: RetrievalConfig,
}
impl PromptInjector {
pub fn new(config: RetrievalConfig) -> Self {
Self { config }
}
/// 构建增强的 system_prompt
pub fn inject(
&self,
base_prompt: &str,
memories: &RetrievalResult,
) -> String {
let mut result = base_prompt.to_string();
// 注入偏好
if !memories.preferences.is_empty() {
let prefs_section = self.format_preferences(
&memories.preferences,
self.config.preference_budget,
);
result.push_str("\n\n## 用户偏好\n");
result.push_str(&prefs_section);
}
// 注入知识
if !memories.knowledge.is_empty() {
let knowledge_section = self.format_knowledge(
&memories.knowledge,
self.config.knowledge_budget,
);
result.push_str("\n\n## 相关知识\n");
result.push_str(&knowledge_section);
}
// 注入经验
if !memories.experience.is_empty() {
let exp_section = self.format_experience(
&memories.experience,
self.config.experience_budget,
);
result.push_str("\n\n## 经验参考\n");
result.push_str(&exp_section);
}
result
}
fn format_preferences(&self, entries: &[MemoryEntry], budget: usize) -> String {
let mut result = String::new();
let mut used = 0;
for entry in entries.iter().take(5) { // 最多 5 条偏好
let line = format!("- {}\n", entry.content);
let line_tokens = line.len() / 4;
if used + line_tokens > budget {
break;
}
result.push_str(&line);
used += line_tokens;
}
result
}
fn format_knowledge(&self, entries: &[MemoryEntry], budget: usize) -> String {
// 类似 format_preferences
// ...
}
fn format_experience(&self, entries: &[MemoryEntry], budget: usize) -> String {
// 类似 format_preferences
// ...
}
}
```
### 3.6 AgentLoop 集成
修改 `crates/zclaw-runtime/src/loop_runner.rs`
```rust
pub struct AgentLoop {
agent_id: AgentId,
driver: Arc<dyn LlmDriver>,
tools: ToolRegistry,
memory: Arc<MemoryStore>,
model: String,
system_prompt: Option<String>,
max_tokens: u32,
temperature: f32,
skill_executor: Option<Arc<dyn SkillExecutor>>,
// 新增:成长系统
memory_retriever: Option<Arc<MemoryRetriever>>,
memory_extractor: Option<Arc<MemoryExtractor>>,
prompt_injector: Option<PromptInjector>,
growth_enabled: bool,
}
impl AgentLoop {
pub async fn run(&self, session_id: SessionId, input: String) -> Result<AgentLoopResult> {
// 1. 检索相关记忆 (新增)
let memories = if self.growth_enabled {
if let Some(retriever) = &self.memory_retriever {
retriever.retrieve(
&self.agent_id,
&input,
&RetrievalConfig::default(),
).await.unwrap_or_default()
} else {
RetrievalResult::default()
}
} else {
RetrievalResult::default()
};
// 2. 构建增强的 system_prompt (修改)
let enhanced_prompt = if self.growth_enabled {
if let Some(injector) = &self.prompt_injector {
injector.inject(
self.system_prompt.as_deref().unwrap_or(""),
&memories,
)
} else {
self.system_prompt.clone().unwrap_or_default()
}
} else {
self.system_prompt.clone().unwrap_or_default()
};
// 3. 添加用户消息
let user_message = Message::user(input);
self.memory.append_message(&session_id, &user_message).await?;
// 4. 获取完整上下文
let mut messages = self.memory.get_messages(&session_id).await?;
// 5. 执行 LLM 循环 (使用增强的 prompt)
let mut iterations = 0;
let max_iterations = 10;
loop {
// ... 现有的 LLM 循环逻辑
// 使用 enhanced_prompt 作为 system message
}
// 6. 对话结束后提取记忆 (新增)
if self.growth_enabled {
if let Some(extractor) = &self.memory_extractor {
let final_messages = self.memory.get_messages(&session_id).await?;
let extracted = extractor.extract(
&final_messages,
&ExtractionConfig::default(),
).await?;
// 写入 OpenViking
for memory in extracted {
// 通过 VikingAdapter 写入
}
}
}
Ok(result)
}
}
```
---
## 四、前端变化
### 4.1 新增组件
```typescript
// desktop/src/components/GrowthPanel.tsx
interface GrowthPanelProps {
agentId: string;
}
export function GrowthPanel({ agentId }: GrowthPanelProps) {
// 功能:
// - 显示 Agent 成长指标
// - 手动触发学习
// - 查看/编辑记忆
// - 配置学习参数
}
```
### 4.2 Store 扩展
```typescript
// desktop/src/store/agentStore.ts
interface AgentState {
// ... 现有字段
// 新增:成长相关
growthEnabled: boolean;
memoryStats: {
totalMemories: number;
preferences: number;
knowledge: number;
experience: number;
lastLearningTime: string | null;
};
}
```
### 4.3 Tauri Commands
```rust
// desktop/src-tauri/src/growth_commands.rs
#[tauri::command]
async fn get_memory_stats(agent_id: String) -> Result<MemoryStats, String>;
#[tauri::command]
async fn trigger_learning(agent_id: String, session_id: String) -> Result<Vec<ExtractedMemory>, String>;
#[tauri::command]
async fn get_memories(agent_id: String, memory_type: Option<String>) -> Result<Vec<MemoryEntry>, String>;
#[tauri::command]
async fn delete_memory(agent_id: String, uri: String) -> Result<(), String>;
#[tauri::command]
async fn update_memory(agent_id: String, uri: String, content: String) -> Result<(), String>;
```
---
## 五、执行计划
### 5.1 Phase 1: Crate 骨架 (1-2 天)
- [ ] 创建 `crates/zclaw-growth/` 目录结构
- [ ] 定义 `types.rs` 核心类型
- [ ] 设置 `Cargo.toml` 依赖
### 5.2 Phase 2: 检索系统 (2-3 天)
- [ ] 实现 `VikingAdapter` 封装
- [ ] 实现 `MemoryRetriever`
- [ ] 单元测试
### 5.3 Phase 3: 注入 + 集成 (2-3 天)
- [ ] 实现 `PromptInjector`
- [ ] 修改 `AgentLoop` 集成点
- [ ] 集成测试
### 5.4 Phase 4: 提取系统 (3-4 天)
- [ ] 实现 `MemoryExtractor`
- [ ] 设计 LLM prompt 模板
- [ ] 测试提取质量
### 5.5 Phase 5: 前端 UI (2-3 天)
- [ ] 实现 `GrowthPanel` 组件
- [ ] 扩展 Agent Store
- [ ] 添加 Tauri Commands
### 5.6 Phase 6: 测试 + 优化 (2-3 天)
- [ ] 端到端测试
- [ ] 性能优化
- [ ] 文档完善
**总计**: 约 12-18 天
---
## 六、关键文件路径
### 核心类型
- `crates/zclaw-types/src/agent.rs` - AgentConfig
- `crates/zclaw-types/src/message.rs` - Message
- `crates/zclaw-types/src/id.rs` - AgentId, SessionId
### 存储层
- `crates/zclaw-memory/src/store.rs` - MemoryStore
- `crates/zclaw-memory/src/schema.rs` - SQLite Schema
### 运行时
- `crates/zclaw-runtime/src/loop_runner.rs` - AgentLoop
### OpenViking 集成
- `desktop/src/lib/viking-client.ts` - 前端客户端
- `desktop/src-tauri/src/viking_commands.rs` - Tauri 命令
- `docs/features/03-context-database/00-openviking-integration.md` - 文档
### 前端学习系统(将被后端化)
- `desktop/src/lib/active-learning.ts`
- `desktop/src/lib/memory-extractor.ts`
- `desktop/src/store/activeLearningStore.ts`
---
## 七、风险与缓解
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| OpenViking 不可用 | 高 | 实现 LocalStorageAdapter 降级方案 |
| 记忆提取质量低 | 中 | 可配置的置信度阈值 + 人工审核 |
| Token 预算超限 | 中 | 严格的 Token 控制和截断 |
| 前端学习数据丢失 | 高 | 提供迁移脚本导入旧数据 |
---
## 八、新会话执行指南
在新会话中执行此方案时,请:
1. **阅读本文档**`docs/superpowers/specs/2026-03-26-agent-growth-design.md`
2. **参考计划文件**`plans/crispy-spinning-reef.md`(包含更多分析细节)
3. **从 Phase 1 开始**:创建 zclaw-growth crate 骨架
4. **遵循设计**:严格按照本文档的接口定义实现
5. **保持沟通**:如有疑问,与用户确认后再修改设计