AI图鉴

AI图鉴首页 › AI 资讯 › # RAG 与智能体记忆:从原理到工程实践 > 一篇给学习者的总结。

# RAG 与智能体记忆:从原理到工程实践 > 一篇给学习者的总结。

来源:掘金 · 发布时间:11-02 · 由 AI图鉴每小时自动聚合

0. 为什么需要这两样东西 大语言模型有两个天生的缺陷: 知识是静态的——训练截止后,它不知道你家产品新出的文档、不知道今天的活动规则。你问它"XX 型号怎么配网",它要么不知道,要么编一个(幻觉)。

0. 为什么需要这两样东西

大语言模型有两个天生的缺陷:

- 知识是静态的——训练截止后,它不知道你家产品新出的文档、不知道今天的活动规则。你问它"XX 型号怎么配网",它要么不知道,要么编一个(幻觉)。

- 没有状态——这轮聊完下轮就忘。用户上周说过"我家是三室两厅",这周问"帮我配全屋灯",它不记得。

RAG 解决第一个问题,记忆解决第二个问题。

- RAG(Retrieval-Augmented Generation,检索增强生成):回答之前先去知识库里检索相关内容,把检索结果塞进 prompt,让模型"开卷考试"。

- 记忆:把历史对话、用户画像、重要事件存起来,在合适的时机取出来注入对话。

一句话:RAG 让模型知道"世界的知识",记忆让模型知道"这个用户的上下文"。

上篇:RAG

1. 基本原理:一条写入线 + 一条读取线

索引侧(写入,离线)

文档上传 → 解析 → 切片(chunking) → 向量化(embedding) → 存入向量索引

检索侧(读取,在线)

用户提问 → 问题向量化 → 相似度检索(取 TopK) → [可选:重排 rerank] → 拼进 prompt → LLM 生成答案

核心概念 embedding:把一段文字变成一个高维向量(比如 1536 个浮点数),语义相近的文本在向量空间里距离近。"灯连不上 Wi-Fi" 和 "灯具配网失败排查" 字面几乎不重叠,但向量距离很近——这就是向量检索能跨词汇鸿沟的原因。

相似度:最常用余弦相似度(cosine similarity),衡量两个向量方向的接近程度,取值 -1~1,越接近 1 越相似。向量数据库的 k-NN 查询(k-Nearest Neighbors)就是"找最相似的 k 条"。

一次 k-NN 向量检索的查询长这样(Elasticsearch / OpenSearch k-NN 的通用形态,其他向量库只是语法不同):

{

"query": {

"script_score": {

"query": { "match_all": {} },

"script": {

"source": "cosineSimilarity(params.query_vector, 'embedding') + 1.0",

"params": { "query_vector": [0.12, -0.45, 0.88, "..."] }

}

}

},

"size": 10

}

读法:拿问题的向量 query_vector,对候选文档逐条算余弦相似度,按分取前 10。真实引擎会用 HNSW 图索引加速(不真的全表扫),script_score 是可读的等价表达。"query": {"match_all": {}} 这个位置可以换成任何过滤条件——先过滤再打分就是后面混合检索的做法。

使用场景

- 智能客服:产品文档、FAQ 灌进知识库,用户提问自动检索回答

- 企业内部问答:规章制度、技术 wiki 检索

- 开发者助手:API 文档检索,帮开发者查用法

- 电商导购:商品说明、参数对比检索

RAG 还是微调?(面试出现频率最高的对比题)

两者都解决"模型不够好",但作用面完全不同:

一句话答法:知识用 RAG,行为用微调,两者不冲突可叠加——先用 RAG 保证有据,再用微调教模型更好地利用检索内容(业内称 RAFT 路线)。

2. 写入流水线的工程设计

一个生产级的向量化流水线,通常长这样:

知识库保存 → 发消息(MQ) → 消费者 → 路由到对应索引的处理器 → 切片 → 调 embedding → 批量写入向量库

四个值得记住的设计决策:

① 为什么走消息队列异步,而不是保存时同步向量化

- embedding 调用是秒级耗时(要调模型服务),同步做会把"保存文档"这个操作拖到几秒

- 失败可重试:向量化挂了,消息还在,重消费就行,源数据不受影响

- 削峰:批量导入 1 万篇文档时不会打爆 embedding 服务

② 为什么用"模板方法"组织处理器

切片 → 向量化 → 写库这个流程对所有索引都一样,变化的只有:切多粗、用哪个 embedding 模型、写到哪个索引。把固定流程抽成骨架(校验 → 前处理 → 同步 → 失败兜底),每种索引只实现差异部分。新增一种索引 = 新写一个子类,不碰主流程。

③ 为什么每种索引绑定一个 embedding 模型(换模型 = 重建索引)

向量维度和语义空间不可比。模型 A 生成的 1536 维向量和模型 B 生成的 1536 维向量,是两个完全不同的空间,放一起算相似度毫无意义。

所以换 embedding 模型的正确姿势是:

- 建一个新索引,绑定新模型

- 全量数据在新索引重跑一遍向量化

- 新旧索引同时存在,做效果对照(A/B),确认新的更好再切流量

- 切换期间可以双写:一条数据同时写新老两个索引,随时可回退

注意对照时新旧索引的文本预处理必须完全一致(同样的切片、同样的拼接方式),否则对比不公平。

④ embedding 输入的"上下文增强"

切片往往是一段孤立的文字,比如一个表格里的半句话。直接对裸切片做 embedding,向量里缺少"这是在说什么的"上下文,检索精度差。

常见做法:拼接后再向量化

类目路径 + 文档标题 + 切片内容 → 拼成一段 → embedding

例如"智能照明 / 故障排查 / 《灯具配网指南》 + 切片原文"。这样每条向量都自带归属信息,召回准确率明显提升。另外要设最大长度截断(比如 2048 字符),embedding 模型有输入上限。

// 切片是孤立文本,embedding 输入要先拼上下文

String input = catePath + " " + title + " " + chunk; // 类目路径 + 标题 + 切片

if (input.length() > 2048) {

input = input.substring(0, 2048); // 模型有输入上限,截断

}

⑤ 批量调用时的下标对齐坑

embedding 接口一般支持批量(比如 25 条一批)。返回结果里有 index 字段标明是第几条的结果——但这个 index 是批内下标(0~24),你必须加上批次起始下标才能还原全局位置。如果接口漏返回或乱序,而你用"到达顺序自增"来对位,从那条起后面所有切片的向量都会挂到错误的文本上,而且很难发现。这是个真实踩过的坑,记住它。

// 分批调用 embedding,灵魂在下标还原这一步

for (int i = 0; i < texts.size(); i += BATCH_SIZE) { // BATCH_SIZE = 25

List<String> batch = texts.subList(i, Math.min(i + BATCH_SIZE, texts.size()));

List<EmbedResult> results = embeddingClient.embed(batch); // 结果自带批内 index

for (EmbedResult r : results) {

// r.getIndex() 是批内下标(0~24),加批次起始下标 i 才是全局位置。

// 用"到达顺序自增"对位 = 接口乱序/漏返时,后面所有向量错挂文本

globalResults.put(i + r.getIndex(), r.getVector());

}

}

3. 检索策略进阶:解决各种"搜不到"

基础 RAG 拿用户原句去搜,效果往往不好。原因:用户的问法和文档的写法是两种语言。业界有一套成熟的进阶策略,每个都解决一种失败模式:

① 稀疏 + 稠密:混合检索(Hybrid Search)

先分清两大检索路线:

- 稠密检索(向量):embedding 语义匹配,能跨词汇鸿沟("灯连不上"≈"配网失败"),但对精确串反而不敏感——型号"X100-Pro-2G"、错误码"E-4021"这类必须字面命中的内容,向量容易糊掉

- 稀疏检索(BM25/关键词):词频倒排匹配,精确串、专有名词、编号一击即中,但不认识同义改写

生产做法:双路并发召回 → 结果合并(常用 RRF 倒数排名融合)→ 统一 rerank。两类盲区互补消掉。

// RRF(倒数排名融合):不用归一化两路的分数量纲,直接按"名次"融合

double score = 0;

for (Retriever r : List.of(bm25Retriever, vectorRetriever)) {

int rank = r.rankOf(docId); // 该文档在这路的名词;没命中视为无穷大

score += 1.0 / (60 + rank); // 60 是常用常数 K

}

// 所有文档按 score 降序取 TopN,再统一送 rerank 精排

为什么用 RRF 而不是把两路分数加权相加:BM25 分数和余弦相似度的量纲完全不可比(一个可以到几十,一个 0~1),硬归一化也容易被离群值带偏;名次是量纲无关的,融合更稳。

解决:向量搜得到语义、搜不准字面;关键词搜得准字面、搜不到语义——混合后都要。

② 查询改写(Query Rewrite)

用户说:"灯死活连不上"

直接拿这句搜知识库,可能什么都搜不到,因为文档里写的是"配网失败排查步骤"。

做法:先让 LLM 把口语改写成文档风格的关键词("灯具 配网 失败 排查"),再拿改写结果去检索。

解决:口语与文档的词汇鸿沟。

③ 多查询检索(Multi-Query)

一个问题从多个角度生成 3~5 个变体查询,并发检索,结果合并去重。

例:"怎么让家里灯自动开关" → 变体:"灯具定时任务设置"、"场景联动配置"、"自动化规则创建"——三个变体各自命中不同文档,合并后覆盖全面。

解决:单一表述视角局限,一次问法只碰得到知识库的一个角落。

④ HyDE(Hypothetical Document Embeddings,假设性文档检索)

思路反直觉但很有效:不是拿问题去搜,而是先让 LLM 编一个假设性的答案,拿这个假答案去搜。

例:用户问"支持多少路继电器?",LLM 先生成一段假想的答案文档"本设备支持 8 路继电器输出,每路最大负载 10A……",用这段话去检索。

为什么有效:答案和文档是同一种语言,问题和文档不是。假答案(即使内容是编的)的"文体、术语、结构"都更接近真实文档,向量距离反而更近。

解决:问题信息量太薄,和文档的语义差距大。

⑤ 重排(Rerank)

向量检索是"海选",快但糙,可能召回 50 条里混着不太相关的。Rerank 是"复试":用一个更精细的交叉编码器模型(cross-encoder)对这 50 条逐一精细打分,重新排序,取最前面的几条。

流程:向量检索 Top50(毫秒级,粗) → rerank 精排(稍慢,准) → 取 Top5 进 prompt。

解决:向量召回快而不精的问题;同时省 token——只有真正相关的几条才进 prompt。

⑥ 分数门控(Score Gating)

不是所有召回都值得进 prompt。设定一个相似度阈值,最高分都低于阈值的,干脆什么都不注入,让模型直接回答(或者坦白说不知道)。

低质量的召回塞进 prompt 是负收益:它误导模型、浪费 token、还可能让模型把不相关的内容硬扯进答案。"宁缺毋滥"是 RAG 质量守则。

List<Chunk> hits = retrieve(query, TOP_K);

if (hits.isEmpty() || hits.get(0).score() < THRESHOLD) {

// 最高分都不达标 → 整体放弃注入,让模型裸答或坦白不知道

// 低分硬塞只会污染 prompt,是负收益

return directAnswer(query);

}

return generateWith(query, hits); // 只有最高分过线才注入

使用场景:RAG 用于做"路由判断"(判断该不该调工具、该走哪个分支)时,门控尤其重要——低分命中就当没命中。

⑦ 智能路由

按问题类型自动选策略:短问题走 HyDE、模糊问题走多查询、精确术语直接搜。一个调度器在前面判断,后面挂一排可插拔的检索器。

4. Agentic RAG:让模型自己当检索员

前面的模式都是固定流程:每轮提问 → 无条件检索 → 塞结果。这叫 pipeline RAG。

进阶模式是把检索做成一个工具(Tool/Function Calling)交给模型:

系统:你有 searchKnowledge 工具可用,需要资料时可自主调用

用户:帮我对比 A 和 B 两个型号

模型:(思考:我需要 A 的参数)→ 调工具,关键词"A 型号 参数"

(拿到结果,还需要 B)→ 再调工具,关键词"B 型号 参数"

→ 综合两次检索结果生成答案

用开源框架(LangChain4j 风格)写出来大概是这样:

class KnowledgeTools {

@Tool("检索知识库。当回答需要产品文档、参数、故障排查步骤等资料时调用")

String searchKnowledge(@P("检索关键词,用文档风格术语,不要用用户原话") String query) {

List<Chunk> hits = retriever.retrieve(query, 5);

hits = dedupByDocIdAndTitle(hits); // 与预召回通道按 文档ID+标题 去重

saveRagTrace(sessionId, query, hits); // 留痕:支撑前端"回答依据"与离线评估

return format(hits); // 返回给模型当参考资料

}

}

// 注册进模型后:检索时机、检索词、检索几次,全由模型自主决定

注意 @P 描述里写"不要用用户原话"——工具的参数描述就是给模型的提示词,写清楚能显著提高检索词质量。

好处:

- 检索词由模型写——比用户原话更精准

- 检索轮次由模型定——简单问题不检索(省时省钱),复杂问题多轮检索

- 可以检索后自我修正——第一次结果不理想,换个关键词再搜

工程上必须配两件事:

- 去重:pipeline 预召回的结果和工具召回的结果要按"文档ID+标题"去重,否则同一段内容出现两次,既浪费 token 又让模型以为它特别重要。

- 留痕:每轮实际用了哪些召回内容,落库存下来。用途:前端展示"回答依据"(引用溯源)、离线评估检索质量。

两种模式怎么选

生产系统常见做法:两者叠加——pipeline 兜底保证每轮都有基础召回,工具作为模型的增强手段,结果合并去重。

新趋势速览:一句话说清五个新词(面试加分题)

记忆口径:CRAG 纠检索、Self-RAG 纠生成、Adaptive 纠流程;Late Chunking 是索引侧优化,CAG 干脆不检索。

5. 向量存储选型与灰度迁移

混合检索是搜索引擎方案的最大卖点:先按类目/权限/语言过滤,再在过滤结果里做向量 TopK——这在很多业务里是刚需(比如:只在"英文文档"且"当前用户有权限看"的范围内做语义检索)。

迁移灰度套路(任何向量库迁移通用):

- 新旧两个库双写,配置开关控制

- 读仍走旧库,核对一段时间

- 灰度比例切读到新库

- 全量切换后关双写

切记:双写期间新旧库的写入逻辑要保证幂等(同一条文档重复写不产生脏数据)。

6. 效果评估:没有度量就没有优化

上线 RAG 后必须回答"答得好不好",业界标准工具是 RAGAS 这类评估框架,四个核心指标:

前两个评生成,后两个评检索。检索指标差先修检索(切片/embedding/策略),生成指标差再修 prompt 和模型——分不清这两层,优化就是盲人摸象。

实践建议:准备 50~100 条真实问题作为固定测试集,每次改动(换切片策略、换 embedding 模型、调阈值)跑一遍对比。没有回归测试集的 RAG 优化 = 开车不看仪表盘。

下篇:智能体记忆

1. 三层记忆模型(类比人类)

人类记忆有感知记忆、短期记忆、长期记忆。智能体的记忆体系几乎同构,分三层:

为什么分层:短期记忆每轮都要读写,必须快,但不用久;长期记忆要跨会话、跨设备共享,还要语义检索,值得独立成一个服务;而全量长期记忆每轮都注入 prompt 既贵又稀释注意力,所以让模型按需查——三者分别是"记住这次""记住永远""想起来了再查"。

2. 短期记忆:对话历史的工程实现

对话框架(如 LangChain/LangChain4j)一般内置 ChatMemory 抽象:

- 按 memoryId(通常 = 会话 ID)隔离,每个会话一条消息列表

- 每轮把历史消息 + 新问题一起发给模型

- 消息序列化成 JSON 存进 KV 存储(Redis 类)

// LangChain4j 风格:每会话一个记忆实例,窗口 + 自定义存储

ChatMemoryProvider provider = memoryId -> MessageWindowChatMemory.builder()

.id(memoryId) // 会话 ID,按会话隔离

.maxMessages(20) // 窗口控制:只保留最近 20 条消息

.chatMemoryStore(store) // 自定义 store:messagesToJson 落 KV,读时 fromJson 还原

.build();

// 发送时框架自动把 [窗口内历史 + 本轮新消息] 组装进请求

三个生产要点:

① 窗口控制

历史不能无限长(token 上限 + 成本 + 注意力稀释)。两种策略:

- 条数窗口:只保留最近 N 轮

- token 窗口:按 token 预算裁剪,超了就丢最老的

更讲究的做法:老消息摘要压缩而不是直接丢弃("用户之前询问过客厅灯的配置问题"代替十轮原文)。

② 历史里不只是聊天文字

生产系统会在历史里同时存:当轮的 RAG 召回内容、工具调用记录、展示卡片。翻历史时才能完整还原"当时模型看到了什么"。只存文字的历史是残缺的——用户回看"刚才那个答案的引用"就需要当时的召回记录。

③ 工具调用的配对

模型发起的工具调用请求(tool call)和工具返回结果(tool result)必须成对进历史。漏了配对,下一轮模型看到自己"发过请求但没收到结果",会重复调用或直接报错。

3. 长期记忆:存储与召回

记忆的两种类型

分开建的原因:画像适合全量注入系统提示词(量小、始终相关),事件适合按时间窗语义检索(量大、只在相关时召回)。

记忆服务的三个原语

一个像样的长期记忆服务只需要三个接口:

upload(写入):内容 + 归属主体(uid)+ 会话 ID + 业务名 + 时间戳

recall(召回):语义向量 + 时间窗过滤 + 结构化字段过滤 + TopK

forget(遗忘):按条件删除

recall 的三重过滤是精髓,举一个例子说明为什么需要:

用户问:"上次那个门锁的问题解决了吗?"

- 语义检索:拿问题向量找"门锁"相关记忆 → 命中 30 条

- 时间过滤:只要最近两周的 → 剩 8 条(两年前买门锁的记录被滤掉)

- 结构化过滤:只看"售后类"记忆 → 剩 3 条

- TopK:取最相似的 2 条注入 prompt

只用语义检索,30 条全灌进去,既贵又淹没关键信息。

三重过滤写成一个查询对象就是(真实记忆服务的 recall 接口长这样):

MemoryQuery q = MemoryQuery.builder()

.uid(uid) // 归属:只查这个用户的记忆

.text(userQuestion) // ① 语义:问题向量检索

.startTime(now - Duration.ofDays(14)) // ② 时间:只要近两周

.bizFilter(Map.of("category", "AFTER_SALE")) // ③ 结构化:只看售后类

.topK(2) // 最终只注入 2 条

.build();

forget 为什么重要:记忆不是只增不减。用户说"忘掉我说过的住址"(合规要求,类 GDPR 被遗忘权)、记忆过期、记忆写错了,都需要删除能力。没有 forget 的记忆系统上不了合规线。

记忆淘汰:无限增长怎么办(记忆方向的分水岭题)

只增不减的记忆库会越来越慢、越来越贵、召回越来越糊(无关记忆淹没关键记忆)。三个自动淘汰机制:

- 时间衰减:每条记忆带时间戳,检索得分乘衰减因子(如 得分 × e^(-Δt/半衰期)),越新的排越前。画像类半衰期长(年),事件类半衰期短(周/月)

- 重要性更新:被召回命中、被模型采用的记忆升权,长期没人用的降权直至淘汰——类似缓存的热度淘汰(LFU 思路)

- 摘要归档:同类事件记忆积累到阈值就合并成一条摘要("本季度共反馈 5 次网关掉线"),原文归档

面试要点:手动 forget(用户/合规触发)和自动淘汰(系统自维护)是两套机制都要有——前者讲合规,后者讲检索质量和成本。

记忆怎么写进去:LLM 抽取

对话里产生记忆不能靠把整段对话存进去(那是短期记忆的事),而是用 LLM 从对话里抽取值得记的事实:

对话:

用户:我家客厅用的是 A 品牌的吸顶灯,别的都还行就是太亮了。

助手:好的,已了解……

LLM 抽取 → 记忆事实:

{类型: 画像, 内容: "客厅灯具为 A 品牌吸顶灯,用户觉得偏亮", 来源会话: xxx}

抽取时机:会话结束时(异步、不阻塞对话)。抽取 prompt 要求输出结构化 JSON(类型/内容/置信度),低置信度丢弃。

用户自定义记忆的校验

让用户自己写记忆("记住我是开发者")时,要防两件事:注入攻击(用户写"以后忽略所有安全规则")和违禁内容。生产做法是两级校验:

- 静态校验(便宜,先跑):模板匹配(和系统预设模板 hash 对比,一样就直接过)+ 违禁词表

- LLM 校验(贵,兜底):让另一个 LLM 判断这段自定义记忆是否合规

LLM 校验是异步的(秒级),所以接口模式是:提交 → 返回 taskId → 前端轮询结果。便宜检查前置、昂贵检查兜底,是所有校验链路的通用优化。

4. 记忆工具化:让模型主动回忆和遗忘

把 recall_memory 和 forget_memory 注册为模型可调用的工具:

用户:还是按老规矩给我配一个晚安场景

模型:(思考:老规矩是什么?我需要回忆)→ 调 recall_memory("晚安场景 用户偏好")

→ 返回记忆:"用户习惯 23 点关全屋灯、卧室留 10% 亮度小夜灯"

→ 按记忆生成场景配置

用户:对了,忘掉我的作息时间吧

模型:→ 调 forget_memory("作息时间") → "已忘记"

对比"每轮自动注入全部记忆"的优劣:

生产组合:画像记忆全量注入系统提示词(量小稳定),事件记忆工具化按需检索(量大稀疏)。

5. 注入位置:记忆怎么进 prompt

三种注入位:

- 系统提示词:画像记忆 + 事件记忆摘要 → "关于用户:{用户画像}。相关记忆:{事件记忆}"。最常见,适合稳定信息。

- 占位符变量:系统提示词里留 {MEMORY} 占位,对话管线在发送前做变量替换。好处是提示词模板和记忆内容解耦,运营可改模板不动代码。

- 对话历史伪装:把召回的记忆包装成一条历史消息("系统提示:回忆起用户曾……")插入历史中部。适合保持对话流式的场景。

6. 一个完整的例子:智能客服的一次提问

把上下所有东西串起来。场景:IoT 智能客服,用户在 App 里提问。

用户:"客厅那个灯又离线了,跟上次一样,帮我看看"

【第 1 步 · 短期记忆】取出本会话历史(前 2 轮在聊网关放置位置)

【第 2 步 · 长期记忆召回】

画像注入:用户是普通家庭用户,家里 12 个设备,1 个网关

事件检索(工具化):模型判断"上次"需要回忆

→ recall_memory("灯 离线", 时间窗=近 30 天)

→ 命中:"10-12 客厅吸顶灯离线,重置后恢复,固件 v1.2"

【第 3 步 · RAG 检索】

查询改写:"吸顶灯 离线 重置 排查"

向量检索 + rerank → 《灯具离线排查指南》第 3 节(含固件升级说明)

分数门控:最高分 0.87 > 阈值 0.6 ✓ 注入

【第 4 步 · 生成】

prompt = 系统提示(含画像) + 历史 + 记忆事件 + RAG 内容 + 当前问题

→ 模型生成:"上次是重置后恢复的,这次大概率同款问题。步骤:1. 长按…

(另外检测到 v1.2 固件有已知离线问题,建议先升级到 v1.3 再观察)"

【第 5 步 · 记忆写入】会话结束,LLM 抽取:

"10-30 客厅吸顶灯再次离线(第 2 次),建议升级 v1.3" → 存事件记忆

【第 6 步 · 留痕】本轮引用的 RAG 文档 ID 落库(前端展示"回答依据")

注意这个例子里三样东西各司其职:RAG 给了"通用解决方案",长期记忆给了"这个用户上次发生了什么",短期记忆给了"这轮对话的语境"。抽掉任何一个,回答质量都会明显下降。

附:学习清单与常见坑

自查清单(能做到 = 学会了)

- 能画出 RAG 写入线和读取线的完整流程图

- 能解释为什么换 embedding 模型必须重建索引、怎么做灰度

- 能说出查询改写 / 多查询 / HyDE / rerank 各自解决什么问题

- 能解释分数门控为什么"宁缺毋滥"

- 能对比 pipeline RAG 和 agentic RAG 的适用场景

- 能说出三层记忆模型的划分依据和各自存储要求

- 能设计一个带"语义+时间+结构化"三重过滤的记忆召回

- 能说出记忆系统为什么必须有 forget

- 知道 RAGAS 四指标分别评检索还是评生成

- 能对比 RAG 与微调的适用边界及组合方式(RAFT)

- 能说出稀疏/稠密检索各自的盲区和混合检索的做法

- 能一句话说清 Late Chunking / CAG / CRAG / Self-RAG / Adaptive RAG

- 能说出记忆自动淘汰的三个机制(时间衰减 / 重要性更新 / 摘要归档)

常见坑(前人踩过的)

- 裸切片直接 embedding——缺上下文,召回差。拼接标题/类目路径再向量化。

- 批量 embedding 用到达顺序对位——接口乱序/漏返时向量错挂文本。必须用返回的 index 字段还原全局下标。

- 两路召回不去重——同一段内容重复占 prompt、误导模型。

- 低分召回也硬塞 prompt——负收益。设阈值门控。

- 工具调用和结果不配对入历史——模型重复调用或报错。

- 对话历史无限增长——token 爆炸 + 注意力稀释。窗口 + 摘要压缩。

- 记忆只增不减——合规过不了,用户也烦。提供 forget。

- 没有评估测试集就调优 RAG——每次改动都是盲调。50~100 条固定问题集 + 每次改动跑对比。

- 把 LLM 校验做成同步接口——秒级耗时拖死接口。taskId + 异步 + 轮询。

- 静态校验和 LLM 校验顺序反了——便宜检查必须前置。

- 只有向量一路召回——精确型号/错误码字面搜不到,关键词(稀疏)路兜底,双路合并。

本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。

相关 AI 工具

相关免费羊毛

同源资讯

阅读原文(juejin.cn)

标题、摘要与正文均来自该信源的公开页面,版权归原作者所有;本站自动聚合与翻译,仅作出处跳转。

← 返回 AI 资讯列表AI工具图鉴首页

返回 AI图鉴首页 · 提交工具 · 商务合作 · 关于本站 · 607 款 AI 工具分类导航 · 每日 AI 热门项目与动态