← Back to home@JiewiW

dsh-cot-anchor

思考锚点:从模型上一段思考中提取已确立结论,在工具结果后注入,减少重复推导;并可识别打转与伪工具调用

Stars
0
Language
JavaScript
Created
Oct 5, 2026
Updated
Oct 6, 2026
GitHub repo

Introduction

dsh-cot-anchor

思考锚点(COT Anchor)—— 让"思考着思考但没真正推进"的模型闭嘴,把已经得出的结论重新递回上下文。

一个 DeepSeek Harness(DSH)插件。


出发点:为什么会有这个插件

本插件诞生于对一类特定模型的真实困扰——以 DeepSeek-V4-Flash(v4.1-flash 变体)为典型代表。具体症状在一次实际会话里被硬数字刻画过:

指标数字含义
思考 : 文本输出14 : 1 ~ 24 : 1模型每写一个字给用户,就要消耗 14~24 个字的"思考"
单条带 reasoning 的消息平均宣告"接下来要做什么"1.6 ~ 3.6 次但兑现率显著偏低——说要做 X,下一条却在做别的
reasoning 块平均长度~7,000 字极度啰嗦
"宣布决定性证据齐全"几乎每轮思考一次然后继续宣称"接下来要验证 XXX"

也就是说:这个模型思考着思考,但其实没在推进。它会在一段看似有逻辑的 CoT 里,把上一轮甚至上几轮已经得出过结论的证据,又重新推一遍;它会把工具调用写成纯文本(<seed:tool_call>...);它会原地重复同一段话;它会语义空转(措辞不同但反复"重新分析");它会输出无意义的递增数字流(639, 640, 641, ...)。

如果让这种模型长时间跑长任务,会发生两件事:

  1. token 烧得极快——每 1 字有效输出要 14~24 字思考,照它的思考规模换算,1MB 的思考里有效推进只占极小比例。
  2. 结论被反复"重新发现"——上一轮已经明确写过的结论,下一轮思考里又当新问题重新推导一遍,浪费 token 的同时让模型注意力被陈年分析占据,看不清真正新出现的信息。

dsh-cot-anchor 就是用来对付这种行为的:把上一轮已经确立的结论重新递回上下文,让模型不必重新推一遍;同时在生成过程中实时打断打转/重述/伪调用/数字流等失控形态。

它不解决一切长任务问题,只解决这一类特定的"思考链极度冗余、结论反复重新推导"问题。


它做什么(结论视角)

把上面那段拗口的症状翻译成"做什么",就是这两条:

  1. 结论回流:每次工具结果返回后,从最新一段思考里抽取已成立的结论(最多 3 条、每条不超过 220 字),作为用户消息插回上下文。下一步思考起手就能看到这些结论,不必再推一遍。
  2. 失控形态打断:在生成过程中按 32 字符一次的频率检查,一旦发现模型开始打转/原地重复/伪工具调用/数字流退化,立即切断当前请求、把已生成部分作为正常消息落盘、把结论锚点排进下一步、让回合续跑。模型不必因为一次误判而丢掉整轮工作。

第一项默认开启,插件装上就生效。第二项默认开启,但需要 DSH 0.1.5-rc.1 / 0.1.5-rc.2 内核提供的 agent/soft-cut 钩子——0.1.5-rc.3 起的内核不再提供该钩子,请参看 docs/softcut-kernel-port.md 恢复。

能力边界:它治不了什么(含实测反例)

本插件的两类机制都是单步边界内的,理解这一点比理解它能做什么更重要:

  • 结论注入发生在工具结果之后,作用于"下一步";软切只观察当前一次生成流内的文本(每 32 字符征询一次),进入下一个 step 后一切计数归零。
  • 因此本插件治得了单流内退化(逐字节重复、伪工具调用、数字流、同一块思考内打转),治不了跨步骤折返跑:同一结论在十几个步骤里被重新推导、同一取证目标反复换路径尝试——没有任何一次单流能看见这个模式。
  • 软切触发后,内核把已生成前缀正常落盘并发起一次全新请求,该请求照常重读全部缓存上下文。软切不减少回合步数;在长任务里,账单的大头是"步数 × 每步膨胀的上下文重读",不是思考长度,也不是插件注入。
  • 模型可能知道自己在空转却停不下来。实测出现过思考中自述"I'm burning turns, let me stop"之后、同一段思考内继续生成下一条探索路线;被注入"立即停止重新分析"后,后续步骤再次触发打转判定。注入的前提是模型会读且会照做,自知不构成刹车。
  • 每个回合软切上限 6 次,打满后当回合剩余生成完全放行。

一次"插件全程在场仍未约束住"的实测

下列数字来自单个真实长任务会话(N=1):同一 v4.1-flash 变体、思考强度调至 low、结论注入与软切全程开启且软切实际触发 4 次(打转判定 2 次、转折句判定 2 次;以无 usage 的助手消息结算点为准)。

指标数值口径
思考 : 正式文本37.8 : 1reasoning 与 text 块的字符数之比
"接下来要做 X"类宣告9.2 次 / 思考块关键词正则计数,口径宽于开头基线表的 1.6–3.6,两者不可直接换算
会话总量 / 助手步骤约 449 万 token / 42 步逐步骤 usage 四字段求和
其中缓存读取占比95.4%cacheReadTokens / totalTokens
零产出思考块4 个、合计约 3 万字符(约占全部思考量 25%)usage 缺失的 assistant 消息,即软切结算点
首次实际改代码发生在第 28 / 42 步此前 68% 思考量用于反复重推同一根因与失败的取证支线

这组数字不与开头的基线表横比(任务类型、思考强度设置、统计口径均不同,N=1),它只证明一件事:插件在场且机制真实开火,不保证思考密度达标。如果你的场景以跨步骤折返跑为主,不要期待调阈值能根治——需要的是步数/预算层面的治理,那超出本插件的职责范围。

复算口径(任何人可在自己的会话日志上复核):解包 session.v*.jsonl.zstd 后,逐条统计 assistant/message 的 usage(inputTokens / outputTokens / cacheReadTokens / totalTokens)与 content 块类型(reasoning / text)的字符长度;软切结算点表现为该消息无 usage 字段且紧随一条 agent/inbox/spliced(target: next-step)。

用户能直接看到的 flash 失控形态(举例)

下面这些是从真实会话里抓出来的典型表现——用户读聊天窗口就能感觉到,不依赖任何内部诊断:

  • 重复推送:同一段话说几遍还不停,每一遍措辞微调但推进为零。"让我重新看一下…" → "再确认一下…" → "确保没遗漏…" → "实际上让我重新…" 五轮之后还在原地。
  • 伪工具调用:模型不再真正调用工具,而是把工具调用格式化成普通文本(常见形态 <seed:tool_call>...</seed:tool_call>),直接拼进正文。用户读起来像在调工具,实际上什么都没发生。
  • 递增数字流:思考里突然开始输出无意义的递增数字(639.640) 640.641)… 641.642)…),像是某个内部计数器被泄露进了输出。
  • 自吹"决定性证据":每思考一次就来一次"我已经得到了决定性证据" / "真相大白" / "证据链齐全",然后紧接"接下来我要验证 XXX"——下一条往往又回到重分析起点。
  • 结论重新发现:上一轮明明已经在正文里写明白的事("X 文件里函数 Y 是关键"),下一轮思考里又被当作新信息重新推导一遍。

如果上面任何一项在你用某些模型跑长任务时出现过,本插件就是为它而生的。

快速开始

从 GitHub Releases 拉 tarball 安装(推荐):

# 直接拉指定 tag
dsh plugin --profile <你的profile> add github:JiewiW/dsh-cot-anchor#v0.1.5

# 或先下载再装
gh release download v0.1.5 --repo JiewiW/dsh-cot-anchor
dsh plugin --profile <你的profile> add ./dsh-cot-anchor-0.1.5.tgz

安装后重启实例,设置页出现「COT 锚点」标签即已生效。

工作方式

模型思考  →  工具执行  →  [插件] 摘出结论、插回上下文  →  下一步思考
                              ↑
                        只在工具边界注入

注入的消息体积很小(默认最多 3 条、每条不超过 220 字),以 user 角色追加在工具结果之后、下一次请求之前。

在设置页开启相应开关后,插件还会在生成过程中按间隔询问内核是否需要中断当前生成("软切断"):检测到问题就切断当前请求、把已生成部分作为正常消息落盘、插入锚点,然后从断点继续。模型不会因为一次误判而丢失整轮工作。

成本

场景额外成本
默认(结论注入开启,其余关闭)每个工具结果后一条短消息,上界 660 字符(3 条 × 220 字);无额外模型调用
命令执行纪律提醒(随锚点同车)仅在锚点实际注入时同车携带一条,与推理锚点共用同一推理指纹去重节奏,不单独排队、不无限堆叠;单条全文固定约 380 token(字符估算)
开启 LLM 提炼结论每个助手回合多一次小模型调用
开启打转判定纯本地字符串计算,无 token 成本;可能增加少量回合数
开启 CoT 静默采集每个助手回合留一条本地样本;默认关闭

注入消息会随之后续请求的上下文一同发送,因此持续占用少量上下文窗口。按中英混排估算,660 字符约合 200–500 token 量级——相对于一次工具往返的上下文可以忽略,但它在每一步都会累积。注入以尾部追加方式写入会话日志,不改写已有内容,因此不会打穿前缀缓存:除新追加的一块外,此前内容始终是稳定前缀。新追加块在其首次出现的那次请求上按未命中计费一次,此后转为缓存命中——这一点与文本是否固定无关。纪律块与锚点的排列顺序仅为内容组织上的一致性,不影响缓存行为。

实测成本占比

在一个 95 次助手请求、累计约 924 万 token 的真实长会话上按消息级口径统计(字符→token 为启发式估算,非 tokenizer 实测;任何人可在自己的 DSH 会话日志上按同口径复算:逐条统计 user/message 中 source.summary 为 cot-anchor: exec-discipline(固定纪律)与其他 cot-anchor:*(动态锚点)的文本,并对照各次助手请求 usage 的 inputTokens / cacheReadTokens):

指标数值
注入新增估算总量21,436 token,占全会话总 token 约 0.23%
其中固定纪律文本17,190 token,占注入估算 80.2%(45 条,单条全文固定约 380 token;仅表示体积占比,不表示缓存命中)
其中动态锚点4,246 token,占注入估算 19.8%(51 条)
注入块首现带来的未命中输入21,436 token,占会话未命中输入约 5.6%(21,436 / 379,969;每个注入块在其首现的那次请求上未命中一次)
该会话缓存读取占全部输入比95% 以上

结论:插件带来的新增输入在全会话 token 总量的 0.3% 以内。每个注入块在首现时按未命中计费一次,此后随上下文增长参与后续每一次请求。纪律块体积约占注入量五分之四,但"文本固定"不带来额外缓存收益——该占比只反映体积,不反映命中。

兼容性

能力DSH 版本
结论注入、设置页0.1.5 起全部版本
生成中途打断(软切断)仅 0.1.5-rc.1 / 0.1.5-rc.2

0.1.5-rc.3 起内核不再提供生成中途打断所需的钩子,该能力会静默失效。插件会在启动日志和设置页顶部明确提示当前内核是否具备该能力——请以设置页显示为准,不要凭"开关是开的"就认为功能在生效。

如果你希望在新版上保留这项能力,可用随包附带的工具把软切机构移植回内核:

node node_modules/dsh-cot-anchor/tools/apply-softcut-port.mjs --check   # 看状态
node node_modules/dsh-cot-anchor/tools/apply-softcut-port.mjs           # 应用(幂等)

移植后需重启实例。完整步骤、原理、风险与回滚见 docs/softcut-kernel-port.md。

本插件不会中止进行中的回合;它只在模型自己写坏输出时切断当前这一次生成请求,随后继续。

设置项

设置页「COT 锚点」共 52 项,按用途分组。下列"建议值"是针对多数场景的推荐;不同模型差异较大,可据实调整。

总开关

键含义建议
enableToolInject工具结果后注入锚点保持开启,这是本插件的主要价值
enableSoftCut允许生成中途打断内核不支持时无效;支持的版本建议开启

打断判定

这五项决定"什么情况算问题"。全部关闭时插件只做结论注入。

键含义能力边界建议
enablePseudoTool识别把工具调用写成文本的输出只能识别已知的文本形态,无法穷举开启
enableRepeat识别同一段内容被反复吐出要求逐字节相同;换一种措辞的重复抓不到开启
enableChurn识别措辞不同但反复"重新分析"启发式判定;遇到未闭合的代码块会自动让路开启
enableTransition识别"接下来我要……"这类转折句转折句不等于打转,正常思考里也会出现见下方说明
enableNumberRunaway识别无意义的递增数字流要求数字流足够长开启

enableTransition 是五项中最容易误判的一项:正常的分阶段思考同样会出现转折句。误判代价较低(只会多一次切断,模型从断点继续),但如果你经常看到它在正常思考中切断,可将其关闭。

锚点内容

键含义建议
minReasoningChars思考短于此长度不提取结论250
maxPoints最多保留几条结论3
maxPointChars单条结论字数上限220

抽取质量边界(v0.1.5 已加过滤): 结论由规则从思考文本中摘句得到(开启 LLM 提炼时由小模型改写),不理解句子的语义角色。v0.1.5 起,抽取器在入池前剔除两类无命题内容:①纯话语标记句——剥掉 Actually / wait / Hmm / hold on / 让我重新 等标记后剩余实词不足 6 个字符("Actually, wait."、"Hmm."、"其实,等等。");②路线意图宣告句——"the decisive route: ask the user…"这类下一步动作宣告。标记词领起但带事实命题的句子("Wait, the kernel never marks in verbose mode.")不受影响。

这是抽取质量问题,不是思考标记词本身的问题——Wait / Actually / Hmm 等转折词在推理链中承担结构控制作用,本插件不从模型输出中删除它们,只阻止它们被当成结论回灌。

重复循环参数

判定"同一段内容被反复吐出"的阈值。调低更激进,调高更保守。

键含义建议
repeatMinCount长块重复几次判定打转2
repeatMinCountShort短周期需重复几次5
repeatUltraShortMinCount极小循环(如 er4er4…)需重复几次30
repeatMinPeriod周期下限(字)12
repeatShortPeriod长块与短块的分界(字)96
repeatMaxPeriod超过此周期不再视为循环8000
repeatWindowChars只在文本尾部这段长度内做预筛16000
repeatProbeChars周期探针长度128
repeatMinTextChars文本短于此长度不判定800

语义空转参数

键含义建议
churnMinHits窗口内"让我想想/重新分析"类用语命中几次即判定18
churnWindowChars空转统计窗口3000
churnMinTextChars文本短于此长度不判定800

转折句参数

键含义建议
softCutScanTail转折句扫描窗口160
softCutTransitionWindow转折词距句末的最大距离48
softCutMinFollowChars转折词后至少展开多少字才允许切断6
pseudoToolScanTail伪调用扫描窗口4000

softCutMinFollowChars 是防误切的关键:它保证不会在句子刚开头就切断、截出半句结论。调低会明显增加误切。

LLM 提炼

用一次小模型调用把整段思考压成摘要式结论,通常比摘句更准。默认关闭。

键含义建议
enableLlmRefine启用 LLM 提炼按需
llmRefineProvider提炼用 provider留空则跟随会话
llmRefineModel提炼用 model留空则跟随会话
llmRefineMaxTokens提炼输出上限256
llmRefineTimeoutMs提炼超时(毫秒)20000
llmRefineMaxInputChars提炼输入截断字数12000

CoT 采集 / CoT 分析 / CoT 增补

这三组用于归纳"现有检测器看不见的打转形态"。默认全部关闭;开启会在本地留存你的思考文本,请自行评估隐私影响。

键含义建议
enableHarvest启用静默采集关闭;开启前先读隐私说明
harvestIncludeToolTrace记录工具调用轨迹跟随 enableHarvest
harvestMaxRecords样本条数上限200
harvestRetentionDays样本保留天数14
harvestMinTextChars存全文的最小思考字数3000
harvestMaxTextChars单条全文上限8000
harvestFlushDebounceMs落盘去抖(毫秒)5000
enableAutoAnalyze达到阈值自动分析关闭
analyzeTriggerSamples自动分析触发条数50
analyzeProvider / analyzeModel分析用模型留空则跟随样本
analyzeMaxTokens分析输出上限4000
analyzeTimeoutMs分析超时(毫秒)180000
analyzeMaxInputChars分析输入截断字数8000
analyzeMaxSamples单批样本数3
analyzeRunBudgetMs单次分析总时长上限(毫秒)300000
enableLearnedPatterns加载已采纳的学习模式保持默认
learnedMaxPhrases学习模式条数上限60
learnedMaxShift学习层对出厂阈值的最大调整幅度20
learnedShadowRounds影子期命中次数(0=直接生效)20

learnedShadowRounds 默认为 20,含义是学习出的模式不会立刻生效,需累计命中 20 次才转正——这是防止一次误学就永久改变判定。如果你希望它立即生效,可设为 0;这会提高误判风险。

隐私

  • 结论注入与打转判定全部在本地完成,不发送任何数据到外部服务。
  • LLM 提炼开启时,会把当前思考文本发给你指定的 provider(留空则发给你正在用的模型对应的服务)。
  • CoT 采集开启时,会在本地留存思考文本与工具调用轨迹。

常见问题

设置页显示"当前内核没有掐断能力" 该版本的 DSH 内核不提供生成中途打断所需的钩子。结论注入不受影响。若需要该能力,可停留在 0.1.5-rc.1 / rc.2。

模型好像没被提醒到 确认 enableToolInject 已开启,且思考长度超过 minReasoningChars。思考过短时本来就不会提取结论。

打断太频繁 优先关闭 enableTransition;其次调高各判定阈值。enableChurn 是启发式判定,最容易偏激进。

开发

node test-repeat.mjs          # 重复循环检测
node test-number-runaway.mjs  # 数字流退化检测
node test-softcut.mjs         # 软切判定

测试以 lib/index.js 为源,剥离 import/export 后用 new Function 求值被测函数,因此新增测试无需改动模块结构。

许可

MIT