feat: 重构工具配置为通用模型,增加使用统计
- ToolConfig 模型:支持多种工具类型(搜索、计算器、代码执行等) - ToolUsageLog 模型:记录工具调用日志 - 工具使用统计:调用次数、成功率、错误记录 - 后台管理界面:工具列表+统计展示 - API 重构:/api/v2/tools(替代 search-tools)
This commit is contained in:
@@ -5,7 +5,7 @@ from sqlalchemy.orm import Session
|
||||
from typing import List, Optional, Dict
|
||||
import logging
|
||||
|
||||
from models_v2 import Agent, LLMProvider, ChannelAgentMapping, Channel, SearchToolConfig, init_default_data
|
||||
from models_v2 import Agent, LLMProvider, ChannelAgentMapping, Channel, ToolConfig, ToolUsageLog, init_default_data
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -409,84 +409,169 @@ class ChannelService:
|
||||
}
|
||||
|
||||
|
||||
class SearchToolService:
|
||||
"""搜索工具管理服务"""
|
||||
class ToolService:
|
||||
"""工具管理服务"""
|
||||
|
||||
def __init__(self, db: Session):
|
||||
self.db = db
|
||||
|
||||
def get_all_configs(self) -> List[SearchToolConfig]:
|
||||
"""获取所有搜索工具配置"""
|
||||
return self.db.query(SearchToolConfig).order_by(SearchToolConfig.is_default.desc(), SearchToolConfig.name).all()
|
||||
def get_all_tools(self) -> List[ToolConfig]:
|
||||
"""获取所有工具配置"""
|
||||
return self.db.query(ToolConfig).order_by(ToolConfig.tool_type, ToolConfig.is_default.desc()).all()
|
||||
|
||||
def get_active_configs(self) -> List[SearchToolConfig]:
|
||||
"""获取活跃的搜索工具配置"""
|
||||
return self.db.query(SearchToolConfig).filter(SearchToolConfig.is_active == True).all()
|
||||
def get_tools_by_type(self, tool_type: str) -> List[ToolConfig]:
|
||||
"""获取指定类型的工具"""
|
||||
return self.db.query(ToolConfig).filter(ToolConfig.tool_type == tool_type).all()
|
||||
|
||||
def get_config(self, config_id: int) -> Optional[SearchToolConfig]:
|
||||
"""获取单个配置"""
|
||||
return self.db.query(SearchToolConfig).filter(SearchToolConfig.id == config_id).first()
|
||||
def get_active_tools(self) -> List[ToolConfig]:
|
||||
"""获取活跃的工具"""
|
||||
return self.db.query(ToolConfig).filter(ToolConfig.is_active == True).all()
|
||||
|
||||
def get_default_config(self) -> Optional[SearchToolConfig]:
|
||||
"""获取默认配置"""
|
||||
config = self.db.query(SearchToolConfig).filter(
|
||||
SearchToolConfig.is_default == True,
|
||||
SearchToolConfig.is_active == True
|
||||
def get_tool(self, tool_id: int) -> Optional[ToolConfig]:
|
||||
"""获取单个工具"""
|
||||
return self.db.query(ToolConfig).filter(ToolConfig.id == tool_id).first()
|
||||
|
||||
def get_default_tool(self, tool_type: str) -> Optional[ToolConfig]:
|
||||
"""获取指定类型的默认工具"""
|
||||
tool = self.db.query(ToolConfig).filter(
|
||||
ToolConfig.tool_type == tool_type,
|
||||
ToolConfig.is_default == True,
|
||||
ToolConfig.is_active == True
|
||||
).first()
|
||||
if not config:
|
||||
config = self.db.query(SearchToolConfig).filter(SearchToolConfig.is_active == True).first()
|
||||
return config
|
||||
if not tool:
|
||||
tool = self.db.query(ToolConfig).filter(
|
||||
ToolConfig.tool_type == tool_type,
|
||||
ToolConfig.is_active == True
|
||||
).first()
|
||||
return tool
|
||||
|
||||
def create_config(self, data: Dict) -> SearchToolConfig:
|
||||
"""创建搜索工具配置"""
|
||||
config = SearchToolConfig(
|
||||
def create_tool(self, data: Dict) -> ToolConfig:
|
||||
"""创建工具配置"""
|
||||
tool = ToolConfig(
|
||||
name=data.get('name'),
|
||||
provider=data.get('provider', 'tavily'),
|
||||
api_key=data.get('api_key'),
|
||||
api_base=data.get('api_base'),
|
||||
max_results=data.get('max_results', 5),
|
||||
include_raw_content=data.get('include_raw_content', False),
|
||||
search_depth=data.get('search_depth', 'basic'),
|
||||
tool_type=data.get('tool_type', 'search'),
|
||||
provider=data.get('provider'),
|
||||
config=data.get('config', {}),
|
||||
is_active=data.get('is_active', True),
|
||||
is_default=data.get('is_default', False)
|
||||
)
|
||||
self.db.add(config)
|
||||
self.db.add(tool)
|
||||
self.db.commit()
|
||||
self.db.refresh(config)
|
||||
return config
|
||||
self.db.refresh(tool)
|
||||
return tool
|
||||
|
||||
def update_config(self, config_id: int, data: Dict) -> Optional[SearchToolConfig]:
|
||||
"""更新配置"""
|
||||
config = self.get_config(config_id)
|
||||
if not config:
|
||||
def update_tool(self, tool_id: int, data: Dict) -> Optional[ToolConfig]:
|
||||
"""更新工具配置"""
|
||||
tool = self.get_tool(tool_id)
|
||||
if not tool:
|
||||
return None
|
||||
|
||||
for key, value in data.items():
|
||||
if hasattr(config, key) and value is not None:
|
||||
setattr(config, key, value)
|
||||
if hasattr(tool, key) and value is not None:
|
||||
setattr(tool, key, value)
|
||||
|
||||
self.db.commit()
|
||||
self.db.refresh(config)
|
||||
return config
|
||||
self.db.refresh(tool)
|
||||
return tool
|
||||
|
||||
def delete_config(self, config_id: int) -> bool:
|
||||
"""删除配置"""
|
||||
config = self.get_config(config_id)
|
||||
if not config:
|
||||
def delete_tool(self, tool_id: int) -> bool:
|
||||
"""删除工具配置"""
|
||||
tool = self.get_tool(tool_id)
|
||||
if not tool:
|
||||
return False
|
||||
|
||||
self.db.delete(config)
|
||||
self.db.delete(tool)
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
def set_default_config(self, config_id: int) -> bool:
|
||||
"""设置默认配置"""
|
||||
self.db.query(SearchToolConfig).update({SearchToolConfig.is_default: False})
|
||||
|
||||
config = self.get_config(config_id)
|
||||
if not config:
|
||||
def set_default_tool(self, tool_id: int) -> bool:
|
||||
"""设置默认工具"""
|
||||
tool = self.get_tool(tool_id)
|
||||
if not tool:
|
||||
return False
|
||||
|
||||
config.is_default = True
|
||||
# 清除同类型的其他默认
|
||||
self.db.query(ToolConfig).filter(
|
||||
ToolConfig.tool_type == tool.tool_type
|
||||
).update({ToolConfig.is_default: False})
|
||||
|
||||
tool.is_default = True
|
||||
self.db.commit()
|
||||
return True
|
||||
return True
|
||||
|
||||
def increment_stats(self, tool_id: int, success: bool):
|
||||
"""更新工具调用统计"""
|
||||
tool = self.get_tool(tool_id)
|
||||
if tool:
|
||||
tool.total_calls += 1
|
||||
if success:
|
||||
tool.success_calls += 1
|
||||
else:
|
||||
tool.failed_calls += 1
|
||||
self.db.commit()
|
||||
|
||||
def log_usage(self, data: Dict) -> ToolUsageLog:
|
||||
"""记录工具使用日志"""
|
||||
log = ToolUsageLog(
|
||||
tool_id=data.get('tool_id'),
|
||||
tool_type=data.get('tool_type'),
|
||||
query=data.get('query'),
|
||||
success=data.get('success', True),
|
||||
error_message=data.get('error_message'),
|
||||
result_summary=data.get('result_summary'),
|
||||
conversation_id=data.get('conversation_id'),
|
||||
agent_id=data.get('agent_id'),
|
||||
user_id=data.get('user_id'),
|
||||
duration_ms=data.get('duration_ms')
|
||||
)
|
||||
self.db.add(log)
|
||||
self.db.commit()
|
||||
self.db.refresh(log)
|
||||
return log
|
||||
|
||||
def get_usage_stats(self, days: int = 7) -> Dict:
|
||||
"""获取工具使用统计"""
|
||||
from datetime import timedelta
|
||||
start_date = datetime.utcnow() - timedelta(days=days)
|
||||
|
||||
# 按工具类型统计
|
||||
logs = self.db.query(ToolUsageLog).filter(
|
||||
ToolUsageLog.called_at >= start_date
|
||||
).all()
|
||||
|
||||
stats = {
|
||||
'total_calls': len(logs),
|
||||
'success_rate': sum(1 for l in logs if l.success) / len(logs) * 100 if logs else 0,
|
||||
'by_type': {},
|
||||
'by_tool': {},
|
||||
'recent_errors': []
|
||||
}
|
||||
|
||||
for log in logs:
|
||||
# 按类型
|
||||
if log.tool_type not in stats['by_type']:
|
||||
stats['by_type'][log.tool_type] = {'total': 0, 'success': 0, 'failed': 0}
|
||||
stats['by_type'][log.tool_type]['total'] += 1
|
||||
if log.success:
|
||||
stats['by_type'][log.tool_type]['success'] += 1
|
||||
else:
|
||||
stats['by_type'][log.tool_type]['failed'] += 1
|
||||
|
||||
# 按工具
|
||||
tool = self.get_tool(log.tool_id) if log.tool_id else None
|
||||
tool_name = tool.name if tool else f'Tool#{log.tool_id}'
|
||||
if tool_name not in stats['by_tool']:
|
||||
stats['by_tool'][tool_name] = {'total': 0, 'success': 0}
|
||||
stats['by_tool'][tool_name]['total'] += 1
|
||||
if log.success:
|
||||
stats['by_tool'][tool_name]['success'] += 1
|
||||
|
||||
# 最近错误
|
||||
if not log.success and log.error_message:
|
||||
stats['recent_errors'].append({
|
||||
'tool': tool_name,
|
||||
'error': log.error_message[:100],
|
||||
'time': log.called_at.isoformat()
|
||||
})
|
||||
|
||||
return stats
|
||||
Reference in New Issue
Block a user