系列文章
1. 一文读懂RAG和KAG:AI知识增强的两大“利器”
2. 探秘检索增强生成:上下文检索、混合搜索与密集检索全解析
3. LangChain与LlamaIndex,RAG框架该怎么选?
4. 2W8000字深度剖析25种RAG变体:全网最全~没有之一
5. 探秘RAG:查询优化全解析,解锁大语言模型新潜能
6. 2W8000字揭秘RAG:从基础到高级的逆袭,彻底重塑大模型!
7. 1W8000 字 探秘 RAG 应用搜索:从语义搜索到评估指标的深度解析
8. 万字解析非结构化文档中的隐藏价值:多模态检索增强生成(RAG)的前景
9. 从 0 到 1 挑选 RAG 评估工具,这份指南请查收
10. 利用Transformer、DPR、FAISS和BART对检索增强生成(RAG)进行深入技术探索
11. 【2万字深度长文】深入浅出RAG详解:语言模型的“开卷考试”——让模型答案锚定现实的外部“记忆”
第1章:语言模型的隐藏操作系统
从理论上讲,你刚刚部署的大语言模型是最先进的。它由GPT-4o驱动,连接到向量数据库,甚至还有一个暂存区并能使用各种工具。但总感觉哪里不对劲。
你注意到智能体忘记了用户三轮对话之前说的话,从无关文档中生成虚假答案,一直在反复解释它本应已经“知道”的事情。
然后你突然意识到:模型没有问题,是你的上下文有问题。
在本文,我们不仅会探索什么是上下文,还会像系统工程师一样拆解它:如何构建、隔离、存储、检索、压缩上下文,并随着时间推移塑造它。
什么是上下文工程?
将大型语言模型(LLM)想象成一位世界级的思想家,但患有短期记忆丧失。除非你在每次调用时提供上下文——提示词、聊天历史、文档、示例、工具结果和暂存区内容——否则它一无所知。
受限于令牌窗口的上下文是LLM感知到的唯一“世界”。这意味着你的系统必须:
构建进入上下文的内容
过滤排除无关内容
精确且一致地格式化所有内容
上下文工程是管理这一过程的工艺:如何填充、组织、存储、压缩、检索和隔离每次交互中传递给LLM的信息。
做好上下文工程,将带来:
减少令牌使用和延迟
准确的多轮记忆
使用工具的智能体的推理行为
更清晰的答案,更少幻觉
若做得不好,则会导致混乱:臃肿的提示词、无关的记忆,以及模型自相矛盾或偏离主题。
AI应用中的上下文类型
要智能地设计系统,必须区分上下文类型及其所属位置。
1. 短暂上下文(每轮记忆)
包括当前用户消息、最近几轮聊天历史和活跃的思考或工具调用。它随请求而变化,除非显式保存,否则不会保留。
示例:
“用户:果阿的天气如何?”
上一次函数调用:
get_weather("Goa")
当前暂存区:“规划度假行程…”
2. 持久上下文(长期记忆)
跨会话、任务甚至智能体仍应可访问的事实。可以是结构化的(如图表或关系表)或非结构化的(如笔记、文件、嵌入)。
示例:
“用户偏好素食选项。”
“公司政策禁止食用贝类。”
“上次航班预订于12月10日。”
3. 运行时任务上下文(工作状态)
解决复杂任务时存储的临时信息,就像子智能体书写中间步骤和结果的白板。
示例:
“步骤1:提取位置实体”
“知识库查询结果”
“工具输出:‘未找到航班’”
现实问题:记忆墙
做个快速练习:问你的智能体:“我之前说过喜欢哪些餐厅?”如果系统不使用长期记忆或未智能存储用户偏好,智能体要么:
猜测(产生幻觉)
道歉(中断流程)
要求用户重复(糟糕的用户体验)
问题不在于模型,而是缺乏经过设计的上下文。
第2章:构建航海家——一个经过上下文工程设计的大语言模型智能体
我们将打造航海家(Voyager),这是一款旅行助手大语言模型智能体,旨在记住你的偏好,实时获取信息,并在多轮任务中保持敏锐。与那些忘记上下文或陷入提示膨胀困境的基础聊天机器人不同,“航海家”使用结构化记忆来清晰思考。
技术栈
为正确实现,我们将结合四个强大组件:
角色 | 工具/技术 |
---|---|
短暂聊天记忆 | LangChain的 |
持久长期记忆 | Neo4j图数据库(结构化) |
上下文文档获取 | LlamaIndex与向量存储 |
LLM推理 | GPT-4o(通过Azure OpenAI) |
这种架构使我们能够清晰地划分内存,高效管理提示窗口,并通过检索参考资料减少幻觉。
分步实现
步骤1:初始化LLM和短期记忆
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationSummaryBufferMemory
llm = ChatOpenAI(
model="gpt-4o",
temperature=0,
openai_api_key="your-azure-openai-key"
)
short_mem = ConversationSummaryBufferMemory(
llm=llm,
max_token_limit=1200 # 防止内存膨胀
)
这将保留最近几次交互并自动总结其余部分,确保随时间推移提示词保持简洁。
步骤2:使用LlamaIndex加载旅行文档
from llama_index import SimpleDirectoryReader, VectorStoreIndex
docs = SimpleDirectoryReader("data/travel_guides").load_data()
vec_index = VectorStoreIndex.from_documents(docs)
这些是本地(或云端)存储的Markdown或纯文本文件,当用户询问目的地、酒店、签证信息等时,提供动态的事实依据。
步骤3:为长期知识设置Neo4j
from neo4j import GraphDatabase
import os
graph = GraphDatabase.driver(
os.getenv("NEO4J_URI"),
auth=(os.getenv("NEO4J_USER"), os.getenv("NEO4J_PASS"))
)
def add_fact(subject, relation, obj):
with graph.session() as s:
s.run("MERGE (a:Entity {name:$s})"
"MERGE (b:Entity {name:$o})"
"MERGE (a)-[r:"+relation+"]->(b)",
s=subject, o=obj)
# 示例:从之前对话中已知的事实
add_fact("Aisha", "ALLERGIC_TO", "Shellfish")
add_fact("Aisha", "FAV_DEST", "Goa")
这作为持久的结构化记忆。与嵌入不同,它提供精确、无歧义的回忆,非常适合用户资料、偏好和关系感知推理。
步骤4:智能组装上下文
def build_context(user_query):
history = short_mem.load_memory_variables({})["history"]
with graph.session() as s:
facts = s.run("MATCH (u:Entity {name:'Aisha'})-->(b) RETURN b.name AS fact")
facts_text = "; ".join([r["fact"] for r in facts])
retriever = vec_index.as_retriever(similarity_top_k=2)
docs_txt = "\n\n".join([d.page_content for d in retriever.retrieve(user_query)])
return f"""
You are Voyager, a travel planner for Aisha.
Known facts:
{facts_text}
Summary of chat so far:
{history}
Reference knowledge:
{docs_txt}
User input:
{user_query}
"""
这确保只将最相关的上下文片段——最近的聊天、已知事实和实时参考信息——交给LLM。
步骤5:运行智能体循环
while True:
user = input("Aisha: ")
ctx = build_context(user)
resp = llm.invoke(ctx)
print("Voyager:", resp.content)
short_mem.save_context({"input": user}, {"output": resp.content})
这是完整的循环架构:智能提示组装、动态记忆和最小化令牌使用。
解决的问题
✅ 通过摘要减少提示词大小
✅ 通过结构化图记忆保持身份
✅ 通过检索参考资料避免幻觉
✅ 支持长期任务执行且不偏离目标
第3章:多智能体上下文——隔离、共享与扩展
在单个LLM智能体能力有限的世界里,多智能体架构正成为应对更复杂工作流程的解决方案。但一旦将两个智能体放在同一环境中(如规划者和研究者),就会意识到共享上下文并非易事:一个智能体可能意外覆盖记忆,另一个可能因无关的过往上下文产生幻觉。你需要一个有范围界定、可读且可组合的记忆系统。
目标
我们希望构建一个系统:
每个智能体(规划器、检索器、执行器)都有独立的作用域上下文
智能体可以向共享便笺本写入并从中读取信息
除非显式调用,否则长期记忆保持不变
通过控制每个智能体可见内容,保持令牌使用效率
可以将其想象成云函数:小型、独立、有内存感知的单元,通过结构化接口通信,而不是共享上下文块。
构建:具有作用域上下文的多智能体任务规划
我们将构建一个包含3个智能体的度假规划助手:
PlannerAgent
:将用户查询分解为子任务ResearchAgent
:从RAG和记忆中获取信息ExecutorAgent
:合成并提供最终答案
每个智能体都有自己的LLM实例和上下文作用域。
设置:共享任务状态与内存注册表
class TaskState:
def __init__(self):
self.scratchpad = []
self.subtasks = []
self.context_registry = {}
def write(self, agent_name, data):
self.scratchpad.append({"from": agent_name, "data": data})
self.context_registry[agent_name] = data
def read(self):
return self.scratchpad[-3:] # 仅保留最近相关步骤
PlannerAgent:分解为子任务
class PlannerAgent:
def __init__(self, llm):
self.llm = llm
self.name = "Planner"
def plan(self, query, state):
prompt = f"You are a task planner. Break this request into subtasks: {query}"
response = self.llm.invoke(prompt).content
subtasks = response.split("\n")
state.subtasks = subtasks
state.write(self.name, f"Planned subtasks: {subtasks}")
ResearchAgent:通过RAG+图获取信息
class ResearchAgent:
def __init__(self, llm, retriever, graph):
self.llm = llm
self.retriever = retriever
self.graph = graph
self.name = "Researcher"
def research(self, task, state):
facts = get_facts_from_graph()
docs = self.retriever.retrieve(task)
merged = "\n\n".join([d.page_content for d in docs])
prompt = f"""
Task: {task}
Known facts: {facts}
Documents: {merged}
Answer concisely for this step.
"""
result = self.llm.invoke(prompt).content
state.write(self.name, f"{task} → {result}")
执行流程
state = TaskState()
planner = PlannerAgent(llm)
researcher = ResearchAgent(llm, retriever=vec_index.as_retriever(), graph=graph)
executor = ExecutorAgent(llm)
query = "Plan me a 5-day beach vacation under ₹80,000 in July."
planner.plan(query, state)
for sub in state.subtasks:
researcher.research(sub, state)
result = executor.execute(state)
print("🚀 Recommendation:\n", result)
为何有效
每个智能体只看到所需内容,减少令牌膨胀
共享内存显式且可控,无隐藏副作用
智能体可独立替换、并行化或调试
若一个智能体失败,其状态可追溯和恢复
第4章:基准测试上下文策略——智能记忆vs长窗口
现在你可能会问:“如果Claude 3.5或Gemini 1.5提供100万令牌,为何还要费这么大劲?”
因为长上下文≠智能上下文。没有工程规范:
仍需为延迟和成本过度付费
模型会被无关令牌干扰
维护任务边界变得困难
我们需要的是智能上下文,而不仅仅是大上下文。
智能上下文(限定范围的多智能体+结构化记忆)
架构:
规划器→检索器→执行器智能体
短期记忆缓冲区(LangChain)
图内存(Neo4j)
上下文窗口从未超过5K令牌
性能:
提供连贯、详细且筛选条件正确的行程安排
在各智能体间保留过敏限制条件
成本:低(单个GPT-4o实例+轻度摘要)
延迟:约6秒
令牌使用量:平均3980个
标准RAG(单智能体+向量检索)
架构:
平铺式提示:用户查询+前3个相关文档(通过嵌入搜索)
除非在提示中手动添加,否则不记忆偏好
性能:
虚构了一个以海鲜为主题的度假村
途中丢失饮食偏好
仍生成行程,但缺乏个性化
延迟:约5秒
令牌使用量:平均6200个
长上下文LLM(Claude 3.5,200k提示词)
架构:
提示转储完整的10万份文档+用户查询+所有过往偏好
无筛选,无智能体模块化
性能:
由于上下文足够大,无幻觉
包含无关细节(天气模式、邮轮套餐)
高延迟和成本
延迟:约17秒(Claude 3.5)
令牌成本:约为Voyager的8倍
结果总结
指标 | 多智能体+作用域内存 | 标准RAG | 长上下文(Claude) |
---|---|---|---|
准确性 | ✅ 高(正确、限定范围) | ⚠️ 中等 | ✅ 高 |
个性化 | ✅ 优秀 | ❌ 弱 | ⚠️ 通用 |
令牌效率 | ✅ 高效(~4k) | ⚠️ 中等(~6k) | ❌ 昂贵(~25k+) |
延迟 | ✅ 快 | ✅ 快 | ❌ 慢 |
幻觉风险 | ✅ 低 | ⚠️ 中等 | ✅ 低 |
模块化与控制 | ✅ 高(智能体&内存) | ❌ 低 | ❌ 无 |
结论:智能上下文制胜
虽然像Claude 3.5这样的长上下文模型是出色的工具,但它们并不能消除良好的上下文工程的必要性。它们使检索更容易,但:
仍需要有选择地总结
仍然难以分离无关信息
成本高昂、速度缓慢,且缺乏任务特定的模块化
智能体——借助图记忆、作用域提示和运行时便签本——提供了一条更可靠、可复用且经济高效的前进路径。
第5章:融合图、向量与结构——可扩展的内存架构
1. 图检索增强生成(Graph-RAG):为何单纯的向量搜索不够
向量数据库(如FAISS、Qdrant、Weaviate)能提供很好的语义匹配,但缺乏推理结构。假设用户提问:“果阿附近有哪三家素食度假村支持家庭入住,且每晚费用低于15000卢比?”向量RAG可能会检索出10条半相关的段落,其中有些重复,然后指望GPT-4o神奇地整合出答案。
而理想的系统应:
检索高相似度文档
使用图查询(
budget
、diet
、location
)反复核对约束条件仅将结构化结果输入LLM
这就是图检索增强生成(Graph-RAG)。
实现:混合图+向量检索
def graph_query(user_prefs):
with graph.session() as s:
results = s.run("""
MATCH (r:Resort)
WHERE r.veg_friendly = true AND r.price < 15000 AND r.location CONTAINS 'Goa'
RETURN r.name AS name, r.rating AS rating, r.price AS price
LIMIT 3
""")
return [dict(r) for r in results]
# 同时,为答案添加语义丰富性
retriever = vec_index.as_retriever(similarity_top_k=3)
sem_docs = retriever.retrieve("Best veg-friendly family beach resorts in Goa")
# 最终上下文
structured_facts = graph_query(user_prefs)
sem_text = "\n\n".join([d.page_content for d in sem_docs])
final_prompt = f"""
Structured filters:
{structured_facts}
Additional references:
{sem_text}
Compose a list of 3 resorts with reasoning.
"""
这种方法兼具精确性和丰富性,是确定性与模糊性语境的完美融合。
2. 结构化+语义记忆融合
想象你正在追踪:
结构化数据:过往预订记录、预算趋势、航空公司会员等级
非结构化数据:电子邮件往来、酒店评论、用户笔记
你需要一个能够将表格、图表和嵌入内容合并到统一上下文界面的系统,该界面可根据任务动态选择。这就是记忆融合技术的用武之地:
对表格事实使用关系查询(SQL/Postgres)
使用图形数据库(Neo4j)处理关系
使用向量数据库(Qdrant)进行开放文本洞察
使用提示组合器合并并格式化LLM输入
🧠 3. DSPy:内存感知提示编译
DSPy 是斯坦福大学开发的框架,旨在编译LLM应用程序(类似编译器构建二进制文件)。它:
分析哪些输入重要
优化检索哪些记忆
随时间学习最有效的提示格式
核心特性:
声明式模块(
Generate
、Select
、Chain
)自动编译+自我调试
与OpenAI、Claude等集成
from dsp.modules.generate import Generate
from dsp.program import DSPyProgram
class ResortRecommender(DSPyProgram):
def __init__(self):
self.step = Generate(
instructions="Recommend resorts using structured data and retrieved context.",
inputs=["facts", "retrieved"],
outputs=["recommendation"]
)
def forward(self, facts, retrieved):
return self.step(facts=facts, retrieved=retrieved)
在实际示例上运行.compile()
,DSPy会优化:
使用哪种记忆
如何构建提示
每个部分分配多少令牌
DSPy不仅有助于上下文工程,还能学习如何为你完成这项工作。
4. 架构:统一内存栈
最终的记忆感知LLM架构如下:
┌────────────────────┐
│ User Query │
└────────┬───────────┘
│
┌────────────────────────────────────────┐
│ Context Engineering Layer │
│ - Prompt Builders │
│ - DSPy Compiler │
└────────┬────────────┬──────────────────┘
│ │ ┌──────────────────┘
└─────────────┐ ▼ ▼
┌────────────┐ ┌──────────────┐
│ Vector RAG │ ←→ semantic search │ Graph DB │ ←→ relationships
└────────────┘ └──────────────┘
▼ ▼
┌────────────────┐ ┌────────────────────┐
│ LLM Context │ ←─ prompt composed ─ │ Structured (SQL) │
└────────────────┘ └────────────────────┘
所有内容都流入精确、模块化、令牌高效的上下文窗口。
总结:智能扩展内存
智能AI的关键不在于更大的模型,而在于更智能的上下文。我们已证明:
为何图+向量比单纯嵌入更优
结构化记忆与语义记忆如何共存
DSPy等工具如何自动化上下文优化
如何构建低延迟、个性化、无幻觉的系统
第6章:多智能体LLM系统中的共享vs隔离内存
随着基于LLM的系统从单个机器人发展为协作智能体团队,我们面临新问题:智能体如何管理共享知识?
它们应该:
共享单一全局内存(黑板模型)?
每个角色维护完全隔离的内存(作用域上下文)?
像系统线程一样异步同步内存?
这是多智能体上下文编排的核心。
权衡:共享上下文vs限定作用域的上下文
类型 | 优点 | 缺点 |
---|---|---|
共享内存 | 协调简单,全局知识 | 令牌膨胀风险,上下文干扰 |
隔离内存 | 封装逻辑,模块化智能体 | 协调难度大,需要消息传递 |
智能系统需兼顾两者:独立的局部上下文+共享的全局便签,如同人类既保留个人笔记,又维护项目文档。
构建:具有共享内存的CrewAI风格团队
我们将实现类似CrewAI架构的简化版本,其中智能体:
拥有个人角色
共享黑板暂存区
通过结构化状态更新通信
class TeamBlackboard:
def __init__(self):
self.entries = []
def post(self, agent, msg):
self.entries.append(f"{agent}: {msg}")
def view(self, limit=4):
return "\n".join(self.entries[-limit:])
定义智能体角色
class PlannerAgent:
def __init__(self, llm): self.llm = llm
def act(self, goal, board):
prompt = f"""
You are a planning agent. Goal: {goal}
Blackboard so far:
{board.view()}
Plan the steps needed and post them.
"""
plan = self.llm.invoke(prompt).content
board.post("Planner", plan)
class ResearcherAgent:
def __init__(self, llm, retriever):
self.llm = llm
self.retriever = retriever
def act(self, goal, board):
prompt = f"""
You are a research agent. Goal: {goal}
Recent updates:
{board.view()}
Search and summarize relevant findings.
"""
docs = self.retriever.retrieve(goal)
content = "\n\n".join([d.page_content for d in docs])
summary = self.llm.invoke(f"Summarize:\n{content}").content
board.post("Researcher", summary)
最终执行者
class ExecutorAgent:
def __init__(self, llm): self.llm = llm
def act(self, board):
context = board.view()
prompt = f"""
You are the final agent. Here's the shared memory:
{context}
Compose a final report or action.
"""
output = self.llm.invoke(prompt).content
board.post("Executor", output)
return output
团队执行
board = TeamBlackboard()
goal = "Find and plan a vegetarian family trip to Goa under ₹80,000"
planner = PlannerAgent(llm)
researcher = ResearcherAgent(llm, retriever=vec_index.as_retriever())
executor = ExecutorAgent(llm)
planner.act(goal, board)
researcher.act(goal, board)
final_output = executor.act(board)
print("🚀 Final Output:\n", final_output)
作用域内存:智能体何时不应看到所有信息
在某些任务中,智能体需要隔离记忆:
自主发展思维链的智能体
安全系统(如法务智能体无法查看财务数据)
需要向前滚动内存的长流程
LangGraph和AutoGen等框架支持作用域内存,可定义哪些内存流向哪个智能体。
LangGraph风格的作用域上下文(概念性)
nodes:
planner:
reads: global_mem
writes: plan_mem
researcher:
reads: [plan_mem, doc_vector_db]
writes: research_mem
executor:
reads: [plan_mem, research_mem]
writes: final_output
此图展示上下文如何在作用域通道间清晰移动——每个智能体只看到所需内容。
总结:构建团队记忆架构
用例 | 最佳实践 |
---|---|
共享上下文(CrewAI) | 黑板内存 |
作用域上下文(LangGraph) | 定向记忆图 |
混合(AutoGen) | 本地内存+共享通道 |
真正的工作从上下文开始
我们常将LLM视为神奇的智能引擎,但没有合适的上下文,即使最智能的模型也只是在盲目猜测。
本文中,我们了解到上下文工程不仅是将文本塞进令牌窗口,更关乎知识结构化、记忆管理,以及设计能像人类一样推理、适应和协作的系统。
上下文工程是LLM从“智能工具”进化为“智能伙伴”的关键桥梁——而这,才是AI开发真正的前沿。