RAG 增强型 Agent 架构:向量检索、知识融合与记忆机制的工程化实现
引言
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.ts
import { 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): Promise<number[]> {
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): Promise<RetrievalResult[]> {
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-10
minScore 相似度阈值,过滤低质量结果 0.3-0.5
maxTokens 注入上下文的最大 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.ts
interface 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_search
L1 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 + 向量扩展实现轻量级记忆系统:
python
import sqlite3
import json
from sentence_transformers import SentenceTransformer
class AgentMemory:
def init(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(), key=lambda x: scores[x], reverse=True)[: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“能记住” 。这条路没有捷径,但每一步都值得。
更多推荐



所有评论(0)