
1. 为什么 Agent 搭建不是重点上下文引擎才是过去一年LLM Agent 的开发范式发生了一个微妙但重要的转向。早期大家在讨论 ReAct、Function Calling 怎么写后来开始聊 Agent 框架选型再往后是 Multi-Agent 编排而现在越来越多团队发现真正的瓶颈不在“能不能让模型调用工具”而在“模型怎么记住并组织这次任务过程中产生的全部信息”。先说一个足够准确的判断Agent 的推理能力上限取决于它当前能看到的上下文质量。Prompt 再精心设计工具链再完整如果上下文组织得乱七八糟Agent 就像一位记忆力只有几秒钟的实习生每个步骤都要重新交代一遍。这才是无数 Agent 项目“Demo 惊艳、生产翻车”的最根本原因。很多开发者遇到的现象极其相似Agent 处理三五个步骤时表现良好任务链路一长就开始重复调用同一个工具忘记用户最开始提的限制条件甚至在多轮对话里把旧结论当成新事实。表面上看是模型不够强实际上是上下文这一层没人认真设计。这篇文章要解决的核心问题就是在 Agent 搭建之外把上下文引擎这个关键组件拆开讲清楚它到底是什么、和普通 Prompt 拼接有什么区别、怎么设计才能撑起一个生产级 Agent以及落地时会遇到哪些真实的坑。适用读者也比较明确已经在用 LangChain、Coze、Dify 或其他框架搭过 Agent但发现长任务、复杂任务稳定性和记忆能力明显不够的开发者正在做 Agent 架构设计需要说服团队把上下文管理当成一等公民的技术负责人以及准备面试 Agent 方向岗位被问到“上下文引擎”“Agent 记忆”“长对话一致性”时希望有体系化理解的候选者。2. 上下文本管理Agent 系统里最容易被低估的组件2.1 从 Prompt 到上下文引擎的认知升级可以用一个类比来理解问题。传统软件里变量是程序在运行过程中保存信息的方式函数调用之间通过参数和返回值传递数据。而 LLM Agent 里上下文就是程序的“内存”模型每生成一个 Token 都要参考它。没有哪段业务逻辑不依赖上下文它决定了 Agent 能记住什么、忽略什么、依据什么做决策。这个类比可以帮助理解后续所有设计决策。如果程序把所有变量都塞进全局变量代码很快就乱成一团。Agent 也一样如果把对话历史、工具返回结果、系统指令、用户目标全部塞进一个巨大字符串里喂给模型短期能跑规模一上来必然失控。所谓上下文引擎就是在模型之外增加的一层系统化机制负责对进入模型视野的信息进行采集、筛选、组织、压缩、检索和更新。它不是某一个函数而是一整套策略的组合。2.2 “框架已解决”的背后是伪共识不少人会问LangChain 的 Memory 模块不是已经解决了吗AutoGPT 不也能记忆吗这个问题本身值得展开。LangChain 的 Memory 确实提供了各类内置实现但本质上多数是对话历史的管理解决的是短期记忆问题。RAG 解决的是从外部知识库检索相关资料的问题属于静态知识引入。长期记忆系统记住用户偏好但它和当前任务执行中的“过程上下文”并不是同一层面的东西。真正容易出问题的是 Agent 在一段复杂任务中产生的中间推理、工具调用参数、返回结果、修正记录。这套信息流既不像长期记忆那样稳定也不像 RAG 那样可以预先索引。如果没有任何结构化设计它会以最原始的形式增长最终把模型上下文窗口塞满或者导致关键信息被淹没。所以这里需要拉高一个关键认知上下文引擎不是简单的“记忆方案”而是 Agent 运行的完整信息底座。2.3 上下文引擎的演进阶段从实践角度可以给上下文管理划分成几个阶段阶段做法典型问题原始拼接把系统提示、对话历史、工具结果直接拼成字符串上下文过长、信息丢失、成本失控手工裁剪开发者写规则截断或摘要历史误删关键信息、摘要失真、规则难维护结构化存储按消息、工具调用、任务状态分表管理需要额外维护成本、检索设计不当会引入噪声记忆管理体系分层记忆自动摘要向量检索上下文压缩设计复杂度高但能真正支撑生产级 Agent上下文引擎从任务目标出发动态组织每次模型调用所需的信息这是当前 Agent 架构上的关键突破口观察 2025 年到 2026 年的技术演进会发现凡是走向生产环境的 Agent 框架几乎都在往上下文工程的方向收敛主动管理 token、按重要性分层记忆、根据任务阶段动态注入信息、对历史内容做语义压缩。这些能力组合起来就是上下文引擎。3. 上下文引擎的工作原理拆解3.1 工作流程总览一个完整的上下文引擎通常包含多个组成部分上下文采集器捕获用户输入、系统事件、工具调用结果、模型中间输出。上下文过滤器基于任务相关性决定哪些信息进入模型视野。上下文压缩器对历史内容做摘要、结构化提取控制 Token 增长。上下文存储短期工作记忆和长期知识分开存放。上下文检索器按需召回而不是全量注入。上下文更新器任务推进后同步状态确保模型看到的信息始终最新。这六个部分组合起来形成一条完整流水线。当用户输入新消息时引擎先更新任务状态再决定当前这一步模型需要看到哪些历史信息从存储中检索出来和当前输入一起组织成最终的模型请求。关键变化在于模型请求不再是从对话开始到现在所有内容的堆积而是“当前这一步最优信息集”。3.2 Token 预算机制生产级上下文引擎的核心设计基础是 Token 预算。模型上下文窗口有硬上限但实践中不建议用到接近上限原因有三推理延迟随 Token 数增长可能拖慢用户体验。输入 Token 越多模型对关键信息的注意力被稀释。成本随 Token 线性增加长任务成本会失控。上下文引擎的工作方式就是把有限的 Token 窗口划分成几个逻辑分区系统指令区固定占用描述 Agent 的身份、能力、约束。任务目标区记录用户当前的核心诉求长期保持不变。最新对话区保留最近几轮对话原文保证即时性。检索记忆区从工作记忆或长期记忆中检索到的相关信息。工具上下文区最近一次工具调用的输出可能被立即消费。引擎每次构造请求时根据当前的阶段动态决定每个分区的内容而不是机械地把所有内容都塞进去。3.3 记忆的分层策略上下文引擎通常把记忆分为三个层次工作记忆当前任务进行中产生的短期信息比如“已经查了 2024 年销售数据”“用户要求排除华东区”。这部分保存在结构化对象里任务结束即可清除。情景记忆过去完成的任务记录、用户偏好、历史结论。这部分适合用向量数据库做语义检索需要时召回。语义记忆从对话中提炼出的用户画像、领域知识、规则约束。这部分时效性长适合独立存储持续更新。这三个层次对应不同的读写频率和生命周期混合检索策略非常适合这种情况先用关键词或规则匹配锁定高置信度信息再用语义向量召回相似记忆最后按时间加权排序把最相关的信息送入上下文。3.4 为什么上下文引擎比提示词工程更根本提示词工程优化的是“怎么说模型才听得懂”上下文引擎解决的是“决策时该把哪些信息摆在模型面前”。二者不在一个层级。一个拥有完美提示词但上下文混乱的 Agent像是给一位聪明人一份残缺的会议纪要让他做决策。而上下文引擎设计好的 Agent即使 Prompt 写得朴素也能在长任务中保持稳定输出。4. 最小实现用代码跑通一个上下文引擎4.1 架构设计为了把原理落到实操这里实现一个最小但完整的上下文引擎示例。核心组成包括TaskContext表示当前任务的状态和结论。HistoryManager管理对话历史、自动摘要。ContextBuilder根据当前任务阶段组装最终上下文。MemoryStore负责短期和长期记忆的存储与检索。示例代码使用 Python模型调用采用 OpenAI 兼容接口方便对接各类兼容模型提供商。版本方面不做强制固定请以你实际选择的依赖为准本文重点展示通用设计思路。4.2 数据库和依赖准备先安装基础依赖pip install openai numpy # 向量存储部分可选项目按需安装 pip install sqlite-vss # 或者使用支持向量检索的库如 chromadb pip install chromadb数据库使用 SQLite 存结构化上下文使用json字段保存扩展信息保证实验环境的轻量性。生产环境可以根据数据量换成 PostgreSQL 加 pgvector或者独立的向量数据库。4.3 TaskContext 核心代码首先定义一个TaskContext类它维护任务的目标、约束和当前结论是上下文引擎的中枢对象。# 文件路径context_engine/task_context.py from pydantic import BaseModel, Field from typing import List, Optional from datetime import datetime class TaskContext(BaseModel): task_id: str Field(..., description任务唯一标识) goal: str Field(..., description用户原始目标不随对话变化) constraints: List[str] Field(default_factorylist, description任务约束列表) status: str Field(defaultrunning, description任务状态) intermediate_results: List[str] Field(default_factorylist, description阶段性结论) current_step: int Field(default0, description当前执行步骤) last_update: str Field(default_factorylambda: datetime.now().isoformat()) def add_constraint(self, constraint: str) - None: 添加用户约束避免 Agent 后续跑偏 if constraint not in self.constraints: self.constraints.append(constraint) self.last_update datetime.now().isoformat() def add_result(self, result: str) - None: 添加阶段性结论 self.intermediate_results.append(result) self.current_step 1 self.last_update datetime.now().isoformat() def to_dict(self) - dict: return self.model_dump()这个类的设计意图很明确goal保持不变constraints持续累积intermediate_results是 Agent 已经产出的结论。构造提示词时前两者必须出现在最关键的位置后者按需截取。4.4 HistoryManager 自动摘要压缩对话历史无限膨胀是长任务失控的重要原因。HistoryManager负责维护最近 N 轮对话原文并在超出阈值时生成摘要。# 文件路径context_engine/history_manager.py from typing import List, Dict import json class HistoryManager: def __init__(self, max_recent_turns: int 6): self.max_recent_turns max_recent_turns self.recent_turns: List[Dict] [] self.summary: str def append(self, role: str, content: str) - None: self.recent_turns.append({role: role, content: content}) if len(self.recent_turns) self.max_recent_turns: # 超出容量先压缩最旧的一半 self._compress_old_turns() def _compress_old_turns(self): overflow self.recent_turns[:-self.max_recent_turns] self.recent_turns self.recent_turns[-self.max_recent_turns:] if overflow: new_summary self._summarize_turns(overflow) self.summary self.merge_summary(self.summary, new_summary) def _summarize_turns(self, turns: List[Dict]) - str: # 实际项目中调用 LLM 做摘要这里返回占位文本 text \n.join( f{turn[role]}: {turn[content][:100]} for turn in turns ) return f[摘要] 用户与助手进行了 {len(turns)} 轮对话核心内容包括{text[:200]}... def merge_summary(self, old_summary: str, new_summary: str) - str: # 合并两段摘要时同样可以通过 LLM 完成 if not old_summary: return new_summary return f{old_summary} {new_summary} def get_messages(self, max_context_size: int 4000) - List[Dict]: messages [] if self.summary: messages.append({role: system, content: f历史摘要{self.summary}}) messages.extend(self.recent_turns) return messages实际生产中可以每次压缩时调用 LLM 生成高质量摘要也可以使用映射归约摘要法对大段内容先分段摘要再合并。示例代码中用占位逻辑演示这个结构方便先跑通整体流程。4.5 ContextBuilder 组装最终请求上下文ContextBuilder是整个引擎的组装车间。它接收任务上下文、历史消息、检索到的记忆按优先级组织成最终请求。# 文件路径context_engine/context_builder.py from typing import List, Dict from .task_context import TaskContext class ContextBuilder: def __init__(self, history_manager): self.history_manager history_manager SYSTEM_PROMPT_TEMPLATE 你是一个执行任务的专业助手。请严格遵循以下信息完成工作。 ## 核心目标 {goal} ## 约束条件 {constraints} ## 当前任务阶段 已完成 {current_step} 个步骤当前状态{status} ## 阶段性结论 {intermediate_results} 请基于以上信息继续推进任务。如果发现信息不足请明确表达需要补充什么。 def build_messages(self, task_context: TaskContext, user_input: str, retrieved_memories: List[str] None) - List[Dict]: system_prompt self.SYSTEM_PROMPT_TEMPLATE.format( goaltask_context.goal, constraints\n.join(f- {c} for c in task_context.constraints) if task_context.constraints else - 无, current_steptask_context.current_step, statustask_context.status, intermediate_results\n.join( f{i1}. {r} for i, r in enumerate(task_context.intermediate_results[-5:]) ) if task_context.intermediate_results else - 暂无 ) messages [{role: system, content: system_prompt}] # 注入检索到的长期记忆 if retrieved_memories: memory_text \n.join(f- {m} for m in retrieved_memories) messages.append({role: system, content: f相关历史记忆\n{memory_text}}) # 注入历史消息含摘要 messages.extend(self.history_manager.get_messages()) # 注入当前用户输入 messages.append({role: user, content: user_input}) return messages这里每一条都有明确用途核心目标和约束放在 System Prompt 最靠前的位置确保模型不会遗忘用户根本诉求阶段性结论只取最近五条避免旧结果干扰当前决策历史消息只保留摘要加最近几轮原文平衡信息和成本。4.6 MemoryStore 加入长期记忆长期记忆部分使用向量检索。可以用 ChromeDB 做实验也可以自己实现一个基于词向量的简易检索器。下面的代码展示结构设计# 文件路径context_engine/memory_store.py import chromadb from chromadb.utils import embedding_functions from typing import List class MemoryStore: def __init__(self, collection_name: str agent_memory): # 默认使用 all-MiniLM-L6-v2 这类 embedding 模型或接口 self.client chromadb.Client() self.collection self.client.get_or_create_collection( namecollection_name, embedding_functionembedding_functions.DefaultEmbeddingFunction() ) def add_memory(self, text: str, metadata: dict None): self.collection.add( documents[text], metadatas[metadata or {}], ids[fmem_{self.collection.count()}] ) def search(self, query: str, top_k: int 3) - List[str]: results self.collection.query(query_texts[query], n_resultstop_k) if not results[documents]: return [] return results[documents][0]需要重点提示的是默认 embedding 模型的效果在中文场景可能不理想实际项目建议使用专门的向量服务或中文效果更好的 Embedding 模型。4.7 主流程打通设计一个可运行的主程序模拟用户发起一个多步骤任务。# 文件路径main.py from context_engine.task_context import TaskContext from context_engine.history_manager import HistoryManager from context_engine.context_builder import ContextBuilder from context_engine.memory_store import MemoryStore def simulate_agent_step(messages): 模拟模型调用真实项目中替换为 LLM API 请求 # 这里简化返回固定内容真实场景调用 OpenAI 兼容接口 return 我已经完成第一步分析正在整理结果。 def main(): # 初始化上下文引擎 history HistoryManager(max_recent_turns4) builder ContextBuilder(history) memory MemoryStore() # 创建任务 task TaskContext( task_idtask_001, goal分析华东区过去三个月的销售趋势并给出下季度建议 ) task.add_constraint(只使用内部销售数据) task.add_constraint(输出必须包含数据支撑) # 让 Agent 执行第一步Agent 内部先查数据 query 请先查询华东区最近三个月的销售数据 messages builder.build_messages(task, query) # 模拟 Agent 调用工具后返回结果 tool_result 华东区1月销售额320万2月350万3月380万 task.add_result(f查询到销售数据{tool_result}) history.append(user, query) history.append(assistant, 已查询相关数据数据来源为内部销售系统。) # 让 Agent 执行第二步分析趋势 query2 基于数据分析销售趋势 messages2 builder.build_messages(task, query2) result2 simulate_agent_step(messages2) task.add_result(销售额呈持续上升趋势环比增幅依次为9.4%和8.6%) history.append(user, query2) history.append(assistant, result2) # 打印最终构造的上下文仅展示关键部分 print( 最终构造的模型请求 ) for msg in messages2: print(f\n--- {msg[role]} ---) print(msg[content][:300]) # 保存长期记忆 memory.add_memory( 华东区2025年Q1销售数据1月320万2月350万3月380万, metadata{region: east_china, quarter: Q1} ) memory.add_memory( 华东区销售趋势持续上升适合下季度加大市场投入, metadata{region: east_china, topic: trend} ) # 验证检索 print(\n 检索测试 ) related memory.search(华东区销售怎么样, top_k2) for r in related: print(f召回{r[:80]}) if __name__ __main__: main()运行方式python main.py预期能正常打印出系统提示、历史消息和检索结果。整个程序的核心验证点是在第二步构造请求时TaskContext中的“阶段性结论”已经包含了第一步的数据查询结果但历史消息不会无限膨胀。4.8 如何接入真实 LLM 和工具调用模拟代码跑通后把simulate_agent_step替换为真实的模型调用即可。以 OpenAI 兼容接口为例from openai import OpenAI client OpenAI( api_key你的API密钥, base_url你的模型服务地址 ) def call_llm(messages): response client.chat.completions.create( model你的模型名称, messagesmessages, temperature0.3 ) return response.choices[0].message.contentAgent 执行工具调用的循环中每次工具返回后都应调用task.add_result()把工具结果写入上下文这样下一轮模型请求才能感知到当前状态。这就是“上下文引擎驱动 Agent”的核心闭环。5. 多 Agent 协作下会大幅放大的上下文复杂性5.1 多 Agent 与上下文的关系单 Agent 的上下文问题已经足够复杂但 Multi-Agent 架构下上下文管理会成倍放大。主从模式中主管 Agent 需要决定哪个子 Agent 最适合处理当前任务子 Agent 执行完任务后需要把结果汇报给主管主管要把多路结果整合进入整体任务上下文。只靠对话历史传递多 Agent 协作状态很快就会让信息流变得难以追踪。5.2 主从模式的上下文传递陷阱一个常见误区是让资深开发者在设计多 Agent 系统时把子 Agent 当成了独立的“员工”。但实际上最合理的实现方式是把子 Agent 当作另一种“工具”Tool来调用。这个认知转变在多 Agent 设计中极其重要。在这种视角下子 Agent 的输入输出就是工具调用的输入输出。主 Agent 无需完整感知子 Agent 内部的每一次思考只需要知道我给子 Agent 什么任务它返回了什么结果。这种模式天然限制了上下文的膨胀子 Agent 的详细推理过程不会进入主 Agent 的视野最终汇报内容才是需要保留的部分。# 文件路径multi_agent_example.py class SubAgentTool: 将子 Agent 包装为工具限制上下文扩散 def __init__(self, name: str, system_prompt: str): self.name name self.system_prompt system_prompt # 子 Agent 有自己的上下文引擎实例 self.context_engine ContextBuilder(HistoryManager()) def run(self, task_input: str) - str: messages [ {role: system, content: self.system_prompt}, {role: user, content: task_input} ] # 调用子 Agent 的 LLM只返回最终结果 result call_llm(messages) # 主 Agent 只拿到最终结果不感知中间过程 return f[{self.name}完成] {result}主 Agent 在调用子 Agent 时只需要把任务描述和必要的背景信息传入子 Agent 返回结果后主 Agent 更新自己的TaskContext完成一次状态推进。子 Agent 内部的上下文历史不会传染给主 Agent两个上下文引擎各司其职。5.3 协作编排中的上下文共享策略两个常见协作模式值得区分协同型所有 Agent 共享同一个任务目标但分工不同。比如“数据分析 Agent”和“报告写作 Agent”协作完成一份调研。这种模式需要共享任务状态可以设计一个共享的TaskContext对象各 Agent 读它、更新它。串联型Agent A 的输出是 Agent B 的输入任务像流水线一样推进。这种模式不需要共享完整上下文只需要把 A 的输出精简后传给 B。中间产物需要做“格式化交接”避免把 A 的噪音传给 B。共享上下文的实现关键在于“谁负责写入哪个字段”要非常清晰否则会出现多个 Agent 同时更新intermediate_results导致状态错乱的问题。最佳方式是定义明确的字段所有权每个 Agent 只允许写自己的命名空间。6. 落地中的常见问题与排查思路上下文引擎第一次接入项目时会碰到一些有共性的问题整理成常见问题排查清单。问题现象可能原因排查方式解决方案Agent 忘记用户最初要求goal没有被固定注入每次请求查看 System Prompt 中目标字段是否存在确保TaskContext.goal永远出现在每次请求的第一块上下文无限膨胀没有做摘要压缩或摘要策略失效打印每次请求前后的 Token 数配置最大保留轮数引入自动摘要历史摘要丢失关键细节摘要算法只保留最近内容检查摘要合并逻辑采用两级摘要结构摘要关键数据摘要工具调用结果反复被丢弃工具结果只进了对话历史没有进TaskContext检查工具循环代码工具返回后调用task.add_result()写入阶段结论多 Agent 信息串扰子 Agent 的详细历史进入了主 Agent 上下文检查主 Agent 请求内容子 Agent 包装为工具只返回最终结果检索召回大量无关记忆Embedding 模型在中文场景效果差或纯向量检索噪声高对召回结果做相关性检查混合检索关键词向量时间加权Token 成本超预期检索到的记忆过多、历史保留过长统计各分区 Token 占比为每个分区设置 Token 预算上限6.1 排错的第一步遇到上下文相关的问题第一件事不是调 Prompt而是把 Agent 每次请求模型的消息完整打印出来。缺少可观测性上下文问题排查就等于盲人摸象。建议在上下文引擎里输出结构化日志import logging logging.basicConfig(levellogging.INFO) def log_context_built(messages): total_tokens sum(len(m[content]) for m in messages) logging.info(f构建上下文{len(messages)}条消息约{total_tokens}字符) for i, msg in enumerate(messages): logging.info(f [{i}] {msg[role]} 前50字{msg[content][:50]})7. 生产级上下文引擎的工程建议7.1 上下文数据模型先行不要等项目跑起来再补上下文结构。先在代码库中把TaskContext、MessageRecord、MemoryRecord定义清楚并明确每个对象的生命周期。数据结构没有想清楚之前写出的 Agent 代码最后几乎都要推倒重来。7.2 每个分区都要 Token 预算为 System Prompt、历史摘要、最近对话、检索记忆分别设置 Token 上限。实现方式可以在ContextBuilder中加一个max_token_budget参数构造时按优先级分配class ContextConfig: system_prompt_budget: int 800 memory_budget: int 600 history_summary_budget: int 1000 recent_turns_budget: int 1500 user_input_budget: int 500实际分配比例要根据任务类型调整但“有预算”本身比“预算多少”更重要。没有预算的上下文系统就像没有经费预算的项目团队迟早失控。7.3 记忆回写策略长期记忆不是自动记录的需要有触发条件任务完成时把任务摘要写入长期记忆。用户明确表达了偏好值得写入。发现与已知事实冲突时需要更新而不是追加。批量写入时做去重防止冗余。写记忆时带上时间戳、来源任务 ID、置信度方便后续检索时排序和过滤。置信度不高但可能相关的记忆宁可暂时不写也不要污染记忆库。7.4 上下文引擎的可测试性把上下文构建做成纯函数风格更有利于测试输入是任务状态和历史记录输出是最终消息数组。不依赖真实 LLM 调用可以写断言验证核心目标字段必然出现在每条系统提示中。超过 N 轮的旧对话不会进入最近消息区。阶段性结论只能取最近 N 条。这种可测试性会让上下文引擎的演进变得安全后续加新策略时不会引入回归。7.5 安全边界上下文引擎管理大量用户信息需要注意记忆库中的敏感信息需要脱敏后存储。检索结果在注入提示词前做过滤防止引入未授权数据。用户有权要求清空自己的记忆数据需要有明确的删除接口。除数据安全外还需要警惕 Prompt Injection恶意工具输出可能尝试篡改上下文。上下文引擎应对工具返回内容做隔离标记并将其视为不可信数据防止工具输出冒充系统指令。8. 总结与后续学习方向Agent 的输入输出接口并不神秘工具调用也有成熟范式可以抄但上下文引擎决定了一个 Agent 在复杂场景中能不能稳定地跑完长链路。这也是当前 Agent 开发从“能跑 Demo”走向“能上生产”的关键分水岭。本文用一个最小上下文引擎示例演示了核心架构但生产级实现还需要进一步探索上下文压缩算法的效果对比摘要式压缩、结构化提取、选择性丢弃、递归摘要之间如何权衡。记忆的长期维护实体记忆、会话记忆、事件记忆如何融合如何处理事实冲突和时效性问题。上下文引擎的可观测性如何让每一次模型调用都变得可调试、可追踪、可回放。多 Agent 场景下的上下文共享与隔离哪些信息必须共享哪些必须隔离怎样避免串扰。Agent 安全在上下文注入环节如何防御恶意输入如何建立信任边界。对于已经开始做 Agent 项目的读者建议下一步不要再继续堆功能而是把当前自己项目里的上下文构建过程完整梳理一遍模型每次请求到底看到了什么这些信息是否都是当前步骤需要的最优信息集有没有关键信息被挤出了窗口把这些问题回答清楚Agent 的稳定性会得到实打实的提升。