159 lines
5.6 KiB
Python
159 lines
5.6 KiB
Python
# 文件位置: app/utils/debug_logger.py
|
||
# 文件名: debug_logger.py
|
||
|
||
"""
|
||
AI调试日志记录器
|
||
记录所有AI模型的输入输出,用于调试和优化
|
||
"""
|
||
import os
|
||
import json
|
||
import logging
|
||
from datetime import datetime
|
||
from typing import Dict, Any, Optional, List
|
||
from config import Config
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class AIDebugLogger:
|
||
"""AI调试日志记录器"""
|
||
|
||
def __init__(self):
|
||
self.debug_dir = os.path.join(Config.DATA_DIR, 'debug')
|
||
os.makedirs(self.debug_dir, exist_ok=True)
|
||
self.current_session_id = None
|
||
self.socketio = None
|
||
|
||
def set_socketio(self, socketio):
|
||
"""设置socketio实例用于实时推送"""
|
||
self.socketio = socketio
|
||
|
||
def set_session(self, session_id: str):
|
||
"""设置当前会话ID"""
|
||
self.current_session_id = session_id
|
||
# 创建会话专属的调试目录
|
||
session_debug_dir = os.path.join(self.debug_dir, session_id)
|
||
os.makedirs(session_debug_dir, exist_ok=True)
|
||
|
||
def log_api_call(self, model: str, agent_type: str, method: str,
|
||
prompt: str, response: str, temperature: float = None,
|
||
max_tokens: int = None, metadata: Dict[str, Any] = None):
|
||
"""记录API调用"""
|
||
timestamp = datetime.now()
|
||
|
||
# 构建日志条目 - 保存完整内容
|
||
log_entry = {
|
||
"timestamp": timestamp.isoformat(),
|
||
"session_id": self.current_session_id,
|
||
"model": model,
|
||
"agent_type": agent_type, # "R1" or "V3"
|
||
"method": method,
|
||
"temperature": temperature,
|
||
"max_tokens": max_tokens,
|
||
"prompt_length": len(prompt),
|
||
"response_length": len(response),
|
||
"prompt": prompt, # 完整保存
|
||
"response": response, # 完整保存
|
||
"metadata": metadata or {}
|
||
}
|
||
|
||
# 保存到文件
|
||
if self.current_session_id:
|
||
self._save_to_file(log_entry)
|
||
|
||
# 通过WebSocket推送(如果可用)
|
||
if self.socketio and self.current_session_id:
|
||
self._emit_debug_log(log_entry)
|
||
|
||
# 记录到标准日志(摘要)
|
||
logger.debug(f"AI Call - {agent_type}/{method}: "
|
||
f"prompt={len(prompt)}chars, response={len(response)}chars")
|
||
|
||
def log_json_parse_error(self, raw_text: str, error: str, fixed_text: Optional[str] = None):
|
||
"""记录JSON解析错误"""
|
||
log_entry = {
|
||
"timestamp": datetime.now().isoformat(),
|
||
"session_id": self.current_session_id,
|
||
"type": "json_parse_error",
|
||
"raw_text": raw_text,
|
||
"error": error,
|
||
"fixed_text": fixed_text,
|
||
"fixed": fixed_text is not None
|
||
}
|
||
|
||
if self.current_session_id:
|
||
self._save_error_log(log_entry)
|
||
|
||
if self.socketio and self.current_session_id:
|
||
self.socketio.emit('parse_error', {
|
||
'session_id': self.current_session_id,
|
||
**log_entry
|
||
}, room=self.current_session_id)
|
||
|
||
def _save_to_file(self, log_entry: Dict[str, Any]):
|
||
"""保存到文件"""
|
||
if not self.current_session_id:
|
||
return
|
||
|
||
# 保存到会话专属文件
|
||
session_log_file = os.path.join(
|
||
self.debug_dir,
|
||
self.current_session_id,
|
||
f"api_calls_{datetime.now().strftime('%Y%m%d')}.jsonl"
|
||
)
|
||
|
||
with open(session_log_file, 'a', encoding='utf-8') as f:
|
||
f.write(json.dumps(log_entry, ensure_ascii=False) + '\n')
|
||
|
||
def _save_error_log(self, log_entry: Dict[str, Any]):
|
||
"""保存错误日志"""
|
||
if not self.current_session_id:
|
||
return
|
||
|
||
error_log_file = os.path.join(
|
||
self.debug_dir,
|
||
self.current_session_id,
|
||
"errors.jsonl"
|
||
)
|
||
|
||
with open(error_log_file, 'a', encoding='utf-8') as f:
|
||
f.write(json.dumps(log_entry, ensure_ascii=False) + '\n')
|
||
|
||
def _emit_debug_log(self, log_entry: Dict[str, Any]):
|
||
"""通过WebSocket发送调试日志"""
|
||
# 发送完整内容,不截断
|
||
self.socketio.emit('ai_debug_log', {
|
||
'session_id': self.current_session_id,
|
||
'log_entry': log_entry # 完整发送
|
||
}, room=self.current_session_id)
|
||
|
||
def get_session_logs(self, session_id: str, log_type: str = 'all') -> List[Dict[str, Any]]:
|
||
"""获取会话的日志"""
|
||
logs = []
|
||
session_debug_dir = os.path.join(self.debug_dir, session_id)
|
||
|
||
if not os.path.exists(session_debug_dir):
|
||
return logs
|
||
|
||
# 根据类型读取不同的日志文件
|
||
if log_type in ['all', 'api_calls']:
|
||
api_files = [f for f in os.listdir(session_debug_dir) if f.startswith('api_calls_')]
|
||
for file_name in api_files:
|
||
file_path = os.path.join(session_debug_dir, file_name)
|
||
with open(file_path, 'r', encoding='utf-8') as f:
|
||
for line in f:
|
||
logs.append(json.loads(line))
|
||
|
||
if log_type in ['all', 'errors']:
|
||
error_file = os.path.join(session_debug_dir, 'errors.jsonl')
|
||
if os.path.exists(error_file):
|
||
with open(error_file, 'r', encoding='utf-8') as f:
|
||
for line in f:
|
||
logs.append(json.loads(line))
|
||
|
||
# 按时间戳排序
|
||
logs.sort(key=lambda x: x.get('timestamp', ''))
|
||
|
||
return logs
|
||
|
||
# 全局调试日志实例
|
||
ai_debug_logger = AIDebugLogger() |