A minimal open-source architecture and reference implementation for building an AI reading companion that avoids hallucination, spoilers, and forgetfulness through real text windows, chapter digests, persistent discussion history, and in-page annotations.

Stars

35

7-day growth

No data

Forks

6

Open issues

0

License

MIT

Last updated

2026-07-05

AI repository intelligence
FR-AI / ANALYSIS

Why it is worth attention

It directly addresses three core pitfalls of AI reading assistants—fabricating book content, spoiling unread chapters, and losing conversational context—with practical, battle-tested design patterns and a detailed list of real-world issues encountered during development.

Who it is for

  • Developers building custom AI reading companions
  • Product designers focused on AI-assisted reading or study tools
  • Self-hosted AI enthusiasts who want privacy and control over their reading assistant
  • Researchers exploring LLM-based human-AI collaborative reading experiences

Use cases

  • Co-reading an ebook with an LLM that discusses only the current passage and remembers past discussions
  • Adding persistent, verified annotations to book pages from AI conversations
  • Building a reading journal that tracks discussion history across sessions and chapters
  • Integrating reading context into a personal AI assistant's memory system

Strengths

  • Eliminates hallucination by feeding the actual text window (selected sentence ±300 chars) with an instruction to discuss only that passage
  • Prevents spoilers by generating chapter summaries lazily only up to the reader's current progress
  • Preserves discussion continuity via SQLite storage and time-aware prompts that distinguish past from present
  • Enables bidirectional annotation: AI marks lines, and readers can click any annotation to continue the discussion

Considerations

  • No ongoing maintenance – shared as-is, with no commitment to respond to issues or pull requests
  • Requires self-deployment and configuration of an LLM endpoint (API key, model, base URL)
  • Multi-user support and permission management are not implemented; the code is a skeleton meant to be extended

README quick start

coread — 和你的 AI 一起读一本书

一个可以跑起来的最小实现 + 一份思路笔记:怎么让 AI 陪你真的共读一本书—— 不编造书里的内容、不剧透你没读到的章节、记得你们聊过什么、还能在页面上留下它的画线批注。

这不是一个「产品」,是一份参考架构。核心价值在下面的设计思路防踩坑清单; 代码是能跑的骨架,人设、记忆系统、部署方式都留了接口让你换成自己的。

维护预期:本仓按现状(as is)分享,各自 clone 各自部署,不承诺维护—— issue 和 PR 随缘看,不回复不代表冒犯。它的使命在你读完 README 那一刻就完成了。

跑起来

npm install
LLM_BASE_URL=https://api.openai.com/v1 \
LLM_API_KEY=sk-xxx \
LLM_MODEL=你的模型 \
node server.js
# 打开 http://localhost:3900/reading 导入一本 epub(请用公版书或你有权使用的书)

任何 OpenAI 兼容端点都行(OpenRouter / DeepSeek / vLLM / Ollama…)。 可选 DIGEST_MODEL 指定一个便宜模型专门做章节摘要。 人设写在 persona.md(已 gitignore,参考 persona.md.example)。

设计思路:为什么这样做

1. AI 没有「读过」这本书——每一轮都要喂真字。 哪怕模型训练时见过这本书,靠印象聊书必然编造细节。解法是每轮把读者正读的真实原文窗口 (选中句 ±300 字)放进上下文,并明确指令「就这段文字本身聊,绝不凭印象补充」。 这是整个系统的地基,做不好其他都白搭。(lib/prompt.js: passageWindow

2. 「读过全书」用章节摘要索引伪造,而且只造到读者的进度为止。 原文窗口只解决"这一段",聊到前情就抓瞎。给每章用便宜模型生成 ≤120 字脉络摘要(懒生成, 读者翻到哪补到哪),聊天时注入到当前章为止的全部摘要。截止线同时就是防剧透线—— AI"只读到"读者读到的地方,后面的它真的不知道,想剧透都没材料。(lib/digest.js

3. 讨论历史落库,服务端无状态。 聊天历史存 SQLite(按书为单位,讨论是一条连续的河,不按章节切断),每轮从库里取最近 N 条重建 messages。刷新页面、换章节、服务重启,讨论都续得上。用户消息在生成之前落库—— 生成失败也留痕。

4. 时间感知。 共读历史是以周为单位延续的。system prompt 里每轮带当前时间;距上次讨论超过几小时, 再补一句「距上次聊这本书已过去约 N 天——之前的讨论都是那时候的事」。不然模型会把 三天前的讨论当成十分钟前。(lib/prompt.js: timeAnchor

5. AI 的批注留在页面上。 聊到好句子,AI 可以在回复末尾用 [批注:原文片段|批注内容] 标记留一条画线批注—— 服务端摘出标记、校验原文片段真是本章子串才入库(防编造原文),从回复里剥掉标记。 读者重开这一章,能看见 AI 留下的线。反过来,点开任何一条已有批注也能「聊这条」。 共读因此有了双向的、留得下来的痕迹,而不是聊完就散。

6. 记忆回写:共读不是孤岛。 如果你的 AI 还有其他会话入口(日常聊天/其他界面),读者合上书出去说「我刚读完那章」, 那边的 AI 应该接得住。两层做法(lib/memory.js,默认留接口不实现):

  • 长期:每轮共读写一条带 reading/书名 tag 的轻记忆进你自己的记忆系统,靠语义检索被动浮现;
  • 短期:最近 3 小时读过书 → 给其他会话注入一行「TA 刚在读《X》到第 N 章,聊了…」, 过窗自动消失。一句 SQL 的事,却是「余温」的关键。

防踩坑清单(每一条都是真踩过的)

  1. 长回复被中间层掐断。 浏览器和 LLM 之间隔着任何反代/隧道/CDN(Cloudflare 免费层 100s), 长生成就会被当死请求掐掉。唯一正解是 SSE 全链路 + 心跳:让字节一直在流。 事后打捞(从服务端历史里把生成完的回复捞回来补投)只配当保险丝。
  2. SSE 清理挂错事件。 下游心跳定时器的清理要挂 res.on('close')不是 req—— POST 的 req 读完 body 就触发 close,挂错位置心跳当场被拆,还极难排查。
  3. UTF-8 被网络分块切碎。 收响应体写 data += chunk 等于把 Buffer 隐式转字符串, 一个中文字 3 字节被切在块边界就变 。流式用 StringDecoder/TextDecoder({stream:true}), 非流式 Buffer.concat 攒齐再解码。回复越长越容易撞上,而共读回复恰恰都长。
  4. SSE 手工分帧。 帧以空行(\n\n)分隔,但网络包不管帧边界——必须自己攒缓冲区再切, 半截 JSON 要静默跳过等下一块。
  5. 别把浏览器 JS 塞进服务端模板字符串。 正则和 \n 要转义两层(\\n 才能吐出 \n), 一处写错就是静默的前端 bug。浏览器代码放独立静态文件。
  6. 懒生成必须「失败即停」。 章节摘

Description

Read a book together with your AI — grounded in the real text, spoiler-safe, with shared margin notes. 和你的 AI 真正共读一本书。

Related repositories

Similar projects matched by category, topics, and programming language.

react
Featured
react GitHub avatar

react

React is a JavaScript library for building user interfaces with declarative, component-based, and cross-platform design.

Web DevelopmentFrontend Frameworks
246,741
tandpfun
Featured
tandpfun GitHub avatar

wardrobe

A GPT‑image powered app that extracts individual garment cutouts from photos, organizes them into a digital wardrobe, and generates modeled outfit previews.

JavaScript
1,570
mshumer
Featured
mshumer GitHub avatar

Claude-of-Duty

A browser-based first-person shooter built entirely with procedural generation and orchestrated AI agents, featuring 55k lines of Three.js/WebGL2 code and no art assets.

AI & Machine LearningAI Agents
1,234