- 13 modified / 8 added / 1 deleted per CHANGES.json (author package 76bc120b) - docs/requirements v1.10 + prototypes/hel-799 + PROVENANCE from bundle, verified byte-identical to63799f0- AGENTS.md + scripts/check_requirements.py restored from governance commit63799f0Co-authored-by: multica-agent <github@multica.ai>
35 KiB
CyberGril 完整前端结构与可实施边界方案(HEL-738 · v1)
依据:HEL-697 首版需求确认记录 v1.6(31 项 CG + 9 项未定稿 T)、原仓库 CyberGril(commit
1e9b08a,2026-10-19)实际代码、P21 源码阅读包。 性质:只读分析 + 设计方案。不写产品代码、不部署、不开施工卡。 状态标记(大白话区分):
- 【已实现·有测试】仓库里真有,且测试守着
- 【已实现·未接线】代码在,但没接真实后端/没人调用
- 【仅规格】SPEC.md 里写了,代码没有
- 【完全缺失】规格都没写或明确没有
- 【拟议】本方案新提的接口,需要后续评审定稿
0. 一页结论(大白话)
现有代码是一个"地基",不是一个产品。 仓库里有:数据契约(7 个契约文件)、情绪动力学、八字编译、投影 φ、记忆存储、关系账本、模型路由——全部带测试。仓库里没有:服务器(api/ 目录不存在)、前端(一行都没有)、四个后台循环(tick/生活/整合/主动联系)、记忆检索与整合、表达管道、备份。所以"完整前端"不能假设任何后台已经在线,方案必须让前端在纯 mock 下就能完整设计、完整演示,接真后台是逐片替换的事。
本方案的核心判断(对应 CG-031):
- 前端 = 一个"观看 + 交流 + 管理"的界面层,它是服务器生活的视图,不是生活本身。关掉网页她的生活继续(CG-008),回来做增量同步。
- 画面(视频)是生活状态的下游:后端发"她在读书(晚上/晴/家居服)",前端据此选片播放;前端永远不因为"手头有哪段视频"而反编生活事件。
- 视频/口型只是一个可替换的呈现组件(Presenter)。缺口型驱动器时,其余全部页面照常设计、照常演示;也不允许用全屏播放器冒充产品(CG-031)。
- 所有界面数据带来源标签(mock / 契约已有 / 拟议),演示模式打"演示数据"角标,不伪装已接通语音或记忆后台。
1. 现有代码事实盘点(证据位置)
1.1 已有(【已实现·有测试】)
| 模块 | 文件 | 内容 | 对前端的意义 |
|---|---|---|---|
| 数据契约 | src/cybergril/contracts/:state.py(State 四维 v/a/d/e、Presence 枚举、StateBlock)、percept.py(Percept:speech_start/end、asr_partial/final、para、vision、env、system、silence)、render.py(RenderCmd:speak/expression/posture/idle/presence;visemes)、context.py(ContextBundle 六块)、utterance.py(text/speech_tags/withheld)、audio.py(AudioChunk、采样率)、capabilities.py(Capabilities/Degradation,"能力必须诚实、降级必须显式") |
全项目共同语言 | WS 协议可以直接投影这些契约,前端事件模型不用发明新概念 |
| 情绪动力学 | core/mood.py(209 行) |
衰减方程、负向偏置、麻木 | 状态是慢变量,前端展示"行为层"(她的样子),不展示数值 |
| 八字编译 | core/bazi.py(332 行) |
排盘→十神→derived 数值向量 | 创建向导的"生辰→底色预览"数据源 |
| 投影 φ | core/projection.py(131 行) |
系统事件→世界内陈述("刚才走神了") | 前端错误提示直接复用这套叙事,不弹技术报错 |
| 记忆存储 | memory/store.py(200 行)+ schema.sql |
SQLite 全套表(bazi_core、life_event、mem_episodic/semantic/relational、dyad_*、projection_gap)写入/衰减/FTS 查询 | 生活页、回忆页的表结构已定;mem_vec 向量表在 schema 里是注释(schema.sql:183),向量检索未实现 |
| 关系账本 | dyad/ledger.py(108 行) |
trust/intimacy/debt、未修复事件→行为偏移 | 关系设定页数据源 |
| 模型路由 | think/router.py、scenes.py、budget.py、wiring.py |
四档(none/low/standard/high)、场景→档位、预算降级 | 前端不用管;能力接口可透出"当前哪些档位可用" |
| Provider 抽象 | providers/:llm(echo/openai_compat)、embedding(openai_compat)、asr(scripted)、tts(silence)、turn(heuristic)、vad(energy)、avatar(只有基类) |
离线可跑通接口形状 | avatar provider 是空壳——浏览器形象端从未实现 |
测试:tests/ 共 7 个测试文件(contracts/providers/mood/bazi/projection/store/router)。
1.2 没有(对前端最关键)
| 缺失 | 证据 | 影响 |
|---|---|---|
| 服务器 api/ | src/cybergril/ 下无 api 目录;SPEC 附录 A 的 api/ws.py、session.py 【仅规格】 |
没有任何 WS/HTTP 接口存在,本方案第 5 节全部接口均为【拟议】 |
| 前端 web/ | SPEC 附录 A 设想 web/src/Avatar.tsx 等 【仅规格】,仓库无一行前端代码 |
前端从零开始,技术选型自由(见 5.1) |
| 四个常驻循环 | core/ 下无 tick.py/life.py/reachout.py(README"下一步"明列未做) |
"她在生活"目前没有运行时——前端演示必须 mock |
| 记忆检索/整合 | 无 retrieval.py/consolidation.py;store.py 只有 FTS 搜索 | 回忆页接真是后期切片 |
| 表达管道 express/ | 不存在;TTS 只有 silence 占位 | 语音会话依赖已有 ASR/TTS 服务另行接线(T-05) |
| 城市/天气 | 无任何代码(T-07 未定稿) | 环境条数据源待定 |
| 备份 | SPEC §11.3 【仅规格】 | 管理页的导出/恢复是拟议接口 |
1.3 与旧仓库冲突处,按 v1.6 用户最新决定执行
- 旧 SPEC §8.2 /
config/models.yaml:130-134的 VRM+three.js 形象路线 → 由 CG-002/029 覆盖为"成品视频为主、呈现器可替换";RenderCmd契约保留作为指令语言。 - 旧仓库"重大经历微调出生核心"(README 三层表里 derived 可被整合器 ±0.02 微调)→ 按 CG-015 收缩为"成长层另存,八字底色只读";前端身份卡不提供任何 derived 编辑入口。
- 旧样图/卧室构图 → 只作视觉参考(CG-031),布局、导航、信息层级围绕使用流程重新设计。
2. 产品形态与用户流程(CG → 流程映射)
2.1 产品一句话
单一伴侣(CG-007)、电脑优先兼顾手机(CG-018)、首版内网访问(CG-020);她有后台生活(CG-008),写实成品视频呈现日常(CG-029),交谈时切到同一人物的实时半身画面(CG-004/028);文字常驻、语音可开可打断(CG-010);网页内主动联系(CG-011);生活与共同回忆可见(CG-008/015);创建一次、八字锁定(CG-012/013);外观可换人不换(CG-005/006);每日自动备份 + 手动全量导出恢复(CG-009)。
2.2 九条关键流程(每条:触发 → 界面行为 → 数据来源)
F1 首次创建(CG-012/013/007)
空库 → 创建向导:① 输入她的出生年月日+时辰(可"自动生成一组"按钮,可手动改)→ ② 实时预览排盘结果与底色倾向(结构化指标+一句白话解释,CG-013/014:展示"倾向",不展示死板台词)→ ③ 设定初始关系(朋友/恋人、称呼;默认值待用户定,T-02)→ ④ 确认。确认即锁定:八字部分永久只读,界面上不出现任何"编辑底色"入口。已有伴侣时创建入口直接禁用(单伴侣守卫),不提供切换 UI。
数据:排盘预览来自 core/bazi.py【已实现】;"创建后进入她已在过自己的生活"首屏叙事由生活事件回填(mock 阶段用演示数据)。
F2 正常打开(CG-008/029) 进入 → 首次交互门(浏览器自动播放策略需要一次点击,正好用作"推门进屋"的仪式感)→ WS hello/sync → 显示:她当前的活动(视频舞台)、环境条(城市时段/天气)、在场状态、未读的她的话。视频按 scene_key 从素材清单选片,选不到就走降级链(见 6.2),不黑屏。
F3 离开后返回(CG-008/011)
网页关闭期间服务器继续生活(前提:服务器没停机,CG-008 边界)。返回时 sync 带 since 游标 → 增量拉取:期间的生活事件时间线、她留的离线消息("你不在的时候"列表)、关系/状态变化。缺席可被提及、不可被武器化(SPEC §10.2 红线)——离线消息展示语气遵循投影 φ,不显示"我等了你好久"式内容(后端约束,前端只展示收到的内容)。
F4 主动消息(CG-011) 在线时:页面内非侵入提醒(角落轻提示 + 舞台她转向镜头的呈现,不打断用户当前操作)。离线后回来:并入 F3 的"你不在的时候"。首版无推送(CG-011 边界)。
F5 语音会话:开始/结束/打断(CG-010/028/022)
- 开始:点麦克风 → 授权提示(需 HTTPS 安全上下文,T-07)→ 会话建立 → 舞台切到交谈近景(过渡素材)→ 连续收音。
- 进行中:字幕流(asr_partial 实时显示,contracts
percept.py的事件形状)→ 她回复(chat.delta + 音频流 + 可选口型驱动)。 - 打断:用户开口 → 服务端/客户端 VAD 判定 → 她 150ms 内停(SPEC 目标值,实测待定)→ 界面给"她停下来了"的轻反馈。
- 结束:点停止或闲置超时 → 停止收音 → 舞台回到日常活动 → 回到文字模式。
- 全程文字输入框不消失(CG-010:语音与文字并存)。
F6 断线恢复 WS 断开 → 界面进入"连接中"(她的画面停在最后一帧或低动效待机,不显示技术错误,用 φ 文案如"信号不太好")→ 指数退避重连 → 重连后带 epoch+游标重同步,期间的消息与事件按序补齐;语音会话中断则安全收尾(停收音、释放麦克风)。旧轮结果隔离见 5.4。
F7 城市改变(CG-023) 管理页 → 选择城市 → 保存 → 环境条与作息节奏切换到新城市;下次选片按新天气/时段走。无需重建伴侣、不动记忆。城市清单与天气源【拟议,待 T-07】。
F8 素材替换(CG-005/006/030) 管理页 → 素材包导入:上传素材包(含 manifest)→ 校验(格式、时长、命名、人脸一致性标记)→ 预览对比 → 应用。只换外观:manifest 的 appearance_id 变,身份/八字/记忆/关系全部不动(CG-006)。素材包规范 v1【拟议】(T-03),首批素材由小队制作(CG-030),导入器首版只收本地文件,无云端付费接口(CG-019)。
F9 备份恢复(CG-009) 管理页 → 备份区:显示每日自动备份状态(份数/空间策略【T-06 待定】);"完整导出"=下载数据库+素材包引用+设定到用户电脑;"从备份恢复"=上传包 → 校验(完整性、版本兼容、八字指纹比对:恢复的必须是同一位她)→ 确认恢复(恢复前自动再做一次当前状态导出,可反悔)。
2.3 明确不做(首版界面不出现)
多伴侣切换/并列(CG-007);创建后编辑八字底色(CG-013);通用人物提示词编辑器(CG-016 边界:可改的只有关系设定);云端付费生成接口入口(CG-019);手机推送(CG-011);外出场景/多房间(CG-017);把视频全屏铺满当产品(CG-031)。
3. 页面/区域结构(四类要素 → 页面模型)
3.1 布局模型(电脑优先,CG-018)
┌────────────────────────────────────────────────┬──────────────┐
│ │ 侧栏(可收起) │
│ 舞台 StageArea │ · 对话流 │
│ (可替换呈现器:视频/交谈/待机/降级图) │ · 文字输入 │
│ │ · 语音控制 │
│ 底部:环境条(时段·天气·城市)+ 她的状态(行为层) │ · 快捷入口 │
└────────────────────────────────────────────────┴──────────────┘
导航(左上/顶栏):相处(主界面)|她的生活|回忆|管理
- 舞台不是全屏播放器:它是产品的一个区域组件,四周永远有产品界面(导航、状态、输入),CG-031 明确禁止"先铺满场景再叠界面"。
- 手机端:舞台置顶 + 对话全屏切换(标签式),保画面、文字聊天和连续语音(CG-018),管理/回忆功能折叠进"更多"。
3.2 四类要素的落位(CG-031)
P1 相处主界面(默认页):舞台 + 环境条 + 在场状态 + 对话侧栏。承担 F2/F3/F4/F5 的日常界面。 P2 交流(与 P1 融合,不单独成页):对话流(文字气泡,她的消息支持副语言标签的可视化由视觉总监定)、语音会话控制器、字幕条、打断反馈。交谈时舞台自动切近景(CG-004)。 P3 她的生活与共同回忆(一页两栏或两标签):
- 她的日子:生活事件时间线(按天分组,
life_event表已有 told 标记→"已经跟你说过的"弱化显示); - 共同回忆:mem_episodic(记得的片段)、mem_relational(你们的习惯/暗语/仪式)、mem_semantic(她知道的事)分层展示;成长记录(CG-015:初始喜好 vs 后天成长分开两段)。 P4 创建与管理:创建向导(仅首次);日常为管理面板:关系设定(CG-016)、城市(CG-023)、素材包(F8)、备份恢复(F9)、视觉观察开关(CG-022:"一扇窗",开/关状态与最近观察摘要,时效性标注)。
3.3 状态流转(mermaid)
title 用户会话与舞台状态流转
stateDiagram-v2
[*] --> ColdStart: 打开页面
ColdStart --> Connecting: WS hello/sync
Connecting --> Syncing: 已连接
ColdStart --> FirstCreate: 无伴侣(仅一次)
FirstCreate --> Syncing: 创建完成
Syncing --> DailyLive: 同步完成
DailyLive --> Talking: 语音开始/用户发文字
Talking --> DailyLive: 会话结束/她答完
DailyLive --> Offline: 断线
Talking --> Offline: 断线
Offline --> Syncing: 重连(带游标)
DailyLive --> AwayView: 用户闲置(她继续活动)
AwayView --> DailyLive: 用户回来
舞台呈现器内部状态(与上正交):daily_clip → transition → talk(可带口型驱动) → transition → daily_clip;任一态可降级为 static_image。
4. 统一生活状态与数据来源表
4.1 生活状态 LifeScene【拟议——契约已有部分投影】
后端是唯一事实源。前端收到的是一个"场景描述",而不是"播放某视频"的指令:
{
"epoch": 41,
"presence": "idle_daily", // 投影 contracts/state.py Presence + 日常态
"scene": {
"mode": "daily", // daily | talk | away | asleep
"activity": "reading", // 来自 life_event/schedule
"time_band": "evening", // 由城市时区计算
"weather": "clear", // 城市天气源(T-07)
"outfit": "home"
},
"visible": "平静", // StateBlock.visible:一个词,不展示 v/a/d/e 数值
"since": "2026-10-21T19:40:00+08:00"
}
铁律三条:
- 媒体是下游:
scene.change事件驱动前端选片;前端/选片器不产生 life_event,也不因素材缺失请求后端改生活事件(素材缺失走降级链,见 6.2)。 - 前端是视图:关网页不等于停机(CG-008);前端本地不保存权威状态,重连必重新 sync。
- 状态展示走行为层:SPEC §8.5"不要把状态画成仪表盘"——展示 visible 词、姿态、回复快慢、活动内容;数值只进调试模式(工程开关,默认隐藏)。
4.2 各区域数据来源一览
| 界面区域 | 数据 | 来源现状 |
|---|---|---|
| 舞台-日常 | scene + 素材 manifest | manifest【拟议】;选片【拟议】;scene 数据 life_event 表【已实现·表结构】但生成循环【完全缺失】 |
| 舞台-交谈 | 音频流 + visemes(可选) | RenderCmd.speak(ts, audio_ref, visemes) 契约【已实现】;驱动器【HEL-739 独立研究,非门禁】 |
| 对话流 | 文字 + speech_tags | Utterance 契约【已实现】;生成管道(think/context+generate)【仅规格】 |
| 字幕条 | asr_partial/final | Percept 契约【已实现】;真实 ASR 接线【T-05】 |
| 她的日子 | life_event 列表 | 表【已实现】;每日生成循环【完全缺失】 |
| 共同回忆 | mem_episodic/relational/semantic | 表与写入【已实现】;检索/整合【完全缺失】(FTS 查询有,向量检索注释着) |
| 关系卡 | dyad_state | 表与账本【已实现】;循环推进【缺失】 |
| 身份卡(只读) | bazi_core + derived 白话化 | bazi.py【已实现·有测试】 |
| 环境条 | 城市/时段/天气 | 【完全缺失】拟议 GET /api/env |
| 能力徽章 | mock/real 标签 | 投影 contracts/capabilities.py 哲学【拟议端点】 |
5. 前端技术选型、组件边界与接口示例
5.1 技术选型(够用且不过度设计)
- React 18 + TypeScript + Vite。理由:SPEC 附录 A 原设想即为 React 组件;单一桌面+移动 Web 应用,无需 SSR/Next;Vite 开发热更新快,内网部署就是静态文件 + 反代。扩展:将来要 3D/二维呈现器时按 Presenter 插件加库,不预先引入。
- 状态管理:zustand(或 React 内置)。单伴侣单用户应用,全局状态很小(连接、场景、会话、UI 偏好),不需要 redux 级别机械。
- 样式:视觉总监定的 Token 体系落地为 CSS Variables + 常规 CSS(或 CSS Modules)。风格不由本方案锁定。
- 不引入:Electron(Web 已够)、大型 UI 库全家桶(组件不多,可控成本内手写 + 少量无头组件)、测试框架之外的重型设施(Vitest + Testing Library 足够)。
- 部署形态:静态构建产物由部署服务器(192.168.200.12)反代承载,与后端 WS 同源,避免跨域与混合内容问题【拟议,部署细节后续定】。
5.2 组件清单与职责边界
web/src/
app/ 布局、路由、导航、首交互门(自动播放策略)
connection/ WSClient(重连/退避/epoch)、SyncStore、useLiveState
stage/ StageArea(容器,唯一决定"用哪个呈现器")
stage/presenters/
DailyVideoPresenter 成品视频选片+双缓冲切换+过渡素材(CG-029)
TalkPresenter 交谈呈现:音频播放+可插接口型驱动器(CG-028/031)
FallbackPresenter 静态图/低动效待机(降级链末端,永不黑屏)
chat/ ChatPanel、MessageList、Composer(文字常驻)、SubtitleBar
voice/ VoiceSessionController(开始/结束/打断/权限)、
BargeInIndicator、MicPermissionGate
life/ LifeTimeline(她的日子)、GrowthPanel(初始喜好vs成长)
memory/ MemoryView(三层回忆)
manage/
CreationWizard 生辰→排盘预览→关系初值→锁定(F1)
RelationSettings 关系设定(CG-016 范围内)
CityPicker 城市(F7)
MaterialImporter 素材包导入/校验/预览(F8)
BackupPanel 备份/导出/恢复(F9)
VisionWindowToggle 视觉观察开关+摘要(CG-022)
shared/ CapabilityBadge(mock/real 角标)、PhiToast(φ 叙事化提示)、
StatusStrip、EnvBar、Skeletons、EmptyStates
Presenter 接口(TS,本方案关键边界):
interface StagePresenter {
mount(el: HTMLElement): void;
applyScene(scene: SceneDesc): Promise<void>; // 幂等;内部处理过渡
degrade(reason: string): void; // 显式降级,触发 FallbackPresenter
destroy(): void;
}
interface TalkPresenter extends StagePresenter {
attachSession(s: VoiceSession): void;
feedAudio(chunk: AudioPcmChunk): void; // 播放她的话
feedVisemes(v: number[]): void; // 有驱动器才被调用;无则忽略
interrupt(): void; // 目标 <150ms 停声停像
}
// 口型驱动器是 TalkPresenter 的注入依赖,可替换:
createTalkPresenter({ lipsync: null | 'local-w4w' | 'future-x' })
// lipsync=null 时:交谈画面退为"预设交谈近景素材/静态图 + 音频",
// 全部其他页面与流程不受影响(CG-031:不等待、不伪装)。
5.3 接口事件示例【全部拟议——api/ 完全缺失,形状投影现有契约】
一条 WebSocket(JSON 控制消息 + 二进制音频帧交错),加少量 REST。
// C→S
{"t":"hello","proto":1,"epoch":40,"caps":{"audio":"opus","video":false}}
{"t":"chat.send","turn_id":"c7-12","text":"今天过得怎么样"}
{"t":"voice.begin","session_id":"v3"} // 请求开麦(授权在浏览器层)
{"t":"voice.end","session_id":"v3"}
{"t":"sync","since":"2026-10-21T08:00:00Z"} // 断线/返回后增量
// S→C
{"t":"sync.done","epoch":41,"companion":{...},"scene":{...},"unread":[{"kind":"reach_out",...}]}
{"t":"scene.change","scene":{"mode":"talk","activity":"talking","time_band":"evening","weather":"clear","outfit":"home"},"reason":"voice_session"}
{"t":"presence.update","presence":"speaking"}
{"t":"chat.delta","turn_id":"c7-12","delta":"今天"}
{"t":"chat.final","turn_id":"c7-12","utterance":{"text":"今天挺好的,下午看了一会儿书[laughter]","speech_tags":["laughter"],"emotion_intent":"warm"}}
{"t":"asr.partial","session_id":"v3","text":"那你还想"}
{"t":"asr.final","session_id":"v3","text":"那你还想不想看电影"}
{"t":"interrupt.ok","session_id":"v3"} // 她已停
{"t":"reach_out","message":{"id":"r9","text":"刚看到窗外的晚霞,想跟你说一声","based_on_life_event":1234}}
{"t":"degraded","cap":"tts.speech_tags","reason":"provider_silent","phi":"(这句她的声音少了点语气)"}
{"t":"error","scope":"chat","retryable":true,"phi":"我没听清,你再说一遍?"}
音频帧:二进制帧头 8 字节(session_id 哈希 + seq + 方向),opus/pcm16k;细节施工时定稿。
REST【拟议】:GET /api/companion(只读档案)、POST /api/companion(创建,已存在则 409)、PATCH /api/relation、GET|PUT /api/env/city、GET /api/media/manifest、POST /api/media/import(multipart 素材包)、GET /api/capabilities(各能力 mock/real 标签,前端据此打角标)、POST /api/backup/export(下载 zip)、POST /api/backup/restore(上传 zip + 校验报告)。
5.4 取消与旧轮结果隔离(防"过期回答覆盖新问题")
- turn_id 配对:客户端每个 chat.send 生成
客户端id-自增;所有 chat.delta/final 必须回带同 turn_id。用户放弃输入/新问题时丢弃不匹配的流;渲染层只认"当前活跃 turn_id"。 - epoch 门槛:sync.done 携带 epoch(后端重启/状态纪元变化时 +1);收到旧 epoch 消息一律丢弃并触发一次 resync。
- 语音会话隔离:一切 asr/tts/interrupt 事件带 session_id;会话结束后的迟到帧全部丢弃;interrupt 后同 session 内更早 seq 的音频块不再播放。
- HTTP:管理页所有请求走 AbortController,页面切换/重复点击即取消;导出/导入等长任务用任务句柄轮询,不悬挂按钮。
- 重连风暴保护:指数退避 + 抖动;重连成功先 sync 再恢复输入框可用。
5.5 错误与降级展示(φ 叙事化,不用技术黑话)
- 原则来自
contracts/capabilities.py:降级必须显式;但显式给的是"世界内"表达,不是堆栈。展示分层:φ 文案("她好像走神了,稍等")为主,次要系统徽章("语音暂不可用")为辅,详细原因收进"工程信息"抽屉。 - 视频素材缺失:FallbackPresenter 静态图 + 轻动效,环境条照常,绝不黑屏、绝不停摆其他区域。
- 语音不可用(无 HTTPS/拒授权/服务离线):麦克风按钮置灰 + φ 文案,文字聊天完全可用。
- 后台不可达:连接态横幅(弱提示),本地保留未发出的文字草稿,重连后由用户重发(不自动重发,避免陈词滥调式重复)。
6. 媒体适配边界
6.1 素材清单 manifest【拟议,规范 T-03 待定稿】
{
"manifest_version": 1,
"appearance_id": "her-v1", // 换外观=换这个 id(CG-006)
"clips": [
{"id":"daily__reading__evening__clear__home",
"src":"clips/daily_reading_evening_clear.mp4",
"scene":{"mode":"daily","activity":"reading","time_band":"evening","weather":"clear","outfit":"home"},
"loop":true,"duration_s":12.5,"in_point":0.0,"out_point":12.5}
],
"transitions":[{"id":"daily_to_talk","from":"daily","to":"talk","src":"clips/tr_daily_talk.mp4"}],
"talk_base":{"src":"clips/talk_base.mp4","loop":true} // 无口型驱动器时的交谈底
}
- 键 =(mode, activity, time_band, weather, outfit)五元组;逻辑状态与素材解耦(CG-017:活动/天气是逻辑状态,不用选片篡改生活记录)。
- 首版范围:一间高层卧室(CG-026 只作视觉参考)、少量服装/动作(CG-017/030),素材数量随试片扩充。
6.2 选片与降级链(顺序固定)
精确匹配 → 去掉 weather → 去掉 outfit → 换近似 activity → FallbackPresenter 静态图。每级降级在工程抽屉记录(对齐 projection_gap 的"泄漏可统计"思想),不向生活状态倒灌任何东西。
6.3 口型/交谈驱动器(可替换,非门禁)
- 驱动器是 TalkPresenter 的注入实现,候选(LiveTalking/MuseTalk/Ditto 等)由 HEL-739 独立研究选型【候选·未测】,本方案不预设结论。
- 无驱动器时:交谈呈现 = talk_base 素材(预设少量身体动作,CG-028:口型/表情/目光优先,身体预设)+ 音频 + 字幕;产品完整可用。
- 驱动器上线时只改注入参数,不动任何页面结构(CG-031:视频/口型仅一种可替换呈现组件)。
- 真人→二维的条件判据未定(CG-027),Presenter 抽象同时兼容二维素材包,不需要结构变更。
6.4 播放工程
双 <video> 缓冲交替切素材实现无黑帧;transition 素材垫在模式切换处(日常↔交谈,CG-004 要求自然切镜、不突兀跳切);移动端降码率副本可选(不做首版承诺);自动播放策略由首交互门解决。
7. Mock 与真实:能力标签、最小演示数据
7.1 能力标签(诚实三档)
每个数据区域绑定标签,来自 GET /api/capabilities(mock 模式下由演示脚本静态给出):
mock:演示数据,UI 打"演示数据"角标(如生活时间线右上角小字);real:<tier>:真实后端(标注来源档位,如 real:echo / real:high);unavailable:能力缺失,显示 φ 化占位("这部分还在准备"),绝不假装在线。
演示模式全局开关:?demo=1。演示脚本(一个定时器驱动的假 WS 层)按剧本推 scene.change / chat.final / reach_out,让全部九条流程(F1–F9)在没有后端的情况下走通——这是"界面和原型阶段与真实功能施工分开"的机制保障。
7.2 最小演示数据约定(demo pack)
demo/
capabilities.json # 全部能力标 mock
companion.json # 一位已创建的她(含八字只读档案、白话底色)
life_events.json # 3 天 × 每天 1–4 条(含 1 条高显著未讲述→主动消息素材)
chat_backlog.json # 6 轮对话(含 1 条带 [laughter])
reach_out.json # 1 条主动消息
manifest.json # 4 段占位素材(早/昼/晚/夜 reading)+ 1 段过渡 + talk_base
clips/*.mp4|*.png # 占位片可用静态图代替(降级链自动接管)
timeline_script.json # 演示剧本:事件发生顺序与时刻
7.3 阶段划分(设计/原型 ≠ 真实施工)
| 阶段 | 内容 | 用什么数据 |
|---|---|---|
| A 设计 | 视觉总监产出完整界面稿与交互原型(HEL-737) | demo pack(本方案 7.2) |
| B 原型验证 | 前端壳 + mock WS 跑通 F1–F9 | demo pack |
| C 真实施工 | 按第 8 节切片逐片接真 | 逐片换 real 标签 |
8. 施工切片与验收清单(供总管排期,不在此开卡)
前端能立的切片尽量先立;每片验收要点独立,对齐 CG-024 的"分阶段验收"。
| # | 切片 | 依赖的后端(现状) | 验收要点 |
|---|---|---|---|
| S1 | 前端壳+路由+demo pack | 无 | F1–F9 全流程在 ?demo=1 走通;降级链全程无黑屏;手机布局不崩 |
| B1 | API 骨架:WS hello/sync/chat + REST | api/【完全缺失】→ 新建 | echo 级应答;epoch/游标正确;断线重连补齐 |
| B2 | tick+life 循环 | core/tick.py【缺失】 | 离线数小时后回来,时间线有新事件且与状态一致 |
| S2 | 文字聊天接真 | think 上下文组装+生成【仅规格】 | chat 流式;turn_id 隔离(旧回答不串台);φ 化错误 |
| S3 | 语音会话接真 | ASR/TTS 接线【T-05 待核实】 | 连续对话;打断停得下来(目标值待实测);结束即停收音 |
| B3 | 记忆检索+整合 | retrieval/consolidation【缺失】 | 回忆页三层数据真实;"她记得"与对话一致 |
| S4 | 生活/回忆页接真 | B2/B3 | 时间线、told 标记、成长分层展示正确 |
| S5 | 管理:城市/素材/备份 | env、media、backup 端点【拟议】 | F7/F8/F9 全走通;换素材后身份卡不变 |
| S6 | 视觉窗 | 4080 小视觉服务【用户部署,T-04/05】 | 开关生效;摘要带时效;关闭显示"窗户关上了" |
9. 与视觉总监(HEL-737)的对齐输入
我方提供(本方案已定,视觉总监可直接取用):第 3 节页面模型、3.3 状态图、5.2 组件清单、7.2 demo pack、第 4 节各区域数据字段。视觉风格、Token、构图由视觉总监决定,不受旧样图锁定(CG-031)。
需要视觉总监设计时覆盖的状态清单(每态都需要可识别的视觉表达):
- 在场/呈现:日常活动、交谈中、待机(away)、睡眠(asleep)、降级占位
- 连接:正常、连接中、离线(φ 语气)
- 语音会话:未开启、授权中、聆听中、她说话中、被打断、已结束、不可用
- 对话:她正在输入(流式)、已说完、发送失败(可重试)
- 场景维度:早/昼/晚/夜 × 晴/阴/雨 × 居家/外出服(素材命名的视觉对照)
- 流程态:创建向导各步、导入校验中/成功/失败、备份进行中、恢复确认(高危操作的双确认样式)
- 空态:无回忆、无生活记录(新生的她)、无未读
- 角标体系:mock"演示数据"、降级徽章、视觉观察"窗"状态
需要视觉总监回传给我方:Token(色/字/圆角/间距/动效曲线)、桌面与手机两版布局定稿、过渡动效规范(含打断的即时反馈)、素材安全框(视频区域内 UI 避让区)——这些直接决定 5.1 样式层的实现参数。
10. 风险与尚待决定项
风险(前端相关):
| 风险 | 影响 | 缓解 |
|---|---|---|
| 内网 HTTPS 方案未定(T-07) | 麦克风/摄像头不可用,语音/视觉窗全灭 | S1 阶段就出"HTTP 降级态"设计;证书方案尽早定(自签+设备安装或内网 CA) |
| 素材制作跟不上切片 | 演示/施工期素材缺口 | 降级链 + 占位图规范进 demo pack;素材数量按 CG-030 试片后再定 |
| 视频自动播放/移动端功耗 | 首屏黑屏、手机发烫 | 首交互门;移动端默认低动效可选 |
| 口型研究(HEL-739)失败或延期 | 交谈近观感打折 | Presenter 已解耦:talk_base+音频先行,不阻塞任何页面(CG-031) |
| WS 重连边界(epoch/游标设计错) | 状态错乱、串台 | 5.4 五条隔离规则 + B1 验收专测 |
| 素材 manifest 版本漂移 | 升级后选片失配 | manifest 带版本,导入校验含兼容性检查 |
| 长时间线数据量 | 生活页变慢 | 分页 + 按天聚合(对齐 state_snapshot 90 天聚合思想) |
尚待用户/总管决定(不在本卡解决,汇总供决策):
- 关系初始值与编辑形态(T-02):默认初识还是情侣、表单还是引导式对话设定;
- 城市清单与天气数据源(T-07);缺失天气素材时的默认呈现;
- 内网可信访问(HTTPS)具体方案;
- 素材包规范 v1 定稿(T-03):格式/时长/命名/人脸一致性检查项;
- 单伴侣"重新创建/清空"规则(T-06)——影响创建守卫与高危操作设计;
- 语音服务(ASR/TTS)流式与打断能力的实测参数(T-05),决定 F5 的实际体验目标;
- 主动消息频率参数化取值(CG-011:原仓库 reach_out 参数可作起点:间隔≥6h、8–23 点)。
11. 附录:CG-001~031 → 界面/流程映射总表
| CG | 落点 |
|---|---|
| 001 需求边界 | 治理原则,无界面 |
| 002 写实风格 | 舞台呈现器路线(视觉风格归 HEL-737) |
| 003 混合路线 | manifest 选片器 + TalkPresenter |
| 004 交流镜头 | 日常↔交谈转场与过渡素材 |
| 005 工程外观/自备素材 | MaterialImporter + 校验预览 |
| 006 换外观不换人 | appearance_id 机制;身份卡不可变 |
| 007 单伴侣 | 创建守卫;无切换 UI |
| 008 后台生活 | LifeScene 唯一事实源 + 返回增量同步 |
| 009 备份恢复 | BackupPanel(F9) |
| 010 连续语音+文字 | VoiceSessionController + Composer 常驻 |
| 011 主动联系 | 页内轻提醒 + 离线消息列表(F3/F4) |
| 012 生辰创建 | CreationWizard 步骤①② |
| 013 底色只读 | 身份卡只读 + 无编辑入口 |
| 014 诠释空间 | 底色展示为倾向词+白话解释 |
| 015 初始习惯与成长 | GrowthPanel 两段分层 |
| 016 关系可调 | RelationSettings(仅关系范围) |
| 017 场景范围 | scene_key 维度约束(一间卧室) |
| 018 电脑优先 | 布局模型 + 手机适配 |
| 019 素材费用 | 仅本地素材包导入,无付费入口 |
| 020 内网访问 | 部署约束 + HTTPS 待定项 |
| 021 按任务选模型 | 能力接口透出,前端不感知 |
| 022 摄像头范围 | VisionWindowToggle + 时效摘要 |
| 023 城市作息 | CityPicker + EnvBar |
| 024 验收顺序 | 第 8 节切片验收对齐 |
| 025 人物样图 | 视觉参考(CG-031),不锁布局 |
| 026 卧室样图 | 同上 |
| 027 真人优先二维备用 | Presenter 抽象兼容两种素材包 |
| 028 交谈动作范围 | TalkPresenter 动作范围=预设;口型表情优先 |
| 029 成品视频主路线 | DailyVideoPresenter 默认实现 |
| 030 首批素材分工 | manifest v1 由小队产出 + 导入检查 |
| 031 设计依据 | 本方案组织原则:流程先行、样图仅参考、嘴型独立 |
HEL-738 · 总工 · 2026-10-21 · 本卡只交方案,未改任何业务代码/配置/PR,未启动 GPU/模型/服务,未访问 4080,未建施工卡。