from pathlib import Path import re import json root = Path(__file__).resolve().parent def ids(name): return set(re.findall(r'^test_\w+ \(([\w.]+)\)', (root/name).read_text(encoding='utf-8'), re.M)) base = ids('visual-target-final.log') trace = ids('visual-trace-target.log') summary = {'baseline_unique_tests':len(base), 'trace_regression_unique_tests':len(trace), 'overlap_tests':len(base & trace), 'union_unique_tests_per_directory':len(base | trace), 'separate_real_ocr_cases_per_directory':24} (root/'visual-deduplicated-counts.json').write_text(json.dumps(summary, indent=2), encoding='utf-8') text = f''' ## 追加:视觉问答知识引用留痕修复 只读复核未发现上述昵称/缓存修复的阻断性回归:`@徽信` 后缀仅在客户主体完全匹配已知名字时纠正;默认头像不同昵称已有独立回归;裁片不可用时不复用识别结果;裁片变化会重新识别,未读筛选页的第二帧也执行深度 OCR。视觉调用仍将按会话取出的 history 传给 `ai_chat.get_ai_reply`;`_call_model_with_observer` 的模型回调在原调用线程执行,不会丢失 thread-local 网关 trace。 发现并修复一项额外可追溯缺陷:视觉 `_orchestrated_reply` 原来取到网关 `knowledge` 后未传给 `_record_model_call`,待回复状态也没有 `knowledge_trace` 与 `model_task_id`,与协议路径不一致。现在按协议字段语义保留这两项,在磁盘持久化/恢复时保留,新客户消息开始下一轮时清除旧引用。仅保存知识开关、检索模式、耗时及每个命中的 id/revision/score(最多20条),不保存知识正文或聊天原文;调用前消费旧 thread-local trace,防止内部识别调用的旧记录误挂到安全兜底回复。发送和审核判断未改动。 新增 `test_visual_knowledge_trace_audit.py` 6 项:历史/图像与引用同时保留;无裁判也可记录引用;重启恢复;丢弃额外大正文;下一轮清除旧引用;旧内部调用不冒充本次问答;其中有关清除行为分别覆盖新消息和新模型结果。最小复现 `visual-trace-before.json` 为6项全部未通过(2失败/4错误),修复后两目录各 **526项/0失败/0错误/0跳过**。涉及 `test_reply_safety`、`test_visual_pipeline`、`test_model_router` 及新增模块;526项包含原625项的大量复测,不能相加声称1151个独立测试。 按日志测试 ID 去重:原集合{len(base)}项,追加集合{len(trace)}项,重合{len(base & trace)}项,**合并为每目录{len(base | trace)}个独立自动测试**。本机真实 OCR 的24组图片单独计数,未包含在自动测试数字中。两个目录同一组场景重复运行,不把数量翻倍当作独立场景。 追加改动仍仅为两目录 `wechat_bot.py` 与新增测试;补丁前版本保存在各目录 `backups/recognition-audit-20260916/visual-trace/wechat_bot.py`。相关证据:`visual-trace-source.json/.log`、`visual-trace-target.json/.log`、`visual-trace-changes.json`、`visual-deduplicated-counts.json`。末次回归外部动作拦截次数仍为0。 ''' with (root/'visual-report.md').open('a', encoding='utf-8') as output: output.write(text) print(json.dumps(summary))