UnderStory 绿色书籍树苗标志

UnderStory

栽一棵智能的树,
为阅读而生长。

一款AI原生的轻小说阅读器

UnderStory玻璃温室 · 3 卷
分析完毕
卷与章节
第 1 卷雨季的归来
  1. 门外的灯1
  2. 旧笔记2
  3. 北侧的玻璃3
  4. 等她回来4
  5. 一封没有寄出的信5
  6. 雨中的访客6
  7. 修灯的人7
  8. 温室的清晨8
第 2 卷越冬的种子
  1. 第一场霜1
  2. 空着的位置2
  3. 旧车站3
  4. 种子档案4
  5. 守夜人的约定5
  6. 停电之夜6
  7. 河岸的雪7
第 3 卷春天的回声
第 1 卷 · 第 1 篇

门外的灯

玻璃温室 · 雨季的归来

林回到温室时,雨还没有停。玻璃上挂着细密的水珠,路灯的光穿过它们,在木桌上投下一片晃动的金色。

“你终于来了。”守夜人说。他没有抬头,只把一本旧笔记推到桌子的另一端。

笔记封面已经褪色,边角却收拾得很整齐。林认得那条绿色的书签——三年前,她离开小镇时,把它留在了这里。

“灯一直亮着吗?”她问。

守夜人指了指门外。那盏灯比记忆里低了一些,灯罩上多出一道裂纹。每逢下雨,灯光就会像现在这样,在玻璃上分成无数小小的光点。

林翻开笔记。第一页没有日期,只有一张温室的平面图。她小时候种下的树被画在最北边,旁边添了一行很轻的铅笔字:等她回来。

她忽然明白,自己一直记得的并不是一盏灯,而是有人愿意在一个地方等候。

雨声渐渐小了。守夜人打开窗,让夜晚潮湿的空气流进来。那棵树还在那里,比她离开时高出许多。

“明天,我们把灯修好。”林说。

她把笔记合上,又将绿色书签放回原处。门外的灯仍然亮着。

本章轻览
第 1 卷 · 第 1 篇

门外的灯

一场夜雨,让林回到离开三年的温室。旧笔记、绿色书签与始终亮着的灯,将她带向一段未曾结束的记忆。

人物

守夜人温室的照看者,也是旧笔记的保管人。
林三年后回到小镇的年轻人。

地点

玻璃温室林童年的记忆所在。

阅读之外,故事之内。

让阅读器主动理解故事情节,营造资料主动环抱的阅读体验。

作品分析的功能演示

让 UnderStory 读懂作品,
再陪你看下去。

导入EPUB后,分析流程会逐卷读完整本书,整理成内部知识结构,再主动整理成你可以直接阅读的Wiki、概览等内容。

你可以配置任意自己喜爱的模型,包括文本嵌入和语言模型。

*Demo 视频的经过模拟和加速处理。

UnderStory

深入文本的轻小说阅读器

阅读提醒:文中的界面和测试示例取自《86-不存在的战区-》《关于我转生变成史莱姆这档事》与《猫的地球仪》。其中可能出现少量人物、设定与情节信息,介意剧透的读者可以酌情跳过相关图片。

记得很早以前用 Kindle 读书时,我就对 X-Ray 印象深刻。长按一个人名或术语,便能看到它在书中的介绍与出场信息。它有点像一本随书附赠的小型百科:人物、地点、组织、物品、事件,以及那些读到一半已经记不太清的专有名词,都能从这里找回来。

我一直觉得,这类功能尤其适合轻小说。许多作品有着层层展开的世界观,几十卷、十几年的连载并不稀奇。读到后面,某个曾经登场的人物突然回来,或一段很早之前埋下的设定重新变得重要,读者往往需要先回忆好一阵子,才能接上故事。

有人喜欢一口气读到底,不愿意被任何辅助信息打扰。我自己则属于另一种读者:读懵了、隔了很久再续读,偶尔甚至跳着看时,我很乐意停下来翻翻资料。以前用 X-Ray,后来查萌娘百科和各种作品 Wiki。想知道的常常只是“这个人原来是什么身份”“几个组织之间是什么关系”,倒不是想提前打听结局。恰恰相反,弄明白这些背景,通常能让我更有兴趣接着往下读。

但现有资料并不总能找到。社区 Wiki 依赖爱好者长期维护,热门角色可能有完整的生平,次要人物却仍在施工,冷门作品甚至没有像样的词条。X-Ray 也不是每本书都有,内容完整度很不一致。

到了今天,很自然会想到让大语言模型来帮忙。最容易实现的办法,是把小说切成片段,做成 RAG 检索库,再给阅读器加一个聊天侧栏。许多“阅读器接入 AI”的想法大概都会从这里起步。我也考虑过,只是很快发现,它与我真正想要的体验还有一点距离。

检索擅长把相关段落找出来,却不等于真正把一个故事从头到尾读过。假如问的是某段台词出现在哪里,它很好用;但如果想理解一个人物跨越数卷的变化、两件相隔很久的事情为何相关,答案往往藏在许多看似不直接相关的段落里。单靠找出几块相似文本再拼成回答,容易遗漏那些需要通读才能串起来的关系。

而且,就算回答足够准确,我也不想每了解一件事都先组织一道问题。浏览 Wiki 的乐趣,恰恰是有时根本不知道自己要找什么:从一个人物跳到某个组织,又顺着一个名词发现另一段故事。这种漫无目的的探索,很难靠一问一答替代。聊天机器人也不可能因为我问了一句话,就自然地把整部作品的百科呈现在面前。

所以我想换一种思路:先让模型尽可能完整地“读”过作品,再把读到的内容整理成一个可以自由探索的知识空间。 对话只是进入这个空间的途径之一。

在动手之前,我先估算了这件事的规模。以 DeepSeek V4-Flash 的 Tokenizer 为例,我把《魔女之旅》前 24 卷中文译本 EPUB 的全文读入统计,结果是 1,298,480 Token,比 100 万上下文窗口多约 29.8%。比预想中乐观:一些更短的作品,或许真能一次放进长上下文。但碰到四十卷的小说、上下文较小的模型,或者输入 Token 与长上下文价格较高的服务,这个办法立刻就不那么从容了。即使放得下,也未必适合每次提问都重新发送一整部书。

我需要的仍然是一套能够分段阅读、记住重要内容、再把信息交给不同任务使用的工作流,也就是后面会提到的模型运行架构,即 Harness。

于是有了 UnderStory。在林业中,understory 指的是森林树冠下方的植被层。我选择这个名字,因为它恰好契合了我对这款阅读器的想象:一切从故事本身出发,也围绕故事生长。它首先是一款可以安静地阅读轻小说的阅读器;而在需要的时候,AI 能从原文中梳理人物、事件与世界观,让那些散落在章节之间、读过却未必记得住的细节变得清晰。

Logo 也延续了这个想法:一棵小树从摊开的书中生长出来,根系扎进书页,枝叶向上舒展。由 AI 生成的 Wiki、人物关系和其他辅助内容,就像这棵树逐渐长出的枝叶,从整部作品中汲取养分,又始终与原文相连。读者可以沿着枝叶探索故事的更多细节,也可以只是坐在树下,继续读手里的书。

第一部分:产品设计

基础板块

UnderStory 的产品形态并不是先写好一份需求文档,再照着逐条实现的。我一边 Vibe Coding,一边把自己放到实际阅读的场景中,不断调整界面与交互。最后逐渐收敛成三个相对独立、又能够彼此衔接的板块:阅读、Wiki 和对话。

阅读

阅读首先得是一件舒服的事。所以最基础的仍然是一个正常的 EPUB 阅读器:左边是按卷和章节展开的目录,像文件管理器一样方便定位;中间是一整块可以连续滚动的正文;右边留给辅助信息;底部则有阅读进度。字体、字号、行距、插图与部分高级排版都能按需调整。我希望一个人即使完全不使用 AI,也能够坐下来好好读完一本书。

阅读板块。以《猫的地球仪》为例,正文居中,卷章目录和阅读辅助侧栏分居两侧。

Wiki

相比之下,Wiki 才是最初真正让我想做这个项目的东西。每导入一部作品,UnderStory 都可以先分析原文,为它生成独立的 Wiki:有首页、目录、分类、专题和具体词条。读者进入这里,不需要先提出问题,只要循着自己感兴趣的地方一路点下去即可。

我沿用了人们熟悉的 Wiki 阅读方式:左侧的大纲,右侧的词条内容;页面上有信息表、结构化正文、相关词条、作品导航,以及能够跳回原文的引用。不过底层并没有嵌入 MediaWiki 之类完整的维基系统。它们为社区协作和内容编辑准备了庞大的功能,而我更需要一个能承载 AI 生成结构、供读者浏览的轻量空间。于是索性自己实现了一套“看起来、用起来都像 Wiki”的界面。

《猫的地球仪》的作品 Wiki 主页。即使没有明确要查的内容,也可以从这里开始探索。

对话

第三个板块是对话。如果我已经知道自己想问什么,聊天当然最直接:Wiki 中没有解释清楚的关系、某段情节的疑问,都能拿来问。只不过这里的对话并非独立悬浮在阅读器之外的助手。它可以调用原文引用、Wiki 词条卡片和跳转链接,也能请求创建专题、补充词条,甚至直接修改阅读器设置。

对话板块。它既用于了解作品,也承担一部分阅读器操作入口的职责。

对话界面本身也经历过一次改动。最初我采用类似短信应用的回合式气泡:我问一句,模型回一条。但用下来发现,读者往往是在认真阅读模型给出的一段内容,而不是像聊天软件那样快速互发消息。于是我改用接近 ChatGPT 的全宽回答排版,让标题、段落、粗体、列表和引用成为内容本身的组成部分。

等待过程也做了一些处理。Agent 调查问题、调用工具时,哪怕模型已经很快,读者仍可能等上数十秒。现在它会先给出一条简短的即时回应,再逐步显示工具调用和计时,让人知道这段等待正在发生什么。回答中还可以按需出现 Wiki 卡片、章节跳转、原文引用块、表格或叙事轨迹,点击即可回到相应内容。

一份特意密集展示各种组件的回答示例。平时由模型按需要选择,不必每次都出现全部组件。
顶部三个并列入口,对应三种不同的使用方式。

三个板块之间的关系很清晰和简洁:想读书时就读书,想随便逛逛就打开 Wiki,遇到明确的问题才去对话。不会全部挤在同一个界面里,也不必一同争夺读者的注意力。


阅读侧栏

当然,真正读书时最常见的情况,是正读到某句话,突然想确认一个名字或设定。这时专门切到 Wiki,再找到词条,又回到原文,还是有点麻烦。

于是阅读页面右侧保留了一个多功能侧栏,里面有四种视图:

  • 轻览会为每章生成一段尽量不剧透的前情提示和内容引入,并列出本章相关的词条快捷卡片。
  • Wiki 和 对话则把对应板块直接带到正文旁,方便一边读一边查。
  • 搜索整合原文 RAG、Wiki 词条与章节检索。

不管我是在回忆上一章的情节,还是想问正在读的这一段,都不必离开阅读页面。

从左到右:章节轻览、Wiki、阅读伙伴与搜索。辅助信息始终与正文相邻。

原文选中与快速查询

我还做了一个更顺手的动作:直接选中文字。比如一段对话中突然提到了早已忘记的角色,我只要选中人名,弹窗便会找出可能对应的词条,点一下就能查看;如果我关心的是这句话本身,还能把选中的段落附给阅读伙伴,让它围绕这段文字回答。

选中文字后,词条入口和对话操作就出现在附近;右侧同时展示围绕选中内容的讨论。

这里并不只是简单地把选中文字与词条名逐字比较。小说里的称呼很复杂:人物可能有全名、姓氏、绰号与称号,《86》中的“修雷·诺赞”“诺赞”“死神”就可能指向同一个角色。仅凭标题完全匹配,很多读者真正选中的词反而会漏掉。

所以弹窗会同时提供准确匹配的词条和相关词条推荐,并在别名匹配时标明它对应的正式名称。文本里可识别的关键词也会相应高亮。支撑这件事的别名信息,并不是为弹窗单独写的一套规则,而是后面作品分析阶段积累的“公共要点表”中的数据。

绿色标签对应直接匹配的词条,其他标签提供相关建议;别名匹配可以帮助辨认角色的不同称呼。
内部调试界面。一个对象可以拥有多个别名,也会保留在不同卷中逐步补充的记录;读者通常不会直接看到这张表。

阅读进度条

小说本身是一条不断向前的时间线。为了让读者在这条线上有方位感,我还在阅读区域底部加了一条进度栏,同时显示全书与当前卷两个尺度。动辄二十多卷的作品,如果只在一条横线上排满章节,很难看清自己究竟到了哪里;把全书与单卷分开,既能知道整体还剩多远,也能知道这一章大概读到了什么位置。

它还会和 Wiki 的叙事轨迹联动:当侧栏打开某个词条,进度栏会标出这个对象出现过的章节。于是“这个人物后面还有戏份吗”“某个概念究竟贯穿了多少卷”,不仅能在百科里读到,也能直接在阅读进度上看到分布。

全书与本卷的双尺度进度,以及词条出场章节在进度栏中的标记和跳转。

反剧透

做到这里,一个很微妙的问题出现了:UnderStory 的 Wiki 正是因为提前分析了整部作品,才有机会把人物和故事讲清楚。但对尚未读完的读者来说,这些知识也可能毁掉阅读体验。

我不想让 Wiki 变成一本把结局写在第一页的答案册。章节轻览从生成时就被要求“只引导,不剧透”;Wiki 与对话则分别设计了不同的防剧透交互。

Wiki 的剧透屏蔽

Wiki 中最常见的情况,是某个词条前半部分可以放心阅读,后半部分却涉及人物未来的命运。我参考了萌娘百科等站点常见的遮罩方式,用色块盖住可能剧透的段落。读者知道这里还有内容,但不会在无意间直接看到。

人物词条里的部分内容被绿色遮罩覆盖,未涉及剧透的部分仍能正常阅读。

这些遮罩不是前端靠关键词猜出来的。词条撰写 Agent(Wiki Composer)生成内容时,会在结构化输出中标记哪些片段属于剧透,并尽可能附上 unlock_after_chapter_id,也就是读到哪一章以后,这段信息可以解锁。引用跳转、段落定位和剧透规则都包含在同一份 Wiki 数据结构里。

Wiki 生成时的原始结构示例,包含引用位置、剧透片段及章节解锁标记。

有了章节条件,遮罩就不必永远停在原地。系统可以跟踪读者的实际进度:当我已经读过对应情节,这段文字对我而言就不再算剧透,可以自动显示出来。

当然这是可调整的,取决于读者个人的接受能力:

  • 关:始终显示所有剧透。
  • 自动:阅读达到指定章节后自动解锁指定剧透屏蔽。
  • 严格:始终屏蔽任何剧透内容。同时提供更多严格的屏蔽特性,有可能影响 Wiki 使用体验。

对于单段屏蔽内容,也允许读者主动点击临时解锁。

三种屏蔽策略与单段手动解锁。是否知道后续情节,最终仍由读者决定。

还有一种更棘手的词条:事件的标题或简介本身就是剧透。此时只遮住正文已经来不及了,因此 Wiki Composer 也可以把整个词条标记为剧透。阅读器会锁定整页、模糊页面内容,并遮住目录中的词条名称,尽量避免读者仅仅扫过导航就提前知道了某件事。

当词条标题本身可能泄露情节时,标题与内容会一并被隐藏。

对话的剧透确认

对话的处理方式则不同。读者可能主动问“这个人最后怎么样了”,也可能只想问“他来自哪里”。同样访问过全文的 Agent,没法仅凭一个问题就断定我是否愿意接受剧透。于是它在调查到相关信息、准备回答前,可以调用一个确认工具,先中断生成,然后询问读者意见。

Agent 识别到可能严重剧透的问题后,先询问读者,再决定如何继续。

阻断工具会提供三种选择:

  • 忽略警告,提供完整回答:认为读者接受剧透。模型会根据真实作品信息完整回答。
  • 回答,但尽量减少剧透:将“尽量减少剧透”要求注入提示词中,模型会被要求修改回答方式,减少剧透。
  • 取消并停止回答:停止当前回答。

我希望这些机制最终让阅读器既有“知道全书”的能力,也懂得在读者尚未走到某个地方时,先把答案留在那里。


阅读体验与设置

UnderStory 的大部分设计都围绕 Wiki 和 AI 展开,但我平时真正面对它的时间,仍然是在读一行行文字。阅读体验如果不舒服,再丰富的百科也只会变成一个偶尔打开的功能。

阅读设置因此覆盖了字号、行距、EPUB 样式、插图显示、浅色与深色主题,以及前面提到的剧透策略。设置以模态窗口弹出;因为弹窗会暂时遮住正文,我在窗口里加了一块实时预览,让调整字号或行距时不用反复关闭设置再看效果。

设置弹窗。修改排版参数时,旁边的预览会同步变化。

EPUB 排版

其中我花了不少心思的是 EPUB 样式。EPUB 本质上使用网页相关技术组织内容,但不同书籍的排版质量差别很大。有些制作精良,包含精细的段落、字号与图片布局;有些却带着凌乱的 CSS,换一个阅读器就会变得难看。也有人只是喜欢所有书都维持简单、统一的排版。

我提供了三个显示档位。关闭时只保留基础换行,不沿用原书样式;基础保留粗体、斜体、下划线、上下标、引用等明确的语义格式;高级则进一步应用 EPUB 内嵌与外部 CSS,包括布局、颜色和局部字体大小等。书籍导入时会先解析、清洗样式,阅读时再根据选项决定采用到什么程度。

同一段文本分别以关闭、基础、高级三种方式呈现。保留多少原书样式,可以交给读者选择。

插画显示

插画则有另一个细节。我的浅色阅读界面偏暖白,普通轻小说的黑白插画却往往带着纯白底色,嵌进页面就会像贴了一块亮白纸。于是我做了“沉浸式插画”选项:将图片以正片叠底的方式与页面融合。对黑白插图来说,这通常能保留线条的可读性,同时减少画面与纸张背景之间突兀的色差。

同一幅插图的两种显示方式。开启后,白底不再与暖白的阅读页面明显分离。

自然语言调整设置

还有一个来自“对话即系统”想法的小功能:我可以直接对阅读伙伴说“把字号调大一点”“行距再松一些”“改变插画显示方式”。Agent 通过读取设置和更新设置两个工具,修改字号、行距、图片模式、EPUB 特性或主题,并把改动整理成一张可操作的卡片。

这张卡片上有撤回入口。我觉得这个细节很重要:某些排版习惯是在读了很久以后才慢慢调到合适的,一句自然语言虽然方便,但不该让人忘记原来的数字、又费力找回去。修改应当看得见,也应当退得回。

自然语言请求、设置工具调用和可撤回的更新卡片。

阅读主题

至于主题,阅读器提供了浅色与深色两套界面。它们并没有不同的功能,只是让白天、夜晚或不同的阅读偏好都能得到合适的呈现。

同一个阅读布局下的两种主题。

Wiki 页面设计

如果说 UnderStory 是“为了 Wiki 这碟醋包的饺子”,那么这部分就是那碟醋本身。

我最初的不满很具体:打开一个冷门作品的 Wiki,感兴趣的人物没有词条;换一本热门长篇,次要设定又散落在多个页面里。社区 Wiki 自有它们经过人工验证、长期维护的价值,但我想要的阅读辅助应该可以跟着自己导入的作品一起出现,不必先等有足够多的爱好者来填坑。

因此,在 UnderStory 中,用户配置好模型、选择“分析作品”,系统就会尝试从原文中生成属于这部作品的 Wiki。它不依赖网上有没有现成百科;小说被拆解成可索引的内容,多个分工明确的 Agent 再据此规划和撰写页面。实际分析规模可以达到数百万字,流程需要一定时间,原稿中的测试场景约半小时完成一次完整分析。具体怎么完成,下一部分再讲。

界面参考了 Wikipedia、萌娘百科、Wikidot 和 Fandom,但我没有直接使用一个真正的开源维基项目。UnderStory 是单机阅读器,主要是读者浏览,而非一群编辑共同维护;AI 生成的词条也有自己的引用、剧透、进度与叙事结构。比起移植庞大的社区系统,从头实现一套轻量但体验完整的 Wiki,反而更适合这里。

首页与类目

它当然需要一个首页。读者有时只是想随便看看,不妨从一段作品介绍出发,点开一个专题推荐、人物卡片,或者某个分类。Wiki Architect 会为作品规划主页内容与导航,除了 Markdown 正文,还可以使用专题推荐、词条、类目与卷的链接卡片,让探索有可以随手抓住的线索。

主页并不只是词条索引,还提供简介、专题推荐、词条推荐和类目入口。

从首页继续向下,则有针对人物、组织、地点、事件等对象的类目页。类目由模型依据作品自行组织,不强求每本书都使用同一套分类。类目页本身不急着详细介绍谁是谁,更多是在某一主题下展示可供继续阅读的词条,像图书馆里某一排书架。

地点类目示例。它负责把同类词条摆到一起,让读者沿着类别继续浏览。

词条页

再往里才是具体词条。词条本身由并行的 Wiki Worker 撰写。根据词条的重要程度,留给 Wiki Worker 的思考和工具调用预算也不同。一份词条页面由标题/简介、Markdown正文、信息表、相关词条表、叙事轨迹和作品导航板块组成。

从正文、引用、信息表,到关联词条、剧透遮罩和导航模块,一篇 Wiki 词条所包含的内容。
  • 标题与简介:词条名称与介绍。
  • 信息表:模仿常见Wiki,提供一个快速查询词条属性的结构化表格。
  • 正文:标准Markdown+自定义格式(引用角标、链接、屏蔽片段等)
  • 相关词条卡片:快速跳转到与当前词条相关的其他词条。
  • 叙事轨迹:该词条在全书出现的标记。
  • 作品导航:对整个Wiki提供结构化的概览,也可以用于跳转至其他词条。

叙事轨迹

其中有个我特别喜欢的模块,叫作叙事轨迹。

Kindle X-Ray 可以指出一个词或人物在书中哪些地方出现过。我想再往前走一步:不仅知道“在哪里出现”,还可以一眼看清这个对象在整个故事里何时登场、何时消失、在哪里重新进入主线。它看上去有点像编曲软件中的音符轨道——一段人物弧线何时响起、停顿了多久、又在哪一卷被接续,仿佛有了可以观察的形状。

节点有三种类型:AI精选、正文引用和出场章节:

  • AI精选:Wiki Composer主动填写的字段,描述该词条在全书中的主要事件、人物弧线等。但AI主动填写未必特别详细。
  • 正文引用:该词条Wiki引用出现的具体位置。
  • 出场章节:基于要点表和轻览反推的出场章节聚合。

由于AI在标注要点表的证据、填写精选表时,都会附加note字段,因此鼠标点击某个节点时,多数不仅显示卷/章位置,也能显示这个节点到底发生了什么。

横跨多卷的出场记录被组织成时间轨迹;展开节点可以查看对应事件。

作品导航

每篇词条底部还有作品导航。它借鉴了主题 Wiki 常见的导航模板,把一组相关人物或概念整理成二维表格。与侧边目录不同,作品导航出现在词条末尾:读完一个角色以后,顺势看到同一阵营、同一组织里的其他角色,或这一类设定的全貌。传统 Wiki 的模板里常有“查·论·编”,这里既没有讨论页,也不打算让用户像编辑站点一样逐页编修,于是就把它留作纯粹的浏览地图。

用二维关系表把相关角色、组织或概念放在一起。读完当前词条,也就知道下一站可以去哪里。

第二部分:模型运行架构

作品分析流程

前面介绍的是读者能够看到的部分。接下来的内容我会按照我的真实开发顺序梳理:初期如何设计架构,如何发现问题、后期如何修正、简化、收敛、优化。Token消耗量怎样从约40M/10卷降低到20M/10卷。

单次超长上下文虽然越来越常见,却不意味着上下文里的每个位置都同样容易被利用。更大的输入带来更高的成本,某些模型在长上下文中提取细节也不稳定;有的模型宣传的窗口很大,真正处理跨章节关系时的有效能力却未必跟得上。另一方面,纯 RAG 又容易只找回最相似的片段。对跨卷伏笔、人物转变与隐含关系来说,检索之外还需要某种长期保存的叙事记忆。关于层级式检索的 Storyline Trees 研究,也给过我这方面的启发:层级树式的检索明细比朴素RAG能获得更多信息,而更多的 chunks 边际效益非常低。

与其用 RAG 无限增加相似片段,不如让更多上下文用于保留层次与故事结构。

我最终把原文、检索索引与预先整理的结构化资料放到同一套系统中。Agent 既可以搜索原文、取回带出处的证据,也可以查询已经读过的卷章报告和公共要点。下游的 Wiki 写作不必每次从零开始,对话也不需要只盯着几段检索结果。

原文切片和向量索引负责找证据;章节、卷摘要与公共要点提供已经整理过的知识;Agent 将两边结合起来。

新作品导入后,需要从“分析作品”入口启动这些预处理工作。最终界面提供质量分析和快速分析两种模式,区别在于模型可使用的工作预算,方便根据时间和成本选择。

对整部作品发起分析的入口,可以选择处理策略并观察进度。

V1:逐章分析

这套工作流并不是第一版就长成现在这样。我最初的想法非常直观:既然想让模型认真读书,那就让它一章一章读、先做笔记,再由更高一级的 Agent 汇总。

V1 因此采用了“章节 Worker → 卷报告 Agent → Wiki Architect → 词条撰写 Worker”的流水线。

  • 章总结Worker:读取每一章完整内容,然后将本章内容写成章节报告。章报告是一个非常固定化的JSON结构,包含章节摘要、顺序剧情大纲、场景、实体(人物等)、事件、局部关系、设定片段、未决问题、关键细节与重要对白、叙事形式观察几种结构。
  • 卷报告Agent:每一章的报告会进入卷报告Agent。卷报告Agent有更多的调用预算,可以多轮调用,之后生成逐卷的卷总结。而卷总结也包含四个硬编码的概览、人物、事件和知识四个部分。
  • Wiki Architect:完成所有卷总结后,Wiki Architect开始构建Wiki的骨架:首页、词条模板和词条列表。但Wiki Architect不会撰写具体的Wiki词条,而是在有限次的多轮调用中,持续调查-提交-修改,最终形成合适的Wiki骨架。
  • 词条撰写Worker:词条撰写Worker用于并行填充Wiki骨架中的具体词条。词条按照重点/标准/简要分类,并给予不同等级的工具调用预算。重点/标准词条可以以不同的轮次预算主动调取资料;简要词条则只能使用预提供的资料。
  • 轻览:由章总结Worker的摘要字段生成,为进入每一章时提供无剧透的引入。
V1:逐章生成报告,再按卷汇总,最后规划并撰写 Wiki。每一层都在重新阅读上游的整理结果。

问题也正出在这里。为了让章节理解有足够的前后文,我给各章 Worker 提供了整卷内容。单章任务于是反复消耗同一卷输入,到了卷总结和词条撰写阶段,又要再次阅读大量报告。流程虽然工整,却显得很臃肿。

我用《魔女之旅》前四卷试跑过 V1。测试记录中,章节 Worker 占端到端 Token 用量的 16.3%;当时的成本统计还包含每百万 Token 覆盖约 5807 字、每千字约 172 Token 的记录。这套统计本身后来还值得回头统一口径,但最直观的问题已经很清楚:大量预算花在重复传送和重读相同材料上。

V2:逐卷分析

因此在 V2,我干脆去掉了独立的逐章 Worker,让每一卷由一个 Agent 负责完整分析。这个 Agent 可以直接读整卷、整理章节与卷信息,并在必要时查阅前面几卷已经留下的记忆。原先章报告与卷报告之间那一层昂贵的接力,就这样被合并了。

V2:以卷 Agent 为中心,借助公共要点表共享跨卷记忆,再把成果交给 Wiki 规划和撰写流程。

V2 的卷 Agent 是一个可以多轮调查的完整工作单元。按照质量或快速档位,每卷最多可循环4~9轮,有多种工具可调用,且每轮鼓励批量调用工具:

  • get_knowledge_points:批量读取相关既有要点的完整更新历史。仅第二卷及以后提供。

  • create_knowledge_points:批量创建不存在的新要点。

  • append_knowledge_points:为已读取的既有要点追加本卷内容,也可追加别名。

  • add_point_aliases:批量补充要点别名。

  • search_original_text:RAG检索原文。用于查找准确章节、段落定位和引用。

  • search_wiki:对于已有过一次完整分析的作品,在分析新增卷时,按需检索现有 Wiki。

  • submit_volume_summary:提交卷总结、逐章总结及非正文参考章节 ID,是最终提交工具,会终止当前工作流。

同时,我不再把作品信息强行限制在“概览、人物、事件、知识”四种栏目里。不同小说的叙事重点完全不同,奇幻作品可能有庞大的能力与阵营体系,另一部作品可能更值得追踪人物关系和阶段性事件;分类应该让模型根据作品自行确定。

词条 Worker 也不必再把大段原文和报告从头翻一遍。它先拿到与当前词条相关的公共要点,只有重要词条需要更细的证据时,才进一步回到 Wiki 或原文调查。按开发记录中的单位原文处理成本统计,V2 相比 V1 下降了 93.9%,相同 Token 预算可以覆盖约 16 倍的原文。另一次以十卷为规模的流程估算,也从原先约 4000 万 Token 收敛到约 2000 万 Token。不同测试口径下数字并不完全对应,但方向是一致的:减少重复阅读,把预算交给真正需要推理和核查的地方。


公共要点表

V2 最关键的变化,其实不是少了一个 Worker,而是多出了一张“公共要点表”。

最早我只是想用它节省 Token。后来发现,如果要让不同的 Agent 共享对一部小说的理解,它可能比几十份摘要都更重要。

想象一下,把二十卷小说压缩成二十篇卷摘要。这当然比原文短得多,却仍然是二十篇长文章。要写“希尔”的词条,模型需要重新找出所有与她有关的段落;下一次有人问“智慧之王拉斐尔”,另一个 Agent 又得在差不多的材料里搜索一遍。摘要已经浓缩了内容,却没有把内容整理成便于复用的对象。

所以我把值得长期跟踪的人物、组织、地点、道具、能力、事件,以及作品独有的概念,都视作一个个独立的“要点”。第一次遇到它的卷 Agent 负责创建;后面几卷如果这个对象再次出现,就只补充这一卷新增了什么。模型可以查阅这个要点过去的记录,再决定追加哪些信息,而不是把所有历史要点无差别塞进每次调用的上下文。

两个卷 Agent 如何围绕同一个要点创建、读取和追加信息。旧记录被保留下来,新一卷只接着往后写。

例如《转生史莱姆》的真实生成记录里,“希尔”作为人物要点被建立,此后持续接收其他卷的新增内容。每次追加都可以写明本卷变化、原文证据的位置和补充说明。这样查看一个对象时,就能沿着一串结构化的记录回看它的发展。

《转生史莱姆》的内部要点调试界面。展开一个人物,就能看到按卷追加的记录。

这些表格不是给读者直接看的。它更像整部作品被分析后留下的中间表示:比原文短得多,比卷摘要更容易精确查询,同时又没有丢掉事件的发展顺序与原文证据。

别名也是这套记忆里重要的一部分。像“修雷·诺赞”“诺赞”“死神”这样的称呼,如果没有统一的对应关系,模型可能把同一个人物当成几个独立对象。卷 Agent 因此不仅要判断是否创建新要点,也要判断能不能沿用已有要点,或只为它补充一个新别名。有了这一层共识,阅读时选中别名能跳到正确词条,Wiki Worker 也能找到同一角色的跨卷经历;对话 Agent 则可以先查结构化记忆,再回到原文验证具体事实。

这张表从节约成本的小工具,逐渐变成了整个 UnderStory 的信息中枢。


Wiki 生成架构

当每一卷的信息有了自己的去处,下一步就是:怎样把它们组织成一套真正可读的 Wiki?

现实里的大型 Wiki 不是一个人从头到尾写出来的。有人规划栏目,有人写词条,有人负责分类、模板和维护。UnderStory 也需要类似的分工。只安排一个 Agent 写完整个 Wiki,工作会又长又重,上下文很快被塞满;但直接让许多 Agent 各写各的,页面会缺乏一致结构,冷门条目也可能写得比主角还长。

我最后把 Wiki 生成拆成两个阶段:集中规划,并行撰写。

先由 Wiki Architect 负责整部作品的蓝图。它会查看卷报告和公共要点索引,再通过多轮检索与调查,决定首页应该展示什么、需要哪些类目与专题、哪些对象值得单独成为词条、各类词条使用什么模板,以及底部的作品导航如何分组。它还会为待写词条附上关联要点,提交完整的 Wiki 蓝图;程序会检查蓝图里的必要数据是否齐全,才让流程继续往下走。

然后才轮到 Wiki Composer,也就是一组并行工作的词条 Worker。每个 Worker 只负责一个页面,获得 Architect 为它准备的词条模板和相关要点。它可以在权限范围内查找原文或已有 Wiki,却不能随意修改别人的词条。这样既能并行加快生成速度,又能让成百上千段独立文字维持相近的页面结构和视觉语言。

Architect 先确定作品结构,再把各词条的资料和模板交给并行写作的 Worker。

词条分级与撰写

词条的数量不能太少,也不能无限多,在Wiki Architect的提示词里,规则会根据公共要点表的数量估算作品规模,然后将预期Wiki词条数目限制在20到160个不等。

不同词条需要的资料量也不相同:主角可能横跨二十几卷,拥有复杂的人物弧线;一个只在某个单元故事中出现的地点,只需要简单的资料和少量字数的词条。如果让所有词条撰写Worker获得完全相同的上下文、轮次和工具,不仅成本很高,也会诱导模型把边缘内容写得过度膨胀。

因此 Wiki Architect 在规划词条时,也会为词条判断级别。目前分为三档:

  • 核心词条(Core):作品的核心人物、关键能力、长期事件等。Worker拥有较高的轮次预算,可以主动读取多个公共要点、检索已有Wiki,或者用RAG回到原文寻找准确依据。
  • 重要词条(Supporting):重要配角、主要地点、阶段性事件等。Worker仍然可以调查,不过工具和轮次比核心词条更克制。
  • 局部词条(Local):只在较小范围内出现的人物、地点或概念。系统直接准备好相关要点与上下文,让模型在一次调用中完成写作。

早期的 Wiki 词条撰写是整个分析流程中最大的成本来源。V1 中,48个词条消耗了超过 52M Token,占端到端 Token 的近八成,平均每个词条超过一百万 Token。经过公共要点复用和分级后,最终版本生成139个词条,平均每个词条约10.7万 Token。词条数量变多了,单个词条的上下文反倒只有原来的十分之一。


对话 Agent

既然阅读器已经拥有预先整理的 Wiki、章节报告和公共要点表,对话 Agent 便不必回到最初那种“用户问题 + 几段 RAG 搜索结果”的简单模式。

我把它可用的资料分成三个层次:

  • 最上层是已经组织成阅读内容的 Wiki。
  • 中间是卷章摘要和公共要点这些结构化记忆。
  • 最底层仍然是完整原文,用来复核事实、确定段落位置,或者在已有总结不够细时重新调查。

它可以按照问题的性质决定先访问哪一层,而不是每次都翻整本书。这些能力打包成对话Agent可调用的工具集:

  • search_wiki:搜索Wiki词条的标题、摘要和正文。
  • get_wiki_entry:读取指定Wiki词条的完整内容。
  • search_knowledge_points:批量查询和读取公共要点表中的项目。
  • get_content_summaries:批量读取卷总结或指定章节总结。
  • get_chapter_lists:获取指定卷的章节目录、字数和正文标记。
  • search_chapter_titles:根据标题查找章节。
  • search_original_text:使用词法、语义或混合模式RAG检索带定位的原文切片。
  • get_full_chapter:在确有必要时读取一个章节的完整正文。
  • get_reading_settings:读取当前主题、字号、行距等阅读设置。
  • update_reading_settings:按照读者的要求修改系统阅读设置选项。
  • confirm_serious_spoiler:回答可能包含剧透时阻断并征求读者选择。
  • submit_answer:提交最终回答。

侧栏里的阅读伙伴还会收到读者当前在第几卷、第几章的信息,以及作品目录、Wiki 词条索引和阅读设置。这一点很重要:当我指着正在读的那一段问“他为什么这样说”,它至少知道此刻的“这里”指向哪里,也能参考我设定的剧透策略来决定调查和回答的范围。

我请 Codex 对真实测试中的两类问题做过流程分析。一个问题与人物的进化、能力变化有关,Agent 先查 Wiki 和公共要点,再扩展检索相关情节,回原文核验后提交带证据的答案。另一个问题涉及跨卷人物关系,Agent 则需要先找到事件,再整理不同卷里的信息,最后回到具体章节确认。这些流程图最有意思的地方,不是模型调用了多少工具,而是不同问题让它选择了不同的查证路径。

一次实际测试:拆解问题、读取结构化资料、核对原文,再提交带引用的回答。
另一种问题触发了不同的调查顺序:跨卷查找、证据归纳与最终回答。

RAG 检索与混合方案

词法检索

至于最底层的原文检索,我自己也走过一段取舍过程。最开始只有词法匹配:速度快、结果很容易解释,查询独特人名、称号或一句台词时尤其好用。它一开始的表现就不错,但毕竟只理解文字表面。换成“某个人获得某种能力的那段经历”这种自然语言问题,明明原文里有答案,查询词却未必正好出现。

语义检索

后来我加入了真正的语义检索。导入作品时,程序把各章切成连续的 RAG 片段,通过 Embedding 模型生成向量并写入本地 SQLite。测试时使用独立运行的 Qwen3-Embedding-0.6B 服务,也允许用户改用在线 Embedding API;如果嵌入服务不可用,就退回词法模式。查询的相似度计算由 Python 完成,索引直接跟着作品保存在本地,不需要为了单机阅读器再搭一套独立的向量数据库。

混合检索与结果排序

最后又把两者结合起来,做成默认的混合检索(hybrid)。词法搜索对准确名称、特定台词很敏感;向量搜索更容易接住自然语言里的语义。两者并没有绝对的胜负。我让 Codex 用不同问法做过对比,结果很能说明这一点:

查询词 词法匹配 lexical 语义检索 semantic 更适合
希尔老师 前 8 条基本都准确 能找到相关章节,但混入泛化结果 lexical
大贤者进化成智慧之王 只找到第九卷的一句回顾 第 1 名准确找到第五卷进化现场 semantic/hybrid
大贤者 智慧之王拉斐尔 前几条非常准确,直接命中进化原文 更偏向人物关系、命名等相关场景 lexical/hybrid
就叫你希尔 指导 诞生了神智核 核心原文相关度第 1,但混入大量普通“指导” 第 1 名正确找到命名场景,后部有噪声 hybrid
自然语言整句 0 条 第 1 名准确找到命名与伙伴场景 semantic
利姆路/智慧之王拉斐尔/神智核希尔 能找到共同出现,但不一定回答“羁绊” 命名场景相关度第 1 hybrid

所以我没有简单地用向量检索替换掉原先的方法,而是允许 Agent 自己选择词法、语义或混合检索,默认采用混合方式。很多时候,换一种更准确的查询词,就比盲目增加检索片段更有效。

还有一个针对小说的小处理:系统先按相关性挑选原文片段,随后再把结果按照卷、章、段落的原始顺序排回来,同时保留相关性名次。一般知识问答最关注“哪个结果最像问题”,而小说是有先后顺序的。同一人物在第一卷与第十卷的状态可能完全不同;按故事发生的顺序看证据,更容易理解一件事情究竟是怎么发展到那里的。

后记

“有几个情节我读不懂,百科里也查不到。”

“那就问问 AI?”

“什么叫AI给你生成了一个百科?”

这是我最初的愿景。

在这个时代,知识和内容都来得太轻松了。只要打开一个聊天框,似乎任何问题都能迅速得到一段像模像样的回答。但信息仍然是从聊天框中涌出的:我问一句,那边答一段。查找、探索、链接,这些逛 Wiki 曾经带给我的乐趣,却很难在“对话”这样的形式中展开。

UnderStory 试着让辅助信息回到阅读过程本身:它可以是进入章节前的一小段轻览,是选中名字时浮现的词条,也可以是一条从第一卷延伸到最后一卷的叙事轨迹。只有读者真的想问点什么,才打开对话窗口。小说仍然在最中间,其他东西则像散落在故事周围的路径,需要的时候走进去看看。

UnderStory 已经基本实现了产品闭环,不过它目前还远没有达到一个商业产品或正经开源项目的级别。它承载了我对AI阅读、动态Wiki和长文本Harness的一整套思考,诠释了目前我对“AI原生产品”的理解。

完整的项目仓库已经公开,你可以拉取、配置API后亲自体验,希望你能喜欢。

至少对我来说,它让当年翻开 X-Ray 时的那个念头,终于有了一种可以亲手使用的答案。