Skip to content
清晨的一缕阳光
返回

OpenSpec 规范体系详解

OpenSpec 规范体系详解

OpenSpec 是一套开放的 AI 应用规范体系。如何定义标准化的接口?如何管理规范版本?本文详解 OpenSpec 规范体系的设计与实施。

一、OpenSpec 概述

1.1 规范体系

OpenSpec 规范体系:

┌─────────────────────────────────────┐
│ 1. 接口规范                          │
│    - API 设计原则                    │
│    - 请求/响应格式                   │
│    - 错误处理规范                    │
├─────────────────────────────────────┤
│ 2. 数据规范                          │
│    - 数据模型定义                    │
│    - 数据格式标准                    │
│    - 数据交换协议                    │
├─────────────────────────────────────┤
│ 3. 文档规范                          │
│    - API 文档标准                    │
│    - 代码注释规范                    │
│    - 用户文档模板                    │
├─────────────────────────────────────┤
│ 4. 版本规范                          │
│    - 版本号规则                      │
│    - 变更管理流程                    │
│    - 兼容性保证                      │
└─────────────────────────────────────┘

1.2 核心价值

价值说明收益
标准化统一接口设计降低集成成本
互操作性开放数据格式促进系统协作
可维护性清晰文档规范提升维护效率
可扩展性版本管理机制支持平滑演进

二、接口规范

2.1 API 设计原则

# openspec_api_guidelines.yaml
# OpenSpec API 设计指南

api_design_principles:
  # 1. RESTful 设计
  restful:
    use_http_methods_correctly: true
    use_nouns_not_verbs: true
    use_plural_nouns: true
    version_in_url: true
  
  # 2. 资源命名
  resource_naming:
    format: lowercase-hyphenated
    example: "user-profiles"
    avoid: ["camelCase", "snake_case"]
  
  # 3. 响应格式
  response_format:
    content_type: "application/json"
    envelope: true
    envelope_structure:
      data: "响应数据"
      meta: "元数据"
      error: "错误信息"
  
  # 4. 错误处理
  error_handling:
    use_http_status_codes: true
    provide_error_codes: true
    include_error_messages: true
    include_documentation_url: true

# 标准响应示例
standard_response:
  success:
    status: 200
    body:
      data:
        id: "string"
        type: "string"
        attributes: {}
      meta:
        request_id: "string"
        timestamp: "ISO8601"
  
  error:
    status: 400/401/403/404/500
    body:
      error:
        code: "ERROR_CODE"
        message: "人类可读的错误消息"
        details: {}
        documentation_url: "https://..."

2.2 接口定义

# user_api_spec.yaml
# OpenSpec 用户 API 规范

openapi: 3.0.0
info:
  title: User API
  version: 1.0.0
  description: 用户管理接口规范

paths:
  /users:
    get:
      summary: 获取用户列表
      operationId: listUsers
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserListResponse'
    
    post:
      summary: 创建用户
      operationId: createUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: 创建成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'

components:
  schemas:
    User:
      type: object
      required:
        - id
        - name
        - email
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        email:
          type: string
          format: email
        created_at:
          type: string
          format: date-time
    
    UserListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/User'
        meta:
          type: object
          properties:
            total:
              type: integer
            limit:
              type: integer
            offset:
              type: integer
    
    CreateUserRequest:
      type: object
      required:
        - name
        - email
      properties:
        name:
          type: string
        email:
          type: string
          format: email

三、数据规范

3.1 数据模型

# data_models.py
from dataclasses import dataclass, field
from typing import List, Optional, Dict, Any
from datetime import datetime
from enum import Enum

class DataType(Enum):
    """数据类型"""
    STRING = "string"
    INTEGER = "integer"
    FLOAT = "float"
    BOOLEAN = "boolean"
    DATETIME = "datetime"
    OBJECT = "object"
    ARRAY = "array"

@dataclass
class FieldDefinition:
    """字段定义"""
    name: str
    data_type: DataType
    required: bool = True
    description: str = ""
    example: Any = None
    constraints: Dict = field(default_factory=dict)

@dataclass
class DataModel:
    """数据模型"""
    name: str
    version: str
    description: str
    fields: List[FieldDefinition]
    
    def to_openapi_schema(self) -> Dict:
        """转换为 OpenAPI Schema"""
        schema = {
            'type': 'object',
            'properties': {},
            'required': []
        }
        
        for field_def in self.fields:
            schema['properties'][field_def.name] = {
                'type': field_def.data_type.value,
                'description': field_def.description
            }
            
            if field_def.example:
                schema['properties'][field_def.name]['example'] = field_def.example
            
            if field_def.required:
                schema['required'].append(field_def.name)
        
        return schema

# 示例:用户模型
UserModel = DataModel(
    name='User',
    version='1.0.0',
    description='用户数据模型',
    fields=[
        FieldDefinition(
            name='id',
            data_type=DataType.STRING,
            description='用户 ID',
            example='550e8400-e29b-41d4-a716-446655440000'
        ),
        FieldDefinition(
            name='name',
            data_type=DataType.STRING,
            description='用户名称',
            example='张三'
        ),
        FieldDefinition(
            name='email',
            data_type=DataType.STRING,
            description='用户邮箱',
            example='user@example.com',
            constraints={'format': 'email'}
        ),
        FieldDefinition(
            name='created_at',
            data_type=DataType.DATETIME,
            description='创建时间',
            required=False
        )
    ]
)

3.2 数据交换格式

# data_exchange.py
from typing import Dict, Any, List
import json
from datetime import datetime, date
from decimal import Decimal

class DataExchangeFormat:
    """数据交换格式"""
    
    @staticmethod
    def to_standard_json(data: Any) -> str:
        """转换为标准 JSON"""
        return json.dumps(
            data,
            cls=StandardJSONEncoder,
            ensure_ascii=False,
            indent=2
        )
    
    @staticmethod
    def from_standard_json(json_str: str) -> Any:
        """从标准 JSON 解析"""
        return json.loads(json_str, object_hook=standard_json_decoder)
    
    @staticmethod
    def validate_against_schema(
        data: Dict,
        schema: Dict
    ) -> Dict:
        """验证数据是否符合 Schema"""
        import jsonschema
        
        try:
            jsonschema.validate(data, schema)
            return {'valid': True, 'errors': []}
        except jsonschema.ValidationError as e:
            return {
                'valid': False,
                'errors': [str(e)]
            }

class StandardJSONEncoder(json.JSONEncoder):
    """标准 JSON 编码器"""
    
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        elif isinstance(obj, date):
            return obj.isoformat()
        elif isinstance(obj, Decimal):
            return float(obj)
        elif isinstance(obj, bytes):
            return obj.decode('utf-8')
        return super().default(obj)

def standard_json_decoder(dct: Dict) -> Dict:
    """标准 JSON 解码器"""
    for key, value in dct.items():
        # 尝试解析 ISO 日期时间
        if isinstance(value, str):
            try:
                if 'T' in value:
                    dct[key] = datetime.fromisoformat(value)
                elif value.count('-') == 2:
                    dct[key] = date.fromisoformat(value)
            except ValueError:
                pass
    return dct

四、文档规范

4.1 API 文档标准

# API 文档模板

## 接口概述

简要描述接口的用途和适用场景。

## 基础信息

- **版本**: 1.0.0
- **基础 URL**: https://api.example.com/v1
- **认证方式**: Bearer Token

## 接口列表

### 获取用户列表

**请求**

```http
GET /users
Authorization: Bearer {token}

请求参数

参数类型必填说明示例
limitinteger每页数量20
offsetinteger偏移量0

响应示例

{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "张三",
      "email": "user@example.com"
    }
  ],
  "meta": {
    "total": 100,
    "limit": 20,
    "offset": 0
  }
}

错误码

错误码说明解决方案
INVALID_TOKEN无效的 Token重新获取 Token
PERMISSION_DENIED权限不足申请相应权限

### 4.2 代码注释规范

```python
# code_comment_standards.py
"""
代码注释规范模块

本模块定义代码注释的标准格式和要求。
"""

from typing import Dict, List, Optional


class APIEndpoint:
    """
    API 端点类
    
    用于定义和管理 API 端点。
    
    Attributes:
        path (str): 端点路径
        method (str): HTTP 方法
        description (str): 端点描述
    
    Example:
        >>> endpoint = APIEndpoint('/users', 'GET', '获取用户列表')
        >>> endpoint.path
        '/users'
    """
    
    def __init__(
        self,
        path: str,
        method: str,
        description: str
    ):
        """
        初始化 API 端点
        
        Args:
            path: 端点路径,必须以 '/' 开头
            method: HTTP 方法,如 GET、POST 等
            description: 端点描述
        
        Raises:
            ValueError: 当路径格式不正确时
        
        Example:
            >>> endpoint = APIEndpoint('/users', 'GET', '获取用户列表')
        """
        if not path.startswith('/'):
            raise ValueError("Path must start with '/'")
        
        self.path = path
        self.method = method.upper()
        self.description = description
    
    def execute(self, **kwargs) -> Dict:
        """
        执行端点请求
        
        Args:
            **kwargs: 请求参数
        
        Returns:
            响应数据字典
        
        Raises:
            APIError: 当请求失败时
        
        Example:
            >>> result = endpoint.execute(limit=20)
            >>> print(result['data'])
        """
        pass

五、版本管理

5.1 版本号规则

# versioning.py
from typing import Dict, List
from dataclasses import dataclass

@dataclass
class SemanticVersion:
    """语义化版本号"""
    major: int  # 主版本号:不兼容的变更
    minor: int  # 次版本号:向后兼容的功能
    patch: int  # 修订号:向后兼容的问题修复
    
    def __str__(self) -> str:
        return f"{self.major}.{self.minor}.{self.patch}"
    
    def increment_major(self) -> 'SemanticVersion':
        """增加主版本号"""
        return SemanticVersion(self.major + 1, 0, 0)
    
    def increment_minor(self) -> 'SemanticVersion':
        """增加次版本号"""
        return SemanticVersion(self.major, self.minor + 1, 0)
    
    def increment_patch(self) -> 'SemanticVersion':
        """增加修订号"""
        return SemanticVersion(self.major, self.minor, self.patch + 1)
    
    def is_compatible_with(self, other: 'SemanticVersion') -> bool:
        """检查是否兼容"""
        # 主版本号相同则兼容
        return self.major == other.major

class VersionManager:
    """版本管理器"""
    
    def __init__(self):
        self.versions: List[SemanticVersion] = []
        self.changelogs: Dict[str, Dict] = {}
    
    def create_version(
        self,
        version: SemanticVersion,
        changes: Dict
    ):
        """创建新版本"""
        self.versions.append(version)
        self.changelogs[str(version)] = changes
    
    def get_changelog(self, version: str) -> Dict:
        """获取变更日志"""
        return self.changelogs.get(version, {})
    
    def check_compatibility(
        self,
        client_version: SemanticVersion,
        server_version: SemanticVersion
    ) -> Dict:
        """检查兼容性"""
        return {
            'compatible': client_version.is_compatible_with(server_version),
            'client_version': str(client_version),
            'server_version': str(server_version),
            'upgrade_required': not client_version.is_compatible_with(server_version)
        }

# 变更日志模板
CHANGELOG_TEMPLATE = """
# 变更日志 - {version}

## 发布日期
{date}

## 变更类型

### Added(新增)
- 新增的功能

### Changed(变更)
- 对现有功能的变更

### Deprecated(弃用)
- 即将移除的功能

### Removed(移除)
- 已移除的功能

### Fixed(修复)
- 修复的问题

### Security(安全)
- 安全相关的修复
"""

5.2 变更管理

# change_management.py
from typing import Dict, List
from enum import Enum

class ChangeType(Enum):
    """变更类型"""
    ADDED = "Added"
    CHANGED = "Changed"
    DEPRECATED = "Deprecated"
    REMOVED = "Removed"
    FIXED = "Fixed"
    SECURITY = "Security"

class ChangeManager:
    """变更管理器"""
    
    def __init__(self):
        self.pending_changes: List[Dict] = []
        self.released_versions: List[Dict] = []
    
    def propose_change(
        self,
        type: ChangeType,
        description: str,
        breaking: bool = False
    ):
        """提议变更"""
        self.pending_changes.append({
            'type': type.value,
            'description': description,
            'breaking': breaking
        })
    
    def release_version(
        self,
        version: str,
        force: bool = False
    ) -> Dict:
        """发布版本"""
        if not self.pending_changes and not force:
            return {
                'success': False,
                'error': 'No pending changes'
            }
        
        # 检查是否有破坏性变更
        breaking_changes = [
            c for c in self.pending_changes
            if c.get('breaking', False)
        ]
        
        release = {
            'version': version,
            'changes': self.pending_changes.copy(),
            'breaking_changes': breaking_changes,
            'change_count': len(self.pending_changes)
        }
        
        self.released_versions.append(release)
        self.pending_changes = []
        
        return {
            'success': True,
            'release': release
        }
    
    def get_version_impact(
        self,
        changes: List[Dict]
    ) -> Dict:
        """评估版本影响"""
        has_breaking = any(c.get('breaking', False) for c in changes)
        has_features = any(c['type'] == ChangeType.ADDED.value for c in changes)
        has_fixes = any(c['type'] == ChangeType.FIXED.value for c in changes)
        
        if has_breaking:
            version_type = 'major'
        elif has_features:
            version_type = 'minor'
        else:
            version_type = 'patch'
        
        return {
            'version_type': version_type,
            'has_breaking_changes': has_breaking,
            'has_new_features': has_features,
            'has_bug_fixes': has_fixes
        }

六、总结

6.1 核心要点

  1. 接口规范

    • RESTful 设计
    • 统一响应格式
    • 标准错误处理
  2. 数据规范

    • 标准化数据模型
    • 统一交换格式
    • Schema 验证
  3. 文档规范

    • API 文档模板
    • 代码注释标准
    • 用户文档规范
  4. 版本管理

    • 语义化版本号
    • 变更日志
    • 兼容性保证

6.2 实施建议

  1. 渐进式采用

    • 从新接口开始
    • 逐步迁移旧接口
    • 提供过渡期
  2. 工具支持

    • OpenAPI 生成
    • 自动文档
    • Schema 验证
  3. 持续改进

    • 收集反馈
    • 定期审查
    • 更新规范

参考资料


分享这篇文章到:

上一篇文章
RocketMQ 最佳实践与生产经验总结
下一篇文章
Spring Boot 系列完整学习指南