作者打造開源桌面 AI 小説寫作系統 Goink,管理長篇故事記憶
I built a desktop AI novel-writing system because ChatGPT forgets my protagonist by chapter five
作者打造並開源桌面 AI 小説寫作系統 Goink,以結構化記憶和可自行維護狀態的智能體管理長篇故事。系統追蹤角色、伏筆、故事弧線、地點、讀者知情狀態和寫作偏好,並提供 31 項結構化工具及本地語義搜尋。每項修改先以 diff 供用戶審批,接受後提交至每本小説的 Git 儲存庫;安裝檔不足 60MB,支援 Windows/macOS/Linux,除已設定的模型呼叫外可完全離線運行。
If you have ever tried to write a long story with a general-purpose AI, you know the pattern. By chapter five it has forgotten the protagonist's name. By chapter thirty you are manually scrolling through earlier chapters looking for that one foreshadowing line. After every chapter you have to remind it, in your own words, to update character state, check arc progress, and keep the timeline straight.
At some point you stop being a writer and become a context manager.
I wanted to fix that, so I built Goink — an open-source desktop AI writing system with structured memory, an agent that maintains its own state, local semantic search, and Git version control. It is built with Wails (Go + React), ships as a single installer under 60MB, and runs entirely offline apart from the model calls you configure.
This post is about the design decisions, what worked, and what I would do differently.
Why a chat window is the wrong abstraction
A chat window gives you a flat transcript. A novel needs a state machine: who exists, who knows what, which promises have been made to the reader, which ones have been paid off.
Prompt engineering can fake this for a while. You paste a "story bible" into the system prompt, it works for ten chapters, and then the context window fills up and the model starts drifting. Compression makes it worse, because the thing you compress away is often the detail that mattered.
So the core decision was: move state out of the prompt and into a database.
Six dimensions of structured memory
Goink tracks six kinds of creative state, each with its own schema and editing tools:
- Characters — profiles plus a directed relationship graph. "A sees B as a mentor but secretly distrusts him" and "B respects A but hides something" are two separate records. Relationship changes are append-only, so the history is preserved.
- Foreshadowing — every entry has a target resolution chapter and an importance level. The system flags items approaching their deadline and treats overdue ones as anomalies.
- Story arcs — node chains spanning chapters. Nodes advance as chapters are completed. A typical book runs three to five arcs in parallel.
- Locations — a real graph: containment hierarchy (kingdom → palace → great hall) plus undirected spatial edges (two places connected by a mountain path).
- Reader knowledge — what the reader knows, what they are waiting to learn, and what they currently misunderstand. This is how you control suspense.
- Writing preferences — global and per-novel. The instruction "keep dialogue cold and restrained" still applies at chapter thirty-seven without being repeated.
The important part is not the tables. It is that the model can query and mutate them through tools.
An agent, not a pipeline
Most AI writing tools are pipelines: outline → draft → polish, with a human shuttling text between stages. Goink exposes 31 structured tools and lets the model decide which to call, in what order, with what arguments.
While writing a chapter it can look up a character, search earlier chapters for a detail, read an outline, draft, and then update state — all inside one conversation. There is no "handoff" where context is lost.
After a long piece is written, the system injects a maintenance checklist into the conversation: did any character change, were any foreshadowing items resolved, do arc nodes need to advance, does reader knowledge need updating. This is deliberately not optional. Maintenance that depends on the user remembering to ask for it does not happen.
If that is still not enough, you can launch a review sub-agent: an independent agent that compares the chapter against the database from scratch and reports inconsistencies into the main conversation, without polluting the main context.
Semantic search that runs locally
Finding "where did the protagonist first see that pendant" across 500,000 words is not a keyword problem. The relevant passage might never use the word "pendant".
Goink runs a BGE Chinese semantic model through ONNX Runtime locally, stores embeddings in a sqlite-vec virtual table, and uses MMR re-ranking to avoid returning ten near-identical passages. Indexing is incremental and happens in the background after each chapter. No network, no external vector database, no extra service to install.
This was the single biggest quality jump. The model stops hallucinating earlier events when it can actually retrieve them.
Diff approval and Git history
The AI never writes to the manuscript directly. Every edit produces a diff first, and you approve, reject, or ask for a revision. There is an auto mode for continuous drafting, but the default is review-first.
Every accepted change is committed to a per-novel Git repository. You can inspect history and roll back to any state. Chapters live as chapters/NNN.md, outlines as outlines/NNN.md, so the data is plain text and portable — no proprietary format lock-in.
There is also a two-layer sandbox: a regex whitelist restricts writes to known paths, and path traversal is blocked. Files are re-read and compared before writing so manual edits are not silently overwritten.
Skills as markdown, not plugins
Writing methodology lives in .md files with YAML frontmatter. Three layers (built-in, user, per-novel) with override priority, three activation modes (auto, manual slash command, always-on), all hot-reloaded without restarting the app.
There is also a style distillation tool: paste a sample, and the model analyzes sentence structure, word choice, rhetorical devices, pacing, narrative distance, and tone, then generates an imitation skill you can edit by hand.
The design goal was zero-code extensibility. If you can write a markdown file, you can extend the system.
Stack and numbers
- Wails v2 (Go 1.25) + React 19 + TypeScript + Tailwind 4
- SQLite for structured state, sqlite-vec for vectors, Git for file history
- ONNX Runtime with an int8 BGE model for embeddings
- 31 MCP-style tools, 7 built-in LLM provider presets (DeepSeek, GLM, MiniMax, Kimi, Qwen, Doubao, and OpenAI-compatible endpoints)
- Installer under 60MB, Windows/macOS/Linux, AGPL v3
What I would do differently
Tool count is a budget. Thirty-one tools is manageable for a strong model and confusing for a weak one. Tool descriptions matter more than tool count, and a badly named parameter will be misused no matter how good the prompt is.
Append-only state is the right default. Early versions overwrote relationship records and lost the evolution. Keeping history and marking one row as current made both the UI and the agent simpler.
Local-first is a feature, not a compromise. Users care that their manuscript is plain markdown in a Git repo they own. It also removed an entire class of sync bugs.
Approval gates are not friction. Users expected the AI to edit directly and were surprised by the diff step — then almost all of them kept it on.
Try it and tell me what breaks
Goink is open source: github.com/sigpanic/goink. Downloads are on the Releases page. The README has the full feature breakdown and screenshots.
I am especially interested in feedback from people writing long fiction in English, since the bundled semantic model is Chinese-first and the prompt layer assumes Chinese web-novel conventions. If you write in English and want to test it, open an issue and tell me what feels wrong — that is the most useful thing you can do right now.
來源:DEV Community · dev.to