本地桌面 AI 角色聊天:多接口、单聊/群聊、记忆、本地 RAG 与发图。
- 当前版本:3.4.0(安装包/便携包与
package.json/Cargo.toml/tauri.conf.json已对齐) - 当前发布:3.4.0 参数复验与依赖大扫除:没有新界面,全是拿真实环境把当初拍下的参数和判定表打回重做——智能编排的沉默阈值第一次用真实 embedding 量过并重标(
FLOW_RELATIVE_CUT0.6 → 0.725,此前等于没开这个功能)、视觉白名单两轮补漏(GLM-*V 与 Omni 两系、GPT-5 全族,漏判的后果是静默降级而不是报错)、Dependabot 8 个 PR 全部落地(含 vite 8 / TypeScript 7 / lucide 1 三个主版本)与安全公告对应的 h2 0.4.16。 - 版本脉络:1.9.0 一次性发布 v1.2–v1.9 全部功能;2.0.0 对话质量;2.1.0 规模化性能;3.0.0 对话质量二期与全项目审查修复;3.0.1 DeepSeek 一次性兼容迁移;3.1.0 会话状态与流式 DOM 重构;3.2.0 IM 视觉与交互收口;3.3.0 命名统一为 Links 与质量门禁扩容;3.3.1 自用一周收集到的四处缺陷修复;3.3.2 把 3.3.1 语言修复的另一半(已装数据)用迁移补齐;3.4.0 真实环境复验参数与判定表(智能编排阈值重标、视觉白名单两轮补漏)+依赖大扫除。详见
app/CHANGELOG.md。
-
智能编排语义参数待实机复验已于 2026-08-19 复验并重新校准:拿 SiliconFlow 免费的BAAI/bge-m3(1024 维)真实量了 8 个互不相交话题 × 2 段近况,语料构造方式与member_texts一致、请求形状与ai.rs::fetch_embeddings逐字一致。结论三条:①语义路径确实有区分度——同话题余弦中位 0.615、跨话题 0.421(对比 2026-08-13 词面 n-gram 的「两者中位都是 0」);②原来的 0.6 太松,16 个场景里只噤声了 13/40 个跑题成员,智能编排基本等于没开,已改为从真实分布推导的 0.725(噤声 25/40,且一个同话题成员都没被误伤);③绝对下限 0.15 对这个模型从不生效(bge-m3 的余弦下限约 0.28),保留它只是给分数尺度更低的模型兜底。两类分数是重叠的(同话题相对榜首最低 0.832 vs 跨话题最高 0.964),所以不存在零错误的阈值——0.725 是「同话题最低相对分往下留 10% 余量」推出来的,取向偏保守,因为让该说话的人闭麦比放跑题的人插句话更伤。判据措辞同步修正为「低于榜首 72.5% 或低于 0.15」(cut取两者较大值,此前文档写成「且」,与代码不符)。复验脚本app/scripts/verify-flow-semantics.mjs(Key 只从环境变量读、不落盘;常量直接从intelligence.rs解析,防止两边漂移):cd app && FLOW_EMBED_KEY=<你的 key> node scripts/verify-flow-semantics.mjs
-
视觉发图路径:应用侧已验完,还缺的只是一个有余额的视觉模型账号。2026-08-19 新增
app/scripts/verify-vision-image.mjs(当时叫.cjs,2026-08-22 六个真身脚本合并 CDP 驱动层时改成 ESM),在 3.3.2 真身上 21 项全过、不需要任何 Key:把接口指向一个单独进程起的、把请求原样录下来的本机端点,图片走真实#image-input→ 真实压缩 →save_chat_image→ 真实待发送区 → 真实#send,最后逐字节比对「image_url里发出去的字节」与「data/attachments里那张图」——两者相同,说明发出去的不是文件名也不是占位符。换成deepseek-ai/DeepSeek-V3.2后content退回纯字符串文字说明,整个请求里既无image_url也无图片 base64。测试图是脚本用自带 5×7 点阵字模现画的,不依赖系统字体,所以字节可判等。cd app node scripts/acceptance-harness.cjs --dir src-tauri/target/live-vision --phase full --keep --port 9243 --mock-port 40319 node scripts/verify-vision-image.mjs --port 9243剩下的是厂商侧「模型到底看不看得懂图」,这条只能靠外部账号:手上那个 SiliconFlow Key 的免费额度已经用光——11 个视觉模型、3 个对照文本模型连同
BAAI/bge-m3一律返回 HTTP 402 余额不足(code: 30001),/user/info三个余额字段都是"0"。别被 402 误导成「bge-m3 变收费了」:SiliconFlow 先卡账户余额、再看模型单价,余额为 0 时连 0 元的bge-m3/bge-large-zh-v1.5/bge-reranker-v2-m3和免费档聊天模型都一并拒掉,只有/models这类非推理端点还是 200——同一个 Key 当天上午用同一个模型跑完了 110 块 1024 维向量,变的是账户不是模型(上面那条 embedding 复验也因此不能原样复跑)。充值、换号,或本机装 Ollama 跑llava/qwen2.5-vl都能闭合,闭合动作只有一步:建一个视觉模型角色,发一张内容明确的图,确认回复真说出了图里的内容。 -
顺带修掉的真 bug(已随 3.4.0 发布):视觉白名单原先漏判 GLM-*V 与 Omni 两系(
zai-org/GLM-4.5V、Qwen/Qwen3-Omni-*的 id 里既没有vl也没有vision),选中这类真视觉模型发图会静默降级成文字说明。以后接新厂商的视觉模型,记得对着真实模型列表跑一遍model_supports_vision,别只读代码。 -
记忆去重判据待实测已于 2026-08-20 量过并换掉(未发布):和上面FLOW_RELATIVE_CUT同源的一例——字符二元 Jaccard(CONSOLIDATE_SIMILARITY = 0.82)结构上判不了「两条记忆说的是不是同一件事」。离线量了 12 对真实措辞:该合并的落在 0.111–0.444,不该合并的落在 0.250–0.636,前者整段套在后者区间里,没有能分开两者的阈值;最高分 0.636 还出自互相排斥的「生日是三月七号」⇄「生日是三月八号」,判据的排序本身是反的。所以 0.82 保留不动、职责改写成「只兜住逐字重复」,释义级重复改由角色自己的模型判一次分组(MERGE_JUDGE_PROMPT,温度 0、只回 JSON、拿不准回[];一次「整理」一次请求,手动点才发,成本封顶);判不到模型时退回只合并逐字重复,并在界面上明说,免得「合并 0 条」被读成「没有重复」。量表脚本纯离线、不需要 Key,已纳入verify:all:cd app && node scripts/verify-memory-merge.mjs
还没校准的是记忆衰减:半衰期 90 天、下限 0.2、归档线「超 30 天且有效分 < 0.15」这三个数仍然是拍的,且它们和上面那类不同——校准需要长期真实使用数据(同一批记忆在几个月里被命中/未命中的分布),没法靠离线造语料补,所以留在产品计划书阶段 D。
-
RAG 稠密检索阈值
RETRIEVE_DENSE_MIN = 0.15很可能从不生效——待一个有余额的 embeddings 账号确认。3.4.0 在真实BAAI/bge-m3上量到跨话题余弦中位 0.421、下限约 0.28;若检索侧 query↔chunk 的分布也在这个尺度上,0.15 就低于该模型的余弦下限,于是任何查询都会有limit块过线,包括资料里压根没有答案的查询。那意味着同一个功能在两种向量源下行为相反:本地 n-gram 下问无关的事一块都不注入(已用 8 块 × 7 查询量过,见下面命令),换成外部 embedding 后照样注入满额,直接往提示词里塞无关资料。手上的 SiliconFlow Key 已 402,所以结论只写在intelligence.rs常量上方、没有动这个数——没量过就调正是要避免的事。闭合动作:配一个有余额的 embeddings 接口,重建索引,然后问一个资料里完全没有答案的问题,看「检索调试」返回几块。cd app && cargo test --manifest-path src-tauri/Cargo.toml sparse_retrieval -- --nocapture
-
顺带量掉的另一个未量参数(未发布):群聊防复读
is_self_repetition的门槛原先是行内的0.76,从没量过。这条用的是和记忆去重同一个similarity,但在这里它是对的判据(问的是「这句话是不是又发了一遍」而不是「是不是同一件事」),所以结论相反:23 对实测里存在一条干净的缝——真复读 0.600–1.000、字面近但内容不同的对抗组 0.333–0.500、正常发言 0.000。0.76 落在缝的上方,漏掉最廉价的一种真复读(「笑死我了」→「笑死我了哈哈」= 0.600)。已改为缝的中点 0.55,两侧各留 10% 余量,对抗组 8 对 + 正常组 5 对零误拦。cargo test repetition_guard -- --nocapture看全表。 -
CI 只构建 Windows:macOS / Linux 已于 2026-08-21 冻结。矩阵去掉
macos-latest/ubuntu-22.04,构件只留 msi / nsis / portable。理由两条:这两个平台的产物从来没在本机构建过、也从来没真正发过(app/RELEASE.md记着「未在本机构建」),矩阵在为不发布的平台付钱;而私有仓库 Actions 分钟的计费倍率是 Linux 1× / Windows 2× / macOS 10×,三平台矩阵里 macOS 那一条就吃掉大半额度。冻结的是构建目标不是代码——tauri.conf.json的bundle.targets仍是"all"(按宿主平台取有效目标),Rust 侧也没有加任何 Windows-only 的cfg,所以随时可逆;解冻步骤(含原文件的 git 定位)写在.github/workflows/build.yml文件头,特性检查里有一项会在解冻时故意变红,逼着改的人同时更新判据。代价:mac/Linux 的编译问题从此要到解冻时才会发现。真要发那两个平台之前,先把矩阵补回去跑一次workflow_dispatch,确认还编得过,再谈发布。 -
依赖公告扫描已从每次 push 上摘下来,改成每周定时(2026-08-21)。RustSec 与 npm advisory 这两条是时间驱动的——代码一行不改,结论也会随新公告变红——挂在 push 上等于「改个错别字也可能收到一封 CI 失败邮件」,久了就会开始无视红灯,那时候真正的代码问题也会被一起无视。现在拆成两个 workflow,红灯各有单一含义:
build.yml红 = 你的代码有问题,.github/workflows/audit.yml红 = 外面出了新公告。后者按 UTC37 3 * * 1(北京时间周一 11:37)每周跑一次,外加只在锁文件变动时跑 push / PR(Dependabot 的升级 PR 会被扫,源码 PR 不会)。三个要记住的坑写在audit.yml文件头:定时只在默认分支生效、仓库连续 60 天无活动会被 GitHub 自动停用定时任务并发邮件(那封邮件不是出错,是要手动点回来)、cron 是 UTC。
聊天群/ # 仓库根(X:\聊天群)
├── README.md # 本入口
├── 产品计划书.md # 唯一产品/版本路线图
├── AGENTS.md # 协作与代码落点
├── 启动Links.bat # 开发版一键启动
└── app/ # 唯一工程
├── src/ # 前端
│ ├── main.ts # UI 编排
│ ├── domain.ts # 领域纯函数(可测)
│ ├── session.ts # 会话身份、分支、渲染与请求状态
│ ├── ui/dom.ts # 通用 DOM 小工具
│ ├── ui/chat-dom.ts # 稳定气泡定位与滚动锚点
│ ├── ui/icons.ts # Lucide 线性图标渲染
│ ├── api.ts / types.ts
│ └── styles.css
├── src-tauri/ # Rust / Tauri
│ └── src/ # ai / intelligence / storage…
├── characters/ # 示例角色
├── evaluation/ # 人格评测
├── scripts/ # verify / e2e / release
├── CHANGELOG.md
└── RELEASE.md
前提:Node.js、Rust、Cargo。
启动Links.bat或:
cd app
npm install
npm run tauri dev首次在「AI 接口」配置 Key 并「测试连接」。
请使用(不要用已删除的旧 app.exe):
app\src-tauri\target\release\character-theater.exe
或安装包:
app\src-tauri\target\release\bundle\nsis\Links_3.4.0_x64-setup.exe
app\src-tauri\target\release\bundle\msi\Links_3.4.0_x64_zh-CN.msi
app\src-tauri\target\release\bundle\portable\Links_3.4.0_portable.zip
(便携版解压到任意目录,双击 exe 即用,数据存同目录 data/。)
cd app
npm run verify:all
cargo test --manifest-path src-tauri/Cargo.toml
npm run release:build
npm run release:acceptance -- phase=full port=9239 mockUrl=http://127.0.0.1:9238/v1/chat/completionsrelease:acceptance 驱动已启动且开启 WebView2 远程调试端口的 release 真身;可按 RELEASE.md 选择 phase,并使用独立应用数据目录,避免触碰日常数据。
- 安装版:系统应用数据目录
CharacterTheater/data - 便携:exe 同级
portable.txt或已有data/→data/ - Key:安装模式进系统凭据;便携模式进
data/secrets.dat - 聊天图片:
data/attachments/