feat: 增加工具配置说明展示
This commit is contained in:
parent
fa5a2fa1ae
commit
d282cab00c
@ -20,6 +20,15 @@ from yuxi.utils.question_utils import normalize_questions
|
|||||||
# Lazy initialization for TavilySearch (only when API key is available)
|
# Lazy initialization for TavilySearch (only when API key is available)
|
||||||
_tavily_search_instance = None
|
_tavily_search_instance = None
|
||||||
|
|
||||||
|
QWEN_IMAGE_CONFIG_GUIDE = """
|
||||||
|
使用前需要先配置硅基流动的图片生成访问凭证。
|
||||||
|
|
||||||
|
请在后端运行环境中配置环境变量:
|
||||||
|
- `SILICONFLOW_API_KEY`:用于调用 SiliconFlow 的图片生成接口
|
||||||
|
|
||||||
|
配置完成后即可使用该工具生成图片。
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
|
||||||
def _create_tavily_search():
|
def _create_tavily_search():
|
||||||
"""Create and register TavilySearch tool with metadata."""
|
"""Create and register TavilySearch tool with metadata."""
|
||||||
@ -280,7 +289,12 @@ def query_knowledge_graph(query: Annotated[str, "The keyword to query knowledge
|
|||||||
return f"知识图谱查询失败: {str(e)}"
|
return f"知识图谱查询失败: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
@tool(category="buildin", tags=["图片", "生成"], display_name="Qwen-Image")
|
@tool(
|
||||||
|
category="buildin",
|
||||||
|
tags=["图片", "生成"],
|
||||||
|
display_name="Qwen-Image",
|
||||||
|
config_guide=QWEN_IMAGE_CONFIG_GUIDE,
|
||||||
|
)
|
||||||
async def text_to_img_qwen_image(
|
async def text_to_img_qwen_image(
|
||||||
prompt: Annotated[str, "用于生成图片的文本描述"],
|
prompt: Annotated[str, "用于生成图片的文本描述"],
|
||||||
negative_prompt: Annotated[str, "负面提示词,用于指定不想出现在图片中的元素"] = "",
|
negative_prompt: Annotated[str, "负面提示词,用于指定不想出现在图片中的元素"] = "",
|
||||||
|
|||||||
@ -17,6 +17,22 @@ from .security import MySQLSecurityChecker
|
|||||||
# 全局连接管理器实例
|
# 全局连接管理器实例
|
||||||
_connection_manager: MySQLConnectionManager | None = None
|
_connection_manager: MySQLConnectionManager | None = None
|
||||||
|
|
||||||
|
MYSQL_CONFIG_GUIDE = """
|
||||||
|
使用前需要先配置 MySQL 连接相关环境变量。
|
||||||
|
|
||||||
|
必填环境变量:
|
||||||
|
- `MYSQL_HOST`
|
||||||
|
- `MYSQL_PORT`
|
||||||
|
- `MYSQL_USER`
|
||||||
|
- `MYSQL_PASSWORD`
|
||||||
|
- `MYSQL_DATABASE`
|
||||||
|
|
||||||
|
可选环境变量:
|
||||||
|
- `MYSQL_DATABASE_DESCRIPTION`:数据库说明,会追加到工具描述中,帮助模型理解库表语义
|
||||||
|
|
||||||
|
请在后端运行环境中完成以上配置后再使用这些 MySQL 工具。
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
|
||||||
def get_connection_manager() -> MySQLConnectionManager:
|
def get_connection_manager() -> MySQLConnectionManager:
|
||||||
"""获取全局连接管理器"""
|
"""获取全局连接管理器"""
|
||||||
@ -50,6 +66,7 @@ def get_connection_manager() -> MySQLConnectionManager:
|
|||||||
category="mysql",
|
category="mysql",
|
||||||
tags=["数据库", "查询"],
|
tags=["数据库", "查询"],
|
||||||
display_name="列出MySQL表",
|
display_name="列出MySQL表",
|
||||||
|
config_guide=MYSQL_CONFIG_GUIDE,
|
||||||
name_or_callable="mysql_list_tables",
|
name_or_callable="mysql_list_tables",
|
||||||
)
|
)
|
||||||
def mysql_list_tables() -> str:
|
def mysql_list_tables() -> str:
|
||||||
@ -110,6 +127,7 @@ class TableDescribeModel(BaseModel):
|
|||||||
category="mysql",
|
category="mysql",
|
||||||
tags=["数据库", "结构"],
|
tags=["数据库", "结构"],
|
||||||
display_name="描述MySQL表结构",
|
display_name="描述MySQL表结构",
|
||||||
|
config_guide=MYSQL_CONFIG_GUIDE,
|
||||||
name_or_callable="mysql_describe_table",
|
name_or_callable="mysql_describe_table",
|
||||||
args_schema=TableDescribeModel,
|
args_schema=TableDescribeModel,
|
||||||
)
|
)
|
||||||
@ -212,6 +230,7 @@ class QueryModel(BaseModel):
|
|||||||
category="mysql",
|
category="mysql",
|
||||||
tags=["数据库", "SQL"],
|
tags=["数据库", "SQL"],
|
||||||
display_name="执行MySQL查询",
|
display_name="执行MySQL查询",
|
||||||
|
config_guide=MYSQL_CONFIG_GUIDE,
|
||||||
name_or_callable="mysql_query",
|
name_or_callable="mysql_query",
|
||||||
args_schema=QueryModel,
|
args_schema=QueryModel,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -10,6 +10,7 @@ class ToolExtraMetadata:
|
|||||||
tags: list[str] = field(default_factory=list)
|
tags: list[str] = field(default_factory=list)
|
||||||
display_name: str = "" # 显示名称(给人看的名字)
|
display_name: str = "" # 显示名称(给人看的名字)
|
||||||
icon: str = ""
|
icon: str = ""
|
||||||
|
config_guide: str = "" # 配置说明(给人看的使用前配置提示)
|
||||||
|
|
||||||
|
|
||||||
# 全局注册表: tool_name -> ToolExtraMetadata
|
# 全局注册表: tool_name -> ToolExtraMetadata
|
||||||
@ -40,6 +41,7 @@ def tool(
|
|||||||
tags: list[str] = None,
|
tags: list[str] = None,
|
||||||
display_name: str = "",
|
display_name: str = "",
|
||||||
icon: str = "",
|
icon: str = "",
|
||||||
|
config_guide: str = "",
|
||||||
name_or_callable: str | Callable | None = None,
|
name_or_callable: str | Callable | None = None,
|
||||||
description: str | None = None,
|
description: str | None = None,
|
||||||
args_schema: type | None = None,
|
args_schema: type | None = None,
|
||||||
@ -83,6 +85,7 @@ def tool(
|
|||||||
tags=tags or [],
|
tags=tags or [],
|
||||||
display_name=display_name,
|
display_name=display_name,
|
||||||
icon=icon,
|
icon=icon,
|
||||||
|
config_guide=config_guide,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 自动收集工具实例
|
# 自动收集工具实例
|
||||||
|
|||||||
@ -55,6 +55,7 @@ def _ensure_metadata_loaded():
|
|||||||
extra = extra_meta[tool_name]
|
extra = extra_meta[tool_name]
|
||||||
runtime_info["category"] = extra.category
|
runtime_info["category"] = extra.category
|
||||||
runtime_info["tags"] = extra.tags
|
runtime_info["tags"] = extra.tags
|
||||||
|
runtime_info["config_guide"] = extra.config_guide
|
||||||
# display_name 优先级高于 tool.name
|
# display_name 优先级高于 tool.name
|
||||||
if extra.display_name:
|
if extra.display_name:
|
||||||
runtime_info["name"] = extra.display_name
|
runtime_info["name"] = extra.display_name
|
||||||
@ -62,6 +63,7 @@ def _ensure_metadata_loaded():
|
|||||||
# 未注册,设为默认分类
|
# 未注册,设为默认分类
|
||||||
runtime_info["category"] = "buildin"
|
runtime_info["category"] = "buildin"
|
||||||
runtime_info["tags"] = []
|
runtime_info["tags"] = []
|
||||||
|
runtime_info["config_guide"] = ""
|
||||||
|
|
||||||
_metadata_cache.append(runtime_info)
|
_metadata_cache.append(runtime_info)
|
||||||
|
|
||||||
|
|||||||
@ -38,3 +38,14 @@ async def test_admin_can_fetch_config_and_reload_info(test_client, admin_headers
|
|||||||
reload_payload = reload_response.json()
|
reload_payload = reload_response.json()
|
||||||
assert reload_payload["success"] is True
|
assert reload_payload["success"] is True
|
||||||
assert "data" in reload_payload
|
assert "data" in reload_payload
|
||||||
|
|
||||||
|
|
||||||
|
async def test_admin_can_fetch_tools_with_config_guide_field(test_client, admin_headers):
|
||||||
|
response = await test_client.get("/api/system/tools", headers=admin_headers)
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
|
payload = response.json()
|
||||||
|
assert payload["success"] is True
|
||||||
|
assert isinstance(payload["data"], list)
|
||||||
|
assert payload["data"]
|
||||||
|
assert "config_guide" in payload["data"][0]
|
||||||
|
|||||||
48
backend/test/unit/services/test_tool_service.py
Normal file
48
backend/test/unit/services/test_tool_service.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from yuxi.services import tool_service
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_tool_metadata_includes_config_guide(monkeypatch):
|
||||||
|
tool_service._metadata_cache.clear()
|
||||||
|
|
||||||
|
fake_tool = SimpleNamespace(
|
||||||
|
name="demo_tool",
|
||||||
|
description="demo description",
|
||||||
|
metadata={},
|
||||||
|
args_schema=None,
|
||||||
|
)
|
||||||
|
fake_extra = SimpleNamespace(
|
||||||
|
category="buildin",
|
||||||
|
tags=["demo"],
|
||||||
|
display_name="演示工具",
|
||||||
|
config_guide="请先配置 DEMO_API_KEY",
|
||||||
|
)
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"yuxi.agents.toolkits.registry.get_all_tool_instances",
|
||||||
|
lambda: [fake_tool],
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"yuxi.agents.toolkits.registry.get_all_extra_metadata",
|
||||||
|
lambda: {"demo_tool": fake_extra},
|
||||||
|
)
|
||||||
|
|
||||||
|
result = tool_service.get_tool_metadata()
|
||||||
|
|
||||||
|
assert result == [
|
||||||
|
{
|
||||||
|
"id": "demo_tool",
|
||||||
|
"name": "演示工具",
|
||||||
|
"description": "demo description",
|
||||||
|
"metadata": {},
|
||||||
|
"args": [],
|
||||||
|
"category": "buildin",
|
||||||
|
"tags": ["demo"],
|
||||||
|
"config_guide": "请先配置 DEMO_API_KEY",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
tool_service._metadata_cache.clear()
|
||||||
@ -48,6 +48,7 @@
|
|||||||
- 重构内置 Skills/MCP/Subagents 安装/添加/移除机制:内置 skill 支持按需安装、基于 `version + content_hash` 的更新提示与覆盖确认,不再使用服务器级开关切换
|
- 重构内置 Skills/MCP/Subagents 安装/添加/移除机制:内置 skill 支持按需安装、基于 `version + content_hash` 的更新提示与覆盖确认,不再使用服务器级开关切换
|
||||||
- 新增知识库 PDF、图片的预览功能
|
- 新增知识库 PDF、图片的预览功能
|
||||||
- 重构后端测试目录结构:按 `unit / integration / e2e` 分层迁移现有测试,拆分全局 `conftest.py`,统一测试入口为 `uv run --group test pytest`,并新增独立测试规范文档 `docs/vibe/testing-guidelines.md`
|
- 重构后端测试目录结构:按 `unit / integration / e2e` 分层迁移现有测试,拆分全局 `conftest.py`,统一测试入口为 `uv run --group test pytest`,并新增独立测试规范文档 `docs/vibe/testing-guidelines.md`
|
||||||
|
- 新增工具元数据 `config_guide` 字段:后端工具列表接口现在可返回“给人看的配置说明”,前端工具详情页会展示该说明,用于提示工具使用前需要配置的环境变量或入口;首批为 MySQL 工具和 `Qwen-Image` 补充了配置指引
|
||||||
|
|
||||||
<!-- 添加到这里 -->
|
<!-- 添加到这里 -->
|
||||||
|
|
||||||
|
|||||||
@ -101,6 +101,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-section" v-if="currentTool.config_guide">
|
||||||
|
<div class="section-header">
|
||||||
|
<FileText :size="14" />
|
||||||
|
<span>配置说明</span>
|
||||||
|
</div>
|
||||||
|
<div class="section-content description config-guide">
|
||||||
|
{{ currentTool.config_guide }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="detail-section">
|
<div class="detail-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<Tag :size="14" />
|
<Tag :size="14" />
|
||||||
@ -190,7 +200,8 @@ const filteredTools = computed(() => {
|
|||||||
(t) =>
|
(t) =>
|
||||||
t.name.toLowerCase().includes(q) ||
|
t.name.toLowerCase().includes(q) ||
|
||||||
t.id.toLowerCase().includes(q) ||
|
t.id.toLowerCase().includes(q) ||
|
||||||
t.description?.toLowerCase().includes(q)
|
t.description?.toLowerCase().includes(q) ||
|
||||||
|
t.config_guide?.toLowerCase().includes(q)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@ -261,4 +272,8 @@ defineExpose({
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.config-guide {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user