Goink v1.1.1 技术架构深度拆解:国产大模型如何驱动一个真正的 AI 长篇写作桌面应用
Goink v1.1.1 技术架构深度拆解国产大模型如何驱动一个真正的 AI 长篇写作桌面应用7 个国产大模型内置模板 ONNX 本地语义搜索 自研 ReAct Agent 引擎 33 个 MCP 工具60MB 安装包开箱即用。本文从代码层面拆解 Goink 的核心架构设计。写在前面为什么通用 AI 聊天写不了长篇小说如果你用 ChatGPT 或 DeepSeek 聊过天你会觉得 AI 很聪明。但如果你试着让它帮你写一篇十万字的小说——写到第五章它忘了主角在第三章养的那只猫叫什么。你手动翻了几百条聊天记录把角色设定重新贴给它。第十章时它又搞混了两个配角的师徒关系。你设的伏笔散落在对话的犄角旮旯里别说 AI 记不住你自己都快忘了。核心矛盾LLM 的单次上下文窗口再大百万 token 已经很常见也无法替代一个持久化的、结构化的、可自动维护的创作状态数据库。这是通用聊天产品的根本天花板——它们把一切都塞进对话流里而长篇创作需要的是一套区别于对话流的独立状态管理。Goink 是我为了解决这个问题写的。它不是一个套壳的 AI 聊天客户端而是一个专门为长篇小说创作设计的桌面 AI 系统核心技术目标就一个让 AI 在写作过程中自动追踪、维护、查证所有创作状态不让作者操这个心。这篇文章会从技术视角拆解 Goink 的架构设计重点放在如何将国产大模型的能力深度嵌入一个具体的创作场景以及在这个过程中踩过的坑和做出的关键决策。一、整体架构三层分离各司其职┌─────────────────────────────────────────┐ │ React 19 前端 │ │ 对话 UI Monaco 编辑器 G6 图可视化 │ │ 六套可视化面板 Diff 审批 │ ├─────────────────────────────────────────┤ │ Wails v2 绑定层 (app/) │ │ Go 方法 → 前端 API │ ├─────────────────────────────────────────┤ │ Go 后端 (internal/) │ │ ReAct Agent 引擎 33 MCP 工具 │ │ 六维记忆数据库 Git 版本管理 │ │ ONNX Runtime 本地推理 │ ├─────────────────────────────────────────┤ │ SQLite sqlite-vec 向量索引 │ │ Git 文件存储 │ └─────────────────────────────────────────┘选择 Wails 而不是 Electron 的原因很明确安装包体积。Electron 的 Chromium 运行时动辄上百 MB而 Wails 利用系统自带的 WebViewGoink 的跨平台安装包不到 60MB。Go 做后端也带来了一个关键优势编译为原生二进制无运行时依赖。用户不需要装 Python、Node.js、数据库或任何环境一个 exe/dmg/AppImage 打开即用。二、Agent 引擎设计ReAct 循环 工具自主决策Goink 的 Agent 引擎没有使用 LangChain 或任何第三方框架而是在 Go 中自研了一个 ReActReasoning Acting循环。核心流程用户输入 → LLM 流式推理 → 解析 tool_calls → 执行工具 ↑ ↓ └──────────── 工具结果追加回消息列表 ←────────────────┘ (循环直到 LLM 不再调用工具)关键设计决策事件驱动的流式推送Agent 的每一步状态变化思考过程、内容生成、工具调用开始/执行中/完成/失败、Token 用量都通过 WailsEventsEmit实时推送到前端。用户看到的不只是AI 在打字而是能看到 AI 正在查什么资料、调用什么工具、思考什么——整个过程对用户透明。安全机制死循环检测最近 4 轮工具调用 ≤2 种模式且全为只读时触发、连续 3 次系统异常自动禁用工具、最大 50 轮工具调用限制——这些是实战中遇到频繁工具调用死循环后逐步加入的。子 Agent 系统主 Agent 可以启动子 Agent 处理特定任务审稿 Agent 四维检查、记忆 Agent 多维度检索子 Agent 运行在独立上下文环境中不污染主对话。子 Agent 和主 Agent 复用同一个Run()方法差异仅在于 RunOptions 中的 AgentType、ParentTurnID 和 AllowedTools。主流程代码片段简化func(a*Agent)Run(ctx context.Context,msgs[]Message,opts RunOptions)error{forturn:0;turnmaxTurns;turn{// 1. 注入系统提示词 小说状态快照 Skill 常驻注入systemMsgs:a.buildSystemMessages(opts)// 2. 调用 LLM 流式接口stream,err:a.client.CreateChatCompletionStream(ctx,allMsgs)// 3. 解析响应文本内容 vs 工具调用content,toolCalls:a.processStream(stream)// 4. 无工具调用 → 本轮结束iflen(toolCalls)0{break}// 5. 并行执行工具调用results:a.executeTools(ctx,toolCalls)// 6. 安全检测死循环、异常率ifa.detectLoop(recentTurns){break}// 7. 结果追加回消息列表继续循环allMsgsappend(allMsgs,results...)}}三、国产大模型全链路落地7 个 Provider 内置模板这是 Goink 在模型集成方面最有价值的部分——如何在一个桌面应用中实现对多个国产大模型的统一接入和智能调度。当前内置的 7 个 ProviderProvider代表模型上下文窗口输出上限ThinkingVisionDeepSeekdeepseek-v4-pro1M384K✅ high/max-豆包(火山引擎)doubao-seed-2-1-pro256K256K✅✅Qwen(通义千问)qwen3.7-max1M64K✅✅GLM(智谱)glm-5.1200K128K✅-MiniMaxMiniMax-M31M128K✅✅MiMo(小米)mimo-v2.5-pro1M128K✅-Kimi(月之暗面)kimi-k2.7-code262K128K✅✅统一抽象层设计所有 Provider 通过 OpenAI 兼容 API 接入但每个有自己的毛刺需要处理Qwen阿里云 DashScope需要自定义请求构造阿里云的 API 格式有细微差异BuildRequest钩子MiniMax同样的 OpenAI 兼容但请求体格式不完全一致BuildRequest钩子MiMo小米需要自定义请求头BuildHeaders钩子豆包火山引擎标准 OpenAI 格式但 endpoint 路径不同解决方案是在llm/client.go中设计了 Provider 级别的拦截器链typeProviderstruct{IDstringNamestringChatURLstringModels[]Model BuildRequestfunc(*http.Request,*ChatRequest)// 可选钩子BuildHeadersfunc(*http.Request)// 可选钩子}用户在前端界面选择 Provider → 填入 API Key → 自动发现可用模型列表 → 一键切换。底层 Provider 的差异对用户完全透明。为什么这是有意义的很多AI 写作工具其实只是内置了一个模型通常是 GPT的 API Key你换不了模型。Goink 的设计理念是模型是基础设施用户应该有选择权。不同模型在中文文学创作上的表现确实有差异——有的擅长对话描写有的擅长情节推进有的便宜适合打草稿。7 个 Provider 内置模板 自定义 Provider 支持让用户可以根据自己的预算和效果偏好灵活组合。更重要的是这 7 个 Provider全部是国产品牌。在当前的 AI 应用格局下这意味着用户不需要任何海外支付方式就能用好用的 AI 写作工具。四、本地语义搜索将国产 NLP 模型打包进桌面应用Goink 的 RAG 系统是另一个展示模型落地的典型案例。技术选型嵌入模型BAAI 的bge-small-zh-v1.5int8 量化版512 维向量推理引擎ONNX Runtime通过 Go 绑定github.com/yalue/onnxruntime_go调用向量存储sqlite-vec创建虚拟表vec_novel_{id}cosine 距离度量分块策略BERT WordPiece Tokenizer420 tokens/块50 token 重叠段落边界优先切分重排序MMR最大边际相关性λ0.7兼顾相关性和多样性全链路流程图源文本 → WordPiece 分词 → 段落/句子边界切块 → ONNX 推理 → CLS Pooling L2 归一化 → sqlite-vec 写入 → 向量索引 ↓ 用户查询 → BGE 指令前缀 → ONNX 推理 → 向量搜索 → MMR 重排 → 结果BGE 指令前缀的设计很关键查询时加前缀文档不加。这是 BGE 模型的设计约定——为这个句子生成表示以用于检索相关文章只在查询侧追加告诉模型这是一个问题请用检索模式编码。全局单例 异步加载ONNX Runtime 的初始化在桌面应用中有个特殊问题加载模型和运行时库可能需要几秒钟如果同步加载会阻塞 GUI 渲染。Goink 的解决方案var(embedder*OnnxEmbedder embedderOnce sync.Once embedderChmake(chanstruct{}))funcInitEmbedder(){embedderOnce.Do(func(){gofunc(){embedderloadEmbedder()close(embedderCh)// 初始化完成广播信号}()})}funcGetEmbedder()*OnnxEmbedder{-embedderCh// 阻塞等待初始化完成returnembedder}GUI 先渲染ONNX 在后台异步加载。等加载完成后界面的搜索功能自动变为可用。用户感知不到加载过程。离线运行的价值整个语义搜索链路完全在用户本机完成——不需要调用任何 embedding API不需要联网。这对目标用户写作者意味着隐私你的小说内容永远不会离开你的电脑去做向量化零成本不按 token 计费搜索多少次都行离线可用没有网络照样搜BGE 模型约 100MBint8 量化后推理时 CPU 即可不需要 GPU。在现代 CPU 上单次查询的 embedding 耗时在毫秒级。五、为什么需要结构化记忆而不只是更大的上下文百万 token 上下文窗口已经很普及了。但你试试把一部几十万字的小说的全部章节、角色设定、伏笔列表、地点描述、读者认知状态全部塞进 system prompt 里——LLM 对长文本的中间部分的注意力天然衰减而且每次调用都要为这几十万 token 付费。Goink 的解决方案是结构化记忆 按需检索而不是全量注入记忆维度存储结构检索策略角色档案 有向关系图AI 主动查数据库伏笔目标章节 重要度 状态按回收章节排序故事弧线节点链 进度状态按关联章节查询地点包含树 连通图按名称/层级查询读者认知已知/悬念/误解按状态过滤创作偏好全局 单书两层语义搜索匹配AI 写作时不是一次性读完所有资料而是像人类作者一样需要什么查什么——需要确认角色关系时调search_characters需要找之前埋的伏笔时调list_timeline需要确认某段前文时调search_story_memory语义搜索。这 33 个 MCP 工具就是 AI 的手脚让它能自主地在创作过程中维护状态而不是靠用户手动贴设定。写完自动维护三层保障写完整章内容后AI 并不只是把文本写入文件就完事。系统分三层确保状态被正确更新System Prompt 指令系统提示词中明确要求 AI 在写完后检查角色变化、伏笔回收、弧线推进动态状态注入每轮对话开头将当前小说状态快照角色列表摘要、待回收伏笔、弧线进度注入系统消息审稿子 Agent独立的 review Agent用四维标准角色一致性、伏笔回收、弧线推进、读者认知扫描新章发现问题后输出修正建议三层设置的原因是单靠 system prompt 不够可靠LLM 可能忽略单靠状态注入不够全面只有摘要信息单靠子 Agent 太慢。三层叠加后状态遗漏率大幅降低。六、Git 版本管理 Diff 审批让 AI 的修改可控这是另一个从实战中长出来的需求。早期版本中AI 可以直接修改章节文件。后果是有时候改对了有时候偷偷改坏了你不知道。于是加入了Diff 审批机制AI 生成修改时不是直接写入而是生成 search/replace 操作后端对目标文件执行替换生成 Git Diff前端展示 Diff 视图逐行对比用户点批准才实际 commit用户点拒绝则 revert文件恢复原样全部文件操作都被 Git 追踪。每次对话结束自动 commit随时可以git log查看历史或git revert回退到任意版本。每次对话自动 commit → 随时回退写作者的无限撤销。七、实际运行数据项目自 2026 年 6 月初开源到目前7 月中旬在 GitHub 上获得了99 Stars和17 Forks。最新版本 v1.1.1 的跨平台安装包累计下载超过 300 次Windows 为主力平台占 80%。技术栈Go 1.25 Wails v2.12 React 19 TypeScript 6 Tailwind CSS 4 SQLite ONNX Runtime 1.26代码规模约 40,000 行 Go 22,000 行 TypeScript/TSX八、总结从模型到产品的最后一公里Goink 做的事情本质上不复杂——把国产大模型的能力封装进一个桌面应用让写作者不用关心 prompt engineering、不用手动维护设定、不用在几十万字的聊天记录里翻找信息。但从技术实现角度看把这件事做好确实需要解决一系列工程问题Agent 循环的可靠性防止死循环、处理异常、流式推送状态多 Provider 的兼容性每个国产模型的 API 都有自己的 quirks本地推理的集成ONNX Runtime 的 Go 绑定、异步加载、分词器实现结构化记忆的持久化六维数据的 schema 设计、append-only 历史追踪写入安全的保障Diff 审批、Git 回退、沙箱隔离如果你也在做大模型 桌面应用方向的产品或者对 Agent 架构设计感兴趣欢迎来 GitHub 交流github.com/sigpanic/goink。项目使用 AGPL v3 开源Skill 方法论仓库 goink-skills 也欢迎社区贡献写作方法论。本文基于 Goink v1.1.1 编写。如果你也在做 AI 桌面应用或对 Agent 架构感兴趣欢迎在评论区交流也欢迎来 GitHub 点个 Star。