fix: 修复35个安全与功能缺陷,补全知识进化/数字孪生/行为采集模块

## 安全修复 (12项)
- Webhook接口添加全局Token认证,过滤敏感请求头
- 修复JWT Base64 padding公式,防止签名验证绕过
- 数据库密码/飞书Token从源码移除,改为环境变量
- 工作流引擎添加路径遍历防护 (_resolve_safe_path)
- eval()添加模板长度上限检查
- 审批API添加认证依赖
- 前端v-html增强XSS转义,console.log仅开发模式输出
- 500错误不再暴露内部异常详情

## Agent运行时修复 (7项)
- 删除_inject_knowledge_context中未定义db变量的finally块
- 工具执行添加try/except保护,异常不崩溃Agent
- LLM重试计入budget计数器
- self_review异常时passed=False
- max_iterations截断标记success=False
- 工具参数JSON解析失败时记录警告日志
- run()开始时重置_llm_invocations计数器

## 配置与基础设施
- DEBUG默认False,SQL_ECHO独立配置项
- init_db()补全13个缺失模型导入
- 新增WEBHOOK_AUTH_TOKEN/SQL_ECHO配置项
- 新增.env.example模板文件

## 前端修复 (12项)
- 登录改用URLSearchParams替代FormData
- 401拦截器通过Pinia store统一清理状态
- SSE流超时从60s延长至300s
- final/error事件时清除streamTimeout
- localStorage聊天记录添加24h TTL
- safeParseArgCount替代模板中裸JSON.parse
- fetchUser 401时同时清除user对象

## 新增模块
- 知识进化: knowledge_extractor/retriever/tasks
- 数字孪生: shadow_executor/comparison模型
- 行为采集: behavior_middleware/collector/fingerprint_engine
- 代码审查: code_review_agent/document_review_agent
- 反馈学习: feedback_learner
- 瓶颈检测/优化引擎/成本估算/需求估算
- 速率限制器 (rate_limiter)
- Alembic迁移 015-020

## 文档
- 商业化落地计划
- 8篇docs文档 (架构/API/部署/开发/贡献等)
- Docker Compose生产配置

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
renjianbo
2026-05-10 19:50:20 +08:00
parent f79dc0b3c6
commit ab1589921a
77 changed files with 9442 additions and 265 deletions

View File

@@ -1,29 +1,25 @@
"""
执行日志服务
- ExecutionLogger: 工作流执行的日志记录器per-execution
- ExecutionLoggerService: Agent 执行的日志服务(全局单例)
"""
from typing import Dict, Any, Optional
from typing import Dict, Any, Optional, List
from datetime import datetime
from sqlalchemy.orm import Session
from app.models.execution_log import ExecutionLog
import asyncio
import logging
logger = logging.getLogger(__name__)
class ExecutionLogger:
"""执行日志记录器"""
"""执行日志记录器(工作流/编排执行上下文)"""
def __init__(self, execution_id: str, db: Session):
"""
初始化日志记录器
Args:
execution_id: 执行ID
db: 数据库会话
"""
self.execution_id = execution_id
self.db = db
def log(
self,
level: str,
@@ -33,17 +29,6 @@ class ExecutionLogger:
data: Optional[Dict[str, Any]] = None,
duration: Optional[int] = None
):
"""
记录日志
Args:
level: 日志级别 (INFO/WARN/ERROR/DEBUG)
message: 日志消息
node_id: 节点ID可选
node_type: 节点类型(可选)
data: 附加数据(可选)
duration: 执行耗时(毫秒,可选)
"""
try:
log_entry = ExecutionLog(
execution_id=self.execution_id,
@@ -57,61 +42,129 @@ class ExecutionLogger:
)
self.db.add(log_entry)
self.db.commit()
# 同时输出到标准日志
log_method = getattr(logger, level.lower(), logger.info)
log_msg = f"[执行 {self.execution_id}]"
if node_id:
log_msg += f" [节点 {node_id}]"
log_msg += f" {message}"
log_method(log_msg)
except Exception as e:
# 如果数据库记录失败,至少输出到标准日志
logger.error(f"记录执行日志失败: {str(e)}")
logger.error(f"[执行 {self.execution_id}] {message}")
def info(self, message: str, **kwargs):
"""记录INFO级别日志"""
self.log("INFO", message, **kwargs)
def warn(self, message: str, **kwargs):
"""记录WARN级别日志"""
self.log("WARN", message, **kwargs)
def error(self, message: str, **kwargs):
"""记录ERROR级别日志"""
self.log("ERROR", message, **kwargs)
def debug(self, message: str, **kwargs):
"""记录DEBUG级别日志"""
self.log("DEBUG", message, **kwargs)
def log_node_start(self, node_id: str, node_type: str, input_data: Optional[Dict[str, Any]] = None):
"""记录节点开始执行"""
self.info(
f"节点 {node_id} ({node_type}) 开始执行",
node_id=node_id,
node_type=node_type,
node_id=node_id, node_type=node_type,
data={"input": input_data} if input_data else None
)
def log_node_complete(self, node_id: str, node_type: str, output_data: Optional[Dict[str, Any]] = None, duration: Optional[int] = None):
"""记录节点执行完成"""
self.info(
f"节点 {node_id} ({node_type}) 执行完成",
node_id=node_id,
node_type=node_type,
node_id=node_id, node_type=node_type,
data={"output": output_data} if output_data else None,
duration=duration
)
def log_node_error(self, node_id: str, node_type: str, error: Exception, duration: Optional[int] = None):
"""记录节点执行错误"""
self.error(
f"节点 {node_id} ({node_type}) 执行失败: {str(error)}",
node_id=node_id,
node_type=node_type,
node_id=node_id, node_type=node_type,
data={"error": str(error), "error_type": type(error).__name__},
duration=duration
)
class ExecutionLoggerService:
"""Agent 执行日志服务(全局单例,记录 Agent 每次执行的完整信息)"""
def log_execution_sync(
self,
*,
agent_id: Optional[str] = None,
agent_name: Optional[str] = None,
goal_id: Optional[str] = None,
task_id: Optional[str] = None,
user_id: Optional[str] = None,
session_id: Optional[str] = None,
input_text: Optional[str] = None,
output_text: Optional[str] = None,
output_truncated: bool = False,
success: bool = True,
error_message: Optional[str] = None,
latency_ms: int = 0,
iterations_used: int = 0,
tool_calls_made: int = 0,
tool_chain: Optional[List[Dict[str, Any]]] = None,
llm_calls: Optional[List[Dict[str, Any]]] = None,
steps: Optional[List[Dict[str, Any]]] = None,
model: Optional[str] = None,
provider: Optional[str] = None,
) -> Optional[str]:
"""同步写入执行日志,返回日志 ID。"""
from app.core.database import SessionLocal
from app.models.agent_execution_log import AgentExecutionLog
db: Optional[Session] = None
try:
db = SessionLocal()
entry = AgentExecutionLog(
agent_id=agent_id, agent_name=agent_name,
goal_id=goal_id, task_id=task_id,
user_id=user_id, session_id=session_id,
input_text=input_text[:5000] if input_text else None,
output_text=output_text[:10000] if output_text else None,
output_truncated=output_truncated,
success=success,
error_message=error_message[:2000] if error_message else None,
latency_ms=latency_ms,
iterations_used=iterations_used,
tool_calls_made=tool_calls_made,
tool_chain=tool_chain, llm_calls=llm_calls, steps=steps,
model=model, provider=provider,
)
db.add(entry)
db.commit()
db.refresh(entry)
return str(entry.id)
except Exception as e:
logger.warning("写入 Agent 执行日志失败: %s", e)
if db:
try:
db.rollback()
except Exception:
pass
return None
finally:
if db:
try:
db.close()
except Exception:
pass
async def log_execution(self, **kwargs) -> Optional[str]:
"""异步写入执行日志(线程池)。"""
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, lambda: self.log_execution_sync(**kwargs))
def log_execution_fire_and_forget(self, **kwargs):
"""Fire-and-forget 写入。"""
try:
asyncio.ensure_future(self.log_execution(**kwargs))
except Exception:
pass
# 全局单例
execution_logger = ExecutionLoggerService()