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}
请求参数
| 参数 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| limit | integer | 否 | 每页数量 | 20 |
| offset | integer | 否 | 偏移量 | 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 核心要点
-
接口规范
- RESTful 设计
- 统一响应格式
- 标准错误处理
-
数据规范
- 标准化数据模型
- 统一交换格式
- Schema 验证
-
文档规范
- API 文档模板
- 代码注释标准
- 用户文档规范
-
版本管理
- 语义化版本号
- 变更日志
- 兼容性保证
6.2 实施建议
-
渐进式采用
- 从新接口开始
- 逐步迁移旧接口
- 提供过渡期
-
工具支持
- OpenAPI 生成
- 自动文档
- Schema 验证
-
持续改进
- 收集反馈
- 定期审查
- 更新规范
参考资料