trustwiki 0.1.0-alpha.0 → 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 QianJinGuo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,86 +1,206 @@
1
+ <p align="center">
2
+ <img src="assets/hero.svg" alt="trustwiki — knowledge bases your agent can maintain, without lying to you" width="100%">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/QianJinGuo/trustwiki/actions/workflows/ci.yml"><img src="https://github.com/QianJinGuo/trustwiki/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
7
+ <a href="https://www.npmjs.com/package/trustwiki"><img src="https://img.shields.io/npm/v/trustwiki" alt="npm"></a>
8
+ <img src="https://img.shields.io/node/v/trustwiki" alt="node >= 18">
9
+ <img src="https://img.shields.io/badge/license-MIT-green" alt="MIT license">
10
+ </p>
11
+
12
+ English · [简体中文](README.zh.md)
13
+
1
14
  # trustwiki
2
15
 
3
- **Knowledge bases your agent can maintain without lying to you.**
16
+ **Your agent writes your notes. Who checks them?**
4
17
 
5
- Every claim cited. Contradictions surfaced. Rot detected.
18
+ Every knowledge base maintained by an AI agent eventually rots the same way:
19
+ claims nobody can trace, contradictions quietly rewritten away, links that
20
+ die. trustwiki is the checker — it reads your markdown vault and tells you
21
+ exactly where the rot is.
6
22
 
7
23
  ```bash
8
- npx trustwiki lint ./your-vault
24
+ npx trustwiki lint ./your-notes
25
+ ```
26
+
27
+ ```text
28
+ notes/model-comparison.md
29
+ L12 error citation.target-missing citation target not found: sources/ghost.md
30
+ L14 warn provenance.excess-inferred 3/5 prose paragraphs uncited (>0.3)
31
+ L16 warn placeholder.present placeholder text: TODO
32
+ L18 error link.broken broken wikilink [[notes/dead-ref]]
33
+
34
+ Σ 3 errors, 5 warnings across 3 files
9
35
  ```
10
36
 
11
- trustwiki is a provenance linter and an operating method for knowledge bases
12
- maintained by AI agents. It does **not** build your wiki. It makes sure that
13
- when your agent does, every claim can be traced to a source, disagreements
14
- stay visible instead of being silently rewritten, and decay is measured —
15
- not discovered two months later.
37
+ That is a real report on a vault seeded to fail. [See it run (10s GIF)](#2-see-it-run) · [What each finding means](#what-each-check-catches)
38
+
39
+ ## Why it exists
40
+
41
+ AI agents already write knowledge bases — research wikis, team docs, personal
42
+ notes. Nobody proofreads them, because the volume is inhuman. The failure mode
43
+ is **fluent slop**: confident prose, zero provenance, contradictions resolved
44
+ by whoever edits last.
45
+
46
+ trustwiki is the discipline layer. It does **not** build your wiki — it makes
47
+ sure that when your agent does, every claim traces to a source, disagreements
48
+ stay visible, and decay is measured instead of discovered two months later.
49
+
50
+ It is three things:
16
51
 
17
- - **12 mechanical checks** — citation grammar, citation targets, uncited-inference ratio, confidence floor, contradiction consistency, broken links, index drift, orphans, placeholders
18
- - **One config file** `.trustwiki.json`; nothing configured, everything still runs
19
- - **Zero dependencies** — Node 18+, one command, JSON output for CI
20
- - **Proven in production** — [operating stats](proof/STATS.md) from an agent-maintained wiki running since 2026-05
52
+ <p align="center">
53
+ <img src="assets/three-layers.svg" alt="Three layers: the checker (npx trustwiki lint), the spec (frozen citation grammar), the method (SKILL.md)" width="100%">
54
+ </p>
21
55
 
22
- ## Why
56
+ - **A checker** — 12 mechanical checks: citations, contradictions, broken links, index drift, orphans
57
+ - **A spec** — a frozen, versioned definition of "traceable": [schema/spec.md](schema/spec.md)
58
+ - **A method** — the four-phase discipline your agent installs: [SKILL.md](SKILL.md)
23
59
 
24
- Agents are already writing knowledge bases. Without a trust layer they
25
- produce fluent slop: unattributed claims, silent contradiction resolution,
26
- links that rot. trustwiki is the discipline layer — the linter, the schema,
27
- and the method.
60
+ ## Getting started
28
61
 
29
- ## Quick start
62
+ ### 1. Try it on the demo (30 seconds, nothing to install)
30
63
 
31
64
  ```bash
32
65
  git clone https://github.com/QianJinGuo/trustwiki && cd trustwiki
33
- npx trustwiki lint templates/demo-vault
66
+ npx trustwiki lint templates/demo-vault # a vault seeded to fail: 8 findings, exit 1
67
+ npx trustwiki lint templates/starter-vault # the same structure built to pass: exit 0
34
68
  ```
35
69
 
36
- You get a report like this (exit code 1 — errors present):
70
+ ### 2. See it run
37
71
 
38
- ```
39
- notes/sloppy-page.md
40
- L12 error citation.target-missing citation target not found: sources/ghost.md
41
- L14 warn provenance.excess-inferred 3/5 prose paragraphs uncited (>0.3)
42
- L16 warn placeholder.present placeholder text: TODO
43
- ...
44
- Σ 3 errors, 5 warnings across 3 files
72
+ ![trustwiki linting the seeded demo vault — 8 findings](assets/demo.gif)
73
+
74
+ ### 3. Run it on your own vault
75
+
76
+ Already keep notes in markdown (Obsidian, Logseq, plain files)? Just point at
77
+ the directory. Zero config works:
78
+
79
+ ```bash
80
+ npx trustwiki lint ~/Documents/my-notes
45
81
  ```
46
82
 
47
- The eight findings are seeded in `templates/demo-vault` a vault built to
48
- fail. `templates/starter-vault` is the same structure built to pass, and is
49
- the starting point for your own vault.
83
+ Most users then add one small `.trustwiki.json` at the vault root so the
84
+ checker understands your layout:
50
85
 
51
- ## The schema
86
+ ```json
87
+ {
88
+ "roots": ["notes", "sources"],
89
+ "index": "index.md",
90
+ "sourceDir": "sources"
91
+ }
92
+ ```
52
93
 
53
- The citation grammar (`^[path:42-58]`), provenance frontmatter, and
54
- contradiction marking are specified, versioned, and frozen at
55
- [schema/spec.md](schema/spec.md) (中文版: [spec.zh.md](schema/spec.zh.md)).
56
- Implementations other than this linter are welcome.
94
+ - `roots` which directories to scan
95
+ - `index` your index file, if you keep one (enables index-drift checks)
96
+ - `sourceDir` — where raw captured sources live (enables citation-target
97
+ checks, and exempts those pages from "author's voice" rules — they quote,
98
+ they don't cite)
57
99
 
58
- ## The method
100
+ Full reference: [schema/spec.md](schema/spec.md).
59
101
 
60
- Four phases — **Ingest, Synthesize, Evolve, Gate** — as an installable agent
61
- skill: [SKILL.md](SKILL.md). Why each rule exists:
62
- [docs/method.md](docs/method.md) (中文版: [method.zh.md](docs/method.zh.md)).
63
102
 
64
- ## Configuration
103
+ ### Already have an Obsidian / Logseq vault?
65
104
 
66
- Everything is optional. The two keys that matter:
105
+ It works on your vault today, as-is broken `[[wikilinks]]`, real TODOs, and
106
+ dangling index entries are findable without any setup. If the provenance
107
+ culture is not (yet) your vault's culture, switch off those rules and keep the
108
+ checks that are language-agnostic:
67
109
 
68
110
  ```json
69
111
  {
70
- "roots": ["notes", "sources"],
71
- "index": "index.md",
72
- "sourceDir": "sources"
112
+ "rules": {
113
+ "provenance.excess-inferred": "off",
114
+ "provenance.low-confidence": "off",
115
+ "citation.target-missing": "off",
116
+ "frontmatter.required": "warn",
117
+ "frontmatter.fields": "off"
118
+ }
73
119
  }
74
120
  ```
75
121
 
76
- Full reference in [schema/spec.md](schema/spec.md). Rule severities are
77
- `error | warn | off` per rule; exit codes are `0` clean, `1` errors,
78
- `2` usage/config.
122
+ That is the honest floor: **broken-link and placeholder detection help any
123
+ markdown vault**; the citation layer becomes valuable when you start capturing
124
+ sources (see [SKILL.md](SKILL.md) Phase: Ingest).
125
+
126
+ ### 4. Make your agent maintain the vault under the same rules
127
+
128
+ Install the method as an agent skill: copy
129
+ [SKILL.md](SKILL.md) into your agent's skills directory (Claude Code, Codex,
130
+ and most CLIs support project or global skills).
131
+
132
+ <p align="center">
133
+ <img src="assets/four-phases.svg" alt="The four-phase loop: Ingest → Synthesize → Evolve → Gate; findings loop back" width="100%">
134
+ </p>
135
+
136
+ It teaches your agent the four phases with the violation consequences that
137
+ motivated each rule. The short version of the whole method is one line:
138
+ **never write a claim your vault cannot trace.**
139
+
140
+ ### 5. Gate your CI (optional)
141
+
142
+ ```bash
143
+ npx trustwiki lint ./your-notes # exit 0 = clean, 1 = errors present
144
+ npx trustwiki lint ./your-notes --json # machine-readable for CI bots
145
+ ```
146
+
147
+ ## What each check catches
148
+
149
+ | check | catches | example |
150
+ |---|---|---|
151
+ | `citation.malformed` | citations that don't parse — decoration, not provenance | `^[maybe a source?]` |
152
+ | `citation.target-missing` | cited files that don't exist | `^[sources/ghost.md]` |
153
+ | `provenance.excess-inferred` | pages that are mostly uncited prose | 3/5 paragraphs, no `^[…]` |
154
+ | `provenance.low-confidence` | pages admitting they're shaky | `confidence: 0.3` |
155
+ | `provenance.contradicted` | conflicts marked in body but not frontmatter (or vice versa) | callout without `contradicted_by` |
156
+ | `link.broken` | `[[wikilinks]]` that resolve to nothing | `[[notes/dead-ref]]` |
157
+ | `link.index-missing` | pages missing from the index; index entries that dangle | either direction |
158
+ | `page.orphan` | pages with almost no outbound links — islands rot first | 0 links |
159
+ | `frontmatter.required` / `.fields` | missing metadata; sources missing `sha256` (how silent edits get caught) | no `created` date |
160
+ | `placeholder.present` | TODO/FIXME posing as finished content | `TODO: finish` |
161
+
162
+ Severity of each is configurable (`error | warn | off`), warnings never fail
163
+ the build, and the full rule reference lives in
164
+ [schema/spec.md](schema/spec.md).
165
+
166
+ ## Proven in production
167
+
168
+ <p align="center">
169
+ <img src="assets/precision-journey.svg" alt="Precision 49% to 93% after fixes; 8 foreign corpora, zero fabricated findings" width="100%">
170
+ </p>
171
+
172
+ This tool is the extracted discipline of an agent-maintained wiki that has
173
+ run since 2026-05: **8,658 pages, 4,162 raw sources, 0 lint errors** — every
174
+ number with source and verification date in [proof/STATS.md](proof/STATS.md).
175
+
176
+ And the method itself was A/B tested on an identical task — one agent with
177
+ SKILL.md, one without:
178
+
179
+ <p align="center">
180
+ <img src="assets/ab-experiment.svg" alt="Without the method: 3 lint errors, no links, no sources. With it: 0 errors, 11 links, cited and tamper-evident" width="100%">
181
+ </p>
182
+
183
+ Re-run the experiment yourself in 30 seconds:
184
+ [templates/eval-round3/](templates/eval-round3/) · full reports in [docs/eval/](docs/eval/).
185
+
186
+ ## FAQ
187
+
188
+ **Is this for Obsidian/Logseq users or for agent builders?**
189
+ Both. If you keep markdown notes, `npx trustwiki lint` finds rot today. If
190
+ you build agents, [SKILL.md](SKILL.md) makes your agent write vaults that
191
+ pass the same check.
192
+
193
+ **Does it modify my files?**
194
+ No. It reads and reports only. There is no `--fix` on purpose: fixes that
195
+ matter require knowing which source supports the claim, and no tool can
196
+ guess that for you.
79
197
 
80
- ## Stats
198
+ **Does it send my notes anywhere?**
199
+ No. Local files in, stdout out. Zero dependencies, zero telemetry.
81
200
 
82
- Numbers from the production vault, each with source and verification date:
83
- [proof/STATS.md](proof/STATS.md).
201
+ **My vault isn't agent-written is this still useful?**
202
+ Yes, if it has citations to check or links that can rot. The contradiction
203
+ and broken-link checks help any markdown knowledge base.
84
204
 
85
205
  ## License
86
206
 
package/README.zh.md CHANGED
@@ -1,81 +1,194 @@
1
+ <p align="center">
2
+ <img src="assets/hero.svg" alt="trustwiki — 你的 agent 可以维护的知识库,前提是它不对你撒谎" width="100%">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/QianJinGuo/trustwiki/actions/workflows/ci.yml"><img src="https://github.com/QianJinGuo/trustwiki/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
7
+ <a href="https://www.npmjs.com/package/trustwiki"><img src="https://img.shields.io/npm/v/trustwiki" alt="npm"></a>
8
+ <img src="https://img.shields.io/node/v/trustwiki" alt="node >= 18">
9
+ <img src="https://img.shields.io/badge/license-MIT-green" alt="MIT license">
10
+ </p>
11
+
12
+ [English](README.md) · 简体中文
13
+
1
14
  # trustwiki
2
15
 
3
- **你的 agent 可以维护的知识库——前提是它不对你撒谎。**
16
+ **你的 agent 在替你写笔记。谁在检查它们?**
4
17
 
5
- 每个论断有引用。矛盾自动现形。腐烂有仪器测。
18
+ 所有由 AI agent 维护的知识库,最终都以同一种方式腐烂:没人能溯源的论断、
19
+ 被悄悄改写抹平的矛盾、死掉的链接。trustwiki 是检查器——它读你的 markdown
20
+ 知识库,精确告诉你烂在哪里。
6
21
 
7
22
  ```bash
8
- npx trustwiki lint ./your-vault
23
+ npx trustwiki lint ./your-notes
24
+ ```
25
+
26
+ ```text
27
+ notes/model-comparison.md
28
+ L12 error citation.target-missing citation target not found: sources/ghost.md
29
+ L14 warn provenance.excess-inferred 3/5 prose paragraphs uncited (>0.3)
30
+ L16 warn placeholder.present placeholder text: TODO
31
+ L18 error link.broken broken wikilink [[notes/dead-ref]]
32
+
33
+ Σ 3 errors, 5 warnings across 3 files
9
34
  ```
10
35
 
11
- trustwiki 是一个溯源 linter,加一套面向 agent 维护知识库的操作方法。
12
- 它**不**替你长出 wiki。它保证的是:当你的 agent 动笔时,每个论断能
13
- 追溯到来源,分歧保持可见而不是被悄悄改写,腐烂被仪器测出来——
14
- 而不是两个月后被人撞见。
36
+ 这是一座"造出来就是为了失败"的知识库上的真实报告。[看它运行(10 GIF)](#2-看它运行) · [每条 finding 是什么意思](#每条检查抓什么)
37
+
38
+ ## 为什么有它
39
+
40
+ AI agent 已经在替人写知识库——研究 wiki、团队文档、个人笔记。没人校对,
41
+ 因为工作量不是人能承受的。失败模式叫**流畅的 slop**:自信的散文、零溯源、
42
+ 矛盾被最后编辑的人悄悄裁决。
43
+
44
+ trustwiki 是纪律层。它**不**替你长出 wiki——它保证的是:当你的 agent
45
+ 动笔时,每个论断能追溯到来源,分歧保持可见,腐烂被仪器测出来而不是
46
+ 两个月后被人撞见。
47
+
48
+ 它是三样东西:
15
49
 
16
- - **12 条机械检查**——引用文法、引用目标、未引用推断占比、置信度下限、矛盾一致性、断链、索引漂移、孤页、占位符
17
- - **一个配置文件**——`.trustwiki.json`;什么都不配置也照常能跑
18
- - **零依赖**——Node 18+,一条命令,JSON 输出可直接进 CI
19
- - **生产验证**——运行自 2026-05 的 agent 维护知识库的[活体统计](proof/STATS.md)
50
+ <p align="center">
51
+ <img src="assets/three-layers.svg" alt="三层:检查器(npx trustwiki lint)、规范(冻结引用文法)、方法(SKILL.md)" width="100%">
52
+ </p>
20
53
 
21
- ## 为什么
54
+ - **检查器**——12 条机械检查:引用、矛盾、断链、索引漂移、孤页
55
+ - **规范**——"可溯源"的冻结版本化定义:[schema/spec.zh.md](schema/spec.zh.md)
56
+ - **方法**——你的 agent 可安装的四阶段纪律:[SKILL.md](SKILL.md)
22
57
 
23
- agent 已经在替人写知识库了。没有信任层,它们产出的是流畅的 slop:
24
- 无出处的论断、被静默抹平的矛盾、烂掉的链接。trustwiki 是纪律层——
25
- linter、schema、和方法。
58
+ ## 上手
26
59
 
27
- ## 快速开始
60
+ ### 1. 在 demo 上试(30 秒,无需安装任何东西)
28
61
 
29
62
  ```bash
30
63
  git clone https://github.com/QianJinGuo/trustwiki && cd trustwiki
31
- npx trustwiki lint templates/demo-vault
64
+ npx trustwiki lint templates/demo-vault # 造出来为了失败的库:8 条 findings,退出码 1
65
+ npx trustwiki lint templates/starter-vault # 同样结构、造出来为了通过的库:退出码 0
32
66
  ```
33
67
 
34
- 你会得到这样一份报告(退出码 1——存在 error):
68
+ ### 2. 看它运行
35
69
 
36
- ```
37
- notes/sloppy-page.md
38
- L12 error citation.target-missing citation target not found: sources/ghost.md
39
- L14 warn provenance.excess-inferred 3/5 prose paragraphs uncited (>0.3)
40
- L16 warn placeholder.present placeholder text: TODO
41
- ...
42
- Σ 3 errors, 5 warnings across 3 files
70
+ ![trustwiki 检查预埋缺陷的 demo vault——8 条 findings](assets/demo.gif)
71
+
72
+ ### 3. 用在你自己的知识库上
73
+
74
+ 已经在用 markdown 记笔记(Obsidian、Logseq、纯文件)?直接指向目录,
75
+ 零配置可用:
76
+
77
+ ```bash
78
+ npx trustwiki lint ~/Documents/my-notes
43
79
  ```
44
80
 
45
- 8 条 findings 是预埋在 `templates/demo-vault` 里的一座"造出来就是为了
46
- 失败"的库。`templates/starter-vault` 是同样结构、造出来就是为了通过的库——
47
- 也是你自己建库的起点。
81
+ 然后多数人会加一个小小的 `.trustwiki.json`,让检查器理解你的目录布局:
48
82
 
49
- ## 规范
83
+ ```json
84
+ {
85
+ "roots": ["notes", "sources"],
86
+ "index": "index.md",
87
+ "sourceDir": "sources"
88
+ }
89
+ ```
50
90
 
51
- 引用文法(`^[path:42-58]`)、provenance frontmatter、矛盾标记,全部
52
- 有版本化的规范:[schema/spec.zh.md](schema/spec.zh.md)(English:
53
- [spec.md](schema/spec.md))。欢迎 lint 之外的实现。
91
+ - `roots`——扫描哪些目录
92
+ - `index`——你的索引文件(可选,声明后启用索引漂移检查)
93
+ - `sourceDir`——原始抓取来源所在目录(启用引用目标检查;这些页豁免
94
+ "作者声音"类规则——它们引用来源,不需要引用自己)
54
95
 
55
- ## 方法
96
+ 完整参考:[schema/spec.zh.md](schema/spec.zh.md)。
56
97
 
57
- 四个阶段——**Ingest、Synthesize、Evolve、Gate**——封装为可安装的
58
- agent skill:[SKILL.md](SKILL.md)。每条规则为什么存在:
59
- [docs/method.zh.md](docs/method.zh.md)(English: [method.md](docs/method.md))。
60
98
 
61
- ## 配置
99
+ ### 已经有一个 Obsidian / Logseq 库?
62
100
 
63
- 一切可选。最重要的两个键:
101
+ 今天就能直接用——断掉的 `[[wikilink]]`、真实的 TODO、悬空的索引项,零配置
102
+ 即可找出。如果溯源文化还不是你的库的文化,把那部分规则先关掉,保留与语言
103
+ 无关的检查:
64
104
 
65
105
  ```json
66
106
  {
67
- "roots": ["notes", "sources"],
68
- "index": "index.md",
69
- "sourceDir": "sources"
107
+ "rules": {
108
+ "provenance.excess-inferred": "off",
109
+ "provenance.low-confidence": "off",
110
+ "citation.target-missing": "off",
111
+ "frontmatter.required": "warn",
112
+ "frontmatter.fields": "off"
113
+ }
70
114
  }
71
115
  ```
72
116
 
73
- 完整参考见 [schema/spec.zh.md](schema/spec.zh.md)。规则级别为
74
- `error | warn | off`;退出码 `0` 干净、`1` 有 error、`2` 用法/配置错误。
117
+ 这是诚实的下限:**断链与占位符检查对任何 markdown 库都有用**;引用层在
118
+ 你开始捕获来源后才会显现价值(见 [SKILL.md](SKILL.md) Phase: Ingest)。
119
+
120
+ ### 4. 让你的 agent 按同一套规则维护
121
+
122
+ 把方法装成 agent skill:复制 [SKILL.md](SKILL.md) 到你 agent 的 skills
123
+ 目录(Claude Code、Codex 和多数 CLI 都支持项目级或全局 skill)。
124
+
125
+ <p align="center">
126
+ <img src="assets/four-phases.svg" alt="四阶段循环:Ingest → Synthesize → Evolve → Gate;findings 回流" width="100%">
127
+ </p>
128
+
129
+ 它教你的 agent 四个阶段,以及每条规则背后的违规后果。整个方法的一句话
130
+ 版本:**永远不要写下你的知识库无法溯源的论断。**
131
+
132
+ ### 5. 进 CI 门禁(可选)
133
+
134
+ ```bash
135
+ npx trustwiki lint ./your-notes # 退出码 0 = 干净,1 = 有 error
136
+ npx trustwiki lint ./your-notes --json # 机读输出,给 CI bot
137
+ ```
138
+
139
+ ## 每条检查抓什么
140
+
141
+ | 检查 | 抓什么 | 例子 |
142
+ |---|---|---|
143
+ | `citation.malformed` | 解析不了的引用——是装饰不是溯源 | `^[maybe a source?]` |
144
+ | `citation.target-missing` | 被引用的文件不存在 | `^[sources/ghost.md]` |
145
+ | `provenance.excess-inferred` | 大部分段落无引用的页面 | 3/5 段落没有 `^[…]` |
146
+ | `provenance.low-confidence` | 自己承认心虚的页面 | `confidence: 0.3` |
147
+ | `provenance.contradicted` | 冲突只在正文标记、或只在 frontmatter 标记 | 有 callout 无 `contradicted_by` |
148
+ | `link.broken` | 解析不到任何东西的 `[[wikilink]]` | `[[notes/dead-ref]]` |
149
+ | `link.index-missing` | 页面不在索引里;或索引项悬空 | 双向都查 |
150
+ | `page.orphan` | 几乎没有出链的页面——孤岛先腐烂 | 0 条出链 |
151
+ | `frontmatter.required` / `.fields` | 缺元数据;来源页缺 `sha256`(静默篡改的发现手段) | 没有 `created` |
152
+ | `placeholder.present` | 冒充完成品的 TODO/FIXME | `TODO: finish` |
153
+
154
+ 每条的严重度可配置(`error | warn | off`),warning 永远不导致失败,
155
+ 完整规则参考在 [schema/spec.zh.md](schema/spec.zh.md)。
156
+
157
+ ## 生产验证
158
+
159
+ <p align="center">
160
+ <img src="assets/precision-journey.svg" alt="精确率 49%→93%;8 个外部语料零捏造" width="100%">
161
+ </p>
162
+
163
+ 本工具是一套从真实运行中的 agent 维护知识库抽出的纪律——该库自 2026-05
164
+ 运行至今:**8,658 页、4,162 个原始来源、0 lint errors**——每个数字带来源
165
+ 和核实日期,见 [proof/STATS.md](proof/STATS.md)。
166
+
167
+ 方法本身也做了同任务 A/B 对照——一个 agent 带 SKILL.md,一个不带:
168
+
169
+ <p align="center">
170
+ <img src="assets/ab-experiment.svg" alt="无方法:3 个 lint error、无链接、无来源。有方法:0 error、11 链接、可溯源防篡改" width="100%">
171
+ </p>
172
+
173
+ 30 秒复跑这个实验:[templates/eval-round3/](templates/eval-round3/) · 完整报告在 [docs/eval/](docs/eval/)。
174
+
175
+ ## FAQ
176
+
177
+ **是给 Obsidian/Logseq 用户还是给 agent 开发者?**
178
+ 都是。只要你用 markdown 记笔记,`npx trustwiki lint` 今天就能找出腐烂。
179
+ 如果你开发 agent,[SKILL.md](SKILL.md) 让你的 agent 写出能通过同一检查
180
+ 的知识库。
181
+
182
+ **它会修改我的文件吗?**
183
+ 不会。只读、只报告。故意不做 `--fix`:真正要紧的修复需要知道哪个来源
184
+ 支撑哪个论断,没有工具能替你猜。
75
185
 
76
- ## 活体统计
186
+ **它会把我的笔记发到任何地方吗?**
187
+ 不会。本地文件进,stdout 出。零依赖,零遥测。
77
188
 
78
- 来自生产库的数字,每个都带来源和核实日期:[proof/STATS.md](proof/STATS.md)。
189
+ **我的库不是 agent 写的——还有用吗?**
190
+ 有用,只要它有可检查的引用或会烂的链接。矛盾和断链检查对任何 markdown
191
+ 知识库都有效。
79
192
 
80
193
  ## 许可
81
194
 
package/cli/bin.js CHANGED
@@ -16,14 +16,20 @@ let json = false, configFlag, bad;
16
16
  for (let i = 2; i < args.length; i++) {
17
17
  if (args[i] === '--json') json = true;
18
18
  else if (args[i] === '--config') {
19
- if (i + 1 >= args.length) { bad = '--config requires a file argument'; break; }
19
+ if (i + 1 >= args.length || args[i + 1].startsWith('--')) { bad = '--config requires a file argument'; break; }
20
20
  configFlag = args[++i];
21
21
  } else { bad = `unknown argument: ${args[i]}`; break; }
22
22
  }
23
23
  if (bad) { console.error(`trustwiki: ${bad}`); usage(); process.exit(2); }
24
24
  const { config, error } = await loadConfig(vault, configFlag);
25
25
  if (error) { console.error(`trustwiki: ${error.message}`); process.exit(2); }
26
- const findings = await lintVault(config.vaultPath, config);
26
+ let findings;
27
+ try {
28
+ findings = await lintVault(config.vaultPath, config);
29
+ } catch (e) {
30
+ console.error(`trustwiki: failed to lint ${config.vaultPath}: ${e.message}`);
31
+ process.exit(2);
32
+ }
27
33
  console.log(json ? formatJson(findings) : formatText(findings));
28
34
  // exitCode (not process.exit) so large stdout writes flush before teardown
29
35
  process.exitCode = findings.some(f => f.severity === 'error') ? 1 : 0;
package/cli/citations.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const CITATION_RE = /\^\[([^\]\n]*)\]/g;
2
- const RANGE_ANCHOR = /^(.+)#L(\d+)-L(\d+)$/;
3
- const RANGE_COLON = /^(.+?):(\d+)-(\d+)$/;
2
+ const RANGE_ANCHOR = /^([^:\s]+)#L(\d+)-L(\d+)$/;
3
+ const RANGE_COLON = /^([^:\s]+):(\d+)-(\d+)$/;
4
4
 
5
5
  export function parseCitation(raw) {
6
6
  const trimmed = raw.trim();
@@ -24,7 +24,7 @@ function parseSource(part) {
24
24
 
25
25
  function finish(path, start, end, part) {
26
26
  path = path.trim();
27
- if (!path || /\s/.test(path)) return { ok: false, reason: `bad source syntax: "${part}"` };
27
+ if (!path || /[\s:]/.test(path)) return { ok: false, reason: `bad source syntax: "${part}" (path cannot contain spaces or colons)` };
28
28
  if (start !== null && (start < 1 || end < 1)) return { ok: false, reason: `line numbers must be positive in "${part}"` };
29
29
  if (start !== null && start > end) return { ok: false, reason: `reversed line range in "${part}" (start > end)` };
30
30
  return { ok: true, source: { path, start, end } };
package/cli/config.js CHANGED
@@ -6,6 +6,7 @@ export const RULE_IDS = [
6
6
  'link.broken', 'link.index-missing', 'link.type-mismatch', 'page.orphan',
7
7
  'citation.malformed', 'citation.target-missing',
8
8
  'provenance.excess-inferred', 'provenance.low-confidence', 'provenance.contradicted',
9
+ 'config.index-unreadable',
9
10
  ];
10
11
 
11
12
  const DEFAULT_SEVERITY = {
@@ -13,6 +14,7 @@ const DEFAULT_SEVERITY = {
13
14
  'link.broken': 'error', 'link.index-missing': 'warn', 'link.type-mismatch': 'warn', 'page.orphan': 'warn',
14
15
  'citation.malformed': 'error', 'citation.target-missing': 'error',
15
16
  'provenance.excess-inferred': 'warn', 'provenance.low-confidence': 'warn', 'provenance.contradicted': 'warn',
17
+ 'config.index-unreadable': 'warn',
16
18
  };
17
19
 
18
20
  export const DEFAULT_CONFIG = {
@@ -36,6 +38,8 @@ export async function loadConfig(vaultPath, explicitPath) {
36
38
  user = JSON.parse(await readFile(configPath, 'utf8'));
37
39
  } catch (e) {
38
40
  if (e.code !== 'ENOENT') return { error: { message: `invalid ${configPath}: ${e.message}` } };
41
+ // an explicitly passed config file must exist — silent fallback would hide typos
42
+ if (explicitPath) return { error: { message: `config file not found: ${explicitPath}` } };
39
43
  }
40
44
  const rules = { ...DEFAULT_CONFIG.rules, ...(user.rules || {}) };
41
45
  const unknown = Object.keys(rules).filter(id => !RULE_IDS.includes(id));
package/cli/engine.js CHANGED
@@ -4,20 +4,34 @@ import { walkVault } from './walk.js';
4
4
  import { parseFrontmatter } from './frontmatter.js';
5
5
  import { extractWikilinks } from './links.js';
6
6
  import { findCitations } from './citations.js';
7
+ import { normalizeTarget } from './resolve.js';
7
8
  import { RULES } from './rules/index.js';
8
9
 
9
- export function normalizeTarget(t) { return t.trim().replace(/\.md$/, ''); }
10
+ export { normalizeTarget };
10
11
 
11
12
  // Mask fenced code blocks and inline code spans so their contents are never
12
13
  // treated as live citations/wikilinks. Character- and line-preserving: every
13
- // line number in the masked text matches the original file.
14
+ // line number in the masked text matches the original file. Fence state tracks
15
+ // the delimiter type — a ``` line inside a ~~~ fence is content, not a closer.
14
16
  export function maskCode(body) {
15
17
  const lines = body.split('\n');
16
- let inFence = false;
18
+ const fenceRe = /^\s*(`{3,}|~{3,})/;
19
+ let fenceMark = null;
17
20
  const masked = lines.map(line => {
18
- if (/^\s*(```|~~~)/.test(line)) { inFence = !inFence; return ''; }
19
- if (inFence) return '';
20
- return line.replace(/`[^`\n]*`/g, m => '`' + ' '.repeat(Math.max(0, m.length - 2)) + '`');
21
+ const m = line.match(fenceRe);
22
+ if (m) {
23
+ const mark = m[1][0];
24
+ if (!fenceMark) { fenceMark = mark; return ''; }
25
+ if (mark === fenceMark) { fenceMark = null; }
26
+ return '';
27
+ }
28
+ if (fenceMark) return '';
29
+ // mask inline HTML tag content (<tt>, <code>, <kbd>…) — regex literals like
30
+ // ^[a-zA-Z]*$ live there in real-world docs (k8s CEL reference)
31
+ return line
32
+ .replace(/<([a-z][a-z0-9]*)\b[^>]*>([\s\S]*?)<\/\1>/gi,
33
+ (_, tag, inner) => `<${tag}>${' '.repeat(inner.length)}</${tag}>`)
34
+ .replace(/`[^`\n]*`/g, x => '`' + ' '.repeat(Math.max(0, x.length - 2)) + '`');
21
35
  });
22
36
  return masked.join('\n');
23
37
  }
@@ -48,13 +62,15 @@ export async function lintVault(vaultPath, config) {
48
62
  if (config.index) {
49
63
  try {
50
64
  const raw = await readFile(join(vaultPath, config.index), 'utf8');
65
+ const indexMasked = maskCode(raw);
51
66
  model.indexRaw = raw;
52
67
  model.indexEntries = new Set();
53
68
  model.indexLines = new Map();
54
- const indexLines = raw.split('\n');
69
+ const indexLines = indexMasked.split('\n');
55
70
  for (let i = 0; i < indexLines.length; i++) {
56
71
  for (const m of indexLines[i].matchAll(/\[\[([^\]|\n]+)/g)) {
57
- const norm = normalizeTarget(m[1]);
72
+ const norm = normalizeTarget(m[1].split('#')[0]);
73
+ if (!norm) continue;
58
74
  model.indexEntries.add(norm);
59
75
  if (!model.indexLines.has(norm)) model.indexLines.set(norm, i + 1);
60
76
  }
@@ -71,7 +87,7 @@ export async function lintVault(vaultPath, config) {
71
87
  const bodyStartLine = fm.bodyStartLine;
72
88
  const body = maskCode(fm.body);
73
89
  model.files.push({
74
- relPath: rel, text, fm, body: fm.body, bodyStartLine,
90
+ relPath: rel, text, fm, body, bodyStartLine, // body is masked: rules never see code-fence content
75
91
  links: extractWikilinks(body, bodyStartLine),
76
92
  ...findCitations(body, bodyStartLine),
77
93
  paragraphs: proseParagraphs(body, bodyStartLine),
@@ -91,7 +107,8 @@ export async function lintVault(vaultPath, config) {
91
107
  }
92
108
  }
93
109
  if (indexUnreadable) {
94
- findings.push({ severity: 'warn', rule: 'config.index-unreadable', file: config.index, line: 1,
110
+ findings.push({ severity: config.rules['config.index-unreadable'] || 'warn',
111
+ rule: 'config.index-unreadable', file: config.index, line: 1,
95
112
  message: 'configured index could not be read; index rules disabled',
96
113
  hint: 'check the path or remove the index key from .trustwiki.json' });
97
114
  }
package/cli/links.js CHANGED
@@ -1,4 +1,4 @@
1
- const WIKILINK_RE = /\[\[([^\]|\n]+)(?:\|([^\]\n]*))?\]\]/g;
1
+ const WIKILINK_RE = /\[\[([^\]|\n]+)(?:\|([^\]\n]*))?\]\](?!\s*\()/g;
2
2
 
3
3
  export function extractWikilinks(body, bodyStartLine) {
4
4
  const out = [];
package/cli/report.js CHANGED
@@ -1,4 +1,6 @@
1
- const CONTROL = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g;
1
+ // strip ALL control chars including CR/LF and C1 — a hostile vault must not be
2
+ // able to forge report lines or overwrite terminal rows via file names
3
+ const CONTROL = /[\x00-\x1F\x7F-\x9F]/g;
2
4
  const clean = s => String(s).replace(CONTROL, '');
3
5
 
4
6
  export function formatText(findings) {
package/cli/resolve.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, statSync } from 'node:fs';
1
+ import { existsSync, statSync, realpathSync } from 'node:fs';
2
2
  import { join, resolve, sep } from 'node:path';
3
3
 
4
4
  export function normalizeTarget(t) { return t.trim().replace(/\.md$/, ''); }
@@ -13,13 +13,20 @@ export function resolveTarget(t, cfg) {
13
13
  return cands;
14
14
  }
15
15
 
16
- // Contained, regular-file existence check. Rejects `../` escapes, symlink
17
- // escapes, and directory hits a citation/probe must never leave the vault.
16
+ // Contained, regular-file existence check. Realpath-based: an in-vault symlink
17
+ // pointing outside the vault resolves outside and is rejected. Rejects `../`
18
+ // escapes and directory hits — a citation/probe must never leave the vault.
18
19
  export function resolveInVault(vaultPath, cand) {
19
- const vaultRoot = resolve(vaultPath);
20
- const abs = resolve(vaultRoot, cand);
21
- if (abs !== vaultRoot && !abs.startsWith(vaultRoot + sep)) return null;
20
+ const abs = resolve(vaultPath, cand);
22
21
  if (!existsSync(abs)) return null;
23
- try { if (!statSync(abs).isFile()) return null; } catch { return null; }
24
- return abs;
22
+ let rp, rootReal;
23
+ try {
24
+ rp = realpathSync(abs);
25
+ rootReal = realpathSync(vaultPath);
26
+ } catch {
27
+ return null;
28
+ }
29
+ if (rp !== rootReal && !rp.startsWith(rootReal + sep)) return null;
30
+ try { if (!statSync(rp).isFile()) return null; } catch { return null; }
31
+ return rp;
25
32
  }
@@ -1,9 +1,15 @@
1
1
  const BASE = ['title', 'created', 'updated', 'type', 'tags'];
2
2
  const SOURCE = ['source_url', 'ingested', 'sha256'];
3
- const DATE_RE = /^\d{4}-\d{2}-\d{2}/;
3
+ const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
4
4
  const SHA_RE = /^[0-9a-f]{64}$/i;
5
5
  const STATES = new Set(['extracted', 'merged', 'inferred', 'ambiguous']);
6
6
 
7
+ function validDate(v) {
8
+ if (!DATE_RE.test(v)) return false;
9
+ const d = new Date(v + 'T00:00:00Z'); // calendar check, timezone-stable
10
+ return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === v;
11
+ }
12
+
7
13
  export const rule = {
8
14
  id: 'frontmatter.fields',
9
15
  run(model) {
@@ -18,7 +24,10 @@ export const rule = {
18
24
  hint: 'see schema/spec.md — Frontmatter' });
19
25
  const bad = [];
20
26
  for (const k of ['created', 'updated']) {
21
- if (f.fm.fields[k] && !DATE_RE.test(f.fm.fields[k])) bad.push(`${k} is not an ISO date`);
27
+ if (f.fm.fields[k] && !validDate(f.fm.fields[k])) bad.push(`${k} is not a valid ISO date`);
28
+ }
29
+ if (isSource && f.fm.fields.ingested && !validDate(f.fm.fields.ingested)) {
30
+ bad.push('ingested is not a valid ISO date');
22
31
  }
23
32
  if (f.fm.fields.provenance_state && !STATES.has(f.fm.fields.provenance_state)) {
24
33
  bad.push(`provenance_state "${f.fm.fields.provenance_state}" is not one of extracted|merged|inferred|ambiguous`);
@@ -2,8 +2,13 @@ export const rule = {
2
2
  id: 'page.orphan',
3
3
  run(model) {
4
4
  const out = [];
5
+ const srcDir = model.config.sourceDir;
5
6
  for (const f of model.files) {
6
7
  if (!f.fm.ok) continue;
8
+ // raw/source pages quote their source — outbound links are not their job
9
+ const isSource = (model.config.inferredSkipTypes || []).includes(f.fm.fields.type)
10
+ || (srcDir && f.relPath.startsWith(`${srcDir}/`));
11
+ if (isSource) continue;
7
12
  const n = f.links.length;
8
13
  if (n < model.config.minOutboundLinks) out.push({ file: f.relPath, line: 1,
9
14
  message: `only ${n} outbound link(s) — orphaned page`,
@@ -1,4 +1,7 @@
1
- const RE = /\b(TODO|TBD|FIXME|lorem ipsum)\b/i;
1
+ // markers are case-sensitive conventions (TODO/FIXME/TBD) or colon-anchored;
2
+ // bare lowercase "todo" in prose (Spanish "todo", domain terms like
3
+ // "to-do list entity") is vocabulary, not a marker — see eval round 2b.
4
+ const RE = null; // matching is inline in run() — kept for interface stability
2
5
 
3
6
  export const rule = {
4
7
  id: 'placeholder.present',
@@ -6,11 +9,23 @@ export const rule = {
6
9
  const out = [];
7
10
  for (const f of model.files) {
8
11
  const head = f.body.split('\n').slice(0, 20).join('\n');
9
- const m = head.match(RE);
10
- if (m) out.push({ file: f.relPath,
11
- line: f.bodyStartLine + head.slice(0, m.index).split('\n').length - 1,
12
- message: `placeholder text: ${m[0]}`,
13
- hint: 'unfinished content erodes trust finish or remove' });
12
+ for (const m of head.matchAll(/\b(TODO|FIXME|lorem ipsum|TBD)\b|\btodo\b/g)) {
13
+ const word = m[0];
14
+ const lower = word.toLowerCase();
15
+ const before = head.slice(Math.max(0, m.index - 2), m.index);
16
+ const after = head.slice(m.index + m[0].length, m.index + m[0].length + 2);
17
+ // markers: "TODO"/"FIXME" in caps, or any case followed by ":" —
18
+ // bare lowercase "todo" as a plain word (Spanish "todo", domain terms)
19
+ // is vocabulary, not a marker
20
+ const isMarker = word === 'TODO' || word === 'FIXME' || word === 'TBD'
21
+ || /[::]\s*$/.test(after) || /^lorem/i.test(word);
22
+ if (!isMarker) continue;
23
+ out.push({ file: f.relPath,
24
+ line: f.bodyStartLine + head.slice(0, m.index).split('\n').length - 1,
25
+ message: `placeholder text: ${m[0]}`,
26
+ hint: 'unfinished content erodes trust — finish or remove' });
27
+ break; // one finding per file is enough to surface the pattern
28
+ }
14
29
  }
15
30
  return out;
16
31
  },
@@ -9,7 +9,7 @@ export const rule = {
9
9
  const m = f.body.match(CALLOUT);
10
10
  const calloutTargets = m ? targetsIn(m[1]) : [];
11
11
  const fmList = (f.fm?.fields?.contradicted_by || '').replace(/[\[\]]/g, '');
12
- const fmTargets = fmList ? fmList.split(',').map(s => s.trim()).filter(Boolean).map(t => t.replace(/\.md$/, '')) : [];
12
+ const fmTargets = fmList ? fmList.split(/[\s,]+/).map(s => s.trim()).filter(Boolean).map(t => t.replace(/\.md$/, '')) : [];
13
13
  if (m && !fmTargets.length) out.push({ file: f.relPath,
14
14
  line: f.bodyStartLine + f.body.slice(0, m.index).split('\n').length - 1,
15
15
  message: 'contradiction callout without contradicted_by in frontmatter',
@@ -4,7 +4,10 @@ export const rule = {
4
4
  const out = [];
5
5
  for (const f of model.files) {
6
6
  if (!f.fm.ok) continue;
7
- if (model.config.inferredSkipTypes.includes(f.fm.fields.type)) continue;
7
+ // exemption is location-based AND label-based: raw pages often lack the
8
+ // `source` type label, but living under sourceDir is dispositive
9
+ const underSourceDir = model.config.sourceDir && f.relPath.startsWith(`${model.config.sourceDir}/`);
10
+ if (model.config.inferredSkipTypes.includes(f.fm.fields.type) || underSourceDir) continue;
8
11
  const prose = f.paragraphs.filter(p => p.isProse && p.text);
9
12
  if (!prose.length) continue;
10
13
  // a paragraph counts as cited only when a citation sits on its final line
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trustwiki",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.1",
4
4
  "description": "Provenance linter and method for agent-maintained knowledge bases — every claim cited, contradictions surfaced, rot detected.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,8 +24,11 @@
24
24
  "proof"
25
25
  ],
26
26
  "license": "MIT",
27
+ "publishConfig": {
28
+ "registry": "https://registry.npmjs.org/"
29
+ },
27
30
  "repository": {
28
31
  "type": "git",
29
32
  "url": "git+https://github.com/QianJinGuo/trustwiki.git"
30
33
  }
31
- }
34
+ }
@@ -10,4 +10,4 @@ tags: [demo]
10
10
 
11
11
  Tea should be brewed at 100°C.^[sources/tea.md]
12
12
 
13
- > [!contradiction] 参见 [[notes/conflict-b]] 持相反观点
13
+ > [!contradiction] see [[notes/conflict-b]] which holds the opposite view
@@ -0,0 +1,11 @@
1
+ # Agent 记忆方案对比
2
+
3
+ Agent 的跨会话记忆是目前 AI 工程的热点问题。没有记忆的 agent 每次对话都从零开始,无法积累对用户偏好和项目上下文的理解。
4
+
5
+ 目前主流的记忆方案分为两类:
6
+
7
+ **文件式记忆**:把记忆存在 markdown 或文本文件中(如 CLAUDE.md、memory 目录)。优点是简单直接、可版本控制、人类可以直接查看和编辑。缺点是规模受限,当记忆量大了以后检索效率下降。
8
+
9
+ **向量库记忆**:把记忆切分成 chunk 后存入向量数据库,通过语义相似度检索。优点是可扩展到大规模记忆、检索语义相关内容更准。缺点是引入了额外的基础设施、调试困难、机器可读但人类不友好。
10
+
11
+ 对于大多数团队,建议从文件式记忆起步,等规模需求出现后再引入向量检索。
@@ -0,0 +1,12 @@
1
+ # MCP 是什么
2
+
3
+ Model Context Protocol(MCP)是 AI 应用连接外部世界的通用标准。它就像 AI 应用的 USB-C 接口:过去每个 AI 应用要为每个数据源单独写集成,现在只需要接入 MCP,就能统一连接各种工具和数据。
4
+
5
+ MCP 由 Anthropic 于 2024 年 11 月发布并开源。发布后迅速获得社区响应,成为 AI 工具生态中最受关注的协议之一。2025 年,OpenAI 和 Google DeepMind 相继宣布支持 MCP,这标志着 MCP 从单一公司的方案转变成了行业事实标准。
6
+
7
+ MCP 的核心架构包含三种原语:
8
+ - **Tools**:让 AI 可以调用外部函数,比如查询数据库、发送消息
9
+ - **Resources**:让 AI 可以读取文件和数据
10
+ - **Prompts**:预定义的提示模板
11
+
12
+ MCP 的意义在于把 AI 应用从"信息孤岛"变成了"生态节点"。开发者不再需要为每个 AI 应用重复开发集成,工具开发者也只需要写一次 MCP server 就能服务所有支持的 AI 应用。
@@ -0,0 +1,12 @@
1
+ # 有记忆的 MCP Agent:两者如何组合
2
+
3
+ MCP 解决了 agent 与外部工具的连接问题,记忆解决了 agent 跨会话的经验积累问题。两者结合,才能构成一个真正能长期服务的 agent。
4
+
5
+ 一个典型的组合架构:
6
+ - 用 MCP 连接工具生态(数据库、API、文件系统)
7
+ - 用文件式记忆保存用户的偏好、项目上下文、历史决策
8
+ - 每次会话开始时,agent 先读取记忆文件恢复上下文,再通过 MCP 调用所需工具
9
+
10
+ 这种组合的优势在于关注点分离:MCP 管"能力",记忆管"经验"。两者独立演进互不干扰。
11
+
12
+ 实践中需要注意的问题:记忆文件本身也会腐烂(过时信息、重复条目),需要定期整理。这是目前业界的普遍痛点。
@@ -0,0 +1,5 @@
1
+ {
2
+ "roots": ["notes", "sources"],
3
+ "index": "index.md",
4
+ "sourceDir": "sources"
5
+ }
@@ -0,0 +1,7 @@
1
+ # Team KB
2
+
3
+ - [[notes/mcp-explained]] — MCP 是什么
4
+ - [[notes/agent-memory-options]] — agent 记忆方案对比
5
+ - [[notes/memory-mcp-combo]] — 有记忆的 MCP agent
6
+ - [[sources/mcp-overview]] — 来源 1
7
+ - [[sources/agent-memory-landscape]] — 来源 2
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Agent 记忆方案对比
3
+ created: 2026-09-05
4
+ updated: 2026-09-05
5
+ type: note
6
+ tags: [memory, agents]
7
+ ---
8
+
9
+ Agent 跨会话记忆的两条主流路线:**文件式**(CLAUDE.md、memory 目录)与**向量库检索**。^[sources/agent-memory-landscape.md]
10
+
11
+ 文件式的核心优势是三重可审计:可版本控制(git 追溯每次变更)、人类可读(直接打开检查)、agent 友好(无需额外检索层)。代价是规模上限——记忆量大后检索效率下降。^[sources/agent-memory-landscape.md]
12
+
13
+ 向量库路线换取规模与语义召回,但引入基础设施与调试成本。值得注意 2026 年的趋势判断是**混合路线**:文件做索引层、向量做召回层。^[sources/agent-memory-landscape.md]
14
+
15
+ 决策启发:这不是二选一,而是先后问题。相关:[[mcp-explained]]、[[memory-mcp-combo]]。
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: MCP 是什么
3
+ created: 2026-09-05
4
+ updated: 2026-09-05
5
+ type: note
6
+ tags: [mcp, protocol]
7
+ ---
8
+
9
+ MCP(Model Context Protocol)是 Anthropic 于 2024 年 11 月发布的开放协议,用 USB-C 类比其定位:统一 AI 应用与数据源之间的连接方式。^[sources/mcp-overview.md]
10
+
11
+ 2025 年 OpenAI 与 Google DeepMind 相继采纳后,MCP 从单公司方案转变为行业事实标准——这是判断它能否长期投入的关键信号。^[sources/mcp-overview.md]
12
+
13
+ 核心原语有三个:**tools**(可调用的外部函数)、**resources**(可读取的数据)、**prompts**(预定义模板)。理解三者的分工是设计 MCP 集成的基础。^[sources/mcp-overview.md]
14
+
15
+ 实践含义:团队选连接方案时,MCP 的事实标准地位意味着集成成本方向变了——过去是"为每个 AI 应用写一次集成",现在收敛为"写一次 MCP server"。相关:[[agent-memory-options]]、[[memory-mcp-combo]]。
@@ -0,0 +1,13 @@
1
+ ---
2
+ title: 有记忆的 MCP Agent
3
+ created: 2026-09-05
4
+ updated: 2026-09-05
5
+ type: note
6
+ tags: [mcp, memory, architecture]
7
+ ---
8
+
9
+ MCP 与记忆解决的是两个正交问题:MCP 管能力连接,记忆管经验积累。组合架构因此是关注点分离的,不是堆叠的。^[sources/mcp-overview.md] ^[sources/agent-memory-landscape.md]
10
+
11
+ 按来源给出的趋势判断,2026 年的组合形态是:文件式记忆做索引(保存偏好、项目上下文、历史决策),向量召回做扩展层,MCP 统一工具面。^[sources/agent-memory-landscape.md]
12
+
13
+ 一条来源没有展开、但工程师应该警惕的边界:记忆文件自身也会腐烂(过时条目、重复)。本知识库用 trustwiki 纪律对抗这个问题——这正是 Gate 阶段存在的理由(本句为工程推断,非来源内容)。^[sources/agent-memory-landscape.md] 相关:[[mcp-explained]]、[[agent-memory-options]]。
@@ -0,0 +1,12 @@
1
+ ---
2
+ title: Agent memory landscape (task source 2)
3
+ created: 2026-09-05
4
+ updated: 2026-09-05
5
+ type: source
6
+ tags: [memory, source]
7
+ source_url: internal-task-brief
8
+ ingested: 2026-09-05
9
+ sha256: 8e735b918c7874b2e5a46580f06abfeffac2adaaffd265c0b85bc3d9004ae2d6
10
+ ---
11
+
12
+ Agent 跨会话记忆:文件式(CLAUDE.md/memory 目录)vs 向量库检索。文件式可版本控制、人类可读、agent 友好;向量库适合大规模但引入基础设施。混合路线(文件索引+向量召回)为 2026 趋势判断。
@@ -0,0 +1,12 @@
1
+ ---
2
+ title: MCP Overview (task source 1)
3
+ created: 2026-09-05
4
+ updated: 2026-09-05
5
+ type: source
6
+ tags: [mcp, source]
7
+ source_url: internal-task-brief
8
+ ingested: 2026-09-05
9
+ sha256: 122a3aa6112694b91828b4a284e3badeb995fd797f6fdbce07f27c1d787e89d2
10
+ ---
11
+
12
+ MCP: Anthropic 2024-11 发布的开放协议,USB-C 类比统一 AI 应用与数据源连接。2025 年 OpenAI、Google DeepMind 采纳,事实标准初步形成。核心原语:tools、resources、prompts。
@@ -0,0 +1,8 @@
1
+ 任务:为团队知识库创建三个主题页(notes/ 目录下),基于以下两份来源材料:
2
+ 1. 来源一(关于 MCP):Model Context Protocol 是 Anthropic 2024 年 11 月发布的开放协议,
3
+ 用 USB-C 类比统一 AI 应用与数据源之间的连接。2025 年被 OpenAI、Google DeepMind 采纳,
4
+ 事实标准初步形成。核心原语:tools、resources、prompts。
5
+ 2. 来源二(关于 agent 记忆):agent 跨会话记忆的主流方案是文件式记忆(如 CLAUDE.md、
6
+ memory 目录)与向量库检索两类。文件式可版本控制、人类可读、对 agent 友好;
7
+ 向量库适合大规模但引入基础设施。混合路线(文件为索引+向量做召回)是 2026 年的趋势判断。
8
+ 要求:三页建议——「MCP 是什么」「agent 记忆方案对比」「两者如何组合:有记忆的 MCP agent」。