跳到主要内容

创建日期:2026-09-14 | 最近更新:2026-09-14 源码级拆解,事实核对自本地仓库 Narcooo/inkos v1.8.0-4-g6e9979b4(2026-09-07):目录结构、文件清单、关键实现(FTS5 检索、37 维审计、状态校验与投影、工具清单)均为读代码所得;标注「未实测」处为纯静态阅读,未运行验证。

InkOS 架构拆解:长篇为什么不出错

第 0 篇说「InkOS = 把模型当有想法的执行者,把创作当有状态、有校验、可回滚的工程」。这篇把这句话落到代码上:agent 分几类、状态怎么存、检索怎么做、审计查什么、一章是怎么原子落盘的。

1. 三层包结构(先看骨架)

packages/
├─ cli/ # 命令入口 + TUI(ink/react)
├─ core/ # ★ 全部核心逻辑:agent / 状态 / 流水线 / 检索 / 提示词 / 技能
└─ studio/ # Web 工作台(Vite + React + Hono)

core/src20 个子目录、459 个 TS 文件,主要几块:

core/src/
├─ agent/ # 会话 harness(Chat Agent 调工具,建在 pi-agent 上)
├─ agents/ # 领域 agent(规划/编排/审计/润色/检测/雷达…)
├─ state/ # 状态:校验、reducer、投影、章节工作区、SQLite 记忆库
├─ pipeline/ # 主流水线:runner、持久化、审阅循环、状态恢复…
├─ production/ # production harness
├─ retrieval/ # 本地检索(SQLite FTS5 + BM25)
├─ models/ # 领域模型:输入治理、字数治理、上下文压缩、题材/文风…
├─ prompts/ # 提示词资产(内置提示包)
├─ skills/ # SKILL.md 体系:内置/外部加载、注册表、生产绑定
├─ interaction/ # 会话与动作:action 信封、会话记录、请求路由、真相权威
└─ notify/ # 通知:telegram / feishu / wechat-work / webhook + dispatcher

2. 两类 Agent:别搞混

InkOS 里有两种完全不同的 agent,理解这点就看懂一半:

agents/(领域 agent)agent/(会话 harness)
是什么流水线里的固定角色「Chat Agent 调工具」的运行时
例子architect(建基础设定)、planner(本章意图)、composer(选上下文)、continuity(连续性审计)、polisherdetectorai-tellschapter-analyzerconsolidatorradaragent-sessionagent-toolsagent-system-promptpi-streamworker-agentcontext-transformskill-tool
谁驱动PipelineRunner 按阶段调用用户/外部通过 inkos agentinkos interact 驱动
底层直接调 LLM建在 pi-agent 上

agent/agent-session.ts 的注释直说了这件事(原文要点):「pi-agent 的 Agent 会把完整对话(含工具调用)留在内存里。一个 sessionId …」,并且实现里用队列保证「同一 project+session 的轮次排队串行」。这就是第 0 篇那句「底层运行时:pi(@mariozechner/pi-aipi-agent-core)」在代码里的落点——顺带也解释了 inkos agent --session 为什么能跨调用记住上下文。

3. 工具清单:模型「会做什么」全在这张表

agent/agent-tools.ts 里注册的工具(真实名字,节选):

propose_action sub_agent research_web ingest_material
retrieve_material manage_book_reference import_chapters
fanfic_create spinoff_create imitation_create continuation_import
short_fiction_run translation_create script_create
storyboard_create interactive_film_create generate_cover
play_start play_edit play_step

看两个名字就懂设计哲学:

  • propose_action:重动作不是直接干,而是先提议——它和 interaction/action-envelope.ts(动作信封)配合,把「写一章/生成封面/开一本新书」这种重操作打包成结构化请求,交给宿主确认后再执行。这就是第 0 篇「重动作先弹确认卡」在代码里的实现位置。
  • sub_agent:agent 能再开子任务——复杂指令可以拆出去做。

一句话:模型手里只有工具,没有权限。工具列表就是它能触达世界的全部边界。

4. 状态层:长篇不出错的核心

state/ 目录是「可信状态」的实现,四个关键件:

文件职责
state-validator.ts校验:拿到 schema.parse(value) 这样的契约,坏数据直接拒收
state-reducer.ts把模型输出的 JSON delta 应用成新状态(不是让模型重写整个状态)
state-projections.ts把结构化状态投影成人看的 markdowncurrent_state.mdpending_hooks.md…)
chapter-workspace.ts章节工作区:saveChapterUserBriefarchiveChapterVersionlistChapterVersionsreadChapterVersion——每章有版本归档能力,可回看/回滚

外加 memory-db.ts(SQLite 时序记忆库)建了三类表(真实建表语句):

facts -- 事实
chapter_summaries -- 章节摘要
hooks -- 伏笔

这套设计回答了一个关键问题:为什么不让模型直接写 markdown 来「改状态」?因为 markdown 没法校验、没法增量、没法回滚。JSON delta + schema 校验 + 投影才是工程解——这也是第 0 篇里「Markdown 是给人看的投影,JSON 才是权威真相」的来源。

5. 检索:不是「全塞上下文」,而是「按需检索」

retrieval/local-search.ts 里是标准做法(真实代码片段):

CREATE VIRTUAL TABLE IF NOT EXISTS retrieval_documents_fts USING fts5(...);

SELECT ..., bm25(retrieval_documents_fts, 5.0, 1.0) AS rank
FROM retrieval_documents_fts
WHERE retrieval_documents_fts MATCH ?
  • FTS5:SQLite 的全文索引(不是暴力扫表);
  • bm25(...) 排序:按相关性返回最该被记起的片段;
  • 注释里还写了「rebuildable FTS5 projection」——索引是可重建的投影,坏了能重建。

配合前面 facts / chapter_summaries / hooks 三张表和 inkos consolidate(卷级摘要),构成一套分层记忆:近期细节 + 卷级摘要 + 按需检索的历史事实。

6. 流水线:一章是怎么被「做」出来的

pipeline/runner.ts 里的 PipelineRunner 提供了一串阶段方法(真实方法名):

initBook / initFanficBook / initSpinoffBook / initImitationBook
planChapter → writeDraft → auditDraft → reviseDraft → writeChapters
reviseFoundation / importFanficCanon …

配套文件各管一段:

文件作用
chapter-persistence.tspersistChapterArtifacts:把一章的产物先在工作区落盘再提交
chapter-review-cycle.ts审阅循环(draft → audit → revise)
chapter-truth-validation.ts提交前校验「状态与正文是否自洽」
chapter-state-recovery.ts状态退化时的恢复路径(对应 CLI 的 write repair-state
persisted-governed-plan.ts落盘版的「受治理计划」(输入治理产物)
scheduler.ts守护进程(inkos up)的调度
short-fiction-runner.ts / script-storyboard-runner.ts短篇 / 剧本分镜等其它形态的流水线

原子提交的直觉:正文/状态/伏笔先在章节工作区里校验,通过了才提交——避免出现「状态已推进、正文没落盘」的撕裂。CLI 里的 write syncwrite repair-state 就是这套机制的对外出口。

7. 审计 37 维:查的到底是什么

第 0 篇说「37 个维度」。源码 agents/continuity.ts 里是一张维度化名表,比如(真实条目):

37: { zh: "正典事件一致性", en: "Canon Event Consistency Check" },

也就是说:审计不是一个「你检查一下」的笼统提示,而是 37 个具名检查项的清单——角色记忆、物资连续性、伏笔回收、大纲偏离、叙事节奏、情感弧线……每一项都有自己的说明与升级策略(源码里还按语言、按题材做差异化提示)。

另外几个和「像不像 AI 写的」相关的模块也在 agents/ 下:ai-tells.ts(AI 痕迹特征)、detector.ts / detection-insights.ts(AIGC 检测与解读)、post-write-validator.ts(写后校验),对应 CLI 的 inkos detectrevise --mode anti-detect

8. 输入治理与模型层(models/

文件管什么
input-governance.ts输入治理:控制文档优先、上下文选择策略
length-governance.ts字数治理:目标值 vs 允许区间(对应 --words 的真实语义)
context-compression.ts上下文压缩
state.ts / runtime-state.ts状态的类型定义与运行时形态
book-rules.ts / genre-profile.ts / style-profile.ts本书规则 / 题材档案 / 文风指纹
detection.ts检测相关模型

这解释了第 0 篇里那些「看起来像玄学」的行为:字数为什么是「目标值」而不是硬截断(length-governance)、规则为什么要三层叠加(写手内置 + 题材 + 本书 book_rules)、文风指纹怎么注入(style-profile)。

9. Skills:只给指令,不给权限

skills/ 目录(registry / builtin-loader / external-loader / production-bindings)实现的是 SKILL.md 体系。关键设计:外部 Skill 只提供指令与静态资料,通过 skill-tool 暴露给 agent,但不会带来新的文件/网络/写权限——权限边界始终由工具白名单(第 3 节)决定。这也是第 0 篇强调的那条安全模型在代码上的位置。

10. 复盘:一条指令到一章落盘

11. 可借鉴的三条工程原则

  1. 模型提议,宿主执行——重动作走 propose_action + 确认闸门;权限只通过工具白名单授予(agent-tools.ts 就是权限清单)。
  2. 状态是「校验过的 JSON + 人看的投影」——validator 拒收坏数据、reducer 只应用 delta、projections 负责可读性;别让模型直接改「真相」。
  3. 完成以工具结果/落盘为准——persistChapterArtifacts + 章节工作区 + 版本归档,把「模型说写完了」变成「文件确实在、状态确实自洽」。

关联

自测

  1. agents/agent/ 两类 agent 的区别是什么?
  2. propose_action 存在的意义是什么?权限由谁授予?
  3. 状态为什么用「JSON delta + 校验 + 投影」而不是让模型直接写 markdown?
  4. 检索为什么用 FTS5 + BM25,而不是把历史全塞上下文?
  5. 一章「原子提交」想避免的坏情况是什么?

参考

  • 仓库:Narcooo/inkos(本地克隆版本 v1.8.0-4-g6e9979b4,2026-09-07)
  • 上架文档:InkOS 入门(同一仓库 v1.8.0 的安装/CLI 事实)
  • 底层运行时:pi(@mariozechner/pi-aipi-agent-core),见 pi-agent 系列
  • 许可:AGPL-3.0