fix: 鲁班工具开发体验优化 - 五组件架构升级与智能错误处理 (#116)

* feat: 为promptx_tool增加forceReinstall选项支持

解决工具沙箱缓存机制问题,允许强制重新安装工具依赖。

## 修改内容

### 1. 工具定义更新 (toolDefinitions.js)
- 增加 context.options.forceReinstall 参数支持
- 增加 context.options.timeout 参数支持
- 完善参数描述,说明context用途
- 移除暂时不需要的role_id和session_id参数

### 2. 执行逻辑优化 (ToolCommand.js)
- 支持从context.options提取执行选项
- 将选项传递给ToolSandbox构造函数
- 增加调试日志输出沙箱选项
- 完善JSDoc注释

## 解决的问题

- Issue #107: PromptX工具沙箱缓存机制不支持工具集更新
- 鲁班等AI角色在调试工具时遇到的缓存问题
- 工具依赖更新后无法自动重新安装的问题

## 使用方式

```javascript
// 强制重新安装依赖
{
  tool_resource: "@tool://tool-name",
  parameters: { /* 工具参数 */ },
  context: {
    options: {
      forceReinstall: true
    }
  }
}
```

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: 简化context参数,移除暂时不需要的role_id和session_id

按照奥卡姆剃刀原则,移除当前没有实际用途的参数:
- 移除 context.role_id
- 移除 context.session_id
- 保留 context.options 用于执行配置
- 简化API接口,降低复杂度

未来如需要可重新添加这些参数。

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: 简化promptx_tool参数结构,移除context层级

## 主要改进

### 1. 扁平化参数结构
- 移除复杂的context嵌套,直接使用顶级参数
- forceReinstall 和 timeout 作为可选的顶级参数
- 遵循MCP协议惯例,降低AI理解成本

### 2. 参数说明优化
- forceReinstall: 明确默认值为false
- 详细说明使用场景:工具开发和调试中的缓存问题
- timeout: 明确默认值为30000ms

### 3. 防止盲目调用
- 增加重要提醒:要求AI先了解工具说明再调用
- 避免大模型在不了解工具的情况下盲目执行

## 新的调用方式

## 优势
- 符合MCP预训练共识,降低AI学习成本
- 参数结构简洁直观
- 保持向后兼容性

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: 修正鲁班工具开发提示词 - 更新为五组件架构并增加AI引导机制

- 更新DPML工具标签知识:从四组件升级到五组件架构(增加identity标签)
- 在工具开发工作流中增加.tool.md说明书创建步骤
- 添加AI使用提醒机制,强化工具使用前必读原则
- 完善工具标签编写模板,包含完整的五标签结构
- 更新质量检查标准,适配新的标签体系

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat: 添加智能require错误过滤机制到ToolSandbox

解决工具开发中的常见问题:analyze阶段require依赖导致的错误

核心改进:
- 添加_filterRequireError()私有方法,智能识别MODULE_NOT_FOUND错误
- 通过静态分析提取getDependencies()声明的依赖列表
- 对比缺失模块与声明依赖,区分合法缺失和代码错误
- 支持版本号格式的依赖声明(如axios@^1.6.0)

开发者体验提升:
- 可以自然地在文件顶部写require()语句
- 忘记声明依赖时仍会得到明确错误提示
- 降低工具开发的认知负担和学习成本
- 遵循奥卡姆剃刀原则:用技术解决技术问题

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Sean
2025-07-05 09:10:43 +08:00
committed by GitHub
parent e414dc0d18
commit d1d38e046b
3 changed files with 278 additions and 21 deletions

View File

@ -52,7 +52,49 @@ flowchart TD
- 确认沙箱环境兼容性 - 确认沙箱环境兼容性
- 设计错误处理策略 - 设计错误处理策略
**Step 1.3: 接口规范设计** **Step 1.3: 工具说明书设计**
```xml
<!-- {tool-name}.tool.md 模板 -->
<tool>
<identity>
## 工具名称
@tool://{tool-name}
## 简介
工具功能的一句话简介
</identity>
<purpose>
⚠️ **AI重要提醒**: 调用此工具前必须完整阅读本说明书,理解工具功能边界、参数要求和使用限制。禁止在不了解工具功能的情况下盲目调用。
## 核心问题定义
明确描述工具要解决的具体问题
## 价值主张
- 🎯 **解决什么痛点**:具体描述用户痛点
- 🚀 **带来什么价值**:明确量化收益
- 🌟 **独特优势**:相比其他方案的优势
## 应用边界
- ✅ **适用场景**:详细列出适用情况
- ❌ **不适用场景**:明确使用边界
</purpose>
<usage>
<!-- 详细的使用指导 -->
</usage>
<parameter>
<!-- 完整的参数说明 -->
</parameter>
<outcome>
<!-- 返回结果格式说明 -->
</outcome>
</tool>
```
**Step 1.4: 接口规范设计**
```javascript ```javascript
// 标准工具接口模板 // 标准工具接口模板
module.exports = { module.exports = {
@ -90,19 +132,122 @@ module.exports = {
```mermaid ```mermaid
flowchart LR flowchart LR
A[创建工具文件] --> B[实现接口方法] A[创建工具文件] --> B[编写说明书]
B --> C[依赖管理] B --> C[实现接口方法]
C --> D[核心逻辑] C --> D[依赖管理]
D --> E[错误处理] D --> E[核心逻辑]
E --> F[错误处理]
``` ```
**Step 2.1: 工具文件创建** **Step 2.1: 工具文件创建**
```bash ```bash
# 标准文件路径 # 标准文件路径
.promptx/resource/tool/{tool-name}/{tool-name}.tool.js .promptx/resource/tool/{tool-name}/{tool-name}.tool.js # 给计算机的执行代码
.promptx/resource/tool/{tool-name}/{tool-name}.tool.md # 给AI的使用说明书
``` ```
**Step 2.2: 依赖管理实现** **Step 2.2: 工具说明书编写**
基于Phase 1的设计完整编写五组件说明书
```xml
<tool>
<identity>
## 工具名称
@tool://actual-tool-name
## 简介
具体的工具功能描述
</identity>
<purpose>
⚠️ **AI重要提醒**: 调用此工具前必须完整阅读本说明书,理解工具功能边界、参数要求和使用限制。禁止在不了解工具功能的情况下盲目调用。
## 核心问题定义
[具体问题描述]
## 价值主张
- 🎯 **解决什么痛点**[具体痛点]
- 🚀 **带来什么价值**[具体价值]
- 🌟 **独特优势**[核心优势]
## 应用边界
- ✅ **适用场景**[适用情况]
- ❌ **不适用场景**[限制条件]
</purpose>
<usage>
## 使用时机
[具体使用场景]
## 操作步骤
1. **准备阶段**[准备工作]
2. **执行阶段**[执行步骤]
3. **验证阶段**[验证方法]
## 最佳实践
- 🎯 **效率提升**[效率技巧]
- ⚠️ **避免陷阱**[常见问题]
- 🔧 **故障排除**[问题解决]
## 注意事项
[重要提醒事项]
</usage>
<parameter>
## 必需参数
| 参数名 | 类型 | 描述 | 示例 |
|--------|------|------|------|
| [参数] | [类型] | [描述] | [示例] |
## 可选参数
| 参数名 | 类型 | 默认值 | 描述 |
|--------|------|--------|------|
| [参数] | [类型] | [默认值] | [描述] |
## 参数约束
- **[约束类型]**[约束说明]
## 参数示例
```json
{
"[参数名]": "[参数值]"
}
```
</parameter>
<outcome>
## 成功返回格式
```json
{
"success": true,
"data": {
"[数据字段]": "[数据说明]"
}
}
```
## 错误处理格式
```json
{
"success": false,
"error": {
"code": "[错误代码]",
"message": "[错误信息]"
}
}
```
## 结果解读指南
- **[使用方式]**[说明]
## 后续动作建议
- [成功时的建议]
- [失败时的建议]
</outcome>
</tool>
```
**Step 2.3: 依赖管理实现**
```javascript ```javascript
getDependencies() { getDependencies() {
return [ return [
@ -113,7 +258,7 @@ getDependencies() {
} }
``` ```
**Step 2.3: 元信息定义** **Step 2.4: 元信息定义**
```javascript ```javascript
getMetadata() { getMetadata() {
return { return {
@ -127,7 +272,7 @@ getMetadata() {
} }
``` ```
**Step 2.4: Schema定义** **Step 2.5: Schema定义**
```javascript ```javascript
getSchema() { getSchema() {
return { return {

View File

@ -4,11 +4,12 @@
## 🏷️ DPML工具标签框架深度理解 ## 🏷️ DPML工具标签框架深度理解
### 组件架构精通 ### 组件架构精通
DPML#工具提示单元 基于组件架构构建完整的AI工具定义 DPML#工具提示单元 基于组件架构构建完整的AI工具定义
```xml ```xml
<tool> <tool>
<identity>工具身份 - 工具名称和简介</identity>
<purpose>用途说明 - 明确工具解决什么问题</purpose> <purpose>用途说明 - 明确工具解决什么问题</purpose>
<usage>使用方法 - 详细说明如何正确使用</usage> <usage>使用方法 - 详细说明如何正确使用</usage>
<parameter>参数定义 - 明确工具需要什么输入</parameter> <parameter>参数定义 - 明确工具需要什么输入</parameter>
@ -23,9 +24,22 @@ DPML#工具提示单元 基于四组件架构构建完整的AI工具定义
## 📝 标准工具标签编写模板 ## 📝 标准工具标签编写模板
### Identity组件编写精要
```xml
<identity>
## 工具名称
@tool://tool-name
## 简介
简洁明确的工具功能描述,一句话说明工具的核心能力
</identity>
```
### Purpose组件编写精要 ### Purpose组件编写精要
```xml ```xml
<purpose> <purpose>
⚠️ **AI重要提醒**: 调用此工具前必须完整阅读本说明书,理解工具功能边界、参数要求和使用限制。禁止在不了解工具功能的情况下盲目调用。
## 核心问题定义 ## 核心问题定义
明确描述工具要解决的具体问题和适用场景 明确描述工具要解决的具体问题和适用场景
@ -139,6 +153,12 @@ DPML#工具提示单元 基于四组件架构构建完整的AI工具定义
## 🎯 工具标签质量标准 ## 🎯 工具标签质量标准
### Identity质量检查
- ✅ 工具名称符合@tool://格式
- ✅ 简介简洁明确一句话概括
- ✅ 体现工具核心能力
- ✅ 便于快速识别和理解
### Purpose质量检查 ### Purpose质量检查
- ✅ 问题定义清晰具体 - ✅ 问题定义清晰具体
- ✅ 价值主张明确量化 - ✅ 价值主张明确量化
@ -227,15 +247,16 @@ return {
```mermaid ```mermaid
flowchart TD flowchart TD
A[用户需求] --> B[编写Purpose] A[用户需求] --> B[定义Identity]
B --> C[设计Usage] B --> C[编写Purpose]
C --> D[定义Parameter] C --> D[设计Usage]
D --> E[规划Outcome] D --> E[定义Parameter]
E --> F[生成工具标签] E --> F[规划Outcome]
F --> G[映射到代码接口] F --> G[生成工具标签]
G --> H[实现具体逻辑] G --> H[映射到代码接口]
H --> I[测试验证] H --> I[实现具体逻辑]
I --> J[完整工具交付] I --> J[测试验证]
J --> K[完整工具交付]
``` ```
### 开发质量保证 ### 开发质量保证

View File

@ -211,7 +211,17 @@ class ToolSandbox {
const script = new vm.Script(this.toolContent, { filename: `${this.toolId}.js` }); const script = new vm.Script(this.toolContent, { filename: `${this.toolId}.js` });
const context = vm.createContext(basicSandbox); const context = vm.createContext(basicSandbox);
try {
script.runInContext(context); script.runInContext(context);
} catch (error) {
// 使用智能错误过滤处理require错误
const filteredError = this._filterRequireError(error);
if (filteredError) {
throw filteredError;
}
// 如果是预期的require错误继续执行
}
const exported = context.module.exports; const exported = context.module.exports;
if (!exported) { if (!exported) {
@ -241,6 +251,87 @@ class ToolSandbox {
this.toolInstance = toolInstance; this.toolInstance = toolInstance;
} }
/**
* 智能过滤require错误
* @param {Error} error - 捕获的错误
* @returns {Error|null} - 如果是真正的错误则返回Error对象如果是预期的require错误则返回null
* @private
*/
_filterRequireError(error) {
// 检查是否是MODULE_NOT_FOUND错误
if (error.code === 'MODULE_NOT_FOUND') {
const missingModule = this._extractMissingModuleName(error.message);
if (missingModule) {
// 获取已声明的依赖列表
const declaredDependencies = this._extractDeclaredDependencies();
// 检查缺失的模块是否在依赖声明中
if (this._isDeclaredInDependencies(missingModule, declaredDependencies)) {
console.log(`[ToolSandbox] 依赖 ${missingModule} 未安装将在prepareDependencies阶段安装`);
return null; // 预期的错误,忽略
} else {
return new Error(`未声明的依赖: ${missingModule}请在getDependencies()中添加此依赖`);
}
}
}
// 其他错误直接返回
return error;
}
/**
* 从错误信息中提取缺失的模块名
* @param {string} errorMessage - 错误信息
* @returns {string|null} - 模块名或null
* @private
*/
_extractMissingModuleName(errorMessage) {
// 匹配 "Cannot find module 'moduleName'" 或 "Cannot resolve module 'moduleName'"
const match = errorMessage.match(/Cannot (?:find|resolve) module ['"]([^'"]+)['"]/);
return match ? match[1] : null;
}
/**
* 尝试从工具代码中提取已声明的依赖
* @returns {string[]} - 依赖列表
* @private
*/
_extractDeclaredDependencies() {
try {
// 尝试通过正则表达式从代码中提取getDependencies的返回值
const dependencyMatch = this.toolContent.match(/getDependencies\s*\(\s*\)\s*\{[\s\S]*?return\s*\[([\s\S]*?)\]/);
if (dependencyMatch) {
const dependencyString = dependencyMatch[1];
// 提取字符串字面量
const stringMatches = dependencyString.match(/['"]([^'"]+)['"]/g);
if (stringMatches) {
return stringMatches.map(str => str.slice(1, -1)); // 去掉引号
}
}
} catch (error) {
console.warn(`[ToolSandbox] 无法解析依赖声明: ${error.message}`);
}
return [];
}
/**
* 检查模块是否在依赖声明中
* @param {string} moduleName - 模块名
* @param {string[]} declaredDependencies - 已声明的依赖列表
* @returns {boolean} - 是否已声明
* @private
*/
_isDeclaredInDependencies(moduleName, declaredDependencies) {
return declaredDependencies.some(dep => {
// 支持 "axios@^1.6.0" 格式,提取模块名部分
const depName = dep.split('@')[0];
return depName === moduleName;
});
}
/** /**
* 确保沙箱目录存在 * 确保沙箱目录存在
*/ */