中文资讯 掘金 AI Sun, 13 Sep 2026 正文 ok 831 words

长会话不崩盘:DeepSeek Harness 的上下文压缩与目标管理策略

英文标题:长会话不崩盘:DeepSeek Harness 的上下文压缩与目标管理策略

第15章:上下文压缩与目标管理——长会话的生存策略 agent 跑久了,会话日志越来越长。10 万 token 的上下文窗口很快被工具输出塞满——bash 命令的 stdout、文件内容、搜索结果。模
打开原文相关 5 · 机会 1
media3.2 KB media589 B media173.5 KB media5.8 KB

中文

第15章:上下文压缩与目标管理——长会话的生存策略

系列:DeepSeek Harness 源码实战 | 进度 15/16
原文仓库:github.com/deepseek-ai…

agent 跑久了,会话日志越来越长。10 万 token 的上下文窗口很快被工具输出塞满——bash 命令的 stdout、文件内容、搜索结果。模型要么看不全、要么看不到关键信息。

dsh 有两个机制解决这个问题:compaction(上下文压缩)把旧对话摘要成精简版本,goal(目标管理)让 agent 在长会话中不丢失目标。两者都是可选能力,都基于会话日志,都不引入额外的持久化存储。

我是怕浪猫,第 15 章。先从 compaction 开始。15.png

15.1 compaction Seam:压缩是什么

compaction 是一个能力 Seam,与 bash 一样分为三角色:

角色 包 说明
Service Definition dsh-compaction ctx.compaction,抽象服务
Service Provider dsh-compaction-basic 默认后端,基于 LLM 摘要
Consumer dsh-command-compact 面向用户的 /compact 命令

来自子系统文档(docs/subsystems/compaction.zh.md):

压缩是可选能力,不属于 agent loop 主干。与 bash 不同,该接口必然依赖 dsh-session 和 dsh-llm:其动词作用于 agent 所有的 Session,而其持久摘要事件使用 ContentBlock 词汇。

compaction 做的事情可以用一句话概括:把会话日志中的一段旧对话替换成一条摘要消息。替换后,模型看到的是摘要而不是原始的冗长对话。

压缩前后对比:

压缩前(模型看到的上下文):
  [user] 帮我重构 auth.ts
  [assistant] 我来分析...(5000 tokens)
  [tool] 文件内容 auth.ts...(8000 tokens)
  [assistant] 我发现三个问题...(3000 tokens)
  [tool] 搜索结果...(6000 tokens)
  [assistant] 修复完成...(4000 tokens)
  总计:26000 tokens

压缩后:
  [user] [压缩摘要] 重构 auth.ts:分析了文件,发现三个类型安全问题,已全部修复。(500 tokens)
  总计:500 tokens

15.2 compaction 会话事件

compaction 通过声明合并为 SessionEventMap(会话事件映射)扩展三种事件类型。来自文档:

事件 载荷 作用
compaction/start { turn } 获取日志记录的锁
compaction/summary { summary, rawOutput?, shadowedRange, … } 安全摘要投影
compaction/end { turn, error? } 释放锁

关键设计:三个事件都只写入日志,绝不进入 surface(模型可见层)。文档原文:

They record locks, summaries, selected ranges, shadowed event seqs, token counts, and model calls, never entering surface.

摘要本身承载在另一条带有 surfaceOp: { op: 'replace', start, end } 的 user/message 上——这是摘要压缩执行的唯一 surface 变更。

锁的语义很重要。来自文档:

锁括住整个操作:先追加 compaction/start,然后执行摘要生成、写入 compaction/summary 记录与 user/message 替换,最后才追加 compaction/end。

如果操作中途崩溃,日志中会留下 compaction/start 而无匹配的 compaction/end——一个可检测的遗留锁。文档说明:

活动的未匹配 start 会阻塞所有入口点。

这意味着不会有两个 compaction 同时操作同一个会话。

15.3 压缩触发与策略

compaction 有两种触发方式:

触发 来源 时机
自动 pressure(压力)/ context-overflow(上下文溢出) agent/pre-step 串行事件中
手动 /compact 命令 用户显式调用

自动触发的两个原因(来自文档):

type CompactionTrigger = 'pressure' | 'context-overflow'

pressure:常规压力策略,基于 token 估算。context-overflow:上下文溢出,可能强制压缩即使未达到正常阈值。

CompactionEngine 暴露三个方法(来自文档):

compactIfNeeded(agent, trigger, signal)  -- 自动策略
compactNow(agent, signal)                 -- 手动,空闲会话
compactRegion(...)                        -- 显式范围

compactNow() 在轮次之间的 agent maintenance(维护期)运行。没有有效范围时返回 null 且不写入——不浪费资源。

文档说明了 compaction-basic 的策略链:

工具结果剪枝(compaction-tool-result-pruner)是一个可选的中间步骤——先尝试裁剪冗长的工具输出,如果裁剪后仍超限才做完整摘要。来自文档的类型定义:

interface PrunedEntry {
  readonly originalSeq: number     
  readonly replacementSeq: number  
  readonly callId: CallId          
  readonly charsBefore: number     
  readonly charsAfter: number      
}

金句:压缩不是一刀切——先剪枝工具输出,不够再摘要对话。渐进式的,最小化信息损失。

15.4 CompactionResult 与 shadowed 机制

成功压缩返回 CompactionResult(来自文档):

interface CompactionResult {
  readonly compactionId: CompactionId       
  readonly sourceCommandId?: CommandId      
  readonly startSeq: number                 
  readonly summarySeq: number              
  readonly endSeq: number                  
  readonly summary: ContentBlock[]          
  readonly shadowedRange: { start: number; end: number }  
  readonly shadowedSeqs: number[]           
  readonly shadowedTokenCount: number       
}

shadowedRange 是 surface 位置跨度,不是数值区间。文档解释了一个微妙的情况:

After a prior replace lands a fresh high-seq summary node at an older range’s position, start can be GREATER than end.

这是因为 surface replacement(surface 替换)会把新节点放在旧范围的位置——新节点的 seq 更高但位置更早。shadowedSeqs 是权威的被遮蔽节点集合,按 surface 顺序排列。

被遮蔽(shadowed)的对话不会从日志中删除——它们仍在日志中,但不进入模型可见的 surface。这意味着压缩是可逆的:理论上可以取消遮蔽恢复原始对话。

15.5 手动压缩的失败处理

手动压缩有预期的失败类别(来自文档):

type ManualCompactionErrorCode =
  | 'busy'        
  | 'cancelled'   
  | 'changed'     
  | 'summary'     
  | 'commit'      
  | 'persistence' 

文档说明了每种失败的状态影响:

错误码 surface 影响 日志影响
busy 不变 不变
cancelled 不变 清理后抛 abort 原因
changed 不变 闭合失败尝试并持久化
summary 不变 闭合失败尝试并持久化
commit 可能部分变更 闭合尝试
persistence 内存标记已闭合 flush 失败

changed 和 summary 会闭合失败尝试并持久化到日志——即使失败了也有记录。persistence 表示内存中的标记对已闭合但 flush 失败——内存状态正确但持久化可能缺失。

15.6 goal Seam:持久化目标

goal(目标)服务让 agent 在长会话中保持对目标的追踪。来自子系统文档(docs/subsystems/goal.zh.md):

事件溯源目标服务及其策略消费方共享的类型。目标领域负责持久化与激活决策。

Goal 的生命周期阶段(来自文档):

type GoalPhase = 'active' | 'paused' | 'blocked' | 'complete'
阶段 含义
active 正在进行
paused 暂停(可恢复)
blocked 因问题而停止
complete 完成

blocked 是唯一表示「因问题而停止」的持久状态。阻塞原因(来自文档):

interface GoalBlockReason {
  readonly code: string    
  readonly message: string 
}

完整的 Goal 状态(来自文档):

interface GoalSnapshot extends GoalRef {
  readonly objective: string          
  readonly phase: GoalPhase           
  readonly blockedReason?: GoalBlockReason  
  readonly maxGoalRounds: number      
}

GoalRef(来自文档)是 compare-and-set(比较并交换)身份:

interface GoalRef {
  readonly id: GoalId       
  readonly revision: number 
}

每次获准的持久变更都会递增 revision——这是乐观锁,防止并发修改冲突。

15.7 Goal 变更与会话事件

每次 goal 变更都是持久的 goal/change 会话事件。来自文档:

interface GoalSnapshotChangeMeta {
  readonly kind: 'goal/change'
  readonly version: 1
  readonly operation: Exclude<GoalOperation, 'clear'>
  readonly goal: GoalSnapshot
  readonly roundsStarted: number
  readonly createdAt: number
  readonly updatedAt: number
}

清除操作携带墓碑(来自文档):

interface GoalClearChangeMeta {
  readonly kind: 'goal/change'
  readonly version: 1
  readonly operation: 'clear'
  readonly cleared: GoalRef
  readonly clearedAt: number
}

关键设计:goal 状态只从 goal/change 事件派生,inbox 变更不影响 goal 状态。这意味着 goal 的生命周期与消息队列解耦——即使 inbox 中有未处理的消息,goal 状态也不变。

续跑消费方为每个获准的 user/message(用户消息)轮次标注正数且连续的 Round 编号(来自文档):

interface GoalMessageSource {
  readonly kind: 'goal'
  readonly goalId: GoalId
  readonly revision: number
  readonly round: number  
}

回放时的校验规则(来自文档):

回放会拒绝非正数 Round、编号缺口、陈旧修订号、已停止阶段和超出上限。

这保证了日志的完整性——任何损坏的 goal round 序列都会在回放时被发现。

15.8 goal-round-driver:续跑驱动

goal-round-driver 是 goal 的策略消费方。它的工作流:

用户设定目标
  |
  v
goal 创建(phase: active)
  |
  v
goal-round-driver 启动 Round 1
  |-- agent 跑一个完整轮次
  |-- 轮次结束
  |-- 检查 goal 是否完成
  |   |-- complete -> 标记完成
  |   |-- blocked -> 标记阻塞
  |   |-- active && roundsStarted < maxGoalRounds -> 启动 Round 2
  |   |-- active && roundsStarted >= maxGoalRounds -> 标记阻塞(超出上限)
  |
  v
等待用户输入或继续

goal-round-driver 在 agent/turn-stopping serial 事件中检查是否应该续跑。如果 goal 仍 active 且未达上限,它返回非 null 值注入续跑消息——让 agent 继续工作。

这个过程的关键:goal 状态持久化在会话日志中。即使进程重启,冷恢复后 goal 状态仍能从日志重建——agent 不会忘记自己的目标。

金句:goal 不是 todo list(待办清单),是事件溯源的状态机。每次变更都有日志记录,每次恢复都从日志重建——goal 的生命力来自会话日志,不来自进程内存。

15.9 compaction 与 goal 的协作

两个系统协作处理长会话:

场景 compaction 的作用 goal 的作用
长对话上下文溢出 压缩旧对话 保持目标不丢失
多轮目标续跑 压缩早期 round 记录 round 编号和进度
进程重启恢复 从日志重建 surface 从日志重建 goal 状态
阻塞后恢复 压缩阻塞期间的无用对话 恢复 goal 到 active

compaction 压缩时不会触碰 goal/change 事件——goal 事件不在 surface 层,不会被 shadowed(遮蔽)。这意味着即使对话被压缩成摘要,goal 状态仍然完整保留在日志中。

15.10 防御性模式:异步状态管理

dsh 防御性模式文档(docs/defensive-patterns.zh.md)的规则 3 与 compaction 和 goal 直接相关:

异步状态不是同步状态。agent.followup 无逐消息完成状态、后台完成与轮次边界竞争、reader.close 在 EOF 与 dispose 都触发。

对 compaction 和 goal 的启示:

风险 规则
compaction 进行中 goal 变更 锁机制保证串行
goal round 与 compaction 竞争 compaction 在 pre-step 串行运行
进程重启时 compaction 未完成 遗留锁可检测,冷恢复时忽略
goal 状态与内存不一致 从日志重建,不信任内存

本章小结

要点 说明
compaction 三角色 Definition(dsh-compaction)/ Provider(dsh-compaction-basic)/ Consumer(command-compact)
三种事件 compaction/start(锁)/ summary(摘要)/ end(释放锁)
触发方式 自动(pressure/context-overflow)/ 手动(/compact)
渐进压缩 先剪枝工具输出,不够再摘要
shadowed 机制 旧对话遮蔽不删除,可逆
手动失败处理 6 种错误码,失败也有日志记录
goal 四阶段 active / paused / blocked / complete
goal 事件溯源 状态从 goal/change 事件派生
goal-round-driver 续跑驱动,与 turn-stopping 协作
协作 compaction 不触碰 goal 事件,goal 状态完整保留

我是怕浪猫,第 15 章写完。compaction 和 goal 是 dsh 长会话的生存策略——压缩让上下文不爆,目标让 agent 不迷路。

有问题评论区聊,有纠错欢迎指出。如果这篇对你有帮助,收藏起来——设计长会话 agent 时这张机制表最好用。

下一章是系列收官:如何构建你自己的 dsh 发行版——从 profile 定制到生产部署。

English / 原文

第15章:上下文压缩与目标管理——长会话的生存策略

系列:DeepSeek Harness 源码实战 | 进度 15/16
原文仓库:github.com/deepseek-ai…

agent 跑久了,会话日志越来越长。10 万 token 的上下文窗口很快被工具输出塞满——bash 命令的 stdout、文件内容、搜索结果。模型要么看不全、要么看不到关键信息。

dsh 有两个机制解决这个问题:compaction(上下文压缩)把旧对话摘要成精简版本,goal(目标管理)让 agent 在长会话中不丢失目标。两者都是可选能力,都基于会话日志,都不引入额外的持久化存储。

我是怕浪猫,第 15 章。先从 compaction 开始。15.png

15.1 compaction Seam:压缩是什么

compaction 是一个能力 Seam,与 bash 一样分为三角色:

角色 包 说明
Service Definition dsh-compaction ctx.compaction,抽象服务
Service Provider dsh-compaction-basic 默认后端,基于 LLM 摘要
Consumer dsh-command-compact 面向用户的 /compact 命令

来自子系统文档(docs/subsystems/compaction.zh.md):

压缩是可选能力,不属于 agent loop 主干。与 bash 不同,该接口必然依赖 dsh-session 和 dsh-llm:其动词作用于 agent 所有的 Session,而其持久摘要事件使用 ContentBlock 词汇。

compaction 做的事情可以用一句话概括:把会话日志中的一段旧对话替换成一条摘要消息。替换后,模型看到的是摘要而不是原始的冗长对话。

压缩前后对比:

压缩前(模型看到的上下文):
  [user] 帮我重构 auth.ts
  [assistant] 我来分析...(5000 tokens)
  [tool] 文件内容 auth.ts...(8000 tokens)
  [assistant] 我发现三个问题...(3000 tokens)
  [tool] 搜索结果...(6000 tokens)
  [assistant] 修复完成...(4000 tokens)
  总计:26000 tokens

压缩后:
  [user] [压缩摘要] 重构 auth.ts:分析了文件,发现三个类型安全问题,已全部修复。(500 tokens)
  总计:500 tokens

15.2 compaction 会话事件

compaction 通过声明合并为 SessionEventMap(会话事件映射)扩展三种事件类型。来自文档:

事件 载荷 作用
compaction/start { turn } 获取日志记录的锁
compaction/summary { summary, rawOutput?, shadowedRange, … } 安全摘要投影
compaction/end { turn, error? } 释放锁

关键设计:三个事件都只写入日志,绝不进入 surface(模型可见层)。文档原文:

They record locks, summaries, selected ranges, shadowed event seqs, token counts, and model calls, never entering surface.

摘要本身承载在另一条带有 surfaceOp: { op: 'replace', start, end } 的 user/message 上——这是摘要压缩执行的唯一 surface 变更。

锁的语义很重要。来自文档:

锁括住整个操作:先追加 compaction/start,然后执行摘要生成、写入 compaction/summary 记录与 user/message 替换,最后才追加 compaction/end。

如果操作中途崩溃,日志中会留下 compaction/start 而无匹配的 compaction/end——一个可检测的遗留锁。文档说明:

活动的未匹配 start 会阻塞所有入口点。

这意味着不会有两个 compaction 同时操作同一个会话。

15.3 压缩触发与策略

compaction 有两种触发方式:

触发 来源 时机
自动 pressure(压力)/ context-overflow(上下文溢出) agent/pre-step 串行事件中
手动 /compact 命令 用户显式调用

自动触发的两个原因(来自文档):

type CompactionTrigger = 'pressure' | 'context-overflow'

pressure:常规压力策略,基于 token 估算。context-overflow:上下文溢出,可能强制压缩即使未达到正常阈值。

CompactionEngine 暴露三个方法(来自文档):

compactIfNeeded(agent, trigger, signal)  -- 自动策略
compactNow(agent, signal)                 -- 手动,空闲会话
compactRegion(...)                        -- 显式范围

compactNow() 在轮次之间的 agent maintenance(维护期)运行。没有有效范围时返回 null 且不写入——不浪费资源。

文档说明了 compaction-basic 的策略链:

工具结果剪枝(compaction-tool-result-pruner)是一个可选的中间步骤——先尝试裁剪冗长的工具输出,如果裁剪后仍超限才做完整摘要。来自文档的类型定义:

interface PrunedEntry {
  readonly originalSeq: number     
  readonly replacementSeq: number  
  readonly callId: CallId          
  readonly charsBefore: number     
  readonly charsAfter: number      
}

金句:压缩不是一刀切——先剪枝工具输出,不够再摘要对话。渐进式的,最小化信息损失。

15.4 CompactionResult 与 shadowed 机制

成功压缩返回 CompactionResult(来自文档):

interface CompactionResult {
  readonly compactionId: CompactionId       
  readonly sourceCommandId?: CommandId      
  readonly startSeq: number                 
  readonly summarySeq: number              
  readonly endSeq: number                  
  readonly summary: ContentBlock[]          
  readonly shadowedRange: { start: number; end: number }  
  readonly shadowedSeqs: number[]           
  readonly shadowedTokenCount: number       
}

shadowedRange 是 surface 位置跨度,不是数值区间。文档解释了一个微妙的情况:

After a prior replace lands a fresh high-seq summary node at an older range’s position, start can be GREATER than end.

这是因为 surface replacement(surface 替换)会把新节点放在旧范围的位置——新节点的 seq 更高但位置更早。shadowedSeqs 是权威的被遮蔽节点集合,按 surface 顺序排列。

被遮蔽(shadowed)的对话不会从日志中删除——它们仍在日志中,但不进入模型可见的 surface。这意味着压缩是可逆的:理论上可以取消遮蔽恢复原始对话。

15.5 手动压缩的失败处理

手动压缩有预期的失败类别(来自文档):

type ManualCompactionErrorCode =
  | 'busy'        
  | 'cancelled'   
  | 'changed'     
  | 'summary'     
  | 'commit'      
  | 'persistence' 

文档说明了每种失败的状态影响:

错误码 surface 影响 日志影响
busy 不变 不变
cancelled 不变 清理后抛 abort 原因
changed 不变 闭合失败尝试并持久化
summary 不变 闭合失败尝试并持久化
commit 可能部分变更 闭合尝试
persistence 内存标记已闭合 flush 失败

changed 和 summary 会闭合失败尝试并持久化到日志——即使失败了也有记录。persistence 表示内存中的标记对已闭合但 flush 失败——内存状态正确但持久化可能缺失。

15.6 goal Seam:持久化目标

goal(目标)服务让 agent 在长会话中保持对目标的追踪。来自子系统文档(docs/subsystems/goal.zh.md):

事件溯源目标服务及其策略消费方共享的类型。目标领域负责持久化与激活决策。

Goal 的生命周期阶段(来自文档):

type GoalPhase = 'active' | 'paused' | 'blocked' | 'complete'
阶段 含义
active 正在进行
paused 暂停(可恢复)
blocked 因问题而停止
complete 完成

blocked 是唯一表示「因问题而停止」的持久状态。阻塞原因(来自文档):

interface GoalBlockReason {
  readonly code: string    
  readonly message: string 
}

完整的 Goal 状态(来自文档):

interface GoalSnapshot extends GoalRef {
  readonly objective: string          
  readonly phase: GoalPhase           
  readonly blockedReason?: GoalBlockReason  
  readonly maxGoalRounds: number      
}

GoalRef(来自文档)是 compare-and-set(比较并交换)身份:

interface GoalRef {
  readonly id: GoalId       
  readonly revision: number 
}

每次获准的持久变更都会递增 revision——这是乐观锁,防止并发修改冲突。

15.7 Goal 变更与会话事件

每次 goal 变更都是持久的 goal/change 会话事件。来自文档:

interface GoalSnapshotChangeMeta {
  readonly kind: 'goal/change'
  readonly version: 1
  readonly operation: Exclude<GoalOperation, 'clear'>
  readonly goal: GoalSnapshot
  readonly roundsStarted: number
  readonly createdAt: number
  readonly updatedAt: number
}

清除操作携带墓碑(来自文档):

interface GoalClearChangeMeta {
  readonly kind: 'goal/change'
  readonly version: 1
  readonly operation: 'clear'
  readonly cleared: GoalRef
  readonly clearedAt: number
}

关键设计:goal 状态只从 goal/change 事件派生,inbox 变更不影响 goal 状态。这意味着 goal 的生命周期与消息队列解耦——即使 inbox 中有未处理的消息,goal 状态也不变。

续跑消费方为每个获准的 user/message(用户消息)轮次标注正数且连续的 Round 编号(来自文档):

interface GoalMessageSource {
  readonly kind: 'goal'
  readonly goalId: GoalId
  readonly revision: number
  readonly round: number  
}

回放时的校验规则(来自文档):

回放会拒绝非正数 Round、编号缺口、陈旧修订号、已停止阶段和超出上限。

这保证了日志的完整性——任何损坏的 goal round 序列都会在回放时被发现。

15.8 goal-round-driver:续跑驱动

goal-round-driver 是 goal 的策略消费方。它的工作流:

用户设定目标
  |
  v
goal 创建(phase: active)
  |
  v
goal-round-driver 启动 Round 1
  |-- agent 跑一个完整轮次
  |-- 轮次结束
  |-- 检查 goal 是否完成
  |   |-- complete -> 标记完成
  |   |-- blocked -> 标记阻塞
  |   |-- active && roundsStarted < maxGoalRounds -> 启动 Round 2
  |   |-- active && roundsStarted >= maxGoalRounds -> 标记阻塞(超出上限)
  |
  v
等待用户输入或继续

goal-round-driver 在 agent/turn-stopping serial 事件中检查是否应该续跑。如果 goal 仍 active 且未达上限,它返回非 null 值注入续跑消息——让 agent 继续工作。

这个过程的关键:goal 状态持久化在会话日志中。即使进程重启,冷恢复后 goal 状态仍能从日志重建——agent 不会忘记自己的目标。

金句:goal 不是 todo list(待办清单),是事件溯源的状态机。每次变更都有日志记录,每次恢复都从日志重建——goal 的生命力来自会话日志,不来自进程内存。

15.9 compaction 与 goal 的协作

两个系统协作处理长会话:

场景 compaction 的作用 goal 的作用
长对话上下文溢出 压缩旧对话 保持目标不丢失
多轮目标续跑 压缩早期 round 记录 round 编号和进度
进程重启恢复 从日志重建 surface 从日志重建 goal 状态
阻塞后恢复 压缩阻塞期间的无用对话 恢复 goal 到 active

compaction 压缩时不会触碰 goal/change 事件——goal 事件不在 surface 层,不会被 shadowed(遮蔽)。这意味着即使对话被压缩成摘要,goal 状态仍然完整保留在日志中。

15.10 防御性模式:异步状态管理

dsh 防御性模式文档(docs/defensive-patterns.zh.md)的规则 3 与 compaction 和 goal 直接相关:

异步状态不是同步状态。agent.followup 无逐消息完成状态、后台完成与轮次边界竞争、reader.close 在 EOF 与 dispose 都触发。

对 compaction 和 goal 的启示:

风险 规则
compaction 进行中 goal 变更 锁机制保证串行
goal round 与 compaction 竞争 compaction 在 pre-step 串行运行
进程重启时 compaction 未完成 遗留锁可检测,冷恢复时忽略
goal 状态与内存不一致 从日志重建,不信任内存

本章小结

要点 说明
compaction 三角色 Definition(dsh-compaction)/ Provider(dsh-compaction-basic)/ Consumer(command-compact)
三种事件 compaction/start(锁)/ summary(摘要)/ end(释放锁)
触发方式 自动(pressure/context-overflow)/ 手动(/compact)
渐进压缩 先剪枝工具输出,不够再摘要
shadowed 机制 旧对话遮蔽不删除,可逆
手动失败处理 6 种错误码,失败也有日志记录
goal 四阶段 active / paused / blocked / complete
goal 事件溯源 状态从 goal/change 事件派生
goal-round-driver 续跑驱动,与 turn-stopping 协作
协作 compaction 不触碰 goal 事件,goal 状态完整保留

我是怕浪猫,第 15 章写完。compaction 和 goal 是 dsh 长会话的生存策略——压缩让上下文不爆,目标让 agent 不迷路。

有问题评论区聊,有纠错欢迎指出。如果这篇对你有帮助,收藏起来——设计长会话 agent 时这张机制表最好用。

下一章是系列收官:如何构建你自己的 dsh 发行版——从 profile 定制到生产部署。