跳到内容
live EVIE RUNTIME · INTERACTION / talking-with

talking-with · 情感聊天端

基于酒馆 (SillyTavern) 架构 fork — 情感聊天 + 角色卡对话的端到端应用. 生图、语音、多 LLM router 都为对话服务. 当前 PWA 形态, 原生客户端 (Tauri / Capacitor) 封装在评估中.

在体系里属于 Evie Runtime · Interaction 子模块 · 上游接 Evie Runtime · Cognition (evie-agent persona + state) 与 Offline Model Adaptation (lora-tts-studio 训出的 ckpt / LoRA / 自用音色) · 下游被用户 (浏览器 / 移动端 PWA install) 直接触达 (见 架构图) · 最近验证: 2026-06

01 / demo 展示

demo · 截图 · 架构片段

下方是脱敏后的演示版, 单文件 standalone, sandbox 内运行 — 可见 chat / gen / speak 三 tab 围绕同一张角色卡协作的形态, 以及 4 套主题切换.

02 / 在系统里的位置

为什么这一层需要它

模型层训出权重, 交互层把这套权重变成用户与虚拟人交互的端. 这是体系的对外 surface — 用户不进 ComfyUI, 不写 API call, 不装本地依赖, 打开浏览器或 PWA 安装到桌面即可使用.

选 fork 酒馆 (SillyTavern) 是因为它本身就是为情感聊天 + 角色扮演设计的, 不是为 task / 助手设计的. 沿用角色卡 v3 spec 作为 persona 载体, 上游消费 Cognition 层的 persona state 与 dispatch 决策, 下游让用户跟一个有连贯人格的虚拟人聊天.

请求链路: 用户输入在 PWA 本地打包成完整 JSON (用户输入 + 角色卡 system prompt + 对话历史 + 用户偏好), 通过第一段 TCP 连接发到 Cloudflare Workers 边缘节点, Workers 加上 API key 和模型参数后通过第二段独立 TCP 连接转发给 LLM API. 两段连接完全独立、互不阻塞. 选择本地打包而非拆开并行发送, 是因为 LLM 需要完整上下文才能回复; 当前实现也选择不在 Workers 侧维护中间对话状态 (Workers 本身可配合 KV / Durable Objects / Cache API 持有状态), 后续如需跨设备恢复, 再引入 KV / Durable Objects 承担会话状态.

边缘部署: 全部走 Cloudflare Workers 边缘计算, 全球 300+ 节点 anycast, TCP 握手在离用户最近的边缘节点完成而非源站. 对话请求和生图请求可以并行 — HTTP/1.1 下浏览器开独立 TCP 连接, HTTP/2 下共用连接但在不同流中并行.

03 / 想解决什么

vision-aligned 问题陈述

虚拟人愿景的核心不是"更强的助手", 是"能让人产生情感连接的 entity". 这就要求交互层不能走"task → response"的工具范式, 必须走"角色 → 对话 → 关系"的陪伴范式. 多模态 (生图/语音) 是手段而不是目的 — 让对话更立体, 让虚拟人在用户脑中具象化, 而不是把它当多模态 ChatGPT 用.

酒馆架构提供的角色卡 v3 + 长程对话 + 情感聊天范式是当前最接近这件事的工程基底, 它本身就是为情感 + 角色扮演设计的, 不是为 task / 工具设计的. 我们 fork 它, 替换掉它的多 LLM 接入层换成自己的 Cloudflare Workers 中转 + 4 provider router, 加入 ComfyUI dispatch 让生图随对话情境自动选 ckpt, 加入 TTS 输出链路让角色回复可自动朗读 — 始终把"对话 + 角色 + 情感"放在中心.

04 / 现状 + 已知 limit

现在做到哪了

已发布并在日常使用, 是体系里少数 live 状态的组件之一. 走 vanilla JS 单文件主控, 避开框架, 把 Service Worker 兼容性放在第一位.

A请求架构

PWA 在本地将用户输入 + 角色卡 system prompt + 对话历史 + 偏好设置打包成完整 JSON, 经第一段 TCP 连接发到 Cloudflare Workers edge runtime, Workers 注入 API key 和模型参数后经第二段独立 TCP 连接转发 LLM API. 两段连接互不阻塞, 对话和生图请求可并行.

BMulti-LLM 路由

4 provider (Claude / Ollama / OpenRouter / OpenAI-compat), Workers 端做供应商隔离 + 故障切换 + 限流 + 密钥隔离. 当前大多数模型走普通 HTTP 请求-响应, 尚未全面实现 SSE 流式输出 (仅 Claude 通过 Meridian 桥接有小范围流式).

C三层缓存

Service Worker 拦截静态资源请求 (sw.js cache-first); Cloudflare 边缘节点缓存 (CDN 层); IndexedDB 本地持久化角色卡和对话数据. PWA 安装后离线可加载 shell, 模型/语音/图像调用仍依赖网络.

D对话历史管理

长对话 context 膨胀是实际问题. 当前方案: 用户手动触发 compact (可见). 评估中: 自动隐式 compact (后台按 token 阈值裁剪, 用户无感); 未来方向: Workers KV 持久化 + TTL 自动过期清理不活跃会话.

E其他已落地

ComfyUI dispatch 按对话情境自动选 checkpoint 生图; 角色卡 v3 spec 兼容; chat / gen / speak 三 tab 围绕同一张角色卡运转. 语音输入 (STT) 尚未接入, 当前语音相关仅有麦克风电平可视化与 TTS 输出.

F已发现的真实 bug: auto-compact 竞态丢消息

当对话条数触发 auto-compact 阈值时, compact 请求 (TCP 连接 A) 和用户紧接着输入的新对话请求 (TCP 连接 B) 并行发出. 如果新对话的响应先于 compact 返回, 会被正常渲染到页面; 但 compact 响应随后到达时, 它携带的是发出时的状态快照, 刷新页面状态会覆盖掉 compact 发出后到达的新消息, 导致第 21/22 条对话丢失. 本质是并行请求下的状态竞态 (race condition) — compact 操作没有对 UI 状态加锁, 也没有在刷新前 diff 当前状态与快照的差异. 修复方向: compact 发出时锁定 UI 输入 (简单但影响体验), 或 compact 返回时与当前状态做 merge 而非覆盖 (复杂但无感).

G其他待解决

SSE 流式输出尚未全面覆盖 (TransformStream + EventSource/ReadableStream 待实现); persona 一致性在长对话中会 drift; 长程情感记忆还没接 vault, 跨会话记不住关系.

05 / 下一里程碑

接下来推什么

AEmotion Eval (首批结果已上线)

情感陪伴场景评测 — 首批受控横评已发布: 7 个模型接入同一套单模型双调用运行时, 跑同一段五轮多模态对话, 对比结构稳定性、成本延迟与共情质地. 多采样稳定性、记忆漂移与人格漂移专项完成后增量更新, 配套 Badcase 归因整理. 结果见 Emotion Eval 页.

B修复 auto-compact 竞态

compact 返回时与当前 UI 状态做 merge (对比 compact 发出时的消息 ID 快照与当前消息列表, 保留 diff 中的新增消息), 而非直接覆盖刷新. 这是日常使用中发现的真实 bug, 从用户体感出发定位到并发请求下的状态管理问题.

CSSE 流式输出全覆盖

用 TransformStream 在 Workers 端做流式中转, 前端用 fetch + ReadableStream 逐 chunk 渲染, 消除当前"等完整响应再显示"的体感延迟.

D隐式 auto-compact

后台监控 token 阈值, 超限自动裁剪旧轮次, 用户无感. 进一步: 将 compact 后的摘要写入 Workers KV (带 TTL 自动过期), 实现跨设备会话恢复.

Eevie-agent 状态接入

把 persona / mood / context 在 tab 间和会话间共享, 长程关系不再每次重置.

FTTS 回路

voice → LLM reply → TTS 输出, 完成语音闭环.

06 / 迭代日志

版本演进 · 问题驱动

以下是从 v2.13 到 v2.27 的关键迭代节点. 每条记录的结构: 遇到什么问题 → 用什么思路解决. 不列全部版本, 只列有工程决策意义的节点.

v2.27

生图功能从独立 Worker 迁入主端点. 问题: 原有独立生图 Worker URL 已下线, 用户点生图按钮无响应. 解决: 统一走 comfygen discover 端点, 删掉死链路.

v2.27.4

Service Worker 误缓存 API 请求. 问题: sw.js 的 cache-first 策略把 /api/* 动态请求也缓存了, 导致 LLM 回复不更新. 解决: sw.js 加 /api/* 路径的 network-only bypass 规则.

v2.24

云备份 OOM. 问题: 一次性读取全部 IndexedDB 数据推送到 KV, 角色卡库大时浏览器内存溢出. 解决: 改用 IDB cursor 流式读取 + 单 store 8MB 上限截断.

v2.23

角色卡丢失. 问题: 某些操作路径下 activeCardId 指向已删除的卡, 后续所有对话引用空卡. 解决: 加载时校验 activeCardId 有效性, 无效时 fallback 到默认卡; lookup 排序修复.

v2.22

角色卡改造标签注入错位. 问题: 用户给角色加 mod tag (如性别转换), 标签注入位置不对导致 LLM 忽略. 解决: 重新设计 tag 注入优先级, 改造标签提升到 system prompt 前部.

v2.15

生图/视频目标切换. 问题: 生图只能出当前角色全身, 无法指定场景或视角. 解决: 加入 target + style 选择器, 用户可选特写/全身/场景, 生图 prompt 据此调整构图.

v2.14

对话深度挖掘. 问题: LLM 回复总是表层应答, 不主动展开角色内心世界. 解决: 加入 on-demand depth digging, 用户点击可让 LLM 对当前话题做深层展开.

v2.13.89

deploy 路径错误导致 API 全挂. 问题: wrangler deploy 时 working directory 在项目根而非 PWA 子目录, 导致 functions bundle 不打包, 所有 /api/* 返回 index.html. 解决: 固定 deploy 命令必须 cd 进 PWA 目录执行, 加 deploy 后二验 (curl grep 版本号).

v2.13.74

切对话后生图出上个角色. 问题: 切换对话时没有同步清理 activeCardCache, 生图模块仍引用前一个对话的角色身份. 解决: 统一在对话切换时清理所有按对话派生的缓存, 生图取材改为按消息位置而非生成事件.

v2.13.72

多角色生图幻觉. 问题: LLM 从对话上下文推断出不存在的角色并画进图里. 解决: 回滚多角色生图, 限制为只画当前活跃角色卡的单一角色.

v2.13.67

长对话 AI 降智. 问题: 对话超过 20 条后 context window 接近上限, LLM 回复质量明显下降. 解决: 引入 auto-compact, 满 20 条时后台 LLM 压缩前 14 条为摘要, 保留最近 6 条原文 + 1 条摘要, 原文归档到 IndexedDB 可回档.

v2.13.64

视觉小说模式. 问题: 纯文字对话缺乏叙事沉浸感. 解决: 加入 galgame 固定脚本模式 (种子→骨架→背景预生成→全屏 VN UI), 支持存档/路线分支/好感度/结局.

完整迭代超过 60 个版本号. 以上选取了涉及架构决策、数据完整性、部署安全和用户体验的关键节点.

体系 / 模块