一家独立书店最擅长的生意,是聊天。有人推门进来问"心碎的时候该读点什么",或者"要难过的,但别太难过",店员知道手往哪排书架伸。可到了线上,同一家店只剩一个按书名检索的目录。不知道书名的读者转身就走,书店也永远不知道他刚才想找什么。

打开网易新闻 查看精彩图片

Subtext 想补的就是这段对话。它把书店的库存摆出来,让读者用自己的话去问。本地模型把每本书的简介读一遍,之后书架按"意思"回应请求,说明每本书为什么被选中,实在没有合适的就直说没有,还把读者问过什么记下来——只记问题,不记是谁问的,好让店员知道该进什么货。

这套东西分成两头。

读者这头,是摆出来的书架。你说"忧郁一点,但要让人安心",最贴近的几本书就从各自的格子里探出来,页边一行小字写着这句话是怎么被听懂的。点开一本书,它像展开的护封:读起来是什么感觉、为什么它回应了你的请求(附一句从它自己简介里摘出来的原文)、价格、预约入口。你要是在一家文学书店里问"素食食谱",它会告诉你这家店没有这类东西,而不是编一个答案给你。

书店这头,是柜台。每本书都带着 AI 的读解——氛围、节奏、密度、声音、主题——以及它依据的那些段落。店员可以改任何一条,改完之后这个读解就是店员的,AI 不会覆盖回去。"需求室"把每一次搜索按意思归类:书架答不上来的,列成一张进货清单;读者要的情绪,对上书架实际有的情绪。

谁掏钱?独立书店。它们本来就靠推荐卖书,Subtext 把这套对话搬到线上,外加一份每月一次的"读者想要什么"报告。目录里更多书能被找到,不知道书名的读者也能摸到一本,护封页最后落在预约上;进货清单是读者写的;编目这活儿还是店员说了算,新书到店就被读一遍,改过的读解留着。

因为跑在店里本来就有的电脑上,每次搜索没有成本,所以定价可以是一口价订阅,包安装、更新和支持。举个数,不是价目表:如果一家店每本 60 雷亚尔的书能留下约三分之一,150 雷亚尔的月费靠每月多卖八本书——一周两本——就回本了。

技术栈是 Next.js、SQLite 和 Ollama,全部跑在书店自己的机器上,两个开放权重的 Gemma 模型分工干活。gemma4:e4b 在后台把每本书读一遍,对每段简介返回按显著程度排序的氛围(从固定的十二种里选,比如忧郁、安心、反讽、明亮)、节奏、密度、声音和主题,以及支撑每一条的原文段落。在一台 M4 芯片、16 GB 内存的 Mac 上,前 200 本书花了 47 分钟,一次四个。

embeddinggemma 负责应答。请求先被向量化,和书比对;然后书架再读一遍请求本身("忧郁,但要安心""别太长""像 Clarice Lispector 那种"),按每本书的读解重新排序。一个新请求大约 30 毫秒出结果(十一次的中位数),模型 621 MB。置信度决定显示什么:最佳匹配很弱时,书架少摆几本并说明匹配是松的;什么都不接近时,它拒绝回答。

那它到底找得更准吗?同一批 31 个请求,分别喂给三个引擎:关键词搜索(SQLite FTS5)、对简介做朴素向量、以及这个书架。第三个 Gemma 模型 gemma4:12b 给每个引擎的前五个答案打 0 到 2 分。24 个请求被评分,另外 7 个问的是书店答不了的东西,那里只检查书架会不会拒绝。

老实说:书架明显赢过关键词(24 个请求里 15 个更好,6 个更差),和朴素向量总体打平(9 好 8 差,差距小于一个请求能撬动的均值)。它领先的地方正是这个项目的意义所在——情绪类请求(0.86 对 0.62),以及带"但是"的请求。请求里点名作者时它落后于朴素向量(0.56 对 0.70)。7 个超范围请求,它全部拒绝了。评委是模型不是读者,所以这些数字只是线索;柜台里有个搜索评测室,每条查询和它的三份列表都在,谁都能自己核。

对一家书店来说,开放模型不是哲学选择,是这东西能不能成立的前提。

  • 读者的请求是书店最值钱的数据,它留在店里。"需求室"完全建立在人们问了什么之上。换成闭源 API,每一条请求都得离开书店,去别人的服务器上处理。这里,请求、目录条目、笔记都不出这台电脑,搜索记录保留 90 天,不留是谁搜的。
  • 没有按次计费。书店没法给一个产品定价,而成本随读者逛多少往上涨。应答一个请求需要本地跑一个 621 MB 的向量模型,一次搜索的边际成本是零,订阅才能是一口价。
  • AI 可以被核对,而不只是被信任。每条读解都引用它依据的段落,每个答案都列出理由。店员不同意就改,改的算数。这能成立,是因为整条流水线——提示词、分类体系、重排和阈值——都在自己手里。
  • 没有锁定。不要账号、不要 API key、不要供应商。换模型改两个环境变量,出了更好的开放模型,书店可以搬过去。

开放比闭源好在哪,有个具体的例子:读解这一步要求模型用简介里的段落为自己的判断举证,应用只在段落逐字出现在简介里时才保留它;被验证的段落越少,这条读解的置信度越低。因为模型跑在本地,改完提示词或换完模型,重读整个目录就是一条命令(npm run ai:enrich -- --all),除了时间不花别的。

演示目录里有 277 本真书,价格和库存是演示用的,另有 21 条示例店员笔记。"需求室"里的搜索是拿真实书架跑出来的示例请求,界面上都标了。