位置:LLM 应用工程四层演进的第二层 调研日期:2026-05-01
Context Engineering(上下文工程) 在 2024 下半年由 Karpathy 在 YC AI School 演讲中正式命名,被 Anthropic [6] 定位为”提示词工程的自然演进”。其核心观察是:在 100K-1M 上下文窗口和 Agent 时代,决定模型表现的不再是”那一句 prompt”,而是 模型在推理这一刻能看到的全部信息——system prompt、对话历史、工具定义、检索文档、记忆、规划状态。
本章覆盖:
/compact)关键转折:生产级 LLM 应用的核心难度从”写 prompt”迁移到了”管 context”——这是 Agent(03)和驾驭工程(04)的前提。
“Context Engineering”(上下文工程)这个词在 2024 年下半年开始在社区流传,普遍归功于 Karpathy 在 YC AI School 的 Software Is Changing (Again) 演讲 [5]。他给的定义后来被反复引用:
“In every industrial-strength LLM app, context engineering is the delicate art and science of filling the context window with just the right information for the next step.” (在每一个工业级 LLM 应用里,上下文工程是把恰好正确的信息填进上下文窗口、为下一步推理做准备的精细艺术与科学。)
Anthropic 在 2025 年发布的官方文章 Effective Context Engineering for AI Agents [6] 进一步把它定位为”提示词工程的自然演进”:
「prompt engineering 关心的是你给模型说的那句话;context engineering 关心的是模型在推理那一刻能看到的所有信息——system prompt、对话历史、工具定义、检索到的文档、记忆、规划状态、约束与策略。」
LangChain 团队在 Context Engineering for Agents 博客 [7] 里给了一个常被引用的工程定义:
“Context Engineering 是设计动态系统的学科,这些系统在正确的时间、以正确的格式,向 LLM 提供正确的信息和工具,让模型有完成任务所需的一切。”
为什么”提示词工程”不够了?三个根本变化:
到 2026 年初,业界共识是:生产级 LLM 应用的核心难度从”写 prompt”迁移到了”管 context”。
要理解为什么”塞满窗口”是个坏主意,必须先理解上下文的三个物理约束:
回到 prompt-engineering.md §1.1:标准 attention 是 N² 复杂度。上下文从 32K 翻到 128K,注意力计算量是 16 倍而非 4 倍。对推理服务来说这意味着两件事:
每个 attention 层在推理时要维护 K 和 V 的缓存(避免重复计算)。一个 70B 模型在 128K 上下文时,KV cache 占用可达 40+ GB,比模型权重本身还大。这就是为什么 vLLM 的 PagedAttention 和 SGLang 的 RadixAttention 这类”管 KV cache”的技术成为推理服务器的核心竞争力(详见 harness-engineering.md §3.9.2)。
Liu 等人 2024 TACL 论文 Lost in the Middle: How Language Models Use Long Contexts [2] 系统验证了一个反直觉现象:把关键信息放在 prompt 开头或结尾,模型能很好地利用;放在中间,模型常常视而不见。召回率随位置呈 U 型曲线——开头有 primacy bias、结尾有 recency bias、中间是黑洞。
这条曲线在 GPT-3.5 / GPT-4 / Claude 上都验证过。即便 2025 年的 Gemini 1.5 Pro 在简单的 needle-in-a-haystack 测试上能拿到 99.7% 召回,但当任务变成”在 1M token 里找出 8 个相关事实并综合”,平均召回率掉到 ~60% [41]。
NVIDIA 等机构 2024 年发布的 RULER 基准 [12](What’s the Real Context Size of Your Long-Context Language Models?)把”长上下文能力”从 needle-in-a-haystack 的玩具测试升级为”折磨套件”:
RULER 暴露出的真相:几乎所有声称 100K+ 上下文的模型,在 RULER 上的”有效上下文”远小于宣称值。比如某模型宣称支持 200K,但 RULER 上 50K 后准确率就掉到 80% 以下,128K 时已经接近 random。
这把行业拉回了现实:long context 不是 RAG 的替代,而是补充。
Tianpan 在 Long-Context Models vs RAG: When the 1M-Token Window Is the Wrong Tool [41] 给了一个 2026 主流的决策框架:
| 维度 | 1M 上下文模型 | RAG |
|---|---|---|
| 延迟 | 30-60 倍慢(必须 prefill 整个窗口) | 检索 + 生成两阶段,但生成阶段输入小 |
| 每查询成本 | ~1250 倍贵 | 检索几乎免费,生成成本随相关 chunk 数 |
| 数据更新 | 每次查询都要重传 | 索引更新一次,所有查询受益 |
| 可解释性 | 黑箱 | 检索结果可审计、可引用 |
| 多事实召回 | ~60%(实战) | 配合 reranker 可达 90%+ |
| 真正适合长上下文的场景 | 单次深度分析(“读完这篇 200 页的 PDF 总结洞察”) | 知识库问答、客服、文档检索 |
实战指引:90% 的”知识检索”场景应该用 RAG,10% 的”深度分析单文档”才用长上下文。
RAG(Retrieval-Augmented Generation)是上下文工程最成熟的一支,已演进出完整的子学科。2025-2026 年的状态可以用四代演进概括 [43]:
下面按 RAG 流水线顺序拆解 2026 年的选型空间。
现实世界的文档是 PDF、PPT、扫描件、Excel、HTML 而不是纯文本。这一步常被忽略但影响极大:
pypdf /
pdfplumber(速度快,但表格 / 多栏排版易错)unstructured /
LlamaParse(带 layout 模型,能识别标题 / 段落 / 表格 /
列表)PaddleOCR /
Tesseract / EasyOCR(扫描件、图片)最佳实践:至少跑两套 parser 对比,再决定用哪个。表格 / 公式必须特别处理(转 markdown / LaTeX)。
切分策略对召回质量影响远大于嵌入模型的选择:
通用建议:chunk size 256-512 token、overlap 50-100 token,是大多数任务的 sweet spot。
2026 年 4 月 MTEB(Massive Text Embedding Benchmark)榜单 [13] 主要选手:
| 模型 | MTEB | 维度 | 价格 / 1M tok | 特点 |
|---|---|---|---|---|
| OpenAI text-embedding-3-large | 64.6 | 3072(可截短) | $0.13 | 老牌稳定 |
| Cohere embed-v4 | 65.2 | 1536 | $0.10 | 当前 MTEB 第一 |
| Voyage 3 large | ~66 | 1024/2048 | $0.18 | 平均比 OpenAI 高 ~10% |
| Google Gemini Embedding 2 | 64+ | 3072 | $0.006 | 多模态原生(文本/图/视频/音频/PDF),100+ 语言 |
| BGE-M3(BAAI) | 64+ | 1024 | 免费(开源) | 同模型支持 dense / sparse / multi-vector 三种检索 |
| Jina v5-text-small | 71.7(v2 榜) | 512 | 免费(Apache 2.0) | 677M 参数,最佳质量 / 体积比 |
2026 年的两个关键趋势:
中文场景实测:BGE-M3 是免费第一(开源 + 多语言强 + 同模型支持稀疏/稠密),Voyage 3 large 是付费第一(中文召回普遍比 OpenAI 高 10%+)。
2026 年生产部署的五大主流 [14]:pgvector / Qdrant / Weaviate / Milvus / LanceDB。
性能基准(2026,128 维 embedding,1M 向量量级)[14]:
| 数据库 | p99 延迟 | QPS(单节点) | 备注 |
|---|---|---|---|
| Qdrant | 2 ms | 12,000 | Rust 实现,元数据过滤最强 |
| FAISS(in-memory) | 3 ms | 15,000 | 单机性能王,无服务化能力 |
| Milvus | 5 ms | 8,000 | 分布式 + 高可用,2.5 版本混合检索 30x 快于 ES |
| Pinecone | 8 ms | 5,000 | 全托管,运维零负担 |
| Weaviate | 10 ms | 4,000 | 内置 GraphQL 与知识图能力 |
选型决策:
pgvector 0.9
版本性能跟上了,避免运营第二个数据库的复杂度向量检索(稠密)擅长语义相似度但不擅长精确关键词匹配(产品 ID、专有名词、错别字容忍)。生产级 RAG 几乎都是 hybrid search:
混合策略:用 RRF(Reciprocal Rank Fusion)合并稀疏与稠密的结果列表。简单且无需调参。
RRF 公式:
\[ \text{RRF}(d) = \sum_{r \in R} \frac{1}{k + r(d)} \]
其中 R 是各路检索器,r(d) 是文档 d 在该路检索器里的排名(1, 2, 3...),k 是平滑常数(默认 60,Cormack 等 2009)。直觉:排名越靠前贡献越大,但因为是倒数,差距收敛快,避免某一路霸榜。
RRF 调权(生产实践):默认 RRF 把所有检索器等权对待。如果想给稠密 / 稀疏不同权重:
def weighted_rrf(results_dict, weights, k=60):
# results_dict = {"dense": [doc1, doc2, ...], "sparse": [docA, docB, ...]}
# weights = {"dense": 0.7, "sparse": 0.3}
scores = defaultdict(float)
for retriever, docs in results_dict.items():
w = weights[retriever]
for rank, doc in enumerate(docs, start=1):
scores[doc.id] += w / (k + rank)
return sorted(scores.items(), key=lambda x: -x[1])
RRF vs 其他融合:
| 方法 | 是否需调参 | 跨检索器分数对齐 | 何时用 |
|---|---|---|---|
| RRF | 否(k=60 几乎万能) | 用 rank 不用 score,天然对齐 | 默认首选 |
| CombSUM / Linear | 是(要归一化分数) | 难(BM25 vs cosine 量纲不同) | 不推荐,除非已有校准 |
| WAND(Weighted AND) | 中 | 在 sparse 内部用,不跨融合 | Lucene / Elasticsearch 加速倒排 |
| Convex Combination + ML | 高(要 learning-to-rank) | 训练对齐 | 大规模、有点击数据时最优 |
实证:hybrid search + reranker 比 semantic-only 提升 +9.3 个百分点 MRR,配合 Cohere Rerank 准确率可以到 90%+。
第一阶段检索(embedding / BM25)召回 top-100 后,用更贵但更准的 cross-encoder 模型对 query-doc 对重排,取 top-5 给 LLM。这一步的 ROI 极高:
| Reranker | 类型 | ELO | 备注 |
|---|---|---|---|
| Zerank 2 | API | 1638 | 当前榜首 |
| Cohere Rerank v4 Pro | API | 1629 | 老牌 |
| Cohere Rerank 3 Nimble | API | — | 速度优化版本 |
| BGE-reranker-v2-m3 | 自托管 | — | 开源,效果接近 Cohere |
| FlashRank | 自托管 | — | 极轻量,毫秒级 |
| ColBERT v2 | 自托管 | — | 后期交互架构 |
| RankLLM(用 LLM 直接 rerank) | LLM | — | 最贵但解释性最好 |
top rerankers 比纯 embedding 提升 15-40% 精确率。如果只允许加一项 RAG 优化,加 reranker 通常 ROI 第一。
用户问的”今天天气咋样”和文档里写的”current weather conditions”语义相同但词汇不重叠。Query rewriting 在检索前先对 query 做处理:
怎么选(按 query 形态决策):
| query 形态 | 推荐方法 | 原因 |
|---|---|---|
| 短查询 / 关键词缺失 | HyDE / Query2Doc | 短 query 与文档 chunk 长度不匹配,扩成假文档可拉近 embedding 距离 |
| 多义词 / 同义词问题 | Multi-Query | 改写覆盖不同措辞,提升召回 |
| 涉及背景知识的窄问题 | Step-Back | 先检索宏观知识再聚焦细节 |
| 复合 / 比较 / 多跳 | Sub-Query Decomposition | 单次检索无法覆盖多个子主题 |
| 通用对话 + 代码 / 数字 | 不要重写 | LLM 重写可能改变关键术语 / 数字,反而召回掉 |
陷阱:
Microsoft GraphRAG(2024 开源)和 LightRAG / Nano-GraphRAG 等社区变体把检索从”向量相似”扩展到”知识图遍历”:
GraphRAG 在某些场景把检索精度推到 99%(金融合规、法律、医疗等关系密集的领域)[43]。
三者横向对比:
| 维度 | Self-RAG | CRAG | Agentic RAG |
|---|---|---|---|
| 决定何时检索 | 模型自己(特殊 token [Retrieve]) |
始终检索 | Agent 控制器决定 |
| 检索结果评判 | 模型自己([IsRel] 反思 token) |
独立 retrieval evaluator(小模型,分高/中/低) | LLM judge 或工具 |
| 失败兜底 | 不再检索 + 提示不确定 | 触发 web search 重检索 | 改写 query / 换数据源 / 多轮迭代 |
| 训练成本 | 高(需对反思 token 做 SFT) | 低(plug-and-play) | 0(运行时编排) |
| 延迟 | 中(一次推理多输出 token) | 高(始终检索 + 评估 + 可能 web) | 最高(可能多轮 loop) |
| 适用 | 自带知识充足的通用问答 | 知识库时新性差、需要 web 兜底 | 多跳推理、研究型问答、Deep Research |
| 2026 主流落地 | OpenAI gpt-4o web search 模式 | Perplexity / You.com 内部架构 | Claude / Cursor / Devin 的研究 agent |
实证:Self-RAG 在生产部署中能减少 25-40% 的”不必要检索”——因为模型学会了对自己已有知识的事情不去检索。Agentic RAG 在多跳问答(HotpotQA / MuSiQue)准确率比 vanilla RAG +15-25 个点,但 token 成本翻 3-10 倍。
详见 agent-engineering.md §2 范式演进、§5 框架对比。
图像 + 文本混合的索引方案:
Layout-aware chunking(关键工程点,被低估):
传统 RecursiveCharacterTextSplitter 对 PDF / Word 这类有版式的文档效果差 —— 表格被劈成两半、figure caption 与图分离、目录与正文混合。生产级方案:
| 工具 | 路线 | 强项 |
|---|---|---|
| Unstructured.io | 规则 + ML 元素分类 | 通用,HTML/PDF/PPT/Email 全覆盖 |
| LlamaParse | LlamaIndex 自家 LLM 解析 | Markdown 输出,表格保真度高 |
| PyMuPDF + pdfplumber | 底层 PDF SDK | 自定义 layout 规则,保留 bbox 坐标 |
| Docling(IBM 2024) | 开源结构化文档解析 | 表格 / 公式 / 图像 caption 关联 |
| MinerU(OpenDataLab 2024) | 中文 PDF 友好 | 中文学术论文 / 教材效果好 |
多模态 RAG 选型决策:
陷阱:多模态 embedding 在跨模态检索(用文字找图)效果远好于跨模态对齐(图与文字到底说的是不是一件事),生成阶段仍需 VLM 二次确认相关性。
把上面 10 个子节串起来,一个生产级 RAG pipeline 的最小可运行骨架:
# 1. 文档解析
from llama_parse import LlamaParse
parser = LlamaParse(result_type="markdown")
docs = parser.load_data("./knowledge_base/")
# 2. 切分
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=512, chunk_overlap=80,
separators=["\n\n", "\n", "。", "!", "?", ".", " "]
)
chunks = splitter.split_documents(docs)
# 3. 嵌入 + 入向量库
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
from langchain_qdrant import QdrantVectorStore
embeddings = HuggingFaceBgeEmbeddings(model_name="BAAI/bge-m3")
vectorstore = QdrantVectorStore.from_documents(
chunks, embeddings, url="http://localhost:6333", collection_name="kb"
)
# 4. 混合检索(dense + sparse + RRF)
from langchain.retrievers import EnsembleRetriever, BM25Retriever
bm25_retriever = BM25Retriever.from_documents(chunks); bm25_retriever.k = 20
dense_retriever = vectorstore.as_retriever(search_kwargs={"k": 20})
hybrid_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, dense_retriever], weights=[0.4, 0.6]
)
# 5. 查询改写(multi-query + step-back)
from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(model="claude-sonnet-4-6")
multi_q_retriever = MultiQueryRetriever.from_llm(retriever=hybrid_retriever, llm=llm)
# 6. 重排(Cohere Rerank 或 BGE-reranker)
from langchain.retrievers import ContextualCompressionRetriever
from langchain_cohere import CohereRerank
reranker = CohereRerank(model="rerank-v3.5", top_n=5)
final_retriever = ContextualCompressionRetriever(
base_retriever=multi_q_retriever, base_compressor=reranker
)
# 7. 生成(带 citation)
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个严谨的助手。仅根据给定 context 回答,每条事实必须标注 [doc_id]。"),
("human", "Context:\n{context}\n\n问题: {input}"),
])
combine_chain = create_stuff_documents_chain(llm, prompt)
rag_chain = create_retrieval_chain(final_retriever, combine_chain)
answer = rag_chain.invoke({"input": "Transformer 的 attention 复杂度是多少?"})这个骨架覆盖了 RAG 的 7 个核心步骤。生产化还需要加:
| 资源 | 类型 | 推荐用法 |
|---|---|---|
NirDiamant/RAG_Techniques |
高级技术 notebook 集 | 30+ 种 RAG 高级技术每个都有可跑 notebook,最全 |
infiniflow/ragflow |
开源 RAG 引擎 | 想要”开箱即用”的企业级 RAG 平台 |
HKUDS/LightRAG |
EMNLP 2025 论文 + 实现 | 简洁高效的 RAG 范式参考 |
Danielskry/Awesome-RAG |
awesome list | 找特定主题资源时的入口 |
lehoanglong95/rag-all-in-one |
全栈指南 | 想要按组件对照工具时 |
pguso/rag-from-scratch |
从零实现 | 想搞清”黑盒里到底发生了什么” |
microsoft/graphrag |
GraphRAG 官方 | 实体关系图 RAG 的参考实现 |
abhinav-kimothi/A-Simple-Guide-to-RAG |
入门书配套 | 完全新手入门 |
如果 RAG 解决”外部知识库怎么塞进 context”,记忆系统解决”agent 自身经验怎么塞进 context”。这两件事在工程上有相似的部分(都是检索 + 注入 prompt),但有本质差异:
| 维度 | RAG | 记忆系统 |
|---|---|---|
| 数据来源 | 外部静态文档(公司 wiki、PDF) | 历史对话、agent 行为、用户偏好 |
| 写入触发 | 索引时一次性 | 每次交互都可能触发增量写入 |
| 数据生命周期 | 跟着源文档走 | 有独立的”过期 / 淘汰 / 摘要”逻辑 |
| 隔离粒度 | 全局或部门级 | 必须 per-user / per-agent / per-session |
| 时效性 | 通常分钟到天 | 可能秒级(“用户刚才说的”) |
| 一致性 | 强(一份 ground truth) | 弱(事实会变、有矛盾) |
“上下文窗口已经 1 M 了,还需要记忆系统吗?” 是个真实问题。三个理由:
记忆系统本质上是 “把过去的全部经验变成一个可检索的小数据库,每次推理只检索最相关的几条注入 context”——把”全部输入到模型”换成”模型按需检索”。
借自认知心理学的分类,每一类对应不同的工程实现:
| 记忆类型 | 认知含义 | 工程实现 | 存储后端 | 写入时机 | 检索时机 |
|---|---|---|---|---|---|
| Working Memory(工作) | 当前任务的”草稿纸” | 当前 context window | LLM 内部 KV cache | 每个 token | 每个 token |
| Short-term(短期) | 最近 N 轮对话 | 滚动 message buffer | 内存 / Redis | 每轮 | 每轮全注入 |
| Episodic(情景) | “什么时候发生了什么” | 带时间戳的对话片段 | 向量库 + 时间索引 | 会话结束 / 重要事件 | 按主题 + 时间窗 |
| Semantic(语义) | 关于世界 / 用户的稳定事实 | KV pair 或图节点 | 图库 / KV 库 / SQL | 抽取出新事实时 | 按实体名 |
| Procedural(程序) | “这类任务怎么做” | 可执行 skill / 流程脚本 | 文件系统 / skill registry | 任务成功后总结 | 按任务类型 |
| Long-term(长期) | 跨 session 的所有上述类型 | 上面几种的合集 | 通常多种后端组合 | 异步 / 后台 worker | 按需 |
关键洞察:一个生产级 agent 的”记忆”通常不是单一系统,而是上面 6 类的不同子集组合。Letta(MemGPT)把短期 + 语义放 in-context(Core Memory),把情景放向量库(Recall Memory),把外部知识放另一个向量库(Archival Memory)——这是 OS-inspired 设计的经典三层。
任何记忆系统拆开都是这 5 个组件:
┌──────────────────────────┐
│ Conversation / Action │
│ (raw events) │
└────────────┬─────────────┘
│
┌─────────────────────▼──────────────────────┐
│ ① Extractor(抽取器) │
│ LLM 把原始事件抽成结构化"记忆事实" │
│ e.g. "用户偏好暗色主题" │
└─────────────────────┬──────────────────────┘
│
┌─────────────────────▼──────────────────────┐
│ ② Writer(写入器) │
│ 去重 / merge / conflict resolution │
│ embed + 入库 │
└─────────────────────┬──────────────────────┘
│
┌─────────▼─────────┐
│ Storage Backend │
│ vector / graph / │
│ KV / SQL / file │
└─────────┬─────────┘
│
┌─────────────────────▼──────────────────────┐
│ ③ Retriever(检索器) │
│ query → top-K 相关记忆 │
│ hybrid: semantic + keyword + graph │
└─────────────────────┬──────────────────────┘
│
┌─────────────────────▼──────────────────────┐
│ ④ Forgetter(遗忘器) │
│ 按 LRU / 重要性 / 时间衰减 淘汰 │
└─────────────────────┬──────────────────────┘
│
┌─────────────────────▼──────────────────────┐
│ ⑤ Compressor(压缩器) │
│ 老记忆摘要、冲突合并、知识图重建 │
└────────────────────────────────────────────┘
每个组件都有独立的工程决策点。下面 §2.5.4-2.5.8 逐个展开。
记忆系统的存储后端不是”选一个”而是”选哪几个组合”。常见选项:
| 后端 | 强项 | 弱项 | 典型用法 |
|---|---|---|---|
| 向量库(Qdrant / Weaviate / Pinecone / pgvector) | 语义相似检索 | 不擅长精确匹配 / 关系查询 | 情景记忆主存储 |
| 图库(Neo4j / FalkorDB / Memgraph) | 实体关系 / 多跳查询 / 时序 | 部署复杂 / 检索慢 | 语义记忆 + 关系 + 时序 |
| KV 库(Redis / DynamoDB) | 极低延迟 / 简单 | 无语义检索 | 短期 working memory + 缓存 |
| 关系型(Postgres + pgvector / Tiger Data) | 事务性 / SQL filter / 一站式 | 大规模向量索引调优麻烦 | 中小规模一体化 |
| 文件系统(Anthropic Memory Tool) | 简单 / 可审计 / agent 自管 | 检索能力依赖 agent 自己 grep | Claude 生态原生 |
| 专用记忆 SaaS(Mem0 Cloud / Zep Cloud / Supermemory) | 零运维 | 锁定 / 数据出域 | 创业 MVP |
你的记忆主体是什么?
│
├─ 主要是用户偏好 / 实体事实 → 语义记忆为主
│ ├─ 关系密集("A 是 B 的同事"、"X 公司 2024 收购 Y")→ Zep(Graphiti 图)
│ └─ 关系简单 → Mem0(vector + 可选 graph)
│
├─ 主要是对话历史 + 长跑任务 → 情景记忆为主 + agent 自管
│ └─ → Letta(Core + Recall + Archival 三层)
│
├─ 主要是 agent 行为可审计 / 可回滚
│ └─ → Anthropic Memory Tool(filesystem,可 git 管理)
│
└─ 已用 Redis / Postgres,不想加新栈
├─ 用 Redis Agent Memory Server
└─ 用 Postgres + pgvector + Tiger
记忆不是简单地把对话原文存下来,而是经过 LLM 抽取的”事实”。Mem0 v2 的写入算法(论文 [19])拆解:
1. Retrieve 相关记忆(top-10 semantically similar)
2. LLM 单次调用抽取新事实(Add-only, 不 Update / Delete)
└─ Prompt: "Given new conversation + existing memories,
extract NEW facts that should be remembered.
Return JSON list."
3. Embed + MD5 hash 去重
4. 实体抽取 + 链接(用于 graph memory)
5. 批量写入 vector store + entity collection
关键设计选择:
core_memory_replace 工具)写入伪代码(自实现版):
def write_memory(user_id, conversation_chunk):
# 1. 检索相关历史
relevant = vector_store.search(
embed(conversation_chunk), filter={"user_id": user_id}, k=10
)
# 2. LLM 抽取
extracted = llm.extract_facts(
prompt=f"对话:{conversation_chunk}\n现有记忆:{relevant}\n新事实?",
schema={"facts": [{"text": str, "category": str, "confidence": float}]},
)
# 3. 过滤低置信度
facts = [f for f in extracted["facts"] if f["confidence"] > 0.7]
# 4. 去重
new_facts = []
for f in facts:
h = hashlib.md5(f["text"].encode()).hexdigest()
if not vector_store.exists(hash=h, user_id=user_id):
new_facts.append({**f, "hash": h, "user_id": user_id, "ts": now()})
# 5. 写入
vector_store.add_batch(new_facts)仅用 embedding 检索召回会漏,2026 年生产级记忆系统都用 multi-signal fusion——和 RAG 的混合检索是同一思路。Mem0 v2 的检索:
Query → preprocess(lemmatize + entity 抽取)
↓
┌──┴──────────────────────────┬────────────────────────┐
▼ ▼ ▼
Semantic(vector cosine) BM25 keyword Entity match
│ │ │
└──────────────┬──────────────┴────────────────────────┘
▼
Score Fusion (RRF or weighted)
▼
Top-K Memories → 注入 system prompt
进阶技术:
exp(-λ·age),新记忆优先记忆爆炸是真实问题。MaRS 架构(arXiv 2603.07670)形式化了 6 种主流淘汰策略:
| 策略 | 公式 / 规则 | 适用 |
|---|---|---|
| FIFO | 按插入时间 | 最简单,几乎不用(删了重要老事实) |
| LRU | 按最后访问时间 | desktop agent 默认起点 |
| Priority Decay | score = w₁·prior + w₂·recency + w₃·frequency |
通用最佳 baseline |
| Reflection-Summary | 老的 N 条 → LLM 摘要成 1 条 | 配合 LRU 用,长跑 agent 必备 |
| Random-Drop | 随机删 | 仅作 baseline 对比 |
| Hybrid | LRU + Priority Decay + Summary 联用 | 生产推荐 |
实战配方(Letta 与 Mem0 都接近这个思路):
def evict_if_needed(memories, max_count=10000):
if len(memories) <= max_count:
return memories
# 1. 计算每条记忆的"保留分数"
for m in memories:
m.keep_score = (
m.importance * 0.4 # 写入时打的重要性
+ recency_score(m.last_access) * 0.3 # 越近越高
+ access_frequency(m) * 0.2 # 被引用次数
- age_penalty(m.created) * 0.1 # 越老越低
)
# 2. 按分数排序,淘汰末尾 N 条
memories.sort(key=lambda m: m.keep_score, reverse=True)
to_evict = memories[max_count:]
# 3. 老记忆不直接删,先尝试摘要
summary_chunks = group_by_topic(to_evict, n=10) # 10 条一组
for chunk in summary_chunks:
summary = llm.summarize(chunk)
memories.append(Memory(
text=summary, importance=avg([m.importance for m in chunk]),
ts=min([m.created for m in chunk]),
sources=[m.id for m in chunk],
))
# 4. 真正删除原始 N 条
delete_batch([m.id for m in to_evict])
return memories经验法则:
架构:5 个支柱(论文 [19] 与 Mem0 docs 总结):
注:上述 5 项是按工程职责整理的检查清单,不是 Mem0 / Letta / Zep 任何一家的官方架构图。Mem0 论文实际描述的是 Add/Update/Delete/NoOp 四种操作 + LLM 抽取 pipeline;Letta 用 Core/Recall/Archival 三层;Zep 用 Episodic/Entity/Edge 三类节点。
性能(v2 算法,按 Mem0 论文 arXiv:2504.19413 报告口径):在 LoCoMo / LongMemEval 等长对话记忆 benchmark 上显著优于 RAG / full-context baseline(具体提升幅度按子任务和指标维度差异较大,详见原论文表 4-5);抽取延迟降一半。
完整代码示例:
from mem0 import Memory
# 初始化(用 Qdrant + Neo4j + OpenAI)
config = {
"vector_store": {
"provider": "qdrant",
"config": {"host": "localhost", "port": 6333},
},
"graph_store": { # 可选
"provider": "neo4j",
"config": {"url": "bolt://localhost:7687",
"username": "neo4j", "password": "..."},
},
"llm": {
"provider": "openai",
"config": {"model": "gpt-4o-mini"},
},
"embedder": {
"provider": "openai",
"config": {"model": "text-embedding-3-small"},
},
}
m = Memory.from_config(config)
# 写入:从对话自动抽事实
m.add(
messages=[
{"role": "user", "content": "我是 Liang,住北京,做 AI 工程师,我喜欢冷咖啡"},
{"role": "assistant", "content": "记下了"},
],
user_id="liang_001",
)
# Mem0 自动:提取 ["用户名是 Liang", "住北京", "职业 AI 工程师", "偏好冷咖啡"]
# 检索
memories = m.search(query="他喝什么咖啡?", user_id="liang_001", limit=3)
# [{"memory": "用户偏好冷咖啡", "score": 0.89}, ...]
# 注入下次 prompt
context = "\n".join([f"- {m['memory']}" for m in memories])
prompt = f"已知用户信息:\n{context}\n\n用户:今天给我推荐什么饮料?"AWS 推荐生产架构:Mem0 + ElastiCache for Valkey(短期 + 缓存)+ Neptune Analytics(图)+ OpenSearch(向量)。
适合:通用 chatbot、客服、个人助手——“5 行代码加记忆”的场景。
架构:把 LLM 当 OS,记忆分三层(核心创新):
| 层 | 名称 | 位置 | 容量 | 谁能改 |
|---|---|---|---|---|
| L1 | Core Memory(核心) | In-context(system prompt 里) | 固定(如 4 KB) | agent 自己用 tool |
| L2 | Recall Memory(回忆) | 外部 DB | 完整对话历史 | 自动 |
| L3 | Archival Memory(档案) | 向量库 | 无限 | agent 自己用 tool |
Core Memory
是关键创新——它不是数据库而是system prompt
的一部分。Agent 可以用 core_memory_append /
core_memory_replace
工具像编辑文件一样改它,下一次推理这些内容自动出现在 context
头部。这等价于让 agent “持续编辑自己的人设”。
6 个内置工具(agent 自管内存):
| 工具 | 作用 |
|---|---|
send_message |
给用户发消息 |
core_memory_append |
往 in-context 内存追加新 fact |
core_memory_replace |
替换 in-context 内存(修正错误 / 更新偏好) |
conversation_search |
搜历史对话(recall memory) |
archival_memory_insert |
写入长期档案 |
archival_memory_search |
检索长期档案 |
完整代码:
from letta_client import Letta
client = Letta(base_url="http://localhost:8283") # 自托管 server
# 创建 agent,定义 core memory blocks
agent = client.agents.create(
name="my_assistant",
memory_blocks=[
{"label": "human", "value": "用户的名字、偏好、当前正在做的事"},
{"label": "persona", "value": "你是一个简洁、礼貌的助理"},
],
model="anthropic/claude-sonnet-4-6",
embedding="openai/text-embedding-3-small",
)
# 对话(多轮,每次 Letta 自动管理 context)
response = client.agents.messages.create(
agent_id=agent.id,
messages=[{"role": "user", "content": "我叫 Liang,下周要面试"}],
)
# Letta 内部:agent 调用 core_memory_append("human", "用户叫 Liang,下周面试")
# 关掉、重启、明天继续——agent 还记得 Liang 与他的面试
response = client.agents.messages.create(
agent_id=agent.id,
messages=[{"role": "user", "content": "帮我准备一下"}],
)
# agent 看 core memory → 知道 Liang 要面试 → 自动反馈适合:长跑 agent(数天 / 数周)、需要”持久身份”的产品(个性化助理 / AI 教练)、Anthropic Memory Tool 之外的 OSS 替代。
核心创新:每条事实带 双时间轴:
架构:3 类节点 + 边
┌────────────────┐ rel ┌────────────────┐
│ Entity Node │◀─────▶│ Entity Node │
│ Alice │ edge │ TechCorp │
└───────┬────────┘ └────────────────┘
│ source ▲
▼ │ joined
┌────────────────┐ │
│ Episodic Node │──────────────────┘
│ "对话片段" │ derives
│ T=2024-03-15 │
└────────────────┘
关键能力:当事实变化时(“Alice 2024-06 离职”),Zep
不删除老事实,而是给老 edge 标
valid_to=2024-06,新 edge 标
valid_from=2024-06。查询时可问:
valid_to=NULL 的
edgevalid_from <= 2024-04 <= valid_to 的 edgeHybrid 检索:semantic embedding + BM25 + 直接图遍历——无需 LLM 调用,纯索引和图查询,延迟 100-500 ms。
最小代码(Graphiti SDK):
from graphiti_core import Graphiti
from graphiti_core.nodes import EpisodeType
g = Graphiti(uri="bolt://localhost:7687", user="neo4j", password="...")
await g.build_indices_and_constraints()
# 写入(episode = 一段对话或事件)
await g.add_episode(
name="meeting_2026_03",
episode_body="Alice 离开 TechCorp,加入 Anthropic 做 research engineer",
source=EpisodeType.message,
reference_time=datetime.now(),
)
# Graphiti 自动:抽 entities → 建 nodes → 抽 relations → 建 edges → invalid 旧 edges
# 查询
results = await g.search(query="Alice 现在在哪工作?", limit=5)
# [{"fact": "Alice 现在在 Anthropic", "valid_from": "2026-03", "valid_to": None}, ...]适合:用户状态会变的场景——CRM、客服、医疗病史、合规审计、法律事实库。
2026-04-23
公测发布。设计哲学:把记忆当文件,让 Claude
用熟悉的 bash + code execution 工具读写。
架构:
Claude Agent
│
├─ tool: bash (read/write/grep files)
├─ tool: code execution
└─ tool: memory (mount filesystem)
│
▼
┌───────────────────┐
│ /memories/ │
│ ├ liang.md │ ← user profile
│ ├ tasks/ │
│ │ ├ today.md │
│ │ └ done.md │
│ └ playbooks/ │
│ └ deploy.md │ ← procedural
└───────────────────┘
(any backend:
local fs, S3,
encrypted, ...)
完整代码(Python SDK,public beta):
from anthropic import Anthropic
from anthropic.lib.tools import BetaAbstractMemoryTool
# 选项 1:默认本地 filesystem 后端
client = Anthropic()
response = client.beta.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
tools=[
{"type": "memory_2026_04_23", "name": "memory"},
{"type": "bash_2026_04_23", "name": "bash"},
],
messages=[{"role": "user", "content": "记一下:我下周三要去机场,记得提醒我提前 3 小时出发"}],
extra_headers={"anthropic-beta": "memory-2026-04-23"},
)
# Claude 自动:bash > echo "trip_2026_03_20 ..." > /memories/calendar.md
# 选项 2:自定义后端(S3 / 加密 / 数据库)
class S3MemoryTool(BetaAbstractMemoryTool):
def __init__(self, bucket):
self.bucket = bucket
async def list_files(self, path: str) -> list[str]:
return list_s3_keys(self.bucket, path)
async def read_file(self, path: str) -> str:
return s3_get(self.bucket, path)
async def write_file(self, path: str, content: str):
s3_put(self.bucket, path, content)
# ... delete / move / search 等早期客户案例(Anthropic 官方公布):
适合:已绑 Claude 生态、需要”agent 像程序员一样管自己的笔记本”、需要审计/版本控制(memory 文件可入 git)的场景。
| 维度 | Mem0 | Letta | Zep / Graphiti | Anthropic Memory Tool |
|---|---|---|---|---|
| 数据模型 | facts (vector + graph) | core / recall / archival | temporal KG | filesystem |
| Agent 主动性 | passive(auto extract) | agent 自管 tool | passive | agent 自管 tool |
| 多模型支持 | 16+ LLM | 任意 | 任意 | 仅 Claude |
| 后端选择 | 24+ vector + graph | Postgres / 内置 | Neo4j / FalkorDB | filesystem 任意 |
| 时序追踪 | 弱(基于 timestamp) | 弱 | 强(双时间轴) | 文件 mtime |
| 关系建模 | 中(可选 graph) | 弱 | 强(图原生) | 弱 |
| 上手成本 | 5 行 | 50 行 | 30 行 | 10 行 |
| 开源 | ✓ | ✓ | ✓(Graphiti) | × |
| 中文支持 | 配中文 embedder 即可 | 同 | 同 | 原生 |
| 论文 / 数字 | LoCoMo +20pt | MemGPT 论文 | arXiv 2501.13956 | Netflix 错误率 -97% |
绝大多数 SaaS 必踩的坑:记忆系统跨用户泄露。两层防御:
# 错误示范(会跨用户混)
m.add(messages=msgs) # ❌ 没 user_id
# 正确:强制 user_id 必填
m.add(messages=msgs, user_id=request.user.id)
m.search(query=q, user_id=request.user.id, ...) # 检索也必须带
# 强制约束(包一层):
class TenantSafeMemory:
def __init__(self, m, user_id):
self.m = m; self.uid = user_id
def add(self, messages, **kw):
return self.m.add(messages=messages, user_id=self.uid, **kw)
def search(self, query, **kw):
return self.m.search(query=query, user_id=self.uid, **kw)| 方案 | 隔离强度 | 性能 | 适合规模 |
|---|---|---|---|
共享 collection + user_id filter |
弱(依赖应用层) | 好 | < 1k 用户 |
| 每用户一个 collection | 强 | 中(collection 数有上限) | 1k - 10k |
| 每用户一个 namespace(Pinecone / Turbopuffer) | 强 | 好 | 10k - 1M+ |
| 每租户一个独立 DB instance | 最强(物理隔离) | 中 | 大客户 / 合规 |
Turbopuffer 等专门为多租户设计的向量库可以撑百万 namespace,单租户内部数据严格隔离。
# 审计:所有跨用户的访问立刻报警
@audit_log(level="critical")
def admin_search_all(query):
return vector_store.search(query=query, no_user_filter=True)
# GDPR "被遗忘权"
def forget_user(user_id):
vector_store.delete_all(user_id=user_id)
graph_store.delete_subgraph(user_id=user_id)
redis.delete_pattern(f"user:{user_id}:*")
audit_log(f"user {user_id} memory wiped at {now()}")实战中记忆和 RAG 经常并存,不冲突:
User Query
│
├──▶ RAG(公司文档 / 产品手册 / 政策) → 通用知识
│
└──▶ Memory(用户偏好 / 历史交互 / agent 经验) → 个性化
两路检索结果合并 → 注入 system prompt → LLM
典型生产 prompt 结构:
你是 ACME 公司客服。
# 用户档案(来自 Memory)
- 姓名:Liang
- 历史订单:3 单(2 单已完成)
- 偏好:电邮通知优先
# 相关知识(来自 RAG)
[政策片段] 30 天内可全额退款
[FAQ 片段] 退款流程通常 3-5 工作日
# 当前对话
用户:我想退订单 #12345
vector memory / KV memory 解决”事实”,但长跑 coding agent / research agent 还需要一种结构化、人类可读、可 diff 的”工作进度”记忆 —— 这就是 progress file(Augment Code、Claude Code、Devin 都在用的事实标准)。
典型 schema(Markdown,常见命名 PROGRESS.md / CONTEXT.md / .agent/state.md):
---
task_id: refactor-auth-2026-05-03
started: 2026-05-03T09:12Z
last_update: 2026-05-03T11:48Z
status: in_progress
parent_branch: main
worktree: /Users/x/dev/repo/.worktrees/refactor-auth
---
## Goal
迁移 session-based auth 到 JWT,保持向前兼容 30 天
## Decisions made
- [2026-05-03 09:30] 采用 RS256 而非 HS256(多服务部署需要公钥分发)— 见 ADR-007
- [2026-05-03 10:15] 旧 session cookie 保留 30 天,双写策略
## Files touched
- `src/auth/jwt.ts` (new)
- `src/auth/middleware.ts` (modified, +120/-45)
- `src/auth/session.ts` (deprecated, kept for 30d)
## Open questions
- [ ] refresh token 是否需要黑名单?需问 @security-team
- [ ] mobile 客户端最低支持版本是否覆盖 JWT 解析?
## Next steps
1. 写 jwt.test.ts 单元测试覆盖签 / 验 / 过期 / tampering
2. 在 staging 跑 24h 双写,看 metric
3. 文档 docs/auth-migration.md
为什么这种格式而不是 vector memory:
PROGRESS.md → 立刻知道上次到哪关键工程点:
git status + git log -5 + cat PROGRESS.md,三件套是恢复”我是谁、上次到哪、下一步”的最低成本方式参考:Anthropic Memory Tool、Augment Code 工程博客、Claude Code 的 .claude/ 目录、Cursor 的 .cursorrules、Codename Goose 的 goose hints。
| 失败模式 | 表象 | 对策 |
|---|---|---|
| 记忆爆炸 | 记 1000 条后检索全是噪声 | LRU + 摘要(§2.5.7) |
| 过期记忆 | “我喜欢冷咖啡”过期但仍生效 | Zep 时序 / 写入时检测冲突 / 用户主动覆盖 |
| 幻觉记忆 | LLM 把没说过的事抽成事实 | 抽取时加 confidence 阈值 + 用户可审计 |
| 跨用户泄露 | A 的偏好被注入 B 的 prompt | 强制 user_id(§2.5.9) |
| 冷启动 | 新用户记忆为空,agent 显得”陌生” | 显式 onboarding + 通用画像兜底 |
| 隐私 / 合规 | GDPR / CCPA 要求删除用户数据 | forget_user API 全栈级联删除 |
| 抽取成本 | 每轮调 LLM 抽事实,token 成本翻倍 | importance gate 跳过无关对话 + 批处理 N 轮 |
| 检索召回低 | LLM 看不到关键事实 | 加 BM25 + entity match(§2.5.6 multi-signal) |
| 记忆腐坏(memory corruption) | 见下面专栏 | 定期一致性检查 + ground truth 校准 + ttl |
”记忆腐坏”是 stateful agent 部署超过几周后必然出现的失败模式,但很少被早期工程团队预防。Anthropic / Letta / Mem0 团队公开过的 4 类典型 corruption:
| 腐坏类型 | 例子 | 根因 | 防御 |
|---|---|---|---|
| 过期事实(Stale fact) | “用户在杭州” → 三个月后已搬到上海,但旧记忆仍在 top-K | 没有 TTL、没有”事实新鲜度”的写入时检测 | Zep 风格 valid_from / valid_to;写入时检测同实体的旧事实并标 invalid |
| 矛盾事实(Contradiction) | 一条说”用户素食”,另一条说”用户喜欢牛排” | 多 session 抽取没有跨 session 一致性检查 | 写入前跑”该用户已有事实 + 新事实”的矛盾检测 LLM;或定期 reconciliation job |
| 幻觉事实(Hallucinated fact) | LLM 抽取时从对话里”推理”出用户没明说的偏好 | 抽取 prompt 没限定”只抽显式陈述” | 抽取时返回 evidence span(原对话引用);无 evidence 不落库 |
| 人格漂移(Persona drift) | 长期 self-update memory 导致 agent 自我描述与初始 system prompt 严重不一致 | agent 自管 memory + 没有 freeze section | core memory 区分 immutable / mutable 段;mutable 段定期 reset 到 baseline |
防御工程实践(按 ROI 排序):
created_at / valid_from / valid_to,检索时按当前时间过滤(fact, evidence_quote, confidence),confidence < 0.7 不落库| 资源 | 类型 | 推荐用法 |
|---|---|---|
mem0ai/mem0 |
官方仓库 | 通用记忆层,5 行接入 |
letta-ai/letta |
官方仓库 | OS-inspired stateful agent |
getzep/graphiti |
官方仓库 | Temporal KG 引擎(Zep 内核) |
getzep/zep |
官方仓库 | 完整 Zep 服务(基于 Graphiti) |
redis/agent-memory-server |
官方仓库 | Redis 一站式 agent memory |
topoteretes/cognee |
官方仓库 | 图原生 + ontology |
supermemoryai/supermemory |
官方仓库 | 跨厂商记忆 SDK |
| Anthropic Memory Tool 文档 | 官方文档 | https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool |
论文必读:
Mem0 论文 —
Production-Ready AI Agents with Scalable Long-Term MemoryMemGPT 论文 —
Towards LLMs as Operating SystemsZep 论文
— A Temporal Knowledge Graph Architecture for Agent MemoryMaRS 论文 —
Memory for Autonomous LLM Agents: Mechanisms, Evaluation, and Emerging
Frontiers(2026 综述)Forgetful but Faithful
— Cognitive Memory Architecture and BenchmarkContext window 即使到了 1M tokens,仍然是稀缺资源——三个原因:
所以生产级 agent 不是”塞满 1 M”,而是精打细算:每个 token 都该有理由出现在 prompt 里。
把 context window 看作有逻辑分区的固定空间,每个分区有不同优先级和淘汰策略:
┌─────────────────────────────────────────────────────┐
│ ┌─────────────────────────────────────────────────┐ │
│ │ [固定区] System Prompt + Persona │ │ ← 永不淘汰
│ │ - 人设、约束、风格、角色 │ │ ~500-2000 tokens
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [可变高优] Memory(注入的用户/agent 记忆) │ │ ← 检索 + 重排
│ │ - 用户档案、偏好、近期事件 │ │ ~500-3000 tokens
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [可变高优] RAG 检索结果 │ │ ← top-K + reranker
│ │ - 公司知识、文档片段 │ │ ~2000-8000 tokens
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [可变中优] 工具定义 schema │ │ ← 仅放当前可能用到的
│ │ - tool descriptions + signatures │ │ ~500-3000 tokens
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [可变中优] 历史对话 │ │ ← 滑窗 + 摘要
│ │ - 最近 N 轮 + 早期摘要 │ │ 可变
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [低优 / 易淘汰] 工具调用结果 │ │ ← 旧的截断或摘要
│ │ - tool outputs,往往最长 │ │ 可变
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [尾部] 当前 user message │ │ ← 永远是最后
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
头部 尾部
↑ primacy bias recency bias ↑
Lost-in-the-Middle 黑洞
配额分配建议(按业务类型):
| 业务类型 | System | Memory | RAG | Tools | 历史 | 工具结果 | User |
|---|---|---|---|---|---|---|---|
| 客服 chatbot | 10% | 15% | 30% | 5% | 20% | 10% | 10% |
| Coding agent | 5% | 5% | 40% | 10% | 10% | 25% | 5% |
| 研究 agent | 5% | 10% | 30% | 5% | 10% | 35% | 5% |
| 个人助理 | 15% | 30% | 10% | 10% | 25% | 5% | 5% |
实战经验:任何分区超过 50% 都是设计有问题的信号。
业界两个权威来源给的拆法都不是”5”——Anthropic Effective Context Engineering [6] 给 compaction / structured note-taking / sub-agent architectures 三类策略;LangChain Context Engineering for Agents [7] 给 write / select / compress / isolate 四类。下面 5 个动作是把两家的拆法合并展开,便于工程实施时逐项检查;不是业界标准 taxonomy。
利用 primacy + recency bias,把最重要的放头尾,次要的放中间:
def assemble_context(system, memories, rag_chunks, tools, history, current_user_msg):
return [
system, # 头部:永远在前
*high_priority(memories), # 头部 2:用户身份与偏好
*high_priority(rag_chunks, top=3), # 头部 3:最相关的 3 个片段
*medium_priority(history), # 中部:历史对话(可被中部黑洞稀释)
*low_priority(rag_chunks, rest=True),# 中部:剩余 RAG 片段
*truncate(tool_results), # 尾部 -2:截断的工具结果
tools_definitions, # 尾部 -1:tool schema
current_user_msg, # 尾部:当前消息
]定时检查 token 使用率,超阈值触发:
def trim_if_needed(context, max_tokens=180_000, target=140_000):
current = count_tokens(context)
if current < max_tokens: return context
# 优先淘汰:旧的工具结果
context = truncate_old_tool_results(context, keep_recent=5)
if count_tokens(context) <= target: return context
# 其次:早期对话压缩成摘要
context = summarize_old_history(context, summary_length=500)
if count_tokens(context) <= target: return context
# 再次:删除非 top-K 的 RAG 片段
context = drop_low_score_rag(context, threshold=0.6)
return context/compact 实现Claude Code 的 /compact 命令把当前长对话压成摘要 + 关键
facts,回收 token:
def compact(messages, llm):
# 1. 抽取关键事实(不能丢)
facts = llm.invoke(f"""
Extract critical facts from this conversation that MUST be preserved:
{messages}
Return as bullet list. Include: user requirements, decisions made, code locations,
pending tasks, open questions.
""")
# 2. 写一段精简摘要
summary = llm.invoke(f"""
Summarize this conversation in <500 words. Focus on:
- What problem we're solving
- What approach we took
- Current progress
{messages}
""")
# 3. 重置 context
new_context = [
SystemMessage(original_system),
SystemMessage(f"# Compressed conversation context\n\n{summary}\n\n# Key facts\n{facts}"),
# 保留最近 3 轮原文(细节在新对话里有用)
*messages[-3:],
]
return new_context关键点:摘要必须 LLM 生成(rule-based 抽取会丢上下文),但 LLM 摘要本身要 prompt 明确要求”保留代码位置/数字/决定”等关键信息。
ReAct 等推理 agent 在思考时会产生大量 thought / observation,不应该污染对外 context:
class AgentMemory:
def __init__(self):
self.public_messages = [] # 用户和 agent 互发
self.scratchpad = [] # agent 内部草稿(thought / 中间 tool result)
def get_llm_context(self):
# 给 LLM 看的:public + 最近 5 步 scratchpad(防止失忆)
return self.public_messages + self.scratchpad[-5:]
def get_user_facing(self):
# 给用户看的:仅 public
return self.public_messagesLangGraph 的 state 机制 / Letta 的 core vs recall 都是这个思路的产品化。
工具返回的 JSON 经常爆长(比如一次 SQL 查询 1000 行)。两层过滤:
def clip_tool_result(tool_name, result, current_step, total_steps):
# 1. 大结果直接截断
s = str(result)
if len(s) > 4000:
s = s[:2000] + f"\n... [truncated {len(s)-2000} chars] ...\n" + s[-500:]
# 2. 老的 tool result(>3 步前)摘要化
if total_steps - current_step > 3:
s = summarize_tool_result(s, max_length=300)
return s| 方法 | 压缩比 | 信息损失 | 延迟 | 适用 |
|---|---|---|---|---|
| 简单截断 | 中 | 高 | 0 | 工具结果 / 兜底 |
| rule-based 抽取(regex / structured) | 中 | 低 | 0 | 已知格式(JSON / 表格) |
LLM 摘要(/compact) |
高 | 低 | 1-3 秒 | 长对话 / 高价值历史 |
| Anchored Iterative Summary(2026 SOTA) | 高 | 最低 | 3-10 秒 | 极长任务(数小时 agent) |
Compaction 不是”context 满了再压”,否则 LLM 已经被噪声污染。生产实践用多信号触发:
| 触发信号 | 阈值(参考) | 适用场景 |
|---|---|---|
| Token 占用率 | > 70% max_tokens(如 180K / 256K = 70%) | 通用兜底,最常用 |
| 历史轮次 | > 50 轮对话 | 长对话客服 / 陪伴产品 |
| 工具结果累积 | 单步 tool result > 4000 tokens 或累积 > 30K | coding agent / data agent |
| 质量信号下滑 | 最近 5 轮 user 满意度 / verifier 通过率下降 | 有 reward signal 的产品 |
| 任务边界 | 一个子任务完成(agent 自己 declare) | 长跑 multi-step agent |
| 时间衰减 | 历史消息 > 30 分钟无引用 | 多任务并行 agent |
| Anthropic Auto-Compact 信号 | Claude API usage.context_window_remaining < 20% |
用 Claude 的产品直接读官方信号 |
关键原则(Anthropic Effective Context Engineering 2026):
class ContextManager:
def __init__(self, max_tokens=180_000, target_tokens=140_000):
self.max = max_tokens
self.target = target_tokens
def assemble(self, system, memories, rag, tools, history, user_msg):
ctx = self._assemble_by_priority(system, memories, rag, tools, history, user_msg)
if count_tokens(ctx) > self.max:
ctx = self._compress(ctx)
return ctx
def _assemble_by_priority(self, ...):
# 见 §2.6.3 动作 1
def _compress(self, ctx):
# 顺序尝试 5 种压缩,越早越轻:
for fn in [
lambda c: drop_low_score_rag(c, threshold=0.6),
lambda c: clip_tool_results(c, keep_recent=5),
lambda c: summarize_old_history(c, length=500),
lambda c: drop_low_priority_memory(c, keep=10),
lambda c: hard_truncate_history(c, keep_recent=3),
]:
ctx = fn(ctx)
if count_tokens(ctx) <= self.target:
return ctx
return ctx # 实在压不下,让 LLM 自己拒绝
def metrics(self, ctx):
# observability:每次返回 dict 让 dashboard 跟踪每分区占比
return {
"total": count_tokens(ctx),
"system_pct": count_tokens(ctx[0]) / count_tokens(ctx),
"rag_pct": ...,
"history_pct": ...,
"tool_results_pct": ...,
}把 metrics() 接进 Langfuse / LangSmith,能在 dashboard
看到”哪类内容在吃 context 配额”——这是诊断 agent 漂移最实用的工具。
所有现代 LLM provider 都支持 prompt caching——把”prompt 前缀”在 KV cache 层缓存起来,下次相同前缀复用。这是 2025-2026 年单次最大的 cost 优化机会,最高 90% 折扣。
| Provider | 触发方式 | 命中折扣 | 最小命中长度 | TTL | 写入 |
|---|---|---|---|---|---|
| Anthropic | 显式(cache_control 标记) |
90%(cached input $0.30 vs base $3.00 / Sonnet) | 1024 tokens(Sonnet)/ 2048(Haiku) | 5 min(默认)/ 1h(付费) | 1.25× base |
| OpenAI | 自动(≥1024 token prefix 自动缓存) | 50-90%(视模型) | 1024 tokens | 5-10 min | 0(免费写) |
| Google Gemini | 隐式(2.5+ 默认开)+ 显式 API | 隐式约 50% / 显式约 75% | 4096 tokens | 默认 1h | 按存储付费 |
| AWS Bedrock | 全 provider 转译 | 同各 provider | 同 | 同 | 同 |
实证(ProjectDiscovery 公开数据):把 prompt caching 接好后,LLM 月账单降 59-70%。学术研究(arXiv 2601.06007)跑 long-horizon agent 任务,API cost 降 41-80%、TTFT 降 13-31%。
按”复用频率 × 长度”打分:
| 内容类型 | 适合缓存? |
|---|---|
| System prompt + persona | ★★★ 强烈推荐 |
| 大量 few-shot examples | ★★★ |
| 工具定义 schema(10+ tools) | ★★★ |
| RAG 检索的固定文档(如 SLA / 政策手册) | ★★ |
| 会变的对话历史 | ✗ 别缓存(缓存击穿) |
| 每次都不同的 user query | ✗ |
经验法则:prefix 重复率 > 50% 的就缓存,否则别。
from anthropic import Anthropic
client = Anthropic()
# system + tools + 大段 RAG 都加 cache_control
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=[
{
"type": "text",
"text": "你是 ACME 公司客服,遵循以下规则...", # 长 system
"cache_control": {"type": "ephemeral"}, # ★ 标记为可缓存
},
{
"type": "text",
"text": KB_KNOWLEDGE_BASE, # 100K token 的知识库
"cache_control": {"type": "ephemeral"},
},
],
tools=[
{"name": "search", "description": "...", "input_schema": {...},
"cache_control": {"type": "ephemeral"}},
],
messages=[
{"role": "user", "content": user_query}, # 这部分不缓存
],
)
# 看 cache 命中
print(f"Cache read: {response.usage.cache_read_input_tokens}")
print(f"Cache write: {response.usage.cache_creation_input_tokens}")
print(f"Regular input: {response.usage.input_tokens}")
# Cost 计算
cost = (
response.usage.cache_read_input_tokens * 0.30 / 1e6 # 命中超便宜
+ response.usage.cache_creation_input_tokens * 3.75 / 1e6 # 写入贵 1.25×
+ response.usage.input_tokens * 3.00 / 1e6 # 普通输入
+ response.usage.output_tokens * 15.00 / 1e6 # 输出
)cache_control
标记位置必须完全一致——多一个空格都 missagent 跑 long-horizon 任务时,prompt 是”前缀(系统)+ 不断追加的对话历史”。追加新内容不破坏前缀缓存,所以前缀部分一直命中:
Round 1: [system] [user1] → write cache(system)
Round 2: [system] [user1] [bot1] [user2] → cache HIT 前缀,付钱仅 [bot1][user2]
Round 3: [system] [user1] [bot1] [user2] [bot2] [user3] → cache HIT 更长前缀
...
反面:如果你在中间插入 / 修改 / 删除任何内容(如 RAG 重新检索把不同 chunk 塞进 system),cache 直接失效,下一次又要全付费。
agent 设计的隐藏 best practice:把”会变的内容”放尾部,前缀永远只追加不修改。
# Langfuse 把 cache metrics 也记下来
@observe(metadata={"cache_strategy": "anthropic_explicit"})
def my_agent_call(query):
response = client.messages.create(...)
langfuse_context.update_current_observation(
usage={
"cache_read": response.usage.cache_read_input_tokens,
"cache_write": response.usage.cache_creation_input_tokens,
"regular_input": response.usage.input_tokens,
"output": response.usage.output_tokens,
},
)
return responsedashboard 关注:
把工具和数据源接入 LLM 长期是脏活:
数学模型:M 个 LLM × N 个工具 = M·N 个适配器。10 个 LLM × 50 个工具 = 500 个集成点。
MCP(Model Context Protocol) [9] 是 Anthropic 2024 年 11 月推出的开放标准,把 M·N 拆成 M+N:
没有 MCP 有 MCP
┌───┬───┬───┐ ┌───┬───┬───┐
│GPT│Cld│Gem│ │GPT│Cld│Gem│
└─┬─┴─┬─┴─┬─┘ └─┬─┴─┬─┴─┬─┘
│ │ │ × N tools └───┼───┘
各连各的 (M·N) │ MCP protocol (JSON-RPC 2.0)
│
┌──────┼──────┐
│ │ │
┌─┴─┬──┬─┴─┬──┬─┴─┐
│GH │DB│Fs │Sl│...│ ← N tools
└───┴──┴───┴──┴───┘
类比:USB-C 之前每个手机一根线(M·N);USB-C 之后任意主机接任意外设(M+N)。MCP 是 AI 的 USB-C。
MCP server 暴露给 client 的能力分三类:
| 原语 | 含义 | 例子 | 由谁触发 |
|---|---|---|---|
| Tools | 可执行函数,有副作用 | create_issue、run_sql |
模型决定调用 |
| Resources | 只读数据,URI 寻址 | file:///doc.md、db://table/users |
应用预加载 |
| Prompts | 用户可调用的模板 | /explain-code、/summarize |
用户触发 |
这三类对应 LLM 应用的三种数据流:模型主动调用、应用注入、用户调用。
| Transport | 适用 | 部署 |
|---|---|---|
| stdio | 本地进程间通信(Claude Desktop / Cursor / Cline 默认) | 进程子进程,配置在 mcp_servers.json |
| SSE(Server-Sent Events) | 远程服务,单向流 | HTTP server,长连接 |
| Streamable HTTP(2025 替代 SSE) | 远程服务,双向流,更标准 | HTTP server,事实标准 |
# server.py
from mcp.server.fastmcp import FastMCP
from datetime import datetime
mcp = FastMCP("my-toolkit", version="1.0.0")
# === Tools(有副作用的函数)===
@mcp.tool()
def create_github_issue(repo: str, title: str, body: str) -> dict:
"""在指定 GitHub repo 创建 issue。
Args:
repo: owner/name 格式
title: issue 标题
body: issue 正文(markdown)
Returns:
{"id": int, "url": str}
"""
# 实际调用 GitHub API
response = github.create_issue(repo, title, body)
return {"id": response.id, "url": response.html_url}
@mcp.tool()
async def run_sql(query: str, params: list = None) -> list[dict]:
"""对只读副本执行 SQL 查询。"""
# 安全:用 parameterized query
result = await db.fetch_all(query, params or [])
return [dict(r) for r in result[:1000]] # 上限 1000 行
# === Resources(只读数据)===
@mcp.resource("docs://{topic}")
def get_docs(topic: str) -> str:
"""获取某主题的内部文档。"""
return knowledge_base.get(topic)
@mcp.resource("user://{user_id}/profile")
def get_user_profile(user_id: str) -> dict:
"""获取用户档案(仅 PII redacted 版)。"""
return user_db.get_redacted(user_id)
# === Prompts(用户模板)===
@mcp.prompt()
def code_review(code: str, language: str = "python") -> str:
"""让 AI 做代码审查。"""
return f"""请对以下 {language} 代码做 code review:
- 找出潜在 bug
- 评估可读性
- 建议优化
```{language}
{code}““”
if name == “main”: mcp.run(transport=“stdio”) # 或 sse / streamable-http
#### TypeScript(@modelcontextprotocol/sdk)
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "my-toolkit", version: "1.0.0" });
// Tool
server.tool(
"create_issue",
{ repo: z.string(), title: z.string(), body: z.string() },
async ({ repo, title, body }) => {
const result = await github.createIssue(repo, title, body);
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
);
// Resource
server.resource(
"user-profile",
"user://{user_id}/profile",
async (uri, { user_id }) => ({
contents: [{ uri: uri.href, mimeType: "application/json",
text: JSON.stringify(await userDb.get(user_id)) }],
})
);
const transport = new StdioServerTransport();
await server.connect(transport);
~/.config/claude/mcp_servers.json(Mac/Linux)或 Cursor
/ Cline 类似配置:
{
"mcpServers": {
"my-toolkit": {
"command": "python",
"args": ["/abs/path/to/server.py"],
"env": {"GITHUB_TOKEN": "ghp_..."}
},
"github-official": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_..."}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem",
"/Users/liang/projects"]
}
}
}重启 client,所有 tools / resources / prompts 自动可用。
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
# 一次连多个 MCP server
async with MultiServerMCPClient({
"my-toolkit": {"command": "python", "args": ["./server.py"], "transport": "stdio"},
"github": {"url": "http://localhost:3000/sse", "transport": "sse"},
}) as client:
tools = client.get_tools() # 自动转成 LangChain Tool 列表
agent = create_react_agent("anthropic:claude-sonnet-4-6", tools)
result = await agent.ainvoke({"messages": [("user", "在 acme/api repo 开个 bug issue")]})按类别选用:
| 类别 | 仓库 | 用途 |
|---|---|---|
| 官方参考实现 | modelcontextprotocol/servers |
几十个开箱即用:filesystem / git / github / postgres / sqlite / slack / google-drive / brave-search / sequential-thinking |
| Awesome 列表 | punkpeye/awesome-mcp-servers |
社区 server 索引(数百个) |
| 数据库 | postgres / mysql / sqlite / mongodb / redis 都有官方或社区 MCP | |
| 云服务 | aws-mcp / gcp-mcp / azure-mcp | |
| DevOps | kubernetes / terraform / docker MCP | |
| 生产力 | notion / linear / jira / confluence MCP | |
| 浏览器 | playwright-mcp / browserbase-mcp / Puppeteer MCP | |
| 企业商用 | Zapier MCP(5000+ 集成) |
| 时间 | 事件 |
|---|---|
| 2024-11 | Anthropic 发布 MCP 协议 |
| 2025-Q1 | Cursor / Cline 等 IDE 接入 |
| 2025-Q2 | OpenAI 宣布支持 |
| 2025-Q3 | Google / Microsoft / Amazon 跟进 |
| 2025-Q4 | MCP 治理转入独立 MCP Steering Committee(Anthropic + Block + OpenAI + Microsoft 等共同治理) |
| 2026-02 | Python + TypeScript SDK 月下载量破 9700 万 |
| 2026-Q1 | 几乎所有主流 LLM 客户端原生支持 |
MCP 解决”agent 与工具的沟通”,A2A(Agent-to-Agent) 解决”agent 之间的沟通”。这块仍在多协议竞争:
| 协议 / 框架 | 出处 | 范式 | 状态 |
|---|---|---|---|
| Google A2A | Google 2025 | HTTP-based agent discovery + task delegation | 早期 |
| Microsoft Agent Framework Protocol | Microsoft 2026 | AutoGen 接班,多 agent 对话标准 | 推广中 |
| Anthropic Agent Skills | Anthropic 2026 | Skill 包:可复用 agent 能力单元 | 早期 |
| MCP for A2A | 社区 | 把”另一个 agent”当作 MCP server | 实践中 |
| Letta multi-agent | Letta | 通过 message passing | 框架内 |
MCP vs A2A 类比:
“MCP 给 agent 一双手(连工具/数据),A2A 让多个 agent 组队干活。MCP 是垂直整合(单 agent ↔︎ 工具栈),A2A 是水平协作(agent ↔︎ agent 网络)。”
2026 春的现状:MCP 已是事实标准,A2A 还在 Cambrian 大爆发期。实战建议:现在做单 agent → 全押 MCP;做多 agent → 用具体框架(LangGraph / Microsoft Agent Framework)的内置协议,等 A2A 标准明朗。
| 陷阱 | 表现 | 对策 |
|---|---|---|
| Tool 太多 | 50+ tool 让 LLM 决策困难 | 分组 + 路由(先选类目再选 tool) |
| Tool description 太短 | 模型不知道何时用 | 写”何时用 / 何时不用”+ 1-2 个 example |
| 副作用未声明 | 模型当只读调用,造成意外修改 | description 明确”会修改 X”+ 在 client 侧加 deny-first |
| 超长返回 | 一次返回 10K rows 撑爆 context | server 侧 cap + 分页 + 摘要 |
| 同步阻塞 | 长任务把 client 卡死 | 用 async transport(streamable-http) |
| 认证混乱 | 每个 tool 自己处理 token | server 侧统一从 env 读,client 侧用 OAuth flow |
工具集成在 Agent 实现层面的细节 → agent-engineering.md §3.1 与 §5; LLM 网关 / observability / 推理服务器层面的基础设施 → harness-engineering.md §3.9 附录(注:04 章把这 4 类显式标记为”相关但非 harness 工程”)。
[2] Liu, N. F. et al. (2024). Lost in the Middle: How Language Models Use Long Contexts. TACL. [5] Karpathy, A. Software Is Changing (Again). YC AI School talk. [6] Anthropic. Effective Context Engineering for AI Agents. https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents [7] LangChain. Context Engineering for Agents. https://www.langchain.com/blog/context-engineering-for-agents [9] Wikipedia. Model Context Protocol. https://en.wikipedia.org/wiki/Model_Context_Protocol [12] Hsieh, C. et al. (2024). RULER: What’s the Real Context Size of Your Long-Context Language Models? https://arxiv.org/abs/2404.06654 [13] Cheney Zhang. Embedding Models Benchmark 2026. [14] CallSphere. Vector Database Benchmarks 2026: pgvector / Qdrant / Weaviate / Milvus / LanceDB. [18] Packer, C. et al. (2023). MemGPT: Towards LLMs as Operating Systems. [19] Mem0 Team (2025). Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory. https://arxiv.org/abs/2504.19413 [21] Anthropic Memory Tool docs. https://docs.anthropic.com [41] TianPan. Long-Context Models vs RAG: When the 1M-Token Window Is the Wrong Tool. [43] Squirro. RAG in 2026: Bridging Knowledge and Generative AI.
补充: - Asai et al. (2023). Self-RAG. - Microsoft GraphRAG. https://github.com/microsoft/graphrag - LightRAG. https://github.com/HKUDS/LightRAG - Edge et al. (2024). From Local to Global: A Graph RAG Approach to Query-Focused Summarization. - BGE-M3. https://huggingface.co/BAAI/bge-m3
答案中以
[术语]标记的概念都可点击跳到本表对应条目并自动展开。本表按 10 个主题分组收录 60+ 高频术语,默认全部收起——遇到不熟的术语再展开看精要解释,避免被信息淹没。
不实际运行代码的情况下分析源码,识别潜在 bug、未使用变量、风格违规、可疑用法。常见工具:
在 Agent 工程中,lint 经常被用作 harness 的硬约束——把 lint 错误反馈给模型重写代码,比单纯在 prompt 里说"请遵守编码规范"可靠得多(Augment Code 把这叫做 Constraint Harness)。
确保代码中变量赋值、函数调用的类型一致:
对 Agent:让 agent 写完代码后强制跑 tsc --noEmit / mypy . / cargo check,类型错误作为反馈喂回模型继续修。比让 agent 自己"声明检查过类型"可靠得多——这是 harness 工程的典型做法。
CI(Continuous Integration):每次代码提交自动跑测试、构建、lint、type check 等校验。
CD(Continuous Deployment):通过校验后自动部署到测试 / 预发 / 生产环境。
常见工具:GitHub Actions、GitLab CI、Jenkins、CircleCI、Buildkite。
在 Agent 工程里,CI 是最重要的 deterministic 反馈源——LLM 自评不可靠,但 CI 通过/失败是 0/1 信号,可以直接作为 ground truth。Hashimoto 的 harness engineering 强调"能不能解决问题强相关于 agent 能不能验证自己的工作"。
让不可信代码(用户上传、LLM 生成)在受限环境中跑,防止破坏宿主系统。隔离强度从弱到强:
Agent 跑用户/LLM 生成代码必须沙箱,并叠加纵深防御:网络出口白名单 + 文件系统只读 + 时间/内存配额 + 不挂载 host 凭据。
来自煤矿用金丝雀检测毒气的典故。部署流程典型为:shadow(双跑不返回)→ 1% → 5% → 25% → 50% → 100%,每阶段观察核心指标无回归才推进。
对 LLM 应用:新 prompt / 新模型 / 新 agent harness 上线必走 canary,关键指标(解决率、CSAT、refusal 率、p99 延迟、单次成本)任何一个恶化 > 阈值就回滚。LLM 应用的"非确定性"特性让 canary 比传统软件更重要。
按 user_id hash 切流量保证一个用户始终进同一桶(避免体验跳变)。判定要素:
LLM 应用调 prompt / 换模型 / 改 RAG 都应过 A/B;只靠 offline eval 上线常翻车(线上流量分布与黄金集差异大)。
业务侧验收 Agent 是否上线最看的几个指标:
Agent 工程关注:把模型 / prompt / RAG 改动放进 A/B 看这些业务指标,而非只看离线 benchmark 分数。改动让 benchmark 涨但 GMV / CSAT 跌的情况非常常见。
Agent 自治走到关键节点时暂停等待人工:
设计原则:"Humans steer. Agents execute."(OpenAI Harness Engineering)。Agent 不是替代人,是放大人的判断——把人放在制定计划、设定约束、审核结果的位置,agent 处理执行。
Google SRE 体系定义的四个术语:
1 - SLO。SLO 99.9% 意味着 30 天有 43.2 分钟"预算"可以不达标,预算用完就冻结非必要发布。Agent 应用怎么用:
经验法则:SLO 不是越严越好——99.99% 比 99.9% 贵 5-10 倍,且大概率超过用户感知阈值。Google 建议"刚刚够用户不抱怨"的水位。
到期自动失效。常见用法:
经验换算:1000 英文单词 ≈ 1300 token;1000 汉字 ≈ 1500-2000 token。
为什么重要:context window、API 计费、推理速度都按 token 算。控制 token 数 = 控制成本和延迟。Anthropic / OpenAI / Google 各自的 tokenizer 不同,相同文本 token 数差 ±20% 很常见。
文本 → 神经网络 → 一个 768 / 1024 / 1536 / 3072 维的浮点数向量。同义词 / 同主题文本的向量在空间中距离近(cosine similarity 高)。
主要用途:
主流模型 2026:OpenAI text-embedding-3-large、Cohere v4、Voyage-3、BGE-M3(开源 SOTA,中英双语 + dense/sparse/multi-vector 三合一)。
包含 system prompt + 历史对话 + 用户输入 + 模型输出,所有都计算在内。2026 主流模型:
注意:宣称窗口 ≠ 有效窗口。RULER 基准显示多数模型在窗口 1/4 之外性能急剧下降(Lost in the Middle)。工程建议把"应当塞入 context"的阈值定在 8-32k,更长走 RAG 或层级摘要。
两个阶段瓶颈不同,调优策略也不同——prefill 优化靠并行 / FlashAttention,decode 优化靠 continuous batching / speculative decoding。
生成第 t 个 token 时需要前面所有 token 的 K/V 参与注意力计算。把它们缓存下来避免每次重算,是 LLM 推理的最基础优化。
显存占用:2 × num_layers × num_heads × head_dim × seq_len × batch × bytes_per_elem。70B 模型上 128k 上下文的 KV cache 可达数十 GB,远超模型权重本身。
进阶优化:
GPT 类 decoder-only 模型的生成方式:P(x_t | x_1, ..., x_{t-1})。每生成一个 token 都要跑一次完整 forward(虽然能复用 KV cache),所以输出长度直接决定延迟。
对比:BERT 是非自回归(masked LM,一次预测所有 mask 位置),扩散模型(diffusion)也是非自回归(一次去噪整段)。
自回归的代价催生了大量加速技术:speculative decoding、parallel decoding、Medusa、EAGLE 等。
decoder 最后一层输出 [vocab_size] 维的 logit 向量;过 softmax 后得到下一个 token 的概率分布。
工程上能拿到 logit(或 top-k logprob)的场景:
注意:OpenAI / Anthropic 商用 API 默认只返回采样结果,需显式开 logprobs 选项。
P(token) ∝ exp(logit / T):
何时调高:Self-Consistency 需要多样路径、creative writing、brainstorm。
何时调低:代码生成、数学推理、严格格式输出,T=0.1-0.3。
陷阱:T=0 不完全等同 deterministic——浮点 GPU 算子的非确定性 + 并发调度可能让相同 prompt 的输出仍有微差。
Xiao et al. 2023 发现:长序列推理时,靠后的 token 往往把不成比例多的注意力分给了序列最前面的几个 token(即使它们语义无关)——这些 token 成为"sink",吸收掉 softmax 必须分配的归一化质量。
工程意义:
LayerNorm:y = (x - μ) / σ * g + b,沿 hidden 维度求均值方差。与 batch 无关,天然支持变长序列与单条推理——这是 Transformer 选 LayerNorm 而非 BatchNorm 的原因。
RMSNorm(Zhang & Sennrich 2019):y = x / sqrt(mean(x²)+ε) * g,去掉减均值和 bias。Llama / PaLM / Mistral 等现代 LLM 几乎都改用 RMSNorm,省 7-64% 归一化耗时且效果不降。
Pre-LN vs Post-LN:LN 在残差前做(Pre-LN)训练比放残差后(Post-LN)稳定,可去掉 warmup,GPT-2 起几乎统一用 Pre-LN。
Su et al. 2021。把位置 m 表示为旋转矩阵 R_m 作用在 Q/K 上,使得 <R_m·q, R_n·k> = f(q, k, m-n)——注意力得分只依赖相对距离。
优点:(1) 无额外参数;(2) 天然编码相对位置;(3) 可与线性注意力兼容。已成 Llama / Qwen / Mistral 等绝大多数现代 LLM 的位置编码方案。
外推问题:纯外推 2k→32k 会崩——高频分量旋转角度落入未见区域。缓解:
RoPE 纯外推会崩——高频分量在训练上下文外旋转角度落入未见区域。两种工程化方案:
主流应用:
经验数字:YaRN 在 4× 上下文扩展(2k→8k 或 32k→128k)几乎无损;8× 以上仍有性能下降。极致长上下文(1M+)通常需要重训而非仅靠 YaRN。
标准 multi-head attention:H 个头各有独立 Q/K/V。
收益:KV cache 减小到 1/8 ~ 1/H,推理吞吐显著提升,效果几乎无损。Llama-2 70B / Llama-3 / Mistral / Qwen2 等都用 GQA。
把传统 dense FFN 换成 N 个并行的 expert FFN + 一个 router。每个 token 通过 router 选 top-k 个 expert(k 通常 1-2),只激活这部分 expert 计算。
优点:参数量大、算力省。Mixtral 8x7B 总参数 47B,激活参数仅 13B;DeepSeek-V3 671B 总参数,激活仅 37B。同等推理成本下 capacity 远超 dense 模型。
挑战:(1) 训练时负载均衡(防一两个 expert 被过度使用);(2) 显存占用按总参数计而非激活参数;(3) 通信开销大(expert parallelism)。
单头注意力对每对 token 只产生一个 attention 分布——一种"关系视角"。多头(h=8/32/64/128)让模型同时学多种关系——句法头关注短距离依赖,语义头关注共指、远程实体。
为什么单头加大维度不行:多头是 h 个低秩注意力的混合,表达能力严格更强;单头是一个高秩注意力,自由度反而少。
进阶变体:GQA / MQA 在 KV 侧减头省显存,Q 侧仍保多头。
Dao 2022。标准 attention 显存 O(N²)(要存 N×N 的 attention matrix),长序列瓶颈。FlashAttention 把 Q/K/V 分块加载到 GPU SRAM 计算,不写出完整 attention matrix,显存降到 O(N),速度也快——attention 是 memory-bound,少读 HBM 就快。
演进:
Kwon et al. 2023。解决传统推理 KV cache 的三类碎片问题:
机制:KV cache 切成固定大小 block(如 16 token),每个序列维护 block table(逻辑→物理映射)。显存利用率从 ~20% 提到 >96%;同 prompt 多采样共享前缀 block;配合 continuous batching 吞吐相对 HuggingFace TGI 提升 2-4×,相对 FasterTransformer 24×。
2023 年起的事实标准开源推理服务器。核心特性:
对比:TGI(HuggingFace)、TensorRT-LLM(NVIDIA,私有更快)、SGLang(前缀复用更彻底)、LMDeploy(OpenCompass 出品)。
也叫 in-flight batching,Yu et al. ORCA 2022 提出。对比 static batching(等齐 batch 再一起跑,长短请求混在一起时 GPU 利用率经常 <30%),continuous batching:
vLLM 数据:吞吐相比 static 提升 5-23×。已成所有现代推理引擎的默认。
Leviathan et al. 2023。流程:
数学上保证输出分布与原模型完全一致(rejection sampling)。
收益条件:(1) draft 与 target 分布相似(接受率 >60%);(2) 大模型受 memory bandwidth 限制,并行验证不增延迟。代码生成接受率 70-90%,开放生成 30-50%。变体:EAGLE-2/3、Medusa、Llama 3.1 self-speculative。
KV cache 是单 request 内复用;prefix caching 是跨 request 复用——多个 request 共享 system prompt / few-shot / agent 历史时,首次计算后缓存这部分 KV,后续 request 直接命中跳过 prefill。
效果:agent / RAG / 多轮对话场景,system prompt 通常 1-4k token、几乎所有请求共享,TTFT 可下降 10-100×。
实现:vLLM --enable-prefix-caching、SGLang RadixAttention(用 radix tree 自动管理所有历史前缀)、Anthropic prompt caching API(5min TTL,cached 部分 0.1× 价格)。
主要由 prefill 决定(处理 input 所有 token)。计费、监控的核心指标之一。
降低 TTFT 的手段:
伴生指标:TBT(time between tokens,单 token 间隔)— 由 decode 决定。
混合精度训练:权重和梯度用 BF16/FP16,optimizer state 仍用 FP32。
部署阶段把权重量化到低 bit 整数,推理时反量化回浮点计算。
INT8 / INT4 量化通常需要校准集(128-512 样本),AWQ 更智能(保护 1% salient 通道)。极致显存场景再用 GGUF(llama.cpp 格式)+ 3-bit / 2-bit 量化跑手机端。
Lin et al. 2023。核心洞察:只需保护 ~1% 的"重要权重通道"(由激活幅值识别)即可保住精度。对这些通道按 per-channel scale 放大再量化。
对比 GPTQ:实现更简单,Llama-2 上困惑度更低,已成 vLLM / TensorRT-LLM 默认。
Frantar 2022。用 OBQ 启发的逐层最小化量化误差,需要少量校准集(128 样本)。优点:早、生态全(exllama / vllm / TGI 都支持);缺点:对激活分布敏感,重要通道可能被压坏。
2026 部署选型:H100/H200 → FP8;A100/3090 → AWQ INT4;手机/边缘 → INT4 + GGUF。
最基础的指令微调方式:把 (输入, 期望输出) 对当做监督学习样本,cross-entropy loss 教模型学指令跟随。
经验数据量:
LIMA 论文:1000 条精选样本即可让 65B base 接近 GPT-4 对话质量——质量 >> 数量。
Hu et al. 2021。把权重更新分解为两个低秩矩阵 ΔW = BA(r=8/16/64),只训练这两个矩阵,可训参数减少 100-10000×。
显存:
优点:可训参数少 / 多个 adapter 可热切换 / 缓解 灾难性遗忘。
Dettmers et al. 2023。三项关键:
65B 模型 QLoRA 约 33GB,单 A100-48G 或双 RTX 3090 可跑。代价:训练慢 30-40%(反量化开销)。
三步走:(1) SFT 训出 base policy;(2) 人类标注 (chosen, rejected) 偏好对训出 Reward Model;(3) 用 RM 给 policy 输出打分,PPO/RLOO/REINFORCE 等算法更新 policy。
InstructGPT、ChatGPT 让 RLHF 出圈。三大痛点:
RLHF 第二步训出。输入:(prompt, response),输出:一个 scalar reward。训练 loss 是 Bradley-Terry:让 RM 在 (chosen, rejected) 对上给 chosen 更高分。
常见弱点:
Rafailov et al. 2023。证明:在 Bradley-Terry 偏好模型 + KL 约束下,最优 policy 有闭式解,可直接用偏好对 (chosen, rejected) 做监督式损失:
L_DPO = -log σ(β(log π(y_w)/π_ref(y_w) - log π(y_l)/π_ref(y_l)))
优势:(1) 无 RM / 无在线采样 / 无 critic,仅 policy + ref 两个模型;(2) 用 SFT 同样的训练栈;(3) 超参稳定。
劣势:严重依赖偏好数据质量;容易过拟合 chosen 的表面模式;reward hacking 风险大。
Llama-3、Qwen2 后训练主路径都是 DPO 或其变种(IPO、KTO、SimPO)。
Schulman 2017。Actor 学策略,critic 学 value function 估计 advantage,clip 限制每步策略更新幅度防崩。
RLHF 中需同时载入 actor / ref / critic / reward 四个模型,显存 ≥4× 单模型。超参敏感(KL 系数、clip ratio、advantage 归一化),训练不稳定。
催生了 DPO(去 RL)、GRPO(去 critic)、RLOO(用平均 reward 当 baseline)等简化方案。
DeepSeek-Math 2024 提出,R1 验证。核心创新:去掉 critic(value 网络),对同一 prompt 采样 G 个 response(典型 G=64),用组内 reward 的均值/标准差做 advantage 归一化:A_i = (r_i - mean(r)) / std(r),再做 PPO-like 的 clipped objective + KL 正则。
效果:
在数学、代码、形式逻辑等可自动验证的任务上,直接用程序判定 reward:答案匹配、单测通过、定理证明器验证。
优势:
DeepSeek-R1、DAPO(ByteDance 开源 SOTA)都属此类。局限:仅适用可验证任务,开放对话仍需 RLHF / DPO。
机制:参数被新任务 loss 拉走,原本编码通用知识的方向被覆盖。
缓解组合(生产推荐):
进阶:EWC(按 Fisher 信息加权 L2 正则)、Model Souping(微调模型与原模型权重平均)、MoE 路由扩展。
Wei et al. 2022。最简形式:"Let's think step by step"(Kojima 2022 zero-shot CoT)。
为什么有效:(1) 增加有效 compute(更多 forward step);(2) 把"系统 1"直觉答题转成"系统 2"分步推理;(3) 暴露错误推理便于 self-consistency 投票纠正。
是涌现能力,<10B 模型几乎无效。Long CoT(R1 / o1 用 RL 训出,含回溯/自我验证,数千 token 长)是 2025 的新范式。
Yao et al. 2023。CoT 是线性单链,错了无法回头;ToT 每步生成多个候选 thought,LLM 自评估状态价值(promising / sure / impossible),BFS/DFS 展开可回溯剪枝。
Game of 24 上 GPT-4 CoT 4% vs ToT 74%。代价:LLM 调用数从 1 涨到 50-200。
适合:解空间需显式搜索(puzzle、规划、定理证明),中间状态可评估。不适合:闲聊、摘要、翻译。
Yao et al. 2022。结构 Thought → Action → Observation 循环:模型先 "Thought: 我需要查 X",输出 "Action: search[X]",工具结果作为 "Observation: ..." 喂回 prompt,再下一轮 Thought。
三大效果:
是现代 agent 框架(Claude tool use、OpenAI function calling、LangChain、AutoGPT)的概念原型。
Shinn et al. 2023。Agent 在一次任务失败后(由外部 verifier 给二元 reward),用 LLM 把失败原因总结成自然语言 lesson 存入 episodic memory,下一次任务时把 lesson 加进 prompt 影响行为。
vs Self-Refine:
HumanEval 上 GPT-4 Reflexion 91%,超过 GPT-4 baseline 80%。
Madaan et al. 2023。在一个 trajectory 里循环 N 轮,无外部 reward、无跨任务记忆。7 个任务平均提升 20%。
陷阱:弱模型自评不准,可能越改越差。生产常组合 Self-Refine(局内)+ Reflexion(跨局)。
Wang et al. 2022。temperature>0 采样 N 条 CoT 路径,对最终答案投票。GSM8K 上比 greedy CoT 提升 ~17 个百分点(PaLM 55%→74%)。
收益大场景:(1) 答案空间离散(数字、选择题);(2) 模型错误率 <50%;(3) 推理路径多样(temperature 0.7-1.0)。代价:N×(典型 N=5-40),开放生成不适用。
何时 few-shot:(1) 任务格式新颖/输出结构复杂(JSON schema、特定 markdown);(2) 分类任务标签多。
何时 zero-shot:通用任务现代模型已饱和(摘要、翻译、QA),加 example 反而引入偏差。
陷阱:example 顺序影响大(recency bias);偶然规律会被学走;example 多了 Lost in the Middle。建议 ≤8 条。
标准 pipeline = Ingestion + Retrieval + Generation:
每段都有瓶颈:chunking 切大小、embedding 选型、召回率不足、context 顺序、模型不忠于 context。生产 RAG ≠ "embedding + 向量库 + LLM"——是一个工程系统。
策略:
Okapi BM25。文档与 query 的相关性按 Σ IDF(qi) · f(qi,d) · (k+1) / (f + k·(1-b+b·|d|/avgdl)) 计算。强项:精确词命中、稀有词高权重、专有名词、数字。短板:同义词不通("医生" vs "doctor")。
与 dense embedding 互补——hybrid search 把两者融合,召回率比单 dense 提升 5-15 个百分点。
dense embedding 擅长语义近似,BM25 擅长精确词匹配,两者互补。
融合方法:
score(d) = Σ 1/(k + rank_i(d)),k=60。生产首选,无需归一化两路分数α·dense + (1-α)·bm25,需要调 α 且分数归一化Anthropic Contextual Retrieval 实验:单 embedding → +contextual embedding → +BM25 → +rerank,失败率 5.7% → 4.0% → 3.7% → 1.9%。
Bi-encoder(embedding):query / doc 分别编码再 cosine 比对,快但无交互。
Cross-encoder(reranker):query + doc 拼接喂 transformer,每层 self-attention 交互,相关性建模能力强 20-40%(NDCG@10),慢 100×。
生产标配:bi-encoder 召回 top-100 → cross-encoder rerank 到 top-10 → 喂 LLM。
主流:Cohere Rerank v3/v4、BGE-reranker-v2-m3(开源 SOTA 中英)、Jina Reranker v2、Mixedbread。
陷阱:召回 top-100 里没有相关文档,rerank 也救不了——reranker 锦上添花,不能替代召回。
2026 主流 embedding 模型对比:
选型框架:
关键陷阱:
主流:
ANN 算法:HNSW(最常见)、IVF、ScaNN(Google)。索引时间 vs 检索精度 vs 显存有 trade-off。
朴素 RAG 在多文档跨实体推理、召回质量不稳、多跳查询场景下都会力不从心。四个常见进阶方案:
实战选择:
Es et al. 2023。四大指标:
生产阈值:4 个指标都 ≥0.7 算合格;Faithfulness < 0.7 优先修;Context Recall < 0.7 调 retriever。
Mitchell Hashimoto 2026 命名:"anytime you find an agent makes a mistake, take the time to engineer a solution such that the agent never makes that mistake again."
不是模型微调,而是工程化环境让模型物理上无法以同样方式再犯错。
两条法则:
OpenAI 公式:Agent = Model + Harness;"Humans steer. Agents execute."
2024-2026 主流框架对比:
经验:先用 SDK 直调写原型;workflow 模式(routing / parallelization / orchestrator-workers)成熟后再考虑 LangGraph;纯 RAG 场景考虑 LlamaIndex;避免一上来就 LangChain "全家桶"。
2024-11 推出,2025-Q4 转入独立 Steering Committee(Anthropic / Block / OpenAI / Microsoft)。三种核心原语:
解决"每接一个新工具就要写一遍 adapter"的 N×M 问题。2026 年增速最快的单一技能,97M+ 月下载,已是 Anthropic Applied AI Engineer JD 显式必备。
模型输出 {"name": "search", "arguments": {"query": "..."}} JSON,runtime 解析后真实执行函数,结果回喂模型。Anthropic Tool Use API、OpenAI function calling 都是同思路。
设计原则(Anthropic 实测:"agent 性能正比于工具描述清晰度"):
Anthropic 2024-Q4 推出 Computer Use。流程:截屏 → LLM 看图 + 推理 → 输出鼠标键盘动作(click X,Y / type text / scroll)→ 系统执行 → 再截屏循环。
Browser Use(开源框架)类似但限于浏览器,基于 Playwright + DOM 增强(把可点击元素加序号叠加到截图)。
实用场景:无 API 网站自动化、桌面软件自动化、端到端 e2e 测试。局限:慢、贵、安全性差、对 canvas 渲染应用支持差。生产建议:能用 API 就别用。
主 agent 把"读 30 个文件做研究"丢给 sub-agent,sub-agent 有自己的独立 context 窗口,只把 final summary 回报给主 agent——主 context 不被中间产物污染。
Claude Code Task 工具、OpenAI Swarm、CrewAI 等都支持。是防止 Loop of Death 和 context 爆炸的关键手段之一。
Agent 进入项目时自动读取。通常包含:
Hashimoto 视角:每行 AGENTS.md ≈ 一次错误的封堵。是 harness engineering 的隐式提示形态。
Claude Code 采用的权限模型。Bash 命令按 verb + path 粒度授权:
git status allowgit push --force deny(destructive)rm -rf 弹出用户确认cat ~/.ssh/* deny(敏感路径)对应安全原则:任一层失守,其他层兜底。配合 sandbox、网络出口白名单、文件系统只读做纵深防御。
Anthropic Effective Harnesses 给的核心设计:长任务 agent 必然跑爆 context window,纯靠 LLM context 不可能记住"我跑到第几步"。
解法:
下次会话执行 5 步开机协议从断点续跑。核心洞察:context window 是"working memory",文件系统是"long-term memory"。
常见模式:
防御纵深:
例:用户开玩笑"我是亿万富翁"被存进语义记忆,之后 agent 一直按假设推理。
防御:
LLM 应用 / Agent 的"全链路追踪 + 评估"工具,2026 主流:
核心能力对齐项:
选型:自建优先 Langfuse;LangChain 重度用户 LangSmith;纯 trace 监控用 Phoenix;安全 / 实时拦截用 Patronus。
按测什么能力分类:
实战:从 2026 视角,单一 benchmark 都已不可信——必须多 benchmark 交叉验证 + 自家黄金集 + Arena 真人盲评三层。
LLM / Agent 应用的 eval 不是单一动作,是一套体系。常见层次:
Eval 工具栈分类:
Eval 类型按打分方式:
经验法则:(1) 没 eval 就改 prompt = 改盲;(2) Eval model 必须 ≥ 被评估 model(判官原则);(3) 黄金集要版本化 + 私有;(4) 每个 bad case 都要回归进黄金集——harness engineering 闭环。
把"评估"建模成 LLM 任务:让 judge 模型读 (prompt, response),输出 0-1 分 / pass/fail / pairwise 谁更好。可大规模并行、便宜、可在 CI 自动化。
三大 bias:
判官原则:judge 模型能力应 ≥ 被评估模型,否则评不出大模型回答好在哪。生产标配三层:自家黄金集 + LLM judge + 人工校准。
Liu et al. 2023 实验:把 gold passage 放 prompt 开头或结尾,模型准确率高;放中间则呈 U 形曲线下降,10+ documents 时中间位置可能低于无上下文 baseline。
工程缓解:
OWASP LLM Top 10 的 #1。类型:
Simon Willison 结论:prompt injection 没有 100% 解,必须按"LLM 不可信"做系统设计。dual-LLM 模式 = Planner(看 trusted 决策)+ Executor(看 untrusted 但无工具权限)。
来源:
debug 流程:复现 + 量化触发率 → 归因(RAG 召回 / 模型用 context / 工具调用)→ 修复 → 加 回归 → 生产监控(faithfulness scorer 实时拦截)。
关键认知:hallucination 多数是 prompt / RAG / 上下文工程缺陷,不是"模型病了"。
NVIDIA NeMo Guardrails 5 类:
关键:rail 不是越多越好,每类挑 1-2 个高 ROI 的开,否则延迟/成本爆炸。
常见手法:
防御:厂商 RLHF safety training + 应用层 input/output classifier(Llama Guard)+ 限制 max context(对抗 many-shot)+ red team 定期演练。