refactor: 将 A_Memorix 重构为主线长期记忆子系统并重建管理界面

- 将 A_Memorix 从旧 submodule / 插件形态迁入主线源码,主体落到 src/A_memorix
- 调整主程序接入方式,使 A_Memorix 作为源码内长期记忆子系统运行
- 回收父项目插件体系中针对 A_Memorix 的特判,减少对 plugin 通用层的侵入
- 将长期记忆配置、运行时、自检、导入、调优等能力收口到 memory 路由与主线服务层
- 重做长期记忆控制台与图谱页面,按 MaiBot 现有 dashboard 风格接入
- 补充实体关系图与证据视图双视图能力,支持查看节点、关系、段落及其证据链路
- 新增长期记忆配置编辑器与 memory-api,支持主线内配置管理
- 补齐删除管理能力:删除预览、混合删除、来源批量删除、删除操作恢复
- 优化删除预览与删除操作详情的前端展示,支持分页、检索,并以实体名/关系内容/段落摘要替代单纯 hash 展示
- 修复图谱与控制台相关前端问题,包括证据视图切换、查询触发时机、删除弹层空值保护等
- 新增或更新 A_Memorix 相关测试、WebUI 路由测试、前端 vitest 测试与辅助验证脚本
- 移除旧 plugins/A_memorix、.gitmodules 及相关历史维护文档
This commit is contained in:
A-Dawn
2026-04-03 08:08:24 +08:00
parent bf5eb45709
commit 15d436b3a1
136 changed files with 52533 additions and 629 deletions

View File

@@ -0,0 +1,65 @@
# Group Chat Stream Benchmark
这套基准数据专门用于 A_memorix 当前“群聊聊天流”设计的量化评估。
设计对齐点:
- 只把 Bot 参与过的话题段落纳入长期记忆总结。
- 群聊内容先按话题批次收束,再写入 `chat_summary`
- 回复前通过 `search_long_term_memory` 做长期记忆检索增强。
- 回复后只把“关于人物的稳定事实”写回 `person_fact`
- 检索需要覆盖 `search / time / episode / aggregate` 四种模式。
- 需要有明确的负样本,验证“无 Bot 参与”的纯群友闲聊不会被误写入。
- 当前 summarizer 的原生触发条件需要被显式覆盖:
`80` 条消息直接触发,或 `8` 小时后累计至少 `20` 条消息触发。
数据文件:
- `group_chat_stream_memory_benchmark.json`
- `group_chat_stream_memory_benchmark_hard.json`
第二套更长、更刁钻的压力数据,刻意加入跨话题重叠词、自然句 episode query、
以及更容易淹没人物事实的长聊天流,用于验证修复是否具有泛化效果。
推荐量化指标:
- `search.accuracy_at_1`
- `search.recall_at_5`
- `search.keyword_recall_at_5`
- `knowledge_fetcher.success_rate`
- `profile.success_rate`
- `writeback.success_rate`
- `episode_generation.success_rate`
- `negative_control.zero_hit_rate`
当前 fixture 结构:
- `simulated_stream_batches`
用于模拟话题级聊天窗口适合检索、episode、画像、写回等离线量化评估。
- `runtime_trigger_streams`
用于模拟真正能触发当前 summarizer 阈值的原生聊天流。
这部分数据满足 `20 条 + 8 小时` 的时间触发条件,可直接用于验证
“是否进入话题检查”与“无 Bot 发言是否被丢弃”。
- `chat_history_records`
用于模拟宿主将群聊话题总结后写入长期记忆的主路径。
- `person_writebacks`
用于模拟发送回复后的稳定人物事实写回。
- `search_cases / time_cases / episode_cases / knowledge_fetcher_cases / profile_cases`
用于直接驱动量化检索评估。
- `negative_control_cases`
用于验证“无 Bot 发言的群聊片段应被忽略”。
覆盖主题:
- 值班柜第二层的备用物资与物资报备
- 停电夜投影仪抢救与应急灯 / 橙色延长线盘
- 风铃观测前的温湿度计校准与无糖姜茶
- 东侧窗边狸花猫、绿色硬壳笔记本与黄铜回形针
- 无 Bot 参与的零食闲聊负样本
使用建议:
- 如果要验证“当前 summarizer 是否真的会被触发”,优先喂 `runtime_trigger_streams`
- 如果要验证“当前实现是否真正符合总结后写入和检索设计”,优先喂 `simulated_stream_batches``chat_history_records`
- 如果要快速跑检索、画像、episode、写回指标直接使用 `chat_history_records + person_writebacks + cases`
- 如果要切换到第二套压力数据,可在运行 benchmark 前设置
`A_MEMORIX_BENCHMARK_DATA_FILE=pytests/A_memorix_test/data/benchmarks/group_chat_stream_memory_benchmark_hard.json`

View File

@@ -0,0 +1,728 @@
{
"meta": {
"scenario_id": "group_chat_stream_memory_benchmark",
"description": "面向群聊聊天流的长期记忆量化评估数据集,覆盖 Bot 参与门槛、话题收束、检索增强、人物事实写回与负样本忽略。",
"designed_for": [
"group_chat",
"topic_batching",
"bot_participation_gate",
"runtime_trigger",
"search_mode",
"time_mode",
"episode_mode",
"aggregate_mode",
"person_fact_writeback",
"negative_control"
],
"quantitative_targets": {
"search": {
"accuracy_at_1": 0.4,
"recall_at_5": 0.75,
"keyword_recall_at_5": 0.85
},
"knowledge_fetcher": {
"success_rate": 0.75,
"keyword_recall": 0.8
},
"profile": {
"success_rate": 0.67,
"evidence_rate": 1.0
},
"writeback": {
"success_rate": 0.67,
"keyword_recall": 0.8
},
"episode": {
"success_rate": 0.75,
"keyword_recall": 0.8
},
"negative_control": {
"zero_hit_rate": 1.0
},
"runtime_trigger": {
"positive_trigger_rate": 1.0,
"negative_discard_rate": 1.0
}
}
},
"session": {
"session_id": "qq_group_424242",
"platform": "qq",
"user_id": "10086",
"group_id": "424242",
"display_name": "松烟阁夜谈群"
},
"simulated_stream_batches": [
{
"batch_id": "supply_round_march_03",
"topic": "值班柜第二层物资补位",
"bot_participated": true,
"expected_behavior": "should_be_summarized",
"message_count": 12,
"participants": [
"Mai",
"林澈",
"周枝",
"秦昭",
"唐未"
],
"start_time": 1772526180.0,
"end_time": 1772526960.0,
"messages": [
"[2026-03-03 19:03] 林澈:空调又直吹值班桌,我先把风扇拨回二档。值班柜第二层是不是只剩一把备用钥匙了?我记得黑柄折叠伞和创可贴本来都在那层。",
"[2026-03-03 19:04] 周枝:昨天雨太大,我把黑柄折叠伞借给来送样本的人了,回来的时候顺手挂回柜里,但创可贴我没核对。",
"[2026-03-03 19:05] 秦昭:柜门里侧还夹着旧清单,最底下一行写着“备用钥匙、折叠伞、创可贴、充电头”,不过充电头早就改放一层了。",
"[2026-03-03 19:06] Mai那我记一下第二层以后固定只放备用钥匙、黑柄折叠伞和创可贴谁临时拿走就在群里报备不然下次值夜的人会抓瞎。",
"[2026-03-03 19:08] 唐未:我刚看了一眼,创可贴只剩两片,外包装已经翘边了,最好连同备用纱布一起补一盒新的。",
"[2026-03-03 19:10] 林澈:我晚上值夜的时候最怕找东西翻半天,尤其空调风一吹头就疼,所以物资位置还是固定最省心。",
"[2026-03-03 19:11] 周枝:我待会儿把创可贴和纱布一起补上,再把备用钥匙套个红绳,不然黑灯找起来太慢。",
"[2026-03-03 19:12] 秦昭:折叠伞我建议继续放第二层最左边,别再和记录本挤一起,上回纸边都被伞骨刮卷了。",
"[2026-03-03 19:14] Mai我把“第二层左侧放折叠伞中间放备用钥匙右边放创可贴”记到今晚值班备注里。",
"[2026-03-03 19:15] 唐未:顺便提醒一下,如果谁把第二层的物资借出去,至少在群里发一句“已借出+归还时间”,别只口头说。",
"[2026-03-03 19:16] 周枝:收到,我今晚补完会拍一张柜内照片,省得之后又靠记忆猜。",
"[2026-03-03 19:16] 林澈:谢谢,等柜里补齐了我就把桂花乌龙和个人杯子继续放回上层,省得和公用物资混在一起。"
],
"combined_text": "[2026-03-03 19:03] 林澈:空调又直吹值班桌,我先把风扇拨回二档。值班柜第二层是不是只剩一把备用钥匙了?我记得黑柄折叠伞和创可贴本来都在那层。\n[2026-03-03 19:04] 周枝:昨天雨太大,我把黑柄折叠伞借给来送样本的人了,回来的时候顺手挂回柜里,但创可贴我没核对。\n[2026-03-03 19:05] 秦昭:柜门里侧还夹着旧清单,最底下一行写着“备用钥匙、折叠伞、创可贴、充电头”,不过充电头早就改放一层了。\n[2026-03-03 19:06] Mai那我记一下第二层以后固定只放备用钥匙、黑柄折叠伞和创可贴谁临时拿走就在群里报备不然下次值夜的人会抓瞎。\n[2026-03-03 19:08] 唐未:我刚看了一眼,创可贴只剩两片,外包装已经翘边了,最好连同备用纱布一起补一盒新的。\n[2026-03-03 19:10] 林澈:我晚上值夜的时候最怕找东西翻半天,尤其空调风一吹头就疼,所以物资位置还是固定最省心。\n[2026-03-03 19:11] 周枝:我待会儿把创可贴和纱布一起补上,再把备用钥匙套个红绳,不然黑灯找起来太慢。\n[2026-03-03 19:12] 秦昭:折叠伞我建议继续放第二层最左边,别再和记录本挤一起,上回纸边都被伞骨刮卷了。\n[2026-03-03 19:14] Mai我把“第二层左侧放折叠伞中间放备用钥匙右边放创可贴”记到今晚值班备注里。\n[2026-03-03 19:15] 唐未:顺便提醒一下,如果谁把第二层的物资借出去,至少在群里发一句“已借出+归还时间”,别只口头说。\n[2026-03-03 19:16] 周枝:收到,我今晚补完会拍一张柜内照片,省得之后又靠记忆猜。\n[2026-03-03 19:16] 林澈:谢谢,等柜里补齐了我就把桂花乌龙和个人杯子继续放回上层,省得和公用物资混在一起。",
"expected_memory_targets": [
"值班柜第二层",
"备用钥匙",
"黑柄折叠伞",
"创可贴",
"报备"
]
},
{
"batch_id": "blackout_projection_march_06",
"topic": "停电夜投影仪抢救",
"bot_participated": true,
"expected_behavior": "should_be_summarized",
"message_count": 11,
"participants": [
"Mai",
"秦昭",
"唐未",
"林澈",
"周枝"
],
"start_time": 1772791980.0,
"end_time": 1772792820.0,
"messages": [
"[2026-03-06 21:33] 秦昭:北墙射灯刚关,投影仪才开到一半就跳闸了,整间资料室一下子全黑,镜头还没盖上。",
"[2026-03-06 21:34] 唐未:别先碰镜头,我去摸仪器桌下层的应急灯,上次检修后应该还塞在最里边。",
"[2026-03-06 21:35] 林澈:我在门边摸到橙色延长线盘了,先别急着全拉开,确认是不是排插过载再说。",
"[2026-03-06 21:36] Mai先按顺序来唐未开应急灯秦昭别挪投影仪林澈看一下是不是延长线和热风枪同时挂在同一路上。",
"[2026-03-06 21:38] 唐未:应急灯找到了,在仪器桌下层右手边的蓝色档案盒后面,亮度够,至少能先护住镜头和电源键。",
"[2026-03-06 21:39] 秦昭:问题找到了,热风枪和投影仪都接在橙色延长线盘上,刚才我又把扫描灯也插进去了,估计就是这个组合把闸打掉了。",
"[2026-03-06 21:40] 周枝:我把热风枪先拔掉,扫描灯改到东墙独立插口,橙色延长线盘只留投影仪和笔记本电源。",
"[2026-03-06 21:41] Mai先恢复最少设备镜头盖等重新上电稳定后再扣别在黑里来回碰。",
"[2026-03-06 21:43] 林澈:现在电回来了,投影仪风扇声正常,橙色延长线盘没有再冒热,看来就是同路负载堆太多。",
"[2026-03-06 21:44] 秦昭:记一下,应急灯平时别乱挪,必须固定放在仪器桌下层,橙色延长线盘也只给投影相关设备用。",
"[2026-03-06 21:47] Mai我已经记到故障备忘里了停电夜先找仪器桌下层应急灯再检查橙色延长线盘负载不要让热风枪和投影仪挂同一路。"
],
"combined_text": "[2026-03-06 21:33] 秦昭:北墙射灯刚关,投影仪才开到一半就跳闸了,整间资料室一下子全黑,镜头还没盖上。\n[2026-03-06 21:34] 唐未:别先碰镜头,我去摸仪器桌下层的应急灯,上次检修后应该还塞在最里边。\n[2026-03-06 21:35] 林澈:我在门边摸到橙色延长线盘了,先别急着全拉开,确认是不是排插过载再说。\n[2026-03-06 21:36] Mai先按顺序来唐未开应急灯秦昭别挪投影仪林澈看一下是不是延长线和热风枪同时挂在同一路上。\n[2026-03-06 21:38] 唐未:应急灯找到了,在仪器桌下层右手边的蓝色档案盒后面,亮度够,至少能先护住镜头和电源键。\n[2026-03-06 21:39] 秦昭:问题找到了,热风枪和投影仪都接在橙色延长线盘上,刚才我又把扫描灯也插进去了,估计就是这个组合把闸打掉了。\n[2026-03-06 21:40] 周枝:我把热风枪先拔掉,扫描灯改到东墙独立插口,橙色延长线盘只留投影仪和笔记本电源。\n[2026-03-06 21:41] Mai先恢复最少设备镜头盖等重新上电稳定后再扣别在黑里来回碰。\n[2026-03-06 21:43] 林澈:现在电回来了,投影仪风扇声正常,橙色延长线盘没有再冒热,看来就是同路负载堆太多。\n[2026-03-06 21:44] 秦昭:记一下,应急灯平时别乱挪,必须固定放在仪器桌下层,橙色延长线盘也只给投影相关设备用。\n[2026-03-06 21:47] Mai我已经记到故障备忘里了停电夜先找仪器桌下层应急灯再检查橙色延长线盘负载不要让热风枪和投影仪挂同一路。",
"expected_memory_targets": [
"停电夜",
"投影仪",
"应急灯",
"橙色延长线盘",
"仪器桌下层"
]
},
{
"batch_id": "wind_bell_observation_march_10",
"topic": "风铃观测与姜茶准备",
"bot_participated": true,
"expected_behavior": "should_be_summarized",
"message_count": 12,
"participants": [
"Mai",
"唐未",
"林澈",
"周枝",
"许棠"
],
"start_time": 1773136020.0,
"end_time": 1773136980.0,
"messages": [
"[2026-03-10 20:27] 唐未:周六夜里要做风铃塔观测,我下午先去校准温湿度计,镜片也顺便擦一遍,不然露点记录会飘。",
"[2026-03-10 20:28] 林澈:我可以把南平台的小风扇带上去,但别让我坐空调口边上值守,上次吹得我偏头痛一整晚。",
"[2026-03-10 20:30] 周枝:观测箱里还剩两包姜片,我去值班室再补一盒无糖姜茶,甜的那种唐未不喝。",
"[2026-03-10 20:31] 许棠:屋顶东侧栏杆边那只风铃这两天响得特别密,最好把记录本提前夹在硬板夹上,别到时一手按纸一手扶灯。",
"[2026-03-10 20:32] Mai那就按这个分工唐未负责温湿度计和镜片周枝准备无糖姜茶和记录夹林澈盯现场风向和风扇位置。",
"[2026-03-10 20:35] 唐未:我会把校准后的时间写在第一页右上角,免得后面整理的时候又分不清哪组数据是校准前的。",
"[2026-03-10 20:37] 林澈:如果夜里温差太大,我会把风扇固定在二档,既能带走雾气,又不至于把纸吹跑。",
"[2026-03-10 20:39] 周枝:姜茶我放保温壶里,标签写“无糖”,免得有人顺手加糖包,唐未每次都喝不下去。",
"[2026-03-10 20:41] 许棠:记录本别用软封皮,上回屋顶起风,边角被栏杆刮得卷起来,还是硬板夹最稳。",
"[2026-03-10 20:43] Mai再补一条风铃塔观测前先在群里报“温湿度计已校准”这样后面谁接手都知道状态。",
"[2026-03-10 20:45] 唐未:收到,我会先发校准完成,再把镜片状态和气温一起报出来。",
"[2026-03-10 20:46] 林澈:那我带桂花乌龙给自己,公用保温壶就只放无糖姜茶,别把味道混了。"
],
"combined_text": "[2026-03-10 20:27] 唐未:周六夜里要做风铃塔观测,我下午先去校准温湿度计,镜片也顺便擦一遍,不然露点记录会飘。\n[2026-03-10 20:28] 林澈:我可以把南平台的小风扇带上去,但别让我坐空调口边上值守,上次吹得我偏头痛一整晚。\n[2026-03-10 20:30] 周枝:观测箱里还剩两包姜片,我去值班室再补一盒无糖姜茶,甜的那种唐未不喝。\n[2026-03-10 20:31] 许棠:屋顶东侧栏杆边那只风铃这两天响得特别密,最好把记录本提前夹在硬板夹上,别到时一手按纸一手扶灯。\n[2026-03-10 20:32] Mai那就按这个分工唐未负责温湿度计和镜片周枝准备无糖姜茶和记录夹林澈盯现场风向和风扇位置。\n[2026-03-10 20:35] 唐未:我会把校准后的时间写在第一页右上角,免得后面整理的时候又分不清哪组数据是校准前的。\n[2026-03-10 20:37] 林澈:如果夜里温差太大,我会把风扇固定在二档,既能带走雾气,又不至于把纸吹跑。\n[2026-03-10 20:39] 周枝:姜茶我放保温壶里,标签写“无糖”,免得有人顺手加糖包,唐未每次都喝不下去。\n[2026-03-10 20:41] 许棠:记录本别用软封皮,上回屋顶起风,边角被栏杆刮得卷起来,还是硬板夹最稳。\n[2026-03-10 20:43] Mai再补一条风铃塔观测前先在群里报“温湿度计已校准”这样后面谁接手都知道状态。\n[2026-03-10 20:45] 唐未:收到,我会先发校准完成,再把镜片状态和气温一起报出来。\n[2026-03-10 20:46] 林澈:那我带桂花乌龙给自己,公用保温壶就只放无糖姜茶,别把味道混了。",
"expected_memory_targets": [
"风铃塔观测",
"温湿度计",
"无糖姜茶",
"硬板夹",
"屋顶东侧栏杆"
]
},
{
"batch_id": "archive_cat_march_14",
"topic": "东侧窗边狸花猫与绿色笔记本",
"bot_participated": true,
"expected_behavior": "should_be_summarized",
"message_count": 11,
"participants": [
"Mai",
"许棠",
"周枝",
"秦昭",
"林澈"
],
"start_time": 1773478440.0,
"end_time": 1773479280.0,
"messages": [
"[2026-03-14 19:34] 许棠:东侧窗边那只狸花猫又钻进档案室了,刚才直接跳上长桌,把绿色硬壳笔记本踩得翻到中间页。",
"[2026-03-14 19:35] 周枝:长桌上不是还夹着黄铜回形针和旧雨量页吗?猫要是蹭一下,纸页顺着窗缝就容易飞。",
"[2026-03-14 19:36] 秦昭:我刚把窗缝先关小了,绿色硬壳笔记本已经挪到蓝色档案盒上面,黄铜回形针我也收回铁盘里了。",
"[2026-03-14 19:38] Mai先别赶猫确认一下它是不是又往暖气后面钻。长桌这边只保留绿色笔记本和今晚要抄的旧雨量页其他散物都收走。",
"[2026-03-14 19:40] 林澈:狸花猫刚从暖气后面出来,蹭了一圈又去窗边晒了,至少没再踩记录纸。我顺手把长桌右侧的纸镇也压上了。",
"[2026-03-14 19:41] 许棠:绿色笔记本第七页记的是去年秋天的风速补注,别被猫爪勾破了,那页我明天还要录系统。",
"[2026-03-14 19:43] 周枝:以后东侧窗边长桌如果要摊资料,先把黄铜回形针和纸镇放好,不然有风再加猫,纸真的收不回来。",
"[2026-03-14 19:45] 秦昭:蓝色档案盒我也顺手换到桌角了,避免猫跳上来时把整盒推下去。",
"[2026-03-14 19:46] Mai我记个规则东侧窗边长桌只放绿色硬壳笔记本、当次要抄的纸页和纸镇黄铜回形针统一回铁盘。",
"[2026-03-14 19:48] 林澈:那只狸花猫现在缩在窗台垫子上了,看样子只是找暖和,不是故意拆台。",
"[2026-03-14 19:48] 许棠:收到,明天我会先去看第七页,再决定要不要把整本绿色笔记本转移到里间。"
],
"combined_text": "[2026-03-14 19:34] 许棠:东侧窗边那只狸花猫又钻进档案室了,刚才直接跳上长桌,把绿色硬壳笔记本踩得翻到中间页。\n[2026-03-14 19:35] 周枝:长桌上不是还夹着黄铜回形针和旧雨量页吗?猫要是蹭一下,纸页顺着窗缝就容易飞。\n[2026-03-14 19:36] 秦昭:我刚把窗缝先关小了,绿色硬壳笔记本已经挪到蓝色档案盒上面,黄铜回形针我也收回铁盘里了。\n[2026-03-14 19:38] Mai先别赶猫确认一下它是不是又往暖气后面钻。长桌这边只保留绿色笔记本和今晚要抄的旧雨量页其他散物都收走。\n[2026-03-14 19:40] 林澈:狸花猫刚从暖气后面出来,蹭了一圈又去窗边晒了,至少没再踩记录纸。我顺手把长桌右侧的纸镇也压上了。\n[2026-03-14 19:41] 许棠:绿色笔记本第七页记的是去年秋天的风速补注,别被猫爪勾破了,那页我明天还要录系统。\n[2026-03-14 19:43] 周枝:以后东侧窗边长桌如果要摊资料,先把黄铜回形针和纸镇放好,不然有风再加猫,纸真的收不回来。\n[2026-03-14 19:45] 秦昭:蓝色档案盒我也顺手换到桌角了,避免猫跳上来时把整盒推下去。\n[2026-03-14 19:46] Mai我记个规则东侧窗边长桌只放绿色硬壳笔记本、当次要抄的纸页和纸镇黄铜回形针统一回铁盘。\n[2026-03-14 19:48] 林澈:那只狸花猫现在缩在窗台垫子上了,看样子只是找暖和,不是故意拆台。\n[2026-03-14 19:48] 许棠:收到,明天我会先去看第七页,再决定要不要把整本绿色笔记本转移到里间。",
"expected_memory_targets": [
"东侧窗边",
"狸花猫",
"绿色硬壳笔记本",
"黄铜回形针",
"蓝色档案盒"
]
},
{
"batch_id": "snack_gossip_march_15_negative",
"topic": "无 Bot 参与的零食闲聊负样本",
"bot_participated": false,
"expected_behavior": "ignored_by_summarizer_without_bot_message",
"message_count": 8,
"participants": [
"许棠",
"周枝",
"秦昭",
"林澈"
],
"start_time": 1773565320.0,
"end_time": 1773566160.0,
"messages": [
"[2026-03-15 19:42] 许棠:我准备给周末值班买海盐柠檬饼干,你们有人忌口吗?",
"[2026-03-15 19:43] 周枝:我不吃太甜的,但海盐的可以,顺便来点原味苏打更稳。",
"[2026-03-15 19:45] 秦昭:我想要辣味海苔片,饼干别买太碎的,上次全压成粉了。",
"[2026-03-15 19:46] 林澈:如果有无糖薄荷糖也帮我带一盒,值夜后半段嘴里太淡容易犯困。",
"[2026-03-15 19:48] 许棠:那我就下单海盐柠檬饼干、原味苏打、辣味海苔和无糖薄荷糖。",
"[2026-03-15 19:50] 周枝:别忘了备注送到北门值班室,不然又会被前台放到快递架最里面。",
"[2026-03-15 19:54] 秦昭:海苔别买大片装,碎屑掉在键盘里太难清。",
"[2026-03-15 19:56] 林澈:收到,等到了我去北门拿。"
],
"combined_text": "[2026-03-15 19:42] 许棠:我准备给周末值班买海盐柠檬饼干,你们有人忌口吗?\n[2026-03-15 19:43] 周枝:我不吃太甜的,但海盐的可以,顺便来点原味苏打更稳。\n[2026-03-15 19:45] 秦昭:我想要辣味海苔片,饼干别买太碎的,上次全压成粉了。\n[2026-03-15 19:46] 林澈:如果有无糖薄荷糖也帮我带一盒,值夜后半段嘴里太淡容易犯困。\n[2026-03-15 19:48] 许棠:那我就下单海盐柠檬饼干、原味苏打、辣味海苔和无糖薄荷糖。\n[2026-03-15 19:50] 周枝:别忘了备注送到北门值班室,不然又会被前台放到快递架最里面。\n[2026-03-15 19:54] 秦昭:海苔别买大片装,碎屑掉在键盘里太难清。\n[2026-03-15 19:56] 林澈:收到,等到了我去北门拿。",
"expected_memory_targets": [
"海盐柠檬饼干",
"原味苏打",
"辣味海苔",
"无糖薄荷糖"
]
}
],
"runtime_trigger_streams": [
{
"stream_id": "runtime_supply_trigger_march_18",
"topic": "值班柜第二层物资规则长流触发样本",
"trigger_mode": "time_threshold",
"elapsed_since_last_check_hours": 8.7,
"bot_participated": true,
"expected_check_outcome": "should_trigger_topic_check_and_pass_bot_gate",
"expected_next_stage": "topic_cache_should_update",
"message_count": 22,
"participants": [
"Mai",
"林澈",
"周枝",
"秦昭",
"唐未"
],
"start_time": 1773795720.0,
"end_time": 1773827160.0,
"messages": [
"[2026-03-18 09:02] 林澈:我早上开柜门的时候又被空调风正面吹到,顺手看了一眼,值班柜第二层现在只有备用钥匙和半包创可贴了。",
"[2026-03-18 09:05] 周枝:黑柄折叠伞昨晚借给送样的人还没放回,我记得归还口头说的是今天中午前。",
"[2026-03-18 09:07] 秦昭:柜门里那张旧清单我还没撕,最下面还是写着备用钥匙、折叠伞、创可贴,不过字已经糊了。",
"[2026-03-18 09:09] 唐未:创可贴我刚翻过,只剩一条完整包装,纱布也只余两片,今天最好一起补齐。",
"[2026-03-18 09:12] Mai先别各自乱挪今天这轮就按第二层只放备用钥匙、黑柄折叠伞、创可贴和备用纱布来整理借出统一在群里报备。",
"[2026-03-18 09:16] 林澈:收到,我主要担心晚上值夜的时候黑灯摸东西太慢,所以备用钥匙最好再挂个醒目的红绳。",
"[2026-03-18 09:24] 周枝:我中午前会带红绳过去,顺手把创可贴和纱布一起补上,再拍一张第二层的照片。",
"[2026-03-18 09:31] 秦昭:折叠伞还是建议固定在第二层最左边,钥匙放中间,创可贴和纱布靠右,别再和记录本混在一起。",
"[2026-03-18 10:02] 唐未:我去医务柜那边借到了一盒新创可贴和一包小纱布,午休回来就补进去。",
"[2026-03-18 10:28] Mai补充一条凡是从第二层拿走物资都要发“已借出+用途+预计归还时间”,不要只在现场口头说。",
"[2026-03-18 11:14] 周枝:我先把备用钥匙套上红绳了,照片也拍了,不过黑柄折叠伞还在门口晾水,等干一点再放回柜里。",
"[2026-03-18 12:43] 林澈:我刚在值班桌后面找到那把黑柄折叠伞,伞骨没问题,就是伞套湿着,我先挂在柜侧透气。",
"[2026-03-18 13:26] 秦昭:旧清单我重新写了一份,第二层现在明确写成“左伞中钥匙右医用小物”,这样新来的人也看得懂。",
"[2026-03-18 14:18] 唐未:新的创可贴和纱布都补齐了,我另外塞了一卷医用胶带,但没和创可贴压在一起,怕拿的时候全带出来。",
"[2026-03-18 15:07] Mai医用胶带别挤占创可贴的位置优先保证备用钥匙、黑柄折叠伞、创可贴、纱布这四样一眼能看到。",
"[2026-03-18 15:52] 周枝:现在第二层左边是折叠伞,中间是带红绳的备用钥匙,右边是创可贴和纱布,拍照存档已经发群文件了。",
"[2026-03-18 16:21] 林澈:这样就清楚多了,我自己的桂花乌龙和杯子还是继续放上层,不和公用物资混放。",
"[2026-03-18 16:57] 秦昭:我把旧清单撕掉了,柜门里只留新版规则,写了借出后必须报备和归还时间。",
"[2026-03-18 17:08] 唐未:医用小物这边都齐了,晚上如果还有人借伞或拿创可贴,记得照新版格式报一下。",
"[2026-03-18 17:24] Mai今晚值班备注我已经更新成固定摆放规则了之后默认按这版执行除非群里另行通知。",
"[2026-03-18 17:38] 周枝:我补一条,借出的东西最好二十四小时内归还,超过时间就在群里继续报状态。",
"[2026-03-18 17:46] 林澈:这个规则对夜班太友好了,至少以后不会一边被空调吹一边在柜里翻半天。"
],
"expected_memory_targets": [
"值班柜第二层",
"备用钥匙",
"黑柄折叠伞",
"创可贴",
"纱布",
"报备"
]
},
{
"stream_id": "runtime_snack_gossip_trigger_march_22_negative",
"topic": "无 Bot 参与的零食拼单长流负样本",
"trigger_mode": "time_threshold",
"elapsed_since_last_check_hours": 8.6,
"bot_participated": false,
"expected_check_outcome": "should_trigger_topic_check_but_be_discarded_without_bot_message",
"expected_next_stage": "topic_cache_should_not_update",
"message_count": 21,
"participants": [
"许棠",
"周枝",
"秦昭",
"林澈"
],
"start_time": 1774144860.0,
"end_time": 1774175700.0,
"messages": [
"[2026-03-22 10:01] 许棠:周末值班太长了,我准备拼一单零食,先问下大家海盐柠檬饼干和原味苏打能不能接受。",
"[2026-03-22 10:12] 周枝:原味苏打可以,海盐柠檬也行,但别买太甜的夹心款,我夜里吃那个会腻。",
"[2026-03-22 10:20] 秦昭:我想要辣味海苔片,别选大片易掉渣的版本,上回碎屑全进键盘缝了。",
"[2026-03-22 10:33] 林澈:如果有无糖薄荷糖也帮我带一盒,后半夜嘴里太淡的时候容易犯困。",
"[2026-03-22 10:52] 许棠:那我先记海盐柠檬饼干、原味苏打、辣味海苔和无糖薄荷糖,坚果你们有人想加吗?",
"[2026-03-22 11:15] 周枝:坚果少一点吧,值班桌一忙起来很容易忘记封口,还是独立小包装更稳。",
"[2026-03-22 11:42] 秦昭:海苔一定别买油太大的,拿文件之前还得擦手太麻烦。",
"[2026-03-22 12:08] 林澈:如果可以的话顺便来两包原味小麻花,脆一点但别掉很多渣。",
"[2026-03-22 12:44] 许棠:行,我把小麻花也记上,不过总价快到免配送门槛了,再凑一点就够。",
"[2026-03-22 13:17] 周枝:可以补几包茶包,别太香精味重,清淡一点的红茶或者大麦茶都行。",
"[2026-03-22 13:58] 秦昭:辣条就别买了,油会蹭到键帽上,还是海苔和苏打这种最安全。",
"[2026-03-22 14:26] 林澈:如果店里有小盒装薄荷糖,比大袋装方便,值夜带着走也不会散一桌。",
"[2026-03-22 15:09] 许棠:配送时间我打算约到晚上六点半左右,正好你们晚饭后能一起收。",
"[2026-03-22 15:47] 周枝:每个人单独贴名字吧,不然海盐柠檬和原味苏打长得太像,容易拿混。",
"[2026-03-22 16:13] 秦昭:再加一包湿巾也行,吃完零食直接擦手,省得摸鼠标留油印。",
"[2026-03-22 16:42] 林澈:奶味太重的糖就不要了,我夜里喝水本来就少,吃那个更容易口干。",
"[2026-03-22 17:01] 许棠:配送地址我本来想填前台,后来还是觉得北门值班室最稳,省得被塞到最里面。",
"[2026-03-22 17:18] 周枝:对,直接送北门值班室,备注写清楚到门口电话联系,不然外卖员总找不到。",
"[2026-03-22 17:46] 秦昭:如果凑单还差一点,就补最普通的瓜子,别买调味太重的版本。",
"[2026-03-22 18:12] 林澈:到了之后我去门口拿,顺便把无糖薄荷糖先分出来,免得被当成普通糖。",
"[2026-03-22 18:35] 许棠:已经下单了,截图我发群里,内容就是海盐柠檬饼干、原味苏打、辣味海苔、无糖薄荷糖、小麻花和茶包。"
],
"expected_memory_targets": []
}
],
"import_payload": {
"paragraphs": [
{
"content": "东侧档案室窗边长桌通常用于整理当晚资料,绿色硬壳笔记本、黄铜回形针和纸镇经常在这里配套出现。",
"source": "fixture:group_chat_stream_memory_benchmark",
"knowledge_type": "narrative",
"entities": [
"东侧档案室",
"长桌",
"绿色硬壳笔记本",
"黄铜回形针",
"纸镇"
]
},
{
"content": "停电或抢修投影设备时,仪器桌下层的应急灯和橙色延长线盘是最常被提到的两个关键物品。",
"source": "fixture:group_chat_stream_memory_benchmark",
"knowledge_type": "factual",
"entities": [
"应急灯",
"橙色延长线盘",
"仪器桌下层",
"投影设备"
]
},
{
"content": "周枝平时看管值班柜第二层的备用钥匙、黑柄折叠伞和创可贴,借出时要求在群里报备。",
"source": "fixture:group_chat_stream_memory_benchmark",
"knowledge_type": "factual",
"entities": [
"周枝",
"值班柜第二层",
"备用钥匙",
"黑柄折叠伞",
"创可贴"
]
},
{
"content": "唐未在观测前通常先校准温湿度计和擦镜片,并且只喝无糖姜茶。",
"source": "fixture:group_chat_stream_memory_benchmark",
"knowledge_type": "factual",
"entities": [
"唐未",
"温湿度计",
"镜片",
"无糖姜茶"
]
},
{
"content": "林澈值夜时不喜欢空调直吹,常把风扇固定在二档,同时会自带桂花乌龙。",
"source": "fixture:group_chat_stream_memory_benchmark",
"knowledge_type": "factual",
"entities": [
"林澈",
"空调直吹",
"风扇二档",
"桂花乌龙"
]
},
{
"content": "秦昭做投影演示前会先关北墙射灯,避免镜头反光,再检查投影设备是否与大功率工具共用同一路电源。",
"source": "fixture:group_chat_stream_memory_benchmark",
"knowledge_type": "factual",
"entities": [
"秦昭",
"北墙射灯",
"镜头反光",
"投影设备"
]
}
],
"relations": [
{
"subject": "周枝",
"predicate": "看管",
"object": "值班柜第二层"
},
{
"subject": "值班柜第二层",
"predicate": "存放",
"object": "黑柄折叠伞"
},
{
"subject": "唐未",
"predicate": "负责",
"object": "温湿度计校准"
},
{
"subject": "唐未",
"predicate": "偏好",
"object": "无糖姜茶"
},
{
"subject": "林澈",
"predicate": "不喜欢",
"object": "空调直吹"
},
{
"subject": "秦昭",
"predicate": "会先关闭",
"object": "北墙射灯"
},
{
"subject": "东侧档案室长桌",
"predicate": "摆放",
"object": "绿色硬壳笔记本"
}
]
},
"chat_history_records": [
{
"record_id": 920001,
"theme": "值班柜第二层物资补位",
"summary": "群里确认值班柜第二层固定放备用钥匙、黑柄折叠伞和创可贴,借出后必须在群里报备;林澈再次提到自己值夜时怕空调直吹,会把风扇调到二档。",
"participants": [
"Mai",
"林澈",
"周枝",
"秦昭",
"唐未"
],
"start_time": 1772526180.0,
"end_time": 1772526960.0,
"original_text": "[2026-03-03 19:03] 林澈:空调又直吹值班桌,我先把风扇拨回二档。值班柜第二层是不是只剩一把备用钥匙了?我记得黑柄折叠伞和创可贴本来都在那层。\n[2026-03-03 19:04] 周枝:昨天雨太大,我把黑柄折叠伞借给来送样本的人了,回来的时候顺手挂回柜里,但创可贴我没核对。\n[2026-03-03 19:05] 秦昭:柜门里侧还夹着旧清单,最底下一行写着“备用钥匙、折叠伞、创可贴、充电头”,不过充电头早就改放一层了。\n[2026-03-03 19:06] Mai那我记一下第二层以后固定只放备用钥匙、黑柄折叠伞和创可贴谁临时拿走就在群里报备不然下次值夜的人会抓瞎。\n[2026-03-03 19:08] 唐未:我刚看了一眼,创可贴只剩两片,外包装已经翘边了,最好连同备用纱布一起补一盒新的。\n[2026-03-03 19:10] 林澈:我晚上值夜的时候最怕找东西翻半天,尤其空调风一吹头就疼,所以物资位置还是固定最省心。\n[2026-03-03 19:11] 周枝:我待会儿把创可贴和纱布一起补上,再把备用钥匙套个红绳,不然黑灯找起来太慢。\n[2026-03-03 19:12] 秦昭:折叠伞我建议继续放第二层最左边,别再和记录本挤一起,上回纸边都被伞骨刮卷了。\n[2026-03-03 19:14] Mai我把“第二层左侧放折叠伞中间放备用钥匙右边放创可贴”记到今晚值班备注里。\n[2026-03-03 19:15] 唐未:顺便提醒一下,如果谁把第二层的物资借出去,至少在群里发一句“已借出+归还时间”,别只口头说。\n[2026-03-03 19:16] 周枝:收到,我今晚补完会拍一张柜内照片,省得之后又靠记忆猜。\n[2026-03-03 19:16] 林澈:谢谢,等柜里补齐了我就把桂花乌龙和个人杯子继续放回上层,省得和公用物资混在一起。"
},
{
"record_id": 920002,
"theme": "停电夜投影仪抢救",
"summary": "资料室停电后,群里确认应急灯固定放在仪器桌下层,橙色延长线盘只给投影相关设备使用;问题由热风枪、扫描灯和投影仪共路导致。",
"participants": [
"Mai",
"秦昭",
"唐未",
"林澈",
"周枝"
],
"start_time": 1772791980.0,
"end_time": 1772792820.0,
"original_text": "[2026-03-06 21:33] 秦昭:北墙射灯刚关,投影仪才开到一半就跳闸了,整间资料室一下子全黑,镜头还没盖上。\n[2026-03-06 21:34] 唐未:别先碰镜头,我去摸仪器桌下层的应急灯,上次检修后应该还塞在最里边。\n[2026-03-06 21:35] 林澈:我在门边摸到橙色延长线盘了,先别急着全拉开,确认是不是排插过载再说。\n[2026-03-06 21:36] Mai先按顺序来唐未开应急灯秦昭别挪投影仪林澈看一下是不是延长线和热风枪同时挂在同一路上。\n[2026-03-06 21:38] 唐未:应急灯找到了,在仪器桌下层右手边的蓝色档案盒后面,亮度够,至少能先护住镜头和电源键。\n[2026-03-06 21:39] 秦昭:问题找到了,热风枪和投影仪都接在橙色延长线盘上,刚才我又把扫描灯也插进去了,估计就是这个组合把闸打掉了。\n[2026-03-06 21:40] 周枝:我把热风枪先拔掉,扫描灯改到东墙独立插口,橙色延长线盘只留投影仪和笔记本电源。\n[2026-03-06 21:41] Mai先恢复最少设备镜头盖等重新上电稳定后再扣别在黑里来回碰。\n[2026-03-06 21:43] 林澈:现在电回来了,投影仪风扇声正常,橙色延长线盘没有再冒热,看来就是同路负载堆太多。\n[2026-03-06 21:44] 秦昭:记一下,应急灯平时别乱挪,必须固定放在仪器桌下层,橙色延长线盘也只给投影相关设备用。\n[2026-03-06 21:47] Mai我已经记到故障备忘里了停电夜先找仪器桌下层应急灯再检查橙色延长线盘负载不要让热风枪和投影仪挂同一路。"
},
{
"record_id": 920003,
"theme": "风铃观测与姜茶准备",
"summary": "群里安排周六夜间风铃塔观测,唐未负责校准温湿度计和擦镜片,周枝准备无糖姜茶与记录夹,林澈负责风向与风扇位置,观测前需先报温湿度计已校准。",
"participants": [
"Mai",
"唐未",
"林澈",
"周枝",
"许棠"
],
"start_time": 1773136020.0,
"end_time": 1773136980.0,
"original_text": "[2026-03-10 20:27] 唐未:周六夜里要做风铃塔观测,我下午先去校准温湿度计,镜片也顺便擦一遍,不然露点记录会飘。\n[2026-03-10 20:28] 林澈:我可以把南平台的小风扇带上去,但别让我坐空调口边上值守,上次吹得我偏头痛一整晚。\n[2026-03-10 20:30] 周枝:观测箱里还剩两包姜片,我去值班室再补一盒无糖姜茶,甜的那种唐未不喝。\n[2026-03-10 20:31] 许棠:屋顶东侧栏杆边那只风铃这两天响得特别密,最好把记录本提前夹在硬板夹上,别到时一手按纸一手扶灯。\n[2026-03-10 20:32] Mai那就按这个分工唐未负责温湿度计和镜片周枝准备无糖姜茶和记录夹林澈盯现场风向和风扇位置。\n[2026-03-10 20:35] 唐未:我会把校准后的时间写在第一页右上角,免得后面整理的时候又分不清哪组数据是校准前的。\n[2026-03-10 20:37] 林澈:如果夜里温差太大,我会把风扇固定在二档,既能带走雾气,又不至于把纸吹跑。\n[2026-03-10 20:39] 周枝:姜茶我放保温壶里,标签写“无糖”,免得有人顺手加糖包,唐未每次都喝不下去。\n[2026-03-10 20:41] 许棠:记录本别用软封皮,上回屋顶起风,边角被栏杆刮得卷起来,还是硬板夹最稳。\n[2026-03-10 20:43] Mai再补一条风铃塔观测前先在群里报“温湿度计已校准”这样后面谁接手都知道状态。\n[2026-03-10 20:45] 唐未:收到,我会先发校准完成,再把镜片状态和气温一起报出来。\n[2026-03-10 20:46] 林澈:那我带桂花乌龙给自己,公用保温壶就只放无糖姜茶,别把味道混了。"
},
{
"record_id": 920004,
"theme": "东侧窗边狸花猫与绿色笔记本",
"summary": "档案室东侧窗边的狸花猫跳上长桌,群里因此整理了绿色硬壳笔记本、黄铜回形针、蓝色档案盒和纸镇的摆放规则,避免纸页被风或猫弄乱。",
"participants": [
"Mai",
"许棠",
"周枝",
"秦昭",
"林澈"
],
"start_time": 1773478440.0,
"end_time": 1773479280.0,
"original_text": "[2026-03-14 19:34] 许棠:东侧窗边那只狸花猫又钻进档案室了,刚才直接跳上长桌,把绿色硬壳笔记本踩得翻到中间页。\n[2026-03-14 19:35] 周枝:长桌上不是还夹着黄铜回形针和旧雨量页吗?猫要是蹭一下,纸页顺着窗缝就容易飞。\n[2026-03-14 19:36] 秦昭:我刚把窗缝先关小了,绿色硬壳笔记本已经挪到蓝色档案盒上面,黄铜回形针我也收回铁盘里了。\n[2026-03-14 19:38] Mai先别赶猫确认一下它是不是又往暖气后面钻。长桌这边只保留绿色笔记本和今晚要抄的旧雨量页其他散物都收走。\n[2026-03-14 19:40] 林澈:狸花猫刚从暖气后面出来,蹭了一圈又去窗边晒了,至少没再踩记录纸。我顺手把长桌右侧的纸镇也压上了。\n[2026-03-14 19:41] 许棠:绿色笔记本第七页记的是去年秋天的风速补注,别被猫爪勾破了,那页我明天还要录系统。\n[2026-03-14 19:43] 周枝:以后东侧窗边长桌如果要摊资料,先把黄铜回形针和纸镇放好,不然有风再加猫,纸真的收不回来。\n[2026-03-14 19:45] 秦昭:蓝色档案盒我也顺手换到桌角了,避免猫跳上来时把整盒推下去。\n[2026-03-14 19:46] Mai我记个规则东侧窗边长桌只放绿色硬壳笔记本、当次要抄的纸页和纸镇黄铜回形针统一回铁盘。\n[2026-03-14 19:48] 林澈:那只狸花猫现在缩在窗台垫子上了,看样子只是找暖和,不是故意拆台。\n[2026-03-14 19:48] 许棠:收到,明天我会先去看第七页,再决定要不要把整本绿色笔记本转移到里间。"
}
],
"person_writebacks": [
{
"person_id": "6c4a50b4f4f34bdbb1a7a1c1e56c9001",
"person_name": "林澈",
"memory_content": "林澈值夜时不喜欢空调直吹,通常把风扇固定在二档,并且会自带桂花乌龙。",
"expected_keywords": [
"林澈",
"空调直吹",
"风扇",
"二档",
"桂花乌龙"
]
},
{
"person_id": "cbfd79d4680849b5ac7e23a3f6f09002",
"person_name": "唐未",
"memory_content": "唐未每次观测前都会先校准温湿度计和擦镜片,而且只喝无糖姜茶。",
"expected_keywords": [
"唐未",
"温湿度计",
"校准",
"镜片",
"无糖姜茶"
]
},
{
"person_id": "a6eb73d41251472a8d229f1202df9003",
"person_name": "周枝",
"memory_content": "周枝负责补和值班柜第二层的备用钥匙、黑柄折叠伞和创可贴,借出后会在群里报备。",
"expected_keywords": [
"周枝",
"值班柜第二层",
"备用钥匙",
"黑柄折叠伞",
"创可贴"
]
}
],
"search_cases": [
{
"query": "值班柜第二层 备用钥匙 折叠伞 创可贴",
"expected_keywords": [
"值班柜第二层",
"备用钥匙",
"黑柄折叠伞",
"创可贴"
],
"minimum_keyword_hits": 2
},
{
"query": "停电夜 投影仪 橙色延长线盘 应急灯",
"expected_keywords": [
"停电夜",
"投影仪",
"橙色延长线盘",
"应急灯"
],
"minimum_keyword_hits": 2
},
{
"query": "风铃塔观测 温湿度计 无糖姜茶",
"expected_keywords": [
"风铃塔观测",
"温湿度计",
"无糖姜茶",
"校准"
],
"minimum_keyword_hits": 2
},
{
"query": "东侧窗边 狸花猫 绿色硬壳笔记本 黄铜回形针",
"expected_keywords": [
"东侧窗边",
"狸花猫",
"绿色硬壳笔记本",
"黄铜回形针"
],
"minimum_keyword_hits": 2
}
],
"time_cases": [
{
"query": "投影仪 应急灯",
"time_expression": "2026/03/06",
"expected_keywords": [
"投影仪",
"应急灯",
"橙色延长线盘"
]
},
{
"query": "温湿度计 姜茶",
"time_expression": "2026/03/10",
"expected_keywords": [
"温湿度计",
"无糖姜茶",
"风铃塔观测"
]
},
{
"query": "狸花猫 绿色笔记本",
"time_expression": "2026/03/14",
"expected_keywords": [
"狸花猫",
"绿色硬壳笔记本",
"黄铜回形针"
]
}
],
"episode_cases": [
{
"query": "那次值班柜第二层重新补物资的经过",
"expected_keywords": [
"值班柜第二层",
"备用钥匙",
"黑柄折叠伞",
"创可贴"
],
"minimum_keyword_recall": 0.75
},
{
"query": "停电夜抢救投影仪的经过",
"expected_keywords": [
"停电夜",
"投影仪",
"应急灯",
"橙色延长线盘"
],
"minimum_keyword_recall": 0.75
},
{
"query": "风铃塔观测前准备姜茶和温湿度计的那次安排",
"expected_keywords": [
"风铃塔观测",
"温湿度计",
"无糖姜茶",
"硬板夹"
],
"minimum_keyword_recall": 0.75
},
{
"query": "东侧窗边那只狸花猫闯进档案室那次",
"expected_keywords": [
"东侧窗边",
"狸花猫",
"绿色硬壳笔记本",
"黄铜回形针"
],
"minimum_keyword_recall": 0.75
}
],
"knowledge_fetcher_cases": [
{
"query": "群里后来把值班柜第二层固定放哪些东西?",
"expected_keywords": [
"值班柜第二层",
"备用钥匙",
"黑柄折叠伞",
"创可贴"
],
"minimum_keyword_recall": 0.75
},
{
"query": "停电那次救投影仪时先找的是什么,延长线后来怎么规定?",
"expected_keywords": [
"应急灯",
"仪器桌下层",
"橙色延长线盘",
"投影相关设备"
],
"minimum_keyword_recall": 0.75
},
{
"query": "谁会在观测前校准温湿度计,还只喝无糖姜茶?",
"expected_keywords": [
"唐未",
"温湿度计",
"校准",
"无糖姜茶"
],
"minimum_keyword_recall": 0.75
}
],
"profile_cases": [
{
"person_id": "6c4a50b4f4f34bdbb1a7a1c1e56c9001",
"expected_keywords": [
"林澈",
"空调直吹",
"二档",
"桂花乌龙"
],
"minimum_keyword_recall": 0.75
},
{
"person_id": "cbfd79d4680849b5ac7e23a3f6f09002",
"expected_keywords": [
"唐未",
"温湿度计",
"校准",
"无糖姜茶"
],
"minimum_keyword_recall": 0.75
},
{
"person_id": "a6eb73d41251472a8d229f1202df9003",
"expected_keywords": [
"周枝",
"值班柜第二层",
"备用钥匙",
"黑柄折叠伞"
],
"minimum_keyword_recall": 0.75
}
],
"negative_control_cases": [
{
"query": "海盐柠檬饼干 原味苏打 辣味海苔 无糖薄荷糖",
"source_batch_id": "snack_gossip_march_15_negative",
"expected_behavior": "should_return_no_hits_if_only_positive_batches_are_ingested",
"reason": "当前设计要求没有 Bot 发言的群聊批次不应进入长期记忆总结主路径。"
}
]
}

View File

@@ -0,0 +1,862 @@
{
"meta": {
"scenario_id": "group_chat_stream_memory_benchmark_hard",
"description": "第二套更长、更刁钻的群聊聊天流长期记忆量化评估数据,刻意加入跨话题重叠词、自然句检索、长时段触发流以及更容易被群聊摘要淹没的人物事实。",
"designed_for": [
"group_chat",
"topic_batching",
"bot_participation_gate",
"runtime_trigger",
"search_mode",
"time_mode",
"episode_mode",
"aggregate_mode",
"person_fact_writeback",
"negative_control",
"cross_topic_overlap",
"hard_mode"
],
"quantitative_targets": {
"search": {
"accuracy_at_1": 0.4,
"recall_at_5": 0.75,
"keyword_recall_at_5": 0.85
},
"knowledge_fetcher": {
"success_rate": 0.75,
"keyword_recall": 0.8
},
"profile": {
"success_rate": 0.67,
"evidence_rate": 1.0
},
"writeback": {
"success_rate": 0.67,
"keyword_recall": 0.8
},
"episode": {
"success_rate": 0.75,
"keyword_recall": 0.8
},
"negative_control": {
"zero_hit_rate": 1.0
},
"runtime_trigger": {
"positive_trigger_rate": 1.0,
"negative_discard_rate": 1.0
}
}
},
"session": {
"session_id": "qq_group_535353",
"platform": "qq",
"user_id": "10010",
"group_id": "535353",
"display_name": "岚桥夜航协作群"
},
"import_payload": {
"paragraphs": [
{
"content": "北塔夹层药箱抽屉通常固定放铜牌备用钥匙、银色保温毯、薄荷膏和黑色丁腈手套,夜间临时取用后需要在群里补报用途与归还时间。",
"source": "fixture:group_chat_stream_memory_benchmark_hard",
"knowledge_type": "factual",
"entities": [
"北塔夹层药箱",
"铜牌备用钥匙",
"银色保温毯",
"薄荷膏",
"黑色丁腈手套"
]
},
{
"content": "雨棚工具架附近的短波电台返潮时,大家通常会先把机身擦干,再把黑色盘线和备用听筒重新卷回灰色防潮箱,最后更换新的硅胶包。",
"source": "fixture:group_chat_stream_memory_benchmark_hard",
"knowledge_type": "narrative",
"entities": [
"短波电台",
"黑色盘线",
"灰色防潮箱",
"硅胶包",
"备用听筒"
]
},
{
"content": "晨雾采样结束后,蓝盖保温箱里的冷敷袋、编号试剂架和玻璃记号笔需要按固定顺序归位,避免交接时把样本编号和温控记录混在一起。",
"source": "fixture:group_chat_stream_memory_benchmark_hard",
"knowledge_type": "factual",
"entities": [
"蓝盖保温箱",
"冷敷袋",
"编号试剂架",
"玻璃记号笔",
"样本编号"
]
},
{
"content": "西廊风口观测前,迟雨会先校准气压计并核对湿度夹板,桌边常备温梨汤和风向丝带,避免记录员在长风口里来回找物件。",
"source": "fixture:group_chat_stream_memory_benchmark_hard",
"knowledge_type": "factual",
"entities": [
"迟雨",
"气压计",
"湿度夹板",
"温梨汤",
"风向丝带"
]
},
{
"content": "沈砚值夜时不喜欢冷白荧光灯直照,常把小风扇固定在三档,并且会自带盐渍梅子气泡水放在个人层架上。",
"source": "fixture:group_chat_stream_memory_benchmark_hard",
"knowledge_type": "factual",
"entities": [
"沈砚",
"冷白荧光灯",
"小风扇三档",
"盐渍梅子气泡水"
]
},
{
"content": "贺岚平时看管北塔夹层药箱和铜牌备用钥匙,取用后会顺手补回黑色丁腈手套,并检查银色保温毯有没有被塞反。",
"source": "fixture:group_chat_stream_memory_benchmark_hard",
"knowledge_type": "factual",
"entities": [
"贺岚",
"北塔夹层药箱",
"铜牌备用钥匙",
"黑色丁腈手套",
"银色保温毯"
]
},
{
"content": "绘图室天窗边长桌常压着象牙描图纸、琥珀夹子和黄铜镇纸,白耳鸮闯入时最容易把这一角的纸页和夹具掀乱。",
"source": "fixture:group_chat_stream_memory_benchmark_hard",
"knowledge_type": "narrative",
"entities": [
"绘图室天窗边",
"象牙描图纸",
"琥珀夹子",
"黄铜镇纸",
"白耳鸮"
]
}
],
"relations": [
{
"subject": "贺岚",
"predicate": "看管",
"object": "北塔夹层药箱"
},
{
"subject": "北塔夹层药箱",
"predicate": "存放",
"object": "铜牌备用钥匙"
},
{
"subject": "北塔夹层药箱",
"predicate": "存放",
"object": "银色保温毯"
},
{
"subject": "顾澄",
"predicate": "维护",
"object": "短波电台"
},
{
"subject": "迟雨",
"predicate": "负责",
"object": "气压计校准"
},
{
"subject": "迟雨",
"predicate": "偏好",
"object": "温梨汤"
},
{
"subject": "沈砚",
"predicate": "不喜欢",
"object": "冷白荧光灯直照"
},
{
"subject": "绘图室天窗边",
"predicate": "摆放",
"object": "象牙描图纸"
}
]
},
"simulated_stream_batches": [
{
"batch_id": "mezzanine_first_aid_march_21",
"topic": "北塔夹层药箱补位",
"bot_participated": true,
"expected_behavior": "should_be_summarized",
"message_count": 12,
"participants": [
"Mai",
"沈砚",
"贺岚",
"迟雨",
"顾澄"
],
"start_time": 1774095120.0,
"end_time": 1774098180.0,
"messages": [
"[2026-03-21 20:12] 沈砚:北塔值夜的冷白灯又直打眼睛,我把小风扇拨回三档时顺手翻了一下夹层药箱,铜牌备用钥匙还在,但银色保温毯和薄荷膏的位置都乱了。",
"[2026-03-21 20:14] 贺岚:黑色丁腈手套昨晚急救演练拿走了两副,我还没来得及补,药箱里左边那格现在确实空得很明显。",
"[2026-03-21 20:18] 顾澄:旧清单上写的是铜牌备用钥匙、银色保温毯、薄荷膏、黑色丁腈手套和哨卡,但哨卡早改到门侧挂袋了。",
"[2026-03-21 20:22] Mai那今晚重新定一遍夹层药箱只保留铜牌备用钥匙、银色保温毯、薄荷膏和黑色丁腈手套谁临时拿走都在群里补报用途和预计归还时间。",
"[2026-03-21 20:27] 迟雨:薄荷膏只剩底,保温毯边角也卷了,最好一并补新,不然真遇到低温处理时手忙脚乱。",
"[2026-03-21 20:31] 沈砚:我半夜最怕一边被灯晃一边找物资,所以钥匙和保温毯的位置最好别再动来动去,我自己的盐渍梅子气泡水也不会放抽屉里。",
"[2026-03-21 20:36] 贺岚:我待会儿带一盒新薄荷膏和两包丁腈手套过去,顺便把保温毯重新折成外翻口,黑里摸会更快。",
"[2026-03-21 20:40] 顾澄:铜牌备用钥匙建议继续夹中间,别再和创伤贴挤在一起,上次取钥匙把整包小药片都带出来了。",
"[2026-03-21 20:45] Mai我记成新版摆放顺序了左保温毯中央铜牌备用钥匙右边薄荷膏和丁腈手套个人饮料和私人物件一律不进抽屉。",
"[2026-03-21 20:49] 迟雨:如果临时取保温毯或手套,至少发“已取用+用途+归还时间”,不要只在走廊口头说一声。",
"[2026-03-21 20:56] 贺岚:补完我会拍张抽屉照片,连折法和钥匙朝向一起拍进去,之后新值夜的人直接看图就行。",
"[2026-03-21 21:03] Mai就按这版执行后面谁要改夹层药箱布局先在群里说不然下次夜班还是会翻半天。"
],
"combined_text": "北塔夹层药箱这一轮群聊把铜牌备用钥匙、银色保温毯、薄荷膏、黑色丁腈手套的摆放与补位规则重新说得很细。大家先从沈砚值夜时被冷白荧光灯直照、顺手检查抽屉说起接着确认旧清单、演练后没有补回的丁腈手套、卷边的银色保温毯和几乎见底的薄荷膏到底该放在哪一格。Mai 明确要求夹层药箱只保留四样公用物资,谁取用都要在群里补报用途与归还时间;贺岚负责补回手套和薄荷膏,并承诺拍清楚抽屉照片;顾澄补充钥匙不能再与小药片混放;沈砚再次提到自己值夜时会把小风扇固定在三档,个人带来的盐渍梅子气泡水不会放进公用抽屉里。后半段群聊又进一步确认了左保温毯、中间钥匙、右侧药品的最终布局,说明旧清单中的哨卡已经迁出,不应再占用抽屉空间,并强调夜里应急时谁先摸到物资、谁负责在群里补一句用途和归还时间。整段对话同时覆盖了物资补位、摆放顺序、夜间可见性、旧规则迁移和报备流程,是一段标准而冗长的群聊收束样本。",
"expected_memory_targets": [
"北塔夹层药箱",
"铜牌备用钥匙",
"银色保温毯",
"薄荷膏",
"黑色丁腈手套",
"报备"
]
},
{
"batch_id": "rain_shed_radio_march_23",
"topic": "雨棚短波电台返潮排查",
"bot_participated": true,
"expected_behavior": "should_be_summarized",
"message_count": 12,
"participants": [
"Mai",
"顾澄",
"贺岚",
"迟雨",
"沈砚"
],
"start_time": 1774275240.0,
"end_time": 1774278360.0,
"messages": [
"[2026-03-23 22:14] 顾澄:雨棚那台短波电台今晚一开机就有潮噪,灰色防潮箱外壳摸着也冷,黑色盘线有一截像是沾了雾水。",
"[2026-03-23 22:16] 贺岚:备用听筒没事,主要是箱里那包硅胶已经发软了,估计这两天夜里返潮比想的更重。",
"[2026-03-23 22:20] 沈砚:别直接通电硬顶,我先把台面上的小黄灯关掉再看接口,反光太重会误判,盘线最好先拆下来擦干。",
"[2026-03-23 22:26] Mai按顺序处理顾澄先把机身外壳擦干贺岚把黑色盘线和备用听筒摊开迟雨看一下灰色防潮箱底部的吸水毡有没有饱和。",
"[2026-03-23 22:31] 迟雨:吸水毡已经发黏,箱底还有一点冷凝水,硅胶包也该整包换新,不然把盘线卷回去还是会闷出潮气。",
"[2026-03-23 22:35] 顾澄:机身擦干后杂音小了一半,问题大概不是主板,是存放时盘线缠得太紧,潮气都闷在接头这圈。",
"[2026-03-23 22:40] 贺岚:我把黑色盘线全部放开了,备用听筒也擦过一遍,灰色防潮箱里原来的旧硅胶包先扔掉。",
"[2026-03-23 22:45] Mai之后统一改成机身先晾五分钟、盘线松卷、听筒单独装袋、灰色防潮箱最后再关盖别再图快一股脑塞进去。",
"[2026-03-23 22:50] 沈砚:雨棚边夜里冷风大,机身外壳要是还冒凉气就别急着合盖,明早又得重新返潮。",
"[2026-03-23 22:56] 迟雨:新硅胶包已经放进去两袋,吸水毡我改成倒放在最底层,至少不会直接顶着电台外壳。",
"[2026-03-23 23:01] 顾澄:我把黑色盘线重新分成两圈,接头朝上,短波电台和备用听筒都回箱了,现在基本没有潮噪。",
"[2026-03-23 23:06] Mai结论记一下雨棚短波电台返潮时先擦机身、松黑色盘线、换硅胶包再按灰色防潮箱的新顺序归位。"
],
"combined_text": "雨棚短波电台这段对话比一般设备检修更刁钻因为大家反复讨论的是返潮噪声、灰色防潮箱底部冷凝水、黑色盘线缠绕方式和硅胶包失效这些容易被混成同类词的细节。顾澄先发现短波电台有潮噪贺岚确认备用听筒没坏而是防潮箱和硅胶包出了问题沈砚提醒不要在反光和冷风里急着通电Mai 则把处理顺序拆成机身擦干、盘线摊开、吸水毡检查和硅胶包更换。迟雨进一步指出灰色防潮箱底部的吸水毡已经发黏,需要调整放置方式。后面的讨论还涉及盘线要不要分成两圈、接头是否朝上、机身晾多久再合盖、备用听筒需不需要单独装袋,以及第二天谁来复查灰色防潮箱里的湿气状态。最后大家把短波电台、黑色盘线、备用听筒和灰色防潮箱的归位规则重新说清楚,让这一段既像设备维护,又像存放流程重整,天然适合测试跨话题重叠词下的检索稳定性。",
"expected_memory_targets": [
"短波电台",
"灰色防潮箱",
"黑色盘线",
"硅胶包",
"备用听筒"
]
},
{
"batch_id": "fog_transfer_march_26",
"topic": "晨雾采样蓝盖保温箱排序",
"bot_participated": true,
"expected_behavior": "should_be_summarized",
"message_count": 12,
"participants": [
"Mai",
"迟雨",
"顾澄",
"贺岚",
"沈砚"
],
"start_time": 1774475280.0,
"end_time": 1774478520.0,
"messages": [
"[2026-03-26 05:48] 迟雨:晨雾采样刚收尾,蓝盖保温箱里冷敷袋、编号试剂架和玻璃记号笔被塞反了,后面交接的人可能一打开就拿错。",
"[2026-03-26 05:52] 顾澄:我刚把样本管临时放回去时太赶了,冷敷袋压在编号试剂架上面了,玻璃记号笔也滑到最底层去了。",
"[2026-03-26 05:57] 贺岚:吸水垫已经湿透一半,保温箱里要是再混着放,样本编号和温控记录很容易一起糊掉。",
"[2026-03-26 06:02] Mai先别再往里塞东西按新版顺序来最下层冷敷袋中层编号试剂架右侧槽放玻璃记号笔和封口贴样本管最后再扣。",
"[2026-03-26 06:08] 沈砚:我先把外侧工作灯压低一点,冷白灯映在箱盖上太亮,我看编号时总会反光,看错一位就麻烦。",
"[2026-03-26 06:13] 迟雨:我把蓝盖保温箱里的冷敷袋重新平码了,编号试剂架按奇偶分左右,这样交接时一眼就能扫清楚。",
"[2026-03-26 06:18] 顾澄:玻璃记号笔现在固定在右槽,不再横放,我顺手把备用封口贴也放在笔旁边了。",
"[2026-03-26 06:24] 贺岚:样本管外壁的雾水有点重,我加了一层薄吸水纸,不然冷敷袋融水会一路蹭到标签。",
"[2026-03-26 06:30] Mai之后凌晨交接都照这个次序谁要临时改动蓝盖保温箱内部顺序也得在群里补一句免得下一班以为还是旧布局。",
"[2026-03-26 06:35] 迟雨:我已经把编号试剂架和样本位次对应关系写到箱盖内侧了,等会儿拍照发群文件。",
"[2026-03-26 06:39] 沈砚:这样就算我半困着看,也不至于拿错玻璃记号笔或者把冷敷袋从最上层一把抽出来。",
"[2026-03-26 06:42] Mai确认一下本次晨雾采样交接按蓝盖保温箱新顺序执行冷敷袋、编号试剂架、玻璃记号笔和样本管都不要再混层。"
],
"combined_text": "晨雾采样这一段专门压在“凌晨交接、低温、反光、编号顺序”这些容易让检索混淆的词上。大家围绕蓝盖保温箱里冷敷袋、编号试剂架、玻璃记号笔和样本管的归位顺序聊得很细,从顾澄一开始匆忙把冷敷袋压在试剂架上,到贺岚担心吸水垫、标签和融水互相污染,再到 Mai 明确规定最下层冷敷袋、中层编号试剂架、右槽玻璃记号笔和封口贴、样本管最后扣紧。沈砚额外提到冷白灯反光会影响编号辨认,迟雨把架位关系写到箱盖内侧并承诺拍照存档。随后大家又补充了吸水纸、备用封口贴、样本管雾水、奇偶编号左右分层和凌晨换班时必须按箱盖顺序复核这些细节,让这一段不仅仅是物品清单,而是包含低温保存、交接动作、标签保护和视觉干扰的完整操作窗口。整段话题兼有物品清单、流程顺序、交接规则和环境噪声,非常适合验证更长文本下的 search、time 和 episode 区分能力。",
"expected_memory_targets": [
"蓝盖保温箱",
"冷敷袋",
"编号试剂架",
"玻璃记号笔",
"样本管"
]
},
{
"batch_id": "west_corridor_wind_march_29",
"topic": "西廊风口观测准备",
"bot_participated": true,
"expected_behavior": "should_be_summarized",
"message_count": 12,
"participants": [
"Mai",
"迟雨",
"沈砚",
"许窈",
"贺岚"
],
"start_time": 1774779720.0,
"end_time": 1774783080.0,
"messages": [
"[2026-03-29 18:22] 迟雨:今晚西廊风口比预想更急,我先把气压计拿出来校准,湿度夹板也得重新夹一张干纸,不然一小时后全卷边。",
"[2026-03-29 18:25] 沈砚:工作台这边冷白灯太刺,我先换成侧边黄灯,再把小风扇关小一点,不然气流会把风向丝带吹得乱抖。",
"[2026-03-29 18:29] 许窈:温梨汤我装到窄口保温壶里了,放在西廊门内侧靠墙的位置,别和公用记录板挤在一起。",
"[2026-03-29 18:34] 贺岚:风向丝带刚整理过一遍,我把备用夹子和细线都放到记录板后面的小袋里,等会儿就不用来回摸。",
"[2026-03-29 18:39] Mai流程再说一遍迟雨先报气压计校准完成许窈确认温梨汤和备用夹子位置沈砚负责灯光和风扇不要干扰风口读数。",
"[2026-03-29 18:44] 迟雨:气压计现在回到零点附近了,湿度夹板也夹好了新纸,等第一轮风向记录时我再复核一次。",
"[2026-03-29 18:48] 沈砚:黄灯角度调好了,这样不会直照读数盘面,我自己的盐渍梅子气泡水也还是放个人架,不会碰公用桌面。",
"[2026-03-29 18:53] 许窈:温梨汤和备用杯已经摆好,门口风太直,我给壶套了防滑圈,谁喝完别顺手放回记录夹旁边。",
"[2026-03-29 18:58] 贺岚:风向丝带试摆过了,夹点不动,细线也没打结,记录板后袋里还有两枚备用夹子。",
"[2026-03-29 19:04] Mai今晚西廊风口观测前的关键点就三件气压计校准、湿度夹板换纸、温梨汤和夹具别混放之后都照这版执行。",
"[2026-03-29 19:11] 迟雨:收到,我把“先校准气压计再开记录”写到第一行了,后面换班的人也不用再问一次。",
"[2026-03-29 19:18] Mai这段我会记进观测前准备说明里尤其是气压计、湿度夹板、风向丝带和温梨汤的位置别再临时换。"
],
"combined_text": "西廊风口观测准备这段特意把人物偏好和观测流程混在一起模拟真实群聊里“事实很容易被话题背景淹没”的情况。迟雨一直在谈气压计校准和湿度夹板换纸许窈负责温梨汤和备用杯摆位贺岚补充风向丝带、备用夹子和细线的位置沈砚则反复抱怨冷白灯太刺眼、改用侧边黄灯并控制小风扇不要干扰读数。Mai 把整套流程收束成观测前三步,并强调温梨汤、夹具和记录板不能混放。后续大家又把保温壶的防滑圈、备用夹子分配、风向丝带试摆、个人饮料不能挤进公用桌面、以及第一轮记录前必须先报校准完成这些动作重复确认了一遍。这里既有人物稳定偏好,又有西廊风口这一特定场景和大量易重叠的工具名,还包含多轮确认与换班提示,是验证 person_fact 是否能真正压过群聊背景噪声的好素材。",
"expected_memory_targets": [
"西廊风口",
"气压计",
"湿度夹板",
"温梨汤",
"风向丝带"
]
},
{
"batch_id": "skylight_owl_april_02",
"topic": "绘图室天窗白耳鸮闯入",
"bot_participated": true,
"expected_behavior": "should_be_summarized",
"message_count": 12,
"participants": [
"Mai",
"许窈",
"顾澄",
"贺岚",
"沈砚"
],
"start_time": 1775133060.0,
"end_time": 1775136540.0,
"messages": [
"[2026-04-02 20:31] 许窈:绘图室天窗缝刚被风顶开,一只白耳鸮直接扑到长桌上,把象牙描图纸掀起来一大片,琥珀夹子全滑到地上了。",
"[2026-04-02 20:34] 顾澄:别先追鸟,我先把黄铜镇纸按回纸角,不然描图纸卷得更厉害,边上的玻璃尺也快掉了。",
"[2026-04-02 20:38] 贺岚:折梯在门后,我去把天窗先拉上,琥珀夹子我捡回一半了,有两枚滑到东侧柜脚下。",
"[2026-04-02 20:42] 沈砚:先别开顶灯,那盏冷白灯一亮鸟会更乱撞,我把侧边小灯压低,别让它再扑向纸架。",
"[2026-04-02 20:47] Mai处理顺序先固定顾澄压纸贺岚关天窗找夹子许窈把卷起的象牙描图纸按编号重新叠回去不要一边追鸟一边挪桌上工具。",
"[2026-04-02 20:51] 许窈:最上面的两张描图纸已经有折痕了,但编号还在,我先用黄铜镇纸压住,再找少掉的琥珀夹子。",
"[2026-04-02 20:56] 顾澄:白耳鸮现在站到东梁上了,暂时不碰桌子,玻璃尺和铅笔盒我已经移到靠墙这边,省得再被翅膀扫下去。",
"[2026-04-02 21:00] 贺岚:天窗锁好了,东侧柜脚下捡回三枚琥珀夹子,另外一枚卡在桌脚和纸箱中间,我伸手就能拿到。",
"[2026-04-02 21:05] 沈砚:侧灯角度够了,至少不会再直照到鸟和纸面,我这边风扇也先停了,免得象牙描图纸继续抖。",
"[2026-04-02 21:10] Mai等鸟自己往高处退我们先把桌面恢复象牙描图纸按编号叠好琥珀夹子回左盒黄铜镇纸继续压角别因为一阵慌把原来的顺序全打散。",
"[2026-04-02 21:18] 许窈:桌面基本恢复了,少掉的那枚夹子也找回来了,描图纸折痕部分我另放最上层,明天再单独压平。",
"[2026-04-02 21:29] Mai今晚的结论我记一下绘图室天窗边长桌继续固定放象牙描图纸、琥珀夹子和黄铜镇纸白耳鸮闯入时先稳纸再关窗。"
],
"combined_text": "白耳鸮闯进绘图室这一段专门拿来压 episode query 的自然语言理解因为话题里既有事件性描述又有大量静物摆放细节。大家先处理的是天窗缝被顶开后象牙描图纸、琥珀夹子和黄铜镇纸被掀乱的问题随后又讨论冷白灯会刺激白耳鸮、侧灯角度、风扇是否需要停、折梯和玻璃尺的位置、以及描图纸折痕如何单独压平。Mai 没有只说‘把鸟赶出去’,而是明确总结成‘先稳纸再关窗’,并恢复了绘图室天窗边长桌的固定摆放规则。后半段群聊还补了夹子掉落点、东侧柜脚、桌脚纸箱缝、顶灯和侧灯的切换、以及第二天如何单独压平最上层折痕纸这些容易被普通检索忽略的细枝末节。这类又长又绕、既有事件又有物件的群聊,非常适合验证我们刚修过的中文自然句 episode 检索是否真的泛化。",
"expected_memory_targets": [
"白耳鸮",
"象牙描图纸",
"琥珀夹子",
"黄铜镇纸",
"天窗"
]
},
{
"batch_id": "dessert_idle_april_04_negative",
"topic": "夜宵甜点口味闲聊",
"bot_participated": false,
"expected_behavior": "ignored_by_summarizer_without_bot_message",
"message_count": 10,
"participants": [
"许窈",
"顾澄",
"迟雨",
"沈砚"
],
"start_time": 1775301000.0,
"end_time": 1775304120.0,
"messages": [
"[2026-04-04 19:10] 许窈:我刚在街口买到荔枝冻,里面还有一点桂花糖浆,冰得很夸张。",
"[2026-04-04 19:14] 顾澄:我更想吃焦糖海盐脆片,那种一咬就掉屑的比糯口点心适合夜里配热水。",
"[2026-04-04 19:19] 迟雨:乌龙奶酥我也喜欢,但甜得太快,还是百香果软糖更耐嚼。",
"[2026-04-04 19:24] 沈砚:夜里我其实只想吃不黏手的,海盐脆片和柚子糖都比奶酥方便。",
"[2026-04-04 19:31] 许窈:那我下次把荔枝冻换成常温的,不然冰箱味太重。",
"[2026-04-04 19:37] 顾澄:要是有烤糯米团就好了,焦糖和海盐一起裹会很香。",
"[2026-04-04 19:44] 迟雨:百香果软糖一定要选酸一点的,不然吃两颗就发腻。",
"[2026-04-04 19:51] 沈砚:乌龙奶酥掉渣太厉害,我更想找一盒柠檬脆片。",
"[2026-04-04 19:58] 许窈:行,那我下次夜宵就别买太黏的,省得桌上全是糖粉。",
"[2026-04-04 20:02] 顾澄:决定了,下一轮就试荔枝冻、海盐脆片和百香果软糖三选二。"
],
"combined_text": "这一批是刻意保留下来的负样本,整段只围绕荔枝冻、焦糖海盐脆片、乌龙奶酥、百香果软糖和柚子糖的口味闲聊,没有 Mai 参与,也没有任何需要被长期记忆化的群聊任务结论。它的设计目的不是构造一个完全短小的无聊片段,而是制造一段在词面上同样丰富、长度也足够的闲聊,以便验证 summarizer 的 Bot 参与门槛和检索层的误召回控制是否真的有效。",
"expected_memory_targets": [
"荔枝冻",
"焦糖海盐脆片",
"乌龙奶酥",
"百香果软糖"
]
}
],
"runtime_trigger_streams": [
{
"stream_id": "runtime_west_corridor_trigger_april_07",
"topic": "西廊风口准备长流触发样本",
"trigger_mode": "time_threshold",
"elapsed_since_last_check_hours": 8.8,
"bot_participated": true,
"expected_check_outcome": "should_trigger_topic_check_and_pass_bot_gate",
"expected_next_stage": "topic_cache_should_update",
"message_count": 22,
"participants": [
"Mai",
"迟雨",
"沈砚",
"许窈",
"贺岚"
],
"start_time": 1775523960.0,
"end_time": 1775555880.0,
"messages": [
"[2026-04-07 09:06] 迟雨:今早西廊风口比昨晚更急,气压计零点又飘了一点,我先不急着开记录。",
"[2026-04-07 09:18] 沈砚:门边冷白灯太刺,我把黄灯转过去一点,不然表盘反光还是读不清。",
"[2026-04-07 09:27] 许窈:温梨汤已经装进新壶了,先放在门内侧,不和记录板摆一排。",
"[2026-04-07 09:41] 贺岚:备用夹子和风向丝带我都放进后袋了,细线也重新绕好了。",
"[2026-04-07 10:03] Mai今天沿用西廊风口旧流程先校准气压计再换湿度夹板的纸最后确认温梨汤和夹具位置。",
"[2026-04-07 10:17] 迟雨:气压计现在回稳了,但湿度夹板上的旧纸边角已经卷起来,最好直接重夹。",
"[2026-04-07 10:33] 沈砚:我把风扇关小了,别让风向丝带被室内气流带偏。",
"[2026-04-07 10:57] 许窈:保温壶外壁有点滑,我又套了防滑圈,省得一碰就滚到门口去。",
"[2026-04-07 11:15] 贺岚:记录板后袋里的夹子还够,两枚大的留给风向丝带,细夹子给湿度纸。",
"[2026-04-07 11:39] Mai谁临时挪了气压计或湿度夹板记得在群里说一声别让下一班找不到。",
"[2026-04-07 12:08] 迟雨:第一轮记录结束,我把气压计位置和校准时间写到夹板右上角了。",
"[2026-04-07 12:44] 沈砚:黄灯角度再压低一点就够了,现在表盘基本没有白斑。",
"[2026-04-07 13:11] 许窈:温梨汤还热,我又加了一个空杯,下午那班不用再回去找。",
"[2026-04-07 13:43] 贺岚:风向丝带其中一根打了结,我已经拆开重绑,备用细线也补了一卷。",
"[2026-04-07 14:06] Mai下午继续按这版不要把温梨汤放到公用记录夹旁边也别让个人饮料挤进工作台。",
"[2026-04-07 14:38] 迟雨:第二轮之前我再复核一次气压计,湿度夹板的纸目前还是干的。",
"[2026-04-07 15:09] 沈砚:我自己的盐渍梅子气泡水还是放个人架,公用桌上只留气压计和夹板。",
"[2026-04-07 15:37] 许窈:门口风更大了,保温壶我往里挪了半步,但还是在原来的内侧角落。",
"[2026-04-07 16:03] 贺岚:备用夹子数量没变,风向丝带和细线现在都在后袋左边。",
"[2026-04-07 16:41] Mai我把“先校准气压计再开记录”的提示写到最上面了换班的人照着做就行。",
"[2026-04-07 17:12] 迟雨:第三轮读数也稳定,湿度夹板今天的纸还能撑到收工。",
"[2026-04-07 17:58] Mai今天西廊风口准备流程就按这版固化气压计、湿度夹板、风向丝带和温梨汤都别再临时换位。"
],
"expected_memory_targets": [
"西廊风口",
"气压计",
"湿度夹板",
"温梨汤",
"风向丝带"
]
},
{
"stream_id": "runtime_skylight_trigger_april_09",
"topic": "白耳鸮闯入长流触发样本",
"trigger_mode": "time_threshold",
"elapsed_since_last_check_hours": 8.9,
"bot_participated": true,
"expected_check_outcome": "should_trigger_topic_check_and_pass_bot_gate",
"expected_next_stage": "topic_cache_should_update",
"message_count": 22,
"participants": [
"Mai",
"许窈",
"顾澄",
"贺岚",
"沈砚"
],
"start_time": 1775707860.0,
"end_time": 1775739780.0,
"messages": [
"[2026-04-09 12:11] 许窈:中午检窗时发现绘图室天窗锁没扣紧,我先把象牙描图纸压回去,免得下午又起风。",
"[2026-04-09 12:25] 顾澄:琥珀夹子今天早上少了一枚,我怀疑还卡在桌脚边。",
"[2026-04-09 12:42] 贺岚:折梯已经挪回门后,等会儿再顺手看一眼天窗扣件。",
"[2026-04-09 13:06] 沈砚:侧灯现在正常,顶灯还是先别开,冷白光一照纸面就晃得厉害。",
"[2026-04-09 13:31] Mai先把桌面顺序恢复象牙描图纸、琥珀夹子和黄铜镇纸都照旧摆天窗问题下午统一处理。",
"[2026-04-09 14:02] 许窈:刚刚那只白耳鸮又落回外沿了,好在没再扑进来,我先不惊它。",
"[2026-04-09 14:29] 顾澄:少的那枚夹子找到了,果然卡在桌脚和纸箱中间。",
"[2026-04-09 14:58] 贺岚:天窗扣件我试了一下,有一边发涩,合上时得多推半格。",
"[2026-04-09 15:21] 沈砚:风扇先停吧,纸边已经有点抖,别把描图纸又吹翘了。",
"[2026-04-09 15:46] Mai如果白耳鸮再闯进来还是按先稳纸、再关窗、最后收桌面工具的顺序来。",
"[2026-04-09 16:03] 许窈:我把折痕最明显的两张描图纸单独压在最上面,明天再慢慢压平。",
"[2026-04-09 16:24] 顾澄:黄铜镇纸这边够用,四角都压住了,玻璃尺也移到了靠墙那边。",
"[2026-04-09 16:52] 贺岚:门后的折梯没再动,天窗如果晚上还发涩,我明早再上去调。",
"[2026-04-09 17:15] 沈砚:侧灯角度现在刚好,不会直打鸟也不会晃到纸面。",
"[2026-04-09 17:41] Mai晚班别改桌面布局尤其别把琥珀夹子和黄铜镇纸分开放不然一慌又找不到。",
"[2026-04-09 18:08] 许窈:描图纸编号已经重新核对完,缺页没有新增。",
"[2026-04-09 18:39] 顾澄:桌脚周围也清出来了,之后夹子掉落会更容易捡。",
"[2026-04-09 19:02] 贺岚:天窗锁现在能扣上,只是右边还是偏紧,先留个提醒。",
"[2026-04-09 19:34] 沈砚:今晚继续用侧灯,冷白顶灯别开,省得纸面和鸟都受刺激。",
"[2026-04-09 20:07] Mai我把白耳鸮闯入时的桌面恢复顺序写进说明里了谁值班都照着做。",
"[2026-04-09 20:36] 许窈:象牙描图纸、琥珀夹子、黄铜镇纸都在固定位置,没有再乱。",
"[2026-04-09 21:03] Mai这波可以收尾了绘图室天窗边长桌的固定摆放和应急顺序都不再改。"
],
"expected_memory_targets": [
"白耳鸮",
"象牙描图纸",
"琥珀夹子",
"黄铜镇纸",
"天窗"
]
},
{
"stream_id": "runtime_dessert_negative_april_11",
"topic": "甜点闲聊长流负样本",
"trigger_mode": "time_threshold",
"elapsed_since_last_check_hours": 8.6,
"bot_participated": false,
"expected_check_outcome": "should_trigger_topic_check_but_be_discarded_without_bot_message",
"expected_next_stage": "topic_cache_should_remain_empty",
"message_count": 21,
"participants": [
"许窈",
"顾澄",
"迟雨",
"沈砚"
],
"start_time": 1775884080.0,
"end_time": 1775915160.0,
"messages": [
"[2026-04-11 13:08] 许窈:中午那盒荔枝冻居然还剩两块,桂花糖浆味道比昨天重,盒底那层糖水也比我记得更黏一些。",
"[2026-04-11 13:22] 顾澄:我还是更想吃焦糖海盐脆片,起码不会晃一下就碎成汤。",
"[2026-04-11 13:37] 迟雨:乌龙奶酥闻起来很香,但吃两口就太腻,百香果软糖反而更合适。",
"[2026-04-11 13:55] 沈砚:软糖太黏了,我宁愿吃柚子脆片或者原味苏打。",
"[2026-04-11 14:11] 许窈:荔枝冻如果不冰透其实不错,就是盒子一开就会流糖水。",
"[2026-04-11 14:28] 顾澄:焦糖海盐和烤糯米片如果放一起,应该会比奶酥耐吃,至少不会一拆包装就满桌掉屑。",
"[2026-04-11 14:46] 迟雨:百香果软糖最好选酸一点的,不然甜度太直白。",
"[2026-04-11 15:02] 沈砚:乌龙奶酥掉渣太夸张,晚上值班根本不适合拿着吃。",
"[2026-04-11 15:19] 许窈:那下次我换成荔枝冻和海盐脆片,至少不用到处拍碎屑。",
"[2026-04-11 15:37] 顾澄:再加一包柠檬薄片吧,甜口之间吃一点会清爽很多。",
"[2026-04-11 15:56] 迟雨:如果有酸梅软糖我也可以,和百香果轮着吃不会太腻。",
"[2026-04-11 16:14] 沈砚:苏打饼和软糖混着吃也行,主要是别太黏手,不然摸完包装还得再找纸擦。",
"[2026-04-11 16:33] 许窈:荔枝冻最好还是用浅盒,不然挖到后面全是糖浆。",
"[2026-04-11 16:57] 顾澄:海盐脆片如果换成细砂糖版应该会更稳一点。",
"[2026-04-11 17:21] 迟雨:百香果软糖配温水挺好,但跟奶酥一起就太重口了。",
"[2026-04-11 17:49] 沈砚:我还是投原味苏打一票,夜里拿着方便。",
"[2026-04-11 18:12] 许窈:那下轮夜宵我就不带太黏的点心了,省得桌上全是糖粉。",
"[2026-04-11 18:43] 顾澄:焦糖海盐脆片和柠檬薄片暂时领先,看起来大家还是更偏爱不容易腻口的那种。",
"[2026-04-11 19:18] 迟雨:百香果软糖至少还在前三,不算完全出局。",
"[2026-04-11 20:06] 沈砚:乌龙奶酥还是留到白天吧,晚上收拾起来太麻烦。",
"[2026-04-11 21:46] 许窈:行,那这轮夜宵就先定海盐脆片、柠檬薄片和百香果软糖,奶酥和荔枝冻留到白天再慢慢吃。"
],
"expected_memory_targets": [
"荔枝冻",
"焦糖海盐脆片",
"乌龙奶酥",
"百香果软糖"
]
}
],
"chat_history_records": [
{
"record_id": 930001,
"theme": "北塔夹层药箱补位",
"summary": "群里重新确认北塔夹层药箱固定放铜牌备用钥匙、银色保温毯、薄荷膏和黑色丁腈手套,临时取用后必须在群里报备;沈砚再次提到值夜时怕冷白荧光灯直照,会把小风扇调到三档。",
"participants": [
"Mai",
"沈砚",
"贺岚",
"迟雨",
"顾澄"
],
"start_time": 1774095120.0,
"end_time": 1774098180.0,
"original_text": "这段北塔夹层药箱的原始聊天记录围绕夜班抽屉补位展开。沈砚先从冷白荧光灯晃眼、自己值夜时会把小风扇调到三档说起顺手发现铜牌备用钥匙还在但银色保温毯和薄荷膏的位置已经乱了。贺岚补充黑色丁腈手套在演练后没补齐顾澄提到旧清单已经与现实摆放不完全一致。Mai 把公用物资重新收束为铜牌备用钥匙、银色保温毯、薄荷膏和黑色丁腈手套四样,要求任何临时取用都在群里补报用途与归还时间,并且明确个人饮料与私人物件不进入抽屉。大家随后又讨论保温毯折法、钥匙居中、手套补回、拍照留档和夜间可见性,让整段群聊既有稳定事实,也有具体流程决议。"
},
{
"record_id": 930002,
"theme": "雨棚短波电台返潮排查",
"summary": "雨棚那台短波电台返潮后,群里确认灰色防潮箱、黑色盘线和硅胶包的处理顺序需要重排:先擦干机身、松开盘线、替换硅胶包,再按新顺序归箱。",
"participants": [
"Mai",
"顾澄",
"贺岚",
"迟雨",
"沈砚"
],
"start_time": 1774275240.0,
"end_time": 1774278360.0,
"original_text": "这段设备维护聊天从短波电台的潮噪开始但真正讨论的核心很快扩展到灰色防潮箱、黑色盘线、备用听筒、吸水毡和硅胶包如何重新归位。顾澄一开始以为是电台本体故障贺岚判断更像是防潮箱内环境失效迟雨进一步指出箱底冷凝水和发软的硅胶包才是主因。Mai 将整个排查流程拆成擦干机身、摊开盘线、检查吸水毡、整包更换硅胶包、最后再按新顺序回箱几步。沈砚还补充了反光、冷风和合盖时机等环境细节。整段记录兼有设备名称、存放规则和操作顺序,能很好地区分“只是提到短波电台”和“真正形成可检索的返潮处理规则”。"
},
{
"record_id": 930003,
"theme": "晨雾采样蓝盖保温箱排序",
"summary": "晨雾采样结束后,群里把蓝盖保温箱的新布局固定为冷敷袋最下层、编号试剂架居中、玻璃记号笔与封口贴放右槽,样本管最后扣入,避免交接时拿错顺序。",
"participants": [
"Mai",
"迟雨",
"顾澄",
"贺岚",
"沈砚"
],
"start_time": 1774475280.0,
"end_time": 1774478520.0,
"original_text": "这段凌晨交接聊天聚焦蓝盖保温箱的内部顺序。大家从冷敷袋压住编号试剂架、玻璃记号笔滑到底层、样本管外壁水汽太重这些小问题谈起最后把冷敷袋、编号试剂架、玻璃记号笔、封口贴和样本管的排布重新钉死。Mai 明确禁止谁忙就谁随手塞回去的旧习惯,迟雨把位次关系写到箱盖内侧并准备拍照存档,顾澄和贺岚则分别补充了封口贴、吸水纸和标签保护的细节。沈砚提到冷白灯反光会影响编号辨认,这又为后续人物画像提供了背景噪声。整段记录很长,而且既像物资整理又像样本交接,适合检验系统是否能从高重叠词背景里抓住真正的主结论。"
},
{
"record_id": 930004,
"theme": "西廊风口观测准备",
"summary": "群里把西廊风口观测前准备固定成三步:先校准气压计,再换湿度夹板新纸,最后确认风向丝带、备用夹子和温梨汤的位置;迟雨负责开场前复核气压计。",
"participants": [
"Mai",
"迟雨",
"沈砚",
"许窈",
"贺岚"
],
"start_time": 1774779720.0,
"end_time": 1774783080.0,
"original_text": "西廊风口这一段本身并不短里面混杂着观测流程、环境噪声和人物习惯。迟雨一直在说气压计校准与湿度夹板换纸许窈在安顿温梨汤和保温壶贺岚在整理风向丝带、备用夹子和细线而沈砚则不断从灯光太刺眼、小风扇可能扰动读数这些角度插话。Mai 最终把真正需要记住的主流程收束成三步,并提醒公用桌上不要混入私人物件。由于这一段既有流程结论,也有大量背景描述,它特别适合验证 profile 是否还能稳定抓住迟雨和沈砚各自的稳定事实,而不是被场景语句淹没。"
},
{
"record_id": 930005,
"theme": "绘图室天窗白耳鸮闯入",
"summary": "白耳鸮闯进绘图室后,群里确认应急顺序是先稳住象牙描图纸,再关天窗,最后把琥珀夹子与黄铜镇纸按原位归回;绘图室天窗边长桌的固定摆放规则保持不变。",
"participants": [
"Mai",
"许窈",
"顾澄",
"贺岚",
"沈砚"
],
"start_time": 1775133060.0,
"end_time": 1775136540.0,
"original_text": "这段聊天从白耳鸮扑进绘图室开始话题里先后出现天窗缝、折梯、象牙描图纸、琥珀夹子、黄铜镇纸、玻璃尺、灯光角度和风扇停开等多个细节。许窈和顾澄一开始更关心压纸和找夹子贺岚负责天窗和折梯沈砚提醒冷白灯会刺激鸟并且让纸面反光Mai 则把整件事稳定成先稳纸、再关窗、最后恢复桌面固定摆放的规则。因为里面既有明显的事件经过,又有不少可能误导检索的场景词,所以很适合当作自然语言 episode query 的压力样本。"
}
],
"person_writebacks": [
{
"person_id": "71aa50b4f4f34bdbb1a7a1c1e56c9011",
"person_name": "沈砚",
"memory_content": "沈砚值夜时不喜欢冷白荧光灯直照,通常把小风扇固定在三档,并且会自带盐渍梅子气泡水。",
"expected_keywords": [
"沈砚",
"冷白荧光灯",
"小风扇",
"三档",
"盐渍梅子气泡水"
]
},
{
"person_id": "82bb79d4680849b5ac7e23a3f6f09122",
"person_name": "迟雨",
"memory_content": "迟雨每次西廊观测前都会先校准气压计并核对湿度夹板,而且只喝温梨汤。",
"expected_keywords": [
"迟雨",
"气压计",
"湿度夹板",
"校准",
"温梨汤"
]
},
{
"person_id": "93cb73d41251472a8d229f1202df9333",
"person_name": "贺岚",
"memory_content": "贺岚平时看管北塔夹层药箱和铜牌备用钥匙,取用后会把黑色丁腈手套和银色保温毯补回原位。",
"expected_keywords": [
"贺岚",
"北塔夹层药箱",
"铜牌备用钥匙",
"黑色丁腈手套",
"银色保温毯"
]
}
],
"search_cases": [
{
"query": "北塔夹层 铜牌备用钥匙 银色保温毯 薄荷膏",
"expected_keywords": [
"北塔夹层",
"铜牌备用钥匙",
"银色保温毯",
"薄荷膏"
],
"minimum_keyword_hits": 2
},
{
"query": "短波电台 灰色防潮箱 黑色盘线 硅胶包",
"expected_keywords": [
"短波电台",
"灰色防潮箱",
"黑色盘线",
"硅胶包"
],
"minimum_keyword_hits": 2
},
{
"query": "蓝盖保温箱 编号试剂架 冷敷袋 玻璃记号笔",
"expected_keywords": [
"蓝盖保温箱",
"编号试剂架",
"冷敷袋",
"玻璃记号笔"
],
"minimum_keyword_hits": 2
},
{
"query": "白耳鸮 琥珀夹子 象牙描图纸 黄铜镇纸",
"expected_keywords": [
"白耳鸮",
"琥珀夹子",
"象牙描图纸",
"黄铜镇纸"
],
"minimum_keyword_hits": 2
}
],
"time_cases": [
{
"query": "铜牌钥匙 保温毯",
"time_expression": "2026/03/21",
"expected_keywords": [
"铜牌备用钥匙",
"银色保温毯",
"薄荷膏"
]
},
{
"query": "气压计 梨汤",
"time_expression": "2026/03/29",
"expected_keywords": [
"气压计",
"温梨汤",
"西廊风口"
]
},
{
"query": "白耳鸮 描图纸",
"time_expression": "2026/04/02",
"expected_keywords": [
"白耳鸮",
"象牙描图纸",
"琥珀夹子"
]
}
],
"episode_cases": [
{
"query": "那回半夜说北塔夹层抽屉里只剩铜牌钥匙,还得把银色保温毯和薄荷膏补回去的那整段经过",
"expected_keywords": [
"北塔夹层",
"铜牌备用钥匙",
"银色保温毯",
"薄荷膏"
],
"minimum_keyword_recall": 0.75
},
{
"query": "雨棚底下那回先把电台擦干,又讨论灰色防潮箱和黑色盘线怎么归位的来龙去脉",
"expected_keywords": [
"短波电台",
"灰色防潮箱",
"黑色盘线",
"硅胶包"
],
"minimum_keyword_recall": 0.75
},
{
"query": "黎明前重新排蓝盖保温箱、编号试剂架和冷敷袋顺序的那次交接",
"expected_keywords": [
"蓝盖保温箱",
"编号试剂架",
"冷敷袋",
"玻璃记号笔"
],
"minimum_keyword_recall": 0.75
},
{
"query": "天窗边那只白耳鸮冲进来,把琥珀夹子和象牙描图纸搅乱之后大家怎么收拾的",
"expected_keywords": [
"白耳鸮",
"琥珀夹子",
"象牙描图纸",
"黄铜镇纸"
],
"minimum_keyword_recall": 0.75
}
],
"knowledge_fetcher_cases": [
{
"query": "群里后来把北塔夹层药箱里固定放哪些东西?",
"expected_keywords": [
"北塔夹层药箱",
"铜牌备用钥匙",
"银色保温毯",
"薄荷膏"
],
"minimum_keyword_recall": 0.75
},
{
"query": "谁每次观测前都会先校准气压计,而且只喝温梨汤?",
"expected_keywords": [
"迟雨",
"气压计",
"校准",
"温梨汤"
],
"minimum_keyword_recall": 0.75
},
{
"query": "那次白耳鸮闯进绘图室后,大家把哪些东西重新压好固定?",
"expected_keywords": [
"白耳鸮",
"琥珀夹子",
"象牙描图纸",
"黄铜镇纸"
],
"minimum_keyword_recall": 0.75
}
],
"profile_cases": [
{
"person_id": "71aa50b4f4f34bdbb1a7a1c1e56c9011",
"expected_keywords": [
"沈砚",
"冷白荧光灯",
"三档",
"盐渍梅子气泡水"
],
"minimum_keyword_recall": 0.75
},
{
"person_id": "82bb79d4680849b5ac7e23a3f6f09122",
"expected_keywords": [
"迟雨",
"气压计",
"湿度夹板",
"温梨汤"
],
"minimum_keyword_recall": 0.75
},
{
"person_id": "93cb73d41251472a8d229f1202df9333",
"expected_keywords": [
"贺岚",
"北塔夹层药箱",
"铜牌备用钥匙",
"黑色丁腈手套"
],
"minimum_keyword_recall": 0.75
}
],
"negative_control_cases": [
{
"query": "焦糖海盐脆片 荔枝冻 乌龙奶酥 百香果软糖",
"source_batch_id": "dessert_idle_april_04_negative",
"expected_behavior": "should_return_no_hits_if_only_positive_batches_are_ingested",
"reason": "当前设计要求没有 Bot 发言的群聊批次不应进入长期记忆总结主路径;这条负样本还故意拉长并保留丰富词面,避免仅靠“太短”通过。"
}
]
}

View File

@@ -0,0 +1,120 @@
{
"meta": {
"scenario_id": "long_novel_memory_benchmark_v1",
"description": "长篇叙事检索基准围绕北塔木梯、蓝漆铁盒与海潮图展开验证导入、摘要写回、人物写回、episode 与工具检索链路。",
"designed_for": "A_memorix long novel benchmark",
"quantitative_targets": {
"search_accuracy_at_1": 0.35,
"search_recall_at_5": 0.6,
"search_keyword_recall_at_5": 0.8,
"writeback_success_rate": 0.66,
"knowledge_fetcher_success_rate": 0.66,
"profile_success_rate": 0.66,
"tool_modes_success_rate": 0.75
}
},
"session": {
"session_id": "private:novel:beita",
"platform": "qq",
"user_id": "10001",
"group_id": "",
"display_name": "北塔潮痕"
},
"import_payload": {
"paragraphs": [
{
"content": "北塔木梯第四阶后面的夹层里藏着一个蓝漆铁盒,铁盒里压着卷起的海潮图,图角写着旧码头会在大潮夜开启暗门。",
"source": "fixture:long_novel_memory_benchmark",
"knowledge_type": "narrative",
"entities": ["北塔木梯", "蓝漆铁盒", "海潮图", "旧码头", "暗门"]
},
{
"content": "沈砚秋每次登北塔前都会先用银壳指南针校正方向,再把海潮图折成三折塞回蓝漆铁盒;她提醒顾回,真正藏铁盒的位置不是第七阶,而是北塔木梯第四阶。",
"source": "fixture:long_novel_memory_benchmark",
"knowledge_type": "narrative",
"entities": ["沈砚秋", "银壳指南针", "海潮图", "蓝漆铁盒", "顾回", "北塔木梯第四阶"]
},
{
"content": "顾回在北塔木梯旁的潮痕墙上看见一幅褪色海潮图摹本,他据此确认蓝漆铁盒里的红线标记指向旧码头东侧的石闸门。",
"source": "fixture:long_novel_memory_benchmark",
"knowledge_type": "narrative",
"entities": ["顾回", "北塔木梯", "海潮图", "蓝漆铁盒", "旧码头", "石闸门"]
},
{
"content": "他们后来把蓝漆铁盒重新藏回北塔木梯第四阶后面,只留下海潮图的摹本带去旧码头,避免有人提前发现暗门的位置。",
"source": "fixture:long_novel_memory_benchmark",
"knowledge_type": "narrative",
"entities": ["蓝漆铁盒", "北塔木梯第四阶", "海潮图", "旧码头", "暗门"]
}
],
"relations": [
{
"subject": "蓝漆铁盒",
"predicate": "藏在",
"object": "北塔木梯第四阶后面"
},
{
"subject": "蓝漆铁盒",
"predicate": "装有",
"object": "海潮图"
},
{
"subject": "海潮图",
"predicate": "指向",
"object": "旧码头东侧石闸门"
},
{
"subject": "沈砚秋",
"predicate": "使用",
"object": "银壳指南针"
}
]
},
"chat_history_records": [
{
"record_id": 930001,
"theme": "北塔木梯与蓝漆铁盒",
"summary": "沈砚秋和顾回确认蓝漆铁盒一直藏在北塔木梯第四阶后面,铁盒里压着海潮图,海潮图上的红线指向旧码头东侧石闸门;他们最终把铁盒放回原位,只带走摹本继续查暗门。",
"participants": ["Mai", "沈砚秋", "顾回"],
"start_time": 1775041200.0,
"end_time": 1775043600.0,
"original_text": "[2026-04-01 18:00] 沈砚秋:我又去摸了一遍北塔木梯,蓝漆铁盒还在第四阶后面的夹层里。\n[2026-04-01 18:02] 顾回:里面那张海潮图的红线还是指着旧码头东侧石闸门,看来暗门位置没有变。\n[2026-04-01 18:04] Mai你们把铁盒放回去了吗\n[2026-04-01 18:05] 沈砚秋:放回去了,只带了海潮图摹本,免得别人发现第四阶的夹层。\n[2026-04-01 18:07] 顾回:银壳指南针也重新校过了,等夜潮退下去我们再去旧码头。 "
}
],
"person_writebacks": [
{
"person_id": "person-shen-yanqiu",
"person_name": "沈砚秋",
"memory_content": "沈砚秋习惯用银壳指南针校正方向,确认北塔木梯第四阶后的蓝漆铁盒和海潮图位置后才继续行动。",
"expected_keywords": ["沈砚秋", "银壳指南针", "蓝漆铁盒", "海潮图"]
}
],
"search_cases": [
{
"query": "蓝漆铁盒 北塔木梯 海潮图 在哪里",
"expected_keywords": ["蓝漆铁盒", "北塔木梯", "海潮图"],
"minimum_keyword_hits": 2
}
],
"knowledge_fetcher_cases": [
{
"query": "他们后来是在什么位置找到蓝漆铁盒和海潮图的?",
"expected_keywords": ["蓝漆铁盒", "北塔木梯第四阶", "海潮图"],
"minimum_keyword_recall": 0.67
}
],
"profile_cases": [
{
"person_id": "person-shen-yanqiu",
"expected_keywords": ["沈砚秋", "银壳指南针", "海潮图"],
"minimum_keyword_recall": 0.67
}
],
"episode_cases": [
{
"query": "那次他们在北塔木梯寻找蓝漆铁盒和海潮图的经过",
"expected_keywords": ["蓝漆铁盒", "北塔木梯第四阶", "海潮图"],
"minimum_keyword_recall": 0.67
}
]
}

View File

@@ -0,0 +1,90 @@
{
"meta": {
"scenario_id": "private_alice_weekend",
"description": "私聊周末回忆场景,用于验证导入/检索/画像/episode 全链路"
},
"session": {
"session_id": "qq_private_10001",
"platform": "qq",
"user_id": "10001",
"group_id": "",
"display_name": "小爱"
},
"import_payload": {
"paragraphs": [
{
"content": "周末我们在北塔木梯旁找到一个蓝漆铁盒,盒里夹着海潮图。",
"source": "fixture:private_alice_weekend",
"knowledge_type": "narrative",
"entities": [
"小爱",
"蓝漆铁盒",
"北塔木梯",
"海潮图"
]
},
{
"content": "小爱说蓝漆铁盒是她外公留下的,她会把海潮图带去修复。",
"source": "fixture:private_alice_weekend",
"knowledge_type": "factual",
"entities": [
"小爱",
"蓝漆铁盒",
"海潮图"
]
}
],
"relations": [
{
"subject": "小爱",
"predicate": "发现",
"object": "蓝漆铁盒"
},
{
"subject": "蓝漆铁盒",
"predicate": "内含",
"object": "海潮图"
},
{
"subject": "小爱",
"predicate": "提到地点",
"object": "北塔木梯"
}
]
},
"chat_history_record": {
"record_id": 900001,
"theme": "周末北塔发现",
"summary": "小爱在北塔木梯旁找到蓝漆铁盒,铁盒里有海潮图,并计划修复。",
"participants": [
"小爱",
"Mai"
],
"start_time": 1732903200.0,
"end_time": 1732904100.0,
"original_text": "周六傍晚,小爱说她在北塔木梯边发现了一个蓝漆铁盒,里面夹着海潮图,想下周拿去修复。"
},
"person": {
"person_id": "7ee9e14d602520af84e57b8665f8e14d",
"person_name": "小爱"
},
"person_fact": {
"memory_content": "小爱熟悉北塔木梯路线,知道蓝漆铁盒里夹着海潮图。"
},
"search_queries": {
"direct": "蓝漆铁盒 北塔木梯 海潮图",
"knowledge_fetcher": "小爱周末提到的铁盒和地图是什么"
},
"expectations": {
"search_keywords": [
"蓝漆铁盒",
"北塔木梯",
"海潮图"
],
"profile_keywords": [
"小爱",
"蓝漆铁盒"
],
"episode_source": "chat_summary:qq_private_10001"
}
}

View File

@@ -0,0 +1,166 @@
from __future__ import annotations
from types import SimpleNamespace
import numpy as np
import pytest
from A_memorix.core.embedding import api_adapter as api_adapter_module
from A_memorix.core.embedding.api_adapter import EmbeddingAPIAdapter
from A_memorix.core.utils.runtime_self_check import run_embedding_runtime_self_check
class _FakeEmbeddingClient:
def __init__(self, *, natural_dimension: int = 12) -> None:
self.natural_dimension = int(natural_dimension)
self.requests = []
async def get_embedding(self, request):
self.requests.append(request)
requested_dimension = request.extra_params.get("dimensions")
if requested_dimension is None:
requested_dimension = request.extra_params.get("output_dimensionality")
dimension = int(requested_dimension or self.natural_dimension)
return SimpleNamespace(embedding=[1.0] * dimension)
def _build_adapter(
monkeypatch: pytest.MonkeyPatch,
*,
client_type: str,
configured_dimension: int = 1024,
effective_dimension: int | None = None,
model_extra_params: dict | None = None,
):
adapter = EmbeddingAPIAdapter(default_dimension=configured_dimension)
if effective_dimension is not None:
adapter._dimension = int(effective_dimension)
adapter._dimension_detected = True
fake_client = _FakeEmbeddingClient()
model_info = SimpleNamespace(
name="embedding-model",
api_provider="provider-1",
model_identifier="embedding-model-id",
extra_params=dict(model_extra_params or {}),
)
provider = SimpleNamespace(name="provider-1", client_type=client_type)
monkeypatch.setattr(adapter, "_resolve_candidate_model_names", lambda: ["embedding-model"])
monkeypatch.setattr(adapter, "_find_model_info", lambda model_name: model_info)
monkeypatch.setattr(adapter, "_find_provider", lambda provider_name: provider)
monkeypatch.setattr(
api_adapter_module.client_registry,
"get_client_class_instance",
lambda api_provider, force_new=True: fake_client,
)
return adapter, fake_client
@pytest.mark.asyncio
async def test_encode_uses_canonical_dimension_for_openai_provider(monkeypatch):
adapter, fake_client = _build_adapter(
monkeypatch,
client_type="openai",
configured_dimension=1024,
effective_dimension=1024,
model_extra_params={"task_type": "SEMANTIC_SIMILARITY"},
)
embedding = await adapter.encode("北塔木梯")
request = fake_client.requests[-1]
assert request.extra_params["dimensions"] == 1024
assert "output_dimensionality" not in request.extra_params
assert request.extra_params["task_type"] == "SEMANTIC_SIMILARITY"
assert embedding.shape == (1024,)
@pytest.mark.asyncio
async def test_encode_explicit_dimension_override_wins(monkeypatch):
adapter, fake_client = _build_adapter(
monkeypatch,
client_type="openai",
configured_dimension=1024,
effective_dimension=1024,
)
embedding = await adapter.encode("海潮图", dimensions=256)
request = fake_client.requests[-1]
assert request.extra_params["dimensions"] == 256
assert "output_dimensionality" not in request.extra_params
assert embedding.shape == (256,)
@pytest.mark.asyncio
async def test_encode_maps_dimension_to_gemini_output_dimensionality(monkeypatch):
adapter, fake_client = _build_adapter(
monkeypatch,
client_type="gemini",
configured_dimension=1024,
effective_dimension=768,
)
embedding = await adapter.encode("广播站")
request = fake_client.requests[-1]
assert request.extra_params["output_dimensionality"] == 768
assert "dimensions" not in request.extra_params
assert embedding.shape == (768,)
@pytest.mark.asyncio
async def test_encode_does_not_force_dimension_for_unsupported_provider(monkeypatch):
adapter, fake_client = _build_adapter(
monkeypatch,
client_type="custom",
configured_dimension=1024,
effective_dimension=640,
model_extra_params={
"dimensions": 999,
"output_dimensionality": 888,
"custom_flag": "keep-me",
},
)
embedding = await adapter.encode("蓝漆铁盒")
request = fake_client.requests[-1]
assert "dimensions" not in request.extra_params
assert "output_dimensionality" not in request.extra_params
assert request.extra_params["custom_flag"] == "keep-me"
assert embedding.shape == (fake_client.natural_dimension,)
@pytest.mark.asyncio
async def test_runtime_self_check_reports_requested_dimension_without_explicit_override():
class _FakeEmbeddingManager:
def __init__(self) -> None:
self.detected_dimension = 384
self.encode_calls = []
async def _detect_dimension(self) -> int:
return self.detected_dimension
def get_requested_dimension(self) -> int:
return self.detected_dimension
async def encode(self, text):
self.encode_calls.append(text)
return np.ones(self.detected_dimension, dtype=np.float32)
manager = _FakeEmbeddingManager()
report = await run_embedding_runtime_self_check(
config={"embedding": {"dimension": 1024}},
vector_store=SimpleNamespace(dimension=384),
embedding_manager=manager,
)
assert report["ok"] is True
assert report["configured_dimension"] == 1024
assert report["requested_dimension"] == 384
assert report["detected_dimension"] == 384
assert report["encoded_dimension"] == 384
assert manager.encode_calls == ["A_Memorix runtime self check"]

View File

@@ -0,0 +1,86 @@
from __future__ import annotations
import json
from pathlib import Path
DATA_DIR = Path(__file__).parent / "data" / "benchmarks"
def _fixture_files() -> list[Path]:
return sorted(DATA_DIR.glob("group_chat_stream_memory_benchmark*.json"))
def _load_fixture(path: Path) -> dict:
return json.loads(path.read_text(encoding="utf-8"))
def _assert_fixture_matches_current_design_constraints(dataset: dict) -> None:
assert dataset["meta"]["scenario_id"]
assert dataset["session"]["group_id"]
assert dataset["session"]["platform"] == "qq"
simulated_batches = dataset["simulated_stream_batches"]
assert len(simulated_batches) >= 5
positive_batches = [item for item in simulated_batches if item["bot_participated"]]
negative_batches = [item for item in simulated_batches if not item["bot_participated"]]
assert len(positive_batches) >= 4
assert len(negative_batches) >= 1
assert any(item["expected_behavior"] == "ignored_by_summarizer_without_bot_message" for item in negative_batches)
for batch in positive_batches:
assert "Mai" in batch["participants"]
assert batch["message_count"] >= 10
assert len(batch["combined_text"]) >= 300
assert batch["start_time"] < batch["end_time"]
assert len(batch["expected_memory_targets"]) >= 4
runtime_streams = dataset["runtime_trigger_streams"]
assert len(runtime_streams) >= 2
runtime_positive = [item for item in runtime_streams if item["bot_participated"]]
runtime_negative = [item for item in runtime_streams if not item["bot_participated"]]
assert len(runtime_positive) >= 1
assert len(runtime_negative) >= 1
for stream in runtime_streams:
stream_text = "\n".join(stream["messages"])
assert stream["trigger_mode"] == "time_threshold"
assert stream["elapsed_since_last_check_hours"] >= 8.0
assert stream["message_count"] >= 20
assert len(stream["messages"]) == stream["message_count"]
assert len(stream_text) >= 1000
assert stream["start_time"] < stream["end_time"]
assert any(item["expected_check_outcome"] == "should_trigger_topic_check_and_pass_bot_gate" for item in runtime_positive)
assert any(
item["expected_check_outcome"] == "should_trigger_topic_check_but_be_discarded_without_bot_message"
for item in runtime_negative
)
records = dataset["chat_history_records"]
assert len(records) >= 4
for record in records:
assert "Mai" in record["participants"]
assert len(record["summary"]) >= 40
assert len(record["original_text"]) >= 200
assert record["start_time"] < record["end_time"]
assert len(dataset["person_writebacks"]) >= 3
assert len(dataset["search_cases"]) >= 4
assert len(dataset["time_cases"]) >= 3
assert len(dataset["episode_cases"]) >= 4
assert len(dataset["knowledge_fetcher_cases"]) >= 3
assert len(dataset["profile_cases"]) >= 3
assert len(dataset["negative_control_cases"]) >= 1
def test_group_chat_stream_fixture_matches_current_design_constraints():
files = _fixture_files()
assert files, "未找到 group_chat_stream_memory_benchmark*.json fixture"
for path in files:
_assert_fixture_matches_current_design_constraints(_load_fixture(path))

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,6 @@ from src.services.memory_service import MemoryHit, MemorySearchResult
def test_knowledge_fetcher_resolves_private_memory_context(monkeypatch):
monkeypatch.setattr(knowledge_module, "LLMRequest", lambda *args, **kwargs: object())
monkeypatch.setattr(
knowledge_module,
"_chat_manager",
@@ -31,7 +30,6 @@ def test_knowledge_fetcher_resolves_private_memory_context(monkeypatch):
@pytest.mark.asyncio
async def test_knowledge_fetcher_memory_get_knowledge_uses_memory_service(monkeypatch):
monkeypatch.setattr(knowledge_module, "LLMRequest", lambda *args, **kwargs: object())
monkeypatch.setattr(
knowledge_module,
"_chat_manager",
@@ -73,7 +71,6 @@ async def test_knowledge_fetcher_memory_get_knowledge_uses_memory_service(monkey
@pytest.mark.asyncio
async def test_knowledge_fetcher_falls_back_to_chat_scope_when_person_scope_misses(monkeypatch):
monkeypatch.setattr(knowledge_module, "LLMRequest", lambda *args, **kwargs: object())
monkeypatch.setattr(
knowledge_module,
"_chat_manager",

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio
import inspect
import json
from datetime import datetime
from pathlib import Path
from types import SimpleNamespace
from typing import Any, Dict, List
@@ -71,21 +72,11 @@ class _KnownPerson:
class _KernelBackedRuntimeManager:
is_running = True
def __init__(self, kernel: SDKMemoryKernel) -> None:
self.kernel = kernel
async def invoke_plugin(
self,
*,
method: str,
plugin_id: str,
component_name: str,
args: Dict[str, Any] | None,
timeout_ms: int,
):
del method, plugin_id, timeout_ms
async def invoke(self, component_name: str, args: Dict[str, Any] | None, *, timeout_ms: int = 30000):
del timeout_ms
payload = args or {}
if component_name == "search_memory":
return await self.kernel.search_memory(
@@ -299,6 +290,12 @@ async def _evaluate_tool_modes(*, session_id: str, dataset: Dict[str, Any]) -> D
search_case = dataset["search_cases"][0]
episode_case = dataset["episode_cases"][0]
aggregate_case = dataset["knowledge_fetcher_cases"][0]
first_record = (dataset.get("chat_history_records") or [{}])[0]
reference_ts = first_record.get("end_time") or first_record.get("start_time") or 0
if reference_ts:
time_expression = datetime.fromtimestamp(float(reference_ts)).strftime("%Y/%m/%d")
else:
time_expression = "最近7天"
tool_cases = [
{
"name": "search",
@@ -318,7 +315,7 @@ async def _evaluate_tool_modes(*, session_id: str, dataset: Dict[str, Any]) -> D
"mode": "time",
"chat_id": session_id,
"limit": 5,
"time_expression": "最近7天",
"time_expression": time_expression,
},
"expected_keywords": ["蓝漆铁盒", "北塔木梯"],
"minimum_keyword_recall": 0.67,
@@ -402,7 +399,6 @@ async def benchmark_env(monkeypatch, tmp_path):
return {"ok": True, "message": "ok", "encoded_dimension": 32}
monkeypatch.setattr(kernel_module, "run_embedding_runtime_self_check", fake_self_check)
monkeypatch.setattr(memory_service_module, "get_plugin_runtime_manager", None)
monkeypatch.setattr(summarizer_module, "_chat_manager", fake_chat_manager)
monkeypatch.setattr(knowledge_module, "_chat_manager", fake_chat_manager)
monkeypatch.setattr(person_info_module, "_chat_manager", fake_chat_manager)
@@ -424,7 +420,7 @@ async def benchmark_env(monkeypatch, tmp_path):
},
)
manager = _KernelBackedRuntimeManager(kernel)
monkeypatch.setattr(memory_service_module, "get_plugin_runtime_manager", lambda: manager)
monkeypatch.setattr(memory_service_module, "a_memorix_host_service", manager)
await kernel.initialize()
try:

View File

@@ -10,7 +10,7 @@ import pytest
import pytest_asyncio
from A_memorix.core.runtime.sdk_memory_kernel import SDKMemoryKernel
from pytests.test_long_novel_memory_benchmark import (
from pytests.A_memorix_test.test_long_novel_memory_benchmark import (
_evaluate_episode_admin_query,
_evaluate_episode_generation,
_evaluate_episode_search_mode,
@@ -58,7 +58,6 @@ async def benchmark_live_env(monkeypatch, tmp_path):
registry = {item["person_name"]: item["person_id"] for item in dataset["person_writebacks"]}
reverse_registry = {value: key for key, value in registry.items()}
monkeypatch.setattr(memory_service_module, "get_plugin_runtime_manager", None)
monkeypatch.setattr(summarizer_module, "_chat_manager", fake_chat_manager)
monkeypatch.setattr(knowledge_module, "_chat_manager", fake_chat_manager)
monkeypatch.setattr(person_info_module, "_chat_manager", fake_chat_manager)
@@ -80,7 +79,7 @@ async def benchmark_live_env(monkeypatch, tmp_path):
},
)
manager = _KernelBackedRuntimeManager(kernel)
monkeypatch.setattr(memory_service_module, "get_plugin_runtime_manager", lambda: manager)
monkeypatch.setattr(memory_service_module, "a_memorix_host_service", manager)
await kernel.initialize()
try:

View File

@@ -73,7 +73,7 @@ async def test_search_respects_filter_by_default(monkeypatch):
{
"query": "mai",
"limit": 5,
"mode": "hybrid",
"mode": "search",
"chat_id": "stream-1",
"person_id": "person-1",
"time_start": None,

View File

@@ -64,7 +64,7 @@ def test_init_all_tools_registers_long_term_memory_tool():
@pytest.mark.asyncio
async def test_query_long_term_memory_search_mode_maps_to_hybrid(monkeypatch):
async def test_query_long_term_memory_search_mode_keeps_search(monkeypatch):
captured = {}
async def fake_search(query, **kwargs):
@@ -83,7 +83,7 @@ async def test_query_long_term_memory_search_mode_maps_to_hybrid(monkeypatch):
"query": "Alice 喜欢什么",
"kwargs": {
"limit": 5,
"mode": "hybrid",
"mode": "search",
"chat_id": "stream-1",
"person_id": "person-1",
"time_start": None,

View File

@@ -59,21 +59,11 @@ class _FakeEmbeddingAdapter:
class _KernelBackedRuntimeManager:
is_running = True
def __init__(self, kernel: SDKMemoryKernel) -> None:
self.kernel = kernel
async def invoke_plugin(
self,
*,
method: str,
plugin_id: str,
component_name: str,
args: Dict[str, Any] | None,
timeout_ms: int,
):
del method, plugin_id, timeout_ms
async def invoke(self, component_name: str, args: Dict[str, Any] | None, *, timeout_ms: int = 30000):
del timeout_ms
payload = args or {}
if component_name == "search_memory":
return await self.kernel.search_memory(
@@ -132,7 +122,6 @@ async def real_dialogue_env(monkeypatch, tmp_path):
return {"ok": True, "message": "ok"}
monkeypatch.setattr(kernel_module, "run_embedding_runtime_self_check", fake_self_check)
monkeypatch.setattr(memory_service_module, "get_plugin_runtime_manager", None)
monkeypatch.setattr(summarizer_module, "_chat_manager", fake_chat_manager)
monkeypatch.setattr(knowledge_module, "_chat_manager", fake_chat_manager)
monkeypatch.setattr(person_info_module, "_chat_manager", fake_chat_manager)
@@ -148,7 +137,7 @@ async def real_dialogue_env(monkeypatch, tmp_path):
},
)
manager = _KernelBackedRuntimeManager(kernel)
monkeypatch.setattr(memory_service_module, "get_plugin_runtime_manager", lambda: manager)
monkeypatch.setattr(memory_service_module, "a_memorix_host_service", manager)
await kernel.initialize()
try:

View File

@@ -32,21 +32,11 @@ def _load_dialogue_fixture() -> Dict[str, Any]:
class _KernelBackedRuntimeManager:
is_running = True
def __init__(self, kernel: SDKMemoryKernel) -> None:
self.kernel = kernel
async def invoke_plugin(
self,
*,
method: str,
plugin_id: str,
component_name: str,
args: Dict[str, Any] | None,
timeout_ms: int,
):
del method, plugin_id, timeout_ms
async def invoke(self, component_name: str, args: Dict[str, Any] | None, *, timeout_ms: int = 30000):
del timeout_ms
payload = args or {}
if component_name == "search_memory":
return await self.kernel.search_memory(
@@ -100,7 +90,6 @@ async def live_dialogue_env(monkeypatch, tmp_path):
get_session_name=lambda session_id: session_cfg["display_name"] if session_id == session.session_id else session_id,
)
monkeypatch.setattr(memory_service_module, "get_plugin_runtime_manager", None)
monkeypatch.setattr(summarizer_module, "_chat_manager", fake_chat_manager)
monkeypatch.setattr(knowledge_module, "_chat_manager", fake_chat_manager)
monkeypatch.setattr(person_info_module, "_chat_manager", fake_chat_manager)
@@ -116,7 +105,7 @@ async def live_dialogue_env(monkeypatch, tmp_path):
},
)
manager = _KernelBackedRuntimeManager(kernel)
monkeypatch.setattr(memory_service_module, "get_plugin_runtime_manager", lambda: manager)
monkeypatch.setattr(memory_service_module, "a_memorix_host_service", manager)
await kernel.initialize()
try: