"""记忆系统状态探针(只读):一次看清「库里有没有」「事件消费到哪了」。 用途:排查"记忆系统是否真的在工作"时,先跑这个脚本拿到事实基线, 再去看日志定位是哪一段断了。全部是 SELECT,不写任何数据。 用法: D:/conda/envs/jr_py313/python.exe tools/probe_memory_state.py [customer_id] """ from __future__ import annotations import asyncio import sys from typing import Any from sqlalchemy import text from app.infrastructure.db import SessionFactory TABLES = ( "memory_unit", "memory_evidence", "memory_conflict", "memory_sync_outbox", "user_facts", "profile_snapshots", ) async def _scalar(session: Any, sql: str, **params: Any) -> Any: try: return await session.scalar(text(sql), params) except Exception as exc: # 表不存在/连不上库都要能看,而不是整个脚本崩掉 return f"ERROR {type(exc).__name__}: {exc}" async def main() -> None: customer_id = int(sys.argv[1]) if len(sys.argv) > 1 else None async with SessionFactory() as session: print("=" * 60) print("1) 记忆相关表的总行数") print("=" * 60) for table in TABLES: print(f" {table:<22} {await _scalar(session, f'SELECT COUNT(*) FROM {table}')}") print() print("=" * 60) print("2) memory_unit 按状态分组") print("=" * 60) try: rows = (await session.execute( text("SELECT status, COUNT(*) c FROM memory_unit GROUP BY status"))).all() for row in rows: print(f" {row[0]:<12} {row[1]}") except Exception as exc: print(f" ERROR {type(exc).__name__}: {exc}") print() print("=" * 60) print("3) domain_event_outbox 按 (event_type, status) 分组") print(" —— 这里能看到事件是不是堆在 pending(= Worker 没跑 / 处理失败)") print("=" * 60) try: rows = (await session.execute(text( "SELECT event_type, status, COUNT(*) c, MAX(occurred_at) latest " "FROM domain_event_outbox GROUP BY event_type, status ORDER BY c DESC" ))).all() if not rows: print(" (空)") for row in rows: print(f" {row[0]:<38} {row[1]:<10} {row[2]:<6} latest={row[3]}") except Exception as exc: print(f" ERROR {type(exc).__name__}: {exc}") if customer_id is not None: print() print("=" * 60) print(f"4) 客户 {customer_id} 的记忆明细") print("=" * 60) try: rows = (await session.execute(text( "SELECT memory_uuid, memory_key, content, memory_type, status, " "confidence, source_type, updated_at " "FROM memory_unit WHERE customer_id=:cid ORDER BY updated_at DESC LIMIT 20" ), {"cid": customer_id})).all() if not rows: print(" (该客户没有任何 memory_unit 行)") for row in rows: print(f" [{row[4]}] {row[1]} = {row[2]!r}") print(f" type={row[3]} conf={row[5]} src={row[6]} at={row[7]}") except Exception as exc: print(f" ERROR {type(exc).__name__}: {exc}") print() print(f" user_facts:") try: rows = (await session.execute(text( "SELECT fact_key, confidence FROM user_facts " "WHERE customer_id=:cid LIMIT 20"), {"cid": customer_id})).all() for row in rows: print(f" {row[0]} (conf={row[1]})") if not rows: print(" (无)") except Exception as exc: print(f" ERROR {type(exc).__name__}: {exc}") print(f" profile_snapshots:") try: rows = (await session.execute(text( "SELECT version, is_current, generated_at FROM profile_snapshots " "WHERE customer_id=:cid ORDER BY id DESC LIMIT 5"), {"cid": customer_id})).all() for row in rows: print(f" v{row[0]} current={row[1]} at={row[2]}") if not rows: print(" (无)") except Exception as exc: print(f" ERROR {type(exc).__name__}: {exc}") if __name__ == "__main__": asyncio.run(main())