AgentDepthAgent 工程学习指南
输入概念或问题,检索正文和待编写目录。
第一次学习?从 L0 总览开始 →

压缩、摘要失真与缓存

我的学习 →

正在读取学习记录…

上下文压缩是把需要交给模型的信息整理得更短。删去重复段落、选取相关句子、把长记录整理成摘要,都可以减少请求中的文字。上一节先选出需要的资料;资料选对之后,还可以进一步缩短,但要保留回答所需的条件。

摘要像做会议纪要:大家寒暄了十分钟,可以缩成一句;“谁负责、何时完成、哪些情况例外”需要写清楚。省下的文字越多,越要留意是否连着事实一起省掉了。

先看一个容易失真的规则

原始资料写着:

已发货订单在签收7天内可退货,生鲜商品除外。申请时请填写订单编号,客服将在工作时间处理。

客户问“签收第10天还能退货吗”,摘要至少需要保留期限。如果只留下“订单可退货”,阅读的人可能以为任何订单、任何时间都能申请。

要素原规则过度简写后的缺口
期限签收7天内缺少时间限制
适用条件已发货订单范围变成所有订单
例外生鲜商品除外商品限制消失
来源版本v1保留版本便于核对更新

句子变短只是长度变化。判断摘要是否有用,要对照当前问题,检查作答需要的事实和条件是否还在。

原始退货规则提取期限条件例外形成摘要,再用资料内容和访问范围查找缓存,资料更新重新生成
图1:先保留影响答案的规则,再决定这份摘要能否复用。

运行一次压缩与缓存演示

缓存是保存已经计算过的结果,下次输入相同就直接取出,减少重复工作。它适合保存经常读取、变化较少的资料摘要。

先在 PowerShell 创建并进入学习目录:

Bash
mkdir "F:\学习\l5-compression" -Force
cd "F:\学习\l5-compression"

保存下面的文件后,在这个终端运行 dir 核对文件名,再执行本节给出的运行命令。

新建 F:\学习\l5-compression,保存下面完整代码为 compression_demo.py。它先检查一个过度简写的摘要,然后依次读取旧规则、重复读取和更新后的规则。

先在 PowerShell 创建并进入学习目录:

Bash
mkdir "F:\学习\l5-compression" -Force
cd "F:\学习\l5-compression"

保存下面的文件后,在这个终端运行 dir 核对文件名,再执行本节给出的运行命令。

Python
"""保留规则中的条件,并观察资料更新后的摘要缓存。"""
from dataclasses import dataclass


@dataclass(frozen=True)
class Policy:
    version: str
    deadline_days: int
    condition: str
    exception: str


@dataclass(frozen=True)
class Summary:
    text: str
    version: str
    deadline_days: int
    condition: str
    exception: str


def summarize(policy):
    # 提取期限、适用条件和例外,组合为短句。
    text = (f"{policy.condition}在签收{policy.deadline_days}天内可退货,"
            f"{policy.exception}除外。")
    return Summary(text, policy.version, policy.deadline_days,
                   policy.condition, policy.exception)


def inspect(summary, policy):
    issues = []
    # 比较可明确核对的字段,指出摘要丢失或改动了哪项规则。
    for field in ("version", "deadline_days", "condition", "exception"):
        if getattr(summary, field) != getattr(policy, field):
            issues.append(field)
    return issues


class SummaryCache:
    def __init__(self):
        self.values = {}

    def get(self, policy, scope, strategy="extract-v1"):
        # 资料内容、可见范围和摘要策略共同决定缓存是否可复用。
        key = (policy, scope, strategy)
        hit = key in self.values
        if not hit:
            self.values[key] = summarize(policy)
        return self.values[key], hit


if __name__ == "__main__":
    original = Policy("v1", 7, "已发货订单", "生鲜商品")
    bad = Summary("订单可退货。", "v1", 0, "订单", "")
    print("过度简写丢失:" + "、".join(inspect(bad, original)))
    cache = SummaryCache()
    for label, policy in (
        ("首次读取", original),
        ("再次读取", original),
        ("规则更新", Policy("v2", 14, "已发货订单", "生鲜商品")),
    ):
        result, hit = cache.get(policy, scope="公开规则")
        print(f"{label}:命中={hit};{result.text}")

在 PowerShell 运行:

Bash
cd "F:\学习\l5-compression"
ls
python -X utf8 compression_demo.py

程序输出:

TXT
过度简写丢失:deadline_days、condition、exception
首次读取:命中=False;已发货订单在签收7天内可退货,生鲜商品除外。
再次读取:命中=True;已发货订单在签收7天内可退货,生鲜商品除外。
规则更新:命中=False;已发货订单在签收14天内可退货,生鲜商品除外。

deadline_days 是期限天数,condition 是适用条件,exception 是例外。第一行指出简写摘要改动了这三项。

接下来三行观察缓存。False 表示尚未找到相同输入的摘要,需要生成;True 表示已经保存过,可以复用。规则改成14天后,缓存重新生成,因此最终句子也改为14天。

摘要怎样生成,怎样检查

Policy 保存原始规则的结构化字段,Summary 保存摘要及其对应字段。summarize 读取期限、条件和例外,组合成短句。这是一种提取式整理:保留资料中的字段,再删去当前回答用不到的操作说明。

实际资料通常是长文本。可以先选取相关原句,也可以让模型改写成摘要。选取原句容易对照来源;改写可以合并重复内容,但还需要检查是否改动了数字、主体或条件。

inspect 比较明确的字段,逐项列出差异。例子里的坏摘要把期限写成0,把“已发货订单”改成“订单”,还丢掉生鲜例外,因此得到三项差异。实际改写后,也可以先做这类可自动检查的字段对照,再检查完整句子的含义。

里面的 getattr(policy, field) 表示按名字取出对象属性。例如 field='deadline_days' 时,取到的是 policy.deadline_days。循环逐个取出原文与摘要的相同字段,再比较两个值。

摘要应怎样适应问题

对于“第10天能否退货”,期限和商品例外很重要;对于“怎样填写申请”,订单编号和处理步骤更重要。压缩前先明确本次问题,再选择保留内容。

如果把一份很短的摘要用于所有问题,读者可能在需要操作步骤时只得到期限。更合适的做法是保留原始资料,按问题提取相关内容;常用摘要用于快速定位,需要细节时继续读取原文。

期限问题保留7天和例外,操作问题保留订单编号和处理步骤,两种摘要都保留原始资料入口
图2:不同问题需要不同细节,压缩前先明确要回答什么。

缓存如何识别“相同输入”

key = (policy, scope, strategy) 是缓存键,相当于存放摘要的抽屉标签:

  • policy 包含资料版本与规则内容。
  • scope 表示这份资料的可见范围。
  • strategy 表示使用哪套摘要规则。

三项都相同,才取出已有摘要。Policy 使用 frozen=True,实例创建后字段保持固定,便于作为字典键使用。可见范围本身仍需由资料访问逻辑确认;这里将已确认的范围传入缓存。

把内容也放进键,是为了覆盖“资料已经改了,但版本号没更新”的情况。只有版本号的键会继续找到旧摘要;包含内容后,规则由7天改成14天,即使仍叫v1,也会得到新结果。

大文档通常会计算内容摘要值作为键的一部分,不必把整段原文塞进键。摘要策略、模型配置变化时也应调整对应版本;不同策略可能产生不同长度和不同表述。

缓存保存在哪里

例子把缓存放在当前进程的字典里。关闭程序后字典消失,再次启动会从首次生成开始。需要跨进程复用时,可以写入数据库或缓存服务,并设置保留时间和失效条件。

缓存占空间,也需要清理。文件数量增加后,可以限制条目数、删除长期未用条目,或在资料更新时移除旧版本。频繁变化的订单状态通常需要短保留时间;稳定公开规则可以保存更久。

对已有摘要再摘要,会把前一次丢失的细节永久带到后续结果中。更新摘要时优先回到原始资料,重新提取;保留来源标识,便于遇到问题时查原句。

跟着固定改法检查更新

将主程序“规则更新”中的14改成30,再执行同一命令。最后一行会显示“签收30天内”,前两行仍显示7天。恢复14即可回到上面的输出。

再将更新样本里的 "v2" 改为 "v1",保留14,运行 python -X utf8 compression_demo.py。最后一行仍是 命中=False,因为规则内容不同。这能直接观察到缓存键同时比较版本和内容。恢复更新样本为v2、14天后重跑。

检查七种情况

将测试保存为 test_compression_demo.py,覆盖正常摘要、条件失真、重复命中、资料更新、同版本内容变更、范围变更与策略变更。

测试代码:test_compression_demo.py
Python
"""检查摘要规则字段及缓存复用条件。"""
import unittest
from compression_demo import Policy, Summary, SummaryCache, inspect, summarize


class CompressionTests(unittest.TestCase):
    def setUp(self):
        self.policy = Policy("v1", 7, "已发货订单", "生鲜商品")

    def test_preserved_fields(self):
        result = summarize(self.policy)
        self.assertEqual(inspect(result, self.policy), [])
        self.assertEqual(result.text, "已发货订单在签收7天内可退货,生鲜商品除外。")

    def test_missing_conditions(self):
        bad = Summary("订单可退货。", "v1", 0, "订单", "")
        self.assertEqual(inspect(bad, self.policy),
                         ["deadline_days", "condition", "exception"])

    def test_repeated_read_hits(self):
        cache = SummaryCache()
        self.assertFalse(cache.get(self.policy, "公开")[1])
        self.assertTrue(cache.get(self.policy, "公开")[1])

    def test_document_update_misses(self):
        cache = SummaryCache()
        cache.get(self.policy, "公开")
        updated = Policy("v2", 14, "已发货订单", "生鲜商品")
        result, hit = cache.get(updated, "公开")
        self.assertFalse(hit)
        self.assertEqual(result.deadline_days, 14)

    def test_content_change_same_version_misses(self):
        cache = SummaryCache()
        cache.get(self.policy, "公开")
        changed = Policy("v1", 14, "已发货订单", "生鲜商品")
        self.assertFalse(cache.get(changed, "公开")[1])

    def test_other_scope_misses(self):
        cache = SummaryCache()
        cache.get(self.policy, "公开")
        self.assertFalse(cache.get(self.policy, "内部")[1])

    def test_changed_strategy_misses(self):
        cache = SummaryCache()
        cache.get(self.policy, "公开")
        self.assertFalse(cache.get(self.policy, "公开", "extract-v2")[1])


if __name__ == "__main__":
    unittest.main()
Bash
cd "F:\学习\l5-compression"
ls
python -X utf8 -m unittest -v test_compression_demo

应显示7项通过。

现象优先检查
摘要中数字与原文不同期限、金额等字段的提取与改写
更新后仍显示旧规则缓存键是否包含变化后的内容
每次运行都未命中是否重启了程序、输入或策略是否变化
缩短后答复缺少细节当前问题需要的条件是否被删去

聊天历史也可以按最近消息、重要事实和历史摘要整理,具体接口可查阅 LangChain 短期记忆文档。下一篇上下文隔离、污染与可信来源会进一步解释:这些内容进入同一次请求时,怎样保持来源和职责清楚。

课后练习展开习题收起习题

4 道练习 · 客观题规则评分 · 其余题目待批改

正在读取本地记录…

不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。

练习 1 / 4 · v1.0.1

选择题 · 先判断边界

关于“压缩、摘要失真与缓存”,哪种做法更稳妥?

对应知识点:L5-compression

选择一个答案
练习 2 / 4 · v1.0.0

简答题 · 解释失败

写出一个失败输入并说明记录字段。

对应知识点:L5-compression

练习 3 / 4 · v1.0.0

代码题 · 边界测试

补充正常、边界、失败三组输入和预期结果。

对应知识点:L5-compression

练习 4 / 4 · v1.0.0

场景题 · 结果未知

证据不足时如何设计状态和人工接管?

对应知识点:L5-compression

图片查看器
100%

文章配图 · 按 Esc 关闭