تبدیل کد بین زبان‌های برنامه‌نویسی با هوش مصنوعی؛ ساخت AI Code Translator

در این آموزش یاد می‌گیرید کد را بدون تغییر ناخواسته رفتار بین زبان‌ها تبدیل کنید و یک AI Code Translator بسازید که قرارداد، تست و تفاوت‌های معنایی Python و JavaScript را کنترل می‌کند.

Share
تبدیل کد بین زبان‌های برنامه‌نویسی با هوش مصنوعی؛ ساخت AI Code Translator

تبدیل کد بین زبان‌های برنامه‌نویسی با هوش مصنوعی؛ از Python تا JavaScript

تبدیل چند خط کد از یک زبان برنامه‌نویسی به زبان دیگر ساده به نظر می‌رسد. کافی است کد Python را به یک مدل هوش مصنوعی بدهیم و بگوییم:

این کد را به JavaScript تبدیل کن.

مدل نیز در چند ثانیه نسخه‌ای شبیه کد مقصد تولید می‌کند. اما در پروژه‌های واقعی، شباهت ظاهری کد به معنی یکسان‌بودن رفتار نیست.

زبان‌های برنامه‌نویسی در جزئیات مهمی با یکدیگر تفاوت دارند:

  • نوع عدد و دقت محاسبات
  • رفتار Integer و Float
  • مقدارهای null، None و undefined
  • Truthiness
  • مقایسه مساوی
  • نحوه گردکردن
  • مدیریت تاریخ و Timezone
  • ترتیب Propertyهای Object
  • رفتار Exception
  • Async و Promise
  • Unicode و طول رشته
  • Regex Dialect
  • Serialization
  • Overflow
  • Mutable و Immutable بودن داده‌ها
  • کتابخانه استاندارد
  • سیستم Type
  • مدیریت Dependency

بنابراین تبدیل کد با هوش مصنوعی نباید فقط «ترجمه Syntax» باشد. هدف اصلی باید حفظ Contract و رفتار قابل مشاهده نرم‌افزار باشد.

در این مقاله یک پروژه واقعی می‌سازیم که:

  1. یک تابع قیمت‌گذاری را در Python تعریف می‌کند.
  2. رفتار آن را با Specification مستقل ثبت می‌کند.
  3. Test Fixtureهای مشترک می‌سازد.
  4. با API درواره برنامه مهاجرت تولید می‌کند.
  5. نسخه JavaScript را با هوش مصنوعی می‌سازد.
  6. خروجی Python و JavaScript را روی داده‌های یکسان اجرا می‌کند.
  7. اختلاف رفتاری دو نسخه را پیدا می‌کند.
  8. نتیجه را فقط در صورت عبور از تست‌ها قابل قبول می‌داند.

AI Code Translator چیست؟

AI Code Translator ابزاری است که Source Code یک زبان را دریافت و نسخه‌ای معادل در زبان مقصد تولید می‌کند.

ورودی:

def clamp(value, minimum, maximum):
    return max(minimum, min(value, maximum))

خروجی JavaScript:

export function clamp(value, minimum, maximum) {
  return Math.max(minimum, Math.min(value, maximum));
}

این نمونه ساده است، اما تبدیل یک ماژول واقعی به اطلاعات بیشتری نیاز دارد:

  • زبان و نسخه مبدأ
  • زبان و نسخه مقصد
  • Runtime مقصد
  • Specification
  • API عمومی
  • Typeها
  • Dependencyها
  • Test Suite
  • اثرهای جانبی
  • محدودیت‌های عملکرد
  • رفتار خطا
  • قرارداد Serialization
  • ساختار فایل مقصد

تفاوت ترجمه کد، رفکتور و بازنویسی

ترجمه یا Code Translation

رفتار موجود در یک زبان دیگر پیاده‌سازی می‌شود.

Python → JavaScript
PHP → Python
Java → Kotlin
C# → TypeScript

رفکتور

زبان و رفتار عمومی حفظ می‌شوند، اما ساختار داخلی بهبود می‌یابد.

Python قدیمی → Python خواناتر

بازنویسی

پیاده‌سازی جدید ممکن است معماری، Dependency و رفتار متفاوتی داشته باشد.

برنامه قدیمی PHP → سرویس‌های جدید TypeScript

تبدیل بین دو زبان معمولاً به بازنویسی نزدیک‌تر از Refactor است، زیرا تفاوت‌های معنایی زبان مقصد می‌تواند رفتار را تغییر دهد.

هوش مصنوعی در تبدیل کد چه کمکی می‌کند؟

تبدیل Syntax

  • تعریف تابع
  • کلاس
  • شرط
  • حلقه
  • Collection
  • Import
  • Exception
  • Async Function

نگاشت کتابخانه استاندارد

برای مثال:

json.dumps(data)

ممکن است به این تبدیل شود:

JSON.stringify(data)

تبدیل Typeها

list[str]

به TypeScript:

string[]

ساخت تست مقصد

مدل می‌تواند Test Suite معادل برای Runtime مقصد تولید کند.

شناسایی تفاوت‌های معنایی

یک مدل مناسب می‌تواند هشدار دهد که Decimal در Python معادل مستقیم Number در JavaScript نیست.

تولید Migration Plan

قبل از تولید کد، ماژول‌ها، Dependencyها و ترتیب مهاجرت را مشخص می‌کند.

توضیح بخش‌های نیازمند تصمیم

برای مثال:

در نسخه Python، Dictionary با کلید ناموجود KeyError ایجاد می‌کند.
در JavaScript، دسترسی به Property ناموجود undefined برمی‌گرداند.
رفتار مقصد باید صریحاً مشخص شود.

چه چیزهایی را نباید به مدل واگذار کنیم؟

مدل نباید تنها مرجع این تصمیم‌ها باشد:

  • آیا رفتار قدیمی صحیح است؟
  • کدام خطا باید حفظ شود؟
  • آیا دقت عددی قابل کاهش است؟
  • آیا تغییر API مجاز است؟
  • آیا Dependency جدید قابل قبول است؟
  • آیا ترتیب Side Effectها قابل تغییر است؟
  • آیا نتیجه Migration آماده انتشار است؟
  • آیا تست‌های موجود پوشش کافی دارند؟

مدل پیش‌نویس و تحلیل تولید می‌کند؛ Test Suite و Specification درباره صحت نتیجه تصمیم می‌گیرند.

مهم‌ترین تفاوت‌های Python و JavaScript

None، null و undefined

Python:

value = None

JavaScript:

const value = null;

اما JavaScript مقدار undefined نیز دارد. تبدیل خودکار این دو مقدار ممکن است Contract JSON را تغییر دهد.

تقسیم عدد صحیح

Python:

5 // 2

نتیجه:

2

JavaScript:

5 / 2

نتیجه:

2.5

معادل نزدیک‌تر برای اعداد مثبت:

Math.floor(5 / 2);

اما رفتار floor برای عدد منفی نیز باید بررسی شود.

دقت عددی

Python از Integer با دقت دلخواه پشتیبانی می‌کند. Number در JavaScript محدودیت دقت عدد صحیح دارد.

Number.MAX_SAFE_INTEGER

برای عددهای بزرگ ممکن است به BigInt نیاز باشد.

مقادیر Truthy و Falsy

Python:

bool([])

نتیجه:

False

JavaScript:

Boolean([])

نتیجه:

true

تبدیل مستقیم شرط‌ها می‌تواند رفتار را تغییر دهد.

مقایسه

Python:

1 == True

نتیجه True است.

JavaScript:

1 === true

نتیجه false است.

در JavaScript باید تا حد امکان از مقایسه صریح و Typeهای مشخص استفاده شود.

رشته و Unicode

در Python، len تعداد Code Pointها را در بسیاری از حالت‌های رایج نشان می‌دهد. در JavaScript، length تعداد UTF-16 Code Unitها را برمی‌گرداند.

برای بعضی Emojiها:

"😀".length

نتیجه:

2

این تفاوت در Validation طول رشته مهم است.

Dictionary و Object

Python Dictionary می‌تواند انواع مختلفی از کلیدها داشته باشد. Object در JavaScript عمدتاً کلیدهای String یا Symbol دارد. برای برخی کاربردها Map معادل مناسب‌تری است.

Exception

Python:

raise ValueError("invalid page")

JavaScript:

throw new RangeError("invalid page");

نوع و متن خطا باید در Contract تعریف شوند.

Async

Python:

result = await service.fetch()

JavaScript:

const result = await service.fetch();

شباهت Syntax به معنی یکسان‌بودن مدیریت Task، Cancellation، Timeout و Event Loop نیست.

فرایند صحیح تبدیل کد با AI

مرحله اول: Inventory

مشخص کنید چه چیزی باید مهاجرت کند:

  • فایل‌ها
  • ماژول‌ها
  • API عمومی
  • CLI
  • Dependencyها
  • تست‌ها
  • Configuration
  • ورودی و خروجی
  • فرمت فایل
  • اثرهای جانبی

مرحله دوم: تعریف Contract

برای هر تابع یا Endpoint:

  • ورودی
  • خروجی
  • نوع داده
  • Validation
  • Exception
  • Edge Case
  • Side Effect
  • ترتیب عملیات

مرحله سوم: Test Baseline

تمام تست‌های نسخه مبدأ باید اجرا شوند. وضعیت فعلی باید مشخص باشد.

مرحله چهارم: Fixture مشترک

داده‌های ورودی و خروجی مرجع در JSON ذخیره شوند تا هر دو Runtime از همان Fixture استفاده کنند.

مرحله پنجم: Migration Plan

مدل فقط برنامه تبدیل را تولید کند؛ هنوز کد ساخته نشود.

مرحله ششم: تبدیل یک ماژول

تبدیل باید مرحله‌ای باشد، نه کل Repository در یک درخواست.

مرحله هفتم: اجرای تست مقصد

کد مقصد Compile یا Parse و سپس تست شود.

مرحله هشتم: Differential Testing

نسخه مبدأ و مقصد روی ورودی یکسان اجرا و خروجی مقایسه شوند.

مرحله نهم: بررسی انسانی

تفاوت‌های معنایی، Dependency و خوانایی بررسی شوند.

پروژه عملی: تبدیل موتور محاسبه قیمت از Python به JavaScript

در این پروژه تمام مبلغ‌ها در کوچک‌ترین واحد پول به‌صورت Integer نگهداری می‌شوند. برای مثال اگر واحد سیستم ریال باشد، مقدار 1250000 دقیقاً یک عدد صحیح است.

نرخ تخفیف و مالیات نیز با Basis Point تعریف می‌شوند:

10000 basis points = 100%
1000 basis points = 10%
900 basis points = 9%

این طراحی از بسیاری از اختلاف‌های Float میان Python و JavaScript جلوگیری می‌کند.

Specification پروژه

فایل specifications/pricing.md:

# Pricing contract

## Input

تابع calculate_order_total این ورودی‌ها را می‌گیرد:

- items: فهرستی از Itemها
- discount_bps: نرخ تخفیف بر حسب Basis Point
- tax_bps: نرخ مالیات بر حسب Basis Point

هر Item:

- sku: رشته غیرخالی
- unit_price: عدد صحیح غیرمنفی
- quantity: عدد صحیح غیرمنفی

## Validation

- items باید Array یا List باشد.
- discount_bps باید عدد صحیح بین 0 و 10000 باشد.
- tax_bps باید عدد صحیح بین 0 و 10000 باشد.
- unit_price و quantity باید عدد صحیح غیرمنفی باشند.
- sku باید رشته غیرخالی باشد.

## Calculation

- subtotal مجموع unit_price ضرب‌در quantity است.
- discount از subtotal محاسبه می‌شود.
- taxable_amount برابر subtotal منهای discount است.
- tax از taxable_amount محاسبه می‌شود.
- total برابر taxable_amount به‌علاوه tax است.
- تقسیم نسبت‌ها با روش Round Half Up انجام می‌شود.

## Output

خروجی شامل عددهای صحیح زیر است:

- subtotal
- discount
- taxable_amount
- tax
- total

## Compatibility

نسخه Python و JavaScript باید برای Fixtureهای مشترک خروجی یکسان یا خطای معادل تولید کنند.

ایجاد پروژه

mkdir ai-code-translator
cd ai-code-translator

python -m venv .venv

فعال‌سازی در Linux و macOS:

source .venv/bin/activate

فعال‌سازی در Windows:

.venv\Scripts\Activate.ps1

نصب وابستگی‌ها:

pip install \
  openai \
  python-dotenv \
  pydantic \
  pytest

ساخت پوشه‌ها:

mkdir \
  source_python \
  target_javascript \
  tests_python \
  tests_javascript \
  fixtures \
  translator \
  specifications \
  plans \
  output

فایل‌های اصلی:

source_python/pricing.py
target_javascript/pricing.mjs
tests_python/test_pricing.py
tests_javascript/pricing.test.mjs
fixtures/pricing-cases.json
translator/__init__.py
translator/schemas.py
translator/collector.py
translator/planner.py
translator/generator.py
translator/validator.py
translator/renderer.py
plan_translation.py
generate_translation.py
compare_runtimes.py

پیاده‌سازی مبدأ در Python

فایل source_python/pricing.py:

from dataclasses import dataclass


BASIS_POINT_DENOMINATOR = 10_000


@dataclass(frozen=True)
class Item:
    sku: str
    unit_price: int
    quantity: int


def round_ratio(
    value: int,
    numerator: int,
    denominator: int,
) -> int:
    return (
        value * numerator
        + denominator // 2
    ) // denominator


def validate_rate(
    value: int,
    name: str,
) -> None:
    if not isinstance(value, int):
        raise TypeError(
            f"{name} must be an integer"
        )

    if value < 0 or value > 10_000:
        raise ValueError(
            f"{name} must be between "
            f"0 and 10000"
        )


def validate_item(
    item: Item,
) -> None:
    if not isinstance(item.sku, str):
        raise TypeError(
            "sku must be a string"
        )

    if not item.sku:
        raise ValueError(
            "sku cannot be empty"
        )

    if not isinstance(
        item.unit_price,
        int,
    ):
        raise TypeError(
            "unit_price must be an integer"
        )

    if item.unit_price < 0:
        raise ValueError(
            "unit_price cannot be negative"
        )

    if not isinstance(
        item.quantity,
        int,
    ):
        raise TypeError(
            "quantity must be an integer"
        )

    if item.quantity < 0:
        raise ValueError(
            "quantity cannot be negative"
        )


def calculate_order_total(
    items: list[Item],
    discount_bps: int = 0,
    tax_bps: int = 0,
) -> dict[str, int]:
    if not isinstance(items, list):
        raise TypeError(
            "items must be a list"
        )

    validate_rate(
        discount_bps,
        "discount_bps",
    )

    validate_rate(
        tax_bps,
        "tax_bps",
    )

    subtotal = 0

    for item in items:
        validate_item(item)

        subtotal += (
            item.unit_price
            * item.quantity
        )

    discount = round_ratio(
        subtotal,
        discount_bps,
        BASIS_POINT_DENOMINATOR,
    )

    taxable_amount = (
        subtotal - discount
    )

    tax = round_ratio(
        taxable_amount,
        tax_bps,
        BASIS_POINT_DENOMINATOR,
    )

    total = taxable_amount + tax

    return {
        "subtotal": subtotal,
        "discount": discount,
        "taxable_amount": (
            taxable_amount
        ),
        "tax": tax,
        "total": total,
    }

Test Fixture مشترک

فایل fixtures/pricing-cases.json:

[
  {
    "name": "empty order",
    "input": {
      "items": [],
      "discount_bps": 0,
      "tax_bps": 0
    },
    "expected": {
      "subtotal": 0,
      "discount": 0,
      "taxable_amount": 0,
      "tax": 0,
      "total": 0
    }
  },
  {
    "name": "one item without discount or tax",
    "input": {
      "items": [
        {
          "sku": "A",
          "unit_price": 10000,
          "quantity": 2
        }
      ],
      "discount_bps": 0,
      "tax_bps": 0
    },
    "expected": {
      "subtotal": 20000,
      "discount": 0,
      "taxable_amount": 20000,
      "tax": 0,
      "total": 20000
    }
  },
  {
    "name": "discount before tax",
    "input": {
      "items": [
        {
          "sku": "A",
          "unit_price": 10000,
          "quantity": 1
        }
      ],
      "discount_bps": 1000,
      "tax_bps": 2000
    },
    "expected": {
      "subtotal": 10000,
      "discount": 1000,
      "taxable_amount": 9000,
      "tax": 1800,
      "total": 10800
    }
  },
  {
    "name": "round half up",
    "input": {
      "items": [
        {
          "sku": "A",
          "unit_price": 5,
          "quantity": 1
        }
      ],
      "discount_bps": 1000,
      "tax_bps": 0
    },
    "expected": {
      "subtotal": 5,
      "discount": 1,
      "taxable_amount": 4,
      "tax": 0,
      "total": 4
    }
  },
  {
    "name": "multiple items",
    "input": {
      "items": [
        {
          "sku": "A",
          "unit_price": 1250,
          "quantity": 3
        },
        {
          "sku": "B",
          "unit_price": 980,
          "quantity": 2
        }
      ],
      "discount_bps": 500,
      "tax_bps": 900
    },
    "expected": {
      "subtotal": 5710,
      "discount": 286,
      "taxable_amount": 5424,
      "tax": 488,
      "total": 5912
    }
  }
]

در مورد آخر:

Subtotal = 3750 + 1960 = 5710
Discount = 5710 × 500 / 10000 = 285.5 → 286
Taxable = 5710 - 286 = 5424
Tax = 5424 × 900 / 10000 = 488.16 → 488
Total = 5424 + 488 = 5912

فرمول‌ها به‌صورت متن ساده نوشته شده‌اند تا با Ghost سازگار باشند.

تست نسخه Python

فایل tests_python/test_pricing.py:

import json
from pathlib import Path

import pytest

from source_python.pricing import (
    Item,
    calculate_order_total,
)


CASES = json.loads(
    Path(
        "fixtures/pricing-cases.json"
    ).read_text(encoding="utf-8")
)


@pytest.mark.parametrize(
    "case",
    CASES,
    ids=[
        case["name"]
        for case in CASES
    ],
)
def test_pricing_cases(case):
    items = [
        Item(**item)
        for item in (
            case["input"]["items"]
        )
    ]

    result = calculate_order_total(
        items,
        discount_bps=(
            case["input"][
                "discount_bps"
            ]
        ),
        tax_bps=(
            case["input"]["tax_bps"]
        ),
    )

    assert result == case["expected"]


@pytest.mark.parametrize(
    "discount_bps",
    [
        -1,
        10001,
    ],
)
def test_rejects_invalid_discount(
    discount_bps,
):
    with pytest.raises(ValueError):
        calculate_order_total(
            [],
            discount_bps=discount_bps,
        )


def test_rejects_empty_sku():
    with pytest.raises(
        ValueError,
        match="sku cannot be empty",
    ):
        calculate_order_total(
            [
                Item(
                    sku="",
                    unit_price=100,
                    quantity=1,
                )
            ]
        )

اجرای تست:

pytest -q tests_python

قبل از مهاجرت، تست‌های نسخه مبدأ باید موفق باشند.

تعریف Schema برنامه مهاجرت

فایل translator/schemas.py:

from typing import Literal

from pydantic import BaseModel, Field


class SemanticDifference(BaseModel):
    topic: str
    source_behavior: str
    target_risk: str
    required_strategy: str
    verification: str


class DependencyMapping(BaseModel):
    source_dependency: str
    target_dependency: str | None = None
    strategy: Literal[
        "standard_library",
        "third_party",
        "custom_implementation",
        "not_required",
        "unresolved",
    ]
    notes: str


class MigrationStep(BaseModel):
    order: int
    title: str
    source_files: list[str]
    target_files: list[str]
    goal: str
    preserved_contracts: list[str]
    tests_to_run: list[str]
    completion_criteria: list[str]
    risk: Literal[
        "high",
        "medium",
        "low",
    ]


class TranslationPlan(BaseModel):
    source_language: str
    source_version: str
    target_language: str
    target_runtime: str
    public_api: list[str]
    behavior_contracts: list[str]
    semantic_differences: list[
        SemanticDifference
    ]
    dependency_mapping: list[
        DependencyMapping
    ]
    steps: list[MigrationStep]
    out_of_scope: list[str]
    unresolved_questions: list[str]


class GeneratedFile(BaseModel):
    path: str
    language: str
    content: str
    purpose: str


class TranslationOutput(BaseModel):
    summary: str
    generated_files: list[
        GeneratedFile
    ]
    preserved_behaviors: list[str]
    known_differences: list[str]
    assumptions: list[str]
    validation_commands: list[str]

جمع‌آوری Context مهاجرت

فایل translator/collector.py:

import platform
import subprocess
import sys
from pathlib import Path


def read_file(
    file_path: Path,
    max_chars: int = 60_000,
) -> str:
    content = file_path.read_text(
        encoding="utf-8"
    )

    if len(content) > max_chars:
        return (
            content[:max_chars]
            + "\n\n[CONTENT TRUNCATED]"
        )

    return content


def run_python_tests() -> dict:
    command = [
        sys.executable,
        "-m",
        "pytest",
        "-q",
        "tests_python",
    ]

    result = subprocess.run(
        command,
        capture_output=True,
        text=True,
        timeout=60,
        check=False,
    )

    return {
        "command": command,
        "return_code": result.returncode,
        "stdout": result.stdout[-10_000:],
        "stderr": result.stderr[-5_000:],
    }


def collect_translation_context() -> dict:
    return {
        "source_language": "Python",
        "source_version": (
            sys.version.split()[0]
        ),
        "target_language": (
            "JavaScript ESM"
        ),
        "target_runtime": (
            "Node.js 20 or newer"
        ),
        "source_files": {
            "source_python/pricing.py": (
                read_file(
                    Path(
                        "source_python/"
                        "pricing.py"
                    )
                )
            )
        },
        "specification": read_file(
            Path(
                "specifications/pricing.md"
            )
        ),
        "python_tests": read_file(
            Path(
                "tests_python/"
                "test_pricing.py"
            )
        ),
        "shared_fixtures": read_file(
            Path(
                "fixtures/"
                "pricing-cases.json"
            )
        ),
        "test_baseline": (
            run_python_tests()
        ),
        "constraints": [
            "Use only Node.js standard library.",
            "Generate ECMAScript modules.",
            "Do not add npm dependencies.",
            "Keep all monetary values as integers.",
            "Reject unsafe JavaScript integers.",
            "Preserve calculation order.",
            "Preserve error messages where practical.",
            "Do not change the shared fixture format.",
        ],
        "environment": {
            "platform": platform.platform(),
        },
    }

تولید Migration Plan با API درواره

فایل translator/planner.py:

import json
import os

from dotenv import load_dotenv
from openai import OpenAI
from pydantic import ValidationError

from translator.schemas import (
    TranslationPlan,
)


load_dotenv()

api_key = os.getenv("DARVAREH_API_KEY")
model = os.getenv("DARVAREH_MODEL")

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 = """
تو یک مهندس ارشد مهاجرت نرم‌افزار هستی.

در این مرحله فقط Migration Plan تولید کن و کد مقصد نساز.

قواعد:
- Specification منبع رفتار مورد انتظار است.
- Source Code منبع رفتار فعلی است.
- تفاوت معنایی Python و JavaScript را صریح ثبت کن.
- Syntax مشابه را معادل رفتاری قطعی فرض نکن.
- برنامه را مرحله‌ای و قابل تست بساز.
- Dependency جدید پیشنهاد نده مگر در Constraint مجاز باشد.
- تمام مبلغ‌ها باید Integer باقی بمانند.
- رفتار Round Half Up باید حفظ شود.
- خروجی فقط JSON معتبر باشد.
"""


OUTPUT_TEMPLATE = {
    "source_language": "Python",
    "source_version": "3.12",
    "target_language": "JavaScript ESM",
    "target_runtime": "Node.js 20+",
    "public_api": [
        "calculateOrderTotal"
    ],
    "behavior_contracts": [
        "string"
    ],
    "semantic_differences": [
        {
            "topic": "integer precision",
            "source_behavior": "string",
            "target_risk": "string",
            "required_strategy": "string",
            "verification": "string",
        }
    ],
    "dependency_mapping": [],
    "steps": [
        {
            "order": 1,
            "title": "string",
            "source_files": [
                "source_python/pricing.py"
            ],
            "target_files": [
                "target_javascript/pricing.mjs"
            ],
            "goal": "string",
            "preserved_contracts": [
                "string"
            ],
            "tests_to_run": [
                "node --test tests_javascript"
            ],
            "completion_criteria": [
                "string"
            ],
            "risk": "medium",
        }
    ],
    "out_of_scope": [],
    "unresolved_questions": [],
}


def generate_translation_plan(
    context: dict,
) -> TranslationPlan:
    response = client.chat.completions.create(
        model=model,
        temperature=0.1,
        messages=[
            {
                "role": "system",
                "content": SYSTEM_PROMPT,
            },
            {
                "role": "user",
                "content": (
                    "Context مهاجرت:\n\n"
                    + json.dumps(
                        context,
                        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 an "
            "empty migration plan."
        )

    try:
        parsed = json.loads(raw_output)
    except json.JSONDecodeError as error:
        raise RuntimeError(
            f"Invalid JSON from model: "
            f"{error}"
        ) from error

    try:
        return TranslationPlan.model_validate(
            parsed
        )
    except ValidationError as error:
        raise RuntimeError(
            f"Migration plan validation "
            f"failed: {error}"
        ) from error

ساخت برنامه مهاجرت

فایل plan_translation.py:

from pathlib import Path

from translator.collector import (
    collect_translation_context,
)
from translator.planner import (
    generate_translation_plan,
)


def main():
    context = (
        collect_translation_context()
    )

    if (
        context["test_baseline"][
            "return_code"
        ]
        != 0
    ):
        raise RuntimeError(
            "Python baseline tests must pass "
            "before translation."
        )

    plan = generate_translation_plan(
        context
    )

    output_path = Path(
        "plans/translation-plan.json"
    )

    output_path.parent.mkdir(
        parents=True,
        exist_ok=True,
    )

    output_path.write_text(
        plan.model_dump_json(indent=2),
        encoding="utf-8",
    )

    print(plan.model_dump_json(indent=2))
    print(f"\nPlan saved to {output_path}")


if __name__ == "__main__":
    main()

فایل .env:

DARVAREH_API_KEY=YOUR_API_KEY
DARVAREH_MODEL=MODEL_ID_DARVAREH

اجرا:

python plan_translation.py

برای دریافت API Key در درواره ثبت‌نام کنید. Model ID را از صفحه مدل‌های درواره بردارید.

تولید کد مقصد

فایل translator/generator.py:

import json
from pathlib import Path

from translator.planner import (
    client,
    model,
)
from translator.schemas import (
    TranslationOutput,
    TranslationPlan,
)


SYSTEM_PROMPT = """
تو فقط کد مقصد و تست مقصد را بر اساس Plan تأییدشده تولید می‌کنی.

قواعد:
- فقط فایل‌های مجاز Plan را تولید کن.
- از JavaScript ESM و Node.js standard library استفاده کن.
- Dependency خارجی اضافه نکن.
- از Number فقط با کنترل Number.isSafeInteger استفاده کن.
- تمام مبلغ‌ها Integer باقی بمانند.
- Round Half Up نسخه Python را حفظ کن.
- Fixture مشترک را تغییر نده.
- تست‌ها از node:test و node:assert/strict استفاده کنند.
- API عمومی calculateOrderTotal باشد.
- Feature جدید اضافه نکن.
- خروجی فقط JSON معتبر باشد.
"""


OUTPUT_TEMPLATE = {
    "summary": "string",
    "generated_files": [
        {
            "path": (
                "target_javascript/"
                "pricing.mjs"
            ),
            "language": "javascript",
            "content": "string",
            "purpose": "string",
        },
        {
            "path": (
                "tests_javascript/"
                "pricing.test.mjs"
            ),
            "language": "javascript",
            "content": "string",
            "purpose": "string",
        },
    ],
    "preserved_behaviors": [
        "string"
    ],
    "known_differences": [],
    "assumptions": [],
    "validation_commands": [
        "node --check target_javascript/pricing.mjs",
        "node --test tests_javascript/pricing.test.mjs"
    ],
}


def generate_translation() -> TranslationOutput:
    plan = TranslationPlan.model_validate_json(
        Path(
            "plans/translation-plan.json"
        ).read_text(encoding="utf-8")
    )

    payload = {
        "plan": plan.model_dump(),
        "source": Path(
            "source_python/pricing.py"
        ).read_text(encoding="utf-8"),
        "specification": Path(
            "specifications/pricing.md"
        ).read_text(encoding="utf-8"),
        "python_tests": Path(
            "tests_python/test_pricing.py"
        ).read_text(encoding="utf-8"),
        "shared_fixtures": Path(
            "fixtures/pricing-cases.json"
        ).read_text(encoding="utf-8"),
    }

    response = client.chat.completions.create(
        model=model,
        temperature=0,
        messages=[
            {
                "role": "system",
                "content": SYSTEM_PROMPT,
            },
            {
                "role": "user",
                "content": (
                    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 no "
            "translated files."
        )

    return TranslationOutput.model_validate(
        json.loads(raw_output)
    )

اعتبارسنجی مسیر فایل‌ها

فایل translator/validator.py:

from pathlib import Path

from translator.schemas import (
    TranslationOutput,
)


ALLOWED_OUTPUT_FILES = {
    "target_javascript/pricing.mjs",
    "tests_javascript/pricing.test.mjs",
}


class TranslationValidationError(
    ValueError
):
    pass


def validate_generated_files(
    output: TranslationOutput,
) -> None:
    generated_paths = {
        generated_file.path
        for generated_file
        in output.generated_files
    }

    unknown_paths = (
        generated_paths
        - ALLOWED_OUTPUT_FILES
    )

    if unknown_paths:
        raise TranslationValidationError(
            "Unexpected generated files: "
            + ", ".join(
                sorted(unknown_paths)
            )
        )

    missing_paths = (
        ALLOWED_OUTPUT_FILES
        - generated_paths
    )

    if missing_paths:
        raise TranslationValidationError(
            "Required files were not "
            "generated: "
            + ", ".join(
                sorted(missing_paths)
            )
        )

    for generated_file in (
        output.generated_files
    ):
        path = Path(
            generated_file.path
        )

        if ".." in path.parts:
            raise TranslationValidationError(
                "Invalid output path."
            )

ذخیره فایل‌های تولیدشده

فایل generate_translation.py:

from pathlib import Path

from translator.generator import (
    generate_translation,
)
from translator.validator import (
    validate_generated_files,
)


def main():
    output = generate_translation()

    validate_generated_files(
        output
    )

    for generated_file in (
        output.generated_files
    ):
        output_path = Path(
            generated_file.path
        )

        output_path.parent.mkdir(
            parents=True,
            exist_ok=True,
        )

        output_path.write_text(
            generated_file.content,
            encoding="utf-8",
        )

        print(
            f"Generated {output_path}"
        )

    metadata_path = Path(
        "output/translation-result.json"
    )

    metadata_path.parent.mkdir(
        parents=True,
        exist_ok=True,
    )

    metadata_path.write_text(
        output.model_dump_json(indent=2),
        encoding="utf-8",
    )


if __name__ == "__main__":
    main()

اجرا:

python generate_translation.py

فایل‌های تولیدشده باید پیش از اجرا بررسی شوند.

نسخه JavaScript مورد انتظار

فایل target_javascript/pricing.mjs باید ساختاری نزدیک به این داشته باشد:

const BASIS_POINT_DENOMINATOR = 10_000;

function assertSafeInteger(value, name) {
  if (!Number.isSafeInteger(value)) {
    throw new TypeError(
      `${name} must be a safe integer`,
    );
  }
}

function roundRatio(
  value,
  numerator,
  denominator,
) {
  assertSafeInteger(value, "value");
  assertSafeInteger(
    numerator,
    "numerator",
  );
  assertSafeInteger(
    denominator,
    "denominator",
  );

  const multiplied = value * numerator;

  if (!Number.isSafeInteger(multiplied)) {
    throw new RangeError(
      "calculation exceeds safe integer range",
    );
  }

  return Math.floor(
    (
      multiplied
      + Math.floor(denominator / 2)
    )
    / denominator,
  );
}

function validateRate(value, name) {
  assertSafeInteger(value, name);

  if (value < 0 || value > 10_000) {
    throw new RangeError(
      `${name} must be between 0 and 10000`,
    );
  }
}

function validateItem(item) {
  if (
    typeof item !== "object"
    || item === null
    || Array.isArray(item)
  ) {
    throw new TypeError(
      "item must be an object",
    );
  }

  if (typeof item.sku !== "string") {
    throw new TypeError(
      "sku must be a string",
    );
  }

  if (item.sku.length === 0) {
    throw new RangeError(
      "sku cannot be empty",
    );
  }

  assertSafeInteger(
    item.unit_price,
    "unit_price",
  );

  if (item.unit_price < 0) {
    throw new RangeError(
      "unit_price cannot be negative",
    );
  }

  assertSafeInteger(
    item.quantity,
    "quantity",
  );

  if (item.quantity < 0) {
    throw new RangeError(
      "quantity cannot be negative",
    );
  }
}

export function calculateOrderTotal(
  items,
  discountBps = 0,
  taxBps = 0,
) {
  if (!Array.isArray(items)) {
    throw new TypeError(
      "items must be an array",
    );
  }

  validateRate(
    discountBps,
    "discount_bps",
  );

  validateRate(
    taxBps,
    "tax_bps",
  );

  let subtotal = 0;

  for (const item of items) {
    validateItem(item);

    const lineTotal = (
      item.unit_price
      * item.quantity
    );

    if (!Number.isSafeInteger(lineTotal)) {
      throw new RangeError(
        "line total exceeds safe integer range",
      );
    }

    subtotal += lineTotal;

    if (!Number.isSafeInteger(subtotal)) {
      throw new RangeError(
        "subtotal exceeds safe integer range",
      );
    }
  }

  const discount = roundRatio(
    subtotal,
    discountBps,
    BASIS_POINT_DENOMINATOR,
  );

  const taxableAmount = (
    subtotal - discount
  );

  const tax = roundRatio(
    taxableAmount,
    taxBps,
    BASIS_POINT_DENOMINATOR,
  );

  const total = taxableAmount + tax;

  if (!Number.isSafeInteger(total)) {
    throw new RangeError(
      "total exceeds safe integer range",
    );
  }

  return {
    subtotal,
    discount,
    taxable_amount: taxableAmount,
    tax,
    total,
  };
}

نکته مهم: نسخه JavaScript برای اعداد بزرگ‌تر از محدوده Safe Integer خطا می‌دهد. نسخه Python چنین محدودیتی ندارد. این یک تفاوت شناخته‌شده است که باید در Migration Report ثبت شود یا با BigInt حل شود.

تست JavaScript با Node.js

فایل tests_javascript/pricing.test.mjs:

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";

import {
  calculateOrderTotal,
} from "../target_javascript/pricing.mjs";


const fixtureUrl = new URL(
  "../fixtures/pricing-cases.json",
  import.meta.url,
);

const cases = JSON.parse(
  await readFile(
    fixtureUrl,
    "utf8",
  ),
);

for (const caseItem of cases) {
  test(caseItem.name, () => {
    const input = caseItem.input;

    const result = calculateOrderTotal(
      input.items,
      input.discount_bps,
      input.tax_bps,
    );

    assert.deepEqual(
      result,
      caseItem.expected,
    );
  });
}

test(
  "rejects an empty sku",
  () => {
    assert.throws(
      () => calculateOrderTotal(
        [
          {
            sku: "",
            unit_price: 100,
            quantity: 1,
          },
        ],
      ),
      /sku cannot be empty/,
    );
  },
);

test(
  "rejects an invalid discount rate",
  () => {
    assert.throws(
      () => calculateOrderTotal(
        [],
        10_001,
        0,
      ),
      /discount_bps must be between/,
    );
  },
);

بررسی Syntax و اجرای تست مقصد

بررسی Syntax:

node --check target_javascript/pricing.mjs

اجرای تست:

node --test tests_javascript/pricing.test.mjs

قبولی تست مقصد لازم است، اما هنوز برای اثبات کامل هم‌ارزی کافی نیست.

Differential Testing میان Python و JavaScript

هدف این است که هر دو نسخه روی ورودی یکسان اجرا شوند و خروجی‌هایشان مقایسه شود.

Runner نسخه Python

فایل source_python/run_case.py:

import json
import sys

from source_python.pricing import (
    Item,
    calculate_order_total,
)


def main():
    payload = json.loads(
        sys.stdin.read()
    )

    try:
        items = [
            Item(**item)
            for item in payload["items"]
        ]

        result = calculate_order_total(
            items,
            discount_bps=payload.get(
                "discount_bps",
                0,
            ),
            tax_bps=payload.get(
                "tax_bps",
                0,
            ),
        )

        output = {
            "ok": True,
            "result": result,
            "error": None,
        }

    except Exception as error:
        output = {
            "ok": False,
            "result": None,
            "error": {
                "type": (
                    type(error).__name__
                ),
                "message": str(error),
            },
        }

    print(
        json.dumps(
            output,
            ensure_ascii=False,
        )
    )


if __name__ == "__main__":
    main()

Runner نسخه JavaScript

فایل target_javascript/run-case.mjs:

import {
  calculateOrderTotal,
} from "./pricing.mjs";


let input = "";

for await (const chunk of process.stdin) {
  input += chunk;
}

const payload = JSON.parse(input);

let output;

try {
  const result = calculateOrderTotal(
    payload.items,
    payload.discount_bps ?? 0,
    payload.tax_bps ?? 0,
  );

  output = {
    ok: true,
    result,
    error: null,
  };
} catch (error) {
  output = {
    ok: false,
    result: null,
    error: {
      type: error.constructor.name,
      message: error.message,
    },
  };
}

process.stdout.write(
  JSON.stringify(output),
);

اسکریپت مقایسه Runtimeها

فایل compare_runtimes.py:

import json
import subprocess
import sys
from pathlib import Path


FIXTURES = json.loads(
    Path(
        "fixtures/pricing-cases.json"
    ).read_text(encoding="utf-8")
)


def run_process(
    command: list[str],
    payload: dict,
) -> dict:
    result = subprocess.run(
        command,
        input=json.dumps(payload),
        capture_output=True,
        text=True,
        timeout=10,
        check=False,
    )

    if result.returncode != 0:
        raise RuntimeError(
            result.stderr.strip()
            or "Runtime process failed."
        )

    return json.loads(
        result.stdout
    )


def main():
    differences = []

    for case in FIXTURES:
        payload = case["input"]

        python_output = run_process(
            [
                sys.executable,
                "-m",
                "source_python.run_case",
            ],
            payload,
        )

        javascript_output = run_process(
            [
                "node",
                "target_javascript/"
                "run-case.mjs",
            ],
            payload,
        )

        if (
            python_output
            != javascript_output
        ):
            differences.append(
                {
                    "case": case["name"],
                    "python": python_output,
                    "javascript": (
                        javascript_output
                    ),
                }
            )

    if differences:
        print(
            json.dumps(
                differences,
                ensure_ascii=False,
                indent=2,
            )
        )

        raise SystemExit(1)

    print(
        f"All {len(FIXTURES)} cases "
        "matched across runtimes."
    )


if __name__ == "__main__":
    main()

اجرا:

python compare_runtimes.py

خروجی موفق:

All 5 cases matched across runtimes.

مقایسه Exceptionها

نوع Exception میان زبان‌ها یکسان نیست:

PythonJavaScript
TypeErrorTypeError
ValueErrorمعمولاً RangeError یا Error
KeyErrorرفتار مستقیم مشابه ندارد
IndexErrorممکن است undefined دریافت شود

بنابراین مقایسه مستقیم نام Exception همیشه مناسب نیست. بهتر است یک Error Contract زبان‌خنثی تعریف کنید:

{
  "code": "INVALID_RATE",
  "message": "discount_bps must be between 0 and 10000"
}

سپس هر Runtime Exception داخلی خود را به این قرارداد تبدیل کند.

Error Contract مشترک

Python:

class PricingError(ValueError):
    def __init__(
        self,
        code: str,
        message: str,
    ):
        super().__init__(message)
        self.code = code

JavaScript:

export class PricingError extends Error {
  constructor(code, message) {
    super(message);
    this.name = "PricingError";
    this.code = code;
  }
}

این تغییر اگر در API عمومی ظاهر شود باید جداگانه و با تصمیم روشن انجام شود.

تولید تست‌های بیشتر با Property-based Testing

Fixtureهای ثابت همه حالت‌ها را پوشش نمی‌دهند.

در Python می‌توان از Hypothesis استفاده کرد:

pip install hypothesis

نمونه تولید Case:

from hypothesis import (
    given,
    strategies as st,
)


@given(
    unit_price=st.integers(
        min_value=0,
        max_value=1_000_000,
    ),
    quantity=st.integers(
        min_value=0,
        max_value=100,
    ),
    discount_bps=st.integers(
        min_value=0,
        max_value=10_000,
    ),
    tax_bps=st.integers(
        min_value=0,
        max_value=10_000,
    ),
)
def test_generated_case(
    unit_price,
    quantity,
    discount_bps,
    tax_bps,
):
    ...

می‌توان این ورودی‌ها را در فایل JSON ثبت و هر دو Runtime را با آن‌ها اجرا کرد.

تفاوت Round در Python و JavaScript

Python:

round(2.5)

از Banker's Rounding استفاده می‌کند و ممکن است نتیجه 2 باشد.

JavaScript:

Math.round(2.5)

نتیجه 3 است.

به همین دلیل در پروژه ما الگوریتم Round Half Up را با حساب Integer به‌صورت صریح پیاده‌سازی کردیم:

rounded = floor(
  (value × numerator + denominator / 2)
  / denominator
)

هر تفاوت گردکردن می‌تواند در مبالغ انباشته اثر زیادی ایجاد کند.

تبدیل Decimal از Python

اگر Source از Decimal استفاده می‌کند، سه راه کلی وجود دارد:

Integer Minor Unit

مبالغ در کوچک‌ترین واحد پول نگهداری شوند. برای بسیاری از سیستم‌ها انتخاب مناسبی است.

BigInt

برای عددهای صحیح بزرگ:

const amount = 1250000n;

اما BigInt مستقیماً در JSON Serialize نمی‌شود و به قرارداد جدا نیاز دارد.

کتابخانه Decimal

یک Dependency مقصد برای Decimal Arithmetic استفاده شود. انتخاب کتابخانه باید آگاهانه و متناسب با پروژه باشد.

تبدیل مستقیم به Number بدون بررسی دقت مناسب نیست.

تبدیل Date و Time

Python:

datetime.now(timezone.utc)

JavaScript:

new Date()

هر دو می‌توانند لحظه فعلی را نشان دهند، اما تفاوت‌هایی در Parse، Formatting و Timezone دارند.

روش مناسب:

  • تبادل زمان با ISO 8601
  • ذخیره زمان مرجع به UTC
  • ثبت Timezone ورودی
  • تست تاریخ‌های مرزی
  • Parse با روش مشخص
  • اجتناب از رشته تاریخ مبهم

نمونه‌های مبهم:

01/02/2026

ممکن است اول فوریه یا دوم ژانویه تفسیر شود.

تبدیل Regex میان زبان‌ها

Python:

(?P<year>[0-9]{4})

JavaScript:

(?<year>[0-9]{4})

Flagها و برخی قابلیت‌ها نیز متفاوت‌اند. هر Pattern باید در Runtime مقصد Compile و روی Test Case مشترک اجرا شود.

تبدیل Async Code

Python:

async def load_user(user_id):
    return await repository.get(user_id)

JavaScript:

export async function loadUser(userId) {
  return await repository.get(userId);
}

اما باید این موارد بررسی شوند:

  • نوع Promise
  • مدیریت Reject
  • Timeout
  • Cancellation
  • ترتیب اجرای Taskها
  • Parallelism
  • Cleanup
  • Connection Lifecycle

حذف await در JavaScript همیشه بهینه‌سازی بی‌خطر نیست؛ ممکن است محل ثبت Stack Trace یا مدیریت خطا را تغییر دهد.

تبدیل Class و Dataclass

Python:

@dataclass(frozen=True)
class Product:
    id: int
    name: str

JavaScript ساده:

export class Product {
  constructor(id, name) {
    this.id = id;
    this.name = name;
    Object.freeze(this);
  }
}

TypeScript:

export interface Product {
  readonly id: number;
  readonly name: string;
}

انتخاب Class یا Object یا Interface باید بر اساس نحوه استفاده از Type انجام شود.

تبدیل Python Dictionary Comprehension

Python:

result = {
    item.id: item.name
    for item in items
}

JavaScript با Object:

const result = Object.fromEntries(
  items.map((item) => [
    item.id,
    item.name,
  ]),
);

اگر کلیدها غیررشته‌ای یا ترتیب خاص مهم باشد، Map ممکن است مناسب‌تر باشد.

تبدیل Generator

Python:

def iter_active(users):
    for user in users:
        if user.active:
            yield user

JavaScript:

export function* iterActive(users) {
  for (const user of users) {
    if (user.active) {
      yield user;
    }
  }
}

مدل باید Lazy بودن رفتار را حفظ کند و آن را بی‌دلیل به Array کامل تبدیل نکند.

تبدیل API Backend

برای مهاجرت FastAPI به یک Framework JavaScript فقط تبدیل Handler کافی نیست. باید این موارد مقایسه شوند:

  • Path
  • Method
  • Query Parsing
  • Validation
  • Status Code
  • Response Schema
  • Serialization
  • Middleware
  • Dependency Injection
  • Exception Mapping
  • Streaming
  • Lifecycle Hook
  • OpenAPI

بهترین منبع مقایسه، OpenAPI و Contract Test است.

تبدیل تست‌ها

مدل باید مفهوم تست را تبدیل کند، نه فقط Syntax آن را.

Pytest:

with pytest.raises(
    ValueError,
    match="invalid rate",
):
    calculate(...)

Node Test:

assert.throws(
  () => calculate(...),
  /invalid rate/,
);

موارد مهم:

  • Setup و Teardown
  • Fixture
  • Parametrize
  • Async Test
  • Mock
  • Snapshot
  • Error Matching
  • Precision عددی
  • Test Isolation

تبدیل پروژه چندفایلی

برای Repository بزرگ:

مرحله اول: Dependency Graph

مشخص کنید هر ماژول به چه اجزایی وابسته است.

مرحله دوم: Leaf Moduleها

ابتدا ماژول‌هایی را تبدیل کنید که Dependency داخلی کمتری دارند.

مرحله سوم: Contract Boundary

در مرز دو Runtime از JSON، HTTP، Queue یا Interface مشخص استفاده کنید.

مرحله چهارم: اجرای موازی

نسخه قدیمی و جدید برای بخشی از ورودی‌ها هم‌زمان اجرا و نتیجه مقایسه شوند.

مرحله پنجم: انتقال تدریجی

مصرف‌کننده‌ها مرحله‌ای به نسخه جدید منتقل شوند.

معماری Strangler برای مهاجرت تدریجی

Request
   ↓
Compatibility Layer
   ├── Old Python Module
   └── New JavaScript Module

در مرحله مقایسه:

  1. نسخه قدیمی پاسخ اصلی را می‌دهد.
  2. نسخه جدید در پس‌زمینه همان ورودی را پردازش می‌کند.
  3. خروجی‌ها مقایسه می‌شوند.
  4. اختلاف‌ها ثبت می‌شوند.
  5. پس از رسیدن به هم‌ارزی، مسیر اصلی تغییر می‌کند.

تولید مستقیم کل Repository چرا مناسب نیست؟

  • Context بیش از حد بزرگ می‌شود.
  • Dependencyها فراموش می‌شوند.
  • رفتارهای ضمنی تغییر می‌کنند.
  • Review Diff دشوار می‌شود.
  • پیدا کردن علت اختلاف سخت می‌شود.
  • تست هدفمند وجود ندارد.
  • امکان Rollback کمتر می‌شود.

مهاجرت باید SymbolبهSymbol یا ModuleبهModule انجام شود.

تشخیص Hallucination در Code Translation

مدل ممکن است:

  • کتابخانه موجودنبوده Import کند.
  • Method خیالی بسازد.
  • Type جدید اضافه کند.
  • Validation را حذف کند.
  • Exception را تغییر دهد.
  • ورودی جدید فرض کند.
  • Test Fixture را برای Pass شدن تغییر دهد.
  • بخشی از Contract را نادیده بگیرد.

کنترل‌ها:

  • Allowlist مسیر فایل
  • Allowlist Dependency
  • Syntax Check
  • Type Check
  • Test مقصد
  • Fixture مشترک
  • Differential Test
  • بررسی API عمومی
  • مقایسه خروجی
  • Review انسانی

ساخت Translation Report

گزارش نهایی باید شامل این موارد باشد:

{
  "source": "Python 3.12",
  "target": "Node.js 20 ESM",
  "translated_modules": [
    "pricing"
  ],
  "tests": {
    "source_passed": true,
    "target_passed": true,
    "differential_cases": 100,
    "differences": 0
  },
  "known_differences": [
    "JavaScript implementation rejects values outside safe integer range"
  ],
  "unresolved_questions": [],
  "ready_for_review": true
}

عبارت ready_for_review با ready_for_production یکسان نیست.

ارزیابی AI Code Translator

Dataset ارزیابی باید شامل این موارد باشد:

  • تابع خالص
  • Integer Division
  • Float
  • Decimal
  • Unicode
  • Date
  • Regex
  • Exception
  • Dictionary و Object
  • Set و Map
  • Generator
  • Async
  • فایل و I/O
  • Serialization
  • API Endpoint
  • ماژول چندفایلی

معیارها:

معیارتوضیح
Parse Rateکد مقصد از نظر Syntax معتبر است
Compile Rateدر زبان‌های کامپایل‌شونده Build موفق است
Unit Test Pass Rateتست مقصد موفق است
Differential Matchخروجی دو Runtime برابر است
Error Contract Matchرفتار خطا معادل است
Dependency Accuracyکتابخانه خیالی اضافه نشده است
API PreservationContract عمومی حفظ شده است
Unsupported Behavior Detectionتفاوت‌های غیرقابل تبدیل گزارش شده‌اند
Human Edit Rateمیزان اصلاح دستی
Cost per Moduleهزینه تبدیل هر ماژول

انتخاب مدل مناسب

مدل مناسب باید در این زمینه‌ها قوی باشد:

  • درک زبان مبدأ
  • تولید کد زبان مقصد
  • تحلیل تفاوت معنایی
  • پیروی از Specification
  • خواندن تست
  • تولید Structured Output
  • مدیریت Context چندفایلی
  • تولید Patch یا فایل محدود

برای تابع‌های ساده، مدل سریع‌تر کافی است. برای مهاجرت Framework، Async Code یا ماژول‌های چندفایلی، مدل قوی‌تر مناسب‌تر خواهد بود.

فهرست مدل‌ها و قیمت‌های به‌روز در صفحه مدل‌های درواره قرار دارد.

مدیریت هزینه تبدیل کد

Plan را یک‌بار تولید کنید

برنامه مهاجرت را Cache و برای هر ماژول استفاده کنید.

فقط Context مرتبط

Source، Test، Specification و Dependencyهای همان ماژول ارسال شوند.

مرحله‌ای کار کنید

هر درخواست یک ماژول یا Symbol محدود را تبدیل کند.

ابزارهای قطعی را محلی اجرا کنید

Syntax، Test، Formatting و Type Check را مدل انجام ندهد.

کد بدون تغییر را دوباره ارسال نکنید

کلید Cache:

source_hash +
spec_hash +
test_hash +
target_runtime +
model_id +
prompt_version

اشتباهات رایج

ترجمه فقط بر اساس Syntax

رفتار عدد، تاریخ، Null و Exception باید بررسی شود.

نداشتن Specification

مدل رفتار فعلی را رفتار صحیح فرض می‌کند.

تغییر Fixture برای Pass شدن

Fixture مرجع نباید توسط Code Translator تغییر کند.

استفاده مستقیم از Float برای پول

دقت عددی مقصد باید صریحاً طراحی شود.

تبدیل کل Repository در یک درخواست

مهاجرت را ماژول‌به‌ماژول انجام دهید.

اعتماد به موفقیت Build

Build موفق به معنی یکسان‌بودن رفتار نیست.

مقایسه فقط Happy Path

Edge Case و Error Case نیز باید مقایسه شوند.

نادیده‌گرفتن محدودیت Integer در JavaScript

مقادیر باید با Number.isSafeInteger بررسی یا با روش دیگری نمایش داده شوند.

انتشار بدون Differential Testing

هر دو Runtime باید روی ورودی یکسان اجرا شوند.

نقشه راه Production

مرحله اول: انتخاب ماژول خالص

یک تابع بدون I/O و Dependency پیچیده انتخاب کنید.

مرحله دوم: Contract و Fixture

رفتارها و نمونه‌های مرجع را ثبت کنید.

مرحله سوم: Migration Plan

تفاوت‌های زبان‌ها شناسایی شوند.

مرحله چهارم: تولید کد مقصد

فقط فایل‌های مجاز ساخته شوند.

مرحله پنجم: Test مقصد

Syntax، Type و Unit Test اجرا شوند.

مرحله ششم: Differential Testing

ورودی‌های ثابت و تولیدی روی هر دو نسخه اجرا شوند.

مرحله هفتم: اجرای موازی

در صورت امکان، خروجی نسخه جدید با نسخه قدیمی مقایسه شود.

مرحله هشتم: انتقال تدریجی

مصرف‌کننده‌ها مرحله‌ای منتقل شوند.

چک‌لیست تبدیل کد با AI

  • زبان و نسخه مبدأ مشخص است.
  • زبان و Runtime مقصد مشخص است.
  • API عمومی ثبت شده است.
  • Specification مستقل وجود دارد.
  • تست‌های مبدأ موفق‌اند.
  • Fixture مشترک ساخته شده است.
  • تفاوت‌های معنایی بررسی شده‌اند.
  • Strategy دقت عددی مشخص است.
  • رفتار Null و Undefined تعریف شده است.
  • Exceptionها نگاشت شده‌اند.
  • Date و Timezone بررسی شده‌اند.
  • Regex در Runtime مقصد تست شده است.
  • Dependency جدید کنترل شده است.
  • فقط فایل‌های مجاز تولید شده‌اند.
  • Syntax و Build مقصد موفق‌اند.
  • Test مقصد موفق است.
  • Differential Test اختلاف ندارد یا اختلاف تأیید شده است.
  • API Key فقط در Backend نگهداری می‌شود.
  • نتیجه توسط توسعه‌دهنده بررسی شده است.

پرسش‌های متداول

آیا هوش مصنوعی می‌تواند کد را از یک زبان به زبان دیگر تبدیل کند؟

بله. AI می‌تواند Syntax، ساختار و بخش زیادی از منطق را تبدیل کند، اما هم‌ارزی رفتار باید با Specification، Test و Differential Testing تأیید شود.

چگونه Python را به JavaScript تبدیل کنیم؟

ابتدا API عمومی و رفتار Python را ثبت کنید، Fixture مشترک بسازید، تفاوت‌های عدد، Null، Date و Exception را مشخص و سپس کد JavaScript را مرحله‌ای تولید و تست کنید.

آیا کد تبدیل‌شده مستقیماً قابل استفاده است؟

خیر. ابتدا باید Syntax، Dependency، Unit Test، Error Case و خروجی آن با نسخه مبدأ بررسی شود.

چرا خروجی Python و JavaScript متفاوت می‌شود؟

تفاوت در Float، Integer، Rounding، Truthiness، Unicode، Null، Date، Regex و Exception می‌تواند رفتار را تغییر دهد.

Differential Testing چیست؟

هر دو نسخه برنامه روی ورودی یکسان اجرا و خروجی یا خطای آن‌ها مقایسه می‌شود.

برای مبلغ در JavaScript از چه روشی استفاده کنیم؟

برای بسیاری از کاربردها، نگهداری مبلغ در کوچک‌ترین واحد پول به‌صورت Integer مناسب است. برای عددهای بزرگ باید محدودیت Safe Integer یا استفاده از BigInt و Decimal Library بررسی شود.

آیا تبدیل کد همان Refactor است؟

خیر. Refactor معمولاً زبان و رفتار را حفظ می‌کند. تبدیل میان زبان‌ها نوعی Migration یا Rewrite کنترل‌شده است.

آیا AI می‌تواند یک پروژه کامل را تبدیل کند؟

از نظر فنی می‌تواند بخش‌های زیادی را تولید کند، اما تبدیل کامل باید مرحله‌ای، ماژول‌محور و همراه تست و اجرای موازی باشد.

بهترین مدل برای ترجمه کد چیست؟

انتخاب مدل به زبان‌های مبدأ و مقصد، حجم Context و پیچیدگی پروژه بستگی دارد. مدل‌ها و قیمت‌های به‌روز را در صفحه مدل‌های درواره مقایسه کنید.

API درواره چگونه استفاده می‌شود؟

در Backend یا ابزار محلی، base_url را روی https://api.darvareh.ir/v1 قرار دهید و Source، Specification، تست و Migration Plan را برای مدل ارسال کنید.

جمع‌بندی

تبدیل کد با هوش مصنوعی فقط جایگزین‌کردن Syntax یک زبان با زبان دیگر نیست. هدف واقعی، حفظ رفتار نرم‌افزار در Runtime مقصد است.

برای یک مهاجرت قابل اعتماد باید ابتدا Contract، Specification، Test Baseline و Fixture مشترک ساخته شوند. سپس تفاوت‌های معنایی زبان‌ها شناسایی و کد مقصد به‌صورت مرحله‌ای تولید شود. در پایان، نسخه مبدأ و مقصد باید روی ورودی یکسان اجرا و مقایسه شوند.

در پروژه این مقاله یک AI Code Translator با Python، Node.js، Pydantic و API درواره ساختیم. نسخه Python موتور قیمت‌گذاری را به JavaScript ESM تبدیل کردیم، تست‌های مشترک ساختیم و با Differential Testing هم‌ارزی خروجی دو Runtime را بررسی کردیم.

برای شروع، در درواره ثبت‌نام و API Key دریافت کنید. سپس مدل مناسب را از صفحه مدل‌های درواره انتخاب کنید و مهاجرت را با یک تابع خالص، کوچک و دارای Test Suite آغاز کنید.

مقالات مرتبط

برای مطالعه شرایط استفاده و محدودیت‌های مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.

Read more

اتوماسیون هوش مصنوعی چیست؟ کاربردها و آموزش ساخت AI Automation

اتوماسیون هوش مصنوعی چیست؟ کاربردها و آموزش ساخت AI Automation

اتوماسیون هوش مصنوعی با ترکیب گردش‌کارهای خودکار و مدل‌های هوش مصنوعی، پردازش متن، دسته‌بندی، استخراج اطلاعات و تصمیم‌های پیشنهادی را خودکار می‌کند. در این راهنما، معماری و ساخت نمونه عملی آن با API درواره را می‌آموزید.

Agentic Commerce چیست؟ آینده خرید با ایجنت هوش مصنوعی

Agentic Commerce چیست؟ آینده خرید با ایجنت هوش مصنوعی

Agentic Commerce شیوه‌ای جدید برای خرید اینترنتی است که در آن ایجنت هوش مصنوعی می‌تواند نیاز کاربر را بفهمد، محصولات را جست‌وجو و مقایسه کند و فرایند خرید را پیش ببرد. در این راهنما با معماری، UCP، ACP و پیاده‌سازی آن با API درواره آشنا می‌شوید.