06 / Production Readiness

从能运行到
可以被信任

真正困难的不只是并发、失败和坏文档,还包括证明内容质量、控制错误扩散、恢复一致状态,并让每次模型或规则升级都可比较、可回滚。

基本盘Deterministic Shell
现有写入Per-file Atomic + History
关键增量Eval · Transaction · Migration
退出能力Open Files + Rebuild Contract

总原则:确定性外壳包围概率核心

模型只负责必须依赖语义判断的候选工作;程序接管可验证的不变量,评测系统负责判断语义结果是否达到发布门槛。

交给 LLM交给程序
提出实体、概念、主张与矛盾候选路径合法性、格式解析、日期、来源规范
决定新建还是补充哪个领域页Index 更新、Log 追加、聚合页保护
生成语义合并草案版本检查、结构化 diff、事务发布、历史与缓存提交
生成研究主题与答案检索、排名融合、Token 预算、引用结构

这种边界减少模型“看起来成功”的空间:文本很漂亮不代表文件协议完整,页面写出来也不代表索引、缓存和血缘一致;工程状态一致也不代表内容真实,因此还需要独立语义评测。

源码阅读地图

src/App.tsx启动与项目水合总入口;理解配置和后台任务如何恢复。
src/components/layout/app-layout.tsx三栏产品骨架与视图切换。
src/stores/wiki-store.ts项目、LLM、搜索、Embedding、多模态、图谱 UI 等领域状态定义。
src/lib/ingest.ts最关键 TypeScript:知识写入协议、提交顺序与部分失败语义。
src/lib/source-lifecycle.ts来源的创建、变化与删除如何传播。
src-tauri/src/commands/fs.rs文件系统与多格式解析主入口。
src-tauri/src/commands/search.rs共享混合检索核心。
src-tauri/src/agent/runtime.rs工具规划循环、事件、引用、取消和最终生成。
src-tauri/src/lib.rs所有原生模块的装配根。
mcp-server/src/index.ts外部 Agent 接入面的工具定义。
推荐阅读顺序

先读项目模板和数据目录,再读 ingest;之后读 search/graph;最后进入 Agent runtime。直接从最大文件 runtime.rs 开始,容易只看到复杂度而看不到领域模型。

状态与持久化边界

内容资产

raw/wiki/ 是用户资产,可由 Obsidian、Git 和文本工具直接使用。

项目运行状态

.llm-wiki/ 保存聊天、审核、摄入缓存、队列、文件历史、图片描述缓存和 Skill。

全局偏好

Tauri Store 的 app-state.json 保存最近项目、Provider、代理、主题、语言和服务设置。

可见生成物

agent-workspace/ 明确不隐藏,便于用户发现、预览和带走 Agent 生成的文件。

聊天保存有竞态保护:项目切换与异步水合同时发生时,会检查当前项目身份并合并期间新产生的消息,避免旧加载覆盖新会话。

文件写入与历史

  • 原子写:提供 write_file_atomic,先写临时文件再替换目标,降低崩溃留下半文件的概率。
  • 页面历史:写入前把旧版本保存到 .llm-wiki/page-history/,记录时间、作者与工具,可恢复。
  • 内容清洗:生成文件统一规范 frontmatter、sources 和日期。
  • 受管页面:Index 与 Overview 不接受模型直接输出,避免大 Wiki 全量重写和并发覆盖。
  • 解析警告:非致命异常进入活动面板和持久日志,不隐藏在 DevTools。
  • 刷新与失效:写入后统一刷新文件树、递增 dataVersion,让图谱缓存与 UI 同步。
边界说明

write_file_atomic 保护的是单个目标文件,页面历史提供恢复材料;一次摄入涉及多个页面、Index、Log、缓存与向量,目前没有共同事务提交。不要把“单文件原子”写成“知识库原子”。

删除比创建更难

来源删除不是简单 unlink。系统必须同时维护知识血缘:

  1. 删除原始文件、解析缓存、解析器标记与摄入缓存。
  2. 扫描 Wiki 页的 sources frontmatter,移除被删来源。
  3. 仍有其他来源支撑的共享页面保留并改写 sources。
  4. 只由被删来源支撑的页面级联删除,并清理它的媒体目录与向量。
  5. 从 Index、正文 wikilink 和 related 数组中移除悬空引用。
  6. 向 Log 追加删除原因、删除页数和保留页数。
领域难点

知识页是多来源共同派生物,删除本质上是“带引用计数的数据血缘回收”。这也是持久 Wiki 相比纯查询索引多出的真实成本。

多模型兼容不是换一个 Base URL

Provider 层要处理 OpenAI Chat Completions、Anthropic Messages、Azure 特殊部署、Reasoning 参数、流式事件、错误 Envelope、代理和自定义 Header。

  • Chat 与 Ingest 可以路由到不同模型,项目还能覆盖全局配置。
  • Provider Preset 将默认模型与用户 override 合并,版本升级时默认值可以修正而不覆盖显式选择。
  • 前端网络请求通过 Tauri Rust HTTP 插件发出,绕开第三方 API 的浏览器 CORS 限制。
  • Reasoning-only 响应、<think> 流和普通正文分开检测。
  • Embedding、图片描述、结构化摄入与聊天使用各自适合的 Token 和推理配置。

跨平台与坏输入

问题实现应对
Windows 路径分隔符TypeScript/Rust 边界统一成正斜杠,canonicalize 后再检查作用域。
Linux WebKit/MesaLinux 启动前按需设置合成与 DMA-BUF 兼容环境变量。
解析器 PanicRelease 使用 unwind,Tauri Command 边界 catch_unwind 转成可展示错误。
端口冲突本地服务重试绑定并公开 starting/running/conflict/error 状态。
超大文件预处理缓存、长文分块、读取与 API Body 上限、渐进列表渲染。
隐藏文件与秘密目录遍历跳过 .git、.cache、内部状态和敏感配置来源。

测试策略

仓库当前有 143 个 TypeScript 测试文件(主应用 140、MCP Server 3)和约 400 个 Rust 测试标记,测试不只覆盖纯函数。

Mock 单元测试

协议解析、路径、缓存、Token、Provider、搜索、图谱、删除和队列状态机。

Property Tests

使用 fast-check 验证路径、语言检测、审核变换等在大量随机输入下保持不变量。

Scenario Tests

测试助手把 ingest、search、lint、enrich 等组织成可复用真实内容场景。

Real LLM Tests

*.real-llm.test.ts 独立运行、不并行,验证 Mock 无法覆盖的模型行为。

测试脚本将 mocks 与 real-llm 分开,使日常 CI 可稳定快速运行,同时保留对 Provider 真实行为的验收层。MCP Server 也有自己的构建和测试。

测试数量不是质量证据

现有测试很好地覆盖协议、路径、状态机和兼容性,但固定提交中没有发现可复现的检索评测集,也没有系统展示摘要忠实度、主张支持率、合并信息保留率或端到端故障注入结果。代码测试与知识质量评测必须分别建设。

当前工程风险与可改进点

风险现状判断建议
核心文件过大ingest.ts、agent/runtime.rs、agent/tools.rs、api_server.rs 都已达数千行。按状态机阶段、协议解析、工具执行器、HTTP handler 拆模块,保持公共不变量集中。
缓存指纹不完整摄入缓存核心绑定来源内容,没有完整绑定解析器、Schema、Purpose、Prompt、模型与流水线版本。构建统一 derivation fingerprint,并提供精确失效、重建计划与迁移报告。
跨文件半提交失败会诚实报错并保留已写页面,但读者和重试可观察到部分状态。staging + manifest + intent log;索引和向量只消费已提交版本,启动时恢复未完成事务。
主张级血缘不足来源主要挂在页面 frontmatter,难以证明具体句子、时间和冲突状态。为主张记录来源版本、文本跨度、有效期、置信、审核状态和派生指纹。
前后端检索双实现Rust 是共享主路径,但 TypeScript 仍保留图关联与部分搜索逻辑。明确 canonical implementation,减少算法长期漂移。
Markdown 轻 Schema灵活但依赖正则与 YAML 容错,页面规模增大后冲突更难。引入版本化 Schema 校验报告,但保持文件仍可人工编辑。
文档/行为漂移README Rust 版本低于 Cargo 实际要求;README 声称摄入自动更新 Overview,但当前 ingest 路径只明确更新 Index 与 Log。从清单生成环境要求;为 Overview 建立明确更新器或修正文档。
效果声明不可复现README 有检索提升数字,仓库没有对应数据集、脚本和报告。版本化 eval corpus、标注规范、运行清单与结果;所有指标注明基线和置信区间。
本地服务信任过宽已有 Token/CORS/限流,但回环与共享 Token 仍扩大入口间泄漏半径,query token 还可能进入 URL 记录。拒绝 URL Token,严格 Origin/Host;按客户端、项目和能力签发可撤销短期令牌。
写时模型成本两阶段、图片描述与审核提高质量,也增加延迟和费用。暴露每来源成本与阶段耗时,允许按项目选择质量档。

这些并不否定项目;相反,它们说明产品已经从 Demo 进入“复杂度需要主动治理”的阶段。

如果从零建设,应按什么顺序

  1. 先写验收问题:选一组真实查询、来源更新、冲突、删除与故障场景,冻结基线和隐私边界。
  2. 定义知识契约:版本化 Evidence、Claim、View、Schema、Purpose 与派生指纹;明确谁是可重建资产。
  3. 做最小可信摄入:只支持 Markdown,候选主张绑定文本跨度,结构校验后进入 staging。
  4. 先实现事务与恢复:manifest、intent log、幂等 operation_id、历史、取消和故障注入要早于批量格式支持。
  5. 建立来源生命周期:更新、共享支撑、冲突、有效期和级联失效必须在规模化前完成。
  6. 从词法基线开始检索:先做可解释关键词和引用;只有评测证明有增益才加入向量、RRF 与图。
  7. 补语义与运行评测:持续测支持率、遗漏、检索相关性、延迟、成本和失败恢复,而非只跑单元测试。
  8. 最后开放 Agent/API:先完成服务端鉴权、能力隔离、审计和撤回,再把能力交给模型与外部客户端。
  9. 按故障收益扩格式:每种解析器和多模态路径都必须带契约测试、缓存版本和坏输入样本。
真正的 MVP

不是三栏 UI,也不是炫目的图谱;而是“放入一篇 Markdown → 生成可定位证据的候选主张 → 第二篇资料能更新或保留冲突 → 任一步崩溃都不暴露半提交 → 删除来源后派生内容准确失效”。

上线前的四级门槛

等级必须具备不能用什么冒充
L0 · Demo单文档可生成、可检索、可人工查看来源。不能把演示成功称为长期可靠。
L1 · Personal幂等、历史、恢复、完整缓存指纹、来源更新和删除闭环。不能用单文件原子写冒充整体一致性。
L2 · Trusted主张级血缘、固定评测集、发布门槛、冲突与撤回、成本监控。不能用测试数量或主观体验冒充质量。
L3 · Shared / Agentic多主体并发、权限隔离、审计、秘密治理、Prompt 注入防护与服务 SLO。不能用隐藏 UI、回环监听或 System Prompt 冒充安全。

一个项目可以有意停在 L1,也可以只为单用户服务。重要的是明确承诺等级,不让功能丰富度掩盖可靠性边界。

最终结论

LLM Wiki 展示了一个值得继续验证的方向:LLM 的价值可以不只体现在每次回答,也可以用于维护开放、结构化、可追踪的知识制品。 项目的强项是覆盖了摄入、查询、维护、删除和多入口接入;它的不足也很典型——实现完整度领先于证据粒度、事务语义、可复现评测和安全隔离。

最值得带走

开放资产、Purpose + Schema、确定性外壳和薄适配值得保留;更应补上的,是版本化证据、原子主张、完整派生指纹、可恢复提交、检索与忠实度评测、以及模型之外的最小权限边界。

Return回到专题总览
案例证据:固定提交的核心实现与测试,特别是 ingest*source-lifecycle*project-mutex*api_server.rsagent/* 和构建清单。成熟度门槛、建设顺序与评测要求是本次审计的通用化结论。