مستندسازی کد و API با هوش مصنوعی؛ ساخت AI Documentation Generator
در این آموزش یک AI Documentation Generator واقعی میسازید که OpenAPI و کد پروژه را تحلیل میکند، مستندات فارسی API، مثالهای curl و Python میسازد و مغایرت مستندات با قرارداد را تشخیص میدهد.
مستندسازی کد و API با هوش مصنوعی؛ ساخت مولد مستندات خودکار
مستندات نرمافزار معمولاً در روز اول پروژه دقیقاند، اما با تغییر کد بهتدریج قدیمی میشوند. توسعهدهنده Endpoint جدیدی اضافه میکند، نام یک فیلد تغییر میکند یا پاسخ خطای جدیدی ساخته میشود، اما مستندات بهروزرسانی نمیشوند.
نتیجه این وضعیت:
- کاربران API درخواست اشتباه ارسال میکنند.
- اعضای جدید تیم برای شناخت پروژه زمان زیادی صرف میکنند.
- مثالهای قدیمی دیگر اجرا نمیشوند.
- رفتار واقعی کد با README متفاوت میشود.
- تیم پشتیبانی بارها به سؤالهای تکراری پاسخ میدهد.
- توسعهدهندگان برای فهم یک تابع مجبور به خواندن تمام پیادهسازی میشوند.
- تغییرات مهم Release بدون توضیح باقی میمانند.
هوش مصنوعی میتواند از روی Source Code، OpenAPI Schema، تستها و توضیحات تغییر، پیشنویس مستندات تولید کند. اما مستندسازی خودکار زمانی قابل اعتماد است که:
- منبع حقیقت مشخص باشد.
- مدل اجازه نداشته باشد رفتار جدید اختراع کند.
- مثالهای کد اجرا یا اعتبارسنجی شوند.
- مستندات تولیدشده با Schema تطبیق داده شوند.
- تغییرات مستندات در Code Review بررسی شوند.
- نسخهبندی و تشخیص Drift وجود داشته باشد.
در این آموزش یک ابزار عملی با Python و API درواره میسازیم که OpenAPI یک برنامه FastAPI را میخواند و برای هر Endpoint موارد زیر را تولید میکند:
- توضیح فارسی
- پارامترها
- بدنه درخواست
- پاسخ موفق
- خطاهای مستندشده
- مثال curl
- مثال Python
- نکات استفاده
- پرسشها و ابهامهای موجود در Schema
مستندسازی با هوش مصنوعی چیست؟
AI Documentation یعنی استفاده از مدلهای زبانی برای تولید یا بهروزرسانی مستندات فنی بر اساس منابع معتبر پروژه.
منابع ورودی میتوانند شامل این موارد باشند:
- Source Code
- OpenAPI Schema
- JSON Schema
- Type Definition
- Docstring
- تستها
- README فعلی
- Pull Request
- Release Notes
- ADR
- مثالهای واقعی
- تنظیمات پروژه
خروجیها:
- API Reference
- README
- راهنمای نصب
- Quickstart
- Docstring
- توضیح کلاس و تابع
- مثال استفاده
- Migration Guide
- Changelog
- راهنمای خطاها
- مستندات معماری
- راهنمای مشارکت
چرا OpenAPI منبع خوبی برای مستندسازی API است؟
OpenAPI یک رابط استاندارد و مستقل از زبان برای توصیف APIهای HTTP فراهم میکند. مصرفکننده میتواند بدون دسترسی به Source Code، Endpointها، ورودیها و پاسخها را درک کند. این تعریف همچنین برای تولید مستندات، Client و تست قابل استفاده است. مشخصات رسمی OpenAPI
اطلاعات موجود در OpenAPI:
- مسیر Endpoint
- HTTP Method
- Operation ID
- پارامترهای Path و Query
- Headerها
- Request Body
- Responseها
- Schema دادهها
- Tagها
- توضیحات
- وضعیت Deprecated
- روشهای احراز هویت تعریفشده
FastAPI میتواند Schema برنامه را با متد app.openapi() تولید کند. بنابراین برای پروژه آموزشی لازم نیست ابتدا Server را اجرا و سپس /openapi.json را دانلود کنیم.
AI چه چیزی به OpenAPI اضافه میکند؟
OpenAPI ساختار فنی را ارائه میدهد، اما ممکن است توضیح آن برای کاربر کافی نباشد.
Schema:
{
"path": "/products/{product_id}",
"method": "GET",
"parameters": [
{
"name": "product_id",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
]
}
مستندات قابلاستفاده:
این Endpoint اطلاعات یک محصول را با استفاده از شناسه عددی آن
برمیگرداند. اگر محصول وجود نداشته باشد، پاسخ 404 دریافت میشود.
نمونه درخواست:
curl http://localhost:8000/products/42
مدل نباید پاسخ 404 را اختراع کند. این پاسخ باید در OpenAPI یا Source Code مرتبط وجود داشته باشد.
انواع مستندات پروژه
API Reference
شرح دقیق هر Endpoint، پارامتر، Request و Response.
Tutorial
کاربر را برای رسیدن به یک نتیجه مشخص گامبهگام هدایت میکند.
How-to Guide
راهحل یک مسئله محدود مانند «چگونه محصول جدید بسازیم؟».
Explanation
چرایی تصمیمها و مفاهیم معماری را توضیح میدهد.
README
معرفی، نصب، اجرا و نقطه شروع پروژه.
Docstring
قرارداد محلی تابع، کلاس یا ماژول را توضیح میدهد.
یک مولد مستندات نباید همه این قالبها را با یک پرامپت بسازد. هر نوع سند هدف، مخاطب و ساختار متفاوتی دارد.
چه چیزهایی را نباید از روی کد حدس زد؟
مدل نباید این اطلاعات را بدون منبع معتبر تولید کند:
- هدف کسبوکار یک قابلیت
- تضمین عملکرد
- محدودیت نرخ درخواست
- مدت نگهداری داده
- سازگاری با نسخههای قبلی
- زمان پاسخ تضمینشده
- رفتار خطایی که در Schema نیست
- متغیر محیطی تعریفنشده
- Dependency نصبنشده
- Endpoint موجودنبوده
- پارامتر اختیاری یا اجباری برخلاف Schema
- نمونه Response غیرمنطبق
برای اطلاعات نامشخص، خروجی باید سؤال یا هشدار تولید کند:
{
"documentation_gaps": [
"پاسخ 404 در Schema تعریف نشده است.",
"واحد فیلد price مشخص نیست.",
"محدوده مجاز page_size توضیح ندارد."
]
}
پروژه عملی این آموزش
یک API ساده محصولات میسازیم. سپس ابزار مستندسازی:
app.openapi()را اجرا میکند.- Operationهای API را استخراج میکند.
- Referenceهای Schema را Resolve میکند.
- Endpointها را جداگانه به مدل میفرستد.
- خروجی JSON را با Pydantic اعتبارسنجی میکند.
- مثالهای curl و Python را بررسی میکند.
- مستندات Markdown میسازد.
- Hash قرارداد را ذخیره میکند.
- در اجرای بعدی تغییر OpenAPI را تشخیص میدهد.
ایجاد پروژه
mkdir ai-documentation-generator
cd ai-documentation-generator
python -m venv .venv
فعالسازی در Linux و macOS:
source .venv/bin/activate
فعالسازی در Windows:
.venv\Scripts\Activate.ps1
نصب وابستگیها:
pip install \
openai \
python-dotenv \
pydantic \
fastapi \
uvicorn \
httpx
ساخت پوشهها:
mkdir app doc_generator generated_docs state
فایلهای زیر را ایجاد کنید:
app/__init__.py
app/main.py
doc_generator/__init__.py
doc_generator/schemas.py
doc_generator/openapi_parser.py
doc_generator/generator.py
doc_generator/validator.py
doc_generator/renderer.py
doc_generator/drift.py
generate_docs.py
تنظیم API درواره
فایل .env:
DARVAREH_API_KEY=YOUR_API_KEY
DARVAREH_MODEL=MODEL_ID_DARVAREH
DOCS_BASE_URL=http://localhost:8000
فایل .gitignore:
.env
.venv/
__pycache__/
.pytest_cache/
state/
برای دریافت API Key در درواره ثبتنام کنید. مدل مناسب تولید مستندات را از صفحه مدلهای درواره انتخاب کنید.
ساخت API نمونه با FastAPI
فایل app/main.py:
from decimal import Decimal
from fastapi import (
FastAPI,
HTTPException,
Query,
status,
)
from pydantic import BaseModel, Field
app = FastAPI(
title="Product Catalog API",
version="1.0.0",
description=(
"API نمونه برای مدیریت کاتالوگ محصولات"
),
)
class ProductCreate(BaseModel):
name: str = Field(
min_length=2,
max_length=120,
description="نام قابل نمایش محصول",
examples=["کیبورد مکانیکی"],
)
price: Decimal = Field(
gt=0,
description="قیمت محصول به ریال",
examples=["3500000"],
)
is_active: bool = Field(
default=True,
description="وضعیت فعالبودن محصول",
)
class ProductResponse(BaseModel):
id: int
name: str
price: Decimal
is_active: bool
PRODUCTS: dict[int, ProductResponse] = {
1: ProductResponse(
id=1,
name="کیبورد مکانیکی",
price=Decimal("3500000"),
is_active=True,
),
2: ProductResponse(
id=2,
name="ماوس بیسیم",
price=Decimal("1200000"),
is_active=True,
),
}
@app.get(
"/products",
response_model=list[ProductResponse],
summary="دریافت فهرست محصولات",
tags=["Products"],
)
def list_products(
active_only: bool = Query(
default=False,
description=(
"در صورت فعالبودن، فقط محصولات "
"فعال برگردانده میشوند"
),
),
):
products = list(PRODUCTS.values())
if active_only:
products = [
product
for product in products
if product.is_active
]
return products
@app.get(
"/products/{product_id}",
response_model=ProductResponse,
summary="دریافت یک محصول",
responses={
404: {
"description": (
"محصول مورد نظر پیدا نشد"
)
}
},
tags=["Products"],
)
def get_product(
product_id: int,
):
product = PRODUCTS.get(product_id)
if product is None:
raise HTTPException(
status_code=404,
detail="Product not found",
)
return product
@app.post(
"/products",
response_model=ProductResponse,
status_code=status.HTTP_201_CREATED,
summary="ایجاد محصول",
tags=["Products"],
)
def create_product(
payload: ProductCreate,
):
new_id = max(
PRODUCTS.keys(),
default=0,
) + 1
product = ProductResponse(
id=new_id,
**payload.model_dump(),
)
PRODUCTS[new_id] = product
return product
اجرای API:
uvicorn app.main:app --reload
مستندات خودکار FastAPI:
http://localhost:8000/docs
OpenAPI:
http://localhost:8000/openapi.json
بهبود Schema پیش از استفاده از AI
کیفیت مستندات تولیدشده به کیفیت OpenAPI وابسته است.
مقایسه کنید:
price: Decimal
با:
price: Decimal = Field(
gt=0,
description="قیمت محصول به ریال",
examples=["3500000"],
)
نسخه دوم به مدل و کاربر اطلاعات مهمی درباره واحد، محدودیت و نمونه مقدار میدهد.
پیش از اضافهکردن AI، این فیلدها را کامل کنید:
summarydescriptiontagsresponse_modelresponses- توضیح
Field - Example
- محدودیت Min و Max
- Deprecated
- Operation ID
AI نباید جای Schema ناقص را با حدس پر کند.
تعریف Schema خروجی مستندات
فایل doc_generator/schemas.py:
from pydantic import BaseModel, Field
class ParameterDoc(BaseModel):
name: str
location: str
required: bool
data_type: str
description: str
example: str | int | float | bool | None = None
class ResponseDoc(BaseModel):
status_code: str
description: str
example: dict | list | str | None = None
class CodeExample(BaseModel):
language: str
title: str
code: str
explanation: str
class EndpointDocumentation(BaseModel):
operation_id: str
title: str
method: str
path: str
purpose: str
parameters: list[ParameterDoc] = Field(
default_factory=list
)
request_body_description: str | None = None
request_body_example: dict | list | None = None
responses: list[ResponseDoc] = Field(
default_factory=list
)
examples: list[CodeExample] = Field(
default_factory=list
)
usage_notes: list[str] = Field(
default_factory=list
)
documentation_gaps: list[str] = Field(
default_factory=list
)
class APIDocumentation(BaseModel):
api_title: str
api_version: str
introduction: str
quickstart: list[str]
endpoints: list[EndpointDocumentation]
استخراج Operationها از OpenAPI
فایل doc_generator/openapi_parser.py:
from copy import deepcopy
from typing import Any
HTTP_METHODS = {
"get",
"post",
"put",
"patch",
"delete",
"options",
"head",
}
def resolve_reference(
reference: str,
document: dict,
) -> Any:
if not reference.startswith("#/"):
raise ValueError(
"Only local OpenAPI references "
"are supported."
)
current: Any = document
for part in reference[2:].split("/"):
current = current[part]
return deepcopy(current)
def resolve_schema(
value: Any,
document: dict,
) -> Any:
if isinstance(value, list):
return [
resolve_schema(item, document)
for item in value
]
if not isinstance(value, dict):
return value
if "$ref" in value:
resolved = resolve_reference(
value["$ref"],
document,
)
extra_fields = {
key: item
for key, item in value.items()
if key != "$ref"
}
resolved.update(extra_fields)
return resolve_schema(
resolved,
document,
)
return {
key: resolve_schema(
item,
document,
)
for key, item in value.items()
}
def extract_operations(
openapi_document: dict,
) -> list[dict]:
operations: list[dict] = []
for path, path_item in (
openapi_document
.get("paths", {})
.items()
):
common_parameters = path_item.get(
"parameters",
[],
)
for method, operation in (
path_item.items()
):
if method.lower() not in (
HTTP_METHODS
):
continue
operation_copy = deepcopy(
operation
)
operation_copy["parameters"] = (
common_parameters
+ operation_copy.get(
"parameters",
[],
)
)
resolved = resolve_schema(
operation_copy,
openapi_document,
)
operations.append(
{
"path": path,
"method": method.upper(),
"operation": resolved,
}
)
operations.sort(
key=lambda item: (
item["path"],
item["method"],
)
)
return operations
این نسخه فقط Referenceهای محلی مانند زیر را Resolve میکند:
#/components/schemas/ProductCreate
برای OpenAPIهای چندفایلی باید Resolver کاملتری اضافه شود.
تولید مستندات هر Endpoint
فایل doc_generator/generator.py:
import json
import os
from dotenv import load_dotenv
from openai import OpenAI
from pydantic import ValidationError
from doc_generator.schemas import (
EndpointDocumentation,
)
load_dotenv()
api_key = os.getenv("DARVAREH_API_KEY")
model = os.getenv("DARVAREH_MODEL")
base_url = os.getenv(
"DOCS_BASE_URL",
"http://localhost:8000",
)
if not api_key:
raise RuntimeError(
"DARVAREH_API_KEY is not configured."
)
if not model:
raise RuntimeError(
"DARVAREH_MODEL is not configured."
)
client = OpenAI(
api_key=api_key,
base_url="https://api.darvareh.ir/v1",
)
SYSTEM_PROMPT = """
تو یک Technical Writer ارشد هستی و برای توسعهدهندگان
مستندات API فارسی تولید میکنی.
منبع حقیقت فقط OpenAPI Operation ارائهشده است.
قواعد:
- Endpoint، پارامتر، پاسخ یا رفتار جدید اختراع نکن.
- required و optional را دقیقاً مطابق Schema بنویس.
- واحد و محدودیتی را که در Schema نیست حدس نزن.
- فقط Status Codeهای موجود را مستند کن.
- مثالها باید با Schema سازگار باشند.
- مثال curl و Python تولید کن.
- مثال Python از کتابخانه requests استفاده کند.
- اگر اطلاعات لازم در Schema نیست، آن را در
documentation_gaps ثبت کن.
- پاسخ فقط JSON معتبر باشد.
- Markdown خارج از JSON تولید نکن.
"""
OUTPUT_TEMPLATE = {
"operation_id": "list_products_products_get",
"title": "string",
"method": "GET",
"path": "/products",
"purpose": "string",
"parameters": [
{
"name": "active_only",
"location": "query",
"required": False,
"data_type": "boolean",
"description": "string",
"example": True,
}
],
"request_body_description": None,
"request_body_example": None,
"responses": [
{
"status_code": "200",
"description": "string",
"example": [],
}
],
"examples": [
{
"language": "bash",
"title": "نمونه curl",
"code": "curl ...",
"explanation": "string",
},
{
"language": "python",
"title": "نمونه Python",
"code": "import requests...",
"explanation": "string",
},
],
"usage_notes": [],
"documentation_gaps": [],
}
def generate_endpoint_docs(
operation_data: dict,
) -> EndpointDocumentation:
payload = {
"base_url": base_url,
"path": operation_data["path"],
"method": operation_data["method"],
"openapi_operation": (
operation_data["operation"]
),
}
response = client.chat.completions.create(
model=model,
temperature=0.1,
messages=[
{
"role": "system",
"content": SYSTEM_PROMPT,
},
{
"role": "user",
"content": (
"برای Operation زیر مستندات بساز:\n\n"
+ json.dumps(
payload,
ensure_ascii=False,
indent=2,
)
+ "\n\nقالب خروجی:\n"
+ json.dumps(
OUTPUT_TEMPLATE,
ensure_ascii=False,
indent=2,
)
),
},
],
)
raw_output = (
response.choices[0]
.message.content
)
if not raw_output:
raise RuntimeError(
"The model returned empty docs."
)
try:
parsed = json.loads(raw_output)
except json.JSONDecodeError as error:
raise RuntimeError(
f"Invalid JSON from model: "
f"{error}"
) from error
try:
return (
EndpointDocumentation
.model_validate(parsed)
)
except ValidationError as error:
raise RuntimeError(
f"Documentation validation "
f"failed: {error}"
) from error
اعتبارسنجی مستندات با OpenAPI
مدل ممکن است Method، Path یا پارامتر را اشتباه بازگرداند. خروجی باید با Operation اصلی تطبیق داده شود.
فایل doc_generator/validator.py:
from doc_generator.schemas import (
EndpointDocumentation,
)
class DocumentationValidationError(
ValueError
):
pass
def validate_endpoint_docs(
docs: EndpointDocumentation,
operation_data: dict,
) -> None:
expected_path = operation_data["path"]
expected_method = operation_data["method"]
if docs.path != expected_path:
raise DocumentationValidationError(
f"Path mismatch: {docs.path} "
f"!= {expected_path}"
)
if docs.method != expected_method:
raise DocumentationValidationError(
f"Method mismatch: {docs.method} "
f"!= {expected_method}"
)
operation = operation_data["operation"]
expected_parameters = {
(
parameter.get("name"),
parameter.get("in"),
)
for parameter in operation.get(
"parameters",
[]
)
}
documented_parameters = {
(
parameter.name,
parameter.location,
)
for parameter in docs.parameters
}
unknown_parameters = (
documented_parameters
- expected_parameters
)
if unknown_parameters:
raise DocumentationValidationError(
"Unknown parameters documented: "
+ str(
sorted(unknown_parameters)
)
)
expected_status_codes = {
str(status_code)
for status_code in operation.get(
"responses",
{}
)
}
documented_status_codes = {
response.status_code
for response in docs.responses
}
unknown_status_codes = (
documented_status_codes
- expected_status_codes
)
if unknown_status_codes:
raise DocumentationValidationError(
"Unknown response codes: "
+ str(
sorted(unknown_status_codes)
)
)
در نسخه کاملتر باید این موارد نیز بررسی شوند:
- تمام پارامترهای Required مستند شده باشند.
- نوع پارامتر با Schema سازگار باشد.
- Request Example با JSON Schema تطبیق داشته باشد.
- Response Example معتبر باشد.
- URL مثال با Path Parameterهای واقعی ساخته شده باشد.
اعتبارسنجی مثال Python
مثال Python را میتوان حداقل از نظر Syntax بررسی کرد:
def validate_python_example(
code: str,
) -> None:
try:
compile(
code,
"<documentation-example>",
"exec",
)
except SyntaxError as error:
raise ValueError(
f"Invalid Python example: {error}"
) from error
برای پیدا کردن مثال Python:
for example in docs.examples:
if example.language.lower() == "python":
validate_python_example(
example.code
)
Syntax معتبر تضمین نمیکند درخواست اجرا میشود. بهترین روش، اجرای مثالها علیه یک محیط آزمایشی کنترلشده است.
ساخت Markdown
فایل doc_generator/renderer.py:
from pathlib import Path
from doc_generator.schemas import (
EndpointDocumentation,
)
def render_endpoint_markdown(
docs: EndpointDocumentation,
) -> str:
lines = [
f"# {docs.title}",
"",
f"`{docs.method} {docs.path}`",
"",
docs.purpose,
"",
"## پارامترها",
"",
]
if docs.parameters:
lines.extend(
[
"| نام | محل | نوع | اجباری | توضیح |",
"|---|---|---|---|---|",
]
)
for parameter in docs.parameters:
required = (
"بله"
if parameter.required
else "خیر"
)
description = (
parameter.description
.replace("|", "\\|")
)
lines.append(
f"| `{parameter.name}` "
f"| `{parameter.location}` "
f"| `{parameter.data_type}` "
f"| {required} "
f"| {description} |"
)
else:
lines.append(
"این Endpoint پارامتر مستقیمی ندارد."
)
if docs.request_body_description:
lines.extend(
[
"",
"## بدنه درخواست",
"",
docs.request_body_description,
"",
]
)
if docs.request_body_example is not None:
import json
lines.extend(
[
"```json",
json.dumps(
docs.request_body_example,
ensure_ascii=False,
indent=2,
),
"```",
]
)
lines.extend(
[
"",
"## پاسخها",
"",
]
)
for response in docs.responses:
lines.extend(
[
f"### HTTP {response.status_code}",
"",
response.description,
"",
]
)
if response.example is not None:
import json
if isinstance(
response.example,
(dict, list),
):
rendered_example = json.dumps(
response.example,
ensure_ascii=False,
indent=2,
)
language = "json"
else:
rendered_example = str(
response.example
)
language = "text"
lines.extend(
[
f"```{language}",
rendered_example,
"```",
"",
]
)
lines.extend(
[
"## مثالها",
"",
]
)
for example in docs.examples:
lines.extend(
[
f"### {example.title}",
"",
example.explanation,
"",
f"```{example.language}",
example.code,
"```",
"",
]
)
if docs.usage_notes:
lines.extend(
[
"## نکات استفاده",
"",
]
)
for note in docs.usage_notes:
lines.append(f"- {note}")
lines.append("")
if docs.documentation_gaps:
lines.extend(
[
"## موارد نیازمند تکمیل",
"",
]
)
for gap in docs.documentation_gaps:
lines.append(f"- {gap}")
lines.append("")
return "\n".join(lines)
def save_endpoint_docs(
docs: EndpointDocumentation,
output_directory: Path,
) -> Path:
safe_operation_id = "".join(
character
if character.isalnum()
or character in {"-", "_"}
else "-"
for character in docs.operation_id
)
output_path = (
output_directory
/ f"{safe_operation_id}.md"
)
output_path.parent.mkdir(
parents=True,
exist_ok=True,
)
output_path.write_text(
render_endpoint_markdown(docs),
encoding="utf-8",
)
return output_path
در Markdown تولیدشده از Divider خطی استفاده نشده است.
تشخیص تغییر یا Documentation Drift
اگر OpenAPI تغییر کند ولی مستندات دوباره تولید نشوند، Drift ایجاد میشود.
فایل doc_generator/drift.py:
import hashlib
import json
from pathlib import Path
def calculate_contract_hash(
openapi_document: dict,
) -> str:
normalized = json.dumps(
openapi_document,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
)
return hashlib.sha256(
normalized.encode("utf-8")
).hexdigest()
def read_previous_hash(
state_path: Path,
) -> str | None:
if not state_path.exists():
return None
return state_path.read_text(
encoding="utf-8"
).strip() or None
def write_contract_hash(
state_path: Path,
contract_hash: str,
) -> None:
state_path.parent.mkdir(
parents=True,
exist_ok=True,
)
state_path.write_text(
contract_hash,
encoding="utf-8",
)
این روش فقط مشخص میکند Schema تغییر کرده است. برای گزارش دقیق تغییر باید نسخه قبلی OpenAPI نیز ذخیره و با نسخه جدید مقایسه شود.
برنامه اصلی تولید مستندات
فایل generate_docs.py:
import json
from pathlib import Path
from app.main import app
from doc_generator.drift import (
calculate_contract_hash,
read_previous_hash,
write_contract_hash,
)
from doc_generator.generator import (
generate_endpoint_docs,
)
from doc_generator.openapi_parser import (
extract_operations,
)
from doc_generator.renderer import (
save_endpoint_docs,
)
from doc_generator.validator import (
validate_endpoint_docs,
validate_python_example,
)
OUTPUT_DIRECTORY = Path(
"generated_docs/api"
)
STATE_PATH = Path(
"state/openapi.sha256"
)
OPENAPI_SNAPSHOT_PATH = Path(
"generated_docs/openapi.json"
)
def main():
openapi_document = app.openapi()
current_hash = calculate_contract_hash(
openapi_document
)
previous_hash = read_previous_hash(
STATE_PATH
)
if previous_hash == current_hash:
print(
"OpenAPI contract has not changed."
)
else:
print(
"OpenAPI contract changed; "
"regenerating documentation."
)
operations = extract_operations(
openapi_document
)
generated_paths = []
for index, operation in enumerate(
operations,
start=1,
):
print(
f"Generating docs "
f"{index}/{len(operations)}: "
f"{operation['method']} "
f"{operation['path']}"
)
docs = generate_endpoint_docs(
operation
)
validate_endpoint_docs(
docs,
operation,
)
for example in docs.examples:
if (
example.language.lower()
== "python"
):
validate_python_example(
example.code
)
output_path = save_endpoint_docs(
docs,
OUTPUT_DIRECTORY,
)
generated_paths.append(
output_path
)
OPENAPI_SNAPSHOT_PATH.parent.mkdir(
parents=True,
exist_ok=True,
)
OPENAPI_SNAPSHOT_PATH.write_text(
json.dumps(
openapi_document,
ensure_ascii=False,
indent=2,
),
encoding="utf-8",
)
write_contract_hash(
STATE_PATH,
current_hash,
)
print(
f"Generated {len(generated_paths)} "
f"documentation files."
)
if __name__ == "__main__":
main()
اجرای ابزار:
python generate_docs.py
ساخت صفحه فهرست API
برای دسترسی آسانتر، یک فایل Index بسازید:
def build_index(
docs_list,
output_path: Path,
):
lines = [
"# مستندات API محصولات",
"",
"این مستندات از روی قرارداد OpenAPI "
"و پس از اعتبارسنجی تولید شدهاند.",
"",
"## Endpointها",
"",
]
for docs, file_path in docs_list:
lines.append(
f"- [{docs.method} {docs.path}]"
f"({file_path.name}) — "
f"{docs.title}"
)
output_path.write_text(
"\n".join(lines),
encoding="utf-8",
)
نمونه مستندات Endpoint
# دریافت یک محصول
`GET /products/{product_id}`
این Endpoint اطلاعات یک محصول را با استفاده از شناسه عددی آن برمیگرداند.
## پارامترها
| نام | محل | نوع | اجباری | توضیح |
|---|---|---|---|---|
| `product_id` | `path` | `integer` | بله | شناسه محصول مورد نظر |
## پاسخها
### HTTP 200
اطلاعات محصول با موفقیت برگردانده شد.
### HTTP 404
محصول مورد نظر پیدا نشد.
## مثالها
### نمونه curl
```bash
curl --request GET \
"http://localhost:8000/products/1"
نمونه Python
import requests
response = requests.get(
"http://localhost:8000/products/1",
timeout=10,
)
response.raise_for_status()
print(response.json())
# تولید Docstring با هوش مصنوعی
برای تولید Docstring بهتر است امضای تابع، Typeها، Exceptionها، Specification و تستهای آن را به مدل بدهید.
پرامپت:
```text
برای تابع زیر Docstring بنویس.
قالب:
Google Style
منابع:
- Signature
- Source Code
- Specification
- Tests
قواعد:
- نحوه کار داخلی را خطبهخط توضیح نده.
- قرارداد عمومی تابع را مستند کن.
- پارامترها، خروجی و Exceptionهای قابل اثبات را بنویس.
- Type را برخلاف Signature تغییر نده.
- Example فقط در صورت امکان از API عمومی بساز.
- رفتار یا Exception جدید اختراع نکن.
- اگر رفتار مبهم است، آن را جداگانه گزارش کن.
کد:
[SOURCE]
Specification:
[SPEC]
Tests:
[TESTS]
نمونه Docstring:
def calculate_cart(
items: list[CartItem],
discount_rate: Decimal = Decimal("0"),
tax_rate: Decimal = Decimal("0"),
) -> CartResult:
"""Calculate totals for a shopping cart.
Discount is applied to the subtotal before tax. Monetary
values are rounded to two decimal places using ROUND_HALF_UP.
Args:
items: Cart items with non-negative prices and quantities.
discount_rate: Discount rate between zero and one.
tax_rate: Tax rate between zero and one.
Returns:
Calculated subtotal, discount, taxable amount, tax, and total.
Raises:
ValueError: If discount_rate or tax_rate is outside [0, 1].
InvalidCartItemError: If an item has a negative price or quantity.
"""
هر Exception مستندشده باید در کد یا قرارداد وجود داشته باشد.
تولید README با AI
برای ساخت README این Context را فراهم کنید:
- فایل Dependencyها
- دستور اجرای واقعی
- ساختار پروژه
- Entry Point
- متغیرهای محیطی
- Dockerfile
- Compose
- تستها
- نمونه درخواست
- License موجود
- نسخه Runtime
ساختار پیشنهادی README:
1. معرفی
2. قابلیتها
3. پیشنیازها
4. نصب
5. تنظیم متغیرهای محیطی
6. اجرای محلی
7. اجرای تست
8. نمونه استفاده
9. ساختار پروژه
10. توسعه
11. محدودیتها
مدل نباید دستور نصب یا متغیر محیطی را فقط بر اساس الگوهای رایج حدس بزند.
تولید Changelog از Git Diff
ورودی:
- Git Diff
- عنوان PRها
- Labelها
- نسخه قبلی و جدید
- نوع مخاطب
- تغییرات قرارداد
خروجی:
{
"added": [],
"changed": [],
"fixed": [],
"deprecated": [],
"removed": [],
"migration_notes": []
}
پرامپت:
از تغییرات زیر Changelog کاربرمحور تولید کن.
قواعد:
- Refactor داخلی بدون اثر کاربر را حذف کن.
- تغییر رفتار عمومی را توضیح بده.
- Breaking Change را فقط با شاهد مشخص اعلام کن.
- Issue یا PR جدید اختراع نکن.
- جزئیات پیادهسازی غیرضروری را ننویس.
تولید Migration Guide
اگر Contract قدیمی و جدید را دارید، مدل میتواند تفاوتها را توضیح دهد:
نسخه قبلی:
[OLD OPENAPI]
نسخه جدید:
[NEW OPENAPI]
خروجی:
- Endpointهای جدید
- Endpointهای حذفشده
- پارامترهای تغییرکرده
- تغییر Required
- تغییر Response
- تغییر نام فیلد
- مراحل مهاجرت
- مثال قبل و بعد
- ابهامهای نیازمند بررسی
تشخیص تغییر باید ابتدا با ابزار مقایسه Schema انجام شود. مدل نتیجه قطعی Diff را توضیح دهد، نه اینکه خودش تنها مرجع تشخیص باشد.
تولید مستندات از Source Code بدون OpenAPI
برای یک کتابخانه یا ماژول داخلی میتوان AST را Parse کرد.
نمونه استخراج توابع Python:
import ast
from pathlib import Path
def extract_public_functions(
file_path: Path,
) -> list[dict]:
source = file_path.read_text(
encoding="utf-8"
)
tree = ast.parse(source)
functions = []
for node in tree.body:
if not isinstance(
node,
(
ast.FunctionDef,
ast.AsyncFunctionDef,
),
):
continue
if node.name.startswith("_"):
continue
functions.append(
{
"name": node.name,
"line": node.lineno,
"end_line": node.end_lineno,
"docstring": (
ast.get_docstring(node)
),
"signature_source": (
ast.get_source_segment(
source,
node,
)
),
}
)
return functions
بهتر است فقط امضا و بدنه تابع مرتبط ارسال شود، نه کل Repository.
تشخیص مستندات قدیمی
چند روش:
Hash قرارداد
اگر OpenAPI تغییر کرد، مستندات باید دوباره ساخته شوند.
مقایسه Symbolها
اگر تابع یا کلاس حذف شده ولی هنوز در مستندات لینک دارد، Drift وجود دارد.
اجرای مثالها
مثالهای Python و Shell در CI اجرا شوند.
بررسی لینکها
لینکهای داخلی و Anchorها بررسی شوند.
تطبیق پارامترها
نام، Type و Required بودن پارامتر مستند با Schema مقایسه شود.
Snapshot مستندات
مستندات تولیدشده در Git ذخیره شوند تا تغییرات آن در Pull Request قابل بررسی باشد.
Docs-as-Code در CI
جریان پیشنهادی:
Code Change
↓
Generate OpenAPI
↓
Compare Contract Hash
↓
Generate Documentation Draft
↓
Validate Parameters and Examples
↓
Run Documentation Tests
↓
Show Markdown Diff in Pull Request
↓
Human Review
↓
Publish
اگر OpenAPI تغییر کرده اما مستندات تغییر نکردهاند، CI میتواند هشدار دهد.
تست مثالهای مستندات
مثالها بخشی از محصولاند و باید تست شوند.
برای کدهای Python:
- Syntax را با
compileبررسی کنید. - Importها را بررسی کنید.
- در محیط آزمایشی اجرا کنید.
- Timeout داشته باشید.
- Response را با Schema مقایسه کنید.
برای curl:
- URL درست باشد.
- HTTP Method با Operation یکسان باشد.
- Path Parameter جایگزین شده باشد.
- Request Body با Schema سازگار باشد.
- Headerهای ضروری وجود داشته باشند.
مثال نباید مستقیماً روی محیط Production اجرا شود.
تولید مثال از Schema در کد
برای فیلدهای ساده بهتر است مثال با کد قطعی ساخته شود، نه AI.
def example_from_schema(
schema: dict,
):
if "example" in schema:
return schema["example"]
if "default" in schema:
return schema["default"]
schema_type = schema.get("type")
if schema_type == "string":
return "string"
if schema_type == "integer":
return 1
if schema_type == "number":
return 1.0
if schema_type == "boolean":
return True
if schema_type == "array":
return [
example_from_schema(
schema.get("items", {})
)
]
if schema_type == "object":
properties = schema.get(
"properties",
{}
)
required = set(
schema.get("required", [])
)
return {
name: example_from_schema(
property_schema
)
for name, property_schema
in properties.items()
if (
name in required
or "example"
in property_schema
or "default"
in property_schema
)
}
return None
AI میتواند توضیح مثال را بنویسد، اما ساختار اولیه بهتر است از Schema استخراج شود.
مستندسازی خطاها
هر خطا باید حداقل این اطلاعات را داشته باشد:
- Status Code
- شرایط وقوع
- ساختار Response
- کد خطای داخلی در صورت وجود
- اقدام پیشنهادی کاربر
- مثال معتبر
نمونه:
{
"status_code": 404,
"error": {
"code": "PRODUCT_NOT_FOUND",
"message": "Product not found"
}
}
اگر API فقط detail برمیگرداند، مستندات نباید ساختار code را اختراع کند.
معماری مستندات چندزبانه
منبع حقیقت باید زبانخنثی بماند:
{
"operation_id": "get_product",
"method": "GET",
"path": "/products/{product_id}",
"parameters": [],
"responses": []
}
سپس Renderer یا مدل نسخه فارسی و انگلیسی را تولید کند.
نکته مهم:
- نام فیلدها ترجمه نشوند.
- کد و URL ثابت بمانند.
- JSON Example در همه زبانها یکسان باشد.
- فقط توضیحات انسانی ترجمه شوند.
- هر دو نسخه از یک Contract Hash ساخته شوند.
مدیریت هزینه تولید مستندات
فقط Operationهای تغییرکرده
اگر Hash یک Operation تغییر نکرده است، مستندات آن دوباره ساخته نشود.
کلید Cache:
operation_hash +
model_id +
prompt_version +
language +
template_version
تولید قطعی بخشهای ساختاری
جدول پارامترها، Status Codeها و Schemaها را با کد بسازید. از AI برای توضیح، Tutorial و نکات استفاده کنید.
پردازش جداگانه Endpointها
در صورت خطای یک Endpoint، کل تولید مستندات متوقف نشود.
مدل متناسب با وظیفه
خلاصه و بازنویسی ساده ممکن است به مدل بسیار قوی نیاز نداشته باشد.
انتخاب مدل مناسب
ویژگیهای مهم:
- پیروی دقیق از Schema
- تولید JSON معتبر
- کیفیت نگارش فارسی
- توانایی فهم کد
- حفظ نامهای فنی
- ساخت مثال سازگار
- Context Window مناسب
- هزینه و سرعت
برای مستندات کوتاه Endpoint میتوان از مدل سریعتر استفاده کرد. برای توضیح معماری یا Migration Guide چندفایلی، مدل قویتر ممکن است نتیجه بهتری ارائه دهد.
فهرست و اطلاعات بهروز مدلها در صفحه مدلهای درواره قرار دارد.
ارزیابی Documentation Generator
یک Dataset مرجع بسازید که شامل موارد زیر باشد:
- Endpoint بدون پارامتر
- Path Parameter
- Query Parameter اختیاری
- Request Body
- چند Response
- Schema تو در تو
- Enum
- Pagination
- Endpoint Deprecated
- اطلاعات ناقص
- نامهای فنی انگلیسی در متن فارسی
معیارها:
| معیار | توضیح |
|---|---|
| Contract Accuracy | تطابق با OpenAPI |
| Parameter Coverage | پوشش پارامترها |
| Hallucination Rate | اطلاعات اختراعشده |
| Example Validity | معتبر بودن مثالها |
| Code Syntax Rate | درصد مثالهای قابل Parse |
| Gap Detection | تشخیص اطلاعات ناقص |
| Readability | وضوح برای مخاطب |
| Drift Detection | تشخیص تغییر قرارداد |
| Human Edit Rate | میزان ویرایش لازم |
| Cost per Operation | هزینه هر Endpoint |
اشتباهات رایج
تولید مستندات فقط از روی Source Code
در API، OpenAPI و تست قرارداد منابع دقیقتری هستند.
پذیرش مثال بدون اجرا
مثال اشتباه اعتماد کاربر را کاهش میدهد.
حدسزدن اطلاعات ناقص
اطلاعات ناقص باید در documentation_gaps ثبت شود.
تولید همه بخشها با مدل
جدول پارامتر و Status Code را میتوان بهصورت قطعی از Schema ساخت.
نداشتن Version و Hash
بدون تشخیص Drift، مستندات دوباره قدیمی میشوند.
ترجمه نام فیلدها
فیلدهای JSON، Endpointها و نام پارامترها نباید ترجمه شوند.
مستندسازی جزئیات داخلی
مستندات عمومی باید Contract و روش استفاده را توضیح دهند، نه تمام پیادهسازی داخلی را.
انتشار خودکار بدون Review
تغییر مستندات باید همراه تغییر کد بررسی شود.
برنامه پیادهسازی در تیم
مرحله اول: کاملکردن OpenAPI
- Summary
- Description
- Field Description
- Responseها
- Exampleها
مرحله دوم: تولید Draft
برای هر Endpoint یک فایل Markdown ساخته شود.
مرحله سوم: اعتبارسنجی
Path، Method، Parameter، Response و Syntax مثالها بررسی شوند.
مرحله چهارم: Drift Detection
Hash قرارداد در CI محاسبه شود.
مرحله پنجم: اجرای مثالها
Quickstartها روی محیط آزمایشی اجرا شوند.
مرحله ششم: انتشار
فقط مستندات تأییدشده منتشر شوند.
چکلیست مستندات تولیدشده
- Path و Method درست هستند.
- تمام پارامترهای اجباری ذکر شدهاند.
- پارامتر اختیاری اجباری معرفی نشده است.
- نوع داده با Schema سازگار است.
- واحد مبلغ یا زمان مشخص است.
- Request Example معتبر است.
- Response Example با Schema تطبیق دارد.
- فقط Status Codeهای واقعی مستند شدهاند.
- مثال Python از نظر Syntax معتبر است.
- مثال curl قابل اجرا است.
- نام فیلدها ترجمه نشدهاند.
- اطلاعات ناموجود حدس زده نشدهاند.
- ابهامها ثبت شدهاند.
- Contract Hash ذخیره شده است.
- تغییرات در Code Review دیده میشوند.
پرسشهای متداول
آیا هوش مصنوعی میتواند کد را مستند کند؟
بله. AI میتواند برای تابع، کلاس، ماژول، API و معماری پیشنویس مستندات بسازد. برای نتیجه دقیق باید Source Code، Typeها، Specification و تستها را دریافت کند.
چگونه با AI مستندات API بسازیم؟
OpenAPI Schema را استخراج و هر Operation را جداگانه به مدل ارسال کنید. خروجی باید ساختاریافته باشد و Path، Method، پارامترها و Responseها دوباره با Schema تطبیق داده شوند.
آیا میتوان README را خودکار تولید کرد؟
بله، اما دستور نصب، متغیرهای محیطی و Entry Point باید از فایلهای واقعی پروژه استخراج شوند. مدل نباید آنها را حدس بزند.
چگونه از قدیمیشدن مستندات جلوگیری کنیم؟
OpenAPI یا Source Contract را Hash کنید و در CI با نسخه قبلی مقایسه کنید. اگر قرارداد تغییر کرده باشد، مستندات باید بازتولید و Review شوند.
آیا مثالهای تولیدشده توسط AI قابل اعتمادند؟
بدون تست خیر. کد Python را Parse و اجرا کنید و درخواستهای API را روی محیط آزمایشی با Schema پاسخ مقایسه کنید.
آیا AI جای Swagger UI را میگیرد؟
خیر. Swagger UI یا ابزار مشابه Reference ساختاری API را نمایش میدهد. AI میتواند توضیح فارسی، Quickstart، Tutorial و مثالهای کاربردیتر تولید کند.
برای مستندسازی از کدام مدل استفاده کنیم؟
مدل باید در پیروی از Schema، تولید کد و نگارش فارسی عملکرد مناسبی داشته باشد. مدلها و قیمت بهروز را در صفحه مدلهای درواره ببینید.
API درواره چگونه متصل میشود؟
در Backend، base_url را روی https://api.darvareh.ir/v1 قرار دهید و OpenAPI Operation را همراه پرامپت کنترلشده برای مدل ارسال کنید.
جمعبندی
هوش مصنوعی میتواند زمان تولید مستندات را کاهش دهد، اما نباید منبع حقیقت پروژه باشد. Source Code، OpenAPI، JSON Schema و تستهای قرارداد باید رفتار واقعی را تعیین کنند و مدل آنها را به توضیح قابل فهم تبدیل کند.
در پروژه این مقاله یک AI Documentation Generator ساختیم که OpenAPI برنامه FastAPI را استخراج میکند، Endpointها را جداگانه به API درواره میفرستد، خروجی را با Pydantic اعتبارسنجی میکند و مستندات Markdown فارسی همراه مثال curl و Python میسازد.
همچنین با ذخیره Hash قرارداد، تغییر API و احتمال قدیمیشدن مستندات را تشخیص دادیم. این معماری را میتوان به تولید README، Docstring، Changelog و Migration Guide گسترش داد.
برای شروع، در درواره ثبتنام و API Key دریافت کنید. سپس مدل مناسب را از صفحه مدلهای درواره انتخاب کرده و ابزار را ابتدا روی چند Endpoint دارای OpenAPI کامل آزمایش کنید.
مقالات مرتبط
- چگونه API هوش مصنوعی را به نرمافزار خود اضافه کنیم؟
- آموزش Structured Outputs و JSON Schema
- آموزش اتصال API درواره به Postman
- آموزش API درواره با cURL
- راهنمای ساخت API هوش مصنوعی آماده Production
- ساخت دستیار برنامهنویسی اختصاصی برای شرکت
- آموزش ارزیابی مدلهای هوش مصنوعی و Evals
- راهنمای API سازگار با OpenAI
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.