dsh-fact-check 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/INSTRUCTIONS.md +71 -0
- package/README.md +23 -1
- package/README.zh-CN.md +21 -1
- package/SKILL.md +72 -1
- package/lib/index.js +72 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,36 @@
|
|
|
3
3
|
面向使用者的**版本间净变化**(修复 / 改进 / 新增)。只写最终结果,不写过程。
|
|
4
4
|
判据、阈值、触发词的任何改动都必须同时更新 `MEASUREMENTS.md` 的数字与 `cases/` 的回归结果。
|
|
5
5
|
|
|
6
|
+
## 1.5.0 — 2026-10-23
|
|
7
|
+
|
|
8
|
+
**新增**
|
|
9
|
+
|
|
10
|
+
- **「利益相关与污染面」一节:标注制、不降权、双轨呈现。** 此前来源分级表只有一条轴(来源类型),并把"一手"隐含当成"可信"——真实缺口在于 **provenance(这条说法的源头在哪)** 与 **incentive(说它的人会不会因为"它为真"而获益)** 是两个不同问题,而原表只问了前者。新节**不判定**来源是否存在利益关联、是否被投毒,只做三件事:① 按 **L1 客观可查 / L2 可推断 / L3 不可判定** 三层标注;② 把同一来源的两个角色拆开——**角色 A「它确实说过」**(事实级,不受关联影响,发布行为可单独判已证实)与**角色 B「所以说的是真的」**(受关联与污染影响的那一半);③ 给出**甲轨(标注不参与定档,正式结论)** 与**乙轨(剔除标注源后重算)** 两套固定口径的档位,差额交给读者裁定。
|
|
11
|
+
- **报告模板新增「利益相关与污染面」一节**,并在「一句话结论」里加一行乙轨档位;标注表为空时只写一行"两轨一致",成本随证据量伸缩。
|
|
12
|
+
- **污染面只声明攻击面,不承诺识别投毒**:给出首发时序 / 引用链形状 / 跨生态痕迹 / 结论冗余度四项结构性检查,并明写它们都**不构成"被投毒"的判定**;同时明确承认"一手源本身被投毒"这一类不可解,要求如实写"查不出"而不是假装能查。
|
|
13
|
+
|
|
14
|
+
**为什么不写成降权(三条理由已写进正文)**
|
|
15
|
+
|
|
16
|
+
- **非单调**:任何"没有替代源就不降权"式的条件降权,会让新增一条独立佐证反而触发降权。要做到单调就等价于降权无效,二者不可兼得。
|
|
17
|
+
- **奖励信息垄断**:若"唯一来源"能拿全权重,则"确保别人无法核实"成为拿到高置信度的最优策略——正是投毒与宣传想要的性质。
|
|
18
|
+
- **全局降档**:投毒永远无法排除,把它接进降档条件等价于给全技能统一减一档,判定力归零。
|
|
19
|
+
|
|
20
|
+
**其它**
|
|
21
|
+
|
|
22
|
+
- `SKILL.md` frontmatter 与 `package.json` 版本号同步到 **1.5.0**;`INSTRUCTIONS.md` 由 `make-instructions.mjs` 重新生成。
|
|
23
|
+
- **判定逻辑未变**:档位仍只由「独立源头数」决定;`tools/` 下三个工具与 `cases/` 回归语料本轮**未改动**,`selftest` / `regress` 数字应与 1.4.0 一致。
|
|
24
|
+
|
|
25
|
+
## 1.4.0 — 2026-10-17
|
|
26
|
+
|
|
27
|
+
**新增**
|
|
28
|
+
|
|
29
|
+
- **接入 DSH 0.2.1-rc.2 的两项技能注册表能力**:① 候选与定义带 `path`(`SKILL.md` 的绝对路径,catalog 摘要保留),宿主可据此按文件定位;② provider 监听 `SKILL.md`,保存后调用 `control.invalidate()`,会话中的技能目录**无需重启**即刷新(150 ms 去抖;watcher 在 `control.signal` abort 或 context dispose 时关闭;宿主不传 `control` 时退回「重启生效」)。
|
|
30
|
+
- 这次是**增量接入而非适配**:`@deepseek-ai/dsh-skill` 的 `lib/index.js` 在 0.2.0-rc.2 与 0.2.1-rc.2 之间逐字节一致(差异仅换行与版本元数据),`registerProvider` 的签名没有变,新能力都在既有的 `control = { signal, invalidate() }` 里。同一份代码在 **0.2.0-rc.2(已发布包)** 与 **0.2.1-rc.2(宿主 asar 里抽出的注册表)** 上各 21/21 通过,因此向前兼容。
|
|
31
|
+
|
|
32
|
+
**其它**
|
|
33
|
+
|
|
34
|
+
- 发布 1.4.0:`package.json` 与 `SKILL.md` frontmatter 的版本号同步;README / README.zh-CN 的已验证宿主补上 **0.2.1-rc.2**,并记录本轮证据(两套注册表各 21/21、`snapshot.complete`、`get()` 正文逐字、`provider.list()` rank 600、两轮编辑→刷新;`selftest` 27/27 · 25/25 · 37/37,`regress` 26/26)。
|
|
35
|
+
|
|
6
36
|
## 1.3.0 — 2026-09-22
|
|
7
37
|
|
|
8
38
|
**新增**
|
package/INSTRUCTIONS.md
CHANGED
|
@@ -99,6 +99,8 @@
|
|
|
99
99
|
| 社区/个人(论坛、问答、社交平台、博客) | 低(仅旁证/线索) |
|
|
100
100
|
| AI 生成内容 / 无法溯源内容 | 不采信 |
|
|
101
101
|
|
|
102
|
+
> **类型只是其中一条轴。** 同一来源还可能有利益关联,也可能处在大规模投毒污染的信息链上。这两项**不改变上表的权重、也不改变结论档位**——只按「利益相关与污染面」一节标注,并以双轨结论呈现(甲轨即本节档位)。
|
|
103
|
+
|
|
102
104
|
来源矛盾时**如实并列呈现**,说明分歧点(数据口径、时间线、立场),不做无依据的和稀泥。
|
|
103
105
|
|
|
104
106
|
**记录来源固定四要素**:①出处名称(站点/机构/作者)②来源类型(对照上表)③完整可点击 URL(不截断、不省略协议)④引用位置(标题、段落、表格、时间戳)。原文摘录加引号并注明"原文",转述则注明"转述"。
|
|
@@ -110,6 +112,7 @@
|
|
|
110
112
|
```markdown
|
|
111
113
|
## 一句话结论
|
|
112
114
|
**<已证实 / 很可能属实 / 未证实 / 证伪 / 无法核实>** · 置信度 <高 / 中 / 低>(依据:独立来源数量与权威性)
|
|
115
|
+
**乙轨(保守口径)**:<剔除标注源后重算的档位>(详见「利益相关与污染面」;上句为甲轨正式结论)
|
|
113
116
|
<2~3 行直给判断:哪些关键点成立、哪些不成立、哪些没查清>
|
|
114
117
|
**核查时间**:<日期>;**关键信息时效**:<信息本身的日期>
|
|
115
118
|
|
|
@@ -133,6 +136,16 @@
|
|
|
133
136
|
- 国外渠道:<搜到什么、口径如何> [n]
|
|
134
137
|
- 差异说明:<信息是否只在单侧出现、为何>
|
|
135
138
|
|
|
139
|
+
## 利益相关与污染面(标注,不参与定档)
|
|
140
|
+
**甲轨(默认口径,正式结论)**:<档位> —— 标注不参与定档
|
|
141
|
+
**乙轨(保守口径,剔除标注源后重算)**:<档位> —— <与甲轨的差在哪;两轨相同则写"两轨一致">
|
|
142
|
+
| 来源 | 层级 | 可观察依据 | 乙轨动作 |
|
|
143
|
+
|---|---|---|---|
|
|
144
|
+
| [n] | L1 / L2 | <文件、披露、所有权、历史口径一致性…> | 从"内容为真"的支撑源中剔除 |
|
|
145
|
+
- **污染面**:<哪一段若被投毒则全盘失效;首发时序 / 引用链形状 / 跨生态痕迹 / 结论冗余度四项各自的观察结果>
|
|
146
|
+
- **已隔离(L3)**:<无法检验的动机归因,及"现象相同 ⇒ 不可判定"的说明>
|
|
147
|
+
- **不降权声明**:以上均为标注;甲轨不因本节下调,乙轨是给读者的敏感性视图,不是本报告主张。
|
|
148
|
+
|
|
136
149
|
## 为什么会这样(可选,不适用则整节省略)
|
|
137
150
|
<关键分歧点、数据口径差异、时间线错位或传播机制的说明>
|
|
138
151
|
|
|
@@ -160,6 +173,61 @@
|
|
|
160
173
|
|
|
161
174
|
---
|
|
162
175
|
|
|
176
|
+
## 利益相关与污染面(标注制、不降权、双轨呈现)
|
|
177
|
+
|
|
178
|
+
**本节只标注,不降权。** 一个来源是否存在利益关联、是否处在大规模投毒污染的信息链上,**本技能不做判定**;它只把能拿出来看的东西标出来,并给出两套固定口径下的结论,由读报告的人自己裁定。
|
|
179
|
+
|
|
180
|
+
### 为什么不用降权(改之前先看这三条)
|
|
181
|
+
|
|
182
|
+
1. **非单调**——"没有替代源就不降权"这类条件降权,会让新增一条独立佐证反而触发降权、结论变差。"证据变多、档位变差"不可接受。
|
|
183
|
+
2. **奖励信息垄断**——若"唯一来源"能拿全权重,那么**确保别人无法核实**就是拿到高置信度的最优策略。这正是投毒与宣传想要的性质。
|
|
184
|
+
3. **全局降档**——投毒永远无法排除(连"无法排除"本身都无法排除)。一旦把它接进降档条件,等价于给整个技能统一减一档,判定力归零。
|
|
185
|
+
|
|
186
|
+
三条同源:**把"源的属性"当成了"结论的档位"**。所以本节只标注,档位仍按「信源不足的处理」③ 的独立源头数算。
|
|
187
|
+
|
|
188
|
+
### 标注三层
|
|
189
|
+
|
|
190
|
+
| 层级 | 判据 | 写进哪里 |
|
|
191
|
+
|---|---|---|
|
|
192
|
+
| **L1 客观可查** | 出资方 / 所有权 / 资助披露 / 监管文件 / 广告主集中度 / 作者利益申报——有文件就能引 | 标注表,带来源编号 |
|
|
193
|
+
| **L2 可推断** | 同类事件的历史口径一致性;"若此说为真,谁获益" | 标注表,**必须标明是推断** |
|
|
194
|
+
| **L3 不可判定** | 无法检验的动机归因("被 X 收买""是 Y 的代理人") | 只进**隔离区**;禁止进入标注表与结论 |
|
|
195
|
+
|
|
196
|
+
**L3 的机械化判据**:该指控成立与否,**能观察到的现象完全相同** ⇒ 不可判定 ⇒ 强制隔离。不靠"听起来像阴谋论"来判。
|
|
197
|
+
|
|
198
|
+
### 同一来源的两个角色必须分开
|
|
199
|
+
|
|
200
|
+
- **角色 A「它确实说过」**:事实级,**不受任何关联影响**。谁说的、原文是什么、何时说的,本身就可核实;"发布行为"可单独判「已证实」,不需要第三方佐证。
|
|
201
|
+
- **角色 B「所以说的是真的」**:受关联与污染影响的正是这一半,也是双轨要分开算的那一半。
|
|
202
|
+
|
|
203
|
+
**"官方发布" ≠ "内容为真"**:统计局发布 CPI,角色 A 证明"确实发布了这个数"(已证实),角色 B 证明"物价真涨了这个数"(受口径与发布方动机影响)。把两个问法混成"一手 = 可信",是这类判断出错的根源。
|
|
204
|
+
|
|
205
|
+
### 污染面:只声明攻击面,不承诺识别投毒
|
|
206
|
+
|
|
207
|
+
**单篇文本里看不出投毒**,能看的只有分布与时序。下面四项只用来**标出攻击面**,任一项异常都不构成"被投毒"的判定:
|
|
208
|
+
|
|
209
|
+
| 检查 | 看什么 |
|
|
210
|
+
|---|---|
|
|
211
|
+
| 首发时序 | 谁最早;是否早于它声称的"因" |
|
|
212
|
+
| 引用链形状 | N 条结果是 N 个上游,还是都指向同一个 seed、同时出现在大量低质镜像 |
|
|
213
|
+
| 跨生态痕迹 | 一侧有共识、另一侧零痕迹——必须与"审查 / 本地新闻"并列写出,**不作判据** |
|
|
214
|
+
| 结论冗余度 | 单条结论依赖几条互不共享上游的链;投毒成本随链数上升 |
|
|
215
|
+
|
|
216
|
+
**不可解的那一类要明说**:一手源本身被投毒(注册库、统计口径、监管公告造假)时,下游天然一致、冗余失效。这类只能在报告里承认"查不出",不能假装能查。
|
|
217
|
+
|
|
218
|
+
### 双轨结论(两套口径都要能复算)
|
|
219
|
+
|
|
220
|
+
标注不是免责声明,它必须落到两个**按固定规则算出来**的档位上:
|
|
221
|
+
|
|
222
|
+
- **甲轨(默认口径)**:标注**不参与定档**,即「信源不足的处理」③ 照算。**这是报告的正式结论。**
|
|
223
|
+
- **乙轨(保守口径)**:把标注表里 L1/L2 的源,以及污染面中"若被投毒则该段失效"的条目,**从"支撑内容为真"的独立源头集合中剔除**后重算档位。L3 不参与重算(没有可观察对象)。若某事实点的支撑源全部落在标注表里,乙轨该点就是「无法核实」——这正是要给读者看的东西。
|
|
224
|
+
|
|
225
|
+
**两轨并列呈现,差额本身就是信息**:甲轨高、乙轨低 ⇒ 结论依赖那些被标注的源;两轨相同 ⇒ 关联与污染面不影响这个结论。**不写"我更倾向哪一轨"**——那是读者的判断,不是技能的。
|
|
226
|
+
|
|
227
|
+
**成本控制**:标注表为空时,报告里一行写"未发现可标注的利益关联与污染面,两轨一致",不展开。
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
163
231
|
## 可选增强:把结构化判断交给决策模型
|
|
164
232
|
|
|
165
233
|
若你的环境里有一个"输入状态 + 结构化问题、返回带类型答案与概率"的决策模型(本包工具默认对接 TypeSafe System One 协议,端点/模型可配置),可以用它替代人来做三类**事实性**判断:
|
|
@@ -213,6 +281,8 @@
|
|
|
213
281
|
|
|
214
282
|
单一来源**永远不写成"已证实"**,只能进旁注或"局限与待核实"。
|
|
215
283
|
|
|
284
|
+
**本节档位只看独立源头数。** 利益相关与污染面**不参与**这个计算,也不改变它算出的档位——它们另行按「利益相关与污染面」标注并双轨呈现(甲轨就是本节档位)。
|
|
285
|
+
|
|
216
286
|
**④ 报告如实呈现缺口**:在"局限与待核实"写明——哪个事实点不足、缺到什么程度、为什么(时效太新 / 题材敏感 / 封闭生态 / 登录墙反爬)、已尝试哪些渠道;并给出可操作建议(如"等主流媒体跟进或官方回应后再确认")。
|
|
217
287
|
|
|
218
288
|
**红线**:信源不足时禁止用内部知识/推测填充,禁止编造来源。**搜不到 ≠ 不存在**(尤其国内封闭生态),如实标注"公开渠道未检索到"。
|
|
@@ -263,6 +333,7 @@
|
|
|
263
333
|
- **环境相关的值只放 `tools/local.json`**(模板 `tools/local.example.json`):密钥、端点、state 目录、数据表里的 `${占位符}`,以及可选的能力绑定 `capabilities`、输出约定 `outputConvention`、运行时基线 `runtimeNotes`。代码与数据表**不写死任何主机路径**,`selftest` 里有断言在守这条。
|
|
264
334
|
- **不要在本技能正文里写入任何主机、内网地址、路径或密钥**。本机事实要么进 `local.json`,要么作为"在某环境测得"的证据进 `MEASUREMENTS.md`。
|
|
265
335
|
- **版本纪律**:凡是改动面向使用者的行为(流程、报告模板、判据、触发词),就升 frontmatter 的 `version` 并在 `CHANGELOG.md` 记一条净变化;只改措辞或文档不算。
|
|
336
|
+
- **「利益相关与污染面」是标注制,不是降权制**:不要把任何"利益相关 / 疑似投毒 ⇒ 降档"的规则接进定档。至少会同时坏三件事(非单调、奖励信息垄断、全局降档),三条理由是那一节的正文,改之前先重读。
|
|
266
337
|
- 改文件前先备份;改完跑:`node tools/smoke-plugin.mjs`(插件挂载与单一真相源)、`node tools/doctor.mjs`(需要时加 `--net`)、`node tools/jev-verdict.mjs selftest`、`node tools/route.mjs selftest`、以及受影响的 `regress`。改了本文件还要重跑 `node tools/make-instructions.mjs` 同步 `INSTRUCTIONS.md`(它是生成物)。
|
|
267
338
|
|
|
268
339
|
## 使用边界
|
package/README.md
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
# dsh-fact-check
|
|
2
2
|
|
|
3
3
|
[](https://github.com/7starsseeker/dsh-fact-check/tags)
|
|
4
|
+
[](https://www.npmjs.com/package/dsh-fact-check)
|
|
5
|
+
[](https://www.npmjs.com/package/dsh-fact-check)
|
|
4
6
|
[](https://github.com/7starsseeker/dsh-fact-check/actions/workflows/selftest.yml)
|
|
5
7
|
[](./LICENSE)
|
|
6
8
|
[](https://nodejs.org)
|
|
7
9
|
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
10
|
+
[](https://awesome-dsh-plugin.com)
|
|
8
11
|
[](./package.json)
|
|
9
12
|
[](https://github.com/7starsseeker/dsh-fact-check/commits/main)
|
|
10
13
|
[](https://github.com/7starsseeker/dsh-fact-check/stargazers)
|
|
11
14
|
[](https://github.com/7starsseeker/dsh-fact-check/issues)
|
|
12
15
|
[](./CONTRIBUTING.md)
|
|
16
|
+
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
13
17
|
|
|
14
18
|
> A fact-checking skill for DeepSeek Harness that verifies claims against the open web instead of model
|
|
15
19
|
> memory — multi-source cross-checking, domestic and international search ecosystems in parallel,
|
|
@@ -64,14 +68,32 @@ are kept in their own sections — *unverified* and *undecidable* never sit in t
|
|
|
64
68
|
The skill body is not bound to any host, product or tool chain: it names *capabilities*, and
|
|
65
69
|
[`ADAPTING.md`](./ADAPTING.md) maps them onto your environment.
|
|
66
70
|
|
|
71
|
+
**Verified hosts: DSH 0.1.7-rc.2, 0.2.0-rc.2 and 0.2.1-rc.2.** DSH 0.1.7-rc.2 was checked end to end on 2026-09-25 (Node 24.21.0, WSL/Linux): the host discovers the skill
|
|
72
|
+
and loads its body; the plugin form mounts on the host's own skill registry with `source: bundled`, its
|
|
73
|
+
description read from `SKILL.md` and `get()` returning that body verbatim; `npm run selftest` passes
|
|
74
|
+
(plugin mount 27/27, `jev-verdict` 25/25, `route` 37/37); `route.mjs regress` is 26/26 offline; and the
|
|
75
|
+
judgement corpora reproduce the expectations in [`MEASUREMENTS.md`](./MEASUREMENTS.md) §6 — `support`
|
|
76
|
+
37/40, `injection` 11/12, `provenance` 15/16, `usable_page` 12/12, `sufficient` 24/40 (the last one is
|
|
77
|
+
the criterion §1 already says not to use on its own). No host version is declared in `package.json` —
|
|
78
|
+
`engines` carries only `node`, and an exact version there would make the plugin market report
|
|
79
|
+
"confirmed incompatible" and block every other host — so a host not listed here is untested, not
|
|
80
|
+
forbidden.
|
|
81
|
+
|
|
82
|
+
**DSH 0.2.0-rc.2 was checked end to end on 2026-09-30** — this time the host *was* upgraded (`@deepseek-ai/dsh-desktop-runtime` 0.2.0-rc.2, Node 24.12.0, Windows). The directory form is live on it: the running session's own skill catalog lists `fact-check`, discovered from the host's skill directory by the host's real `@deepseek-ai/dsh-skill-filesystem` provider. The tools were re-run on that host against the live judgement model: `npm run selftest` 27/27, 25/25, 37/37; `route.mjs regress` 26/26; and the judgement corpora, which need a key — `support` 37/40 (Brier 0.0451), `injection` 11/12 (0.0369), `primary_source` 15/16 (0.0487), `usable_page` 12/12 (0.0031) — all reproducing the [`MEASUREMENTS.md`](./MEASUREMENTS.md) §6 expectations. The host file implementing the skill registry is byte-identical between 0.1.7-rc.2 and 0.2.0-rc.2, so no code change was needed and none was made. One thing stays where the 2026-09-29 round left it: the **plugin** form was not mounted on this host — that machine runs the directory form, so the plugin package is not installed there — and its evidence remains the registration check against the host's own **published** `0.2.0-rc.2` packages, where it mounts on the real `@deepseek-ai/dsh-skill` 0.2.0-rc.2 registry with its candidate rank equal to the host's exported `BUNDLED_SKILL_RANK` and `get()` returning the `SKILL.md` body verbatim (15/15), and the directory form wins the same-name merge against the bundled copy at rank 400 against 600 (6/6).
|
|
83
|
+
|
|
84
|
+
**DSH 0.2.1-rc.2 was checked end to end on 2026-10-17** (Node 24.21.0, Windows). The registry implementation (`@deepseek-ai/dsh-skill`'s `lib/index.js`) has **no content change** between 0.2.0-rc.2 and 0.2.1-rc.2 — the diff is line endings and version metadata only (`package.json` bumps the peers to cordis `~4.0.5` and adds a `./locale/*.json` export) — so no API adaptation was needed. What did change is what a provider may do with `control`, and this package now uses both 0.2.1 affordances: the candidate carries `path` (the absolute `SKILL.md`, retained in catalog summaries), and the provider watches `SKILL.md` and calls `control.invalidate()` on save, so editing the body refreshes the session catalog without a restart (150 ms debounce; the watcher is torn down on `control.signal` abort or context dispose, and a host that passes no `control` keeps the previous "restart to re-read" behavior). Evidence: the plugin was mounted on the host's own `0.2.1-rc.2` registry code (taken from the shipped asar, wired with the host's own cordis 4.0.5) and on the **published** `0.2.0-rc.2` package — 21/21 checks on each, `snapshot.complete === true`, `get()` returning the body verbatim, `provider.list()` rank 600, and both edit→refresh cycles observed; `npm run selftest` 27/27, 25/25, 37/37 and `route.mjs regress` 26/26 re-run on Node 24.21.0.
|
|
85
|
+
|
|
67
86
|
## Install
|
|
68
87
|
|
|
69
88
|
### As a DSH plugin
|
|
70
89
|
|
|
71
90
|
```sh
|
|
72
|
-
dsh plugin add
|
|
91
|
+
dsh plugin add dsh-fact-check
|
|
73
92
|
```
|
|
74
93
|
|
|
94
|
+
That is the published npm package — the source the plugin market installs from by preference. The same
|
|
95
|
+
plugin straight from GitHub source is `dsh plugin add github:7starsseeker/dsh-fact-check`.
|
|
96
|
+
|
|
75
97
|
Then restart DSH: the `fact-check` skill appears in the session catalogue. The plugin itself is a thin
|
|
76
98
|
adapter — `lib/index.js` registers the bundled `SKILL.md` on `ctx.skills`, re-reading it on every load,
|
|
77
99
|
so editing the skill needs no code change. It has **zero runtime dependencies** (only `node:` builtins).
|
package/README.zh-CN.md
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
# dsh-fact-check
|
|
2
2
|
|
|
3
3
|
[](https://github.com/7starsseeker/dsh-fact-check/tags)
|
|
4
|
+
[](https://www.npmjs.com/package/dsh-fact-check)
|
|
5
|
+
[](https://www.npmjs.com/package/dsh-fact-check)
|
|
4
6
|
[](https://github.com/7starsseeker/dsh-fact-check/actions/workflows/selftest.yml)
|
|
5
7
|
[](./LICENSE)
|
|
6
8
|
[](https://nodejs.org)
|
|
7
9
|
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
10
|
+
[](https://awesome-dsh-plugin.com)
|
|
8
11
|
[](./package.json)
|
|
9
12
|
[](https://github.com/7starsseeker/dsh-fact-check/commits/main)
|
|
10
13
|
[](https://github.com/7starsseeker/dsh-fact-check/stargazers)
|
|
11
14
|
[](https://github.com/7starsseeker/dsh-fact-check/issues)
|
|
12
15
|
[](./CONTRIBUTING.md)
|
|
16
|
+
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
13
17
|
|
|
14
18
|
> DeepSeek Harness 用的事实核查技能:不轻信模型自己的记忆,一律去公开互联网核实——多源交叉、
|
|
15
19
|
> 国内外信息生态并行、对抗性反查,最后交出一份结论先行、每条论断都可溯源的核查报告。
|
|
@@ -58,14 +62,30 @@
|
|
|
58
62
|
技能正文不绑定任何产品、主机或工具链:它只写**能力名**,由 [`ADAPTING.md`](./ADAPTING.md) 把能力
|
|
59
63
|
落到你的环境里。
|
|
60
64
|
|
|
65
|
+
**已验证宿主:DSH 0.1.7-rc.2、0.2.0-rc.2 与 0.2.1-rc.2。** 0.1.7-rc.2 于 2026-09-25 完整跑过一遍(Node 24.21.0,WSL/Linux):宿主能发现本技能并读入正文;
|
|
66
|
+
插件形态能挂到宿主自己的技能注册表上,`source: bundled`,描述取自 `SKILL.md`,`get()` 交出的正文
|
|
67
|
+
与 `SKILL.md` 逐字一致;`npm run selftest` 全过(插件挂载 27/27、`jev-verdict` 25/25、`route` 37/37);
|
|
68
|
+
`route.mjs regress` 离线 26/26;判据语料复现 [`MEASUREMENTS.md`](./MEASUREMENTS.md) §6 的预期 ——
|
|
69
|
+
`support` 37/40、`injection` 11/12、`provenance` 15/16、`usable_page` 12/12、`sufficient` 24/40
|
|
70
|
+
(最后一条正是 §1 已写明「不要单用」的那条判据)。`package.json` 不声明宿主版本 —— `engines` 只留
|
|
71
|
+
`node`,写精确版本会让插件市场判「确认不兼容」并拦住其他所有宿主 —— 所以未列入的宿主是没验过,
|
|
72
|
+
而不是被禁止。
|
|
73
|
+
|
|
74
|
+
**DSH 0.2.0-rc.2 于 2026-09-30 完整跑过一遍** —— 这次宿主**真的升上来了**(`@deepseek-ai/dsh-desktop-runtime` 0.2.0-rc.2、Node 24.12.0、Windows)。目录形态在上面是活的:运行中会话自己的技能目录里列着 `fact-check`,是宿主真实的 `@deepseek-ai/dsh-skill-filesystem` provider 从技能目录里扫出来的。仓库自带的工具在该宿主上对着真实判定模型重跑:`npm run selftest` 27/27、25/25、37/37;`route.mjs regress` 26/26;判据语料(需要密钥)—— `support` 37/40(Brier 0.0451)、`injection` 11/12(0.0369)、`primary_source` 15/16(0.0487)、`usable_page` 12/12(0.0031)—— 全部复现 [`MEASUREMENTS.md`](./MEASUREMENTS.md) §6 的预期。实现技能注册表的那个宿主文件在 0.1.7-rc.2 与 0.2.0-rc.2 之间逐字节一致,所以无需改动、也确实一行未改。有一项仍停在 2026-09-29 那一轮的位置:**插件形态没有在这台宿主上挂载** —— 那台机器跑的是目录形态,插件包并未安装 —— 它的证据仍是针对宿主自己**已发布**的 `0.2.0-rc.2` 包做的注册检查:挂到真实的 `@deepseek-ai/dsh-skill` 0.2.0-rc.2 注册表上,候选 rank 等于宿主自己导出的 `BUNDLED_SKILL_RANK`,`get()` 交出的正文与 `SKILL.md` 逐字一致(15/15);目录形态也在那里验证过同名合并以 rank 400 压过 bundled 的 600(6/6)。
|
|
75
|
+
|
|
76
|
+
**DSH 0.2.1-rc.2 于 2026-10-17 完整跑过一遍**(Node 24.21.0,Windows)。注册表的实现(`@deepseek-ai/dsh-skill` 的 `lib/index.js`)在 0.2.0-rc.2 与 0.2.1-rc.2 之间**内容没有变化** —— diff 只有换行符与版本号(`package.json` 把 peer 依赖抬到 cordis `~4.0.5`、加了一个 `./locale/*.json` 导出)—— 所以不需要任何 API 适配。变的是 provider 能拿 `control` **做什么**,本包两样都用上了:候选带上 `path`(指向绝对的 `SKILL.md`,摘要会保留它);provider 监听 `SKILL.md`,保存时调 `control.invalidate()`,改正文不必重启就会刷新会话里的技能目录(150 ms 去抖;`control.signal` abort 或上下文销毁时收尾,宿主不传 `control` 时退化为「改完重启生效」)。证据:插件分别挂到宿主自己那套 `0.2.1-rc.2` 注册表代码(从已发布的 asar 里取出,配宿主自带的 cordis 4.0.5)与**已发布**的 `0.2.0-rc.2` 包上,各 21/21 项检查通过:`snapshot.complete === true`、`get()` 交出的正文与 `SKILL.md` 逐字一致、`provider.list()` 的 rank 为 600,两轮「改文件 → 目录自动刷新」都观察到了;`npm run selftest` 27/27、25/25、37/37 与 `route.mjs regress` 26/26 均在 Node 24.21.0 上复跑。
|
|
77
|
+
|
|
61
78
|
## 安装
|
|
62
79
|
|
|
63
80
|
### 作为 DSH 插件
|
|
64
81
|
|
|
65
82
|
```sh
|
|
66
|
-
dsh plugin add
|
|
83
|
+
dsh plugin add dsh-fact-check
|
|
67
84
|
```
|
|
68
85
|
|
|
86
|
+
上面这条装的是已发布到 npm 的包——也就是插件市场优先采用的安装源。想直接从 GitHub 源码装,用
|
|
87
|
+
`dsh plugin add github:7starsseeker/dsh-fact-check`。
|
|
88
|
+
|
|
69
89
|
重启 DSH 后 `fact-check` 技能即出现在会话技能目录里。插件本体是一层很薄的接线——`lib/index.js`
|
|
70
90
|
把包自带的 `SKILL.md` 注册到 `ctx.skills`,每次加载都现读,所以改技能不需要改代码。它是
|
|
71
91
|
**零运行时依赖**(只用 `node:` 内置模块)。因为本 provider 注册在*随包*档位,你自己放在
|
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fact-check
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.5.0
|
|
4
4
|
description: 事实核查与资料搜索。当用户要求核实事实/传闻/新闻/数据/情况的真伪、查证信息、确认现状、补充资料时使用——即使用户只说了"查一下""确认一下""核实""这是不是真的""帮我看看",也应触发。核心行为:不依赖自身知识库,所有关键信息一律通过互联网搜索获取,搜索途径与信息来源尽量多样以便交叉核实,并同时覆盖国内与国外信息生态。执行模式按工作量自动选择:轻量核查由主对话直连完成,渠道多或需抓取大量长文时自动分派并行子代理。
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -100,6 +100,8 @@ description: 事实核查与资料搜索。当用户要求核实事实/传闻/
|
|
|
100
100
|
| 社区/个人(论坛、问答、社交平台、博客) | 低(仅旁证/线索) |
|
|
101
101
|
| AI 生成内容 / 无法溯源内容 | 不采信 |
|
|
102
102
|
|
|
103
|
+
> **类型只是其中一条轴。** 同一来源还可能有利益关联,也可能处在大规模投毒污染的信息链上。这两项**不改变上表的权重、也不改变结论档位**——只按「利益相关与污染面」一节标注,并以双轨结论呈现(甲轨即本节档位)。
|
|
104
|
+
|
|
103
105
|
来源矛盾时**如实并列呈现**,说明分歧点(数据口径、时间线、立场),不做无依据的和稀泥。
|
|
104
106
|
|
|
105
107
|
**记录来源固定四要素**:①出处名称(站点/机构/作者)②来源类型(对照上表)③完整可点击 URL(不截断、不省略协议)④引用位置(标题、段落、表格、时间戳)。原文摘录加引号并注明"原文",转述则注明"转述"。
|
|
@@ -111,6 +113,7 @@ description: 事实核查与资料搜索。当用户要求核实事实/传闻/
|
|
|
111
113
|
```markdown
|
|
112
114
|
## 一句话结论
|
|
113
115
|
**<已证实 / 很可能属实 / 未证实 / 证伪 / 无法核实>** · 置信度 <高 / 中 / 低>(依据:独立来源数量与权威性)
|
|
116
|
+
**乙轨(保守口径)**:<剔除标注源后重算的档位>(详见「利益相关与污染面」;上句为甲轨正式结论)
|
|
114
117
|
<2~3 行直给判断:哪些关键点成立、哪些不成立、哪些没查清>
|
|
115
118
|
**核查时间**:<日期>;**关键信息时效**:<信息本身的日期>
|
|
116
119
|
|
|
@@ -134,6 +137,16 @@ description: 事实核查与资料搜索。当用户要求核实事实/传闻/
|
|
|
134
137
|
- 国外渠道:<搜到什么、口径如何> [n]
|
|
135
138
|
- 差异说明:<信息是否只在单侧出现、为何>
|
|
136
139
|
|
|
140
|
+
## 利益相关与污染面(标注,不参与定档)
|
|
141
|
+
**甲轨(默认口径,正式结论)**:<档位> —— 标注不参与定档
|
|
142
|
+
**乙轨(保守口径,剔除标注源后重算)**:<档位> —— <与甲轨的差在哪;两轨相同则写"两轨一致">
|
|
143
|
+
| 来源 | 层级 | 可观察依据 | 乙轨动作 |
|
|
144
|
+
|---|---|---|---|
|
|
145
|
+
| [n] | L1 / L2 | <文件、披露、所有权、历史口径一致性…> | 从"内容为真"的支撑源中剔除 |
|
|
146
|
+
- **污染面**:<哪一段若被投毒则全盘失效;首发时序 / 引用链形状 / 跨生态痕迹 / 结论冗余度四项各自的观察结果>
|
|
147
|
+
- **已隔离(L3)**:<无法检验的动机归因,及"现象相同 ⇒ 不可判定"的说明>
|
|
148
|
+
- **不降权声明**:以上均为标注;甲轨不因本节下调,乙轨是给读者的敏感性视图,不是本报告主张。
|
|
149
|
+
|
|
137
150
|
## 为什么会这样(可选,不适用则整节省略)
|
|
138
151
|
<关键分歧点、数据口径差异、时间线错位或传播机制的说明>
|
|
139
152
|
|
|
@@ -161,6 +174,61 @@ description: 事实核查与资料搜索。当用户要求核实事实/传闻/
|
|
|
161
174
|
|
|
162
175
|
---
|
|
163
176
|
|
|
177
|
+
## 利益相关与污染面(标注制、不降权、双轨呈现)
|
|
178
|
+
|
|
179
|
+
**本节只标注,不降权。** 一个来源是否存在利益关联、是否处在大规模投毒污染的信息链上,**本技能不做判定**;它只把能拿出来看的东西标出来,并给出两套固定口径下的结论,由读报告的人自己裁定。
|
|
180
|
+
|
|
181
|
+
### 为什么不用降权(改之前先看这三条)
|
|
182
|
+
|
|
183
|
+
1. **非单调**——"没有替代源就不降权"这类条件降权,会让新增一条独立佐证反而触发降权、结论变差。"证据变多、档位变差"不可接受。
|
|
184
|
+
2. **奖励信息垄断**——若"唯一来源"能拿全权重,那么**确保别人无法核实**就是拿到高置信度的最优策略。这正是投毒与宣传想要的性质。
|
|
185
|
+
3. **全局降档**——投毒永远无法排除(连"无法排除"本身都无法排除)。一旦把它接进降档条件,等价于给整个技能统一减一档,判定力归零。
|
|
186
|
+
|
|
187
|
+
三条同源:**把"源的属性"当成了"结论的档位"**。所以本节只标注,档位仍按「信源不足的处理」③ 的独立源头数算。
|
|
188
|
+
|
|
189
|
+
### 标注三层
|
|
190
|
+
|
|
191
|
+
| 层级 | 判据 | 写进哪里 |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| **L1 客观可查** | 出资方 / 所有权 / 资助披露 / 监管文件 / 广告主集中度 / 作者利益申报——有文件就能引 | 标注表,带来源编号 |
|
|
194
|
+
| **L2 可推断** | 同类事件的历史口径一致性;"若此说为真,谁获益" | 标注表,**必须标明是推断** |
|
|
195
|
+
| **L3 不可判定** | 无法检验的动机归因("被 X 收买""是 Y 的代理人") | 只进**隔离区**;禁止进入标注表与结论 |
|
|
196
|
+
|
|
197
|
+
**L3 的机械化判据**:该指控成立与否,**能观察到的现象完全相同** ⇒ 不可判定 ⇒ 强制隔离。不靠"听起来像阴谋论"来判。
|
|
198
|
+
|
|
199
|
+
### 同一来源的两个角色必须分开
|
|
200
|
+
|
|
201
|
+
- **角色 A「它确实说过」**:事实级,**不受任何关联影响**。谁说的、原文是什么、何时说的,本身就可核实;"发布行为"可单独判「已证实」,不需要第三方佐证。
|
|
202
|
+
- **角色 B「所以说的是真的」**:受关联与污染影响的正是这一半,也是双轨要分开算的那一半。
|
|
203
|
+
|
|
204
|
+
**"官方发布" ≠ "内容为真"**:统计局发布 CPI,角色 A 证明"确实发布了这个数"(已证实),角色 B 证明"物价真涨了这个数"(受口径与发布方动机影响)。把两个问法混成"一手 = 可信",是这类判断出错的根源。
|
|
205
|
+
|
|
206
|
+
### 污染面:只声明攻击面,不承诺识别投毒
|
|
207
|
+
|
|
208
|
+
**单篇文本里看不出投毒**,能看的只有分布与时序。下面四项只用来**标出攻击面**,任一项异常都不构成"被投毒"的判定:
|
|
209
|
+
|
|
210
|
+
| 检查 | 看什么 |
|
|
211
|
+
|---|---|
|
|
212
|
+
| 首发时序 | 谁最早;是否早于它声称的"因" |
|
|
213
|
+
| 引用链形状 | N 条结果是 N 个上游,还是都指向同一个 seed、同时出现在大量低质镜像 |
|
|
214
|
+
| 跨生态痕迹 | 一侧有共识、另一侧零痕迹——必须与"审查 / 本地新闻"并列写出,**不作判据** |
|
|
215
|
+
| 结论冗余度 | 单条结论依赖几条互不共享上游的链;投毒成本随链数上升 |
|
|
216
|
+
|
|
217
|
+
**不可解的那一类要明说**:一手源本身被投毒(注册库、统计口径、监管公告造假)时,下游天然一致、冗余失效。这类只能在报告里承认"查不出",不能假装能查。
|
|
218
|
+
|
|
219
|
+
### 双轨结论(两套口径都要能复算)
|
|
220
|
+
|
|
221
|
+
标注不是免责声明,它必须落到两个**按固定规则算出来**的档位上:
|
|
222
|
+
|
|
223
|
+
- **甲轨(默认口径)**:标注**不参与定档**,即「信源不足的处理」③ 照算。**这是报告的正式结论。**
|
|
224
|
+
- **乙轨(保守口径)**:把标注表里 L1/L2 的源,以及污染面中"若被投毒则该段失效"的条目,**从"支撑内容为真"的独立源头集合中剔除**后重算档位。L3 不参与重算(没有可观察对象)。若某事实点的支撑源全部落在标注表里,乙轨该点就是「无法核实」——这正是要给读者看的东西。
|
|
225
|
+
|
|
226
|
+
**两轨并列呈现,差额本身就是信息**:甲轨高、乙轨低 ⇒ 结论依赖那些被标注的源;两轨相同 ⇒ 关联与污染面不影响这个结论。**不写"我更倾向哪一轨"**——那是读者的判断,不是技能的。
|
|
227
|
+
|
|
228
|
+
**成本控制**:标注表为空时,报告里一行写"未发现可标注的利益关联与污染面,两轨一致",不展开。
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
164
232
|
## 可选增强:把结构化判断交给决策模型
|
|
165
233
|
|
|
166
234
|
若你的环境里有一个"输入状态 + 结构化问题、返回带类型答案与概率"的决策模型(本包工具默认对接 TypeSafe System One 协议,端点/模型可配置),可以用它替代人来做三类**事实性**判断:
|
|
@@ -214,6 +282,8 @@ description: 事实核查与资料搜索。当用户要求核实事实/传闻/
|
|
|
214
282
|
|
|
215
283
|
单一来源**永远不写成"已证实"**,只能进旁注或"局限与待核实"。
|
|
216
284
|
|
|
285
|
+
**本节档位只看独立源头数。** 利益相关与污染面**不参与**这个计算,也不改变它算出的档位——它们另行按「利益相关与污染面」标注并双轨呈现(甲轨就是本节档位)。
|
|
286
|
+
|
|
217
287
|
**④ 报告如实呈现缺口**:在"局限与待核实"写明——哪个事实点不足、缺到什么程度、为什么(时效太新 / 题材敏感 / 封闭生态 / 登录墙反爬)、已尝试哪些渠道;并给出可操作建议(如"等主流媒体跟进或官方回应后再确认")。
|
|
218
288
|
|
|
219
289
|
**红线**:信源不足时禁止用内部知识/推测填充,禁止编造来源。**搜不到 ≠ 不存在**(尤其国内封闭生态),如实标注"公开渠道未检索到"。
|
|
@@ -264,6 +334,7 @@ description: 事实核查与资料搜索。当用户要求核实事实/传闻/
|
|
|
264
334
|
- **环境相关的值只放 `tools/local.json`**(模板 `tools/local.example.json`):密钥、端点、state 目录、数据表里的 `${占位符}`,以及可选的能力绑定 `capabilities`、输出约定 `outputConvention`、运行时基线 `runtimeNotes`。代码与数据表**不写死任何主机路径**,`selftest` 里有断言在守这条。
|
|
265
335
|
- **不要在本技能正文里写入任何主机、内网地址、路径或密钥**。本机事实要么进 `local.json`,要么作为"在某环境测得"的证据进 `MEASUREMENTS.md`。
|
|
266
336
|
- **版本纪律**:凡是改动面向使用者的行为(流程、报告模板、判据、触发词),就升 frontmatter 的 `version` 并在 `CHANGELOG.md` 记一条净变化;只改措辞或文档不算。
|
|
337
|
+
- **「利益相关与污染面」是标注制,不是降权制**:不要把任何"利益相关 / 疑似投毒 ⇒ 降档"的规则接进定档。至少会同时坏三件事(非单调、奖励信息垄断、全局降档),三条理由是那一节的正文,改之前先重读。
|
|
267
338
|
- 改文件前先备份;改完跑:`node tools/smoke-plugin.mjs`(插件挂载与单一真相源)、`node tools/doctor.mjs`(需要时加 `--net`)、`node tools/jev-verdict.mjs selftest`、`node tools/route.mjs selftest`、以及受影响的 `regress`。改了本文件还要重跑 `node tools/make-instructions.mjs` 同步 `INSTRUCTIONS.md`(它是生成物)。
|
|
268
339
|
|
|
269
340
|
## 使用边界
|
package/lib/index.js
CHANGED
|
@@ -11,15 +11,25 @@
|
|
|
11
11
|
* · **零依赖** —— 只 import `node:` 内置模块(与 tools/ 下两个脚本同一原则)。判定逻辑、
|
|
12
12
|
* 路由表、回归语料全留在 tools/ 与 cases/,与宿主无关。
|
|
13
13
|
*
|
|
14
|
+
* DSH 0.2.1 起注册表在 `registerProvider` 的 create 里传入 `{ signal, invalidate }`:这里
|
|
15
|
+
* 用 `invalidate()` 接住 SKILL.md 的改动(改正文不必重启),并给出候选的 `path`(摘要会
|
|
16
|
+
* 保留它,供宿主直接定位技能文件)。老宿主不传 control 时静默退化成「重启后生效」。
|
|
17
|
+
*
|
|
14
18
|
* @module dsh-fact-check
|
|
15
19
|
*/
|
|
16
20
|
|
|
21
|
+
import { watch } from 'node:fs'
|
|
17
22
|
import { readFile } from 'node:fs/promises'
|
|
18
23
|
import { fileURLToPath } from 'node:url'
|
|
19
24
|
|
|
20
25
|
/** 包根(lib/ 的上一级)。SKILL.md、tools/、cases/ 都在那里。 */
|
|
21
26
|
const ROOT_URL = new URL('..', import.meta.url)
|
|
22
27
|
const SKILL_URL = new URL('SKILL.md', ROOT_URL)
|
|
28
|
+
/** SKILL.md 的绝对路径:候选与定义的 `path` 都用它。 */
|
|
29
|
+
const SKILL_PATH = fileURLToPath(SKILL_URL)
|
|
30
|
+
|
|
31
|
+
/** 编辑器保存常常连发好几个事件,合并成一次目录失效。 */
|
|
32
|
+
const WATCH_DEBOUNCE_MS = 150
|
|
23
33
|
|
|
24
34
|
/** provider 名,在 `ctx.skills` 注册表里唯一。 */
|
|
25
35
|
const PROVIDER_NAME = 'fact-check'
|
|
@@ -93,6 +103,8 @@ export async function list() {
|
|
|
93
103
|
// 换成 skills/ 子目录会让这些相对路径失效。
|
|
94
104
|
resourceBase: { kind: 'directory', path: fileURLToPath(ROOT_URL) },
|
|
95
105
|
rank: RANK,
|
|
106
|
+
// DSH 0.2.1 起摘要会保留 path:给宿主/子代理一个能直接打开的技能文件位置。
|
|
107
|
+
path: SKILL_PATH,
|
|
96
108
|
locator: SKILL_URL,
|
|
97
109
|
},
|
|
98
110
|
]
|
|
@@ -118,6 +130,7 @@ export async function get(candidate) {
|
|
|
118
130
|
provider: PROVIDER_NAME,
|
|
119
131
|
source: 'bundled',
|
|
120
132
|
resourceBase: candidate.resourceBase,
|
|
133
|
+
path: SKILL_PATH,
|
|
121
134
|
content: body,
|
|
122
135
|
}
|
|
123
136
|
}
|
|
@@ -130,12 +143,69 @@ export const name = PROVIDER_NAME
|
|
|
130
143
|
/** 本插件依赖的服务:技能注册表。 */
|
|
131
144
|
export const inject = ['skills']
|
|
132
145
|
|
|
146
|
+
/**
|
|
147
|
+
* 监听 SKILL.md,改动后让注册表重读目录。
|
|
148
|
+
*
|
|
149
|
+
* 监听**目录**而不是文件:编辑器「写临时文件再改名」的保存方式会让文件级 watcher 直接失效。
|
|
150
|
+
* 只认 `SKILL.md` 这一个文件名(根目录下还有 tools/、cases/,它们改了与技能目录无关)。
|
|
151
|
+
*
|
|
152
|
+
* @param {{invalidate?: Function, signal?: AbortSignal}} control - 注册表给的 control。
|
|
153
|
+
* @param {{logger?: {warn?: Function}}} ctx - 仅用于告警。
|
|
154
|
+
* @returns {() => void} 停止监听(幂等)。
|
|
155
|
+
*/
|
|
156
|
+
function watchSkillFile(control, ctx) {
|
|
157
|
+
// 老宿主(或自检里传空 control)没有 invalidate:不装监听,退化成「改完重启生效」。
|
|
158
|
+
if (typeof control?.invalidate !== 'function') return () => {}
|
|
159
|
+
let timer
|
|
160
|
+
let closed = false
|
|
161
|
+
let watcher
|
|
162
|
+
try {
|
|
163
|
+
watcher = watch(fileURLToPath(ROOT_URL), { persistent: false }, (_event, filename) => {
|
|
164
|
+
// filename 为 null 表示「根目录里有东西变了」但说不出是哪个:刷新比留着旧目录安全。
|
|
165
|
+
if (closed || (filename !== null && filename !== 'SKILL.md')) return
|
|
166
|
+
if (timer !== undefined) clearTimeout(timer)
|
|
167
|
+
timer = setTimeout(() => {
|
|
168
|
+
timer = undefined
|
|
169
|
+
if (!closed) control.invalidate()
|
|
170
|
+
}, WATCH_DEBOUNCE_MS)
|
|
171
|
+
})
|
|
172
|
+
} catch (error) {
|
|
173
|
+
ctx?.logger?.warn?.(`fact-check: SKILL.md 监听未启动,改 SKILL.md 后需重启才刷新技能目录:${error.message}`)
|
|
174
|
+
return () => {}
|
|
175
|
+
}
|
|
176
|
+
/** 幂等停止:abort、ctx 销毁、watcher 自身出错都走这里。 */
|
|
177
|
+
const stop = () => {
|
|
178
|
+
if (closed) return
|
|
179
|
+
closed = true
|
|
180
|
+
if (timer !== undefined) clearTimeout(timer)
|
|
181
|
+
watcher.close()
|
|
182
|
+
}
|
|
183
|
+
watcher.on('error', (error) => {
|
|
184
|
+
if (closed) return
|
|
185
|
+
ctx?.logger?.warn?.(`fact-check: SKILL.md 监听已停用,改 SKILL.md 后需重启才刷新技能目录:${error.message}`)
|
|
186
|
+
stop()
|
|
187
|
+
})
|
|
188
|
+
control.signal?.addEventListener?.('abort', stop, { once: true })
|
|
189
|
+
return stop
|
|
190
|
+
}
|
|
191
|
+
|
|
133
192
|
/**
|
|
134
193
|
* 挂载:把本 provider 注册到 `ctx.skills`。注册是同步的,发现与读正文都在 awaits 里。
|
|
135
194
|
*
|
|
136
|
-
*
|
|
195
|
+
* 每次 create 都配一份 SKILL.md 监听;停止句柄既挂在 `control.signal` 的 abort 上,
|
|
196
|
+
* 也交给 `ctx.effect` 在插件销毁时统一收尾(宿主没有 effect 时就不登记)。
|
|
197
|
+
*
|
|
198
|
+
* @param {{skills: {registerProvider: Function}, effect?: Function, logger?: object}} ctx - Cordis 上下文。
|
|
137
199
|
* @returns {void}
|
|
138
200
|
*/
|
|
139
201
|
export function apply(ctx) {
|
|
140
|
-
ctx.skills.registerProvider(() =>
|
|
202
|
+
ctx.skills.registerProvider((control) => {
|
|
203
|
+
const stop = watchSkillFile(control, ctx)
|
|
204
|
+
if (typeof ctx.effect === 'function') {
|
|
205
|
+
ctx.effect(function* () {
|
|
206
|
+
yield () => stop()
|
|
207
|
+
}, 'fact-check skill watcher')
|
|
208
|
+
}
|
|
209
|
+
return provider
|
|
210
|
+
})
|
|
141
211
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-fact-check",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Fact-checking skill for DeepSeek Harness: verifies claims against the open web instead of model memory, cross-checks independent sources across domestic and international search ecosystems, and can route three factual judgements (does the evidence support the claim / is this source primary / does this page carry usable text) to a TypeSafe Jev (System One) decision model when a key is configured, falling back to the skill's deterministic rules when it is not.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|