开篇:别把大模型当黑盒,它也会“幻觉”
你是不是也遇到过这种情况:在编辑器里接了个 AI 助手,满心欢喜地让它帮你润色或写代码,结果它一本正经地给你塞了一堆“幻觉”内容,还顺手把你辛辛苦苦写的文档给覆盖了?
这十来天,我没铺新摊子,全在忙一件事:把大模型接进 NoteLume 编辑器。侧栏塞个网页最省事,但那样它连你在写啥都不知道。现在它不仅能读当前文档、翻库,还能在允许后把内容写回去。但在这个过程中,我发现了一个反直觉的真相:小模型不能硬上 Agent。这篇文章,我就把这段“踩坑与重构”的血泪史分享出来,顺便聊聊这版更新里那些硬核的技术细节。
选区浮窗与 Rust 侧的 HTTP 引擎
先说 AI 落在哪儿。选区浮窗第二排多了五个按钮:润色、续写、翻译、摘要、自定义。右边栏也多了一块面板,可以跟检查器上下同开,各记各的开合状态。对话默认带上当前文档正文(截前 6000 字);开了库还能全库检索,命中最多 5 篇、每篇 400 字,总量再封 6000。
接口走 OpenAI 兼容的 /chat/completions,DeepSeek、GLM、Kimi、OpenAI 和本机 Ollama 共用一套设置。HTTP 与 SSE 都放在 Rust 侧,前端只管传参和收流。这么分是因为 WebView 里直接发跨域请求会被 CORS 挡掉,再说 API Key 也不该出现在前端代码里。请求随时能取消,已经出来的半截回答留在面板里,可以接着往下问。
避坑指南:模型名千万别手抄! 接口地址填好(本机 Ollama 连 Key 都不用填),设置页会自己去拉 /models,先把向量、重排、文生图、语音这些非对话模型摘掉。聚合服务常把它们和对话模型混在一张表里,按名字排序取第一个,很容易就填成 BAAI/bge-m3,然后发请求回一个 400「Model does not exist」,看着特别像接口没连上——这个坑我自己踩过。
Agent 写回机制:走 ProseMirror 事务,支持 Ctrl+Z
Ask 模式就是纯问答。Agent 多了一条回路,模型可以调五个只读工具:全库检索、按路径读一篇、列出所有笔记、读当前文档、按标签查。循环跑在前端,Rust 只把 tools 透传出去,再按 index 把流式回来的 tool_calls 攒起来。工具执行走的是已有 IPC,没有为它新增写盘命令。
写回只在回答完整结束之后发生:autoApply → appendToDoc → 一条 ProseMirror 事务。走编辑器自己的事务,Ctrl+Z 就能撤。插在哪儿优先听模型的,提示词让它在首行给一个 <insert after|before="文档里已有的标题或段落">,剥掉标记后按标题全等、互相包含、段落前缀三级去匹配。匹配不上就退回光标位置,气泡里会明说「没找到,已改插光标处」。
每次自动写入还会记一条回退记录 {from, to, md, prev}。这里有个容易搞错的地方:md 存的是那一段的序列化产物,不是 Markdown 原文——插进去的是解析后的节点,字符数根本对不上。撤销时先把当前文档同一区间重新序列化,跟记录比对,你要是手动改过那段就拒绝撤销,免得把别的改动一起抹掉。记录存在 localStorage 里,重开面板、重启应用,消息下面那条「撤销上次 AI 自动写入」还在。
限制也比较死:一次任务只留最后一条可撤记录,前面几段只能靠 Ctrl+Z;没有 diff 预览,写完了才看得见;五个工具全是只读,改不了别的文件,删除能力压根没给。
核心反思:小模型不能硬上 Agent
这段是我这版最想记下来的,因为一开始走弯路了。
早期 tools 对所有模型照发。小参数本地模型跑这条链,会出现参数幻觉、该调不调、拿同一组参数死循环重试,而 autoApply 会把这种回答照写进文档。后来发现能力其实不用猜:Ollama 的 /api/tags 每个模型都带 capabilities,qwen3:1.7b 是 completion/tools/thinking,tinyllama:latest 只有 completion。
判定现在是三态:支持、明确不支持、拿不到信号。远程服务不返 capabilities、旧版 Ollama、模型名不在列表里,都算拿不到信号,这时候照旧发 tools,猜错方向比不猜更糟。只有明确不支持才降成「建议模式」:不发 tools,系统提示换成「你查不了库,只给建议,不要声称已查过或已写入」,不自动落盘,「插入到文档」这个钮留着,要不要写由你自己按。设置里还有一档 auto / on / off,自建网关被误判了能强制改回来。得说明白,这条判定链是打桩验的,真机拿 tinyllama、qwen3 各跑完整一轮 Agent 我还没做。
本机 Ollama 的探测拆成四档:能连且有模型、能连但零模型、装了没起、压根没装。零模型那档最坑,服务跑着、地址填对、模型名也填了,发出去只回一个错,看不出哪儿不对。现在确认之后会直接开「AI 设置」拉一个默认模型,进度在面板里滚,拉完自动重拉模型列表。
设置页里还有一节「数据流向」,把每次请求会带出去什么列清楚:当前文档前 6000 字、检索命中、勾选技能的 SKILL.md 全文、附图、全部历史;Agent 模式换成工具结果逐轮回传、单条截 8000。地址是本机回环就显示「不出网络」,否则显示「会经网络发往这个服务」。文案里的数字没有写死,都从代码里生效的常量取,以后改截断值,说明会跟着变。
性能重构:超大文件不再一刀切退源码
超过 2MB 以前一律退源码模式。ProseMirror 要给整篇文档维护 DOM,10MB 铺一遍能卡上几分钟,每敲一下还得全文强制布局。
这版换成了窗口化视图。内存里留着完整文档(master),EditorView 只绑顶层块的一个连续切片,首尾用不可编辑的 spacer 撑出真实滚动高度。编辑先经内核平移回灌 master,master 变了再按影响区间分流回窗口,跨界的直接整窗替换。撤销走 master 的历史,跨窗口操作因此是连续的。库内查找、折叠、行号、源码往返这些原先跟着整篇坐标走的东西,也都按 master 重接了一遍。
解析放在 Rust 侧,用 pulldown-cmark 走一个保守的 CommonMark + GFM 子集。碰到 [[双链]]、、脚注、HTML、callout 这类构造就返回「不支持」,前端退回 JS 解析。两套解析器的结果我不敢赌它一致,宁可多退一次;一致性另有一份 fixture 做三方对拍。
开关在设置页「功能开关」里,标了「实验」,默认关,下次打开生效。10MB 样例的首屏时长、滚动帧率、内存峰值目前只在单测和 jsdom 里验过,真机没跑过;Rust 不认的构造同样会退回源码模式。这两条没解决之前,不敢给它默认开。
体验对齐:代码块 Typora 手感与 Git 面板重做
段首连敲三个反引号就能成代码块,敲空格或回车都行,语言名进围栏 info;块里新起一行再敲三个反引号回车,就退出来。敲完反引号会弹语言联想,常用置顶、前缀过滤,js、ts、py、c++ 这些别名也在候选里,选中即转块。退格和 Delete 到代码块边界时一次删整块,不用先选中。bibtex 块现在渲染成参考文献条目,编号走 CSS counter,跟 Quarkdown 的求值产物长得一样,源文件仍是标准 GFM 代码块。
还修了两个老毛病:预处理指令打到半个词就变色;敲 #include 再按空格,整块会被清空。
Git 面板照着 VS Code 的源代码管理重做了一遍。提交框常驻在改动列表上方,草稿跨刷新保留,提交成功才清;每个文件行内可以放弃更改;历史改成图形化,带分支下拉,能检出旧提交,也能对某个提交做还原。推送撞上「当前分支没有上游」,确认后自动 git push -u;拉取没上游时也不再糊一串 git 原文,先试显式 refspec,拉不到才给引导。
另外修了个不算新功能、但一碰上就没法用的问题:仓库根在库的子目录里,以前 Git 面板整个瘫掉,现在从当前路径向上找仓库根。
零碎但实在的更新与未完成的遗憾
- 支持应用多开:摘掉了 single-instance 插件,第二次启动就是独立实例。代价也摆在那儿,settings.json 只有一份,两个实例同时改设置,后写的赢。
- 主程序图标:换成纸页加白色 N;.md / .canvas 有了自己的文件图标,装完在资源管理器里能认出来。.markdown 在 Win11 上还得手动「始终使用」选一次,这是系统默认应用状态,装包改不动。
- 菜单条重构:Win / Linux 的菜单条从一排平铺图标改成八格分类,分组和 mac 原生菜单同源。
- 全选按 Typora 分段:光标在表格、代码块、引用里时,第一次只选这一层,再按一次才到整篇。
- Bug 修复:表格整表选中后删不掉、从 AI 对话框复制内容粘贴带一堆垃圾、有序列表两位数序号被切掉首位,这几个都修了。
语音输入做了大半,最后收回去了。桌面版三条路都没跑通:mac 没配 ASR 模型时直接判不可用,Windows 的原生听写撞上语音隐私开关,还会静默自终,Linux 各有各的问题。状态机、Rust 侧录音、五语言词条都还在代码里,三个桌面平台的入口一起藏了。留一个点了没反应的麦克风按钮,不如先别给。纯网页预览里的 Chrome Web Speech 还能用。
这版到什么程度?欢迎来提 Issue
还是个人项目,安装包 8–9 MB 量级,Tauri + Rust,无后端。AI 是可选项,不配也能完全离线用;配了之后走哪条路、带出去什么,设置页里都写着。
代码 Apache-2.0 开源,仓库两个,国内国外哪个快用哪个: - GitHub:github.com/HackerSnail… - Gitee(国内镜像):gitee.com/hackerSnail… - 官网:hackersnail.github.io/NoteLume/
Windows / macOS / Linux 安装包都能在官网下到。
想试的话,建议从两件事入手:选中一段文字点「润色」,看结果怎么替换选区;再用 Agent 问一个只有你库里才有的问题,看它调工具,然后检查那条「撤销上次 AI 自动写入」。有问题往 Issue 提,我基本天天看。
顺带提一句,NoteLume 被 XERJ Corpus Hub 收录了,贡献者主页在 hub.xerj.org/contributors/HackerSnail。