当前位置: 首页 > news >正文

第3篇:Context Engineer:构建 AI 的长期记忆与动态知识库

文章目录

  • 引言
  • 一、理解 LLM 的"记忆"机制
    • 1.1 上下文窗口:模型唯一的"工作记忆"
    • 1.2 上下文窗口的物理限制
  • 二、方案一:上下文窗口管理
    • 2.1 滑动窗口:简单粗暴但有效
    • 2.2 摘要压缩:用精炼代替遗忘
    • 2.3 Token 预算管理:把上下文当成"钱"来花
  • 三、方案二:RAG —— 让模型"遇到问题再去查"
    • 3.1 RAG 解决什么问题?
    • 3.2 RAG 的核心组件
    • 3.3 RAG 的关键质量因素
  • 四、方案三:长期记忆 —— 跨会话的信息持久化
    • 4.1 对话摘要 vs 长期记忆
    • 4.2 长期记忆的实现
  • 五、Context Engineer 的天花板
  • 总结

本系列为《Loop Engineer 实战:从提示工程到自主循环》第3篇
前置条件:了解 LLM 的基本工作原理(知道什么是 Token、什么是上下文窗口),有基本的 Python 阅读能力。

引言

上篇我们剖析了 Prompt Engineer 的"三重天花板"——无状态、无工具、无循环。今天我们来拆第一道墙:无状态

想象一个场景:你每天早上让 AI 助手帮你规划当天的工作。第一天,你告诉它"我是后端开发,最近在做微服务拆分"。它给了你不错的建议。第二天,你再次让它规划工作——它问:“请问你是做什么工作的?”

这很让人沮丧。不是因为它不够聪明,而是因为它不记得。每次对话对它来说都是一个全新的世界。

Context Engineer 要解决的核心问题就是:让 AI 拥有跨越时间和会话的记忆能力

本文将沿着"问题→方案→实现→边界"的线索展开:先讲清楚 LLM 的上下文机制(为什么它会"失忆"),再给出三大核心解决方案(上下文窗口管理、RAG、长期记忆),最后诚实地指出 Context Engineer 自身的天花板——它有记忆,但没有手脚。


一、理解 LLM 的"记忆"机制

1.1 上下文窗口:模型唯一的"工作记忆"

LLM 本身是无状态的。它没有"数据库",没有"变量",没有任何持久化存储。它唯一能"看到"的东西,就是你通过 API 发给它的那一串 Token——这就是上下文窗口(Context Window)

API 调用: System Prompt(系统提示) + 历史消息(如果开发者手动传回来) + 用户最新消息 → 模型生成回复

关键点:历史消息不会自动保留。如果你不把上一轮的对话拼回来,模型就不知道上一轮发生了什么。

1.2 上下文窗口的物理限制

上下文窗口不是无限的。以下是几个主流模型的窗口大小:

模型上下文窗口大约页数(中文)
GPT-4 (早期)8K tokens~15页
GPT-4 Turbo128K tokens~240页
Claude 3200K tokens~370页
Gemini 1.5 Pro1M tokens~1800页

看起来很大?但实际使用中有三个约束让"大窗口"并不好用:

① 成本:每次 API 调用,输入 Token 都要计费。如果你每次都把 100K 的历史对话传回去,费用会指数级增长。

② 延迟:输入 Token 越多,模型处理时间越长。一个 100K Token 的请求可能比 5K Token 的请求慢 3-5 倍。

③ 注意力衰减(Lost in the Middle):研究表明,模型对上下文窗口中间位置的信息关注度最低。开头和结尾的信息记得最清楚,中间的信息容易被忽略。窗口越大,这个问题越严重。

模型注意力分布(示意): ████████░░░░░░░░░░░░░░░░████████ 窗口开头 中间(注意力低) 窗口结尾

这意味着:你不能无脑地把所有历史信息都塞进去——你需要管理上下文。


二、方案一:上下文窗口管理

Context Engineer 的第一个任务就是:在有限且昂贵的上下文窗口里,装下最有价值的信息。

2.1 滑动窗口:简单粗暴但有效

最直接的策略:只保留最近 N 轮对话。

defsliding_window(messages:list,max_turns:int=10)->list:"""只保留最近的 max_turns 轮对话"""# 每条用户消息 + 助手回复 = 2 条消息returnmessages[-(max_turns*2):]

适用场景:对话式应用(客服、闲聊),历史信息价值随时间快速衰减。

局限:如果用户在第十一轮问"你记得我第一轮说的那个需求吗?"——模型答不上来。

2.2 摘要压缩:用精炼代替遗忘

与其丢掉旧对话,不如把它们压缩成摘要。

defsummarize_history(messages:list,llm_call)->str:"""将历史对话压缩为一段摘要"""history_text="\n".join([f"{m['role']}:{m['content']}"forminmessages])prompt=f"""请将以下对话历史压缩为一段简洁的摘要,保留关键信息: - 用户的基本信息(姓名、角色、偏好) - 已完成的任务 - 正在进行中的任务 - 重要的决策和约定 对话历史:{history_text}摘要:"""returnllm_call(prompt)

实际应用中通常采用分层摘要策略

近期对话(最近3轮)→ 保留原文,不压缩 中期对话(4-10轮)→ 保留摘要 远期对话(10轮以上)→ 只保留关键事实(用户偏好、重要决策)

2.3 Token 预算管理:把上下文当成"钱"来花

一个成熟的 Context 管理系统,会把 Token 当作预算来管理:

classTokenBudget:def__init__(self,max_tokens:int=100000):self.max_tokens=max_tokens self.reserved_for_output=4000# 预留给模型输出self.reserved_for_system=2000# 预留给系统提示self.reserved_for_tools=3000# 预留给工具定义@propertydefavailable_for_context(self)->int:return(self.max_tokens-self.reserved_for_output-self.reserved_for_system-self.reserved_for_tools)defallocate(self,messages:list)->list:"""从最新到最旧填充消息,直到预算用完"""budget=self.available_for_context selected=[]formsginreversed(messages):cost=count_tokens(msg)ifbudget-cost<0:breakselected.insert(0,msg)budget-=costreturnselected

这保证了无论对话多长,上下文都不会溢出,且不会在单次请求中烧掉过多预算。


三、方案二:RAG —— 让模型"遇到问题再去查"

3.1 RAG 解决什么问题?

上下文窗口管理解决的是"对话历史怎么放"的问题。但还有另一类信息:外部知识

你的公司有 500 页的产品文档。你不可能每次都把它们全部塞进 Prompt——成本太高,大部分内容也和当前问题无关。你需要的是:按需检索,只把相关的部分放进去。

这就是 RAG(Retrieval-Augmented Generation,检索增强生成):

用户提问 → 向量检索 → 取 Top-K 相关文档 → 拼入 Prompt → 模型生成答案

3.2 RAG 的核心组件

一个完整的 RAG 系统包含以下组件:

┌─────────────────────────────────────────────────────────┐ │ RAG 系统架构 │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │ │ 文档导入 │ → │ 文本分块 │ → │ 向量化 & 存储索引 │ │ │ │ (PDF/MD) │ │ (Chunking)│ │ (Embedding + DB) │ │ │ └──────────┘ └──────────┘ └──────────────────┘ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │ │ 用户提问 │ → │ 向量检索 │ → │ Prompt 组装 │ │ │ │ │ │ (Top-K) │ │ + LLM 生成 │ │ │ └──────────┘ └──────────┘ └──────────────────┘ │ └─────────────────────────────────────────────────────────┘

① 文档分块(Chunking)

把长文档切成小块。块太大 → 检索精度下降。块太小 → 语义不完整。

常见策略:

  • 固定大小分块:每 500 Token 一块,重叠 50 Token。
  • 语义分块:按段落、章节自然边界切分。
  • 递归分块:先用大分隔符(章节标题),如果块仍然太大,再用小分隔符(段落)。
fromlangchain.text_splitterimportRecursiveCharacterTextSplitter splitter=RecursiveCharacterTextSplitter(chunk_size=500,chunk_overlap=50,separators=["\n\n","\n","。","."," "])chunks=splitter.split_text(document)

② 向量化(Embedding)

把文本转成向量。语义相似的文本,向量距离近。

fromopenaiimportOpenAI client=OpenAI()defembed(text:str)->list[float]:response=client.embeddings.create(model="text-embedding-3-small",input=text)returnresponse.data[0].embedding

③ 向量检索

用户提问 → 向量化 → 在向量数据库中搜索最相似的 K 个文档块。

importchromadb client=chromadb.Client()collection=client.get_or_create_collection("knowledge_base")results=collection.query(query_embeddings=[embed(user_question)],n_results=5)

④ Prompt 组装

把检索到的文档块拼入 Prompt,作为"参考资料":

context="\n\n".join(results['documents'][0])prompt=f"""请根据以下参考资料回答用户的问题。 如果参考资料中没有相关信息,请如实说明。 参考资料:{context}用户问题:{user_question}回答:"""

3.3 RAG 的关键质量因素

因素影响建议
分块策略检索精度根据文档类型选择(代码用小块,文章用段落)
Embedding 模型语义匹配质量使用 text-embedding-3 或 bge-large 等高质量模型
Top-K 选择信息充分性 vs 噪声一般 3-8 个,根据文档密度调整
检索后重排(Rerank)相关性排序用 Cross-Encoder 对 Top-K 结果重新打分

四、方案三:长期记忆 —— 跨会话的信息持久化

4.1 对话摘要 vs 长期记忆

上下文窗口管理和 RAG 解决的是"本次对话内的信息管理"。但还有一种需求:跨会话的记忆

用户上周跟你说过他喜欢简洁风格,这周你再推荐方案时,应该优先推简洁的。这种记忆需要持久化存储。

4.2 长期记忆的实现

fromdatetimeimportdatetimeimportjsonclassLongTermMemory:def__init__(self,db):self.db=db# 向量数据库 + 结构化存储defextract_memories(self,conversation:list)->list[dict]:"""从对话中提取值得记住的信息"""prompt=f"""从以下对话中提取值得长期记住的信息。对于每条信息, 判断它属于哪种类型:fact(事实)、preference(偏好)、decision(决策)。 对话:{json.dumps(conversation,ensure_ascii=False)}返回 JSON 数组,每条格式: {{"type": "fact|preference|decision", "content": "...", "importance": 1-5}} """response=llm_call(prompt)returnjson.loads(response)defsave_memories(self,memories:list[dict]):"""存入向量数据库"""formeminmemories:embedding=embed(mem["content"])self.db.insert(embedding=embedding,metadata={"type":mem["type"],"content":mem["content"],"importance":mem["importance"],"timestamp":datetime.now().isoformat()})defrecall(self,query:str,top_k:int=5)->list[str]:"""根据当前话题检索相关记忆"""results=self.db.search(embed(query),top_k=top_k)# 按 importance 排序,高重要性的优先results.sort(key=lambdax:x["metadata"]["importance"],reverse=True)return[r["metadata"]["content"]forrinresults]

使用时,每次对话开始前检索相关记忆,拼入 System Prompt;每次对话结束后提取新记忆,存入数据库。


五、Context Engineer 的天花板

Context Engineer 解决了"记忆"问题,但新的天花板随之而来:

它有记忆,但没有手脚。

模型可以记住你喜欢简洁风格,但它不能帮你把代码改成简洁风格。它可以检索到 Docker 部署文档,但它不能帮你执行docker-compose up。它可以分析你的聊天记录总结出你的偏好,但它不能帮你把偏好配置写进系统里。

Context Engineer 的能力边界: ✅ 记住你是谁、你喜欢什么 ✅ 从知识库中检索相关信息 ✅ 基于历史对话理解上下文 ❌ 操作外部系统(发邮件、调API、改数据库) ❌ 执行代码或命令 ❌ 自主发起行动(它还是"你问它答")

Context Engineer 给了 AI “记忆”,但 AI 仍然只是一个"博学的顾问",不是一个"能干的助手"。

要让 AI 从"说"变成"做",我们需要下一阶段的进化——Harness Engineer。


总结

Context Engineer 通过三大方案解决了 LLM 的"失忆"问题:

  1. 上下文窗口管理(滑动窗口、摘要压缩、Token 预算):在有限窗口中装下最有价值的信息。
  2. RAG(检索增强生成):让模型"按需检索"外部知识,而非把所有知识塞进 Prompt。
  3. 长期记忆:跨会话持久化用户偏好、决策和关键事实。

这些技术构成了 AI 应用的"记忆层"——没有它,AI 永远是"鱼的记忆,七秒一轮回"。

但 Context Engineer 的局限同样清晰:它只能"说",不能"做"。下一篇,我们将进入 Harness Engineer 的世界——给 AI 装上"手脚",让它真正能干活的工具调用与 API 编排。

http://www.gsyq.cn/news/1610937.html

相关文章:

  • 储能 PCS 远程运维怎么做?OTA 升级、固件调试与协议授权的 6 个工程点
  • 【python】我用AI辅助开发了LanChat 局域网即时通讯的小软件
  • SwiftKey整合GPT-4 Turbo:移动端AI输入范式重构
  • VLA-Adapter论文解读(二):三大关键发现
  • Advanced XRay技术深度解析:如何通过方块渲染优化实现高效矿石定位
  • 灵衢协议学习——物理层(三)
  • 抖音内容保存终极指南:douyin-downloader让你的收藏变得轻松高效
  • 【数字孪生国标落地第一个月,我给新能源行业测了测段位】
  • 虚拟摇杆vJoy:Windows游戏控制器模拟的技术深度解析
  • 智慧矿场施工状态监测 推土机识别 装载机数据集 挖掘机等工程机械数据集第10096期
  • .NET 8加持:C#上位机调用国产PLC运动控制指令实战
  • lac_agent自愈链路上篇——crontab守护的那些坑与健康检查实战
  • YOLOv8一站式实战:图像分类、目标检测与实例分割全解析
  • 海上船舶识别数据集 渔船监测 货船识别 游艇数据集 油轮识别图像数据集 船舶类分类和测数据集 数据集第10163期 数字化智能化识别数据集
  • 如何用ShaderGlass为Windows桌面添加实时GPU着色器效果
  • 想做 AI 时代的 FDE?先过三关:找行业、定方向、以身入局
  • 3.2 APP测试实战:功能、性能与ADB全解析
  • 卡在 FDE 入门的哪一步了?先判断该扛还是该换
  • AUTOSAR E2E Profile规范介绍
  • 战略升级!从传统定位到数字定位
  • 终极Windows窗口强制调整工具:轻松解决顽固窗口大小问题
  • Python之yandex-annlib包语法、参数和实际应用案例
  • 数字校园SQL注入防御:从原理到实战的纵深检测与动态响应体系
  • Windows系统文件hidserv.dll丢失找不到问题解决
  • 数据分析师成长路径:从思维到工具,构建解决实际问题的核心能力
  • ai-image-gen-mcp MCP 服务说明文档
  • DART:采样两份草稿估计思考预算,节省 67% token 效果还更好
  • 机器学习与模式识别 第一章 机器学习导论 考点压缩
  • 数据安全检查,这3个API盲区最容易被问穿
  • 基于改进YOLOv8与无人机航拍的电动自行车违规行为智能检测系统实战