引言AI Agent 的落地实践中一个反复出现的难题是Agent 记不住。每次会话都从零开始用户昨天交代的偏好、上周讨论的技术决策、上个月确认的项目约束全部归零。RAG检索增强生成为这个困境提供了方向但如何将 RAG 真正工程化地融入 Agent 系统让它既轻量又可靠、既本地又智能仍然是一个需要深入探讨的话题。OpenClaw 作为一款开源智能体开发框架将 RAG、MCP、Skills 等核心能力整合在一起。本文将从工程实践角度拆解 RAG 增强型 Agent 架构中的三个核心模块——向量检索、知识融合与记忆机制并给出可落地的代码实现。一、整体架构RAG 增强型 Agent 的三层设计一个完整的 RAG 增强型 Agent 系统可以抽象为三层感知层接收用户输入执行意图识别与查询改写检索层根据查询从知识库中检索相关文档向量检索 关键词检索生成层将检索结果与上下文融合驱动大模型生成最终响应OpenClaw 在此基础上进一步扩展形成了“推理引擎 记忆系统 知识库 技能库”的四层架构。其中RAG 作为“记忆中枢”承担着连接外部知识与模型推理的关键角色。在选型层面OpenClaw 给出了一个极具启发性的决策路径为什么不直接上专用向量数据库 专用向量数据库功能强大但通常假设了服务化架构——需要独立部署、独立运维。对于桌面级或个人化的 Agent 场景SQLite 是一个更务实的选择无需额外进程、单文件便携、配合 FTS5 和 sqlite-vec 扩展即可构成完整的 RAG 栈。当然对于企业级多租户场景Qdrant、Milvus 等专用向量数据库仍是更合适的选择。架构设计的本质是在零运维与规模化之间找到平衡点。二、向量检索从语义理解到高效召回2.1 向量检索的基本原理RAG 的工作流程分为离线和在线两个阶段离线阶段将文档切分成小块通过嵌入模型转换为向量存入向量数据库在线阶段将用户问题转换为向量检索最相似的 Top-K 文档将检索结果与问题一起输入 LLM向量检索的核心优势在于语义匹配——即使用户的用词与原文不同也能找到语义相近的内容。例如用户问“gateway host”系统能匹配到“the machine running OpenClaw”。2.2 混合检索向量 关键词的工程实践纯向量检索存在一个盲区对专有名词如项目 ID、人名、型号的召回效果不稳定。为此OpenClaw 构建了混合检索引擎并行运行向量检索与 BM25 关键词检索两条路径再合并结果。权重分配上常见的实践是语义 70% 关键词 30%也有方案采用 50/50 的均衡配置。具体权重需要根据知识库的特点进行调整——代码库中术语密集关键词权重应当提高而文档库中概念抽象语义权重则应当占优。2.3 代码示例基于 Qdrant 的向量检索实现以下代码展示如何为 OpenClaw Agent 接入 Qdrant 作为向量检索后端typescript// rag-core/retriever.tsimport { QdrantClient } from ‘qdrant/js-client-rest’;import { GoogleGenerativeAI } from ‘google/generative-ai’;interface RetrievalResult {content: string;score: number;source: string;}class QdrantRetriever {private client: QdrantClient;private embedder: GoogleGenerativeAI;private collectionName: string;constructor(config: { url: string; apiKey: string; collection: string }) {this.client new QdrantClient({ url: config.url, apiKey: config.apiKey });this.embedder new GoogleGenerativeAI(config.apiKey);this.collectionName config.collection;}// 生成查询向量private async getEmbedding(text: string): Promisenumber[] {const model this.embedder.getGenerativeModel({ model: ‘embedding-001’ });const result await model.embedContent(text);return result.embedding.values;}// 执行向量检索async search(query: string, topK: number 6, minScore: number 0.4): PromiseRetrievalResult[] {const vector await this.getEmbedding(query);const response await this.client.search(this.collectionName, { vector: vector, limit: topK, score_threshold: minScore, with_payload: true, }); return response.map(point ({ content: point.payload?.content as string || , score: point.score || 0, source: point.payload?.source as string || unknown, }));}}// 在 Agent 的 before_prompt_build 钩子中注入检索结果// 参考 openclaw-qdrant-rag 插件的实现模式export function createRAGMiddleware(retriever: QdrantRetriever) {return async (context: { query: string; systemPrompt: string }) {const results await retriever.search(context.query);const contextBlock results.map(r [来源: ${r.source}] ${r.content}).join(‘\n\n’);return { ...context, systemPrompt: ${context.systemPrompt}\n\n【相关背景知识】\n${contextBlock} };};}2.4 检索优化的关键参数在实际工程中以下参数对检索质量影响最大参数 说明 推荐值topK 返回最相似文档的数量 5-10minScore 相似度阈值过滤低质量结果 0.3-0.5maxTokens 注入上下文的最大 Token 数 1200-2000分块大小 文档切分粒度 512-1024 字符三、知识融合让检索结果“长”进上下文3.1 检索结果的结构化注入检索本身不是目的让检索结果有效影响模型生成才是关键。知识融合的核心在于 Prompt 的设计text【系统指令】你是一个专业的 AI 助手请基于以下背景知识回答用户问题。【背景知识】[来源: 技术文档 v2.3] OpenClaw 的 RAG 系统默认使用 SQLite 作为存储后端…[来源: 会议纪要 2026-01-15] 团队决定采用混合检索策略…【用户问题】{user_query}【回答要求】优先引用背景知识中的信息如背景知识不足明确告知用户引用时标注来源这种结构化注入方式的优势在于既提供了事实依据又保留了模型的推理空间。3.2 Token 感知的上下文管理上下文窗口是 Agent 系统最稀缺的资源。盲目地将所有检索结果塞入 Prompt只会导致上下文臃肿、成本飙升。OpenClaw 的上下文管理采用了多层策略技能元数据注入只将技能的名称和描述注入系统 Prompt完整内容按需读取对话压缩当上下文接近上限时自动将旧对话总结为摘要检索缓存缓存嵌入向量避免重复计算Token 硬截断对检索结果设置 hardCapTokens超出部分直接截断3.3 代码示例Token 感知的上下文组装器typescript// context-assembler.tsinterface ContextConfig {maxSystemTokens: number;maxRetrievalTokens: number;maxHistoryTokens: number;}class ContextAssembler {private tokenCounter: (text: string) number;constructor(config: ContextConfig, tokenCounter: (text: string) number) {this.tokenCounter tokenCounter;}assemble(params: {systemPrompt: string;retrievalResults: string[];conversationHistory: string[];userQuery: string;}): string {const { systemPrompt, retrievalResults, conversationHistory, userQuery } params;// 1. 压缩检索结果 - 按相关性排序后截断 const sortedResults retrievalResults; // 已按 score 排序 let retrievalBlock ; let usedTokens 0; for (const result of sortedResults) { const tokens this.tokenCounter(result); if (usedTokens tokens 1200) break; // 硬上限 retrievalBlock - ${result}\n; usedTokens tokens; } // 2. 压缩对话历史 - 只保留最近 N 轮 const recentHistory conversationHistory.slice(-6); // 3. 组装最终 Prompt return 【系统指令】${systemPrompt}【背景知识】${retrievalBlock || ‘无相关背景知识’}【对话历史】${recentHistory.join(‘\n’)}【用户问题】${userQuery}【回答】;}}四、记忆机制让 Agent 拥有“长期记忆”如果说向量检索解决的是“外部知识”的召回问题那么记忆机制解决的是“自身经验”的积累问题。4.1 记忆的分层设计OpenClaw 的记忆系统采用了多层架构层级 存储介质 用途 检索方式L0 SQLite (LCM) 全量消息存储与摘要 DAG memory_searchL1 MEMORY.md 用户偏好、项目约定 系统 Prompt 注入L2 事实图谱 实体、关系、别名 结构化查询L3 向量嵌入 语义相似记忆 向量检索这种分层设计的核心思想是用对的工具解决对的问题。结构化信息如“用户的生日是哪天”用结构化查询瞬时高效语义模糊的回忆如“我们之前讨论过什么关于数据库的事”用向量检索。4.2 记忆的自动索引与检索OpenClaw 内置的 memory-core 插件会自动索引 MEMORY.md 和 memory/**/*.md 文件。当 Agent 需要回忆时调用 memory_search 工具即可text用户: “我们之前对缓存层采用了什么方案”Agent: [调用 memory_search(query“缓存层方案”)][返回相关记忆片段]Agent: “根据之前的讨论我们决定采用 Redis 作为缓存层…”这种设计让记忆检索对用户透明——Agent 自主判断何时需要检索记忆而非被动等待用户指令。4.3 代码示例基于 SQLite 的本地记忆系统以下代码展示如何用 SQLite 向量扩展实现轻量级记忆系统pythonimport sqlite3import jsonfrom sentence_transformers import SentenceTransformerclass AgentMemory:definit(self, db_path: str “agent_memory.db”):self.conn sqlite3.connect(db_path)self.conn.execute(“CREATE TABLE IF NOT EXISTS memories (”“id INTEGER PRIMARY KEY AUTOINCREMENT,”“content TEXT,”“embedding BLOB,”“source TEXT,”“timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)”)# 启用 FTS5 全文搜索self.conn.execute(CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts “USING fts5(content)”)self.encoder SentenceTransformer(‘all-MiniLM-L6-v2’)def add_memory(self, content: str, source: str conversation): 添加记忆并自动生成向量 embedding self.encoder.encode(content).tobytes() cursor self.conn.execute( INSERT INTO memories (content, embedding, source) VALUES (?, ?, ?), (content, embedding, source) ) memory_id cursor.lastrowid # 同时写入 FTS 索引 self.conn.execute( INSERT INTO memories_fts (rowid, content) VALUES (?, ?), (memory_id, content) ) self.conn.commit() def search(self, query: str, top_k: int 5, hybrid: bool True): 混合检索向量 关键词 query_embedding self.encoder.encode(query).tobytes() # 向量检索 vector_results self.conn.execute( SELECT id, content, source, 1 - (embedding - ?) as score FROM memories ORDER BY embedding - ? LIMIT ?, (query_embedding, query_embedding, top_k) ).fetchall() if not hybrid: return vector_results # 关键词检索 (BM25 via FTS5) keyword_results self.conn.execute( SELECT m.id, m.content, m.source, 1.0 as score FROM memories_fts fts JOIN memories m ON fts.rowid m.id WHERE fts.content MATCH ? ORDER BY rank LIMIT ?, (query, top_k) ).fetchall() # 合并去重 (RRF 算法) return self._merge_results(vector_results, keyword_results, top_k) def _merge_results(self, vec_results, kw_results, top_k): 使用倒数排名融合 (RRF) 合并两组结果 scores {} for rank, row in enumerate(vec_results): scores[row[0]] scores.get(row[0], 0) 1.0 / (rank 60) for rank, row in enumerate(kw_results): scores[row[0]] scores.get(row[0], 0) 1.0 / (rank 60) # 按融合分数排序 sorted_ids sorted(scores.keys(), keylambda x: scores[x], reverseTrue)[:top_k] return [self.conn.execute( SELECT id, content, source FROM memories WHERE id ?, (id,) ).fetchone() for id in sorted_ids]4.4 记忆的反思与进化更高级的记忆系统不只是“存储-检索”还包含反思与进化机制。Agent 可以定期回顾对话历史抽取关键事实、偏好和决策将其从“会话记忆”升级为“长期记忆”。这种“记忆巩固”过程让 Agent 能够跨会话积累知识真正实现“越用越懂你”。五、工程化落地从原型到生产5.1 部署架构选择RAG 增强型 Agent 的部署有多种路径本地优先SQLite 本地嵌入模型零配置、高隐私适合个人场景云原生Qdrant/Milvus 云端嵌入高性能、可扩展适合企业场景混合本地存储 云端推理兼顾隐私与性能选择的关键在于对 “零运维”与“规模化” 的权衡。5.2 性能优化清单优化方向 具体措施嵌入缓存 对未变化的文本跳过重嵌入检索异步化 检索与模型调用并行执行分块策略 根据文档类型动态调整分块大小索引增量更新 只索引新增/变更的文档Prompt 缓存 利用 Anthropic 的 Prompt Caching 能力5.3 监控与迭代上线后建议持续追踪以下指标检索召回率检索结果中被最终答案引用的比例上下文 Token 消耗平均每次请求的 Token 数端到端延迟从用户提问到返回响应的总耗时记忆命中率memory_search 调用返回有效结果的比例结语RAG 增强型 Agent 的工程化实现远不止是“接一个向量数据库”那么简单。它需要在向量检索的精度、知识融合的深度和记忆机制的持续性之间找到平衡。OpenClaw 的实践告诉我们好的架构是演化出来的不是设计出来的。从 SQLite 起步逐步演进到专用向量数据库从简单的关键词匹配演进到混合检索从会话内记忆演进到跨会话的长期记忆——每一步都基于真实的业务需求驱动。对于正在构建 RAG 增强型 Agent 的开发者我的建议是先跑通最小可行版本让 Agent“能检索”再优化检索质量让 Agent“能找准”最后完善记忆机制让 Agent“能记住” 。这条路没有捷径但每一步都值得。