ساخت SDK و API Client با هوش مصنوعی؛ آموزش تولید کلاینت Python و TypeScript از OpenAPI
در این آموزش یاد میگیرید چگونه از فایل OpenAPI، یک SDK واقعی برای Python و TypeScript بسازید و با API هوش مصنوعی درواره، مستندات، مثالها و تستهای آن را تولید و ارزیابی کنید.
ساخت یک API فقط نیمی از مسیر توسعه محصول است. اگر کاربران نتوانند بهسادگی آن را در برنامههای Python، JavaScript، TypeScript یا زبانهای دیگر استفاده کنند، حتی بهترین API نیز نرخ پذیرش بالایی نخواهد داشت.
یکی از راههای کاهش پیچیدگی استفاده از API، ارائه یک SDK یا API Client رسمی است. بهجای اینکه توسعهدهنده برای هر درخواست HTTP هدرها، پارامترها، مدیریت خطا و تبدیل پاسخ را دستی پیادهسازی کند، میتواند از کدی شبیه نمونه زیر استفاده کند:
from acme_sdk import AcmeClient
client = AcmeClient(api_key="YOUR_API_KEY")
user = client.users.get_user(user_id="usr_123")
print(user.name)
در گذشته، ساخت چنین SDKهایی به پیادهسازی دستی تعداد زیادی کلاس، مدل داده، متد، تست و صفحه مستندات نیاز داشت. اکنون میتوان بخش قطعی کار را با OpenAPI Generator و بخش معنایی آن را با هوش مصنوعی انجام داد.
در این مقاله یک فرایند عملی میسازیم که:
- مشخصات API را از فایل OpenAPI میخواند.
- برای Python و TypeScript کلاینت تولید میکند.
- با کمک API هوش مصنوعی درواره کیفیت توضیحات API را ارزیابی میکند.
- برای عملیات مختلف مثال کاربردی میسازد.
- موارد مشکوک و اطلاعات ناقص را شناسایی میکند.
- تستهای SDK را اجرا میکند.
- از ساختهشدن Endpointهای خیالی توسط مدل جلوگیری میکند.
- برای استفاده در CI/CD آماده است.
SDK چیست؟
SDK مخفف Software Development Kit است و به مجموعهای از ابزارها، کلاسها، مدلها، مستندات و مثالها گفته میشود که استفاده از یک سرویس را برای برنامهنویسان ساده میکند.
یک SDK مربوط به API معمولاً شامل اجزای زیر است:
- کلاینت HTTP
- مدیریت احراز هویت
- متدهای متناظر با Endpointها
- مدلهای درخواست و پاسخ
- مدیریت خطا
- پشتیبانی از Pagination
- Timeout و Retry
- مستندات نصب و استفاده
- مثالهای اجرایی
- تستهای واحد و یکپارچه
- شماره نسخه و Changelog
برای مثال، بهجای ارسال مستقیم درخواست زیر:
import requests
response = requests.get(
"https://api.example.com/v1/users/usr_123",
headers={
"Authorization": "Bearer YOUR_API_KEY"
},
timeout=30
)
response.raise_for_status()
user = response.json()
کاربر SDK میتواند بنویسد:
user = client.users.get_user("usr_123")
این تفاوت ظاهراً کوچک، در یک API بزرگ با دهها Endpoint بسیار مهم است.
API Client چه تفاوتی با SDK دارد؟
API Client معمولاً فقط ارتباط با API را مدیریت میکند؛ اما SDK مفهوم گستردهتری دارد.
| قابلیت | API Client | SDK |
|---|---|---|
| ارسال درخواست HTTP | بله | بله |
| مدیریت احراز هویت | معمولاً | بله |
| مدلهای Type-safe | گاهی | بله |
| مدیریت خطای اختصاصی | محدود | بله |
| مستندات کامل | ضروری نیست | بله |
| مثالهای کاربردی | محدود | بله |
| ابزارهای کمکی | معمولاً خیر | بله |
| تست و نسخهبندی مستقل | گاهی | بله |
در پروژه کوچک ممکن است یک API Client ساده کافی باشد. برای API عمومی، تجاری یا سازمانی، ساخت SDK انتخاب حرفهایتری است.
چرا OpenAPI بهترین نقطه شروع است؟
OpenAPI یک استاندارد مستقل از زبان برنامهنویسی برای توصیف HTTP API است. در یک سند OpenAPI میتوان مسیرها، پارامترها، بدنه درخواست، ساختار پاسخها، سرورها و روشهای احراز هویت را تعریف کرد.
طبق مشخصات رسمی OpenAPI، ابزارها میتوانند از این سند برای تولید مستندات، کلاینت، سرور و تست استفاده کنند.
یک فایل ساده OpenAPI ممکن است چنین ساختاری داشته باشد:
openapi: 3.1.0
info:
title: Task Management API
version: 1.0.0
description: API for creating and managing project tasks
servers:
- url: https://api.example.com/v1
paths:
/tasks:
get:
operationId: listTasks
summary: List project tasks
parameters:
- name: status
in: query
required: false
schema:
type: string
enum:
- pending
- completed
responses:
"200":
description: Task list
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Task"
post:
operationId: createTask
summary: Create a new task
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateTaskRequest"
responses:
"201":
description: Task created successfully
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
/tasks/{taskId}:
get:
operationId: getTask
summary: Get task by ID
parameters:
- name: taskId
in: path
required: true
schema:
type: string
responses:
"200":
description: Task details
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
"404":
description: Task not found
components:
schemas:
Task:
type: object
required:
- id
- title
- status
properties:
id:
type: string
example: task_123
title:
type: string
example: Prepare monthly report
status:
type: string
enum:
- pending
- completed
description:
type:
- string
- "null"
CreateTaskRequest:
type: object
required:
- title
properties:
title:
type: string
minLength: 3
maxLength: 200
description:
type:
- string
- "null"
این فایل منبع حقیقت یا Source of Truth پروژه خواهد بود.
چرا نباید تولید کامل SDK را فقط به مدل هوش مصنوعی سپرد؟
مدلهای زبانی در درک معنا، تولید توضیح، ساخت مثال و شناسایی ابهام بسیار خوب هستند؛ اما تولید مکانیکی SDK از یک Schema باید تا حد امکان قطعی و تکرارپذیر باشد.
اگر کل SDK را فقط با یک Prompt تولید کنیم، ممکن است مشکلات زیر رخ دهد:
- مدل یک Endpoint غیرواقعی ایجاد کند.
- نام پارامتر را تغییر دهد.
- نوع داده را اشتباه تشخیص دهد.
- یک Status Code را نادیده بگیرد.
- بخشی از فایل OpenAPI را بهدلیل محدودیت Context حذف کند.
- در دو اجرای مختلف، ساختار متفاوتی بسازد.
- هنگام بهروزرسانی API، Diff بسیار بزرگی ایجاد کند.
معماری بهتر این است:
- تولید ساختاری SDK با یک ابزار قطعی انجام شود.
- هوش مصنوعی توضیحات، مثالها و تحلیل کیفی را تولید کند.
- خروجی مدل با OpenAPI اصلی اعتبارسنجی شود.
- تستهای خودکار صحت SDK را بررسی کنند.
- انتشار فقط پس از عبور از کنترل کیفیت انجام شود.
هوش مصنوعی در این معماری جایگزین OpenAPI Generator نیست؛ بلکه لایه هوشمند روی فرایند تولید و نگهداری SDK است.
معماری پروژه
ساختار پیشنهادی پروژه بهشکل زیر است:
sdk-builder/
├── openapi.yaml
├── requirements.txt
├── .env
├── scripts/
│ ├── analyze_openapi.py
│ ├── generate_ai_docs.py
│ └── validate_ai_output.py
├── generated/
│ ├── python/
│ └── typescript/
├── ai-output/
│ ├── api-review.json
│ ├── examples.json
│ └── missing-descriptions.json
└── tests/
└── test_openapi_quality.py
پیشنیازها
برای اجرای این پروژه به ابزارهای زیر نیاز دارید:
- Python 3.11 یا جدیدتر
- Node.js و npm برای اجرای OpenAPI Generator
- یک فایل OpenAPI با فرمت YAML یا JSON
- کلید API درواره
- دسترسی به یک مدل مناسب تولید متن و کد
برای مشاهده مدلهای قابل استفاده و انتخاب مدل مناسب، صفحه مدلهای درواره را بررسی کنید. قیمت و مشخصات مدلها ممکن است تغییر کند؛ بنابراین بهتر است اطلاعات مدل را از همین صفحه دریافت کنید.
مرحله اول: ساخت محیط پروژه
پوشههای پروژه را ایجاد کنید:
mkdir sdk-builder
cd sdk-builder
mkdir scripts
mkdir generated
mkdir ai-output
mkdir tests
python -m venv .venv
در Linux و macOS محیط را فعال کنید:
source .venv/bin/activate
در Windows PowerShell:
.venv\Scripts\Activate.ps1
فایل requirements.txt را بسازید:
openai
python-dotenv
PyYAML
jsonschema
pytest
وابستگیها را نصب کنید:
pip install -r requirements.txt
OpenAPI Generator را نیز نصب کنید:
npm install --save-dev @openapitools/openapi-generator-cli
بهتر است نسخه وابستگیها را در پروژه واقعی Pin کنید تا خروجی ساخت SDK بین محیط توسعه و CI تغییر ناگهانی نداشته باشد.
مرحله دوم: دریافت کلید API درواره
برای استفاده از مدل هوش مصنوعی:
- وارد وبسایت درواره شوید.
- حساب کاربری ایجاد کنید.
- کلید API بسازید.
- یک مدل متناسب با نیاز پروژه انتخاب کنید.
- کلید و Model ID را در متغیرهای محیطی قرار دهید.
فایل .env:
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL=MODEL_ID_DARVAREH
فایل .env را وارد مخزن Git نکنید.
.env
.venv/
__pycache__/
.pytest_cache/
node_modules/
در محیط Production بهتر است کلید API در Secret Manager یا تنظیمات امن پلتفرم استقرار نگهداری شود.
مرحله سوم: تولید Python SDK
برای تولید کلاینت Python دستور زیر را اجرا کنید:
npx openapi-generator-cli generate \
-i openapi.yaml \
-g python \
-o generated/python \
--additional-properties=packageName=task_sdk,projectName=task-sdk
گزینهها:
-iمسیر فایل ورودی OpenAPI است.-gنوع Generator را مشخص میکند.-oپوشه خروجی است.packageNameنام پکیج Python را تعیین میکند.projectNameنام پروژه قابل انتشار را تعیین میکند.
پس از تولید، ساختار خروجی تقریباً چنین خواهد بود:
generated/python/
├── task_sdk/
│ ├── api/
│ ├── models/
│ ├── api_client.py
│ ├── configuration.py
│ └── exceptions.py
├── test/
├── docs/
├── pyproject.toml
└── README.md
SDK را در حالت توسعه نصب کنید:
pip install -e generated/python
اکنون میتوانید کلاینت تولیدشده را در Python وارد کنید. نام دقیق کلاسها به عنوانها، Tagها و operationIdهای فایل OpenAPI بستگی دارد.
نمونه کلی:
import task_sdk
from task_sdk.api.tasks_api import TasksApi
from task_sdk.api_client import ApiClient
from task_sdk.configuration import Configuration
configuration = Configuration(
host="https://api.example.com/v1"
)
configuration.api_key["ApiKeyAuth"] = "YOUR_API_KEY"
configuration.api_key_prefix["ApiKeyAuth"] = "Bearer"
with ApiClient(configuration) as api_client:
tasks_api = TasksApi(api_client)
tasks = tasks_api.list_tasks(status="pending")
for task in tasks:
print(task.id, task.title, task.status)
نام ApiKeyAuth باید با نام تعریفشده در بخش securitySchemes فایل OpenAPI یکسان باشد.
مرحله چهارم: تولید TypeScript SDK
برای تولید کلاینت TypeScript مبتنی بر Fetch اجرا کنید:
npx openapi-generator-cli generate \
-i openapi.yaml \
-g typescript-fetch \
-o generated/typescript \
--additional-properties=npmName=@example/task-sdk,supportsES6=true
وابستگیها را نصب و پروژه را Build کنید:
cd generated/typescript
npm install
npm run build
نمونه استفاده:
import {
Configuration,
TasksApi
} from "@example/task-sdk";
const configuration = new Configuration({
basePath: "https://api.example.com/v1",
accessToken: process.env.EXAMPLE_API_KEY
});
const tasksApi = new TasksApi(configuration);
async function main() {
const tasks = await tasksApi.listTasks({
status: "pending"
});
for (const task of tasks) {
console.log(task.id, task.title, task.status);
}
}
main().catch(console.error);
قرار دادن کلید API در کد Frontend عمومی توصیه نمیشود. اگر SDK به API خصوصی شما متصل میشود، آن را در Backend، Server Action، API Route یا سرویس سمت سرور فراخوانی کنید.
مرحله پنجم: تحلیل قطعی OpenAPI
قبل از ارسال اطلاعات به مدل، باید فهرستی دقیق از عملیات API استخراج کنیم. این فهرست بعداً برای جلوگیری از تولید Endpoint خیالی استفاده میشود.
فایل scripts/analyze_openapi.py:
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
import yaml
HTTP_METHODS = {
"get",
"post",
"put",
"patch",
"delete",
"head",
"options",
"trace",
}
def load_openapi(path: str) -> dict[str, Any]:
file_path = Path(path)
raw = file_path.read_text(encoding="utf-8")
if file_path.suffix.lower() in {".yaml", ".yml"}:
document = yaml.safe_load(raw)
else:
document = json.loads(raw)
if not isinstance(document, dict):
raise ValueError("OpenAPI document must be an object")
return document
def extract_operations(document: dict[str, Any]) -> list[dict[str, Any]]:
operations: list[dict[str, Any]] = []
for path, path_item in document.get("paths", {}).items():
if not isinstance(path_item, dict):
continue
shared_parameters = path_item.get("parameters", [])
for method, operation in path_item.items():
if method.lower() not in HTTP_METHODS:
continue
if not isinstance(operation, dict):
continue
parameters = [
*shared_parameters,
*operation.get("parameters", []),
]
operations.append(
{
"method": method.upper(),
"path": path,
"operationId": operation.get("operationId"),
"summary": operation.get("summary"),
"description": operation.get("description"),
"tags": operation.get("tags", []),
"parameters": [
{
"name": parameter.get("name"),
"in": parameter.get("in"),
"required": parameter.get("required", False),
"description": parameter.get("description"),
}
for parameter in parameters
if isinstance(parameter, dict)
and "$ref" not in parameter
],
"hasRequestBody": "requestBody" in operation,
"responses": sorted(
operation.get("responses", {}).keys()
),
}
)
return operations
def main() -> None:
document = load_openapi("openapi.yaml")
operations = extract_operations(document)
output = {
"apiTitle": document.get("info", {}).get("title"),
"apiVersion": document.get("info", {}).get("version"),
"operationCount": len(operations),
"operations": operations,
}
output_path = Path("ai-output/operations.json")
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(
json.dumps(output, ensure_ascii=False, indent=2),
encoding="utf-8",
)
print(f"Extracted {len(operations)} operations")
print(f"Saved to {output_path}")
if __name__ == "__main__":
main()
آن را اجرا کنید:
python scripts/analyze_openapi.py
نمونه خروجی:
{
"apiTitle": "Task Management API",
"apiVersion": "1.0.0",
"operationCount": 3,
"operations": [
{
"method": "GET",
"path": "/tasks",
"operationId": "listTasks",
"summary": "List project tasks",
"description": null,
"tags": [],
"parameters": [
{
"name": "status",
"in": "query",
"required": false,
"description": null
}
],
"hasRequestBody": false,
"responses": [
"200"
]
}
]
}
مرحله ششم: اتصال به API هوش مصنوعی درواره
API درواره با ساختار استاندارد و سازگار با کلاینتهای رایج ارائه میشود. آدرس پایه API:
https://api.darvareh.ir/v1
یک کلاینت مشترک بسازید:
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
api_key = os.environ["DARVAREH_API_KEY"]
model_id = os.environ.get(
"DARVAREH_MODEL",
"MODEL_ID_DARVAREH",
)
client = OpenAI(
api_key=api_key,
base_url="https://api.darvareh.ir/v1",
)
اکنون میتوانیم فهرست قطعی عملیات را برای تحلیل معنایی به مدل ارسال کنیم.
مرحله هفتم: تولید گزارش کیفی API
هدف این مرحله تولید SDK نیست. میخواهیم مدل مشکلاتی را پیدا کند که Generatorهای قطعی معمولاً تشخیص نمیدهند؛ برای مثال:
- Summary مبهم
- نبود توضیح پارامتر
- نام نامناسب
operationId - نبود نمونه کاربردی
- پاسخهای ناکافی
- رفتار نامشخص Pagination
- نبود توضیح برای فیلترها
- ناسازگاری نامگذاری عملیات
فایل scripts/generate_ai_docs.py:
from __future__ import annotations
import json
import os
from pathlib import Path
from typing import Any
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI(
api_key=os.environ["DARVAREH_API_KEY"],
base_url="https://api.darvareh.ir/v1",
)
MODEL_ID = os.environ.get(
"DARVAREH_MODEL",
"MODEL_ID_DARVAREH",
)
def load_operations() -> dict[str, Any]:
return json.loads(
Path("ai-output/operations.json").read_text(
encoding="utf-8"
)
)
def call_model(payload: dict[str, Any]) -> str:
response = client.chat.completions.create(
model=MODEL_ID,
temperature=0.1,
messages=[
{
"role": "system",
"content": (
"You are a senior API and SDK designer. "
"Analyze only the operations included in the input. "
"Never invent endpoints, parameters, status codes, "
"models, or authentication mechanisms. "
"Return valid JSON only."
),
},
{
"role": "user",
"content": json.dumps(
{
"task": (
"Review this API before SDK publication. "
"Identify incomplete documentation, ambiguous "
"operation names, missing parameter descriptions, "
"and developer-experience problems."
),
"requiredOutput": {
"summary": "string",
"issues": [
{
"operationId": "string or null",
"method": "string",
"path": "string",
"severity": "low | medium | high",
"type": "string",
"explanation": "string",
"suggestion": "string"
}
],
"publicationReadiness": (
"blocked | needs_review | ready"
)
},
"api": payload,
},
ensure_ascii=False,
),
},
],
)
content = response.choices[0].message.content
if not content:
raise RuntimeError("Model returned an empty response")
return content
def parse_json_response(content: str) -> dict[str, Any]:
normalized = content.strip()
if normalized.startswith("```"):
lines = normalized.splitlines()
normalized = "\n".join(lines[1:-1])
if normalized.lstrip().startswith("json"):
normalized = normalized.lstrip()[4:].lstrip()
parsed = json.loads(normalized)
if not isinstance(parsed, dict):
raise ValueError("Expected a JSON object")
return parsed
def main() -> None:
operations = load_operations()
raw_response = call_model(operations)
report = parse_json_response(raw_response)
output_path = Path("ai-output/api-review.json")
output_path.write_text(
json.dumps(report, ensure_ascii=False, indent=2),
encoding="utf-8",
)
print(f"AI review saved to {output_path}")
if __name__ == "__main__":
main()
اجرا:
python scripts/generate_ai_docs.py
اگر مدل انتخابی شما از Structured Outputs پشتیبانی میکند، میتوانید خروجی را با JSON Schema محدود کنید. در غیر این صورت، خروجی باید مانند نمونه بالا Parse و سپس بهصورت مستقل اعتبارسنجی شود.
مرحله هشتم: جلوگیری از Hallucination
مهمترین قانون این سیستم آن است که مدل حق ندارد عملیات جدیدی اختراع کند.
برای کنترل این موضوع، خروجی مدل را با مجموعه عملیات واقعی OpenAPI مقایسه میکنیم.
فایل scripts/validate_ai_output.py:
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
def read_json(path: str) -> dict[str, Any]:
data = json.loads(
Path(path).read_text(encoding="utf-8")
)
if not isinstance(data, dict):
raise ValueError(f"{path} must contain a JSON object")
return data
def operation_key(
method: str,
path: str,
) -> tuple[str, str]:
return method.upper(), path
def main() -> None:
source = read_json("ai-output/operations.json")
report = read_json("ai-output/api-review.json")
allowed_operations = {
operation_key(
operation["method"],
operation["path"],
)
for operation in source.get("operations", [])
}
invalid_items: list[dict[str, Any]] = []
for issue in report.get("issues", []):
if not isinstance(issue, dict):
invalid_items.append(
{
"reason": "Issue is not an object",
"value": issue,
}
)
continue
key = operation_key(
str(issue.get("method", "")),
str(issue.get("path", "")),
)
if key not in allowed_operations:
invalid_items.append(
{
"reason": "Unknown API operation",
"value": issue,
}
)
if invalid_items:
print(
json.dumps(
{
"valid": False,
"errors": invalid_items,
},
ensure_ascii=False,
indent=2,
)
)
raise SystemExit(1)
print(
json.dumps(
{
"valid": True,
"checkedIssues": len(
report.get("issues", [])
),
},
ensure_ascii=False,
indent=2,
)
)
if __name__ == "__main__":
main()
اجرا:
python scripts/validate_ai_output.py
اگر مدل عملیاتی غیر از عملیات موجود در OpenAPI تولید کرده باشد، اسکریپت با Exit Code برابر یک متوقف میشود. بنابراین میتوان آن را مستقیماً وارد CI کرد.
مرحله نهم: تولید مثالهای استفاده از SDK
یکی از نقاط ضعف SDKهای خودکار، مثالهای عمومی و غیرواقعی است. هوش مصنوعی میتواند براساس نام عملیات، پارامترها و مدلهای موجود، مثالهای قابلفهمتری تولید کند.
Prompt پیشنهادی:
برای هر operationId موجود در ورودی، یک مثال کوتاه Python و یک مثال TypeScript تولید کن.
قوانین:
1. فقط از operationIdهای ورودی استفاده کن.
2. Endpoint یا پارامتر جدید نساز.
3. برای مقادیر نمونه از دادههای واضح و غیرحساس استفاده کن.
4. هر مثال باید مستقل و کوتاه باشد.
5. نام پکیج Python برابر task_sdk است.
6. نام پکیج TypeScript برابر @example/task-sdk است.
7. اگر اطلاعات لازم برای تولید مثال کافی نیست، بهجای حدسزدن مقدار needsHumanReview را true قرار بده.
8. فقط JSON معتبر برگردان.
ساختار خروجی:
{
"examples": [
{
"operationId": "listTasks",
"method": "GET",
"path": "/tasks",
"python": "tasks = tasks_api.list_tasks(status=\"pending\")",
"typescript": "const tasks = await tasksApi.listTasks({ status: \"pending\" });",
"needsHumanReview": false
}
]
}
بعد از دریافت خروجی، سه کنترل انجام دهید:
methodوpathباید در OpenAPI وجود داشته باشند.operationIdباید دقیقاً با سند اصلی برابر باشد.- مثالها باید در پروژه موقت اجرا یا حداقل Compile شوند.
نباید کد تولیدشده مدل را بدون بررسی مستقیماً وارد مستندات رسمی کرد.
مرحله دهم: تست کیفیت OpenAPI
اگر OpenAPI ناقص باشد، SDK تولیدشده نیز ناقص خواهد بود. بنابراین بهتر است پیش از تولید، قوانین کیفیت را با تست خودکار کنترل کنیم.
فایل tests/test_openapi_quality.py:
from scripts.analyze_openapi import (
extract_operations,
load_openapi,
)
def test_api_has_operations() -> None:
document = load_openapi("openapi.yaml")
operations = extract_operations(document)
assert operations, "API must contain at least one operation"
def test_all_operations_have_operation_id() -> None:
document = load_openapi("openapi.yaml")
operations = extract_operations(document)
missing = [
f"{item['method']} {item['path']}"
for item in operations
if not item["operationId"]
]
assert not missing, (
"Operations without operationId: "
+ ", ".join(missing)
)
def test_operation_ids_are_unique() -> None:
document = load_openapi("openapi.yaml")
operations = extract_operations(document)
operation_ids = [
item["operationId"]
for item in operations
if item["operationId"]
]
assert len(operation_ids) == len(set(operation_ids)), (
"operationId values must be unique"
)
def test_all_operations_have_summary() -> None:
document = load_openapi("openapi.yaml")
operations = extract_operations(document)
missing = [
item["operationId"]
for item in operations
if not item["summary"]
]
assert not missing, (
"Operations without summary: "
+ ", ".join(missing)
)
def test_success_responses_are_documented() -> None:
document = load_openapi("openapi.yaml")
operations = extract_operations(document)
invalid = []
for operation in operations:
has_success = any(
str(status).startswith("2")
for status in operation["responses"]
)
if not has_success:
invalid.append(operation["operationId"])
assert not invalid, (
"Operations without 2xx response: "
+ ", ".join(invalid)
)
تستها را اجرا کنید:
pytest -q
مرحله یازدهم: تست کلاینت Python بدون اتصال واقعی به API
برای تست SDK نباید همیشه به سرویس واقعی درخواست ارسال کرد. میتوان پاسخ HTTP را Mock کرد.
اگر کلاینت اختصاصی شما از HTTPX استفاده میکند، قابلیت MockTransport اجازه میدهد درخواستها را بدون شبکه بررسی کنید. جزئیات Transportها در مستندات رسمی HTTPX آمده است.
نمونه یک کلاینت کوچک:
from __future__ import annotations
from typing import Any
import httpx
class TaskClient:
def __init__(
self,
api_key: str,
base_url: str,
transport: httpx.BaseTransport | None = None,
) -> None:
self._client = httpx.Client(
base_url=base_url,
headers={
"Authorization": f"Bearer {api_key}",
"Accept": "application/json",
},
timeout=30,
transport=transport,
)
def get_task(
self,
task_id: str,
) -> dict[str, Any]:
response = self._client.get(
f"/tasks/{task_id}"
)
response.raise_for_status()
return response.json()
def close(self) -> None:
self._client.close()
تست:
import httpx
from task_client import TaskClient
def mock_handler(
request: httpx.Request,
) -> httpx.Response:
assert request.method == "GET"
assert request.url.path == "/v1/tasks/task_123"
assert (
request.headers["Authorization"]
== "Bearer test_key"
)
return httpx.Response(
status_code=200,
json={
"id": "task_123",
"title": "Prepare monthly report",
"status": "pending",
"description": None,
},
)
def test_get_task() -> None:
transport = httpx.MockTransport(mock_handler)
client = TaskClient(
api_key="test_key",
base_url="https://api.example.com/v1",
transport=transport,
)
task = client.get_task("task_123")
assert task["id"] == "task_123"
assert task["status"] == "pending"
client.close()
در SDK تولیدشده ممکن است لایه HTTP متفاوت باشد؛ اما اصل تست یکسان است: درخواست خروجی SDK را ثبت کنید و آن را با قرارداد OpenAPI مقایسه کنید.
مرحله دوازدهم: مدلهای Type-safe با Pydantic
در Python بهتر است پاسخ API مستقیماً به dict خام محدود نشود. مدلهای Pydantic امکان تبدیل و اعتبارسنجی داده را فراهم میکنند.
طبق مستندات رسمی Pydantic، مدلها میتوانند داده ورودی را Parse و براساس نوعهای تعریفشده اعتبارسنجی کنند.
from typing import Literal
from pydantic import BaseModel
class Task(BaseModel):
id: str
title: str
status: Literal["pending", "completed"]
description: str | None = None
استفاده در کلاینت:
def get_task(
self,
task_id: str,
) -> Task:
response = self._client.get(
f"/tasks/{task_id}"
)
response.raise_for_status()
return Task.model_validate(response.json())
اگر API بهاشتباه مقدار دیگری برای status برگرداند، خطای Validation ایجاد میشود و ناسازگاری قرارداد سریعتر مشخص خواهد شد.
مرحله سیزدهم: طراحی مدیریت خطا
یک SDK حرفهای نباید همه خطاها را به شکل عمومی برگرداند.
ساختار پایه:
class ApiError(Exception):
def __init__(
self,
message: str,
status_code: int | None = None,
request_id: str | None = None,
response_body: object | None = None,
) -> None:
super().__init__(message)
self.status_code = status_code
self.request_id = request_id
self.response_body = response_body
class AuthenticationError(ApiError):
pass
class NotFoundError(ApiError):
pass
class RateLimitError(ApiError):
pass
class ValidationError(ApiError):
pass
تبدیل پاسخ HTTP به خطای SDK:
import httpx
def raise_sdk_error(
response: httpx.Response,
) -> None:
if response.is_success:
return
request_id = response.headers.get("x-request-id")
try:
body = response.json()
except ValueError:
body = response.text
message = f"API request failed with {response.status_code}"
error_class = ApiError
if response.status_code in {401, 403}:
error_class = AuthenticationError
elif response.status_code == 404:
error_class = NotFoundError
elif response.status_code == 422:
error_class = ValidationError
elif response.status_code == 429:
error_class = RateLimitError
raise error_class(
message=message,
status_code=response.status_code,
request_id=request_id,
response_body=body,
)
مدل هوش مصنوعی میتواند توضیح خطا و مثال برخورد با آن را تولید کند؛ اما نگاشت Status Code به کلاس خطا باید براساس قرارداد واقعی API و بهصورت قطعی انجام شود.
مرحله چهاردهم: Pagination
APIهای فهرستمحور معمولاً از یکی از روشهای زیر استفاده میکنند:
- Page-based
- Offset-based
- Cursor-based
برای APIهای بزرگ، Cursor Pagination معمولاً در برابر تغییر دادهها رفتار پایدارتری دارد.
نمونه مدل پاسخ:
from pydantic import BaseModel
class Task(BaseModel):
id: str
title: str
status: str
class TaskPage(BaseModel):
data: list[Task]
next_cursor: str | None = None
has_more: bool = False
Iterator در SDK:
from collections.abc import Iterator
def iter_tasks(
self,
status: str | None = None,
page_size: int = 100,
) -> Iterator[Task]:
cursor: str | None = None
while True:
page = self.list_tasks(
status=status,
cursor=cursor,
limit=page_size,
)
yield from page.data
if not page.has_more or not page.next_cursor:
break
cursor = page.next_cursor
کاربر SDK میتواند بدون مدیریت دستی Cursor بنویسد:
for task in client.iter_tasks(status="pending"):
print(task.id, task.title)
مرحله پانزدهم: Retry را هوشمندانه پیادهسازی کنید
Retry برای همه درخواستها مناسب نیست. درخواستهای GET معمولاً سادهتر Retry میشوند؛ اما تکرار یک درخواست POST ممکن است دو منبع یکسان ایجاد کند.
پیش از Retry درخواستهای تغییردهنده، باید سازوکار Idempotency در API مشخص شده باشد.
الگوی رایج Backoff:
delay = min(max_delay, base_delay * 2^attempt)
برای جلوگیری از ارسال همزمان Retryهای متعدد، مقدار کوچکی Jitter نیز اضافه میشود:
final_delay = delay + random_jitter
این فرمولها بهصورت متن ساده نوشته شدهاند تا در ویرایشگر Ghost بدون وابستگی به موتور فرمول نمایش داده شوند.
Retry را فقط برای خطاهای موقت در نظر بگیرید؛ مانند:
- Timeout
- خطاهای موقت شبکه
- بعضی پاسخهای 429
- بعضی خطاهای 5xx
خطاهای Validation یا Authentication معمولاً با Retry حل نمیشوند.
مرحله شانزدهم: تولید README با هوش مصنوعی
یک README خوب باید حداقل شامل موارد زیر باشد:
- معرفی کوتاه SDK
- نسخههای پشتیبانیشده
- روش نصب
- تنظیم کلید API
- ساخت Client
- نمونه اولین درخواست
- مدیریت خطا
- Pagination
- Timeout و Retry
- توسعه و اجرای تست
- نسخهبندی
- لینک مستندات API
برای تولید README، کل سند OpenAPI را بدون محدودیت به مدل ارسال نکنید. ابتدا یک Manifest کوچک و قابل اعتبارسنجی بسازید:
{
"package": {
"python": "task-sdk",
"typescript": "@example/task-sdk"
},
"authentication": {
"type": "bearer"
},
"operations": [
{
"operationId": "listTasks",
"method": "GET",
"path": "/tasks"
},
{
"operationId": "createTask",
"method": "POST",
"path": "/tasks"
}
]
}
سپس از مدل بخواهید فقط براساس همین داده مستندات بنویسد. بعد از تولید نیز همه نام عملیات را با Manifest مقایسه کنید.
مرحله هفدهم: افزودن فرایند به CI/CD
یک Pipeline ساده میتواند مراحل زیر را اجرا کند:
name: Build SDK
on:
push:
paths:
- "openapi.yaml"
- "scripts/**"
- "tests/**"
jobs:
build-sdk:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install Python dependencies
run: pip install -r requirements.txt
- name: Install Node dependencies
run: npm ci
- name: Run OpenAPI quality tests
run: pytest -q
- name: Analyze OpenAPI
run: python scripts/analyze_openapi.py
- name: Generate Python SDK
run: |
npx openapi-generator-cli generate \
-i openapi.yaml \
-g python \
-o generated/python \
--additional-properties=packageName=task_sdk,projectName=task-sdk
- name: Generate TypeScript SDK
run: |
npx openapi-generator-cli generate \
-i openapi.yaml \
-g typescript-fetch \
-o generated/typescript \
--additional-properties=npmName=@example/task-sdk,supportsES6=true
اگر مرحله تحلیل هوش مصنوعی را هم در CI اجرا میکنید، کلید درواره را در Secretهای مخزن قرار دهید:
- name: Run AI documentation review
env:
DARVAREH_API_KEY: ${{ secrets.DARVAREH_API_KEY }}
DARVAREH_MODEL: MODEL_ID_DARVAREH
run: |
python scripts/generate_ai_docs.py
python scripts/validate_ai_output.py
بهتر است تحلیل هوش مصنوعی در هر Commit اجرا نشود. میتوان آن را فقط در شرایط زیر فعال کرد:
- تغییر فایل OpenAPI
- ساخت Release جدید
- اجرای دستی Workflow
- بازبینی هفتگی مستندات
این روش هزینه و زمان اجرا را کاهش میدهد.
مرحله هجدهم: مدیریت تغییرات API
هر تغییر OpenAPI الزاماً یکسان نیست.
تغییر Breaking
نمونهها:
- حذف Endpoint
- حذف فیلد پاسخ
- Required کردن یک فیلد اختیاری
- تغییر نوع فیلد
- تغییر نام پارامتر
- حذف مقدار Enum
- تغییر روش احراز هویت
این تغییرات معمولاً به نسخه Major جدید SDK نیاز دارند.
تغییر سازگار
نمونهها:
- اضافهشدن Endpoint جدید
- اضافهشدن فیلد اختیاری
- اضافهشدن پارامتر اختیاری
- اصلاح توضیحات
- اضافهشدن مثال
این تغییرات معمولاً در نسخه Minor منتشر میشوند.
تغییر اصلاحی
نمونهها:
- رفع اشکال داخلی SDK
- اصلاح مستندات بدون تغییر API
- بهبود پیام خطا
- اصلاح Typo
این تغییرات معمولاً نسخه Patch هستند.
مدل هوش مصنوعی میتواند Diff دو نسخه OpenAPI را خلاصه کند؛ اما تصمیم نهایی درباره SemVer باید با قواعد قطعی و بازبینی توسعهدهنده انجام شود.
الگوی مناسب Prompt برای تحلیل تغییرات
دو فهرست operation از نسخه قبلی و نسخه جدید API در اختیار تو قرار میگیرد.
وظیفه:
1. عملیات اضافهشده، حذفشده و تغییریافته را مشخص کن.
2. هیچ عملیاتی خارج از ورودی نساز.
3. تغییرات احتمالی Breaking را علامتگذاری کن.
4. برای هر نتیجه، شواهد دقیق شامل method، path و operationId ارائه بده.
5. اگر اطلاعات برای تصمیم قطعی کافی نیست، مقدار confidence را low قرار بده.
6. فقط JSON معتبر برگردان.
ساختار خروجی:
{
"added": [],
"removed": [],
"changed": [],
"breakingChanges": [
{
"method": "POST",
"path": "/tasks",
"operationId": "createTask",
"reason": "A previously optional field is now required",
"confidence": "high"
}
],
"suggestedVersionBump": "major"
}
حتی در این حالت نیز نسخه پیشنهادی مدل نباید بدون کنترل منتشر شود.
اشتباهات رایج در ساخت SDK با هوش مصنوعی
ارسال کل Repository به مدل
این کار مصرف توکن را افزایش میدهد و احتمال نادیدهگرفتن بخشهای مهم را بیشتر میکند. اطلاعات را به Manifestهای کوچک و مرتبط تقسیم کنید.
اعتماد کامل به کد تولیدشده
کد تولیدشده باید Formatter، Linter، Type Checker و تستها را پشت سر بگذارد.
برای Python:
ruff check .
mypy .
pytest
برای TypeScript:
npm run lint
npm run typecheck
npm test
استفاده از Summary بهجای operationId
summary برای انسان نوشته میشود و ممکن است تغییر کند. شناسه پایدار متد SDK باید از operationId تولید شود.
انتشار SDK بدون Pin کردن Generator
تغییر نسخه Generator ممکن است تعداد زیادی فایل را تغییر دهد. نسخه ابزار تولیدکننده را Pin و ارتقا را کنترلشده انجام دهید.
قراردادن منطق تجاری در کد تولیدشده
پوشه تولیدشده نباید محل منطق سفارشی باشد؛ زیرا در اجرای بعدی Generator بازنویسی میشود. قابلیتهای سفارشی را در Wrapper یا لایه جداگانه قرار دهید.
نداشتن نمونه پاسخ در OpenAPI
Schema نوع داده را مشخص میکند؛ اما Example به توسعهدهنده کمک میکند کاربرد واقعی فیلدها را بفهمد.
تولید مستقیم کد از متن آزاد
ابتدا خروجی مدل را به یک Plan یا JSON ساختاریافته تبدیل کنید. سپس Plan را اعتبارسنجی و با کد قطعی پردازش کنید.
چکلیست SDK آماده انتشار
پیش از انتشار SDK بررسی کنید:
- تمام عملیات
operationIdیکتا دارند. - تمام پارامترها توضیح دارند.
- مدل درخواست و پاسخ مشخص است.
- پاسخهای موفق و خطاها مستند شدهاند.
- روش احراز هویت تعریف شده است.
- Timeout قابل تنظیم است.
- Retry فقط برای خطاهای مناسب انجام میشود.
- Pagination ابزار کمکی دارد.
- خطاهای API به Exceptionهای قابلاستفاده تبدیل میشوند.
- هیچ Endpoint خیالی در مستندات وجود ندارد.
- مثالها Compile یا اجرا شدهاند.
- تستهای واحد بدون شبکه اجرا میشوند.
- نسخه Generator ثابت شده است.
- SDK تولیدشده از کد سفارشی جدا است.
- Release با نسخه OpenAPI هماهنگ است.
- کلید API در کد یا Repository قرار ندارد.
چه مدل هوش مصنوعی برای این پروژه مناسب است؟
مدل مناسب باید در موارد زیر عملکرد خوبی داشته باشد:
- درک JSON و YAML
- تحلیل قراردادهای API
- تولید کد Python و TypeScript
- پیروی دقیق از Schema خروجی
- شناسایی تناقضهای مستندات
- Context Window متناسب با اندازه API
- هزینه مناسب برای اجرای دورهای
برای API کوچک، یک مدل سریع و اقتصادی معمولاً کافی است. برای تحلیل Diffهای بزرگ، بررسی چند فایل یا تولید مستندات پیچیده، ممکن است مدلی با توانایی استدلال و Context بیشتر نیاز باشد.
فهرست مدلها، شناسه قابل استفاده و قیمت بهروز را در صفحه مدلهای درواره ببینید.
چرا API درواره برای این معماری مناسب است؟
در این پروژه هوش مصنوعی بخشی از Pipeline توسعه است، نه یک ابزار گفتوگوی مستقل. بنابراین برنامه باید بتواند از Backend یا CI مستقیماً مدل را فراخوانی کند.
با API درواره میتوانید:
- مدل مناسب پروژه را انتخاب کنید.
- درخواستها را از Python، Node.js یا Backend ارسال کنید.
- خروجی مدل را وارد Pipeline تولید SDK کنید.
- مدل را بدون بازنویسی کل معماری تعویض کنید.
- برای تحلیل، مستندسازی و تولید مثال از مدلهای متفاوت استفاده کنید.
- هزینه و مشخصات مدلها را پیش از انتخاب مقایسه کنید.
برای شروع، در درواره ثبتنام کنید، کلید API بگیرید و آدرس پایه زیر را در کلاینت خود قرار دهید:
https://api.darvareh.ir/v1
پرسشهای متداول
آیا هوش مصنوعی میتواند یک SDK کامل بسازد؟
بله، اما سپردن کل فرایند به مدل زبانی قابلاعتمادترین روش نیست. تولید ساختاری کلاینت باید از OpenAPI و ابزار قطعی انجام شود. هوش مصنوعی برای مستندسازی، مثالسازی، بازبینی کیفیت و تحلیل تغییرات مناسبتر است.
برای ساخت SDK حتماً به OpenAPI نیاز داریم؟
اجباری نیست؛ اما بدون قرارداد ساختاریافته، تولید و نگهداری SDK دشوارتر میشود. اگر API موجود OpenAPI ندارد، بهتر است ابتدا قرارداد آن را ایجاد و با رفتار واقعی سرویس تطبیق دهید.
آیا میتوان از Swagger برای ساخت SDK استفاده کرد؟
Swagger نام مجموعهای از ابزارهای مرتبط با توصیف و نمایش API است. در پروژههای جدید معمولاً فایل OpenAPI بهعنوان ورودی ابزار تولید Client استفاده میشود.
آیا SDK تولیدشده برای Production آماده است؟
خروجی اولیه Generator معمولاً به تنظیم، تست، مستندسازی و بازبینی نیاز دارد. مدیریت Timeout، Retry، Pagination، خطا و تجربه توسعهدهنده باید جداگانه ارزیابی شود.
چگونه از ساخت Endpoint خیالی جلوگیری کنیم؟
ابتدا فهرست عملیات را بهصورت قطعی از OpenAPI استخراج کنید. سپس تمام method، path و operationIdهای خروجی مدل را با همین فهرست مقایسه کنید. هر مورد ناشناخته باید باعث شکست Pipeline شود.
آیا میتوان برای چند زبان SDK تولید کرد؟
بله. از یک سند OpenAPI میتوان کلاینت Python، TypeScript، Java، Go، C# و زبانهای دیگر تولید کرد. بااینحال هر SDK باید جداگانه تست و متناسب با استانداردهای همان اکوسیستم طراحی شود.
آیا باید کل فایل OpenAPI را به مدل ارسال کنیم؟
برای فایل کوچک ممکن است امکانپذیر باشد؛ اما روش بهتر استخراج بخش موردنیاز است. ارسال Manifest کوچکتر هزینه را کاهش میدهد و اعتبارسنجی خروجی را آسانتر میکند.
آیا API درواره یک SDK Generator آماده است؟
درواره زیرساخت دسترسی API به مدلهای هوش مصنوعی را فراهم میکند. منطق خواندن OpenAPI، تولید SDK، اعتبارسنجی و انتشار را مطابق نیاز محصول در Backend یا Pipeline خود پیادهسازی میکنید.
Model ID درواره را از کجا بگیریم؟
شناسه مدل و مشخصات بهروز آن را از صفحه مدلهای درواره دریافت کنید و بهجای MODEL_ID_DARVAREH قرار دهید.
جمعبندی
بهترین روش ساخت SDK با هوش مصنوعی، ترکیب ابزارهای قطعی و مدلهای زبانی است.
OpenAPI قرارداد اصلی API را نگهداری میکند. Generator براساس این قرارداد، کلاینت و مدلهای پایه را میسازد. هوش مصنوعی کیفیت توضیحات را بررسی میکند، مثال مینویسد، ابهامها را پیدا میکند و تغییرات نسخهها را توضیح میدهد. در پایان، اعتبارسنجی، تست، Lint و Type Checking مانع ورود خروجی نادرست به Release میشوند.
این معماری سه مزیت مهم دارد:
- سرعت تولید و بهروزرسانی SDK افزایش پیدا میکند.
- احتمال ایجاد مستندات ناسازگار با API کاهش مییابد.
- تجربه استفاده از API برای توسعهدهندگان بهتر میشود.
برای پیادهسازی لایه هوشمند این Pipeline، در درواره ثبتنام کنید، کلید API بگیرید و مدل مناسب پروژه را از صفحه مدلها انتخاب کنید.
مقالات مرتبط
- API چیست؟ راهنمای کامل API هوش مصنوعی
- آموزش دریافت کلید API هوش مصنوعی
- آموزش اتصال API هوش مصنوعی به اپلیکیشن
- آموزش کار با API درواره و cURL
- آموزش تست API درواره با Postman
- Structured Outputs و خروجی JSON ساختاریافته
- ساخت API هوش مصنوعی آماده Production
- راهنمای ارزیابی مدلهای هوش مصنوعی و Evals
- مقایسه SDKهای هوش مصنوعی
- API سازگار با OpenAI چیست؟
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.