From da176ba4c794d826f7fb10f37e539553ac358a3f Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Sat, 22 Aug 2026 19:12:08 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E8=AE=B0=E5=BF=86=E5=AE=A1=E8=AE=A1?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20=E2=80=94=E2=80=94=20AGENTS.md=20APIClient?= =?UTF-8?q?=20=E5=BC=95=E7=94=A8=20+=20=E5=88=A0=E9=99=A4=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E8=B0=83=E8=AF=95=20trace=20=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 通过 --- AGENTS.md | 2 +- utils/conversation_manager/crud_mixin.py | 70 ------------------------ 2 files changed, 1 insertion(+), 71 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ca29e92f..8d07387e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -410,7 +410,7 @@ AI 执行以下流程时,每一步都要向用户说明在做什么: ### 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` 事件,不阻塞前端输入区。 - 子智能体自然的 assistant 输出结束(无 tool_calls)即本轮任务结束,进入 `idle`,上下文保留,不算 failed。 diff --git a/utils/conversation_manager/crud_mixin.py b/utils/conversation_manager/crud_mixin.py index 4ab2765b..afeeeb07 100644 --- a/utils/conversation_manager/crud_mixin.py +++ b/utils/conversation_manager/crud_mixin.py @@ -4,7 +4,6 @@ import json import os import time import tempfile -import threading from datetime import datetime from pathlib import Path from typing import Dict, List, Optional, Any @@ -24,78 +23,10 @@ try: except Exception: perf_log = None -try: - from config import LOGS_DIR as _CONV_SAVE_LOG_DIR -except Exception: - _CONV_SAVE_LOG_DIR = None - # save_conversation 专用哨兵:区分「不更新该字段」与「显式清除为 None」 _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 class ConversationMetadata: """对话元数据""" @@ -266,7 +197,6 @@ class CrudMixin: try: # 确保Token统计数据有效 data = self._validate_token_statistics(data) - _debug_conversation_save_trace(conversation_id, file_path, data) with self._io_lock: self._atomic_write_json(file_path, data) except Exception as e: