feat(wiki): AI 内容巡检、单页检查与闭环验证 - #38
Draft
lyingbug wants to merge 6 commits into
Draft
Conversation
The lint lifecycle only had deterministic rules, so nothing detected defects in what a page actually says, and every scan was whole-wiki. Runs now declare a mode (static rules, AI review, or both) and a scope (the whole wiki, or named pages), which makes "check this page" a first-class operation on the same durable run machinery as a full scan. The AI review is deliberately the cheapest thing that can still find a semantic defect: one small tool-free call per page, over a truncated body, only on pages whose content changed since their last review, under a per-run page budget that is also the call budget. Findings must quote the page verbatim, which both filters hallucinations and gives each finding a stable fingerprint. That quote is what closes the repair loop. Verifying an AI finding first checks whether the quoted span survived the edit — free, deterministic, and true in the common case. Only an edit that changed the page somewhere else spends a single recheck call, and reconciliation is now scoped per source and per page so neither detector family can retire the other's findings. Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
A per-page reviewer is structurally blind to most of what is wrong with a generated wiki. Nothing about either of two duplicate pages is wrong on its own; a summary that omits half its source looks fine until you read the source; a wrong fact is only wrong relative to the document it came from. So the review is now a registry of detectors, each declaring the unit it judges, and each with the same two-stage shape: cheap database-only candidate generation that proposes units worth looking at, then at most one bounded model call per unit. Three detectors ship: page-content (mixed subjects, contradictions, outdated statements, unsupported claims), source-grounding (facts the source contradicts, subjects the source covers and the page omits), and duplicate-pages (pairs that should be merged, found via the title trigram index and shared source documents rather than by comparing every page with every other). A run's call budget is shared across detectors by weight, with a guaranteed call each and unused share released to the detectors after it — so adding a detector changes what a run looks at, not what it costs. The ledger is keyed by (detector, unit) so an unchanged unit is answered from the database. Reconciliation follows the same distinction: a finding anchored to a quoted span may be closed by absence over the pages its detector read, while a finding about a pair or a page-and-source may only be closed by the exact unit that owns it. Verification is deterministic first — rewritten quote, grown coverage, merged or linked pair — and only spends a recheck call when that cannot settle it. Duplicate findings were also unrepairable, so wiki_merge_pages gives the fixer agent a real merge: the survivor absorbs the other page's aliases, source documents and citations, which is what stops the next ingest from recreating the duplicate. Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
The problem centre had one "run rule scan" button, three chip rows competing for the top of the drawer, and a monolithic card list inlined in an 8500-line component. It also had no way to express what the backend can now do. The drawer now leads with the choice that costs money: three labelled scan modes with their cost and what each can find, so starting a scan is never an accidental model-spend decision, and after a run it reports what was actually spent and which detectors covered it. Status became a tab strip, since that is the axis a user changes while triaging, and type/source moved behind a filter toggle. The issue card, the scan panel and the page-check control are now components under wiki/health, with the label vocabulary in one module instead of duplicated between the drawer and the page popup. Cards show what a reader needs to judge a finding: the quoted span for a claim about text, both pages for a duplicate, and the source document with its citation coverage for a grounding finding. Each page also gets its own check control in the reader, so re-examining the page in front of you no longer means scanning the whole wiki. Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
…ecture Explains what each review detector can and cannot see, why the unit of judgement decides that, where the cost of a run is bounded, and what makes a repair verifiable — plus the steps to add a new class of defect. Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
A recheck asked the detector for one candidate and reviewed whatever came back. For a finding identified by its unit that is the wrong question: re-checking the pair (A, C) and finding it clean says nothing about a finding on (A, B), and the fingerprint comparison would then read as "no longer present" and resolve the issue on evidence that never concerned it. The recheck now locates the issue's own unit by fingerprint and fails when it cannot, which falls back to requiring real page progress. Quote-anchored findings are unaffected: for those the page is the unit. Also covers the candidate query and the ledger with repository tests, since the ordering, the per-detector keying, and the page-type and source-document filters are what every detector's cost profile depends on. Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
An AI-only run never enters the static phase, so its bar sat at the static phase's opening value until the first model call returned — and the frontend, which inferred the phase from the percentage alone, labelled that wait "rule check". On a large wiki the wait is real: planning walks candidates and probes the title index once per seed. The phase boundary is now one named constant on each side, an AI-only run publishes it as soon as planning finishes, and the label is derived from the run's mode rather than guessed from its percentage. Co-authored-by: lyingbug <lyingbug@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
叠加在 Tencent#2451 之上,补齐它缺的部分:主动的 AI 巡检、单页检查、跨页面缺陷检测,以及从"检出"到"修复已验证"的闭环。base 设为
agent/unify-wiki-lint-repair,所以 diff 只包含本次改动。问题
Tencent#2451 只实现了确定性规则(断链、孤立页、内容过少、失效引用)。这意味着:
做了什么
1. 巡检统一为一个 run,带 mode 与 scope
mode=static/ai/full,scope= 全库 / 指定页面。全库 AI 巡检和单页检查是同一套机制的两种取值,共用进度、持久化、消解逻辑。并发槽位按(kb, scope_key)加锁,检查单页不会被"全库正在巡检"挡住。POST /wiki/lint-runs {mode}POST /wiki/page-checks/*slug {mode}(新增)static,客户端无法意外产生模型开销2. AI 巡检按"判断单元"组织,而不是逐页扫描
检测器注册表,每个检测器声明它判断的单元,都是两段式:廉价的数据库候选生成 → 每单元一次受限模型调用。
page-contentsource-groundingsource_refs且有改动的页面,附带"引用 N/M 段"覆盖度duplicate-pages全量两两比较是 O(n²),所以
duplicate-pages本质上就是它的候选生成器。3. 成本被限住
wiki_review_ledger按(detector, unit)记 unit_hash,输入没变直接从库里回答;UI 明确显示"N 个单元未变化已跳过",而不是谎称"没发现问题"lint_model_id可与修复模型分开,选更便宜的;留空回退4. 结果可信度
对"某段文字有问题"的判定必须逐字引用页面原文,同时解决三件事:过滤幻觉、给出跨 run 稳定的 fingerprint、让修复可验证。另有类型白名单、置信度阈值、每单元最多 3 条。
5. 闭环验证
每类问题先走确定性、零成本检查,只有它无法定论才花一次复检调用:
incomplete_summaryduplicate_pages只有"引用还在但页面别处改了"这种真正含糊的情况(矛盾可从任一侧解决)才复检。复检会先按 fingerprint 定位这个问题自己的单元,定位不到就失败并回退到"页面必须真的推进过" —— 否则复检 (A, C) 干净会被误读成 (A, B) 的问题已消失。
6. 重复页面变得可修复:
wiki_merge_pages手工"改写 A + 删除 B"会丢掉 B 的
aliases/source_refs/chunk_refs,下次同批文档入库时重复页面会被重新造出来。新工具做的是转移:幸存页吸收别名(含标题)、来源文档、引用分块,入链改指过来,之后才删除。已接入 Wiki 修订智能体及其系统提示词。7. 消解按三个维度收窄
一次 run 只看了一小片,"这次没报"不能推广成"问题没了"。按来源(规则 vs AI)、类型(本次实际跑的检测器)、范围(引用锚定 → 该检测器实际读过的页面;对子/页面+来源 → 只能由那个确切单元按 fingerprint 关闭)收窄。
8. 前端重新设计
wiki/health/组件:WikiHealthScanPanel、WikiIssueCard、WikiPageCheckBar、issueMeta.ts(标签词汇不再在抽屉和页面弹层里重复)Validation
go build ./...、go vet ./internal/...、go test ./internal/...npm run type-check、npm test(282 passed)、npm run buildsqlite3 :memory: < migrations/sqlite/000000_init.up.sqlincomplete_summary覆盖度增长后置条件、复检单元定位文档
docs/wiki/核心功能/Wiki构建与质量巡检.md:构建链路各阶段、每个检测器能看到什么和看不到什么、成本在哪里被限住、修复凭什么算验证通过,以及新增一类问题的步骤。未做 / 已知取舍
duplicate-pages依赖标题 trigram + 同来源两个信号。trigram 是 PostgreSQL 专属,取不到时该信号静默降级而不是让检测器失败。source-grounding只对照来源文档的前若干段,并明确告知模型"节选之外的缺失不算矛盾",避免因节选不全而误报。