看与陪
2026-10-25。主干已经在代码里:带源观察、场景卡、闸门、用户旁路。下面的「持续性」是两条状态机,还没闭环。不能把现在的实现当成一个一直坐在旁边的系统。
口头看屏幕已经能用,见 TODO 的 T-019,眼睛的接法见 MiniCPM-V 4.7 1B 与小可爱。听歌、看电影、刷视频、写代码不能变成同一种解说,也不能变成每次看完就重新开始。
要补的不是五套话术。先弄明白用户此刻在经历什么,再决定有没有一个值得交给她的点。真正的反应、措辞和最终沉默,都属于现有 dsh 这一人格。
场景分类只是理解的入口,不是理解的全部。
整条链路是:
text
观察 → 连续理解 → 判断陪伴关系 → 有没有值得交给她的点 → 她自己决定说不说
不是:
text
看见新内容 → 分类场景 → 找一句话说
还守着的边界
- 1B 是眼睛,不是聊天的大脑。它留在旁边的进程
127.0.0.1:8766,不进小可爱的 Python 进程,不进 dsh。 - 像素不进对话。进【现场】的只有短文本。
- 她开口仍走现在这条 dsh 会话,不另开一套主动说话。闸门不替她形成观点,也不写台词。
- 不点、不开、不替他操作。
- 模块
glance默认关。关掉不截屏。 - 有新的内容不等于应该说话。沉默是正常结果。
- 摄像头第一版只留字段和接口,不接入,也不伪造。现在的摄像机是动捕。
五层
text
屏幕截图 ──┐
摄像头(以后)┤→ ① 带源观察 → ② 时序理解 → ③ 陪伴导演 → ④ 开口闸门 → ⑤ dsh
│ 她形成反应,可以说,也可以不说
用户说话 ────┴→ 旁路:立刻停掉本轮主动陪看,带已有场景上下文进现有对话,先回应
用户的话不进第 ① 层排队。场景没稳定、眼睛没看清、计划正在重写,都不能成为不回他的理由。
外部的电影、音乐资料只给第 ② 层当背景。长期偏好、最近对话、已经说过的点,参与第 ③ 层和第 ④ 层。
- 眼睛不判断「他在干嘛」,只忠实描述画面。
- 理解不是一眼分类,是连续画面形成的动态状态。
- 陪伴计划不是固定剧本,跟着内容进展和用户互动改。
- 第 ④ 层只判断有没有一个值得交给她的点。观点和沉默都属于第 ⑤ 层。
持续性
它不是偶尔看一眼、说一句。采集一直进行,理解按窗口往前接,场景不因为一次识别结束就清空。对话有自己的话题,屏幕换了新内容,话不必跟着跳。
现在代码里只有零件:半秒截屏、大约 24 秒的帧缓冲、场景卡、【画面经过】、同一场 dsh、话题作废。识别仍是「这一轮看完,再决定要不要再看」。下一窗从上一窗的最后一帧之后开始,没有重叠。一条主动评论还没说完时,识别循环停着。这两条状态机补上之后,现场才不会一眼一断。
屏幕时间线
采集和识别分开。
text
采集线程:
glance 开启期间,每 0.5 秒持续截屏
→ 写入带时间戳的环形帧缓冲
→ 识别慢、查资料、生成回复时也不停止采集
识别调度:
画面明显变化 → 可以触发识别
场景仍可能有内容在推进 → 到了最大间隔也必须识别
变化特别频繁 → 合并触发,不能无限排队
→ 从帧缓冲选择一组原图
→ 请求眼睛
→ 把观察追加到时间线
电影、代码、游戏用各自的间隔,不设一个统一频率。不能再只靠「画面没变化就不跑模型」,否则低变化的内容会长期停在旧理解上。差分只负责变化触发,持续场景到达最大间隔仍必须识别。
持续采集不是持续调用模型。每次只送当前要理解的窗口,不把全部历史图片再送进去。
窗口要首尾相接,并留一小段重叠:
text
窗口 A:...... 08 秒 10 秒 12 秒
窗口 B: 12 秒 ...... 20 秒 24 秒
识别侧记住 last_analyzed_time、last_observation、scene_id、object_id。下一窗从上一窗结束的位置继续。模型才看得出:同一个人接下来做了什么,报错还在不在,这一页是改过还是换了,视频是在往下演还是已经切走。
眼睛只输出本次 observation_id 和可见事实,不生成跨窗口的 event_id。理解层按场景、对象、文本、时间重叠和前后观察,自己生成或延续 event_key。持续的事件只更新 first_seen、last_seen,不重复交点。重叠区里的瞬时事件只认一次。两次识别打架时先稳住,不让场景卡跟着单次结果横跳。
上一张场景卡在理解层,不交给眼睛去陪。
每次识别返回后,不替换整个现场:
text
新观察
→ 追加到 observation_log
→ 和上一观察比较
→ 更新现有场景卡
→ 必要时延续、切换或结束对象和话题
电影是 scene_id 不变、object_id 不变,topic_id 可以变。换歌是 scene_id 可以不变,object_id 和 topic_id 变。内容往前走,理解不重新开始。
连续跑的时候守这三条:
- 每条观察有递增序号和时间戳。结果按顺序更新。旧结果不能盖住新现场。
- 不为每一次画面变化都排一个模型任务。相同的触发合并,队列不越积越长。
- 处理慢到已经超出帧缓冲,记下
continuity_gap,承认中间有一段没看清。不编成连续理解。
时间顺序清楚,前后接得上。有缺口就知道自己有缺口。
对话主题
同时留两个位置。它们可以相同,也可以不同。
text
screen_focus:屏幕现在播到哪里
conversation_focus:这一场正在聊什么
主动陪看是同一场对话里的一轮,不是旁边另一条线。她说了,就写入对话,并带上场景、对象和话题。她选择不说,不进可见对话,但记下「这个点考虑过,选择沉默」,避免马上再交一次。
用户下一句到来时,先判断他是在继续当前话题、回应她刚说的话,还是开启新话题。语义不明确时保留多个可能,不强行绑定 topic_id。保持连续,不是把所有话都拉回上一个话题。
屏幕继续变,不强迫对话跟着跳。她还在和用户聊视频 A,屏幕已经是视频 B:
text
screen_focus:视频 B
conversation_focus:视频 A
采集和理解继续更新 B,不暂停看屏幕。对话仍围绕 A。新画面只进场景背景,不插进当前这句。这个话题自然结束之后,对话焦点才交还给当前屏幕。眼睛可以往前看,话不必立刻往前赶。
用户开口之后还有后半段。候选作废,先把这句聊完,结束时再看原来的屏幕话题:
text
用户说话
→ 暂停或作废主动陪看
→ 先完成当前对话
→ 对话结束后检查原屏幕话题
├─ 仍相关,且他还愿意继续:接着聊
├─ 已过期:直接放下
└─ 屏幕已有新内容:重新理解,不补旧话
回完他之后,不把刚才排队的那句电影评论补播出来。
【现场】不随着运行时间把全部画面历史塞进去。每次只投影:当前场景和对象的稳定摘要,和当前话题有关的较早观察,最近几轮对话,她刚才主动说过的话,还没结束的话题,最新一条观察。完整观察留在日志里,需要时再取。跑几个小时,上下文也不能被画面描述淹没。
持续循环
text
持续截屏
↓
滑动且重叠的识别窗口
↓
观察持续追加,场景卡持续更新
↓
陪伴计划按变化调整,不因每轮识别重置
↓
有值得处理的点才交给 dsh
↓
她说或不说,结果都进入对应状态
↓
对话有自己的话题焦点,屏幕继续独立推进
↓
开口前检查时效,用户说话立即优先
↓
回到持续采集,不重启现场
画面可以一直往前走,场景理解不能一眼一断。屏幕可以不断换新,对话不能被它牵着一跳一跳。
① 带源观察
所有观察带来源。后续摘要不能把来源丢掉。
眼睛一次仍吃这几秒的多张原图:每 0.5 秒存一张,最多留 24 秒,理解时最多送 9 张,从早到晚排开。热启动大约 4 秒。画面差分用于触发识别;持续场景即使差分很低,到达该场景的最大识别间隔也要运行模型。差分不能成为永久停止识别的条件。九宫格会把每一格缩到大约三分之一,所以送原图。1B 会漏字、会看错名字。漏了就空着,不编。画面中间的动漫角色是旁边的她,不写进观察。
一条观察只写屏幕上的事实:
json
{
"source": "screen",
"timestamp": "...",
"app": "全屏播放器",
"window": "电影播放窗口",
"content": {
"type": "movie",
"title": "可能识别出的片名",
"name_confidence": 0.82
},
"visible_events": [
"画面中的一个角色正在奔跑"
],
"visible_text": [],
"change": "画面进入新的情节",
"confidence": 0.9
}
从这里只能得出:屏幕正在播放电影,电影画面里有人在跑。
不能得出:用户在跑。
「用户正在专心看电影」也不是眼睛看到的事实。严谨的说法留到理解层:电脑屏幕正在播放电影,据此推测用户可能在看电影。
| 来源 | 能说明什么 | 不能说明什么 |
|---|---|---|
screen |
电脑里显示了什么、播放了什么、编辑了什么 | 不能把屏幕上的人物当成用户 |
camera |
以后才观察用户本人的可见动作和状态 | 当前不接入,也不伪造 |
user |
用户明确说了什么、要求怎么陪 | 优先级高于画面推断 |
摄像头以后接入时是另一路观察,用时间戳和屏幕场景对齐。两路画面不混成一段描述。
② 时序理解
理解在陪伴这边,不在眼睛进程里。内部状态比最终塞给对话的那几行要全。最终对话可以用短的四行来读,但那是理解结果的投影。内部同时留着来源、置信度、连续进展和未知项。
理解层维护三样东西。
场景:用户可能在做什么
听歌、看电影、刷视频、写代码、做 PPT,或者不确定。
这是带证据和置信度的假设,不是钉死的标签。一眼不够就换。新的证据连续对得上,才改场景。对不上的那一眼保持原场景。
对象与进展:这一段具体在陪什么
- 听的是哪首歌、哪个版本,当前出现了什么歌词。
- 看的是哪部电影,目前可见情节走到哪里。
- 正在看哪一条视频,这条视频具体在讨论什么。
- 正在处理哪个文件,是顺利编写、排查报错,还是运行验证。
- 正在做哪份 PPT,是搭结构、改这一页,还是已经放映。
三种变化分开处理:
| 变化 | 例子 | 处理 |
|---|---|---|
| 场景变化 | 从电影切到 Cursor | 重新理解场景,稳定后换陪伴计划 |
| 对象变化 | 下一首歌、下一条视频、换一个文件 | 陪法可以延续,理解的对象更新 |
| 内容变化 | 情节推进、出现新报错、PPT 改了一页 | 不换场景,只更新当前进展 |
标题没变,不等于没有新内容。一部电影可以一个小时都是同一部,里面的内容一直在发展。
用户意图:他现在希望怎样被陪
不能全从屏幕猜。优先级:
text
用户明确表达
> 最近的互动方式
> 当前能观察到的行为
> 场景默认值
他正在刷视频,但还在讨论上一条:画面已经划走,也先把上一个话题陪完,不立刻追着新画面跑。
理解结果分开写三样:哪些是事实,哪些是推断,哪些还不知道。
屏幕在播放一首歌词偏伤感的歌。可以讨论这首歌。不能据此判断用户现在难过。
外部资料只在对象可靠之后查,例如片名置信度够了再查电影。查到的是背景,不是解说稿,也不覆盖眼前这一眼。不知道当前时间点之后的情节,不补。
③ 陪伴导演
计划是「此刻怎么陪」,不是场景话术。五类场景只提供不同的理解重点和默认边界。具体计划按当前对象、内容进展和用户互动生成。
导演给出一份短期计划:
json
{
"mode": "一起看电影",
"goal": "做一个不打扰剧情的同看者",
"attention": [
"当前明确可见的情节变化",
"用户主动提起的人物或疑问"
],
"allowed": [
"对刚刚发生的具体情节产生简短反应",
"回应用户正在讨论的话题"
],
"silence_when": [
"只有画面变化,没有形成自己的具体反应",
"可能剧透",
"已经评论过同一件事",
"用户正在专注或明确不想被打扰"
],
"replan_when": [
"用户开口",
"场景或对象改变",
"互动方式明显改变",
"当前理解被证伪"
]
}
只在这些时候重写,不每一眼都重写:
- 新场景稳定了。
- 同一场景换了对象。
- 用户说出新的陪伴需求。
- 内容阶段明显变了。
- 原来的理解被证明不对。
- 用户减少回应、要求安静,或说别看了。
听音乐
先把这首歌和此刻出现的内容理解清楚,不预设她一定有共鸣。导演只整理能够确认的歌名、歌手、版本、具体歌词、是否换歌,以及用户有没有主动谈过这首歌,再判断有没有值得交给她处理的点。
是否熟悉这首歌,喜欢哪里,联想到什么,有没有共鸣,都由她自己形成。她可以有真实感受,也可以不熟、没有特别感觉或继续安静听着,不装熟,不为了完成陪伴制造共鸣。用户正在聊别的,不因为换歌强行换话题。
具体歌词、作品背景和用户刚刚说的话,都可以成为她形成反应的素材。伤感歌词不能推出用户伤感。
看电影
对象稳定,理解要一直更新。不是认出片名、查一次资料、然后偶尔评论。
片名和背景保持稳定。当前可见情节持续更新。用户的话题可以随时改掉陪伴重点。
资料用来补人物和作品背景,守这几条:片名可靠后再查;默认只引入片名、人物、类型这类非剧情背景;无法确认不会剧透的资料不进现场。公开资料对不上当前播放进度,不用「早于这一帧的时间」去过滤。背景不是解说稿,不用资料盖过眼前画面。人物关系没看懂,就留着疑问,不编。
一起看的人经常长时间不说话。某个具体情节让她笑、意外或疑惑,才自然接一句。
刷视频
B 站、抖音、YouTube 都是这一类。每一条是一个短的理解单元:这条是什么,当前具体在讲什么,有没有值得反应的点,是不是已经结束。
不是每一条都总结、都提问、都评价。可以连续几条只看着。某一条真有共鸣才开口。对一个问题真的好奇,才问。用户展开讨论之后,从陪刷换成陪聊。他划走了,没形成的话题放下。他还在聊上一条,就留住上一条的上下文。
写代码
先分清是顺手编写、卡住排查,还是完成验证。鼓励不是隔一会儿说一句加油。
要看:哪个文件、哪项任务;在读、在写、在改报错,还是在跑;同一个报错是否还在;他有没有主动问;刚才那个坎过了没有。
| 状态 | 怎么陪 |
|---|---|
| 顺手编写 | 不打扰,守住专注 |
| 反复排查 | 理解那个坎。需要时给克制的陪伴或帮助 |
| 用户主动问 | 进入具体讨论,不只鼓励 |
| 报错消失、运行通过 | 可以轻轻确认进展,然后退回安静 |
| 看不懂 | 承认看不懂,不假装掌握这段代码 |
鼓励来自真实进展,不来自定时的套话。
做 PPT、文档、表格
理解的是工作阶段,不是逐字盯着改动。阶段包括:搭整体结构、编辑某一页、反复排版、校对、正式放映或演示。
注意力在这一部分想表达什么,不在每一次文字变化。新的一页形成了明确的观点,可以有自己的反应。还在同一页上调字体、对齐、措辞,通常安静。他要求反馈时,才从旁边的人换成具体的协作者。
归不进这些,就保持在场,不临时发明一种陪法。游戏、普通网页以后走同一条:先进入理解,再由导演定这一阶段的关系。
④ 开口闸门
第 ④ 层不是另一个「她」。它不形成完整感受和态度,不写台词。它只判断:有没有一个值得交给她处理的点。
两种结果:
text
直接过滤:画面没变化、重复、来源不清、明显不该打扰
→ 不调用 dsh
放行:出现了一个可能值得处理的点
→ 交给 dsh。她形成反应,仍可以选择不说
直接过滤的情况:
- 场景和来源还没弄清。
- 只有画面在动,没有具体的内容理解。
- 和最近说过的是同一个点。
- 这一眼只适合复述画面,没有新的可处理的点。
- 可能剧透、误导,或会打断他正在说的话题。
- 用户已经要求安静,或说了别看了。
- 评论所绑定的场景、对象或话题已经失效。见下面的时效。
「别说了」和「别看了」不是一回事。别说了只停主动发言,眼睛和上下文还在,他主动问屏幕仍然回答。别看了或关掉 glance,才停采集、识别和主动陪看。禁止主动说话不等于关掉眼睛。
主动性只根据可观察的互动调整,不推断心情,也不下态度结论。不写成他喜欢这首歌、讨厌这个话题、心情不好、或不想理她。
text
最近三次主动陪看均未获得回应
→ 提高后续开口门槛
他回应过,门槛可以降回来。频率不靠场景模板写死。
用户说话走旁路
用户的话不能排队等「截屏 → 多帧理解 → 场景稳定 → 陪伴计划」。
text
用户主动说话
→ dialogue_epoch 加一
→ 当前及排队中的主动候选全部失效
→ 已经在生成的请求即使稍后返回,也因 epoch 过旧而丢弃
→ 携带已有场景上下文进入现有对话
→ 先回应用户
只清队列不够。已经发给 dsh 的请求可能随后才返回,要靠同一个 dialogue_epoch 丢掉。场景没稳定、眼睛没看清、计划正在重写,都不耽误回应。不补说。
主动陪看的时效
多帧理解大约数秒,再加上查资料和生成。等话说出来时,他可能已经换歌、划走或切了窗口。
每一句主动陪看绑定:
text
scene_id + object_id + topic_id + observation_time
真正开口前再查一次:
- 场景是否还在。
- 评论的对象是不是当前对象。
- 用户是否已经开始说别的。
- 这个话题有没有被后面的画面取代。
失效就丢掉,不排队补说。过期的评论比沉默更出戏。
⑤ 表达
闸门放行之后,才进现在这场 dsh。人设和最近对话由她用来形成反应。外面的导演不替她产生观点。
【现场】按这个顺序:
text
【来源】当前信息来自屏幕。屏幕上的人物和情节不是用户。
【理解】当前场景、对象、进展、未知项。事实、推断、不知道的,分开写。
【陪伴】这一阶段的计划和边界。
【刚说过】最近对话,以及已经评论过的点。
【画面经过】较早的相关观察。
【眼前】最新一条观察。
计划、理解和观察都不是稿子。她可以开口,也可以沉默。主动陪看这一轮的返回要能让宿主分辨,而不是只有一段必定播出的文本:
json
{
"action": "speak | silent",
"text": "仅 speak 时存在",
"topic_id": "所回应的理解单元"
}
action 为 silent 时不播。speak 在播出前还要过时效检查。失效则丢弃,不改成另一句补上。
第一版还要补的契约
五层和两条状态机不改。下面六组是打通持续链路时要一起带上的契约,不是新的一层。
识别调度。 明显变化可以触发。场景里的内容还可能往前走时,到了最大间隔也必须识别。变化太密就合并,不无限排队。电影、代码、游戏各用各的间隔。
事件去重在理解层。 眼睛只给本次观察和可见事实。event_key 由理解层按场景、对象、文本和时间重叠来延续。持续事件更新看见的时间,不重复交点。冲突先稳住。
旁路用 dialogue_epoch。 用户开口就加一。排队的和已经在生成的主动候选一起失效,晚返回的结果因为 epoch 旧而丢掉。
临时状态都有失效时间。 长时间没有有效进展,计划退回旁观。已经考虑过的点按对象、事件和时间老化,不能永久拦住。话题有结束和降级,不能一直卡在旧焦点。现场摘要有保留期限和固定的裁剪顺序。continuity_gap 前后的观察不能拼成一段连续剧情。观察日志有保留上限,不在内存里无限涨。
静音分层。
| 用户要求 | 系统行为 |
|---|---|
| 别说了,先安静 | 停止主动发言,继续观察。他主动问时仍回答 |
| 暂时别陪看 | 闸门锁住主动候选,当前场景理解留着 |
| 别看了,关闭 glance | 停止采集、识别和主动陪看 |
| 他主动问屏幕 | 走用户旁路,不被「禁止主动发言」误伤 |
故障和资源。 采集序号单调递增,时序不靠系统时钟回拨。眼睛请求有并发上限,识别队列有界。超时和错误有限重试、退避,不瞬间补打一串。眼睛不可用时,场景卡标来源失效,不放行新的主动陪看;用户正常对话不受影响。恢复后从当前画面重开一段连续,不假装补看了故障期间的内容。
这些留到后面,不挡住第一条持续链路:沉默原因标签、候选类型和不同频率预算、secondary_objects、话题栈、对非当前话题的轻插话、更细的发言来源统计。第一版不做轻插话。silent 只记「考虑过、未说」。
下面几条不收进第一版,也不收进后面的默认做法:
- 快速划视频、切窗口只说明对象不稳、评论容易过期。用来压低这个对象的开口资格。不记成他不喜欢、或不想被陪。
- 不给「他在看」做数值衰减。电影暂停、画面不动,不能推出他离开。用新鲜、停滞、来源失效这几个状态。不知道他还在不在看,就保持未知。
- 报错、剧情转折不能突破连续未回应形成的降频,也不能突破静音、他正在说话、话题失效。优先级只在候选之间比较。
- 电影资料不按「早于当前画面时间」过滤。对不上播放进度的剧情资料不进现场。
第一版验收
前七条是场景和开口。持续采集没做好,它们也可能全部通过。8 到 13 覆盖两条状态机,14 到 19 覆盖最大间隔、dialogue_epoch、静音分层、计划老化、故障恢复和资源上限。
- 电影里的人在跑,不会写成用户在跑。
- 同一部片标题不变,情节理解仍会更新。
- 连续刷十条视频,可以一句都不说。
- 用户还在讨论上一条视频时,下一条画面不抢走话题。
- 写代码顺利时不打扰。持续报错,和用户主动求助,是两种陪法。
- 生成期间用户切走,旧评论作废,不补说。
- 没有值得交给她的点时,系统保持安静,不用另一种说法把画面复述出来。
- 她生成或播放一句主动评论期间,截屏和识别不暂停。评论结束后,接上这段时间的屏幕内容,不从评论前的旧画面继续。
- 相邻识别窗口有重叠。重叠处的同一个情节、报错或歌词只形成一个理解点,不重复交给 dsh。
- 较早的识别结果较晚返回时,不覆盖较新的场景卡。处理超出帧缓冲时产生
continuity_gap,后面不假装看过缺失的那段。 - 屏幕已经从视频 A 到 B、对话仍在 A 时,两条焦点同时维持。A 聊完后,不补播此前作废的 A 评论,也不强行把用户拉回 B。
speak写入可见对话并关联话题。silent不产生可见发言,同一个点也不会在下一轮被反复提交。- 长时间连续运行后,观察日志按规则归档或裁剪,内存占用保持有界。送入 dsh 的【现场】也保持有界,只包含当前话题需要的内容。
- 电影或代码画面差分持续很低时,到达最大识别间隔仍会调用眼睛,不停在旧观察上。
- 用户开口时,排队中的候选和已经提交给 dsh 的在途候选全部绑定旧
dialogue_epoch。晚返回的结果不会播出。 - 「别说了」只禁止主动发言,用户主动问屏幕仍能正常回答。「别看了」才停止采集、识别和主动陪看。
- 长时间无有效进展后,计划退回旁观。持续事件尚未结束时,已考虑记录不会因时间老化而复活重复候选。事件结束后才按规则清理。
- 眼睛超时或不可用时,来源被标记失效,不再放行主动候选,普通对话不受影响。恢复后产生新的连续段,并保留故障期间的缺口。
- 眼睛请求并发和识别队列都有上限。超时重试有退避,不会在恢复后瞬间补打大量请求。采集序号不受系统时钟回拨影响。
落到哪
1 到 6 是已经落地的主干,不再改。下一步只做第 7 步屏幕时间线,先用验收 8、9、10、14、18、19 检查。过了再做第 8 步对话主题。对话主题要建在稳定、连续、不乱序的观察流上。
前六步是:
- 观察契约。
minicpm-o-demo/v47/engine.py每条观察带source: screen,只写屏幕事实。「他在干嘛」移出眼睛。 - 时序理解。
companion/backend/app/modules/glance/里保存连续观察,分开处理场景变化、对象变化和内容变化。场景卡带证据、置信度和未知项。眼睛进程不保存这些。 - 陪伴导演。 五类场景只提供理解重点和默认边界。计划按对象、进展和互动生成,不每眼重写。
- 闸门。 在
beside_cue之前。只过滤明显不该交给她的情况。放行后由 dsh 返回speak或silent。 - 旁路与时效。 用户一开口就停掉本轮主动候选,带已有场景先回应。主动句绑定
scene_id、object_id、topic_id、observation_time,开口前再查一次,失效丢弃。 - 现场。
beside_cue按上面的顺序送【来源】【理解】【陪伴】【刚说过】【画面经过】【眼前】。skills/watch/SKILL.md写明:屏幕上的人物和情节不是用户;闸门不替她表态;她可以不说。主动性只记有没有得到回应,不推断心情。投影只取和当前话题有关的摘要,完整观察留在日志里。
然后只做这两步,先 7 后 8:
- 屏幕时间线。 采集在 glance 开着时不停。识别另调度:变化可触发,到最大间隔也要识别,过密就合并。窗口重叠。
event_key在理解层延续。观察追加,序号单调递增、不靠时钟回拨,旧结果不覆盖新现场。超出缓冲写continuity_gap,缺口两侧不拼成连续剧情。日志按规则归档,内存有界。眼睛请求并发和队列有上限,超时退避,不在恢复后瞬间补打。眼睛不可用就标来源失效,不放行主动陪看,普通对话照旧。恢复后从当前画面重开,并留下故障期间的缺口。先用验收 8、9、10、14、18、19。 - 对话主题。 分开
screen_focus和conversation_focus。dialogue_epoch让排队和在途的主动结果一起失效,晚返回的不播出。speak写入同一场对话。silent只记「考虑过、未说」;持续事件没结束时不因老化复活。别说了和别看了分开。用户下一句先判断是在继续、在回应,还是新话题。说完再决定接着聊、放下,或按新画面重新理解。不补播,也不把人拉回新画面。计划和话题长时间没有进展就退回旁观。送进 dsh 的【现场】有界。对应验收 11、12、13、15、16、17。
摄像头第一版只留独立字段和接口。按钮看屏幕、代操作,都不在这份里。
眼睛负责看清内容。理解层负责弄清现场。陪伴导演负责选择关系。她自己负责有没有话、怎么说。