Skip to content

8.4 Schema设计艺术 🎨

"好的Schema就像一份清晰的说明书,让用户一看就知道怎么用。"

想象一下,你买了一个复杂的电子产品,但说明书写得云里雾里。你会有什么感受?MCP工具的Schema就是这份"说明书",它决定了用户体验的好坏。

Schema的重要性 🎯

Schema不仅仅是技术规范,更是用户体验的第一道门槛。一个好的Schema能让AI模型更好地理解和使用你的工具,就像给盲人指路的盲道一样重要。

Schema的三大作用

  1. 参数规范 📋 - 定义工具需要什么参数
  2. 类型验证 ✅ - 确保参数类型正确
  3. 用户指导 📖 - 帮助AI模型理解如何使用工具

清晰的参数描述 📝

描述要像讲故事一样生动

typescript
// ❌ 糟糕的描述
const badSchema = {
  type: "object",
  properties: {
    q: { type: "string" },                    // 什么是q?
    n: { type: "integer" },                   // n代表什么?
    f: { type: "boolean" }                    // f是什么意思?
  }
};

// ✅ 优秀的描述  
const goodSchema = {
  type: "object",
  properties: {
    query: {
      type: "string",
      description: "搜索关键词。支持中英文,可以是产品名称、品牌或描述性词语",
      examples: ["iPhone 15", "红色连衣裙", "咖啡机"],
      minLength: 1,
      maxLength: 200
    },
    maxResults: {
      type: "integer", 
      description: "返回结果的最大数量。建议10-50之间,过大可能影响响应速度",
      minimum: 1,
      maximum: 100,
      default: 10
    },
    includeOutOfStock: {
      type: "boolean",
      description: "是否包含缺货商品。true=包含缺货商品,false=仅显示有库存商品",
      default: false
    }
  },
  required: ["query"]
};

描述的黄金法则

  1. 说人话 🗣️ - 避免技术黑话,用用户能理解的语言
  2. 举例子 💡 - 提供具体的使用示例
  3. 讲限制 ⚠️ - 明确说明参数的限制和约束
  4. 给默认 🎯 - 为可选参数提供合理的默认值
python
# 🌟 实战示例:电商搜索工具的Schema设计
def create_product_search_schema():
    return {
        "type": "object",
        "description": "在电商平台搜索商品,支持多种筛选条件",
        "properties": {
            "keyword": {
                "type": "string",
                "description": "搜索关键词。可以是:1)商品名称(如'MacBook Pro')2)品牌名(如'Apple')3)类别(如'笔记本电脑')4)描述性词语(如'轻薄办公本')",
                "examples": [
                    "iPhone 15 Pro",
                    "Nike运动鞋", 
                    "咖啡机",
                    "轻薄笔记本",
                    "婴儿奶粉"
                ],
                "minLength": 1,
                "maxLength": 100
            },
            "category": {
                "type": "string",
                "description": "商品类别筛选。留空表示搜索所有类别",
                "enum": [
                    "electronics",      # 电子产品
                    "clothing",         # 服装
                    "home_garden",      # 家居园艺
                    "sports",           # 运动户外
                    "books",            # 图书音像
                    "beauty",           # 美妆个护
                    "food",             # 食品饮料
                    "baby",             # 母婴用品
                    "automotive",       # 汽车用品
                    "health"            # 健康保健
                ],
                "enumDescriptions": [
                    "电子产品:手机、电脑、家电等",
                    "服装:男装、女装、童装、鞋靴等",
                    "家居园艺:家具、装饰、园艺工具等",
                    "运动户外:健身器材、户外装备等",
                    "图书音像:书籍、音乐、影视等",
                    "美妆个护:化妆品、护肤品、个人护理等",
                    "食品饮料:零食、饮料、生鲜等",
                    "母婴用品:奶粉、玩具、童车等",
                    "汽车用品:汽车配件、装饰等",
                    "健康保健:保健品、医疗器械等"
                ]
            },
            "priceRange": {
                "type": "object",
                "description": "价格范围筛选(人民币),不设置表示不限价格",
                "properties": {
                    "min": {
                        "type": "number",
                        "description": "最低价格(元)",
                        "minimum": 0,
                        "example": 100
                    },
                    "max": {
                        "type": "number", 
                        "description": "最高价格(元)",
                        "minimum": 0,
                        "example": 5000
                    }
                },
                "additionalProperties": False
            },
            "sortBy": {
                "type": "string",
                "description": "结果排序方式",
                "enum": ["relevance", "price_asc", "price_desc", "sales", "rating", "newest"],
                "enumDescriptions": [
                    "relevance - 相关度排序(推荐)",
                    "price_asc - 价格从低到高",
                    "price_desc - 价格从高到低", 
                    "sales - 销量排序",
                    "rating - 评分排序",
                    "newest - 最新上架"
                ],
                "default": "relevance"
            },
            "limit": {
                "type": "integer",
                "description": "返回商品数量。建议20-50,数量过多会影响AI处理效率",
                "minimum": 1,
                "maximum": 100,
                "default": 20
            },
            "includeOutOfStock": {
                "type": "boolean",
                "description": "是否包含缺货商品",
                "default": False
            },
            "region": {
                "type": "string", 
                "description": "配送地区,影响商品可用性和价格",
                "examples": ["北京", "上海", "广州", "深圳"],
                "default": "全国"
            }
        },
        "required": ["keyword"],
        "additionalProperties": False
    }

验证约束设计 🛡️

多层次的验证策略

验证约束就像是多道安全门,层层把关确保数据质量。

java
// 🌟 全面的验证约束示例
public class FileOperationSchema {
    
    public static Map<String, Object> createSchema() {
        Map<String, Object> schema = new HashMap<>();
        schema.put("type", "object");
        schema.put("description", "文件操作工具,支持读取、写入、删除等操作");
        
        Map<String, Object> properties = new HashMap<>();
        
        // 操作类型 - 枚举约束
        Map<String, Object> operation = new HashMap<>();
        operation.put("type", "string");
        operation.put("description", "要执行的文件操作类型");
        operation.put("enum", Arrays.asList("read", "write", "append", "delete", "list", "copy", "move"));
        
        Map<String, String> enumDescriptions = new HashMap<>();
        enumDescriptions.put("read", "读取文件内容");
        enumDescriptions.put("write", "写入内容到文件(覆盖)");
        enumDescriptions.put("append", "追加内容到文件末尾");
        enumDescriptions.put("delete", "删除文件");
        enumDescriptions.put("list", "列出目录内容");
        enumDescriptions.put("copy", "复制文件");
        enumDescriptions.put("move", "移动/重命名文件");
        operation.put("enumDescriptions", enumDescriptions);
        
        // 文件路径 - 字符串格式约束
        Map<String, Object> path = new HashMap<>();
        path.put("type", "string");
        path.put("description", "目标文件或目录的路径。必须是绝对路径,且在允许的目录范围内");
        path.put("pattern", "^(/[^/\\0]+)+/?$|^[A-Za-z]:\\\\(?:[^\\\\/:*?\"<>|\\0]+\\\\)*[^\\\\/:*?\"<>|\\0]*$");
        path.put("minLength", 1);
        path.put("maxLength", 500);
        path.put("examples", Arrays.asList(
            "/home/user/documents/file.txt",
            "/tmp/data.json", 
            "C:\\Users\\Username\\Documents\\file.txt"
        ));
        
        // 文件内容 - 大小约束
        Map<String, Object> content = new HashMap<>();
        content.put("type", "string");
        content.put("description", "文件内容(仅用于write和append操作)");
        content.put("maxLength", 1024 * 1024); // 1MB限制
        
        // 编码格式 - 枚举约束
        Map<String, Object> encoding = new HashMap<>();
        encoding.put("type", "string");
        encoding.put("description", "文件编码格式");
        encoding.put("enum", Arrays.asList("utf-8", "gbk", "ascii", "utf-16"));
        encoding.put("default", "utf-8");
        
        // 文件权限 - 自定义验证
        Map<String, Object> permissions = new HashMap<>();
        permissions.put("type", "string");
        permissions.put("description", "文件权限(Unix格式,如755、644)");
        permissions.put("pattern", "^[0-7]{3}$");
        permissions.put("examples", Arrays.asList("755", "644", "600"));
        
        // 目标路径(用于copy/move操作)
        Map<String, Object> targetPath = new HashMap<>();
        targetPath.put("type", "string");
        targetPath.put("description", "目标路径(仅用于copy和move操作)");
        targetPath.put("pattern", "^(/[^/\\0]+)+/?$|^[A-Za-z]:\\\\(?:[^\\\\/:*?\"<>|\\0]+\\\\)*[^\\\\/:*?\"<>|\\0]*$");
        
        // 覆盖确认
        Map<String, Object> overwrite = new HashMap<>();
        overwrite.put("type", "boolean");
        overwrite.put("description", "如果目标文件已存在,是否覆盖");
        overwrite.put("default", false);
        
        // 递归操作
        Map<String, Object> recursive = new HashMap<>();
        recursive.put("type", "boolean");
        recursive.put("description", "是否递归处理目录(用于delete和list操作)");
        recursive.put("default", false);
        
        // 组装属性
        properties.put("operation", operation);
        properties.put("path", path);
        properties.put("content", content);
        properties.put("encoding", encoding);
        properties.put("permissions", permissions);
        properties.put("targetPath", targetPath);
        properties.put("overwrite", overwrite);
        properties.put("recursive", recursive);
        
        schema.put("properties", properties);
        
        // 必需字段
        schema.put("required", Arrays.asList("operation", "path"));
        
        // 条件验证 - 使用JSON Schema的条件逻辑
        List<Map<String, Object>> allOf = new ArrayList<>();
        
        // 如果operation是write或append,则content是必需的
        Map<String, Object> writeCondition = new HashMap<>();
        Map<String, Object> writeIf = new HashMap<>();
        writeIf.put("properties", Map.of("operation", Map.of("enum", Arrays.asList("write", "append"))));
        Map<String, Object> writeThen = new HashMap<>();
        writeThen.put("required", Arrays.asList("content"));
        writeCondition.put("if", writeIf);
        writeCondition.put("then", writeThen);
        allOf.add(writeCondition);
        
        // 如果operation是copy或move,则targetPath是必需的
        Map<String, Object> copyCondition = new HashMap<>();
        Map<String, Object> copyIf = new HashMap<>();
        copyIf.put("properties", Map.of("operation", Map.of("enum", Arrays.asList("copy", "move"))));
        Map<String, Object> copyThen = new HashMap<>();
        copyThen.put("required", Arrays.asList("targetPath"));
        copyCondition.put("if", copyIf);
        copyCondition.put("then", copyThen);
        allOf.add(copyCondition);
        
        schema.put("allOf", allOf);
        
        return schema;
    }
}

// 使用自定义验证器增强验证逻辑
public class FileOperationValidator extends JsonSchemaValidator {
    
    private final Set<String> allowedDirectories = Set.of(
        "/tmp/mcp/",
        "/var/mcp/data/",
        "/home/mcp/workspace/"
    );
    
    @Override
    public ValidationResult validate(Map<String, Object> parameters) {
        // 先执行基础JSON Schema验证
        ValidationResult baseResult = super.validate(parameters);
        if (!baseResult.isValid()) {
            return baseResult;
        }
        
        List<String> errors = new ArrayList<>();
        
        // 自定义验证:路径安全检查
        String path = (String) parameters.get("path");
        if (!isPathSafe(path)) {
            errors.add("文件路径不安全或不在允许的目录范围内");
        }
        
        // 自定义验证:内容大小检查(更精确)
        if (parameters.containsKey("content")) {
            String content = (String) parameters.get("content");
            if (content != null && content.getBytes(StandardCharsets.UTF_8).length > 1024 * 1024) {
                errors.add("文件内容超过1MB限制");
            }
        }
        
        // 自定义验证:操作权限检查
        String operation = (String) parameters.get("operation");
        if ("delete".equals(operation) && !hasDeletePermission(path)) {
            errors.add("没有删除该文件的权限");
        }
        
        return new ValidationResult(errors.isEmpty(), errors);
    }
    
    private boolean isPathSafe(String path) {
        try {
            Path normalizedPath = Paths.get(path).normalize();
            String normalizedStr = normalizedPath.toString();
            
            // 检查路径遍历攻击
            if (normalizedStr.contains("..")) {
                return false;
            }
            
            // 检查是否在允许的目录内
            return allowedDirectories.stream()
                .anyMatch(allowedDir -> normalizedStr.startsWith(allowedDir));
                
        } catch (Exception e) {
            return false;
        }
    }
    
    private boolean hasDeletePermission(String path) {
        // 实现删除权限检查逻辑
        // 这里可以检查文件所有者、权限位等
        return !path.contains("/system/") && !path.contains("/etc/");
    }
}

一致的返回结构 🔄

标准化的响应格式

一致的返回结构就像是统一的包装盒,让用户(AI模型)能够预期和处理结果。

python
# 🌟 标准化的响应结构设计
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Union
from enum import Enum
import json
from datetime import datetime

class ResponseStatus(Enum):
    SUCCESS = "success"
    PARTIAL_SUCCESS = "partial_success" 
    ERROR = "error"
    WARNING = "warning"

@dataclass
class ResponseMetadata:
    """响应元数据"""
    timestamp: str
    execution_time_ms: int
    request_id: str
    tool_version: str
    
    def to_dict(self) -> Dict[str, Any]:
        return {
            "timestamp": self.timestamp,
            "execution_time_ms": self.execution_time_ms,
            "request_id": self.request_id,
            "tool_version": self.tool_version
        }

@dataclass 
class ResponseError:
    """错误信息"""
    code: str
    message: str
    details: Optional[Dict[str, Any]] = None
    
    def to_dict(self) -> Dict[str, Any]:
        result = {
            "code": self.code,
            "message": self.message
        }
        if self.details:
            result["details"] = self.details
        return result

class StandardToolResponse:
    """标准化的工具响应"""
    
    def __init__(
        self, 
        status: ResponseStatus,
        data: Any = None,
        error: Optional[ResponseError] = None,
        warnings: List[str] = None,
        metadata: Optional[ResponseMetadata] = None
    ):
        self.status = status
        self.data = data
        self.error = error
        self.warnings = warnings or []
        self.metadata = metadata or self._generate_default_metadata()
    
    def _generate_default_metadata(self) -> ResponseMetadata:
        return ResponseMetadata(
            timestamp=datetime.utcnow().isoformat() + "Z",
            execution_time_ms=0,
            request_id="",
            tool_version="1.0.0"
        )
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式"""
        result = {
            "status": self.status.value,
            "metadata": self.metadata.to_dict()
        }
        
        if self.data is not None:
            result["data"] = self.data
            
        if self.error:
            result["error"] = self.error.to_dict()
            
        if self.warnings:
            result["warnings"] = self.warnings
            
        return result
    
    def to_json(self, indent: int = None) -> str:
        """转换为JSON字符串"""
        return json.dumps(self.to_dict(), indent=indent, ensure_ascii=False)
    
    @classmethod
    def success(
        cls, 
        data: Any, 
        warnings: List[str] = None,
        metadata: Optional[ResponseMetadata] = None
    ) -> 'StandardToolResponse':
        """创建成功响应"""
        return cls(
            status=ResponseStatus.SUCCESS,
            data=data,
            warnings=warnings,
            metadata=metadata
        )
    
    @classmethod
    def error(
        cls,
        error_code: str,
        error_message: str,
        error_details: Optional[Dict[str, Any]] = None,
        metadata: Optional[ResponseMetadata] = None
    ) -> 'StandardToolResponse':
        """创建错误响应"""
        return cls(
            status=ResponseStatus.ERROR,
            error=ResponseError(
                code=error_code,
                message=error_message,
                details=error_details
            ),
            metadata=metadata
        )
    
    @classmethod 
    def partial_success(
        cls,
        data: Any,
        warnings: List[str],
        metadata: Optional[ResponseMetadata] = None
    ) -> 'StandardToolResponse':
        """创建部分成功响应"""
        return cls(
            status=ResponseStatus.PARTIAL_SUCCESS,
            data=data,
            warnings=warnings,
            metadata=metadata
        )

# 具体工具实现示例
class WeatherTool:
    def __init__(self):
        self.tool_version = "2.1.0"
    
    async def execute(self, parameters: Dict[str, Any]) -> StandardToolResponse:
        start_time = datetime.now()
        request_id = self._generate_request_id()
        
        try:
            # 参数验证
            location = parameters.get('location')
            if not location:
                return StandardToolResponse.error(
                    error_code="MISSING_PARAMETER",
                    error_message="location参数是必需的",
                    metadata=self._create_metadata(start_time, request_id)
                )
            
            # 执行天气查询
            weather_data = await self._fetch_weather_data(location)
            
            # 检查数据完整性
            warnings = []
            if not weather_data.get('forecast'):
                warnings.append("未能获取预报数据,仅返回当前天气")
            
            # 格式化响应数据
            formatted_data = {
                "location": weather_data['location'],
                "current": {
                    "temperature": weather_data['current']['temp'],
                    "description": weather_data['current']['desc'],
                    "humidity": weather_data['current']['humidity'],
                    "wind_speed": weather_data['current']['wind_speed'],
                    "last_updated": weather_data['current']['updated_at']
                }
            }
            
            # 添加预报数据(如果有)
            if weather_data.get('forecast'):
                formatted_data["forecast"] = [
                    {
                        "date": day['date'],
                        "high_temp": day['high'],
                        "low_temp": day['low'],
                        "description": day['desc'],
                        "chance_of_rain": day.get('rain_chance', 0)
                    }
                    for day in weather_data['forecast']
                ]
            
            # 返回响应
            if warnings:
                return StandardToolResponse.partial_success(
                    data=formatted_data,
                    warnings=warnings,
                    metadata=self._create_metadata(start_time, request_id)
                )
            else:
                return StandardToolResponse.success(
                    data=formatted_data,
                    metadata=self._create_metadata(start_time, request_id)
                )
                
        except LocationNotFoundError as e:
            return StandardToolResponse.error(
                error_code="LOCATION_NOT_FOUND",
                error_message=f"未找到位置:{location}",
                error_details={"searched_location": location, "suggestions": e.suggestions},
                metadata=self._create_metadata(start_time, request_id)
            )
            
        except WeatherServiceError as e:
            return StandardToolResponse.error(
                error_code="SERVICE_UNAVAILABLE", 
                error_message="天气服务暂时不可用,请稍后重试",
                error_details={"service_error": str(e), "retry_after_seconds": 60},
                metadata=self._create_metadata(start_time, request_id)
            )
            
        except Exception as e:
            return StandardToolResponse.error(
                error_code="INTERNAL_ERROR",
                error_message="内部错误,请联系支持团队",
                error_details={"error_type": type(e).__name__},
                metadata=self._create_metadata(start_time, request_id)
            )
    
    def _create_metadata(self, start_time: datetime, request_id: str) -> ResponseMetadata:
        execution_time = (datetime.now() - start_time).total_seconds() * 1000
        return ResponseMetadata(
            timestamp=datetime.utcnow().isoformat() + "Z",
            execution_time_ms=int(execution_time),
            request_id=request_id,
            tool_version=self.tool_version
        )
    
    def _generate_request_id(self) -> str:
        import uuid
        return str(uuid.uuid4())

# 使用示例
async def demo_weather_tool():
    weather_tool = WeatherTool()
    
    # 成功情况
    response = await weather_tool.execute({"location": "北京"})
    print("成功响应:")
    print(response.to_json(indent=2))
    
    # 错误情况  
    response = await weather_tool.execute({})  # 缺少location参数
    print("\n错误响应:")
    print(response.to_json(indent=2))
    
    # 部分成功情况
    response = await weather_tool.execute({"location": "偏远小镇"})  # 只有当前天气,没有预报
    print("\n部分成功响应:")
    print(response.to_json(indent=2))

# 输出示例:
"""
成功响应:
{
  "status": "success",
  "data": {
    "location": "北京",
    "current": {
      "temperature": 15,
      "description": "晴朗",
      "humidity": 45,
      "wind_speed": 12,
      "last_updated": "2024-03-15T14:30:00Z"
    },
    "forecast": [
      {
        "date": "2024-03-16",
        "high_temp": 18,
        "low_temp": 8,
        "description": "多云",
        "chance_of_rain": 20
      }
    ]
  },
  "metadata": {
    "timestamp": "2024-03-15T14:30:15Z",
    "execution_time_ms": 245,
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "tool_version": "2.1.0"
  }
}

错误响应:
{
  "status": "error",
  "error": {
    "code": "MISSING_PARAMETER",
    "message": "location参数是必需的"
  },
  "metadata": {
    "timestamp": "2024-03-15T14:30:16Z",
    "execution_time_ms": 2,
    "request_id": "660f9511-f3ac-52e5-b827-556766551111",
    "tool_version": "2.1.0"
  }
}
"""

Schema版本控制 📈

优雅地处理Schema演进

Schema版本控制就像是软件版本管理,需要考虑向前兼容和平滑升级。

typescript
// 🌟 Schema版本控制实现
interface SchemaVersion {
  version: string;
  releaseDate: string;
  changes: string[];
  deprecated?: string[];
  breaking?: boolean;
}

class VersionedSchema {
  private schemas: Map<string, any> = new Map();
  private versions: SchemaVersion[] = [];
  private currentVersion: string;
  
  constructor(currentVersion: string) {
    this.currentVersion = currentVersion;
  }
  
  // 注册Schema版本
  registerVersion(version: string, schema: any, versionInfo: SchemaVersion) {
    this.schemas.set(version, schema);
    this.versions.push(versionInfo);
    this.versions.sort((a, b) => this.compareVersions(a.version, b.version));
  }
  
  // 获取Schema(支持版本请求)
  getSchema(requestedVersion?: string): any {
    const version = requestedVersion || this.currentVersion;
    
    if (!this.schemas.has(version)) {
      throw new Error(`Schema版本 ${version} 不存在`);
    }
    
    const schema = this.schemas.get(version);
    
    // 添加版本信息到Schema
    return {
      ...schema,
      $schema: "http://json-schema.org/draft-07/schema#",
      $version: version,
      $versionInfo: this.getVersionInfo(version)
    };
  }
  
  // 验证参数(自动处理版本兼容性)
  async validateParameters(
    parameters: any, 
    requestedVersion?: string
  ): Promise<{
    isValid: boolean;
    errors: string[];
    warnings: string[];
    migratedParameters?: any;
  }> {
    const version = requestedVersion || this.currentVersion;
    const schema = this.getSchema(version);
    
    // 基础验证
    const validationResult = this.validateAgainstSchema(parameters, schema);
    
    if (!validationResult.isValid) {
      return validationResult;
    }
    
    // 版本迁移检查
    if (version !== this.currentVersion) {
      const migrationResult = await this.migrateParameters(
        parameters, 
        version, 
        this.currentVersion
      );
      
      return {
        ...validationResult,
        warnings: [
          ...validationResult.warnings,
          `参数已从版本 ${version} 迁移到 ${this.currentVersion}`,
          ...migrationResult.warnings
        ],
        migratedParameters: migrationResult.parameters
      };
    }
    
    return validationResult;
  }
  
  // 参数迁移
  private async migrateParameters(
    parameters: any,
    fromVersion: string,
    toVersion: string
  ): Promise<{
    parameters: any;
    warnings: string[];
  }> {
    const warnings: string[] = [];
    let migratedParams = { ...parameters };
    
    // 获取版本间的迁移路径
    const migrationPath = this.getMigrationPath(fromVersion, toVersion);
    
    for (const migration of migrationPath) {
      const result = await this.applyMigration(migratedParams, migration);
      migratedParams = result.parameters;
      warnings.push(...result.warnings);
    }
    
    return { parameters: migratedParams, warnings };
  }
  
  private compareVersions(v1: string, v2: string): number {
    const parts1 = v1.split('.').map(Number);
    const parts2 = v2.split('.').map(Number);
    
    for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
      const part1 = parts1[i] || 0;
      const part2 = parts2[i] || 0;
      
      if (part1 !== part2) {
        return part1 - part2;
      }
    }
    
    return 0;
  }
}

// 实际使用示例:搜索工具的Schema演进
class SearchToolSchema extends VersionedSchema {
  constructor() {
    super("2.1.0");
    this.initializeVersions();
  }
  
  private initializeVersions() {
    // 版本 1.0.0 - 初始版本
    this.registerVersion("1.0.0", {
      type: "object",
      properties: {
        query: { 
          type: "string",
          description: "搜索关键词"  
        },
        limit: {
          type: "integer",
          minimum: 1,
          maximum: 50,
          default: 10
        }
      },
      required: ["query"]
    }, {
      version: "1.0.0",
      releaseDate: "2024-01-01",
      changes: ["初始发布"]
    });
    
    // 版本 1.1.0 - 添加过滤功能
    this.registerVersion("1.1.0", {
      type: "object", 
      properties: {
        query: {
          type: "string",
          description: "搜索关键词",
          minLength: 1,
          maxLength: 200
        },
        limit: {
          type: "integer",
          minimum: 1,
          maximum: 50,
          default: 10
        },
        filters: {
          type: "object",
          description: "搜索过滤条件",
          properties: {
            category: { type: "string" },
            dateRange: { type: "string" }
          }
        }
      },
      required: ["query"]
    }, {
      version: "1.1.0", 
      releaseDate: "2024-02-01",
      changes: [
        "添加filters参数支持类别和日期范围过滤",
        "为query参数添加长度限制"
      ]
    });
    
    // 版本 2.0.0 - 重大更新
    this.registerVersion("2.0.0", {
      type: "object",
      properties: {
        searchTerms: {  // 重命名:query -> searchTerms
          type: "string",
          description: "搜索关键词或短语",
          minLength: 1,
          maxLength: 500
        },
        maxResults: {  // 重命名:limit -> maxResults  
          type: "integer",
          minimum: 1,
          maximum: 100,
          default: 20
        },
        filterCriteria: {  // 重命名:filters -> filterCriteria
          type: "object",
          description: "高级搜索过滤条件",
          properties: {
            category: { 
              type: "string",
              enum: ["all", "products", "articles", "images", "videos"]
            },
            timeRange: {  // 重命名:dateRange -> timeRange
              type: "string",
              enum: ["any", "day", "week", "month", "year"]
            },
            language: {  // 新增
              type: "string",
              default: "auto"
            }
          }
        },
        sortBy: {  // 新增
          type: "string",
          enum: ["relevance", "date", "popularity"],
          default: "relevance"
        }
      },
      required: ["searchTerms"]
    }, {
      version: "2.0.0",
      releaseDate: "2024-03-01", 
      changes: [
        "重命名query为searchTerms以更清晰",
        "重命名limit为maxResults",
        "重构filters为filterCriteria",
        "添加language和sortBy参数",
        "提高maxResults上限到100"
      ],
      deprecated: ["query", "limit", "filters"],
      breaking: true
    });
    
    // 版本 2.1.0 - 当前版本
    this.registerVersion("2.1.0", {
      type: "object",
      properties: {
        searchTerms: {
          type: "string", 
          description: "搜索关键词或短语。支持布尔操作符(AND, OR, NOT)",
          minLength: 1,
          maxLength: 500,
          examples: [
            "人工智能",
            "机器学习 AND 深度学习", 
            "Python OR JavaScript"
          ]
        },
        maxResults: {
          type: "integer",
          minimum: 1,
          maximum: 100,
          default: 20
        },
        filterCriteria: {
          type: "object",
          properties: {
            category: {
              type: "string",
              enum: ["all", "products", "articles", "images", "videos", "news"],  // 新增news
              default: "all"
            },
            timeRange: {
              type: "string", 
              enum: ["any", "hour", "day", "week", "month", "year"],  // 新增hour
              default: "any"
            },
            language: {
              type: "string",
              enum: ["auto", "zh", "en", "ja", "ko"],  // 扩展语言支持
              default: "auto"
            },
            region: {  // 新增
              type: "string",
              description: "搜索地区限制",
              examples: ["CN", "US", "JP"]
            }
          }
        },
        sortBy: {
          type: "string",
          enum: ["relevance", "date", "popularity", "rating"],  // 新增rating
          default: "relevance"
        },
        includeMetadata: {  // 新增
          type: "boolean",
          description: "是否包含搜索元数据",
          default: false
        }
      },
      required: ["searchTerms"]
    }, {
      version: "2.1.0",
      releaseDate: "2024-04-01",
      changes: [
        "searchTerms支持布尔操作符",
        "category新增news选项",
        "timeRange新增hour选项", 
        "扩展language支持更多语言",
        "新增region和includeMetadata参数",
        "sortBy新增rating选项"
      ]
    });
  }
}

// 使用示例
const searchSchema = new SearchToolSchema();

// 获取最新Schema
const latestSchema = searchSchema.getSchema();
console.log("最新Schema:", JSON.stringify(latestSchema, null, 2));

// 验证旧版本参数
const oldParams = {
  query: "人工智能",  // 旧参数名
  limit: 15,         // 旧参数名
  filters: {         // 旧参数名
    category: "articles"
  }
};

const validationResult = await searchSchema.validateParameters(oldParams, "1.1.0");
console.log("验证结果:", validationResult);

小结

Schema设计的艺术在于平衡几个关键要素:

🎨 设计原则

  1. 清晰描述 - 用户友好的参数说明和示例
  2. 合理约束 - 多层次的验证保护系统安全
  3. 一致结构 - 标准化的响应格式提升用户体验
  4. 版本控制 - 优雅地处理Schema演进和兼容性

💡 实践要点

  • 站在用户角度思考Schema设计
  • 提供充分的示例和文档
  • 实施全面的参数验证
  • 建立标准化的响应格式
  • 规划好版本升级策略

🎯 设计哲学:好的Schema不是约束,而是指导。它应该让用户(AI模型)更容易、更准确地使用你的工具。


下一节错误处理策略 - 学习如何优雅地处理各种异常情况