作者:CSDN博客
分块策略
- // internal.tsexportfunctionchunkMarkdown(params:{
- content:string;
- tokens:number;// 默认 400
- overlap:number;// 默认 80}): MemoryChunk[]{const chunks: MemoryChunk[]=[];const lines = params.content.split('\n');let currentChunk ="";let currentTokens =0;for(const line of lines){const lineTokens =estimateTokens(line);if(currentTokens + lineTokens > params.tokens && currentChunk.length >0){
- chunks.push({ text: currentChunk.trim(),...});const overlapText =extractOverlap(currentChunk, params.overlap);
- currentChunk = overlapText +'\n'+ line;
- currentTokens =estimateTokens(overlapText)+ lineTokens;}else{
- currentChunk += line +'\n';
- currentTokens += lineTokens;}}return chunks;}
复制代码 分块示意
- ┌─────────────────────────────────────────┐
- │ MEMORY.md 内容 │
- ├─────────────────────────────────────────┤
- │ Line 1: # 概述 │
- │ Line 2: │
- │ Line 3: 这是内容... │
- │ ... │
- ├─────────────────────────────────────────┤
- │ Chunk 1 (Lines 1-10) │
- │ Chunk 2 (Lines 5-15) ← overlap │
- │ Chunk 3 (Lines 12-22) │
- └─────────────────────────────────────────┘
复制代码 Embedding 缓存
- classMemoryIndexManager{privateasyncgetEmbeddingWithCache(text:string):Promise<number[]>{const hash =hashText(text);// 1. 查找缓存const cached =this.db
- .prepare(`SELECT embedding FROM embedding_cache WHERE hash = ?`).get(hash);if(cached)returnparseEmbedding(cached.embedding);// 2. 调用 LLM 计算const embedding =awaitthis.provider.embed(text);// 3. 存入缓存this.db
- .prepare(`INSERT INTO embedding_cache VALUES (?, ?, ?, ?, ?, ?)`).run(this.provider,this.model,this.providerKey, hash,JSON.stringify(embedding), Date.now());return embedding;}}
复制代码 记忆存储
文件结构
- ~/.openclaw/workspace/
- ├── AGENTS.md # Agent 配置 (自动加载)
- ├── SOUL.md # Agent 身份定义
- ├── USER.md # 用户信息
- ├── TOOLS.md # 工具配置
- ├── MEMORY.md # 长期记忆
- ├── memory/ # 记忆文件目录
- │ ├── daily-notes/ # 每日笔记
- │ ├── projects/ # 项目相关
- │ └── ...
- ├── sessions/ # 会话历史
- └── [其他项目文件]
复制代码 配置示例
- {"memory":{"sources":["memory","sessions"],"extraPaths":["/path/to/extra"],"provider":"auto","model":"text-embedding-3-small","chunking":{"tokens":400,"overlap":80},"query":{"maxResults":6,"minScore":0.35,"hybrid":{"enabled":true,"vectorWeight":0.7,"textWeight":0.3}}}}
复制代码 配置选项
默认值速查表
| 配置项 | 默认值 | 说明 | | provider | "auto" | 自动选择提供商 | | chunking.tokens | 400 | 每块最大 token | | chunking.overlap | 80 | 重叠 token 数 | | query.maxResults | 6 | 返回结果数 | | query.minScore | 0.35 | 最小相似度 | | query.hybrid.enabled | true | 启用混合搜索 | | query.hybrid.vectorWeight | 0.7 | 向量权重 | | query.hybrid.textWeight | 0.3 | 关键词权重 | | sync.watchDebounceMs | 1500 | 防抖时间 | | cache.enabled | true | 启用缓存 | Embedding 提供商
| 提供商 | 模型 | 特点 | | openai | text-embedding-3-small | 性价比高 | | gemini | gemini-embedding-001 | Google 生态 | | voyage | voyage-4-large | 高质量 | | local | 本地模型 | 隐私保护 | | auto | 自动选择 | 默认推荐 |
使用指南
工具调用
memory_search - 语义搜索
- exportasyncfunctionmemory_search(
- query:string,
- maxResults?:number,
- minScore?:number):Promise<MemorySearchResult[]>
复制代码 使用示例:- 搜索: "Tom 的项目信息"
- 返回:
- [
- {
- "path": "memory/projects/openclaw.md",
- "startLine": 10,
- "endLine": 20,
- "score": 0.85,
- "snippet": "Tom 正在开发 OpenClaw...",
- "source": "memory"
- }
- ]
复制代码 memory_get - 读取记忆
- // 读取文件awaitmemory_get({ path:"memory/daily-notes/2024-01-15.md"});// 读取行范围awaitmemory_get({ path:"memory/projects.md", from:10, lines:20});
复制代码 最佳实践
1. 记忆文件组织
- memory/
- ├── AGENTS.md # Agent 核心配置
- ├── SOUL.md # Agent 身份定义
- ├── USER.md # 用户偏好
- ├── MEMORY.md # 长期重要记忆
- ├── daily-notes/ # 每日笔记
- │ ├── 2024-01-15.md
- │ └── 2024-01-16.md
- ├── projects/ # 项目相关
- │ ├── openclaw.md
- │ └── website.md
- └── preferences/ # 偏好设置
复制代码 2. 内容格式
- # SSH 配置
- ## 概述
- 记录 Tom 的 SSH 配置信息。
- ## 主机信息
- - 服务器: 192.168.1.100
- - 用户: admin
- - 端口: 22
- ## 密钥位置
- ~/.ssh/id_rsa_openclaw
复制代码 3. 性能优化
- // 减少返回结果awaitmemory_search("查询", maxResults=3);// 提高分数阈值awaitmemory_search("查询", minScore=0.5);// 禁用混合搜索{"memory":{"query":{"hybrid":{"enabled":false}}}}
复制代码 源码关键代码解读
1. 混合搜索入口
- // manager.tsasyncsearch(query:string, opts?:{...}):Promise<MemorySearchResult[]>{const queryVec =awaitthis.provider.embed(query);returnawaitthis.hybridSearch(queryVec, opts);}privateasynchybridSearch(queryVec:number[], opts?:{...}){const{ hybrid }=this.settings.query;if(hybrid.enabled && queryVec.length >0){const[vectorResults, keywordResults]=awaitPromise.all([this.searchVector(queryVec, opts),this.searchKeyword(query, opts),]);returnmergeHybridResults({
- vector: vectorResults,
- keyword: keywordResults,
- vectorWeight: hybrid.vectorWeight,
- textWeight: hybrid.textWeight,});}return queryVec.length >0?this.searchVector(queryVec, opts):this.searchKeyword(query, opts);}
复制代码 2. 评分计算
- // 综合评分公式
- score = vectorWeight × vectorScore + textWeight × textScore
- // 示例// 向量 0.9 + 关键词 0.6// 综合 = 0.7 × 0.9 + 0.3 × 0.6 = 0.81
复制代码 3. 增量同步
- // manager.tsprivateasyncsyncFiles():Promise<void>{const files =awaitlistMemoryFiles(this.memoryDir);for(const file of files){const currentHash =awaithashFile(file.path);const dbHash =this.getFileHash(file.path);if(currentHash !== dbHash){awaitthis.reindexFile(file);}}this.cleanupDeletedFiles();}
复制代码 常见问题
Q1: 搜索不到内容?
检查 memory/ 目录下是否有文件确认 memory_search 的 minScore 不要太高运行 memory_sync 手动触发同步
Q2: 性能差?
启用 Embedding 缓存 (cache.enabled: true)使用 sqlite-vec 扩展加速向量搜索减少 chunking.tokens 增加并行度
Q3: FTS 不可用?
SQLite 编译时缺少 FTS5 模块,降级到关键词匹配。
Q4: 如何清除索引?
删除 SQLite 数据库文件:- rm ~/.openclaw/workspace/.memory/index.sqlite
复制代码 Q5: 支持哪些 Embedding 模型?
| 提供商 | 模型 | | OpenAI | text-embedding-3-small, text-embedding-3-large | | Gemini | gemini-embedding-001 | | Voyage | voyage-2, voyage-4-large | | 本地 | 支持 Ollama 等兼容 OpenAI API 的服务 |
总结
OpenClaw 记忆系统核心要点:
混合搜索: 向量 (70%) + 关键词 (30%) 融合SQLite 存储: 轻量级本地数据库自动同步: 文件变化监听 + 防抖增量更新: 只处理变更文件嵌入缓存: 避免重复计算分块策略: 400 tokens + 80 overlap故障转移: QMD → 内置索引回退
掌握这些,就能高效使用 OpenClaw 的记忆系统!
原文地址:https://blog.csdn.net/caicongyang/article/details/158073798 |