Skip to content

Latest commit

 

History

148 Commits

Folders and files

Repository files navigation

Links

本地桌面 AI 角色聊天:多接口、单聊/群聊、记忆、本地 RAG 与发图。

  • 当前版本:3.4.0(安装包/便携包与 package.json/Cargo.toml/tauri.conf.json 已对齐)
  • 当前发布:3.4.0 参数复验与依赖大扫除:没有新界面,全是拿真实环境把当初拍下的参数和判定表打回重做——智能编排的沉默阈值第一次用真实 embedding 量过并重标(FLOW_RELATIVE_CUT 0.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 红 = 外面出了新公告。后者按 UTC 37 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 并「测试连接」。

运行已编译的 release

请使用(不要用已删除的旧 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/completions

release:acceptance 驱动已启动且开启 WebView2 远程调试端口的 release 真身;可按 RELEASE.md 选择 phase,并使用独立应用数据目录,避免触碰日常数据。

数据

  • 安装版:系统应用数据目录 CharacterTheater/data
  • 便携:exe 同级 portable.txt 或已有 data/ → data/
  • Key:安装模式进系统凭据;便携模式进 data/secrets.dat
  • 聊天图片:data/attachments/

About

Links:本地桌面 AI 角色聊天软件(Tauri 2 + TypeScript + Rust,多接口/单聊/群聊/记忆/本地 RAG)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages