DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

JiewiW /

JiewiW/dsh-cot-anchor

Verified

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

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@dafc19c3

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/20/3,总闸 29),打满后当回合剩余生成放行或升级为终止本回合;完整口径见下方「软切预算与内核事件口径」。

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

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

指标 数值 口径
思考 : 正式文本 37.8 : 1 reasoning 与 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% 以内。每个注入块在首现时按未命中计费一次,此后随上下文增长参与后续每一次请求。纪律块体积约占注入量五分之四,但"文本固定"不带来额外缓存收益——该占比只反映体积,不反映命中。

软切预算与内核事件口径(与安装副本逐行对齐)

软切的检查节奏、分类预算与事件名均由安装副本内核定义,不由本插件决定。下列数值与口径对齐移植后的内核文件 node_modules/@deepseek-ai/dsh-agent-loop/lib/index.js(可用随包 tools/apply-softcut-port.mjs --check 确认该副本处于标准移植态);插件侧只返回 cut/ignore 判定与 cutClass,不持有这些常量。

检查间隔

常量 值 含义 来源行
SOFT_CUT_CHECK_INTERVAL_CHARS 32 两次软切检查之间至少累积 32 个新可见字符 lib/index.js:19
SOFT_CUT_TAIL_CHARS 240 每次征询交给监听器的尾部可见字符数 lib/index.js:21

每回合分类预算 6/20/3(total 29)

2026-10-07 缺陷修复后,旧的单一「每回合最多 6 次软切」被按判定类别分桶的三预算取代(lib/index.js:22-23 注释明示取代 MAX_SOFT_CUTS_PER_TURN=6):

预算桶 值 覆盖的判定类别 来源行
BUDGET_HEURISTIC 6 churn / transition 等启发式判定(有误报余量) lib/index.js:25
BUDGET_CONFIRMED_REPEAT 20 repeat / numberRunaway 等字节级确证判定 lib/index.js:27
BUDGET_PSEUDO_TOOL 3 伪工具文本(pseudoTool) lib/index.js:29
BUDGET_TOTAL 29 waterfall 前廉价总闸 = 6 + 20 + 3 lib/index.js:40
  • 6/20/3 分配下 total 达到 29 时任一类别必已达其类上限(抽屉原理,lib/index.js:30-39),总闸在调用插件前直接 continue,不再发起 waterfall。
  • P4 预算策略(lib/index.js:73-79):confirmed-repeat 与 pseudoTool 为高置信命中,恒 "take",计数器照常累加供观测;仅 heuristic 保留可耗尽语义——计数 ≥ 6 时返回 "ignore",由调用点升级为终止本回合。

suppressed 与 abort-turn 事件口径

预算被耗尽或总闸短路时,内核不静默,事件流可观测(事件名可在会话日志按字符串检索):

事件名 发射时机 关键字段 来源行
agent/soft-cut-suppressed(reason=total-gate) 每回合首次到达 total 29 总闸 threshold: 29,append 条带 ignorable: true lib/index.js:1394-1408
agent/soft-cut-suppressed(reason=budget-exhausted) heuristic 桶达 6、本次 cut 被判 ignore cutClass(缺省按 heuristic) lib/index.js:1451-1465
phase.abortTurn(reason=soft-cut-budget-exhausted) budget-exhausted 后置位,请求结束 detail.cutClass lib/index.js:1475-1478
agent/abort-turn(waterfall) 步边界消费 abortTurn 时征询处置 决定后发 agent/abort-turn-decided / agent/abort-turn-vetoed lib/index.js:1172-1213
agent/abort-turn-final / agent/abort-turn-draining P5 粘性终止的最终裁决/排空态 hard-break / immediate / draining lib/index.js:1138-1273
agent/soft-cut-taken 每次软切被实际采纳(三态可观测) cutClass、计数 lib/index.js:1485-1496

口径边界:suppressed 表示「这次 cut 未被采纳」(总闸短路或类预算耗尽),不代表插件未开火;abort-turn 是步边界事件,仅 heuristic 耗尽路径会升级到它。

兼容性

能力 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 锚点」共 61 项,按用途分组。下列"建议值"是针对多数场景的推荐;不同模型差异较大,可据实调整。

总开关

键 含义 建议
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 是启发式判定,最容易偏激进。

切句与数字流检测的边界行为(实现备注)

  • splitSentences(lib/index.js:538-543)换行边界:切句前先用 /\n+/g → 空格 把连续换行折叠为单个空格,即换行本身不构成句子边界,跨行句子按同一句处理;真正的边界只认句末标点——。!?!? 后随空白即切(但标点后紧接编号形态,如 ? 2.、1、) 这类列表序号不切),英文句点须后随空白或文末、且不与相邻句点组成省略号、前一位不是数字或句点(防小数与 ... 误切)。
  • detectNumberRunaway(lib/index.js:1144-1189)枚举/表格形态:纯函数,输入不足 800 字(NUMBER_RUN_MIN_TEXT_CHARS)直接返回 null;用 /\b\d{1,4}\b/g 枚举 1–4 位整数 token,相邻 token 间距 ≤ 8 字符(NUMBER_RUN_MAX_GAP)且中间不含字母或中日韩汉字才归入同一递增串;每个串须同时满足三项才判命中:字符跨度 ≥ 500(NUMBER_RUN_MIN_CHARS)、数字字符占比 ≥ 0.5(NUMBER_RUN_MIN_DIGIT_RATIO)、非递减相邻对占比 ≥ 0.55(NUMBER_RUN_MIN_INCREASE_RATIO,b>=a 即计入);命中返回 {chars, digitRatio, increaseRatio, trimTo}(trimTo 为串首索引,供裁尾定位),五个阈值均由同名 numberRun* 设置键运行时绑定。

开发

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

测试以 lib/index.js 为源,剥离 import/export 后用 new Function 求值被测函数,因此新增测试无需改动模块结构。全量回归:仓库根目录所有 test-*.mjs 均为独立可执行脚本(无测试框架、无 npm test),逐个 node test-*.mjs 运行,退出码 0 即通过(verify-browser.mjs 非 test- 前缀,不在此列)。

许可

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit dafc19c30820

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout