refactor: 移除元数据文件,改为从智能体类属性加载元数据,并更新相关引用

This commit is contained in:
Wenjie Zhang 2026-03-20 12:38:42 +08:00
parent 1bc6285fb1
commit 8804c7446c
6 changed files with 23 additions and 68 deletions

View File

@ -1,9 +1,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
import importlib.util
import os import os
import tomllib as tomli
from abc import abstractmethod from abc import abstractmethod
from inspect import isawaitable from inspect import isawaitable
from pathlib import Path from pathlib import Path
@ -34,7 +32,6 @@ class BaseAgent:
self._async_conn = None self._async_conn = None
self.workdir = Path(sys_config.save_dir) / "agents" / self.module_name self.workdir = Path(sys_config.save_dir) / "agents" / self.module_name
self.workdir.mkdir(parents=True, exist_ok=True) self.workdir.mkdir(parents=True, exist_ok=True)
self._metadata_cache = None # Cache for metadata to avoid repeated file reads
@property @property
def module_name(self) -> str: def module_name(self) -> str:
@ -47,7 +44,7 @@ class BaseAgent:
return self.__class__.__name__ return self.__class__.__name__
async def get_info(self, include_configurable_items: bool = True): async def get_info(self, include_configurable_items: bool = True):
# Load metadata from file # metadata 固定在代码中,由各 Agent 的类属性提供
metadata = self.load_metadata() metadata = self.load_metadata()
configurable_items = {} configurable_items = {}
if include_configurable_items: if include_configurable_items:
@ -56,11 +53,11 @@ class BaseAgent:
# Merge metadata with class attributes, metadata takes precedence # Merge metadata with class attributes, metadata takes precedence
return { return {
"id": self.id, "id": self.id,
"name": metadata.get("name", getattr(self, "name", "Unknown")), "name": getattr(self, "name", "Unknown"),
"description": metadata.get("description", getattr(self, "description", "Unknown")), "description": getattr(self, "description", "Unknown"),
"examples": metadata.get("examples", []), "metadata": metadata,
# "examples": metadata.get("examples", []),
"configurable_items": configurable_items, "configurable_items": configurable_items,
"has_checkpointer": await self.check_checkpointer(),
"capabilities": getattr(self, "capabilities", []), # 智能体能力列表 "capabilities": getattr(self, "capabilities", []), # 智能体能力列表
} }
@ -116,7 +113,6 @@ class BaseAgent:
async def check_checkpointer(self): async def check_checkpointer(self):
app = await self.get_graph() app = await self.get_graph()
if not hasattr(app, "checkpointer") or app.checkpointer is None: if not hasattr(app, "checkpointer") or app.checkpointer is None:
logger.warning(f"智能体 {self.name} 的 Graph 未配置 checkpointer无法获取历史记录")
return False return False
return True return True
@ -253,40 +249,9 @@ class BaseAgent:
return AsyncSqliteSaver(await self.get_async_conn()) return AsyncSqliteSaver(await self.get_async_conn())
def load_metadata(self) -> dict: def load_metadata(self) -> dict:
"""Load metadata from metadata.toml file in the agent's source directory.""" """Load metadata from agent class attribute."""
if self._metadata_cache is not None: metadata = getattr(self, "metadata", {})
return self._metadata_cache if isinstance(metadata, dict):
return metadata
# Try to find metadata.toml in the agent's source directory logger.warning(f"Agent {self.module_name} metadata is not a dict, fallback to empty metadata")
try: return {}
# Get the agent's source file directory
agent_module = self.__class__.__module__
# Use importlib to get the module's file path
spec = importlib.util.find_spec(agent_module)
if spec and spec.origin:
agent_file = Path(spec.origin)
agent_dir = agent_file.parent
else:
# Fallback: construct path from module name
module_path = agent_module.replace(".", "/")
agent_file = Path(f"package/yuxi/{module_path}.py")
agent_dir = agent_file.parent
metadata_file = agent_dir / "metadata.toml"
if metadata_file.exists():
with open(metadata_file, "rb") as f:
metadata = tomli.load(f)
self._metadata_cache = metadata
logger.debug(f"Loaded metadata from {metadata_file}")
return metadata
else:
logger.debug(f"No metadata.toml found for {self.module_name} at {metadata_file}")
self._metadata_cache = {}
return {}
except Exception as e:
logger.error(f"Error loading metadata for {self.module_name}: {e}")
self._metadata_cache = {}
return {}

View File

@ -29,6 +29,15 @@ class ChatbotAgent(BaseAgent):
name = "智能体助手" name = "智能体助手"
description = "基础的对话机器人,可以回答问题,可在配置中启用需要的工具。" description = "基础的对话机器人,可以回答问题,可在配置中启用需要的工具。"
capabilities = ["file_upload", "files", "todo"] # 支持文件上传功能 capabilities = ["file_upload", "files", "todo"] # 支持文件上传功能
metadata = {
"examples": [
"你好,请介绍一下你自己",
"帮我写一封商务邮件",
"解释一下什么是机器学习",
"推荐几本好书",
"如何提高工作效率?"
]
}
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)

View File

@ -1,9 +0,0 @@
name = "智能聊天助手"
description = "基础的AI对话助手支持工具调用MCP并具备丰富的知识库。"
examples = [
"你好,请介绍一下你自己",
"帮我写一封商务邮件",
"解释一下什么是机器学习",
"推荐几本好书",
"如何提高工作效率?"
]

View File

@ -159,8 +159,7 @@ async def get_agent(current_user: User = Depends(get_required_user)):
"id": agent_info["id"], "id": agent_info["id"],
"name": agent_info.get("name", "Unknown"), "name": agent_info.get("name", "Unknown"),
"description": agent_info.get("description", ""), "description": agent_info.get("description", ""),
"examples": agent_info.get("examples", []), "metadata": agent_info.get("metadata", {}),
"has_checkpointer": agent_info.get("has_checkpointer", False),
"capabilities": agent_info.get("capabilities", []), # 智能体能力列表 "capabilities": agent_info.get("capabilities", []), # 智能体能力列表
} }
for agent_info in agents_info for agent_info in agents_info
@ -184,9 +183,8 @@ async def get_single_agent(agent_id: str, current_user: User = Depends(get_requi
"id": agent_info["id"], "id": agent_info["id"],
"name": agent_info.get("name", "Unknown"), "name": agent_info.get("name", "Unknown"),
"description": agent_info.get("description", ""), "description": agent_info.get("description", ""),
"examples": agent_info.get("examples", []), "metadata": agent_info.get("metadata", {}),
"configurable_items": agent_info.get("configurable_items", []), "configurable_items": agent_info.get("configurable_items", []),
"has_checkpointer": agent_info.get("has_checkpointer", False),
"capabilities": agent_info.get("capabilities", []), "capabilities": agent_info.get("capabilities", []),
} }

View File

@ -286,7 +286,7 @@ const exampleQuestions = computed(() => {
let examples = [] let examples = []
if (agentId && agents.value && agents.value.length > 0) { if (agentId && agents.value && agents.value.length > 0) {
const agent = agents.value.find((a) => a.id === agentId) const agent = agents.value.find((a) => a.id === agentId)
examples = agent ? agent.examples || [] : [] examples = agent ? agent.metadata?.examples || [] : []
} }
return examples.map((text, index) => ({ return examples.map((text, index) => ({
id: index + 1, id: index + 1,

View File

@ -71,14 +71,6 @@
show-icon show-icon
class="config-alert" class="config-alert"
/> />
<a-alert
v-if="!selectedAgent.has_checkpointer"
type="error"
message="该智能体没有配置 Checkpointer功能无法正常使用"
show-icon
class="config-alert"
/>
<!-- 统一显示所有配置项 --> <!-- 统一显示所有配置项 -->
<template v-for="(value, key) in configurableItems" :key="key"> <template v-for="(value, key) in configurableItems" :key="key">
<a-form-item <a-form-item