尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Agent 上下文管理详细教程

Agent 上下文管理详细教程 Agent 上下文管理详细教程面向进阶开发者从原理到实战系统讲解 LLM Agent 的上下文Context管理。适合已上手过 LangChain / OpenAI / 自研 Agent但被上下文爆掉、“记忆混乱”、成本失控困扰的同学。目录为什么需要上下文管理上下文窗口与 Token 基础上下文管理的核心难题六大管理策略详解实战一个完整的上下文管理器策略组合与选型最佳实践清单总结1. 为什么需要上下文管理一个 Agent智能体的核心是循环感知 → 思考 → 行动 → 观察。在每一轮循环中它都需要把到目前为止的所有信息作为上下文喂给 LLM。如果你做过真实的 Agent 项目你一定遇到过这些问题❌上下文爆掉对话进行到第 20 轮报错This models maximum context length is 8192 tokens。❌记忆混乱Agent 忘了用户两轮前的要求重复执行同一操作。❌成本失控每轮把 5000 行的历史日志全部塞进 promptAPI 账单飞涨。❌响应变慢prompt 越长首字延迟TTFT越高用户等不起。上下文管理就是要在信息完整与成本可控之间找到平衡。它决定了 Agent 的稳定性、性能和经济性。2. 上下文窗口与 Token 基础2.1 上下文窗口Context Window上下文窗口是模型单次请求能处理的最大 Token 数包含输入prompt 输出completion。模型上下文窗口GPT-48K / 32K / 128KGPT-4o128KClaude 3.5 Sonnet200KDeepSeek-V3 / R164K / 128KLlama 3.1 405B128K注意窗口越大 ≠ 越适合长上下文。研究表明模型对中间部分的注意力会衰减即Lost in the Middle中间迷失现象。所以哪怕窗口够大也要主动管理关键信息的位置。2.2 Token 是计费单位不要把 Token 想成字数。中文大体上1 汉字 ≈ 1.5~2 Token英文 1 单词 ≈ 1.3 Token。你好世界 → 4 个汉字 ≈ 6~8 Token Hello World → 2 个单词 ≈ 3 Token用 Python 快速估算importtiktoken# OpenAI 的 tokenizerdefcount_tokens(text:str,model:strgpt-4)-int:enctiktoken.encoding_for_model(model)returnlen(enc.encode(text))print(count_tokens(你好世界Hello World!))# 输出约 12~153. 上下文管理的核心难题在动手写代码前先理解我们要解决的三个矛盾3.1 记忆的保质期短期记忆工作记忆当前对话轮次必须保留。长期记忆长期偏好/事实用户说过我偏好 Python几轮后仍要生效。过期信息已完成任务的中间推理可以丢弃或压缩。3.2 关键信息的定位Agent 的每一步推理都依赖当前状态已执行了哪些动作、结果如何。如果动作列表被截断Agent 会失忆。3.3 成本与质量的权衡每多 1K token 输入费用增加但信息更全。管理策略的本质是在信息熵和 token 预算之间做取舍。4. 六大管理策略详解策略一滑动窗口Sliding Window思想只保留最近 N 轮对话丢弃最早的部分。最简单、最常用。fromcollectionsimportdequefromdataclassesimportdataclass,fielddataclassclassMessage:role:str# system | user | assistant | toolcontent:strclassSlidingWindowBuffer:滑动窗口只保留最近 max_messages 条消息def__init__(self,max_messages:int20):self.max_messagesmax_messages self.system_prompt:Message|NoneNoneself._history:deque[Message]deque()defadd(self,msg:Message):ifmsg.rolesystem:self.system_promptmsgreturnself._history.append(msg)# 超出窗口则从最前面弹出whilelen(self._history)self.max_messages:self._history.popleft()defbuild_prompt(self)-list[dict]:messages[]ifself.system_prompt:messages.append({role:system,content:self.system_prompt.content})messages[{role:m.role,content:m.content}forminself._history]returnmessages优点实现简单、零推理开销。缺点粗暴丢弃导致慢性失忆窗口太小丢关键信息太大仍会爆。策略二Token 预算控制思想不按轮数截断而是按Token 数截断——从最旧的消息开始删直到总 token 数低于预算。deftrim_to_budget(messages:list[dict],budget:int,count_fn)-list[dict]:从最旧的开始删直到总 token 数 budget。 count_fn: (text) - token_count 的函数。totalsum(count_fn(m[content])forminmessages)# 保留 system 消息不动kept[mforminmessagesifm[role]system]rest[mforminmessagesifm[role]!system]forminrest:iftotalbudget:breaktotal-count_fn(m[content])else:# rest 全删了还不够那只能压缩 systempassreturnkeptrest[len(rest)-len(kept):]ifFalseelsekept[mforminrestif...]⚠️ 上面的伪代码只演示思路。生产环境请用成熟的库见第 5 节的LangChain方案不要手写 tokenizer 边界判断。策略三摘要压缩Summarization思想把旧消息交给 LLM 生成摘要用一小段摘要替代大量原文。这是记忆保质期问题的标准解法。importopenaiclassSummarizingBuffer:def__init__(self,threshold_tokens:int3000,summary_llmNone):self.thresholdthreshold_tokens self.summarizersummary_llmorself._default_summarize self.summary# 累积的历史摘要self.recent:list[Message][]# 最近原始消息defadd(self,msg:Message):self.recent.append(msg)# 若近期消息超阈值触发压缩ifsum(count_tokens(m.content)forminself.recent)self.threshold:self._compress()def_compress(self):mergedself.summary\n.join(m.contentforminself.recent)self.summaryself.summarizer(merged)self.recent.clear()def_default_summarize(self,text:str)-str:respopenai.chat.completions.create(modelgpt-4,messages[{role:system,content:(你是记忆压缩器。把下面的对话压缩成简洁摘要保留用户偏好、完成的动作、关键结论、待办事项。不要遗漏重要事实。),},{role:user,content:text}],)returnresp.choices[0].message.contentdefbuild_prompt(self):messages[]ifself.summary:messages.append({role:system,content:f[历史摘要]\n{self.summary}})messages[{role:m.role,content:m.content}forminself.recent]returnmessages优点显著压缩 token保留核心记忆。缺点有损摘要丢细节每次压缩有 LLM 调用成本摘要可能漂移越压越失准。进阶技巧分层摘要Hierarchical / Rolling Summary——定时对已有摘要再做二次摘要避免摘要无限增长。策略四向量检索RAG 式记忆思想把历史消息切成块chunkEmbedding 后存入向量库每轮只检索与当前问题最相关的 K 个块注入上下文。这是处理超长/跨会话记忆的最优解。# 用 Chroma 做向量存储示例importchromadbfromchromadb.utilsimportembedding_functions clientchromadb.Client()colclient.get_or_create_collection(agent_memory,embedding_functionembedding_functions.DefaultEmbeddingFunction(),)defstore_memory(text:str,mid:str):col.upsert(ids[mid],documents[text])defretrieve_memory(query:str,top_k:int5)-list[str]:rescol.query(query_texts[query],n_resultstop_k)returnres[documents][0]结合 Agent 的典型流程defbuild_context_with_memory(user_query:str)-list[dict]:# 1. system prompt固定system{role:system,content:AGENT_PROMPT}# 2. 检索相关历史记忆memoriesretrieve_memory(user_query,top_k5)memory_block\n.join(f-{m}forminmemories)# 3. 动态注入return[system,{role:system,content:f[相关历史记忆]\n{memory_block}},{role:user,content:user_query},]优点可扩展、跨会话、成本可控、信息命中率高。缺点需要向量库基础设施检索质量依赖 chunk 切分与 embedding 效果对时序性敏感检索结果无序。进阶提醒检索结果要带上时间戳并在 prompt 里告诉模型越新的越可信否则可能把旧记忆当新事实。策略五结构化记忆Memory Store思想不进向量库、也不进 prompt而是把 Agent 提取出的结构化事实用户偏好、实体关系、任务状态存进数据库需要时再以结构化形式注入。fromdataclassesimportdataclassdataclassclassMemory:key:str# user.preference.languagevalue:str# Pythontimestamp:floatconfidence:floatclassMemoryStore:def__init__(self):self._mem{}# key - Memorydefextract_and_store(self,llm_response:str):让 LLM 从回复中提取结构化记忆# 示例正则或 LLM 解析出 key-valuefactsparse_facts(llm_response)# 伪代码fork,vinfacts:self._mem[k]Memory(k,v,now(),1.0)defrelevant(self,keys:list[str])-str:return\n.join(f{k}:{self._mem[k].value}forkinkeysifkinself._mem)优点记忆精准、可查询、可更新覆盖旧值、token 开销极小。缺点需要设计记忆 schema抽取环节有失败风险。策略六工具调用时的上下文隔离思想不要把所有工具结果都堆进主对话。超大工具输出如日志、数据库返回应单独存储只把摘要/指针留在上下文。TOOL_RESULT_STORE{}defrun_tool_with_large_output(tool_name:str,args:dict)-str:rawcall_tool(tool_name,args)# 可能是 10 万 tokenridf{tool_name}:{uuid4()}TOOL_RESULT_STORE[rid]raw# 存到外部# 只把摘要 引用 ID 留给 LLMsummarysummarize(raw,max_tokens200)returnf[结果已存储] 引用ID{rid}\n摘要:{summary}\n如需全文请调用 read_result(id)模型需要全文时再显式调用read_result(rid)获取。5. 实战一个完整的上下文管理器把上面策略整合成一个可用的管理器。这里用LangChain成熟方案演示避免手写 tokenizer 边界。fromlangchain_core.messagesimportHumanMessage,AIMessage,SystemMessagefromlangchain.memoryimportConversationTokenBufferMemoryfromlangchain_anthropicimportChatAnthropic# 1. 基于 token 预算的滑动窗口llmChatAnthropic(modelclaude-3-5-sonnet-20241022)memoryConversationTokenBufferMemory(llmllm,max_token_limit2000,# 上下文 token 预算return_messagesTrue,)# 2. 模拟多轮对话foruser_msgin[你好,帮我列 Python 学习计划,第二步详细一点]:memory.chat_memory.add_user_message(user_msg)respllm.invoke(memory.chat_memory.messages)memory.chat_memory.add_ai_message(resp.content)# 3. 查看当前上下文已被裁剪到预算内forminmemory.chat_memory.messages:print(m.type,|,m.content[:50])自研版策略组合管理器classContextManager:组合策略窗口 预算 摘要 向量检索def__init__(self,token_budget:int,summary_llm,vector_store):self.budgettoken_budget self.summarizersummary_llm self.vectorvector_store self.summaryself.recent:list[Message][]defadd(self,msg:Message):self.recent.append(msg)self.vector.store(msg)# 写入向量库长期记忆ifself._tokens(self.recent)self.budget*0.7:self._summarize_old()# 超预算 70% 触发压缩defbuild_context(self,query:str)-list[dict]:# 1. 检索相关记忆memoriesself.vector.query(query,top_k3)# 2. 只保留最近消息 摘要 检索出来的记忆base[{role:system,content:f[历史摘要]\n{self.summary}},{role:system,content:f[相关记忆]\n{memories}},][m.to_dict()forminself.recent[-5:]]returnbase6. 策略组合与选型场景推荐策略原因简单对话 bot滑动窗口 预算够用、便宜、零依赖单会话多轮工具调用摘要压缩 工具隔离保留推理链条防爆跨会话长期记忆向量检索 结构化记忆可扩展、可查询复杂生产 Agent窗口 摘要 向量 结构化 组合各取所长通用组合公式系统提示词固定最前面 历史摘要压缩后靠前 检索到的相关记忆动态中前部 最近 N 轮原始消息完整靠后距离提问最近 依据Lost in the Middle最重要的信息放最前或最后别放中间。通常把 system规则放最前把当前问题相关的最近消息放最后。7. 最佳实践清单Always 保留 system prompt它定义 Agent 人格与规则永不截断。区分记忆类型短期轮次内、长期事实/偏好、工作记忆当前状态分开管理。Token 预算要留余量给模型输出留出空间输出也占窗口。摘要要防漂移定期用原始数据校验摘要或做分层摘要。向量检索带时间上下文检索结果标注时间戳引导模型判断时效。大工具输出走外部存储绝不把巨型结果直接塞 prompt。监控与埋点记录每轮 token 用量、截断次数、摘要触发频率用数据调参。先压后测任何策略上线前用回归测试集验证关键能力没丢。8. 总结上下文管理是 Agent 工程中性价比最高的一环——它直接决定 Agent 能跑多久、多稳、多省钱。核心就一句话该留的留系统提示、当前状态、关键记忆该压的压旧对话、巨型输出该查的查向量检索。从最简单的滑动窗口开始业务变复杂后再逐步叠加摘要与向量检索永远是性价比最高的演进路径。如果你对某个策略尤其是向量检索的 chunk 切分、或摘要防漂移想深入了解欢迎评论区交流 本文为原创技术教程转载需注明出处。
返回列表