chore: 记忆审计修正 —— AGENTS.md APIClient 引用 + 删除保存调试 trace 代码

- AGENTS.md §11.2: DeepSeekClient → APIClient(2026-07-28 改名的遗留引用)
- crud_mixin.py: 删除已过观察期的 _debug_conversation_save_trace(含 shrink/wipe_suspect 判定、调用栈捕获、conversation_save_debug.log 落盘)及配套 _CONV_SAVE_LOG_DIR / threading 导入
- 保留 ConvSaveGuard 断言与 ConvSaveMerge 矫正打印(机制级长期日志)
- 验证:模块加载成功 + test_server_refactor_smoke 6/6 通过
This commit is contained in:
JOJO 2026-08-22 19:12:08 +08:00
parent bcee17f5de
commit da176ba4c7
2 changed files with 1 additions and 71 deletions

View File

@ -410,7 +410,7 @@ AI 执行以下流程时,每一步都要向用户说明在做什么:
### 11.2 子智能体执行机制 ### 11.2 子智能体执行机制
- 子智能体在主进程内 `asyncio.Task`,跑在独立后台事件循环线程里(避开 Flask-SocketIO threading 冲突)。工具调用复用主进程沙箱/容器链路,网络调用走 `utils.api_client.DeepSeekClient`。 - 子智能体在主进程内 `asyncio.Task`,跑在独立后台事件循环线程里(避开 Flask-SocketIO threading 冲突)。工具调用复用主进程沙箱/容器链路,网络调用走 `utils.api_client.APIClient`。
- 子智能体在多智能体模式下: - 子智能体在多智能体模式下:
- `create_sub_agent` 强制 `run_in_background=False`,不触发 `sub_agent_waiting` 事件,不阻塞前端输入区。 - `create_sub_agent` 强制 `run_in_background=False`,不触发 `sub_agent_waiting` 事件,不阻塞前端输入区。
- 子智能体自然的 assistant 输出结束(无 tool_calls即本轮任务结束进入 `idle`,上下文保留,不算 failed。 - 子智能体自然的 assistant 输出结束(无 tool_calls即本轮任务结束进入 `idle`,上下文保留,不算 failed。

View File

@ -4,7 +4,6 @@ import json
import os import os
import time import time
import tempfile import tempfile
import threading
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional, Any from typing import Dict, List, Optional, Any
@ -24,78 +23,10 @@ try:
except Exception: except Exception:
perf_log = None perf_log = None
try:
from config import LOGS_DIR as _CONV_SAVE_LOG_DIR
except Exception:
_CONV_SAVE_LOG_DIR = None
# save_conversation 专用哨兵:区分「不更新该字段」与「显式清除为 None」 # save_conversation 专用哨兵:区分「不更新该字段」与「显式清除为 None」
_KEEP = object() _KEEP = object()
def _debug_conversation_save_trace(conversation_id: str, file_path, data: Dict):
"""[临时调试] 记录每次对话文件写入,重点捕获「非空被空覆盖」的调用栈。
输出: logs/conversation_save_debug.log JSONL
排查完对话消息被清空问题后应移除
"""
try:
import traceback
new_msgs = data.get("messages") if isinstance(data, dict) else None
new_len = len(new_msgs) if isinstance(new_msgs, list) else -1
old_len = None
try:
p = Path(file_path)
if p.exists():
old_data = json.loads(p.read_text(encoding="utf-8"))
old_msgs = old_data.get("messages")
old_len = len(old_msgs) if isinstance(old_msgs, list) else None
except Exception:
old_len = None
wipe_suspect = bool(old_len and new_len == 0)
shrink_suspect = bool(old_len and 0 < new_len < old_len)
# 只保留项目内调用帧,排除标准库/第三方
try:
repo_root = Path(__file__).resolve().parents[2]
except Exception:
repo_root = None
frames = []
for fr in traceback.extract_stack()[:-2]:
fn = str(fr.filename)
if repo_root and not fn.startswith(str(repo_root)):
continue
# Windows 下调用帧文件名用反斜杠,统一归一化再匹配
fn_norm = fn.replace("\\", "/")
if "/.astrion/" in fn_norm or "site-packages" in fn_norm:
continue
rel = fn[len(str(repo_root)) + 1:] if repo_root and fn.startswith(str(repo_root)) else fn
frames.append(f"{rel}:{fr.lineno}:{fr.name}")
record = {
"ts": datetime.now().isoformat(),
"event": "conversation_save",
"conversation_id": conversation_id,
"old_msg_len": old_len,
"new_msg_len": new_len,
"wipe_suspect": wipe_suspect,
"shrink_suspect": shrink_suspect,
"title": (data.get("title") if isinstance(data, dict) else None),
"thread": threading.current_thread().name,
"stack_tail": frames[-14:],
}
if _CONV_SAVE_LOG_DIR:
log_dir = Path(_CONV_SAVE_LOG_DIR)
else:
log_dir = Path(DATA_DIR).parent / "logs"
log_dir.mkdir(parents=True, exist_ok=True)
with open(log_dir / "conversation_save_debug.log", "a", encoding="utf-8") as f:
f.write(json.dumps(record, ensure_ascii=False) + "\n")
if wipe_suspect or shrink_suspect:
kind = "空列表覆盖" if wipe_suspect else f"缩减 {old_len}->{new_len}"
print(f"🚨 [ConvSaveDebug] 对话 {conversation_id} 消息将被{kind}!栈: {frames[-14:]}")
except Exception:
pass
@dataclass @dataclass
class ConversationMetadata: class ConversationMetadata:
"""对话元数据""" """对话元数据"""
@ -266,7 +197,6 @@ class CrudMixin:
try: try:
# 确保Token统计数据有效 # 确保Token统计数据有效
data = self._validate_token_statistics(data) data = self._validate_token_statistics(data)
_debug_conversation_save_trace(conversation_id, file_path, data)
with self._io_lock: with self._io_lock:
self._atomic_write_json(file_path, data) self._atomic_write_json(file_path, data)
except Exception as e: except Exception as e: