"""
mem1 记忆效果评测

设计连续对话场景，测试 AI 是否能记住之前的对话内容。
每个场景包含多轮连续对话，最后一轮提问检验记忆效果。

评分标准（1-5分）：
- 5分：完全记住，回答准确且详细
- 4分：基本记住，回答正确但不够详细
- 3分：部分记住，回答有遗漏或不准确
- 2分：记忆模糊，回答大部分错误
- 1分：完全忘记，无法回答或答非所问
"""
import os
import json
import logging
from datetime import datetime, timedelta
from pathlib import Path
from dotenv import load_dotenv

from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage

from mem1 import Mem1Memory, Mem1Config, LLMConfig

load_dotenv()
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')

# 配置
config = Mem1Config.from_env()
config.memory.auto_update_profile = True
config.memory.update_interval_rounds = 3

USER_ID = "memory_eval_user"
SAMPLE_IMAGE = Path("天价麻花.png")


# ============================================================
# 连续对话测试场景
# 每个场景包含：
# - conversations: 连续对话列表
# - memory_test: 记忆测试问题
# - expected_answer: 期望答案（用于评分参考）
# - key_points: 必须记住的关键点
# ============================================================

TEST_SCENARIOS = [
    # ========== 场景1：天价麻花舆情事件 ==========
    {
        "id": "scenario_01",
        "name": "天价麻花舆情跟踪",
        "description": "跟踪一个舆情事件的完整生命周期",
        "time_span_hours": 48,  # 跨越48小时
        "conversations": [
            {
                "time_offset_hours": 0,
                "user": "今天发现一个舆情，广东江门某景区卖天价麻花，一根要60元，游客投诉后引发关注。帮我记录一下。",
                "assistant": "已记录。事件概要：广东江门景区天价麻花事件，单价60元，因游客投诉引发舆情。我会持续关注此事件发展。",
                "metadata": {"event": "天价麻花", "stage": "发现"},
                "has_image": True
            },
            {
                "time_offset_hours": 4,
                "user": "天价麻花事件有新进展，当地市场监管局已介入调查，商家暂停营业。",
                "assistant": "已更新。事件进展：市场监管局介入调查，涉事商家已暂停营业。舆情正在发酵中。",
                "metadata": {"event": "天价麻花", "stage": "调查"}
            },
            {
                "time_offset_hours": 12,
                "user": "市场监管局公布调查结果：商家明码标价，不构成价格欺诈，但存在服务态度问题。",
                "assistant": "已记录调查结论。虽不构成价格欺诈，但服务态度问题可能继续引发讨论。建议关注舆论反应。",
                "metadata": {"event": "天价麻花", "stage": "结论"}
            },
            {
                "time_offset_hours": 24,
                "user": "舆情基本平息，但网民对'明码标价就不算欺诈'的说法有争议。我准备写个总结报告。",
                "assistant": "好的。这个事件从发现到平息经历了约24小时，核心争议点是'明码标价'与'价格合理性'的边界问题。",
                "metadata": {"event": "天价麻花", "stage": "总结"}
            }
        ],
        "memory_test": {
            "time_offset_hours": 48,
            "question": "帮我回顾一下天价麻花事件的完整经过，包括时间线、各方反应和最终结论。",
            "expected_answer": "天价麻花事件经过：1）发现阶段：广东江门景区麻花60元一根，游客投诉；2）调查阶段：市场监管局介入，商家暂停营业；3）结论：不构成价格欺诈，但存在服务态度问题；4）后续：舆情基本平息，但'明码标价'说法引发争议。",
            "key_points": ["广东江门", "60元", "市场监管局", "不构成价格欺诈", "服务态度问题", "明码标价争议"]
        }
    },

    # ========== 场景2：用户偏好记忆 ==========
    {
        "id": "scenario_02",
        "name": "工作偏好积累",
        "description": "测试是否记住用户多次表达的工作偏好",
        "time_span_hours": 72,
        "conversations": [
            {
                "time_offset_hours": 0,
                "user": "我是李明，在市网信办舆情监测科工作，以后请叫我李科。",
                "assistant": "好的李科，很高兴认识您。我会记住您的身份信息。",
                "metadata": {"type": "自我介绍"}
            },
            {
                "time_offset_hours": 8,
                "user": "以后给我写报告要简洁一点，领导没时间看长篇大论，控制在500字以内。",
                "assistant": "明白，以后报告控制在500字以内，突出重点。",
                "metadata": {"type": "偏好设置"}
            },
            {
                "time_offset_hours": 24,
                "user": "对了，我每周一要交周报，周五要准备下周计划，记得提醒我。",
                "assistant": "已记录。周一交周报，周五准备下周计划，我会适时提醒您。",
                "metadata": {"type": "工作习惯"}
            },
            {
                "time_offset_hours": 48,
                "user": "数据统计的时候，记得用表格形式，我习惯看表格，比文字清晰。",
                "assistant": "好的，以后数据统计会用表格呈现，更直观清晰。",
                "metadata": {"type": "偏好设置"}
            }
        ],
        "memory_test": {
            "time_offset_hours": 72,
            "question": "你还记得我是谁吗？我的工作习惯和偏好是什么？",
            "expected_answer": "您是李科（李明），在市网信办舆情监测科工作。您的偏好：1）报告要简洁，控制在500字以内；2）周一交周报，周五准备下周计划；3）数据统计用表格形式呈现。",
            "key_points": ["李明/李科", "网信办舆情监测科", "500字以内", "周一周报", "周五计划", "表格形式"]
        }
    },

    # ========== 场景3：投诉案件跟踪 ==========
    {
        "id": "scenario_03",
        "name": "网络诈骗投诉处理",
        "description": "跟踪一个投诉案件的处理全过程",
        "time_span_hours": 96,
        "conversations": [
            {
                "time_offset_hours": 0,
                "user": "收到一个投诉，投诉人张女士，手机尾号8856，被某购物APP诈骗了3000元，APP名叫'优惠购'。",
                "assistant": "已记录投诉信息。投诉人：张女士（手机尾号8856），涉案APP：优惠购，金额：3000元。需要我协助什么？",
                "metadata": {"case": "张女士诈骗投诉", "stage": "受理"}
            },
            {
                "time_offset_hours": 24,
                "user": "联系了APP运营方，他们在深圳，公司名叫'深圳优购科技有限公司'，说会在3个工作日内处理。",
                "assistant": "已更新。运营方：深圳优购科技有限公司（深圳），承诺3个工作日内处理。建议跟进处理结果。",
                "metadata": {"case": "张女士诈骗投诉", "stage": "联系运营方"}
            },
            {
                "time_offset_hours": 72,
                "user": "运营方反馈说是系统故障导致重复扣款，已退还张女士2000元，剩余1000元需要核实。",
                "assistant": "已记录。部分退款2000元，剩余1000元待核实。建议继续跟进剩余款项。",
                "metadata": {"case": "张女士诈骗投诉", "stage": "部分解决"}
            }
        ],
        "memory_test": {
            "time_offset_hours": 96,
            "question": "张女士那个投诉案件现在什么情况？涉及哪个公司？还有多少钱没退？",
            "expected_answer": "张女士投诉案件情况：涉案APP'优惠购'，运营方是深圳优购科技有限公司。原投诉金额3000元，已退还2000元，剩余1000元待核实。",
            "key_points": ["张女士", "优惠购", "深圳优购科技", "3000元", "已退2000元", "剩余1000元"]
        }
    },

    # ========== 场景4：数据统计任务 ==========
    {
        "id": "scenario_04",
        "name": "月度数据统计",
        "description": "分多次汇报数据，测试数据记忆",
        "time_span_hours": 168,
        "conversations": [
            {
                "time_offset_hours": 0,
                "user": "开始统计本月数据。第一周：处理舆情23起，其中重大舆情2起，一般舆情21起。",
                "assistant": "已记录第一周数据：舆情23起（重大2起，一般21起）。",
                "metadata": {"task": "月度统计", "week": 1}
            },
            {
                "time_offset_hours": 48,
                "user": "第二周数据：处理舆情31起，重大舆情5起，一般舆情26起。这周比较忙。",
                "assistant": "已记录第二周数据：舆情31起（重大5起，一般26起）。确实比第一周多了不少。",
                "metadata": {"task": "月度统计", "week": 2}
            },
            {
                "time_offset_hours": 96,
                "user": "第三周：舆情18起，重大1起，一般17起。另外处理投诉45件。",
                "assistant": "已记录第三周：舆情18起（重大1起，一般17起），投诉45件。",
                "metadata": {"task": "月度统计", "week": 3}
            },
            {
                "time_offset_hours": 144,
                "user": "第四周：舆情25起，重大3起，一般22起。投诉38件。",
                "assistant": "已记录第四周：舆情25起（重大3起，一般22起），投诉38件。",
                "metadata": {"task": "月度统计", "week": 4}
            }
        ],
        "memory_test": {
            "time_offset_hours": 168,
            "question": "帮我汇总一下这个月的数据，舆情总数多少？重大舆情多少起？哪周最忙？",
            "expected_answer": "本月数据汇总：舆情总数97起（23+31+18+25），其中重大舆情11起（2+5+1+3），一般舆情86起。第二周最忙，处理了31起舆情，重大舆情5起。投诉共83件（第三周45件+第四周38件）。",
            "key_points": ["舆情97起", "重大11起", "第二周最忙", "31起", "投诉83件"]
        }
    },

    # ========== 场景5：应急事件处置 ==========
    {
        "id": "scenario_05",
        "name": "网络谣言应急处置",
        "description": "处理一起网络谣言的完整过程",
        "time_span_hours": 24,
        "conversations": [
            {
                "time_offset_hours": 0,
                "user": "紧急！发现一条谣言在传播：'本市自来水被污染，不能饮用'。首发于微博账号@本地爆料王，已有5000+转发。",
                "assistant": "收到紧急情况。谣言内容：自来水污染不能饮用。首发：微博@本地爆料王，转发5000+。建议立即启动应急响应。",
                "metadata": {"event": "自来水谣言", "stage": "发现", "priority": "urgent"}
            },
            {
                "time_offset_hours": 2,
                "user": "已联系水务局，确认自来水正常，各项指标合格。他们会出一份官方声明。",
                "assistant": "好的。水务局确认水质正常，等待官方声明。建议同步准备辟谣材料。",
                "metadata": {"event": "自来水谣言", "stage": "核实"}
            },
            {
                "time_offset_hours": 4,
                "user": "水务局声明已发布。同时我们联系了微博，要求对谣言账号进行处理。账号已被禁言7天。",
                "assistant": "进展顺利。官方声明已发，谣言账号@本地爆料王被禁言7天。建议继续监测舆情走向。",
                "metadata": {"event": "自来水谣言", "stage": "处置"}
            },
            {
                "time_offset_hours": 8,
                "user": "舆情已基本平息，辟谣信息阅读量达到12万。但发现有3个小号在继续传播，已标记。",
                "assistant": "基本控制住了。辟谣阅读12万，但仍有3个小号传播，需持续关注。",
                "metadata": {"event": "自来水谣言", "stage": "收尾"}
            }
        ],
        "memory_test": {
            "time_offset_hours": 24,
            "question": "昨天那个自来水谣言事件，帮我回顾一下处置过程和结果，我要写应急处置报告。",
            "expected_answer": "自来水谣言处置报告：1）发现：微博@本地爆料王发布谣言，转发5000+；2）核实：联系水务局确认水质正常；3）处置：水务局发布官方声明，谣言账号被禁言7天；4）效果：辟谣阅读量12万，舆情基本平息；5）遗留：3个小号仍在传播，已标记跟踪。",
            "key_points": ["@本地爆料王", "5000+转发", "水务局", "禁言7天", "辟谣12万阅读", "3个小号"]
        }
    },

    # ========== 场景6：跨天任务交接 ==========
    {
        "id": "scenario_06",
        "name": "工作任务交接",
        "description": "测试跨多天的任务记忆",
        "time_span_hours": 120,
        "conversations": [
            {
                "time_offset_hours": 0,
                "user": "下周有几个重要任务：周一要参加省厅视频会议，周三有个培训要讲课，周五前要交季度报告。",
                "assistant": "已记录下周任务：周一-省厅视频会议；周三-培训讲课；周五前-提交季度报告。",
                "metadata": {"type": "任务安排"}
            },
            {
                "time_offset_hours": 24,
                "user": "补充一下，周三培训的主题是'网络舆情应对技巧'，地点在市委党校，下午2点开始。",
                "assistant": "已更新周三培训信息：主题'网络舆情应对技巧'，地点市委党校，时间下午2点。",
                "metadata": {"type": "任务补充"}
            },
            {
                "time_offset_hours": 72,
                "user": "省厅会议改到周二上午10点了，周一的会取消。另外季度报告需要包含舆情处置案例分析。",
                "assistant": "已更新：周一会议取消，改为周二上午10点。季度报告需包含舆情处置案例分析。",
                "metadata": {"type": "任务变更"}
            }
        ],
        "memory_test": {
            "time_offset_hours": 120,
            "question": "帮我确认一下这周的工作安排，特别是会议和培训的时间地点。",
            "expected_answer": "本周工作安排：1）周二上午10点-省厅视频会议（原周一取消）；2）周三下午2点-培训讲课，主题'网络舆情应对技巧'，地点市委党校；3）周五前-提交季度报告，需包含舆情处置案例分析。",
            "key_points": ["周二上午10点", "省厅会议", "周三下午2点", "市委党校", "网络舆情应对技巧", "季度报告", "案例分析"]
        }
    },

    # ========== 场景7：多平台舆情监测 ==========
    {
        "id": "scenario_07",
        "name": "教育乱收费舆情",
        "description": "同一事件在多平台的舆情跟踪",
        "time_span_hours": 72,
        "conversations": [
            {
                "time_offset_hours": 0,
                "user": "发现一个教育舆情：某中学强制学生购买平板电脑，每台3980元。目前在抖音有相关视频，播放量8万。",
                "assistant": "已记录。事件：某中学强制购买平板3980元。平台：抖音，播放量8万。建议关注其他平台扩散情况。",
                "metadata": {"event": "教育乱收费", "platform": "抖音"}
            },
            {
                "time_offset_hours": 12,
                "user": "舆情扩散了。微博话题#学校强制买平板#阅读量已达200万，微信公众号'教育观察'也发了文章。",
                "assistant": "舆情升级。微博话题阅读200万，微信公众号跟进报道。建议提升关注级别。",
                "metadata": {"event": "教育乱收费", "platform": "微博+微信"}
            },
            {
                "time_offset_hours": 24,
                "user": "教育局介入了，学校是城南中学，校长姓王。教育局要求学校退款并整改。",
                "assistant": "已更新。涉事学校：城南中学，校长姓王。教育局已介入，要求退款整改。",
                "metadata": {"event": "教育乱收费", "stage": "处置"}
            },
            {
                "time_offset_hours": 48,
                "user": "学校已开始退款，目前退了150人，还有约200人没退。家长反映退款流程复杂。",
                "assistant": "退款进行中。已退150人，待退约200人。家长反映流程复杂，可能引发新一轮投诉。",
                "metadata": {"event": "教育乱收费", "stage": "退款"}
            }
        ],
        "memory_test": {
            "time_offset_hours": 72,
            "question": "教育乱收费那个事件，涉及哪个学校？各平台传播情况怎样？退款进度如何？",
            "expected_answer": "教育乱收费事件：涉事学校城南中学（校长姓王），强制购买平板3980元。传播情况：抖音视频播放8万，微博话题阅读200万，微信公众号跟进。处置：教育局介入要求退款整改，已退150人，待退约200人，家长反映退款流程复杂。",
            "key_points": ["城南中学", "王校长", "3980元", "抖音8万", "微博200万", "已退150人", "待退200人"]
        }
    },

    # ========== 场景8：反馈与改进 ==========
    {
        "id": "scenario_08",
        "name": "工作反馈记忆",
        "description": "测试是否记住用户的正面和负面反馈",
        "time_span_hours": 168,
        "conversations": [
            {
                "time_offset_hours": 0,
                "user": "上次你帮我写的舆情报告，领导说太啰嗦了，以后要精简，最好用数据说话。",
                "assistant": "感谢反馈。以后报告会更精简，多用数据支撑，减少冗余描述。",
                "metadata": {"type": "负面反馈", "about": "报告风格"}
            },
            {
                "time_offset_hours": 48,
                "user": "这次的数据统计表格做得不错，格式清晰，领导表扬了。继续保持这个风格。",
                "assistant": "谢谢认可！表格形式确实更直观，以后会继续保持这种风格。",
                "metadata": {"type": "正面反馈", "about": "表格风格"}
            },
            {
                "time_offset_hours": 96,
                "user": "应急预案写得太笼统了，缺少具体的时间节点和责任人，下次要改进。",
                "assistant": "收到。应急预案需要更具体，包含时间节点和责任人。下次会改进。",
                "metadata": {"type": "负面反馈", "about": "应急预案"}
            },
            {
                "time_offset_hours": 144,
                "user": "今天的舆情研判很到位，提前预警了风险点，帮了大忙。",
                "assistant": "很高兴能帮上忙！提前预警是舆情工作的关键，会继续保持敏感度。",
                "metadata": {"type": "正面反馈", "about": "舆情研判"}
            }
        ],
        "memory_test": {
            "time_offset_hours": 168,
            "question": "根据之前的反馈，你觉得我对工作成果有什么要求？哪些方面做得好，哪些需要改进？",
            "expected_answer": "根据您的反馈总结：【需改进】1）报告要精简，多用数据说话，避免啰嗦；2）应急预案要具体，包含时间节点和责任人。【做得好】1）数据统计表格格式清晰；2）舆情研判到位，能提前预警风险。",
            "key_points": ["报告精简", "数据说话", "应急预案具体", "时间节点", "责任人", "表格清晰", "提前预警"]
        }
    },

    # ========== 场景9：长期项目跟踪 ==========
    {
        "id": "scenario_09",
        "name": "专项整治行动",
        "description": "跟踪一个持续多周的专项行动",
        "time_span_hours": 336,  # 14天
        "conversations": [
            {
                "time_offset_hours": 0,
                "user": "启动'清朗·网络直播整治'专项行动，为期一个月，重点整治低俗直播和虚假宣传。",
                "assistant": "已记录。专项行动：清朗·网络直播整治，周期一个月，重点：低俗直播、虚假宣传。",
                "metadata": {"project": "清朗行动", "stage": "启动"}
            },
            {
                "time_offset_hours": 72,
                "user": "第一周成果：排查直播平台12家，发现问题账号156个，已处置89个，其中永久封禁23个。",
                "assistant": "第一周数据已记录：排查平台12家，问题账号156个，已处置89个（永久封禁23个）。",
                "metadata": {"project": "清朗行动", "week": 1}
            },
            {
                "time_offset_hours": 168,
                "user": "第二周：新增排查平台5家，发现问题账号203个，处置178个，约谈平台负责人3次。",
                "assistant": "第二周数据：新增平台5家（累计17家），问题账号203个，处置178个，约谈3次。",
                "metadata": {"project": "清朗行动", "week": 2}
            },
            {
                "time_offset_hours": 264,
                "user": "第三周进展：问题账号98个，全部处置。另外有2家平台因整改不力被通报批评。",
                "assistant": "第三周：问题账号98个全部处置，2家平台被通报批评。整治力度加大。",
                "metadata": {"project": "清朗行动", "week": 3}
            }
        ],
        "memory_test": {
            "time_offset_hours": 336,
            "question": "清朗行动进行到现在，总共排查了多少平台？处置了多少问题账号？有什么重要措施？",
            "expected_answer": "清朗·网络直播整治行动进展：排查平台累计17家（12+5），发现问题账号457个（156+203+98），处置365个（89+178+98），其中永久封禁23个。重要措施：约谈平台负责人3次，2家平台因整改不力被通报批评。",
            "key_points": ["17家平台", "457个问题账号", "处置365个", "永久封禁23个", "约谈3次", "2家通报批评"]
        }
    },

    # ========== 场景10：复杂信息记忆 ==========
    {
        "id": "scenario_10",
        "name": "多方协调会议",
        "description": "测试复杂会议信息的记忆",
        "time_span_hours": 48,
        "conversations": [
            {
                "time_offset_hours": 0,
                "user": "今天开了个协调会，参会单位有：网信办（我）、公安局网安支队（张队长）、市场监管局（李处长）、教育局（王科长）。",
                "assistant": "已记录参会人员：网信办（您）、公安网安支队（张队长）、市场监管局（李处长）、教育局（王科长）。",
                "metadata": {"meeting": "协调会", "part": "参会人员"}
            },
            {
                "time_offset_hours": 1,
                "user": "会议讨论了三个议题：1）校园贷整治，由公安牵头；2）教育APP备案，教育局负责；3）虚假广告治理，市场监管局主导。",
                "assistant": "三个议题已记录：1）校园贷整治-公安牵头；2）教育APP备案-教育局负责；3）虚假广告治理-市场监管局主导。",
                "metadata": {"meeting": "协调会", "part": "议题"}
            },
            {
                "time_offset_hours": 2,
                "user": "会议决定：下周五前各单位报送整治方案，两周后再开一次推进会。我负责汇总各方材料。",
                "assistant": "会议决议：下周五前报送方案，两周后开推进会。您负责汇总材料。",
                "metadata": {"meeting": "协调会", "part": "决议"}
            }
        ],
        "memory_test": {
            "time_offset_hours": 48,
            "question": "前天那个协调会，都有谁参加？讨论了什么？各自负责什么？下一步安排是什么？",
            "expected_answer": "协调会情况：【参会人员】网信办（您）、公安网安支队（张队长）、市场监管局（李处长）、教育局（王科长）。【议题分工】1）校园贷整治-公安牵头；2）教育APP备案-教育局负责；3）虚假广告治理-市场监管局主导。【下一步】下周五前各单位报送方案，两周后开推进会，您负责汇总材料。",
            "key_points": ["张队长", "李处长", "王科长", "校园贷-公安", "教育APP-教育局", "虚假广告-市场监管", "下周五报方案", "两周后推进会"]
        }
    }
]


# ============================================================
# 评分逻辑
# ============================================================

def evaluate_response(response: str, expected: str, key_points: list) -> dict:
    """
    评估 AI 回答的质量
    
    返回：
    - score: 1-5 分
    - matched_points: 匹配的关键点
    - missed_points: 遗漏的关键点
    - reason: 评分理由
    """
    response_lower = response.lower()
    
    matched = []
    missed = []
    
    for point in key_points:
        # 检查关键点是否在回答中出现
        if point.lower() in response_lower or any(p in response_lower for p in point.lower().split('/')):
            matched.append(point)
        else:
            missed.append(point)
    
    # 计算匹配率
    match_rate = len(matched) / len(key_points) if key_points else 0
    
    # 评分逻辑
    if match_rate >= 0.9:
        score = 5
        reason = "完全记住，回答准确且详细"
    elif match_rate >= 0.7:
        score = 4
        reason = "基本记住，回答正确但有少量遗漏"
    elif match_rate >= 0.5:
        score = 3
        reason = "部分记住，回答有明显遗漏"
    elif match_rate >= 0.3:
        score = 2
        reason = "记忆模糊，大部分信息丢失"
    else:
        score = 1
        reason = "完全忘记，无法回答关键信息"
    
    return {
        "score": score,
        "match_rate": f"{match_rate*100:.1f}%",
        "matched_points": matched,
        "missed_points": missed,
        "reason": reason
    }


# ============================================================
# 测试执行
# ============================================================

def run_scenario(memory: Mem1Memory, llm, scenario: dict, base_time: datetime) -> dict:
    """执行单个测试场景"""
    
    print(f"\n{'='*60}")
    print(f"场景: {scenario['name']}")
    print(f"描述: {scenario['description']}")
    print(f"时间跨度: {scenario['time_span_hours']} 小时")
    print(f"{'='*60}")
    
    # 执行连续对话
    for i, conv in enumerate(scenario['conversations'], 1):
        current_time = base_time + timedelta(hours=conv['time_offset_hours'])
        
        print(f"\n--- 对话 {i}/{len(scenario['conversations'])} ---")
        print(f"时间: {current_time.strftime('%Y-%m-%d %H:%M')}")
        print(f"👤 用户: {conv['user'][:60]}...")
        
        # 准备图片
        images = None
        if conv.get('has_image') and SAMPLE_IMAGE.exists():
            images = [{"filename": SAMPLE_IMAGE.name, "path": str(SAMPLE_IMAGE)}]
            print(f"📷 附带图片: {SAMPLE_IMAGE.name}")
        
        # 保存对话
        memory.add_conversation(
            messages=[
                {"role": "user", "content": conv['user']},
                {"role": "assistant", "content": conv['assistant']}
            ],
            images=images,
            metadata=conv.get('metadata', {}),
            timestamp=current_time.strftime('%Y-%m-%d %H:%M:%S')
        )
        
        print(f"🤖 助手: {conv['assistant'][:60]}...")
    
    # 执行记忆测试
    test = scenario['memory_test']
    test_time = base_time + timedelta(hours=test['time_offset_hours'])
    
    print(f"\n{'='*60}")
    print(f"📝 记忆测试")
    print(f"时间: {test_time.strftime('%Y-%m-%d %H:%M')} (距首次对话 {test['time_offset_hours']} 小时)")
    print(f"{'='*60}")
    
    print(f"\n❓ 测试问题: {test['question']}")
    print(f"\n📋 期望答案: {test['expected_answer'][:100]}...")
    print(f"\n🔑 关键点: {', '.join(test['key_points'])}")
    
    # 获取上下文并调用 LLM
    ctx = memory.get_context(query=test['question'])
    
    system_prompt = f"""你是一个专业的网信办工作助手。请根据用户画像和历史对话记录回答问题。

## 当前时间
{test_time.strftime('%Y-%m-%d %H:%M:%S')}

## 用户画像
{ctx['import_content']}

## 最近对话记录
{ctx['normal_content']}

## 重要规则
1. 回答必须基于上述对话记录中的实际内容，严禁编造任何信息
2. 涉及数字（金额、数量、百分比、日期等）时，必须从对话记录中原样提取，不得估算或编造
3. 需要汇总累加时，必须列出计算过程（如：23+31+18+25=97）
4. 涉及人名、公司名、账号名等实体时，必须使用对话中的原始名称
5. 如果对话记录中没有相关信息，请明确说"对话记录中未提及"，不要猜测
"""
    
    messages = [
        SystemMessage(content=system_prompt),
        HumanMessage(content=test['question'])
    ]
    
    response = llm.invoke(messages)
    ai_response = response.content
    
    print(f"\n🤖 AI 回答:\n{ai_response}")
    
    # 评分
    evaluation = evaluate_response(ai_response, test['expected_answer'], test['key_points'])
    
    print(f"\n{'='*60}")
    print(f"📊 评分结果")
    print(f"{'='*60}")
    print(f"得分: {evaluation['score']}/5 ({evaluation['match_rate']})")
    print(f"评价: {evaluation['reason']}")
    print(f"✓ 记住的关键点: {', '.join(evaluation['matched_points']) or '无'}")
    print(f"✗ 遗漏的关键点: {', '.join(evaluation['missed_points']) or '无'}")
    
    return {
        "scenario_id": scenario['id'],
        "scenario_name": scenario['name'],
        "question": test['question'],
        "expected": test['expected_answer'],
        "actual": ai_response,
        "evaluation": evaluation
    }


def run_all_tests():
    """运行所有测试场景"""
    
    print("\n" + "="*60)
    print("mem1 记忆效果评测")
    print("="*60)
    print(f"测试场景数: {len(TEST_SCENARIOS)}")
    print(f"用户ID: {USER_ID}")
    print("="*60 + "\n")
    
    memory = Mem1Memory(config, user_id=USER_ID)
    
    llm = ChatOpenAI(
        model=config.llm.model,
        api_key=config.llm.api_key,
        base_url=config.llm.base_url
    )
    
    # 清理之前的测试数据
    try:
        memory.delete_user()
        print("✓ 已清理历史测试数据\n")
    except Exception:
        pass
    
    results = []
    base_time = datetime.now() - timedelta(days=30)
    
    for i, scenario in enumerate(TEST_SCENARIOS, 1):
        print(f"\n{'#'*60}")
        print(f"# 测试 {i}/{len(TEST_SCENARIOS)}")
        print(f"{'#'*60}")
        
        try:
            result = run_scenario(memory, llm, scenario, base_time)
            results.append(result)
            
            # 更新基准时间，避免场景时间重叠
            base_time += timedelta(hours=scenario['time_span_hours'] + 24)
            
        except Exception as e:
            logging.error(f"场景 {scenario['name']} 执行失败: {e}")
            results.append({
                "scenario_id": scenario['id'],
                "scenario_name": scenario['name'],
                "error": str(e),
                "evaluation": {"score": 0, "reason": f"执行失败: {e}"}
            })
    
    # 生成总结报告
    generate_summary_report(results, memory)
    
    return results


def generate_summary_report(results: list, memory: Mem1Memory):
    """生成总结报告"""
    
    print("\n" + "="*60)
    print("📊 评测总结报告")
    print("="*60 + "\n")
    
    # 统计得分
    scores = [r['evaluation']['score'] for r in results if 'score' in r.get('evaluation', {})]
    
    if scores:
        avg_score = sum(scores) / len(scores)
        
        print(f"总测试场景: {len(TEST_SCENARIOS)}")
        print(f"成功执行: {len(scores)}")
        print(f"平均得分: {avg_score:.2f}/5")
        print(f"最高分: {max(scores)}/5")
        print(f"最低分: {min(scores)}/5")
        
        # 得分分布
        score_dist = {5: 0, 4: 0, 3: 0, 2: 0, 1: 0, 0: 0}
        for s in scores:
            score_dist[s] = score_dist.get(s, 0) + 1
        
        print(f"\n得分分布:")
        for score in [5, 4, 3, 2, 1]:
            count = score_dist[score]
            bar = "█" * count + "░" * (len(TEST_SCENARIOS) - count)
            print(f"  {score}分: {bar} ({count}个)")
    
    # 详细结果
    print(f"\n{'='*60}")
    print("详细结果")
    print("="*60 + "\n")
    
    for r in results:
        score = r['evaluation'].get('score', 0)
        emoji = "🌟" if score >= 4 else "⚠️" if score >= 3 else "❌"
        print(f"{emoji} {r['scenario_name']}: {score}/5 - {r['evaluation'].get('reason', 'N/A')}")
        
        if r['evaluation'].get('missed_points'):
            print(f"   遗漏: {', '.join(r['evaluation']['missed_points'])}")
    
    # 用户画像
    print(f"\n{'='*60}")
    print("最终用户画像")
    print("="*60 + "\n")
    
    ctx = memory.get_context(query="")
    print(ctx['import_content'][:1000])
    if len(ctx['import_content']) > 1000:
        print(f"\n... (共 {len(ctx['import_content'])} 字符)")
    
    # 保存结果到文件
    report_file = f"memory_eval_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
    with open(report_file, 'w', encoding='utf-8') as f:
        json.dump({
            "summary": {
                "total_scenarios": len(TEST_SCENARIOS),
                "executed": len(scores),
                "avg_score": avg_score if scores else 0,
                "score_distribution": score_dist
            },
            "results": results,
            "user_profile": ctx['import_content']
        }, f, ensure_ascii=False, indent=2)
    
    print(f"\n✓ 详细报告已保存到: {report_file}")
    
    # 清理选项
    print(f"\n{'='*60}")
    print("是否清理测试数据？(y/n): ", end="")
    choice = input().strip().lower()
    
    if choice == 'y':
        memory.delete_user()
        print(f"✓ 已删除测试用户 {USER_ID}")
    else:
        print("保留测试数据供进一步分析")


if __name__ == "__main__":
    try:
        run_all_tests()
    except KeyboardInterrupt:
        print("\n\n测试被中断")
    except Exception as e:
        logging.error(f"测试失败: {e}", exc_info=True)
