Day 18 / 共 20 天 · 第 4 周 Agent/Workflow/生态

聊天引擎与记忆(单轮→多轮)

QueryEngine 是"单轮问答",但真实对话是多轮的——"它多少钱?"里的"它"指上文。今天看 ChatEngine(多轮对话)和 Memory(管理对话历史),让 RAG/Agent 记住上下文。

📍 你在整条链的位置
Agent D17 ChatEngine + Memory(多轮) 观测评估 D19
L01

多轮对话的"指代"难题

🤔 QueryEngine 每次 query 是独立的,不记上一句 但对话有上下文——
📝 "指代"翻车现场 第 1 轮:公司的退款政策是什么? → 答"7 天内可退"
第 2 轮:那需要多久到账?
→ 如果直接拿"那需要多久到账"去检索——"那"指什么?检索不到东西!(丢了"退款"这个上下文)
💡 ChatEngine 维护对话历史,把当前问题结合历史处理 比如把"那需要多久到账"改写成"退款到账需要多久"再检索。这是从"问答"到"对话"的关键升级。
L02

ChatEngine 模式

chat_engine/simple.py(纯聊天,无 RAG)、condense_question.pycontext.pycondense_plus_context.py(默认推荐)。index.as_chat_engine(chat_mode=...) 创建。

读法:不同模式 = 不同的"结合历史 + RAG"策略。Condense(浓缩问题再检索)、Context(每轮检索、塞进 system)、CondensePlusContext(两者结合,默认)。下面拆两个主要的。
L03

condense_question:改写问题

chat_engine/condense_question.py:先用 LLM 把"历史 + 当前问题"浓缩成一个独立问题,再走 RAG。

📝 改写解决指代 历史 + "那需要多久到账?" → LLM 改写 → 退款到账需要多久?(独立、无指代)→ 用它检索 → 命中"退款到账时长"。
优点:检索的问题总是完整独立的 缺点:多一次 LLM 调用(改写)。适合"每轮都需要检索"的知识问答。
L04

context 模式:带资料聊天

chat_engine/context.py:每轮用当前消息检索相关 Node,塞进 system 提示,连同完整对话历史给 LLM。

保留完整上下文,LLM 自己理解指代 不改写问题——每轮检索资料作为"背景知识"放进 system 提示,LLM 看着"资料 + 完整对话历史"回答。好处:保留完整对话上下文、资料实时相关。CondensePlusContext(默认)= 浓缩问题做精准检索 + 保留对话上下文——兼顾两者。
L05

Memory:对话历史的管理者

memory/memory.py(新版统一 Memory)、chat_memory_buffer.pychat_summary_memory_buffer.pyvector_memory.py + memory_blocks/。存对话历史(ChatMessage 列表,Day 15)。

🤔 对话越长,历史越长,塞不下 LLM 上下文 聊了 50 轮,全部历史几万 token——塞不进提示词。核心难题:记多少、怎么记?
💡 Memory 存历史,用不同策略管理"记多少" ChatEngine/Agent 每轮从 Memory 取历史、处理完写回。不同 Memory 策略解决"塞不下"(下面)。回想你学过的 SuperAGI 滚动记忆压缩、crewAI 统一记忆——同一问题的不同解法。
L06

缓冲区:只记最近的

memory/chat_memory_buffer.pyChatMemoryBuffer:保留最近消息,用 token_limit 控制——超了丢最老的。

最简单:滑动窗口 只保留最近 N token 的对话,老的丢弃。简单有效,适合大多数场景(远古对话通常不重要)。缺点:丢掉的彻底忘(10 轮前说的名字现在想不起)。这是"记忆容量"和"上下文限制"的最朴素折中。
L07

摘要与向量记忆:越来越像人

  • ChatSummaryMemoryBuffer:历史超限时用 LLM 把旧对话摘要成一段,保留摘要 + 最近消息——不彻底忘,压缩记忆。
  • VectorMemory:把历史也向量化存起来,需要时检索相关历史(而非全带)——长期记忆。
  • memory_blocks(新版):组合短期缓冲 + 长期向量 + 静态事实块。
三层策略,越来越像人的记忆 Buffer(只记最近)→ Summary(旧的压成摘要)→ Vector(旧对话存起来、按需回忆)。越往后越像人——近的记得清、远的记要点、需要时能"回忆"。新版可组合记忆块是当前 Agent 记忆的前沿。选哪种看对话长度和"要不要记住远期信息"。
同一难题(历史塞不下 LLM 上下文)的三种解法 ChatMemoryBuffer 只记最近(滑动窗口) 旧消息 · 超 token_limit 丢弃 最近消息(保留) 最近消息(保留) 简单 · 远古彻底忘 SummaryMemoryBuffer 旧的压成摘要 📝 LLM 摘要 (旧对话压成一段) 最近消息 最近消息 不彻底忘 · 压缩记忆 VectorMemory 向量化存 · 按需检索 历史向量库(全存) 检索相关历史 长期记忆 · 能"回忆" 越往右越像人:近的记得清 → 远的记要点 → 需要时能回忆
Buffer / Summary / Vector 三种 Memory 策略应对"历史太长",代价与"记住远期"能力逐级递增
L08

今日小结 + 动手

🧠 今天你应该能回答

  • 多轮对话的"指代"难题?ChatEngine 怎么解决?
  • condense_question 和 context 模式的区别?
  • Memory 的核心难题(历史太长塞不下)?
  • Buffer/Summary/Vector 三种记忆策略的取舍?

✋ 动手

cd /Users/bitmart/work/codes/github/llama_index/llama-index-core/llama_index/core
ls chat_engine/ memory/
grep -n "class ChatMemoryBuffer\|token_limit" memory/chat_memory_buffer.py | head
grep -n "chat_mode\|CondensePlusContext" chat_engine/types.py | head
明天预告 · Day 19:RAG/Agent 是"黑盒"——怎么看内部、怎么衡量好坏?可观测(callbacks/instrumentation 追踪)+ 评估(用 LLM 给答案打分)。这是从"能跑"到"能上线"的关键。
← Day 17 Day 19 · 可观测评估 →