refactor(agent): 将智能体相关的名称参数更改为ID,优化智能体管理逻辑

This commit is contained in:
Wenjie Zhang 2025-07-22 17:30:00 +08:00
parent 9d9101a3e5
commit 9d58ebc13c
7 changed files with 69 additions and 58 deletions

View File

@ -36,7 +36,7 @@ async def get_default_agent(current_user: User = Depends(get_required_user)):
if not default_agent_id: if not default_agent_id:
agents = await agent_manager.get_agents_info() agents = await agent_manager.get_agents_info()
if agents: if agents:
default_agent_id = agents[0].get("name", "") default_agent_id = agents[0].get("id", "")
return {"default_agent_id": default_agent_id} return {"default_agent_id": default_agent_id}
except Exception as e: except Exception as e:
@ -49,7 +49,7 @@ async def set_default_agent(agent_id: str = Body(..., embed=True), current_user
try: try:
# 验证智能体是否存在 # 验证智能体是否存在
agents = await agent_manager.get_agents_info() agents = await agent_manager.get_agents_info()
agent_ids = [agent.get("name", "") for agent in agents] agent_ids = [agent.get("id", "") for agent in agents]
if agent_id not in agent_ids: if agent_id not in agent_ids:
raise HTTPException(status_code=404, detail=f"智能体 {agent_id} 不存在") raise HTTPException(status_code=404, detail=f"智能体 {agent_id} 不存在")
@ -96,8 +96,8 @@ async def get_agent(current_user: User = Depends(get_required_user)):
# logger.debug(f"agents: {agents}") # logger.debug(f"agents: {agents}")
return {"agents": agents} return {"agents": agents}
@chat.post("/agent/{agent_name}") @chat.post("/agent/{agent_id}")
async def chat_agent(agent_name: str, async def chat_agent(agent_id: str,
query: str = Body(...), query: str = Body(...),
config: dict = Body({}), config: dict = Body({}),
meta: dict = Body({}), meta: dict = Body({}),
@ -106,8 +106,8 @@ async def chat_agent(agent_name: str,
meta.update({ meta.update({
"query": query, "query": query,
"agent_name": agent_name, "agent_id": agent_id,
"server_model_name": config.get("model", agent_name), "server_model_name": config.get("model", agent_id),
"thread_id": config.get("thread_id"), "thread_id": config.get("thread_id"),
"user_id": current_user.id "user_id": current_user.id
}) })
@ -127,10 +127,10 @@ async def chat_agent(agent_name: str,
yield make_chunk(status="init", meta=meta, msg=HumanMessage(content=query).model_dump()) yield make_chunk(status="init", meta=meta, msg=HumanMessage(content=query).model_dump())
try: try:
agent = agent_manager.get_agent(agent_name) agent = agent_manager.get_agent(agent_id)
except Exception as e: except Exception as e:
logger.error(f"Error getting agent {agent_name}: {e}, {traceback.format_exc()}") logger.error(f"Error getting agent {agent_id}: {e}, {traceback.format_exc()}")
yield make_chunk(message=f"Error getting agent {agent_name}: {e}", status="error") yield make_chunk(message=f"Error getting agent {agent_id}: {e}", status="error")
return return
messages = [{"role": "user", "content": query}] messages = [{"role": "user", "content": query}]
@ -185,25 +185,25 @@ async def get_tools(current_user: User = Depends(get_admin_user)):
"""获取所有可用工具(需要登录)""" """获取所有可用工具(需要登录)"""
return {"tools": list(get_all_tools().keys())} return {"tools": list(get_all_tools().keys())}
@chat.post("/agent/{agent_name}/config") @chat.post("/agent/{agent_id}/config")
async def save_agent_config( async def save_agent_config(
agent_name: str, agent_id: str,
config: dict = Body(...), config: dict = Body(...),
current_user: User = Depends(get_admin_user) current_user: User = Depends(get_admin_user)
): ):
"""保存智能体配置到YAML文件需要管理员权限""" """保存智能体配置到YAML文件需要管理员权限"""
try: try:
# 获取Agent实例和配置类 # 获取Agent实例和配置类
agent = agent_manager.get_agent(agent_name) agent = agent_manager.get_agent(agent_id)
if not agent: if not agent:
raise HTTPException(status_code=404, detail=f"智能体 {agent_name} 不存在") raise HTTPException(status_code=404, detail=f"智能体 {agent_id} 不存在")
# 使用配置类的save_to_file方法保存配置 # 使用配置类的save_to_file方法保存配置
config_cls = agent.config_schema config_cls = agent.config_schema
result = config_cls.save_to_file(config, agent_name) result = config_cls.save_to_file(config, agent.module_name)
if result: if result:
return {"success": True, "message": f"智能体 {agent_name} 配置已保存"} return {"success": True, "message": f"智能体 {agent.name} 配置已保存"}
else: else:
raise HTTPException(status_code=500, detail="保存智能体配置失败") raise HTTPException(status_code=500, detail="保存智能体配置失败")
@ -211,18 +211,18 @@ async def save_agent_config(
logger.error(f"保存智能体配置出错: {e}, {traceback.format_exc()}") logger.error(f"保存智能体配置出错: {e}, {traceback.format_exc()}")
raise HTTPException(status_code=500, detail=f"保存智能体配置出错: {str(e)}") raise HTTPException(status_code=500, detail=f"保存智能体配置出错: {str(e)}")
@chat.get("/agent/{agent_name}/history") @chat.get("/agent/{agent_id}/history")
async def get_agent_history( async def get_agent_history(
agent_name: str, agent_id: str,
thread_id: str, thread_id: str,
current_user: User = Depends(get_required_user) current_user: User = Depends(get_required_user)
): ):
"""获取智能体历史消息(需要登录)""" """获取智能体历史消息(需要登录)"""
try: try:
# 获取Agent实例和配置类 # 获取Agent实例和配置类
agent = agent_manager.get_agent(agent_name) agent = agent_manager.get_agent(agent_id)
if not agent: if not agent:
raise HTTPException(status_code=404, detail=f"智能体 {agent_name} 不存在") raise HTTPException(status_code=404, detail=f"智能体 {agent_id} 不存在")
# 获取历史消息 # 获取历史消息
history = await agent.get_history(user_id=str(current_user.id), thread_id=thread_id) history = await agent.get_history(user_id=str(current_user.id), thread_id=thread_id)
@ -232,18 +232,18 @@ async def get_agent_history(
logger.error(f"获取智能体历史消息出错: {e}, {traceback.format_exc()}") logger.error(f"获取智能体历史消息出错: {e}, {traceback.format_exc()}")
raise HTTPException(status_code=500, detail=f"获取智能体历史消息出错: {str(e)}") raise HTTPException(status_code=500, detail=f"获取智能体历史消息出错: {str(e)}")
@chat.get("/agent/{agent_name}/config") @chat.get("/agent/{agent_id}/config")
async def get_agent_config( async def get_agent_config(
agent_name: str, agent_id: str,
current_user: User = Depends(get_required_user) current_user: User = Depends(get_required_user)
): ):
"""从YAML文件加载智能体配置需要登录""" """从YAML文件加载智能体配置需要登录"""
try: try:
# 检查智能体是否存在 # 检查智能体是否存在
if not (agent := agent_manager.get_agent(agent_name)): if not (agent := agent_manager.get_agent(agent_id)):
raise HTTPException(status_code=404, detail=f"智能体 {agent_name} 不存在") raise HTTPException(status_code=404, detail=f"智能体 {agent_id} 不存在")
config = agent.config_schema.from_runnable_config(config={}, agent_name=agent_name) config = agent.config_schema.from_runnable_config(config={}, module_name=agent.module_name)
return {"success": True, "config": config} return {"success": True, "config": config}
except Exception as e: except Exception as e:

View File

@ -10,19 +10,19 @@ class AgentManager:
self._instances = {} # 存储已创建的 agent 实例 self._instances = {} # 存储已创建的 agent 实例
def register_agent(self, agent_class): def register_agent(self, agent_class):
self._classes[agent_class.name] = agent_class self._classes[agent_class.__name__] = agent_class
def init_all_agents(self): def init_all_agents(self):
for agent_class in self._classes.values(): for agent_id in self._classes.keys():
self.get_agent(agent_class.name) self.get_agent(agent_id)
def get_agent(self, agent_name, **kwargs): def get_agent(self, agent_id, **kwargs):
# 检查是否已经创建了该 agent 的实例 # 检查是否已经创建了该 agent 的实例
if agent_name not in self._instances: if agent_id not in self._instances:
agent_class = self._classes[agent_name] agent_class = self._classes[agent_id]
self._instances[agent_name] = agent_class() self._instances[agent_id] = agent_class()
return self._instances[agent_name] return self._instances[agent_id]
def get_agents(self): def get_agents(self):
return list(self._instances.values()) return list(self._instances.values())

View File

@ -26,7 +26,7 @@ class ChatbotAgent(BaseAgent):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.graph = None self.graph = None
self.workdir = Path(sys_config.save_dir) / "agents" / self.name self.workdir = Path(sys_config.save_dir) / "agents" / self.id
self.workdir.mkdir(parents=True, exist_ok=True) self.workdir.mkdir(parents=True, exist_ok=True)
def _get_tools(self, tools: list[str]): def _get_tools(self, tools: list[str]):
@ -47,7 +47,7 @@ class ChatbotAgent(BaseAgent):
async def llm_call(self, state: State, config: RunnableConfig = None) -> dict[str, Any]: async def llm_call(self, state: State, config: RunnableConfig = None) -> dict[str, Any]:
"""调用 llm 模型 - 异步版本以支持异步工具""" """调用 llm 模型 - 异步版本以支持异步工具"""
conf = self.config_schema.from_runnable_config(config, agent_name=self.name) conf = self.config_schema.from_runnable_config(config, module_name=self.module_name)
system_prompt = f"{conf.system_prompt} Now is {get_cur_time_with_utc()}" system_prompt = f"{conf.system_prompt} Now is {get_cur_time_with_utc()}"
model = load_chat_model(conf.model) model = load_chat_model(conf.model)

View File

@ -51,13 +51,13 @@ class Configuration(dict):
@classmethod @classmethod
def from_runnable_config( def from_runnable_config(
cls, config: RunnableConfig | None = None, agent_name: str | None = None cls, config: RunnableConfig | None = None, module_name: str | None = None
) -> Configuration: ) -> Configuration:
"""Create a Configuration instance from a RunnableConfig object. """Create a Configuration instance from a RunnableConfig object.
Args: Args:
config: RunnableConfig object with highest priority config: RunnableConfig object with highest priority
agent_name: Name of the agent to load config file for module_name: Name of the agent to load config file for
Returns: Returns:
Configuration instance with merged config values Configuration instance with merged config values
@ -68,8 +68,8 @@ class Configuration(dict):
# 尝试加载文件配置(中等优先级) # 尝试加载文件配置(中等优先级)
file_config = {} file_config = {}
if agent_name: if module_name:
file_config = cls.from_file(agent_name) file_config = cls.from_file(module_name)
# 获取运行时配置(最高优先级) # 获取运行时配置(最高优先级)
configurable = (config.get("configurable") or {}) if config else {} configurable = (config.get("configurable") or {}) if config else {}
@ -94,39 +94,39 @@ class Configuration(dict):
return cls(**merged_config) return cls(**merged_config)
@classmethod @classmethod
def from_file(cls, agent_name: str) -> Configuration: def from_file(cls, module_name: str) -> Configuration:
"""从文件加载配置""" """从文件加载配置"""
config_file_path = Path(f"src/agents/{agent_name}/config.private.yaml") config_file_path = Path(f"src/agents/{module_name}/config.private.yaml")
file_config = {} file_config = {}
if os.path.exists(config_file_path): if os.path.exists(config_file_path):
try: try:
with open(config_file_path, encoding='utf-8') as f: with open(config_file_path, encoding='utf-8') as f:
file_config = yaml.safe_load(f) or {} file_config = yaml.safe_load(f) or {}
# logger.info(f"从文件加载智能体 {agent_name} 配置: {file_config}") # logger.info(f"从文件加载智能体 {module_name} 配置: {file_config}")
except Exception as e: except Exception as e:
logger.error(f"加载智能体配置文件出错: {e}") logger.error(f"加载智能体配置文件出错: {e}")
return file_config return file_config
@classmethod @classmethod
def save_to_file(cls, config: dict, agent_name: str) -> bool: def save_to_file(cls, config: dict, module_name: str) -> bool:
"""Save configuration to a YAML file """Save configuration to a YAML file
Args: Args:
config: Configuration dictionary to save config: Configuration dictionary to save
agent_name: Name of the agent to save config for module_name: Name of the agent to save config for
Returns: Returns:
True if saving was successful, False otherwise True if saving was successful, False otherwise
""" """
try: try:
config_file_path = Path(f"src/agents/{agent_name}/config.private.yaml") config_file_path = Path(f"src/agents/{module_name}/config.private.yaml")
# 确保目录存在 # 确保目录存在
os.makedirs(os.path.dirname(config_file_path), exist_ok=True) os.makedirs(os.path.dirname(config_file_path), exist_ok=True)
with open(config_file_path, 'w', encoding='utf-8') as f: with open(config_file_path, 'w', encoding='utf-8') as f:
yaml.dump(config, f, indent=2, allow_unicode=True) yaml.dump(config, f, indent=2, allow_unicode=True)
# logger.info(f"智能体 {agent_name} 配置已保存到 {config_file_path}") # logger.info(f"智能体 {module_name} 配置已保存到 {config_file_path}")
return True return True
except Exception as e: except Exception as e:
logger.error(f"保存智能体配置文件出错: {e}") logger.error(f"保存智能体配置文件出错: {e}")
@ -183,7 +183,7 @@ class BaseModelConfiguration(BaseModel):
def from_runnable_config( def from_runnable_config(
cls, cls,
config: Optional["RunnableConfig"] = None, config: Optional["RunnableConfig"] = None,
agent_name: Optional[str] = None, module_name: Optional[str] = None,
) -> "BaseModelConfiguration": ) -> "BaseModelConfiguration":
""" """
RunnableConfig YAML 文件中构建 Configuration 实例 RunnableConfig YAML 文件中构建 Configuration 实例
@ -193,7 +193,7 @@ class BaseModelConfiguration(BaseModel):
default_values = default_instance.dict() default_values = default_instance.dict()
# 文件配置 # 文件配置
file_config = cls.from_file(agent_name) if agent_name else {} file_config = cls.from_file(module_name) if module_name else {}
# 运行时配置(最高优先级) # 运行时配置(最高优先级)
runtime_config = config.get("configurable") if config else {} runtime_config = config.get("configurable") if config else {}
@ -207,11 +207,11 @@ class BaseModelConfiguration(BaseModel):
return cls(**merged_config) return cls(**merged_config)
@classmethod @classmethod
def from_file(cls, agent_name: str) -> dict[str, Any]: def from_file(cls, module_name: str) -> dict[str, Any]:
""" """
YAML 文件加载配置 YAML 文件加载配置
""" """
config_file_path = Path(f"src/agents/{agent_name}/config.private.yaml") config_file_path = Path(f"src/agents/{module_name}/config.private.yaml")
if os.path.exists(config_file_path): if os.path.exists(config_file_path):
try: try:
with open(config_file_path, encoding="utf-8") as f: with open(config_file_path, encoding="utf-8") as f:
@ -221,12 +221,12 @@ class BaseModelConfiguration(BaseModel):
return {} return {}
@classmethod @classmethod
def save_to_file(cls, config: dict, agent_name: str) -> bool: def save_to_file(cls, config: dict, module_name: str) -> bool:
""" """
保存配置到 YAML 文件 保存配置到 YAML 文件
""" """
try: try:
config_file_path = Path(f"src/agents/{agent_name}/config.private.yaml") config_file_path = Path(f"src/agents/{module_name}/config.private.yaml")
os.makedirs(config_file_path.parent, exist_ok=True) os.makedirs(config_file_path.parent, exist_ok=True)
with open(config_file_path, "w", encoding="utf-8") as f: with open(config_file_path, "w", encoding="utf-8") as f:
yaml.dump(config, f, indent=2, allow_unicode=True) yaml.dump(config, f, indent=2, allow_unicode=True)
@ -290,8 +290,19 @@ class BaseAgent:
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.check_requirements() self.check_requirements()
@property
def module_name(self) -> str:
"""Get the module name of the agent class."""
return self.__class__.__module__.split('.')[-2]
@property
def id(self) -> str:
"""Get the agent's class name."""
return self.__class__.__name__
async def get_info(self): async def get_info(self):
return { return {
"id": self.id,
"name": self.name if hasattr(self, "name") else "Unknown", "name": self.name if hasattr(self, "name") else "Unknown",
"description": self.description if hasattr(self, "description") else "Unknown", "description": self.description if hasattr(self, "description") else "Unknown",
"config_schema": self.config_schema.to_dict(), "config_schema": self.config_schema.to_dict(),

View File

@ -92,7 +92,7 @@ export const chatApi = {
* @param {string} threadId - 会话ID * @param {string} threadId - 会话ID
* @returns {Promise} - 历史消息 * @returns {Promise} - 历史消息
*/ */
getAgentHistory: (agentName, threadId) => apiGet(`/api/chat/agent/${agentName}/history?thread_id=${threadId}`, {}, true), getAgentHistory: (agentId, threadId) => apiGet(`/api/chat/agent/${agentId}/history?thread_id=${threadId}`, {}, true),
/** /**
* 获取可用工具列表 * 获取可用工具列表

View File

@ -1014,7 +1014,7 @@ const fetchAgents = async () => {
const data = await chatApi.getAgents(); const data = await chatApi.getAgents();
// //
agents.value = data.agents.reduce((acc, agent) => { agents.value = data.agents.reduce((acc, agent) => {
acc[agent.name] = agent; acc[agent.id] = agent;
return acc; return acc;
}, {}); }, {});
console.log("agents", agents.value); console.log("agents", agents.value);

View File

@ -15,13 +15,13 @@
@change="selectAgent" @change="selectAgent"
> >
<a-select-option <a-select-option
v-for="(agent, name) in agents" v-for="(agent, id) in agents"
:key="name" :key="id"
:value="name" :value="id"
> >
<div class="agent-option"> <div class="agent-option">
<div class="agent-option-content"> <div class="agent-option-content">
<p class="agent-option-name">{{ agent.name }} <StarFilled v-if="name === defaultAgentId" class="default-icon" /></p> <p class="agent-option-name">{{ agent.name }} <StarFilled v-if="id === defaultAgentId" class="default-icon" /></p>
<p class="agent-option-description">{{ agent.description }}</p> <p class="agent-option-description">{{ agent.description }}</p>
</div> </div>
@ -338,7 +338,7 @@ const fetchAgents = async () => {
const data = await chatApi.getAgents(); const data = await chatApi.getAgents();
// //
agents.value = data.agents.reduce((acc, agent) => { agents.value = data.agents.reduce((acc, agent) => {
acc[agent.name] = agent; acc[agent.id] = agent;
return acc; return acc;
}, {}); }, {});
// console.log("agents", agents.value); // console.log("agents", agents.value);