refactor(agent): 移除不必要的要求检查,简化 BaseAgent 初始化逻辑;更新工具注册逻辑,添加获取可运行工具信息的功能;重构聊天 API,优化代码结构和可读性。

This commit is contained in:
Wenjie Zhang 2025-07-24 00:46:15 +08:00
parent 75c52a3855
commit 9f5924b8e6
7 changed files with 59 additions and 1237 deletions

View File

@ -285,10 +285,9 @@ class BaseAgent:
name = "base_agent"
description = "base_agent"
config_schema: Configuration = Configuration
requirements: list[str]
def __init__(self, **kwargs):
self.check_requirements()
pass
@property
def module_name(self) -> str:
@ -306,19 +305,11 @@ class BaseAgent:
"name": self.name if hasattr(self, "name") else "Unknown",
"description": self.description if hasattr(self, "description") else "Unknown",
"config_schema": self.config_schema.to_dict(),
"requirements": self.requirements if hasattr(self, "requirements") else [],
"all_tools": self.all_tools if hasattr(self, "all_tools") else [],
"has_checkpointer": await self.check_checkpointer(),
"met_requirements": self.check_requirements(),
}
def check_requirements(self):
if not hasattr(self, "requirements") or not self.requirements:
return True
for requirement in self.requirements:
if requirement not in os.environ:
raise ValueError(f"没有配置{requirement} 环境变量,请在 src/.env 文件中配置,并重新启动服务")
return True
async def stream_values(self, messages: list[str], config_schema: RunnableConfig = None, **kwargs):
graph = await self.get_graph()

View File

@ -1,5 +1,7 @@
import json
import asyncio
import inspect
import types
from collections.abc import Callable
from typing import Annotated, Any
@ -11,6 +13,10 @@ from src import config, graph_base, knowledge_base
from src.utils import logger
# 工具注册表 - 移到前面以避免NameError
_TOOLS_REGISTRY = {}
class KnowledgeRetrieverModel(BaseModel):
query_text: str = Field(
description=(
@ -19,13 +25,13 @@ class KnowledgeRetrieverModel(BaseModel):
)
)
def get_all_tools():
"""获取所有工具"""
def get_runnable_tools():
"""获取所有可运行的工具(给大模型使用)"""
tools = _TOOLS_REGISTRY.copy()
# 获取所有知识库
for db_Id, retrieve_info in knowledge_base.get_retrievers().items():
name = f"retrieve_{db_Id[:8]}" # Deepseek does not support non-alphanumeric characters in tool names
_id = f"retrieve_{db_Id[:8]}" # Deepseek does not support non-alphanumeric characters in tool names
description = (
f"使用 {retrieve_info['name']} 知识库进行检索。\n"
f"下面是这个知识库的描述:\n{retrieve_info['description']}"
@ -46,15 +52,48 @@ def get_all_tools():
return f"检索失败: {str(e)}"
# 使用 StructuredTool.from_function 创建异步工具
tools[name] = StructuredTool.from_function(
tools[_id] = StructuredTool.from_function(
coroutine=async_retriever_wrapper, # 指定为协程
name=name,
name=_id,
description=description,
args_schema=KnowledgeRetrieverModel
args_schema=KnowledgeRetrieverModel,
metadata=retrieve_info
)
return tools
def get_all_tools_info():
"""获取所有工具的信息(用于前端展示)"""
tools_info = {}
tools = get_runnable_tools()
# 获取注册的工具信息
for _id, tool_obj in tools.items():
metadata = getattr(tool_obj, 'metadata', {}) or {}
info = {
"id": _id,
"name": metadata.get('name', _id),
"description": metadata.get('description') or getattr(tool_obj, 'description', ''),
'metadata': metadata,
"args": []
}
# 获取工具参数信息
if hasattr(tool_obj, 'args_schema') and tool_obj.args_schema:
schema = tool_obj.args_schema.schema()
if 'properties' in schema:
for arg_name, arg_info in schema['properties'].items():
info["args"].append({
"name": arg_name,
"type": arg_info.get('type', ''),
"description": arg_info.get('description', '')
})
tools_info[info['id']] = info
return tools_info
class BaseToolOutput:
"""
LLM 要求 Tool 的输出为 str Tool 用在别处时希望它正常返回结构化数据
@ -103,13 +142,11 @@ def query_knowledge_graph(query: Annotated[str, "The keyword to query knowledge
"""Use this to query knowledge graph."""
return graph_base.query_node(query, hops=2)
_TOOLS_REGISTRY = {
# 更新工具注册表
_TOOLS_REGISTRY.update({
"Calculator": calculator,
"QueryKnowledgeGraph": query_knowledge_graph,
}
})
if config.enable_web_search:
_TOOLS_REGISTRY["WebSearchWithTavily"] = TavilySearch(max_results=10)

View File

@ -9,39 +9,6 @@ import { useUserStore } from '@/stores/user'
// 聊天相关API
export const chatApi = {
/**
* 发送聊天消息
* @param {Object} params - 聊天参数
* @returns {Promise} - 聊天响应流
*/
sendMessage: (params) => {
return fetch('/api/chat/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...useUserStore().getAuthHeaders()
},
body: JSON.stringify(params),
})
},
/**
* 发送可中断的聊天消息
* @param {Object} params - 聊天参数
* @param {AbortSignal} signal - 用于中断请求的信号控制器
* @returns {Promise} - 聊天响应流
*/
sendMessageWithAbort: (params, signal) => {
return fetch('/api/chat/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...useUserStore().getAuthHeaders()
},
body: JSON.stringify(params),
signal // 添加 signal 用于中断请求
})
},
/**
* 发送聊天消息到指定智能体流式响应
@ -105,13 +72,7 @@ export const chatApi = {
* @param {string} provider - 模型提供商
* @returns {Promise} - 模型列表
*/
getProviderModels: (provider) => {
return fetch(`/api/chat/models?model_provider=${provider}`, {
headers: {
...useUserStore().getAuthHeaders()
}
}).then(response => response.json())
},
getProviderModels: (provider) => apiGet(`/api/chat/models?model_provider=${provider}`, {}, true),
/**
* 更新模型提供商的模型列表
@ -119,16 +80,7 @@ export const chatApi = {
* @param {Array} models - 选中的模型列表
* @returns {Promise} - 更新结果
*/
updateProviderModels: (provider, models) => {
return fetch(`/api/chat/models/update?model_provider=${provider}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...useUserStore().getAuthHeaders()
},
body: JSON.stringify(models)
}).then(response => response.json())
}
updateProviderModels: (provider, models) => apiPost(`/api/chat/models/update?model_provider=${provider}`, models, {}, true)
}
// 用户设置API

View File

@ -179,14 +179,14 @@ export const fileApi = {
*/
uploadFile: async (file, dbId = null) => {
checkAdminPermission()
const formData = new FormData()
formData.append('file', file)
const url = dbId
const url = dbId
? `/api/knowledge/files/upload?db_id=${dbId}`
: '/api/knowledge/files/upload'
return apiPost(url, formData, {
headers: {
'Content-Type': 'multipart/form-data'
@ -219,4 +219,3 @@ export const typeApi = {
}
}

View File

@ -177,7 +177,7 @@ export const agentConfigApi = {
*/
setDefaultAgent: async (agentId) => {
checkAdminPermission()
return apiPost('/api/chat/agent/default', { agent_id: agentId }, {}, true)
return apiPost('/api/chat/set_default_agent', { agent_id: agentId }, {}, true)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
<template>
<a-dropdown>
<a-dropdown trigger="click">
<a class="model-select" @click.prevent>
<!-- <BulbOutlined /> -->
<a-tooltip :title="model_name" placement="right">
@ -84,6 +84,7 @@ const handleSelectModel = (provider, name) => {
.model-text {
overflow: hidden;
text-overflow: ellipsis;
color: #000;
}