aiecs 1.2.2__py3-none-any.whl → 1.3.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aiecs might be problematic. Click here for more details.
- aiecs/__init__.py +1 -1
- aiecs/llm/clients/vertex_client.py +22 -2
- aiecs/main.py +2 -2
- aiecs/scripts/tools_develop/README.md +111 -2
- aiecs/scripts/tools_develop/TOOL_AUTO_DISCOVERY.md +234 -0
- aiecs/scripts/tools_develop/validate_tool_schemas.py +80 -21
- aiecs/scripts/tools_develop/verify_tools.py +347 -0
- aiecs/tools/__init__.py +94 -30
- aiecs/tools/apisource/__init__.py +106 -0
- aiecs/tools/apisource/intelligence/__init__.py +20 -0
- aiecs/tools/apisource/intelligence/data_fusion.py +378 -0
- aiecs/tools/apisource/intelligence/query_analyzer.py +387 -0
- aiecs/tools/apisource/intelligence/search_enhancer.py +384 -0
- aiecs/tools/apisource/monitoring/__init__.py +12 -0
- aiecs/tools/apisource/monitoring/metrics.py +308 -0
- aiecs/tools/apisource/providers/__init__.py +114 -0
- aiecs/tools/apisource/providers/base.py +684 -0
- aiecs/tools/apisource/providers/census.py +412 -0
- aiecs/tools/apisource/providers/fred.py +575 -0
- aiecs/tools/apisource/providers/newsapi.py +402 -0
- aiecs/tools/apisource/providers/worldbank.py +346 -0
- aiecs/tools/apisource/reliability/__init__.py +14 -0
- aiecs/tools/apisource/reliability/error_handler.py +362 -0
- aiecs/tools/apisource/reliability/fallback_strategy.py +420 -0
- aiecs/tools/apisource/tool.py +814 -0
- aiecs/tools/apisource/utils/__init__.py +12 -0
- aiecs/tools/apisource/utils/validators.py +343 -0
- aiecs/tools/langchain_adapter.py +95 -17
- aiecs/tools/search_tool/__init__.py +102 -0
- aiecs/tools/search_tool/analyzers.py +583 -0
- aiecs/tools/search_tool/cache.py +280 -0
- aiecs/tools/search_tool/constants.py +127 -0
- aiecs/tools/search_tool/context.py +219 -0
- aiecs/tools/search_tool/core.py +773 -0
- aiecs/tools/search_tool/deduplicator.py +123 -0
- aiecs/tools/search_tool/error_handler.py +257 -0
- aiecs/tools/search_tool/metrics.py +375 -0
- aiecs/tools/search_tool/rate_limiter.py +177 -0
- aiecs/tools/search_tool/schemas.py +297 -0
- aiecs/tools/statistics/data_loader_tool.py +2 -2
- aiecs/tools/statistics/data_transformer_tool.py +1 -1
- aiecs/tools/task_tools/__init__.py +8 -8
- aiecs/tools/task_tools/report_tool.py +1 -1
- aiecs/tools/tool_executor/__init__.py +2 -0
- aiecs/tools/tool_executor/tool_executor.py +284 -14
- aiecs/utils/__init__.py +11 -0
- aiecs/utils/cache_provider.py +698 -0
- aiecs/utils/execution_utils.py +5 -5
- {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/METADATA +1 -1
- {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/RECORD +54 -22
- aiecs/tools/task_tools/search_tool.py +0 -1123
- {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/WHEEL +0 -0
- {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/entry_points.txt +0 -0
- {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.2.2.dist-info → aiecs-1.3.3.dist-info}/top_level.txt +0 -0
aiecs/__init__.py
CHANGED
|
@@ -5,7 +5,7 @@ A powerful Python middleware framework for building AI-powered applications
|
|
|
5
5
|
with tool orchestration, task execution, and multi-provider LLM support.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
__version__ = "1.
|
|
8
|
+
__version__ = "1.3.3"
|
|
9
9
|
__author__ = "AIECS Team"
|
|
10
10
|
__email__ = "iretbl@gmail.com"
|
|
11
11
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
+
import warnings
|
|
4
5
|
from typing import Dict, Any, Optional, List, AsyncGenerator
|
|
5
6
|
import vertexai
|
|
6
7
|
from vertexai.generative_models import GenerativeModel, HarmCategory, HarmBlockThreshold, GenerationConfig, SafetySetting
|
|
@@ -9,6 +10,10 @@ from google.oauth2 import service_account
|
|
|
9
10
|
from aiecs.llm.clients.base_client import BaseLLMClient, LLMMessage, LLMResponse, ProviderNotAvailableError, RateLimitError
|
|
10
11
|
from aiecs.config.config import get_settings
|
|
11
12
|
|
|
13
|
+
# Suppress Vertex AI SDK deprecation warnings (deprecated June 2025, removal June 2026)
|
|
14
|
+
# TODO: Migrate to Google Gen AI SDK when official migration guide is available
|
|
15
|
+
warnings.filterwarnings('ignore', category=UserWarning, module='vertexai.generative_models._generative_models')
|
|
16
|
+
|
|
12
17
|
logger = logging.getLogger(__name__)
|
|
13
18
|
|
|
14
19
|
class VertexAIClient(BaseLLMClient):
|
|
@@ -136,8 +141,23 @@ class VertexAIClient(BaseLLMClient):
|
|
|
136
141
|
text_parts.append(part.text)
|
|
137
142
|
|
|
138
143
|
if text_parts:
|
|
139
|
-
|
|
140
|
-
|
|
144
|
+
# Handle multi-part response format
|
|
145
|
+
if len(text_parts) > 1:
|
|
146
|
+
# Multi-part response (typical for Gemini 2.5 with tool calling)
|
|
147
|
+
# Part 1: Thinking/reasoning content
|
|
148
|
+
# Part 2+: Actual output (JSON or other format)
|
|
149
|
+
|
|
150
|
+
# Wrap first part (thinking) in <thinking> tags
|
|
151
|
+
thinking_part = text_parts[0]
|
|
152
|
+
actual_output_parts = text_parts[1:]
|
|
153
|
+
|
|
154
|
+
# Format: <thinking>Part 1</thinking>\nPart 2\nPart 3...
|
|
155
|
+
content = f"<thinking>\n{thinking_part}\n</thinking>\n" + "\n".join(actual_output_parts)
|
|
156
|
+
self.logger.info(f"✅ Successfully wrapped multi-part response: {len(text_parts)} parts (thinking + output)")
|
|
157
|
+
else:
|
|
158
|
+
# Single part response - use as is
|
|
159
|
+
content = text_parts[0]
|
|
160
|
+
self.logger.info("Successfully extracted single-part response")
|
|
141
161
|
else:
|
|
142
162
|
self.logger.warning("No text content found in multi-part response")
|
|
143
163
|
except Exception as part_error:
|
aiecs/main.py
CHANGED
|
@@ -142,7 +142,7 @@ async def lifespan(app: FastAPI):
|
|
|
142
142
|
app = FastAPI(
|
|
143
143
|
title="AIECS - AI Execute Services",
|
|
144
144
|
description="Middleware service for AI-powered task execution and tool orchestration",
|
|
145
|
-
version="1.
|
|
145
|
+
version="1.3.3",
|
|
146
146
|
lifespan=lifespan
|
|
147
147
|
)
|
|
148
148
|
|
|
@@ -167,7 +167,7 @@ async def health_check():
|
|
|
167
167
|
return {
|
|
168
168
|
"status": "healthy",
|
|
169
169
|
"service": "aiecs",
|
|
170
|
-
"version": "1.
|
|
170
|
+
"version": "1.3.3"
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
|
|
@@ -4,7 +4,116 @@
|
|
|
4
4
|
|
|
5
5
|
## 📋 脚本列表
|
|
6
6
|
|
|
7
|
-
### 1.
|
|
7
|
+
### 1. 工具验证器 (`verify_tools.py`)
|
|
8
|
+
|
|
9
|
+
快速验证和展示 aiecs.tools 注册的所有工具和功能,帮助开发者了解工具注册情况。
|
|
10
|
+
|
|
11
|
+
**用途**:
|
|
12
|
+
- 列出所有注册的工具及其状态
|
|
13
|
+
- 显示每个工具的描述和功能方法
|
|
14
|
+
- 提供工具使用示例
|
|
15
|
+
- 分析工具注册表信息
|
|
16
|
+
- 展示工具继承关系和模块分布
|
|
17
|
+
|
|
18
|
+
**命令**:
|
|
19
|
+
```bash
|
|
20
|
+
# 从项目根目录运行(推荐)
|
|
21
|
+
python -m aiecs.scripts.tools_develop.verify_tools
|
|
22
|
+
|
|
23
|
+
# 直接运行脚本
|
|
24
|
+
python aiecs/scripts/tools_develop/verify_tools.py
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**输出示例**:
|
|
28
|
+
```
|
|
29
|
+
================================================================================
|
|
30
|
+
AIECS Tools 注册工具验证
|
|
31
|
+
================================================================================
|
|
32
|
+
|
|
33
|
+
发现 26 个注册的工具
|
|
34
|
+
|
|
35
|
+
================================================================================
|
|
36
|
+
📁 任务工具 (10 个)
|
|
37
|
+
================================================================================
|
|
38
|
+
|
|
39
|
+
[1] chart
|
|
40
|
+
描述: Chart and visualization operations
|
|
41
|
+
状态: loaded
|
|
42
|
+
|
|
43
|
+
[2] pandas
|
|
44
|
+
描述: Data analysis and manipulation
|
|
45
|
+
状态: loaded
|
|
46
|
+
|
|
47
|
+
... (更多工具)
|
|
48
|
+
|
|
49
|
+
================================================================================
|
|
50
|
+
📁 文档工具 (7 个)
|
|
51
|
+
================================================================================
|
|
52
|
+
|
|
53
|
+
[11] document_parser
|
|
54
|
+
描述: Document parsing and content extraction
|
|
55
|
+
状态: loaded
|
|
56
|
+
|
|
57
|
+
... (更多工具)
|
|
58
|
+
|
|
59
|
+
================================================================================
|
|
60
|
+
📁 数据统计工具 (9 个)
|
|
61
|
+
================================================================================
|
|
62
|
+
|
|
63
|
+
[18] data_loader
|
|
64
|
+
描述: Universal data loading from multiple formats
|
|
65
|
+
状态: loaded
|
|
66
|
+
|
|
67
|
+
... (更多工具)
|
|
68
|
+
|
|
69
|
+
================================================================================
|
|
70
|
+
✅ 工具列表显示完成! 共 26 个工具
|
|
71
|
+
================================================================================
|
|
72
|
+
|
|
73
|
+
================================================================================
|
|
74
|
+
🎮 交互模式
|
|
75
|
+
================================================================================
|
|
76
|
+
|
|
77
|
+
提示:
|
|
78
|
+
- 输入工具序号 (1-26) 查看详细功能
|
|
79
|
+
- 输入工具名称查看详细功能
|
|
80
|
+
- 输入 'list' 重新显示工具列表
|
|
81
|
+
- 输入 'q' 或 'quit' 退出
|
|
82
|
+
|
|
83
|
+
👉 请选择工具 > pandas
|
|
84
|
+
|
|
85
|
+
================================================================================
|
|
86
|
+
🔍 加载工具: pandas
|
|
87
|
+
================================================================================
|
|
88
|
+
|
|
89
|
+
⏳ 正在加载...
|
|
90
|
+
已触发 task_tools.pandas_tool 模块加载
|
|
91
|
+
|
|
92
|
+
✅ 工具已成功加载
|
|
93
|
+
类名: PandasTool
|
|
94
|
+
模块: aiecs.tools.task_tools.pandas_tool
|
|
95
|
+
|
|
96
|
+
📋 原子功能列表 (共 41 个方法):
|
|
97
|
+
--------------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
[1] apply(records: List, func: str, columns: List, axis: int = 0) -> List
|
|
100
|
+
Apply a function to specified columns or rows.
|
|
101
|
+
|
|
102
|
+
[2] astype(records: List, dtypes: Dict) -> List
|
|
103
|
+
Convert column types in DataFrame.
|
|
104
|
+
|
|
105
|
+
[3] concat(records_list: List, axis: int = 0) -> List
|
|
106
|
+
Concatenate multiple DataFrames.
|
|
107
|
+
|
|
108
|
+
... (更多方法)
|
|
109
|
+
|
|
110
|
+
[41] write_file(records: List, file_path: str, file_type: str = 'csv') -> Dict
|
|
111
|
+
Write DataFrame to a file.
|
|
112
|
+
|
|
113
|
+
--------------------------------------------------------------------------------
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 2. 类型注解检查器 (`check_type_annotations.py`)
|
|
8
117
|
|
|
9
118
|
检查工具方法的类型注解完整性,确保所有方法都有完整的类型注解(参数类型 + 返回类型)。
|
|
10
119
|
|
|
@@ -49,7 +158,7 @@ aiecs-tools-check-annotations pandas --verbose
|
|
|
49
158
|
====================================================================================================
|
|
50
159
|
```
|
|
51
160
|
|
|
52
|
-
###
|
|
161
|
+
### 3. Schema 质量验证器 (`validate_tool_schemas.py`)
|
|
53
162
|
|
|
54
163
|
验证自动生成的 Schema 质量,识别需要改进的文档字符串。
|
|
55
164
|
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# 工具自动发现机制
|
|
2
|
+
|
|
3
|
+
## 📋 概述
|
|
4
|
+
|
|
5
|
+
从现在开始,**无需手动维护工具列表**!系统会自动发现所有使用 `@register_tool` 装饰器注册的工具。
|
|
6
|
+
|
|
7
|
+
## ✨ 特性
|
|
8
|
+
|
|
9
|
+
### 1. 自动工具发现
|
|
10
|
+
- ✅ 自动扫描 `aiecs/tools/task_tools/`、`aiecs/tools/docs/`、`aiecs/tools/statistics/` 目录
|
|
11
|
+
- ✅ 自动识别所有 `@register_tool` 装饰器
|
|
12
|
+
- ✅ 自动提取工具名称和描述
|
|
13
|
+
- ✅ 自动分类(task/docs/statistics)
|
|
14
|
+
|
|
15
|
+
### 2. 零维护成本
|
|
16
|
+
- ✅ 添加新工具:只需在工具类上添加 `@register_tool("tool_name")` 装饰器
|
|
17
|
+
- ✅ 删除工具:直接删除文件或移除装饰器
|
|
18
|
+
- ✅ 重命名工具:修改装饰器参数即可
|
|
19
|
+
- ✅ 无需修改任何配置文件
|
|
20
|
+
|
|
21
|
+
## 🔧 工作原理
|
|
22
|
+
|
|
23
|
+
### aiecs/tools/__init__.py
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
def _auto_discover_tools():
|
|
27
|
+
"""自动发现所有工具"""
|
|
28
|
+
# 扫描工具目录
|
|
29
|
+
for dir_name, category in [('task_tools', 'task'), ('docs', 'docs'), ('statistics', 'statistics')]:
|
|
30
|
+
# 查找所有 Python 文件
|
|
31
|
+
for filename in os.listdir(dir_path):
|
|
32
|
+
# 读取文件内容
|
|
33
|
+
# 使用正则表达式查找 @register_tool 装饰器
|
|
34
|
+
pattern = r'@register_tool\([\'"]([^\'"]+)[\'"]\)'
|
|
35
|
+
matches = re.findall(pattern, content)
|
|
36
|
+
# 提取工具名称和描述
|
|
37
|
+
# 注册为占位符,等待懒加载
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### verify_tools.py
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
def auto_discover_tool_modules():
|
|
44
|
+
"""自动发现工具模块映射"""
|
|
45
|
+
# 扫描工具目录
|
|
46
|
+
# 查找 @register_tool 装饰器
|
|
47
|
+
# 建立工具名称到模块路径的映射
|
|
48
|
+
# 返回映射表供动态加载使用
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 📝 如何添加新工具
|
|
52
|
+
|
|
53
|
+
### 步骤 1: 创建工具文件
|
|
54
|
+
|
|
55
|
+
在合适的目录创建工具文件(例如:`aiecs/tools/task_tools/my_new_tool.py`)
|
|
56
|
+
|
|
57
|
+
### 步骤 2: 添加装饰器
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from aiecs.tools import register_tool
|
|
61
|
+
from aiecs.tools.base_tool import BaseTool
|
|
62
|
+
|
|
63
|
+
@register_tool("my_new_tool")
|
|
64
|
+
class MyNewTool(BaseTool):
|
|
65
|
+
"""
|
|
66
|
+
这是一个新工具的简短描述。
|
|
67
|
+
|
|
68
|
+
这个描述会被自动提取并显示在工具列表中。
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def my_method(self, param: str) -> str:
|
|
72
|
+
"""执行某个操作"""
|
|
73
|
+
return f"Result: {param}"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 步骤 3: 验证
|
|
77
|
+
|
|
78
|
+
运行验证脚本查看新工具是否被发现:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
poetry run python -m aiecs.scripts.tools_develop.verify_tools
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
就这样!**无需修改任何其他文件**!
|
|
85
|
+
|
|
86
|
+
## 🎯 支持的目录结构
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
aiecs/tools/
|
|
90
|
+
├── task_tools/ # 任务工具 (category: task)
|
|
91
|
+
│ ├── chart_tool.py # @register_tool("chart")
|
|
92
|
+
│ ├── pandas_tool.py # @register_tool("pandas")
|
|
93
|
+
│ └── ...
|
|
94
|
+
├── docs/ # 文档工具 (category: docs)
|
|
95
|
+
│ ├── document_parser_tool.py # @register_tool("document_parser")
|
|
96
|
+
│ ├── document_writer_tool.py # @register_tool("document_writer")
|
|
97
|
+
│ └── ...
|
|
98
|
+
└── statistics/ # 统计工具 (category: statistics)
|
|
99
|
+
├── data_loader_tool.py # @register_tool("data_loader")
|
|
100
|
+
├── data_profiler_tool.py # @register_tool("data_profiler")
|
|
101
|
+
└── ...
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 🔍 描述提取规则
|
|
105
|
+
|
|
106
|
+
系统会自动提取类文档字符串的**第一行**作为工具描述:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
@register_tool("example")
|
|
110
|
+
class ExampleTool(BaseTool):
|
|
111
|
+
"""
|
|
112
|
+
这一行会被用作工具描述 ✅
|
|
113
|
+
|
|
114
|
+
下面的内容不会被提取。
|
|
115
|
+
可以写详细的文档说明。
|
|
116
|
+
"""
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**建议**:
|
|
120
|
+
- 第一行保持简短(< 200 字符)
|
|
121
|
+
- 清晰描述工具的主要功能
|
|
122
|
+
- 避免使用多行描述
|
|
123
|
+
|
|
124
|
+
## 📊 当前发现的工具统计
|
|
125
|
+
|
|
126
|
+
截至最后扫描,系统发现了 **26 个工具**:
|
|
127
|
+
|
|
128
|
+
- **任务工具** (10个):chart, classifier, image, office, pandas, report, research, scraper, search, stats
|
|
129
|
+
- **文档工具** (7个):document_parser, document_writer, document_creator, document_layout, content_insertion, ai_document_orchestrator, ai_document_writer_orchestrator
|
|
130
|
+
- **数据统计工具** (9个):data_loader, data_profiler, data_transformer, data_visualizer, model_trainer, statistical_analyzer, ai_data_analysis_orchestrator, ai_insight_generator, ai_report_orchestrator
|
|
131
|
+
|
|
132
|
+
## ⚡ 性能优化
|
|
133
|
+
|
|
134
|
+
### 占位符机制
|
|
135
|
+
- 工具发现时创建轻量级占位符
|
|
136
|
+
- 不导入实际工具模块(避免重依赖)
|
|
137
|
+
- 只在实际使用时才加载工具
|
|
138
|
+
|
|
139
|
+
### 懒加载
|
|
140
|
+
- `verify_tools.py` 只在用户选择工具时才加载模块
|
|
141
|
+
- 避免启动时加载所有工具
|
|
142
|
+
- 提高响应速度
|
|
143
|
+
|
|
144
|
+
## 🧪 测试验证
|
|
145
|
+
|
|
146
|
+
### 验证工具发现
|
|
147
|
+
```bash
|
|
148
|
+
poetry run python -m aiecs.scripts.tools_develop.verify_tools
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 验证特定工具
|
|
152
|
+
在交互模式中输入工具名称或序号:
|
|
153
|
+
```
|
|
154
|
+
👉 请选择工具 > pandas
|
|
155
|
+
👉 请选择工具 > 7
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 验证描述提取
|
|
159
|
+
检查工具列表中的描述是否正确提取自类文档字符串。
|
|
160
|
+
|
|
161
|
+
## 🔄 兼容性
|
|
162
|
+
|
|
163
|
+
### 向后兼容
|
|
164
|
+
- 现有工具无需修改
|
|
165
|
+
- 只要有 `@register_tool` 装饰器即可被发现
|
|
166
|
+
- 保持与现有代码的兼容性
|
|
167
|
+
|
|
168
|
+
### 新增目录支持
|
|
169
|
+
如果未来需要添加新的工具目录(如 `aiecs/tools/ml_tools/`),只需修改两处:
|
|
170
|
+
|
|
171
|
+
1. **aiecs/tools/__init__.py**
|
|
172
|
+
```python
|
|
173
|
+
tool_dirs = [
|
|
174
|
+
('task_tools', 'task'),
|
|
175
|
+
('docs', 'docs'),
|
|
176
|
+
('statistics', 'statistics'),
|
|
177
|
+
('ml_tools', 'ml'), # 新增
|
|
178
|
+
]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
2. **verify_tools.py**
|
|
182
|
+
```python
|
|
183
|
+
tool_dirs = {
|
|
184
|
+
'task_tools': 'aiecs.tools.task_tools',
|
|
185
|
+
'docs': 'aiecs.tools.docs',
|
|
186
|
+
'statistics': 'aiecs.tools.statistics',
|
|
187
|
+
'ml_tools': 'aiecs.tools.ml_tools', # 新增
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## ❓ 常见问题
|
|
192
|
+
|
|
193
|
+
### Q: 工具没有被发现?
|
|
194
|
+
**A:** 检查以下几点:
|
|
195
|
+
1. 文件是否在支持的目录中(task_tools/docs/statistics)
|
|
196
|
+
2. 是否使用了 `@register_tool("name")` 装饰器
|
|
197
|
+
3. 文件名是否以 `.py` 结尾且不是 `__init__.py`
|
|
198
|
+
|
|
199
|
+
### Q: 描述显示不正确?
|
|
200
|
+
**A:** 检查类文档字符串:
|
|
201
|
+
1. 文档字符串必须紧跟类定义
|
|
202
|
+
2. 使用三引号 `"""`
|
|
203
|
+
3. 确保第一行是简短描述
|
|
204
|
+
|
|
205
|
+
### Q: 如何重命名工具?
|
|
206
|
+
**A:** 只需修改装饰器参数:
|
|
207
|
+
```python
|
|
208
|
+
# 旧
|
|
209
|
+
@register_tool("old_name")
|
|
210
|
+
|
|
211
|
+
# 新
|
|
212
|
+
@register_tool("new_name")
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Q: 如何临时禁用工具?
|
|
216
|
+
**A:** 两种方法:
|
|
217
|
+
1. 注释掉 `@register_tool` 装饰器
|
|
218
|
+
2. 移动文件到其他目录
|
|
219
|
+
|
|
220
|
+
## 🎉 优势总结
|
|
221
|
+
|
|
222
|
+
| 特性 | 手动维护 | 自动发现 |
|
|
223
|
+
|------|---------|---------|
|
|
224
|
+
| 添加工具 | 需修改配置文件 | 只需添加装饰器 ✅ |
|
|
225
|
+
| 删除工具 | 需修改配置文件 | 直接删除文件 ✅ |
|
|
226
|
+
| 维护成本 | 高 | 零 ✅ |
|
|
227
|
+
| 出错风险 | 容易遗漏 | 自动同步 ✅ |
|
|
228
|
+
| 描述准确性 | 可能不一致 | 直接提取 ✅ |
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
**维护者**: AIECS Tools Team
|
|
233
|
+
**最后更新**: 2025-10-14
|
|
234
|
+
|
|
@@ -104,61 +104,113 @@ def validate_schema_quality(schema: Type[BaseModel], method: callable, method_na
|
|
|
104
104
|
return issues
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
def find_manual_schema(tool_class: Type, method_name: str) -> Optional[Type[BaseModel]]:
|
|
108
|
+
"""
|
|
109
|
+
查找手动定义的 Schema(与 langchain_adapter 逻辑一致)
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
tool_class: 工具类
|
|
113
|
+
method_name: 方法名
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
找到的 Schema 类,如果没有则返回 None
|
|
117
|
+
"""
|
|
118
|
+
schemas = {}
|
|
119
|
+
|
|
120
|
+
# 1. 检查类级别的 schemas
|
|
121
|
+
for attr_name in dir(tool_class):
|
|
122
|
+
attr = getattr(tool_class, attr_name)
|
|
123
|
+
if isinstance(attr, type) and issubclass(attr, BaseModel) and attr.__name__.endswith('Schema'):
|
|
124
|
+
# 标准化:移除 'Schema' 后缀,转小写,移除下划线
|
|
125
|
+
schema_base_name = attr.__name__.replace('Schema', '')
|
|
126
|
+
normalized_name = schema_base_name.replace('_', '').lower()
|
|
127
|
+
schemas[normalized_name] = attr
|
|
128
|
+
|
|
129
|
+
# 2. 检查模块级别的 schemas
|
|
130
|
+
import inspect
|
|
131
|
+
tool_module = inspect.getmodule(tool_class)
|
|
132
|
+
if tool_module:
|
|
133
|
+
for attr_name in dir(tool_module):
|
|
134
|
+
if attr_name.startswith('_'):
|
|
135
|
+
continue
|
|
136
|
+
attr = getattr(tool_module, attr_name)
|
|
137
|
+
if isinstance(attr, type) and issubclass(attr, BaseModel) and attr.__name__.endswith('Schema'):
|
|
138
|
+
schema_base_name = attr.__name__.replace('Schema', '')
|
|
139
|
+
normalized_name = schema_base_name.replace('_', '').lower()
|
|
140
|
+
if normalized_name not in schemas:
|
|
141
|
+
schemas[normalized_name] = attr
|
|
142
|
+
|
|
143
|
+
# 标准化方法名:移除下划线并转小写
|
|
144
|
+
normalized_method_name = method_name.replace('_', '').lower()
|
|
145
|
+
|
|
146
|
+
# 查找匹配的 schema
|
|
147
|
+
return schemas.get(normalized_method_name)
|
|
148
|
+
|
|
149
|
+
|
|
107
150
|
def analyze_tool_schemas(tool_name: str, tool_class: Type) -> Dict[str, Any]:
|
|
108
|
-
"""分析工具的 Schema
|
|
109
|
-
|
|
151
|
+
"""分析工具的 Schema 生成情况(支持手动定义和自动生成)"""
|
|
152
|
+
|
|
110
153
|
metrics = SchemaQualityMetrics()
|
|
111
154
|
methods_info = []
|
|
112
|
-
|
|
155
|
+
|
|
113
156
|
for method_name in dir(tool_class):
|
|
114
157
|
# 跳过私有方法和特殊方法
|
|
115
158
|
if method_name.startswith('_'):
|
|
116
159
|
continue
|
|
117
|
-
|
|
160
|
+
|
|
118
161
|
# 跳过基类方法
|
|
119
162
|
if method_name in ['run', 'run_async', 'run_batch']:
|
|
120
163
|
continue
|
|
121
|
-
|
|
164
|
+
|
|
122
165
|
method = getattr(tool_class, method_name)
|
|
123
|
-
|
|
166
|
+
|
|
124
167
|
# 跳过非方法属性
|
|
125
168
|
if not callable(method) or isinstance(method, type):
|
|
126
169
|
continue
|
|
127
|
-
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
170
|
+
|
|
171
|
+
# 首先尝试查找手动定义的 Schema
|
|
172
|
+
manual_schema = find_manual_schema(tool_class, method_name)
|
|
173
|
+
|
|
174
|
+
if manual_schema:
|
|
175
|
+
schema = manual_schema
|
|
176
|
+
schema_type = 'manual'
|
|
177
|
+
else:
|
|
178
|
+
# 如果没有手动 Schema,则自动生成
|
|
179
|
+
schema = generate_schema_from_method(method, method_name)
|
|
180
|
+
schema_type = 'auto'
|
|
181
|
+
|
|
131
182
|
method_info = {
|
|
132
183
|
'name': method_name,
|
|
133
184
|
'schema': schema,
|
|
185
|
+
'schema_type': schema_type,
|
|
134
186
|
'issues': []
|
|
135
187
|
}
|
|
136
|
-
|
|
188
|
+
|
|
137
189
|
if schema:
|
|
138
190
|
metrics.add_method(True)
|
|
139
|
-
|
|
191
|
+
|
|
140
192
|
# 验证质量
|
|
141
193
|
issues = validate_schema_quality(schema, method, method_name)
|
|
142
194
|
method_info['issues'] = issues
|
|
143
|
-
|
|
195
|
+
|
|
144
196
|
# 统计字段
|
|
145
197
|
for field_name, field_info in schema.model_fields.items():
|
|
146
198
|
has_type = field_info.annotation is not None
|
|
147
199
|
has_meaningful_desc = (
|
|
148
|
-
field_info.description and
|
|
200
|
+
field_info.description and
|
|
149
201
|
field_info.description != f"Parameter {field_name}"
|
|
150
202
|
)
|
|
151
203
|
metrics.add_field(has_type, has_meaningful_desc)
|
|
152
|
-
|
|
204
|
+
|
|
153
205
|
# 记录问题
|
|
154
206
|
for issue in issues:
|
|
155
207
|
metrics.add_issue(f"{tool_name}.{method_name}: {issue}")
|
|
156
208
|
else:
|
|
157
209
|
metrics.add_method(False)
|
|
158
210
|
method_info['issues'] = ['⚠️ 无法生成 Schema(可能是无参数方法)']
|
|
159
|
-
|
|
211
|
+
|
|
160
212
|
methods_info.append(method_info)
|
|
161
|
-
|
|
213
|
+
|
|
162
214
|
return {
|
|
163
215
|
'metrics': metrics,
|
|
164
216
|
'methods': methods_info
|
|
@@ -167,11 +219,15 @@ def analyze_tool_schemas(tool_name: str, tool_class: Type) -> Dict[str, Any]:
|
|
|
167
219
|
|
|
168
220
|
def print_tool_report(tool_name: str, result: Dict, verbose: bool = False, show_examples: bool = False):
|
|
169
221
|
"""打印工具报告"""
|
|
170
|
-
|
|
222
|
+
|
|
171
223
|
metrics = result['metrics']
|
|
172
224
|
methods = result['methods']
|
|
173
225
|
scores = metrics.get_scores()
|
|
174
|
-
|
|
226
|
+
|
|
227
|
+
# 统计手动和自动 schema
|
|
228
|
+
manual_schemas = [m for m in methods if m.get('schema_type') == 'manual']
|
|
229
|
+
auto_schemas = [m for m in methods if m.get('schema_type') == 'auto']
|
|
230
|
+
|
|
175
231
|
# 状态图标
|
|
176
232
|
overall = scores['overall_score']
|
|
177
233
|
if overall >= 90:
|
|
@@ -186,10 +242,12 @@ def print_tool_report(tool_name: str, result: Dict, verbose: bool = False, show_
|
|
|
186
242
|
else:
|
|
187
243
|
status = "❌"
|
|
188
244
|
grade = "D (需改进)"
|
|
189
|
-
|
|
245
|
+
|
|
190
246
|
print(f"\n{status} {tool_name}")
|
|
191
247
|
print(f" 方法数: {metrics.total_methods}")
|
|
192
248
|
print(f" 成功生成 Schema: {metrics.schemas_generated} ({scores['generation_rate']:.1f}%)")
|
|
249
|
+
print(f" - 手动定义: {len(manual_schemas)} 个")
|
|
250
|
+
print(f" - 自动生成: {len(auto_schemas)} 个")
|
|
193
251
|
print(f" 描述质量: {scores['description_quality']:.1f}%")
|
|
194
252
|
print(f" 综合评分: {scores['overall_score']:.1f}% ({grade})")
|
|
195
253
|
|
|
@@ -215,7 +273,8 @@ def print_tool_report(tool_name: str, result: Dict, verbose: bool = False, show_
|
|
|
215
273
|
print(f"\n 示例 Schema:")
|
|
216
274
|
for method_info in methods_with_schema[:2]:
|
|
217
275
|
schema = method_info['schema']
|
|
218
|
-
|
|
276
|
+
schema_type_label = "🔧 手动定义" if method_info.get('schema_type') == 'manual' else "🤖 自动生成"
|
|
277
|
+
print(f"\n {method_info['name']} → {schema.__name__} [{schema_type_label}]")
|
|
219
278
|
print(f" 描述: {schema.__doc__}")
|
|
220
279
|
print(f" 字段:")
|
|
221
280
|
for field_name, field_info in list(schema.model_fields.items())[:3]:
|