跳到正文
Elaine Blog
返回

把上下文工程串起来:实现组装流程,并定位回答失败

更新于:
Context Engineering

现在把前面的机制放进同一个过程:第一次准备 A1042 的运费咨询,第二次用户修改取件意向。我们希望第二轮继承正确状态、保留完整政策条件、排除无关记忆,并且不复用旧状态对应的缓存。

本篇提供的程序使用标准库构造上下文,不调用模型,不访问真实订单,也不执行退款。这样你可以先看到信息如何变化,再接入模型判断答案质量。所有预期现象来自代码设计,不是本次运行记录。

文件怎样分工

将这些文件保存在同一个目录,入口是 context_walkthrough.py下载。它调用 context_pipeline.py下载state_transitions.py下载memory_lifecycle.py下载compaction_snapshot.py下载cache_versions.py下载

组装器负责本次请求,状态文件负责用户修改,记忆文件负责跨会话偏好,快照文件负责保留继续任务所需字段,缓存文件负责区分前后依赖。这里的字典代替外部存储,只用于顺序演示。

# 在保存了上述六个文件的目录执行;主线示例只需要 Python 3.10 或更高版本。
# 此命令供读者运行,本次修订未执行它。
python context_walkthrough.py

先看进入组装器的东西

fixture() 手工构造候选:当前状态、订单、政策条件、运费结论、旧版政策、其他租户资料和很长的旧历史。它故意混入错误候选,以展示装配层的防御性判断;真实数据源应更早按权限限制查询。

Principal 表示可信主体,Item 保留内容与来源,Bundle 由策略声明哪些条目必须一起选择。政策条件和结论放在同一个必需包中,因此不会出现“保留商家承担,却丢掉经核实”的情况。

from context_pipeline import Bundle

# item_ids 引用候选 ID;两项缺一,就不能完成这个必需证据包。
policy_bundle = Bundle(
    name="complete-policy",
    item_ids=("condition", "conclusion"),
    required=True,
    priority=90,
)

本例用固定 ID 表达依赖。真实检索结果往往需要携带父条款或邻接片段关系,选择器才知道哪些部分不可拆开。由模型自由猜依赖可能有帮助,但必需政策条件不能只靠猜测。

先过滤,再按完整请求判断容量

组装器先检查租户与用户范围、用途、到期时间以及可信快照要求的来源版本。同 ID 对应不同内容会明确失败,不按输入顺序挑最后一个。相同条目的重复返回则去重。

随后先处理必需包,再处理可选包。每尝试增加一个包,都用 render() 构造候选请求,再交给计量函数判断容量。这样标题、引用和消息包装也会进入教学计数,而不仅是正文长度。

默认计量函数叫 demo_char_meter,单位明确写为 demo_characters。它计算序列化 JSON 字符数,目的是演示选择流程;它不是模型 Tokenizer,也没有把中文字符近似等同于 Token。

最后再测一次完整请求,避免没有任何条目时,用户问题和规则本身已经超限却仍然返回成功。生产适配器需要按目标接口计数或估算,并利用实际用量校准。

第一轮为什么保留这四项

在本例预算下,当前状态、订单、政策条件和结论应当被保留。其他租户资料被拒绝,旧版政策与当前要求版本不一致,超长历史属于可选包且无法放入。

trace 记录每个决定。它主要给开发者排查使用,不全部发送给模型。模型输入仅包含应用规则、问题、选中资料和来源信息;不能为了“解释为什么拒绝”又把其他租户的敏感内容放回请求。

假设后续模型仍然回答“所有质量问题都由商家承担”,可以查看条件片段是否实际进入。如果已经进入,问题可能在指令表达、生成或回答校验;如果条件根本没取到,应该先检查检索与候选来源。

这让排查从“模型不聪明”变成具体链路问题。

第二轮怎样继承并修改状态

用户说“取件意向改成后天上午,仍然先不要提交”。示例把已经解析好的绝对时间交给 apply_pickup(),它创建新状态、递增 revision,并保留未授权状态。save() 检查写入者基于哪个版本,防止旧状态无条件覆盖新状态。

下一轮 fixture() 根据新状态构造新的 state 条目,组装器重新生成请求。订单和政策可以仍然使用先前有效版本,任务状态则必须是新的。

这里没有让模型从历史自动识别时间,日期解析在调用前完成。真实接入时,可以用模型提取候选时间,再通过时区、可用时段和用户确认规则校验,不能直接把生成字符串作为执行安排。

记忆与快照在哪里参与

示例另行保存用户的 Python 代码偏好,但运费咨询不需要代码,所以不会将它加入上下文。这个安排展示的是“有记忆但不读取”,不是记忆组件坏了。

第二轮之后,从任务状态生成结构化快照,保留订单、时间、核实状态、授权状态和来源事件。validate_summary() 对照当前状态检查字段,能够发现授权位被改成 true 或来源缺失。

它不能评价任意自然语言摘要的忠实程度。若将确定性快照替换为模型摘要,需要增加来源对照、无依据新增检测和继续任务评估。本文没有把一个字段比较函数描述为完整摘要质量评估器。

为什么第二轮不能命中第一轮缓存

缓存键包含任务 revision。第一轮是 1,第二轮已经更新,因此即使查询文字类似,新请求也不会拿到旧的上下文内容。

这并不表示所有资料必须重新从数据库读取。政策只读缓存可以依据自己的版本继续命中;任务组装缓存则需要因为状态变化失效。不同层使用不同依赖,才能既复用稳定部分,又更新动态部分。

示例缓存的是教学请求结构,没有生成或缓存真实模型答案,也不会返回“退款成功”。操作结果属于业务执行记录,不在这段演示中。

用明确失败定位问题

入口最后给出两个失败场景:删除政策条件片段,以及把预算缩到连必需内容都放不下。前者得到 missing_required_data,后者得到 required_budget_exceeded。这些是设计预期,不是已运行测试的通过结果。

还可以用下面的对照表理解后续需要观察什么:

失败现象首先查看不能靠什么掩盖
条件缺失候选来源、依赖包要求模型“更谨慎”
时间没更新状态版本与来源事件重复发送旧摘要
删除的偏好又出现主记录、索引、派生摘要只清一个缓存
引用与结论不符原文支持与适用条件只验证引用 ID 存在
别人的订单进入请求数据源授权与结果复核最终输出敏感词过滤

正式评估可以比较策略前后的必要事实保留、条件正确性、缺口识别和任务完成率,再比较输入用量及等待时间。样本需要涵盖失败类型,不能只看正常咨询的平均得分。用户数据用于评估时仍要满足允许用途和保留要求。

在 LangGraph 源码里理解消息更新

框架可以帮助保存执行状态,但不会自动替你决定哪些业务事实有效。为了把两件事分开,源码导读固定到 LangGraph 1.0.0,不是宣称它是当前最新或生产推荐版本。

查看该版本的 graph/message.pyMessagesState 为消息字段绑定 add_messages 归并函数。它按消息 ID 合并:新 ID 追加,同 ID 替换,删除消息使用相应删除对象。这解释了为什么更新消息不是总在列表末尾增加一条。

langgraph_messages.py下载 先写“明天下午”,再用同一个 ID 写“后天上午”,最后移除它。示例只调用归并函数,不调用模型。

from langchain_core.messages import HumanMessage
from langgraph.graph.message import add_messages

# 相同 ID 表示更新同一条状态消息;它不自动更新独立的业务审计日志。
old = [HumanMessage(content="明天下午取件", id="pickup-1")]
new = [HumanMessage(content="后天上午取件", id="pickup-1")]
merged = add_messages(old, new)
print([(message.id, message.content) for message in merged])

继续结合 memory_graph.py下载 阅读本地执行过程:调用输入进入图,节点返回新的 AIMessage,消息字段归并,检查点保存线程状态;同线程下一轮从已有状态继续。其回复只是统计消息,不具备理解售后政策的能力。

同 ID 替换用于维护当前图状态,不代表所有旧检查点和日志已被物理删除。框架的归并规则也没有检查用户是否有权改历史;授权仍属于应用。我们读取了消息归并源码与持久化文档,没有把检查点后端所有实现都审阅或运行一遍。

源码导读使用单独的 source-requirements.txt下载。原有依赖文件继续保留,两个环境不要混装;直接依赖版本固定不等于全部传递依赖已经锁定。

接入真实模型时补哪几层

将准备好的资料通过目标 API 适配器转换成实际请求,确认角色、工具定义和消息交互格式,再替换计数函数。模型返回后记录实际用量、检查引用与必要条件;需要工具时,由执行循环在授权范围内调用,再准备下一次上下文。

将字典状态换成支持条件更新的持久化存储,加入认证和数据源过滤;将手工版本表换成业务来源提供的版本或有效性检查。需要跨请求恢复时,再接入检查点、日志及幂等执行机制。

配套 Java 工程下载 保留 AgentScope 的状态与压缩入口,供对照相同概念。它不会取代上面的业务设计。

这套实战提供的是清晰可读的上下文准备过程。真正上线还需要依据业务任务接入存储、模型和执行服务;哪些内容已是算法实现、哪些仍是教学假设,都应在代码和运行记录里保持明确。

继续阅读

上一篇: 生产中的上下文工程:设计一个可追溯的上下文管理模块

完整文件、依赖与运行边界见配套指南下载


分享这篇文章:

上一篇
生产中的上下文工程:设计一个可追溯的上下文管理模块
下一篇
Function Calling、Tool Calling 与 MCP:模型怎样使用外部能力