ساخت دستیار GraphQL با هوش مصنوعی؛ تبدیل متن فارسی به Query و Mutation معتبر

در این آموزش یک دستیار واقعی می‌سازیم که درخواست فارسی را به GraphQL Query و Variables تبدیل می‌کند، خروجی را با Schema اعتبارسنجی و سپس با کنترل کامل اجرا می‌کند.

Share
ساخت دستیار GraphQL با هوش مصنوعی؛ تبدیل متن فارسی به Query و Mutation معتبر


GraphQL به توسعه‌دهنده اجازه می‌دهد دقیقاً فیلدهایی را درخواست کند که برنامه به آن‌ها نیاز دارد. این انعطاف‌پذیری یکی از مهم‌ترین مزایای GraphQL است؛ اما نوشتن Queryهای پیچیده، تعریف Variableها، انتخاب فیلدهای درست، استفاده از Fragmentها و هماهنگ‌ماندن با Schema همیشه ساده نیست.

فرض کنید کاربر یا یکی از اعضای تیم می‌خواهد چنین درخواستی را اجرا کند:

پنج محصول موجود در دسته لپ‌تاپ را با نام، قیمت و نام برند نمایش بده.

یک دستیار GraphQL مبتنی بر هوش مصنوعی می‌تواند آن را به چنین خروجی تبدیل کند:

query SearchAvailableLaptops(
  $category: String!
  $limit: Int!
  $available: Boolean!
) {
  products(
    category: $category
    limit: $limit
    available: $available
  ) {
    id
    name
    price
    brand {
      name
    }
  }
}

Variables:

{
  "category": "laptop",
  "limit": 5,
  "available": true
}

اما تولید متن Query فقط بخش ساده مسئله است. یک سیستم واقعی باید بررسی کند:

  • آیا فیلد products در Schema وجود دارد؟
  • آیا آرگومان category معتبر است؟
  • آیا نوع limit واقعاً Int است؟
  • آیا فیلد brand دارای زیرمجموعه name است؟
  • آیا Query از نظر Syntax معتبر است؟
  • آیا Variableهای لازم تعریف شده‌اند؟
  • آیا مدل یک فیلد یا Mutation خیالی ساخته است؟
  • آیا درخواست بیش‌ازحد عمیق یا بزرگ نیست؟
  • آیا کاربر اجازه اجرای Mutation را داده است؟
  • آیا قبل از ارسال به API، Query به‌صورت قطعی اعتبارسنجی شده است؟

در این مقاله، یک دستیار کامل Text-to-GraphQL می‌سازیم که درخواست فارسی را دریافت می‌کند، از طریق API هوش مصنوعی درواره Query تولید می‌کند، آن را با Schema واقعی اعتبارسنجی می‌کند و فقط پس از عبور از کنترل‌های برنامه اجرا می‌کند.

GraphQL چیست؟

GraphQL یک زبان Query و مدل اجرایی برای API است. در GraphQL، API به‌جای مجموعه‌ای از Endpointهای ثابت، یک Schema شامل Typeها، Fieldها، Queryها و Mutationها ارائه می‌کند.

طبق وب‌سایت رسمی GraphQL، GraphQL دارای سیستم نوع قوی است و ابزارها می‌توانند Query را پیش از اجرا با Schema اعتبارسنجی کنند.

نمونه Schema:

type Product {
  id: ID!
  name: String!
  price: Float!
  available: Boolean!
  brand: Brand!
}

type Brand {
  id: ID!
  name: String!
}

type Query {
  product(id: ID!): Product
  products(
    category: String
    available: Boolean
    limit: Int = 20
  ): [Product!]!
}

Query:

query GetProduct($productId: ID!) {
  product(id: $productId) {
    id
    name
    price
    brand {
      name
    }
  }
}

Variables:

{
  "productId": "prd_123"
}

پاسخ API نیز معمولاً ساختاری مشابه شکل Query دارد:

{
  "data": {
    "product": {
      "id": "prd_123",
      "name": "Developer Laptop",
      "price": 72000000,
      "brand": {
        "name": "Example"
      }
    }
  }
}

تفاوت Text-to-GraphQL با Text-to-SQL

در Text-to-SQL مدل زبان طبیعی را به Query پایگاه داده تبدیل می‌کند. در Text-to-GraphQL، مدل براساس Schema عمومی API یک عملیات GraphQL می‌سازد.

ویژگیText-to-SQLText-to-GraphQL
منبع ساختارDatabase SchemaGraphQL Schema
خروجیSQL QueryQuery یا Mutation
محل اجراپایگاه دادهGraphQL API
مدل نوعنوع‌های دیتابیسGraphQL Type System
پارامترهاPlaceholder یا Bind ParameterGraphQL Variables
کنترل اعتبارSQL Parser و DatabaseGraphQL Parser و Schema Validation
انتخاب خروجیستون‌هاSelection Set

در هر دو روش، خروجی مدل نباید بدون Parse و Validation اجرا شود.

کاربردهای دستیار GraphQL

یک GraphQL Assistant می‌تواند در بخش‌های مختلف محصول استفاده شود:

  • تولید Query از توضیح فارسی
  • ساخت Query برای تیم Frontend
  • تولید نمونه مستندات API
  • کمک به تیم پشتیبانی فنی
  • ساخت ابزار داخلی گزارش‌گیری
  • تولید Query برای پنل مدیریت
  • تبدیل درخواست محصول به Prototype
  • توضیح Queryهای پیچیده
  • اصلاح Query نامعتبر
  • تولید Variableهای نمونه
  • شناسایی Fieldهای Deprecated
  • ساخت تست برای عملیات GraphQL

دستیار می‌تواند داخل ابزارهای زیر قرار بگیرد:

  • پنل داخلی شرکت
  • افزونه ویرایشگر کد
  • داشبورد توسعه‌دهندگان
  • ابزار مستندسازی API
  • ربات تیم فنی
  • CI/CD
  • محیط تست GraphQL
  • چت‌بات متصل به داده‌های سازمانی

چرا Prompt ساده کافی نیست؟

یک Prompt ساده ممکن است چنین باشد:

برای دریافت محصولات، GraphQL Query بنویس.

مدل اطلاعات کافی ندارد و ممکن است براساس الگوهای رایج حدس بزند:

query {
  getProducts {
    title
    cost
  }
}

درحالی‌که Schema واقعی شاید از فیلدهای products، name و price استفاده کند.

مدل زمانی می‌تواند Query دقیق‌تری بسازد که حداقل این اطلاعات را داشته باشد:

  • Schema مرتبط
  • نوع عملیات مجاز
  • فیلدهای موردنیاز
  • محدودیت عمق
  • نام‌گذاری عملیات
  • قواعد Variables
  • فهرست Root Fieldهای مجاز
  • شکل دقیق خروجی مورد انتظار

بااین‌حال، حتی با Prompt دقیق نیز Validation قطعی ضروری است.

معماری مناسب Text-to-GraphQL

معماری پیشنهادی:

درخواست فارسی کاربر
    ↓
تشخیص نوع عملیات
    ↓
انتخاب بخش مرتبط Schema
    ↓
ساخت Prompt محدود
    ↓
تولید Query و Variables
    ↓
Parse خروجی JSON
    ↓
Parse سند GraphQL
    ↓
Schema Validation
    ↓
کنترل نوع عملیات
    ↓
کنترل Root Fieldها
    ↓
کنترل عمق و اندازه Query
    ↓
نمایش پیش‌نمایش
    ↓
اجرای GraphQL API
    ↓
نمایش Data و Error

هوش مصنوعی فقط مسئول پیشنهاد عملیات است. اجازه نهایی اجرا را کد برنامه صادر می‌کند.

پروژه‌ای که می‌سازیم

ساختار پروژه:

ai-graphql-assistant/
├── schema.graphql
├── requirements.txt
├── .env
├── graphql_assistant/
│   ├── __init__.py
│   ├── models.py
│   ├── generator.py
│   ├── validator.py
│   ├── executor.py
│   └── main.py
└── tests/
    ├── test_validator.py
    └── test_examples.py

پوشه‌ها را بسازید:

mkdir ai-graphql-assistant
cd ai-graphql-assistant

mkdir graphql_assistant
mkdir tests

محیط مجازی:

python -m venv .venv

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

source .venv/bin/activate

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

.venv\Scripts\Activate.ps1

نصب Dependencyها

فایل requirements.txt:

openai
python-dotenv
pydantic
graphql-core
httpx
pytest

نصب:

pip install -r requirements.txt

ساخت GraphQL Schema نمونه

فایل schema.graphql:

enum ProductSort {
  PRICE_ASC
  PRICE_DESC
  NAME_ASC
  NAME_DESC
}

enum OrderStatus {
  PENDING
  CONFIRMED
  PROCESSING
  COMPLETED
  CANCELLED
}

type Brand {
  id: ID!
  name: String!
}

type Product {
  id: ID!
  name: String!
  description: String
  price: Float!
  available: Boolean!
  category: String!
  brand: Brand!
}

type ProductConnection {
  items: [Product!]!
  totalCount: Int!
  nextCursor: String
}

type OrderItem {
  product: Product!
  quantity: Int!
  unitPrice: Float!
}

type Order {
  id: ID!
  status: OrderStatus!
  items: [OrderItem!]!
  totalAmount: Float!
  createdAt: String!
}

input CreateOrderItemInput {
  productId: ID!
  quantity: Int!
}

input CreateOrderInput {
  customerId: ID!
  items: [CreateOrderItemInput!]!
}

type Query {
  product(id: ID!): Product

  products(
    search: String
    category: String
    available: Boolean
    sort: ProductSort = NAME_ASC
    limit: Int = 20
    cursor: String
  ): ProductConnection!

  order(id: ID!): Order

  orders(
    customerId: ID
    status: OrderStatus
    limit: Int = 20
  ): [Order!]!
}

type Mutation {
  createOrder(input: CreateOrderInput!): Order!
  cancelOrder(orderId: ID!): Order!
}

این Schema منبع حقیقت سیستم است. مدل فقط می‌تواند از فیلدها و نوع‌هایی استفاده کند که در این فایل وجود دارند.

تعریف مدل خروجی هوش مصنوعی

مدل نباید فقط یک رشته GraphQL برگرداند. خروجی ساختاریافته مدیریت و اعتبارسنجی را ساده‌تر می‌کند.

فایل graphql_assistant/models.py:

from __future__ import annotations

from typing import Any, Literal

from pydantic import BaseModel, Field


class GeneratedGraphQLOperation(BaseModel):
    operation_name: str
    operation_type: Literal["query", "mutation"]
    query: str
    variables: dict[str, Any] = Field(
        default_factory=dict
    )
    explanation: str
    assumptions: list[str] = Field(
        default_factory=list
    )
    needs_human_review: bool = False


class ValidationResult(BaseModel):
    valid: bool
    operation_name: str | None = None
    operation_type: str | None = None
    root_fields: list[str] = Field(
        default_factory=list
    )
    depth: int = 0
    field_count: int = 0
    errors: list[str] = Field(
        default_factory=list
    )

دریافت کلید API درواره

برای فراخوانی مدل:

  1. در درواره ثبت‌نام کنید.
  2. کلید API بگیرید.
  3. مدل مناسب تولید کد و خروجی ساختاریافته را انتخاب کنید.
  4. اطلاعات را در .env قرار دهید.

فایل .env:

DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL=MODEL_ID_DARVAREH

GRAPHQL_ENDPOINT=https://api.example.com/graphql
GRAPHQL_ACCESS_TOKEN=YOUR_GRAPHQL_ACCESS_TOKEN

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

آدرس پایه API درواره:

https://api.darvareh.ir/v1

ساخت مولد GraphQL Query

فایل graphql_assistant/generator.py:

from __future__ import annotations

import json
import os
from pathlib import Path

from dotenv import load_dotenv
from openai import OpenAI

from graphql_assistant.models import (
    GeneratedGraphQLOperation,
)


load_dotenv()


def load_schema() -> str:
    return Path("schema.graphql").read_text(
        encoding="utf-8"
    )


def strip_code_fence(value: str) -> str:
    text = value.strip()

    if not text.startswith("```"):
        return text

    lines = text.splitlines()

    if len(lines) < 3:
        return text

    content = "\n".join(lines[1:-1]).strip()

    if content.startswith("json"):
        content = content[4:].lstrip()

    return content


class GraphQLGenerator:
    def __init__(self) -> None:
        self.model_id = os.environ.get(
            "DARVAREH_MODEL",
            "MODEL_ID_DARVAREH",
        )

        self.client = OpenAI(
            api_key=os.environ[
                "DARVAREH_API_KEY"
            ],
            base_url="https://api.darvareh.ir/v1",
        )

        self.schema = load_schema()

    def generate(
        self,
        user_request: str,
        allow_mutation: bool = False,
    ) -> GeneratedGraphQLOperation:
        allowed_operation_types = (
            ["query", "mutation"]
            if allow_mutation
            else ["query"]
        )

        request_payload = {
            "task": (
                "Convert the Persian user request into one "
                "GraphQL operation."
            ),
            "userRequest": user_request,
            "allowedOperationTypes": (
                allowed_operation_types
            ),
            "rules": [
                (
                    "Use only types, fields, arguments, "
                    "and enum values present in the schema."
                ),
                (
                    "Create exactly one named GraphQL "
                    "operation."
                ),
                (
                    "Put user-supplied values in GraphQL "
                    "variables instead of embedding them "
                    "inside the query."
                ),
                (
                    "Do not use introspection fields such "
                    "as __schema or __type."
                ),
                (
                    "Do not invent fields when the request "
                    "cannot be represented."
                ),
                (
                    "Set needs_human_review to true when "
                    "the request is ambiguous."
                ),
                (
                    "Return valid JSON only and do not use "
                    "Markdown code fences."
                ),
            ],
            "requiredOutput": {
                "operation_name": "string",
                "operation_type": "query | mutation",
                "query": "string",
                "variables": "object",
                "explanation": "string",
                "assumptions": ["string"],
                "needs_human_review": "boolean",
            },
            "schema": self.schema,
        }

        response = self.client.chat.completions.create(
            model=self.model_id,
            temperature=0.0,
            messages=[
                {
                    "role": "system",
                    "content": (
                        "You are a GraphQL operation "
                        "generator. The supplied schema is "
                        "the only source of truth. Never "
                        "invent schema fields, arguments, "
                        "types, enum values, or operations."
                    ),
                },
                {
                    "role": "user",
                    "content": json.dumps(
                        request_payload,
                        ensure_ascii=False,
                    ),
                },
            ],
        )

        content = response.choices[0].message.content

        if not content:
            raise RuntimeError(
                "Model returned an empty response"
            )

        parsed = json.loads(
            strip_code_fence(content)
        )

        generated = (
            GeneratedGraphQLOperation.model_validate(
                parsed
            )
        )

        if (
            generated.operation_type == "mutation"
            and not allow_mutation
        ):
            raise ValueError(
                "The model returned a mutation while "
                "mutations are disabled"
            )

        return generated

در این مرحله فقط ساختار JSON بررسی شده است. هنوز مشخص نیست که Query با Schema سازگار است یا خیر.

اعتبارسنجی GraphQL با Schema

یکی از مزایای GraphQL، امکان Validation عملیات براساس Type System است. یعنی می‌توان پیش از ارسال Query به سرور بررسی کرد که فیلد، آرگومان، نوع Variable و Selection Set معتبر هستند.

فایل graphql_assistant/validator.py:

from __future__ import annotations

from pathlib import Path
from typing import Any

from graphql import (
    GraphQLSchema,
    build_schema,
    get_operation_ast,
    parse,
    validate,
)
from graphql.language.ast import (
    DocumentNode,
    FieldNode,
    FragmentDefinitionNode,
    FragmentSpreadNode,
    InlineFragmentNode,
    OperationDefinitionNode,
    SelectionSetNode,
)

from graphql_assistant.models import (
    GeneratedGraphQLOperation,
    ValidationResult,
)


ALLOWED_QUERY_FIELDS = {
    "product",
    "products",
    "order",
    "orders",
}

ALLOWED_MUTATION_FIELDS = {
    "createOrder",
    "cancelOrder",
}

MAX_QUERY_DEPTH = 6
MAX_FIELD_COUNT = 40


def load_graphql_schema() -> GraphQLSchema:
    schema_text = Path("schema.graphql").read_text(
        encoding="utf-8"
    )
    return build_schema(schema_text)


def get_fragments(
    document: DocumentNode,
) -> dict[str, FragmentDefinitionNode]:
    fragments: dict[
        str,
        FragmentDefinitionNode,
    ] = {}

    for definition in document.definitions:
        if isinstance(
            definition,
            FragmentDefinitionNode,
        ):
            fragments[
                definition.name.value
            ] = definition

    return fragments


def inspect_selection_set(
    selection_set: SelectionSetNode,
    fragments: dict[str, FragmentDefinitionNode],
    current_depth: int = 1,
    visited_fragments: set[str] | None = None,
) -> tuple[int, int]:
    if visited_fragments is None:
        visited_fragments = set()

    max_depth = current_depth
    field_count = 0

    for selection in selection_set.selections:
        if isinstance(selection, FieldNode):
            field_count += 1

            if selection.selection_set:
                child_depth, child_count = (
                    inspect_selection_set(
                        selection.selection_set,
                        fragments,
                        current_depth + 1,
                        visited_fragments.copy(),
                    )
                )
                max_depth = max(
                    max_depth,
                    child_depth,
                )
                field_count += child_count

        elif isinstance(
            selection,
            InlineFragmentNode,
        ):
            child_depth, child_count = (
                inspect_selection_set(
                    selection.selection_set,
                    fragments,
                    current_depth,
                    visited_fragments.copy(),
                )
            )
            max_depth = max(
                max_depth,
                child_depth,
            )
            field_count += child_count

        elif isinstance(
            selection,
            FragmentSpreadNode,
        ):
            fragment_name = selection.name.value

            if fragment_name in visited_fragments:
                continue

            fragment = fragments.get(
                fragment_name
            )

            if fragment:
                next_visited = (
                    visited_fragments.copy()
                )
                next_visited.add(fragment_name)

                child_depth, child_count = (
                    inspect_selection_set(
                        fragment.selection_set,
                        fragments,
                        current_depth,
                        next_visited,
                    )
                )
                max_depth = max(
                    max_depth,
                    child_depth,
                )
                field_count += child_count

    return max_depth, field_count


def get_root_fields(
    operation: OperationDefinitionNode,
) -> list[str]:
    fields: list[str] = []

    for selection in (
        operation.selection_set.selections
    ):
        if isinstance(selection, FieldNode):
            fields.append(selection.name.value)

    return fields


def contains_introspection_field(
    document: DocumentNode,
) -> bool:
    def check(
        selection_set: SelectionSetNode,
    ) -> bool:
        for selection in (
            selection_set.selections
        ):
            if isinstance(selection, FieldNode):
                if selection.name.value.startswith(
                    "__"
                ):
                    return True

                if (
                    selection.selection_set
                    and check(
                        selection.selection_set
                    )
                ):
                    return True

            elif isinstance(
                selection,
                InlineFragmentNode,
            ):
                if check(selection.selection_set):
                    return True

        return False

    for definition in document.definitions:
        if isinstance(
            definition,
            (
                OperationDefinitionNode,
                FragmentDefinitionNode,
            ),
        ):
            if check(definition.selection_set):
                return True

    return False


class GraphQLValidator:
    def __init__(self) -> None:
        self.schema = load_graphql_schema()

    def validate_generated_operation(
        self,
        generated: GeneratedGraphQLOperation,
        allow_mutation: bool = False,
    ) -> ValidationResult:
        errors: list[str] = []

        try:
            document = parse(generated.query)
        except Exception as error:
            return ValidationResult(
                valid=False,
                errors=[
                    f"GraphQL syntax error: {error}"
                ],
            )

        schema_errors = validate(
            self.schema,
            document,
        )

        errors.extend(
            str(error)
            for error in schema_errors
        )

        operation = get_operation_ast(
            document,
            generated.operation_name,
        )

        if operation is None:
            errors.append(
                "Named operation was not found"
            )

            return ValidationResult(
                valid=False,
                errors=errors,
            )

        operation_type = (
            operation.operation.value
        )

        if (
            operation_type
            != generated.operation_type
        ):
            errors.append(
                "Declared operation_type does not "
                "match the GraphQL document"
            )

        if (
            operation.name is None
            or operation.name.value
            != generated.operation_name
        ):
            errors.append(
                "Operation name does not match "
                "operation_name"
            )

        if (
            operation_type == "mutation"
            and not allow_mutation
        ):
            errors.append(
                "Mutations are not enabled"
            )

        root_fields = get_root_fields(
            operation
        )

        allowed_root_fields = (
            ALLOWED_MUTATION_FIELDS
            if operation_type == "mutation"
            else ALLOWED_QUERY_FIELDS
        )

        unknown_root_fields = sorted(
            set(root_fields)
            - allowed_root_fields
        )

        if unknown_root_fields:
            errors.append(
                "Root fields are not allowed: "
                + ", ".join(
                    unknown_root_fields
                )
            )

        if contains_introspection_field(
            document
        ):
            errors.append(
                "Introspection fields are not allowed "
                "in generated operations"
            )

        fragments = get_fragments(document)
        depth, field_count = inspect_selection_set(
            operation.selection_set,
            fragments,
        )

        if depth > MAX_QUERY_DEPTH:
            errors.append(
                f"Query depth {depth} exceeds "
                f"limit {MAX_QUERY_DEPTH}"
            )

        if field_count > MAX_FIELD_COUNT:
            errors.append(
                f"Field count {field_count} exceeds "
                f"limit {MAX_FIELD_COUNT}"
            )

        return ValidationResult(
            valid=not errors,
            operation_name=(
                operation.name.value
                if operation.name
                else None
            ),
            operation_type=operation_type,
            root_fields=root_fields,
            depth=depth,
            field_count=field_count,
            errors=errors,
        )

Validation چه خطاهایی را تشخیص می‌دهد؟

فرض کنید مدل این Query را بسازد:

query FindProducts {
  products {
    items {
      id
      title
      discountPrice
    }
  }
}

اما Product در Schema فقط فیلدهای name و price دارد. Validation خطایی مشابه موارد زیر می‌دهد:

Cannot query field 'title' on type 'Product'.
Cannot query field 'discountPrice' on type 'Product'.

این همان کنترل قطعی‌ای است که نباید به مدل زبانی سپرده شود.

چرا Root Field Allowlist لازم است؟

ممکن است یک Schema بزرگ شامل Queryهای زیادی باشد؛ اما دستیار شما فقط باید برای گزارش محصولات استفاده شود.

در این حالت حتی اگر Query از نظر Schema معتبر باشد، نباید هر Root Field قابل اجرا باشد.

مثلاً:

ALLOWED_QUERY_FIELDS = {
    "product",
    "products",
}

این Allowlist دامنه عملکرد دستیار را براساس کاربرد محصول محدود می‌کند.

ساخت اجراکننده GraphQL

فایل graphql_assistant/executor.py:

from __future__ import annotations

import os
from typing import Any

import httpx
from dotenv import load_dotenv

from graphql_assistant.models import (
    GeneratedGraphQLOperation,
)


load_dotenv()


class GraphQLExecutionError(Exception):
    def __init__(
        self,
        message: str,
        errors: list[dict[str, Any]] | None = None,
    ) -> None:
        super().__init__(message)
        self.errors = errors or []


class GraphQLExecutor:
    def __init__(self) -> None:
        self.endpoint = os.environ[
            "GRAPHQL_ENDPOINT"
        ]
        self.access_token = os.environ.get(
            "GRAPHQL_ACCESS_TOKEN"
        )

    def execute(
        self,
        generated: GeneratedGraphQLOperation,
    ) -> dict[str, Any]:
        headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
        }

        if self.access_token:
            headers["Authorization"] = (
                f"Bearer {self.access_token}"
            )

        payload = {
            "query": generated.query,
            "variables": generated.variables,
            "operationName": (
                generated.operation_name
            ),
        }

        with httpx.Client(
            timeout=30,
        ) as client:
            response = client.post(
                self.endpoint,
                headers=headers,
                json=payload,
            )

        response.raise_for_status()

        body = response.json()

        if not isinstance(body, dict):
            raise GraphQLExecutionError(
                "GraphQL response must be an object"
            )

        graphql_errors = body.get("errors")

        if graphql_errors:
            raise GraphQLExecutionError(
                "GraphQL API returned errors",
                errors=graphql_errors,
            )

        return body

نکته مهم این است که پاسخ HTTP موفق لزوماً به معنی اجرای موفق GraphQL نیست. بعضی پاسخ‌ها Status Code موفق دارند اما دارای کلید errors هستند.

بنابراین باید هر دو سطح بررسی شوند:

  1. وضعیت HTTP
  2. فیلد errors در پاسخ GraphQL

ساخت برنامه اصلی

فایل graphql_assistant/main.py:

from __future__ import annotations

import argparse
import json

from graphql_assistant.executor import (
    GraphQLExecutionError,
    GraphQLExecutor,
)
from graphql_assistant.generator import (
    GraphQLGenerator,
)
from graphql_assistant.validator import (
    GraphQLValidator,
)


def main() -> None:
    parser = argparse.ArgumentParser(
        description=(
            "Convert Persian requests to validated "
            "GraphQL operations"
        )
    )

    parser.add_argument(
        "request",
        help="Persian natural-language request",
    )

    parser.add_argument(
        "--allow-mutation",
        action="store_true",
        help="Allow mutation generation",
    )

    parser.add_argument(
        "--execute",
        action="store_true",
        help="Execute after validation",
    )

    args = parser.parse_args()

    generator = GraphQLGenerator()
    validator = GraphQLValidator()

    generated = generator.generate(
        user_request=args.request,
        allow_mutation=args.allow_mutation,
    )

    validation = (
        validator.validate_generated_operation(
            generated,
            allow_mutation=args.allow_mutation,
        )
    )

    output = {
        "generated": generated.model_dump(),
        "validation": validation.model_dump(),
    }

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

    if not validation.valid:
        raise SystemExit(1)

    if generated.needs_human_review:
        print(
            "Operation requires human review "
            "and will not be executed."
        )
        raise SystemExit(2)

    if not args.execute:
        return

    if generated.operation_type == "mutation":
        confirmation = input(
            "This operation changes data. "
            "Type EXECUTE to continue: "
        )

        if confirmation != "EXECUTE":
            print("Execution cancelled.")
            return

    executor = GraphQLExecutor()

    try:
        result = executor.execute(generated)
    except GraphQLExecutionError as error:
        print(
            json.dumps(
                {
                    "success": False,
                    "message": str(error),
                    "errors": error.errors,
                },
                ensure_ascii=False,
                indent=2,
            )
        )
        raise SystemExit(3) from error

    print(
        json.dumps(
            {
                "success": True,
                "response": result,
            },
            ensure_ascii=False,
            indent=2,
        )
    )


if __name__ == "__main__":
    main()

فایل graphql_assistant/__init__.py را خالی بگذارید.

اجرای دستیار

تولید Query بدون اجرا:

python -m graphql_assistant.main \
  "پنج محصول موجود در دسته لپ‌تاپ را بر اساس کمترین قیمت با نام، قیمت و برند نمایش بده"

خروجی احتمالی:

{
  "generated": {
    "operation_name": "FindAvailableLaptops",
    "operation_type": "query",
    "query": "query FindAvailableLaptops($category: String!, $available: Boolean!, $sort: ProductSort!, $limit: Int!) { products(category: $category, available: $available, sort: $sort, limit: $limit) { items { id name price brand { name } } totalCount nextCursor } }",
    "variables": {
      "category": "laptop",
      "available": true,
      "sort": "PRICE_ASC",
      "limit": 5
    },
    "explanation": "Searches available laptop products ordered by ascending price.",
    "assumptions": [],
    "needs_human_review": false
  },
  "validation": {
    "valid": true,
    "operation_name": "FindAvailableLaptops",
    "operation_type": "query",
    "root_fields": [
      "products"
    ],
    "depth": 4,
    "field_count": 8,
    "errors": []
  }
}

برای اجرای Query:

python -m graphql_assistant.main \
  "پنج محصول موجود در دسته لپ‌تاپ را نمایش بده" \
  --execute

اجرای Mutation

Mutationها داده را تغییر می‌دهند؛ بنابراین در نسخه اولیه بهتر است به‌صورت پیش‌فرض غیرفعال باشند.

درخواست:

python -m graphql_assistant.main \
  "برای مشتری cus_123 یک سفارش شامل دو عدد از محصول prd_456 بساز" \
  --allow-mutation

خروجی پیشنهادی:

mutation CreateCustomerOrder(
  $input: CreateOrderInput!
) {
  createOrder(input: $input) {
    id
    status
    totalAmount
    createdAt
    items {
      quantity
      unitPrice
      product {
        id
        name
      }
    }
  }
}

Variables:

{
  "input": {
    "customerId": "cus_123",
    "items": [
      {
        "productId": "prd_456",
        "quantity": 2
      }
    ]
  }
}

برای اجرای واقعی:

python -m graphql_assistant.main \
  "برای مشتری cus_123 یک سفارش شامل دو عدد از محصول prd_456 بساز" \
  --allow-mutation \
  --execute

برنامه پیش از اجرا تأیید صریح می‌خواهد:

This operation changes data. Type EXECUTE to continue:

چرا مقادیر باید در Variables قرار بگیرند؟

این Query از مقدار ثابت استفاده می‌کند:

query {
  product(id: "prd_123") {
    id
    name
  }
}

نسخه بهتر:

query GetProduct($productId: ID!) {
  product(id: $productId) {
    id
    name
  }
}

Variables:

{
  "productId": "prd_123"
}

مزایای استفاده از Variables:

  • Query قابل‌استفاده مجدد می‌شود.
  • کد خواناتر است.
  • مدیریت نوع داده بهتر می‌شود.
  • Log و Cache عملیات قابل‌کنترل‌تر می‌شود.
  • داده کاربر از متن عملیات جدا می‌شود.
  • تست Query آسان‌تر می‌شود.

تست Validator

فایل tests/test_validator.py:

from graphql_assistant.models import (
    GeneratedGraphQLOperation,
)
from graphql_assistant.validator import (
    GraphQLValidator,
)


def test_valid_product_query() -> None:
    generated = GeneratedGraphQLOperation(
        operation_name="GetProduct",
        operation_type="query",
        query="""
        query GetProduct($productId: ID!) {
          product(id: $productId) {
            id
            name
            price
            brand {
              name
            }
          }
        }
        """,
        variables={
            "productId": "prd_123"
        },
        explanation="Gets one product",
    )

    result = (
        GraphQLValidator()
        .validate_generated_operation(
            generated
        )
    )

    assert result.valid is True
    assert result.operation_type == "query"
    assert result.root_fields == ["product"]


def test_unknown_field_is_rejected() -> None:
    generated = GeneratedGraphQLOperation(
        operation_name="GetProduct",
        operation_type="query",
        query="""
        query GetProduct($productId: ID!) {
          product(id: $productId) {
            id
            imaginaryField
          }
        }
        """,
        variables={
            "productId": "prd_123"
        },
        explanation="Invalid query",
    )

    result = (
        GraphQLValidator()
        .validate_generated_operation(
            generated
        )
    )

    assert result.valid is False
    assert any(
        "imaginaryField" in error
        for error in result.errors
    )


def test_mutation_disabled_by_default() -> None:
    generated = GeneratedGraphQLOperation(
        operation_name="CancelOrder",
        operation_type="mutation",
        query="""
        mutation CancelOrder($orderId: ID!) {
          cancelOrder(orderId: $orderId) {
            id
            status
          }
        }
        """,
        variables={
            "orderId": "ord_123"
        },
        explanation="Cancels an order",
    )

    result = (
        GraphQLValidator()
        .validate_generated_operation(
            generated,
            allow_mutation=False,
        )
    )

    assert result.valid is False
    assert "Mutations are not enabled" in (
        result.errors
    )


def test_introspection_is_rejected() -> None:
    generated = GeneratedGraphQLOperation(
        operation_name="InspectSchema",
        operation_type="query",
        query="""
        query InspectSchema {
          __schema {
            types {
              name
            }
          }
        }
        """,
        variables={},
        explanation="Introspection",
    )

    result = (
        GraphQLValidator()
        .validate_generated_operation(
            generated
        )
    )

    assert result.valid is False

اجرای تست‌ها:

pytest -q

بررسی هماهنگی Variables با تعریف Query

Validation استاندارد GraphQL نوع استفاده از Variableها را بررسی می‌کند؛ اما مقادیر JSON نیز باید پیش از اجرا کنترل شوند.

برای نمونه، اگر Query این Variableها را تعریف کند:

query FindProducts(
  $limit: Int!
  $available: Boolean!
) {
  products(
    limit: $limit
    available: $available
  ) {
    totalCount
  }
}

این Variables درست است:

{
  "limit": 5,
  "available": true
}

این مقدار اشتباه است:

{
  "limit": "پنج",
  "available": "بله"
}

سرور GraphQL معمولاً Coercion را بررسی می‌کند؛ اما برای تجربه بهتر می‌توان نوع Variableها را از AST و Schema استخراج و پیش از ارسال اعتبارسنجی کرد.

در نسخه Production بهتر است موارد زیر بررسی شوند:

  • وجود Variableهای Non-null
  • نبود Variable ناشناخته
  • نوع Scalar
  • مقدار Enum
  • ساختار Input Object
  • نوع اعضای List
  • محدودیت‌های تجاری خارج از Schema

گرفتن Schema از GraphQL API

GraphQL از Introspection پشتیبانی می‌کند و ابزارها می‌توانند اطلاعات Type System را از خود API دریافت کنند. مشخصات GraphQL نیز Introspection را بخشی از قابلیت‌های GraphQL تعریف می‌کند.

بااین‌حال، در یک سیستم Production بهتر است Schema مورد استفاده دستیار از یک فرایند کنترل‌شده دریافت شود:

  • Export در CI
  • Schema Registry
  • فایل نسخه‌بندی‌شده
  • Artifact هر Release
  • خروجی Build سرور GraphQL

مزیت فایل نسخه‌بندی‌شده این است که Query تولیدشده دقیقاً با نسخه مورد انتظار برنامه اعتبارسنجی می‌شود.

مدیریت Schemaهای بزرگ

ارسال یک Schema بسیار بزرگ در هر درخواست باعث افزایش مصرف توکن، هزینه و احتمال سردرگمی مدل می‌شود.

روش بهتر:

  1. Root Fieldهای مرتبط با درخواست را پیدا کنید.
  2. Typeهای وابسته را استخراج کنید.
  3. Input Typeها و Enumهای لازم را اضافه کنید.
  4. فقط زیرمجموعه مرتبط Schema را برای مدل ارسال کنید.
  5. Validation نهایی را همچنان با Schema کامل انجام دهید.

مثلاً برای درخواست محصولات فقط این بخش‌ها لازم‌اند:

  • Query.products
  • ProductConnection
  • Product
  • Brand
  • ProductSort

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

انتخاب بخش مرتبط Schema با Retrieval

برای Schemaهای بسیار بزرگ می‌توان هر بخش را به یک سند کوچک تبدیل کرد:

{
  "kind": "root_field",
  "operationType": "query",
  "name": "products",
  "signature": "products(search: String, category: String, available: Boolean, sort: ProductSort, limit: Int, cursor: String): ProductConnection!",
  "relatedTypes": [
    "ProductConnection",
    "Product",
    "Brand",
    "ProductSort"
  ]
}

سپس درخواست کاربر Embedding می‌شود و نزدیک‌ترین Root Fieldها بازیابی می‌شوند. بعد فقط بخش مرتبط Schema وارد Prompt می‌شود.

اما انتخاب Retrieval نباید اجازه اجرای خودکار ایجاد کند. عملیات نهایی همچنان باید با Schema کامل و Allowlist اعتبارسنجی شود.

استفاده از Fragment

مدل ممکن است برای Queryهای تکراری از Fragment استفاده کند:

fragment ProductSummary on Product {
  id
  name
  price
  available
  brand {
    name
  }
}

query FindProducts(
  $category: String!
  $limit: Int!
) {
  products(
    category: $category
    limit: $limit
  ) {
    items {
      ...ProductSummary
    }
    totalCount
  }
}

Validator نوشته‌شده Fragmentها را هنگام محاسبه عمق و تعداد فیلدها دنبال می‌کند.

در سیستم واقعی باید مراقب Fragmentهای تودرتو و چرخه‌ای نیز بود. Validation استاندارد GraphQL بسیاری از خطاهای Fragment را پیش از اجرا شناسایی می‌کند.

محدودکردن عمق Query

Query زیر از نظر Schema ممکن است معتبر باشد، اما بسیار عمیق شود:

query {
  orders {
    items {
      product {
        brand {
          products {
            items {
              brand {
                name
              }
            }
          }
        }
      }
    }
  }
}

برای کنترل چنین عملیات‌هایی، دو معیار ساده تعریف کردیم:

MAX_QUERY_DEPTH = 6
MAX_FIELD_COUNT = 40

این اعداد باید براساس Schema، Resolverها، حجم پاسخ و کاربرد محصول تنظیم شوند. عدد مناسب برای همه APIها یکسان نیست.

کنترل Complexity

تعداد فیلد و عمق همیشه هزینه واقعی Query را نشان نمی‌دهند.

ممکن است این Query عمق کمی داشته باشد اما داده زیادی دریافت کند:

query {
  products(limit: 1000) {
    items {
      id
      name
      description
    }
  }
}

می‌توان برای Fieldها وزن تعریف کرد:

هزینه فیلد ساده = 1
هزینه رابطه تکی = 2
هزینه فهرست = 10
هزینه فهرست × limit = هزینه نهایی تقریبی

فرمول ساده و سازگار با Ghost:

total_cost = sum(field_cost × expected_list_size)

برای مثال:

products = 10 × limit
items.id = 1 × limit
items.name = 1 × limit

اگر limit برابر 100 باشد:

total_cost = 1000 + 100 + 100 = 1200

در پروژه واقعی بهتر است Complexity براساس رفتار Resolverها و منابع داده اندازه‌گیری شود.

اصلاح خودکار Query نامعتبر

اگر Validation خطا برگرداند، می‌توان یک بار Query را برای اصلاح به مدل بازگرداند.

ورودی مرحله Repair:

{
  "originalRequest": "محصول را با عنوان و قیمت تخفیف نمایش بده",
  "generatedQuery": "query { product(id: \"prd_1\") { title discountPrice } }",
  "validationErrors": [
    "Cannot query field 'title' on type 'Product'.",
    "Cannot query field 'discountPrice' on type 'Product'."
  ],
  "relevantSchema": "type Product { id: ID!, name: String!, price: Float! }"
}

قواعد مرحله اصلاح:

  • فقط خطاهای Validation را اصلاح کن.
  • هدف اصلی درخواست را تغییر نده.
  • Field جدید نساز.
  • حداکثر یک یا دو بار تلاش کن.
  • خروجی اصلاح‌شده را دوباره از کل Pipeline عبور بده.
  • اگر درخواست با Schema قابل انجام نیست، آن را صریح اعلام کن.

Loop نامحدود بین مدل و Validator ایجاد نکنید.

نگهداری حافظه مکالمه

اگر دستیار حالت گفت‌وگویی دارد، کاربر ممکن است بگوید:

فقط محصولات موجود را نشان بده.

سپس:

حالا بر اساس بیشترین قیمت مرتب کن.

درخواست دوم بدون Context ناقص است. حافظه موردنیاز بهتر است ساختاریافته باشد:

{
  "currentIntent": "search_products",
  "filters": {
    "available": true
  },
  "sort": "PRICE_DESC",
  "selectedFields": [
    "id",
    "name",
    "price"
  ],
  "limit": 20
}

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

تولید Query در برابر اجرای Query

این دو قابلیت را از هم جدا نگه دارید:

حالت Generate

  • Query تولید می‌شود.
  • Validation اجرا می‌شود.
  • Preview نمایش داده می‌شود.
  • هیچ درخواست GraphQL ارسال نمی‌شود.

حالت Execute

  • Query قبلاً معتبر است.
  • مجوز اجرای نوع عملیات بررسی می‌شود.
  • Variables نهایی مشخص‌اند.
  • Mutation تأیید می‌شود.
  • سپس درخواست ارسال می‌شود.

این جداسازی برای IDE، مستندسازی و محیط‌های توسعه بسیار مفید است.

کش‌کردن Queryهای معتبر

درخواست‌های مشابه ممکن است بارها تکرار شوند:

محصول با شناسه مشخص را نشان بده
اطلاعات یک محصول را بگیر
جزئیات محصول را نمایش بده

می‌توان Template معتبر Query را Cache کرد:

query GetProduct($productId: ID!) {
  product(id: $productId) {
    id
    name
    price
    available
  }
}

در درخواست بعدی فقط Variables تغییر می‌کنند. این روش:

  • هزینه فراخوانی مدل را کاهش می‌دهد.
  • Latency را کم می‌کند.
  • رفتار سیستم را پایدارتر می‌کند.
  • نرخ خطای تولید Query را کاهش می‌دهد.

Cache Key می‌تواند ترکیبی از موارد زیر باشد:

schema_version + normalized_intent + selected_fields

ثبت Log مناسب

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

  • شناسه درخواست
  • نسخه Schema
  • مدل استفاده‌شده
  • Intent تشخیص‌داده‌شده
  • نوع عملیات
  • نام Operation
  • Root Fieldها
  • نتیجه Validation
  • عمق Query
  • تعداد فیلدها
  • زمان تولید مدل
  • زمان اجرای API
  • وجود GraphQL Error
  • تعداد تلاش‌های Repair
  • تعداد توکن مصرفی در صورت دسترسی

Variables ممکن است شامل اطلاعات حساس یا شخصی باشند. بنابراین نباید بدون سیاست مشخص به‌طور کامل در Log ذخیره شوند.

ارزیابی کیفیت سیستم

برای Evals یک Dataset بسازید:

[
  {
    "id": "products-001",
    "request": "پنج محصول موجود را بر اساس کمترین قیمت نمایش بده",
    "expectedOperationType": "query",
    "expectedRootFields": [
      "products"
    ],
    "expectedVariables": {
      "available": true,
      "sort": "PRICE_ASC",
      "limit": 5
    },
    "forbiddenFields": [
      "orders",
      "createOrder"
    ]
  },
  {
    "id": "order-001",
    "request": "سفارش ord_123 را با اقلام آن نمایش بده",
    "expectedOperationType": "query",
    "expectedRootFields": [
      "order"
    ],
    "forbiddenFields": [
      "createOrder",
      "cancelOrder"
    ]
  }
]

معیارهای مناسب:

  • درصد Queryهای دارای Syntax معتبر
  • درصد Queryهای معتبر نسبت به Schema
  • دقت انتخاب Root Field
  • دقت Variableها
  • نرخ تولید Field خیالی
  • نرخ انتخاب اشتباه Mutation
  • میانگین عمق Query
  • نرخ نیاز به Repair
  • نرخ موفقیت اجرای API
  • هزینه متوسط هر درخواست
  • Latency تولید
  • رضایت کاربر

استفاده در Frontend

در Frontend بهتر است درخواست طبیعی به Backend شما ارسال شود:

const response = await fetch(
  "/api/graphql-assistant/generate",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      request: userInput
    })
  }
);

const result = await response.json();

Backend:

  1. API درواره را فراخوانی می‌کند.
  2. Query را اعتبارسنجی می‌کند.
  3. Preview را برمی‌گرداند.
  4. در صورت درخواست جداگانه کاربر، عملیات را اجرا می‌کند.

کلید API درواره و Token سرویس GraphQL نباید در کد عمومی مرورگر قرار بگیرند.

خطاهای رایج در ساخت AI GraphQL Assistant

اعتماد به JSON معتبر

JSON معتبر به معنی GraphQL معتبر نیست. هر دو سطح باید جداگانه بررسی شوند.

ارسال Schema بدون محدودیت

مدل ممکن است از هر Root Field موجود استفاده کند. علاوه بر Schema، Allowlist کاربردی تعریف کنید.

استفاده از Query بدون نام

این عملیات Anonymous است:

query {
  products {
    totalCount
  }
}

نسخه بهتر:

query CountProducts {
  products {
    totalCount
  }
}

Operation Name برای Log، Debug، Cache و Monitoring مفید است.

قراردادن مقادیر داخل Query

از Variables استفاده کنید و مقادیر کاربر را در رشته GraphQL قرار ندهید.

اجازه Mutation به‌صورت پیش‌فرض

نسخه اولیه را Query-only بسازید. Mutation را فقط در کاربردهای مشخص و با تأیید فعال کنید.

اجرای Query قبل از Validation

ترتیب صحیح:

Generate → Parse → Validate → Policy Check → Execute

نادیده‌گرفتن GraphQL Errors

فقط Status Code را بررسی نکنید. پاسخ GraphQL می‌تواند کلیدهای data و errors را هم‌زمان داشته باشد.

ارسال Schema قدیمی

نسخه Schema را در هر گزارش و Cache Key ثبت کنید. Query معتبر برای نسخه قبلی ممکن است با نسخه فعلی ناسازگار باشد.

تلاش نامحدود برای Repair

تعداد تلاش اصلاح را محدود کنید. اگر Query پس از یک یا دو بار همچنان نامعتبر است، خطا را برای بررسی برگردانید.

چک‌لیست آماده‌سازی برای Production

  • Schema منبع حقیقت مشخصی دارد.
  • نسخه Schema ثبت می‌شود.
  • مدل فقط JSON ساختاریافته برمی‌گرداند.
  • JSON با Pydantic یا JSON Schema بررسی می‌شود.
  • Query با Parser واقعی Parse می‌شود.
  • Query با Schema کامل Validation می‌شود.
  • Operation Name اجباری است.
  • فقط یک Operation پذیرفته می‌شود.
  • Root Fieldها Allowlist دارند.
  • نوع Operation بررسی می‌شود.
  • Mutation به‌صورت پیش‌فرض غیرفعال است.
  • عمق Query محدود شده است.
  • تعداد فیلدها محدود شده است.
  • Complexity تخمین زده می‌شود.
  • Variableها جدا از Query هستند.
  • خروجی مدل مستقیماً اجرا نمی‌شود.
  • خطاهای HTTP و GraphQL جدا بررسی می‌شوند.
  • تعداد Repairها محدود است.
  • Queryهای پرتکرار Cache می‌شوند.
  • Dataset ارزیابی وجود دارد.
  • کلیدها فقط در Backend نگهداری می‌شوند.
  • Logها شامل Schema Version هستند.

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

مدل مناسب Text-to-GraphQL باید توانایی‌های زیر را داشته باشد:

  • پیروی دقیق از Schema
  • تولید JSON معتبر
  • درک Type System
  • تولید GraphQL Variables
  • درک درخواست فارسی
  • تشخیص Query و Mutation
  • عملکرد مناسب در تولید کد
  • رعایت محدودیت‌های Prompt
  • هزینه مناسب برای استفاده پرتکرار

برای Schema کوچک و Queryهای ساده، یک مدل سریع و اقتصادی کافی است. برای Schema بزرگ، درخواست‌های چندمرحله‌ای یا تحلیل پیچیده، مدلی با Context Window و توان استدلال بیشتر مناسب‌تر خواهد بود.

مدل‌ها، Model ID و قیمت به‌روز را در صفحه مدل‌های درواره ببینید.

چرا API درواره برای این پروژه مناسب است؟

Text-to-GraphQL یک قابلیت Backend است و باید از داخل برنامه، Agent، داشبورد یا Pipeline فراخوانی شود.

با API درواره می‌توانید:

  • درخواست فارسی را از Backend به مدل ارسال کنید.
  • مدل مناسب کدنویسی را انتخاب کنید.
  • خروجی JSON دریافت کنید.
  • مدل را بدون تغییر معماری اصلی تعویض کنید.
  • Query Generator را وارد محصول خود کنید.
  • برای تولید، Repair و توضیح Query از مدل‌های مختلف استفاده کنید.
  • هزینه مدل‌ها را پیش از انتخاب مقایسه کنید.

برای شروع، در درواره ثبت‌نام کنید، کلید API بگیرید و Base URL را روی مقدار زیر قرار دهید:

https://api.darvareh.ir/v1

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

آیا هوش مصنوعی می‌تواند GraphQL Query معتبر بسازد؟

بله، اما خروجی باید با Schema واقعی اعتبارسنجی شود. مدل ممکن است فیلد، آرگومان یا نوعی تولید کند که در Schema وجود ندارد.

آیا می‌توان درخواست فارسی را به GraphQL تبدیل کرد؟

بله. مدل درخواست فارسی را تحلیل و به Query، Mutation و Variables تبدیل می‌کند. کیفیت خروجی به Schema، Prompt و Validation بستگی دارد.

آیا GraphQL Query قبل از اجرا قابل اعتبارسنجی است؟

بله. GraphQL دارای Type System است و Query را می‌توان پیش از اجرا با Schema بررسی کرد.

آیا باید Schema کامل را به مدل بدهیم؟

برای Schema کوچک امکان‌پذیر است. در Schema بزرگ بهتر است ابتدا بخش مرتبط انتخاب شود؛ اما Validation نهایی باید با Schema کامل انجام شود.

آیا Mutation را هم می‌توان خودکار تولید کرد؟

بله، ولی بهتر است Mutation پیش‌فرض غیرفعال باشد و پیش از اجرای واقعی Preview و تأیید دریافت شود.

تفاوت Query و Mutation چیست؟

Query معمولاً برای دریافت داده و Mutation برای ایجاد، ویرایش یا تغییر داده استفاده می‌شود.

آیا می‌توان GraphQL Assistant را به React یا Next.js متصل کرد؟

بله. Frontend درخواست کاربر را به Backend می‌فرستد و Backend تولید، اعتبارسنجی و اجرای GraphQL را مدیریت می‌کند.

چگونه از ساخته‌شدن Field خیالی جلوگیری کنیم؟

Schema را به مدل بدهید و سپس Query خروجی را با GraphQL Validator واقعی بررسی کنید. Prompt به‌تنهایی کافی نیست.

آیا درواره خودش GraphQL API می‌سازد؟

درواره زیرساخت دسترسی به مدل‌های هوش مصنوعی را از طریق API فراهم می‌کند. Schema، Validator، GraphQL Endpoint و منطق اجرای دستیار را در برنامه خود پیاده‌سازی می‌کنید.

Model ID درواره را از کجا دریافت کنیم؟

Model ID و مشخصات مدل‌ها را در صفحه مدل‌های درواره ببینید و مقدار انتخابی را جایگزین MODEL_ID_DARVAREH کنید.

جمع‌بندی

ساخت GraphQL Query با هوش مصنوعی فقط به معنی تولید یک رشته متنی نیست. یک سیستم قابل‌اعتماد باید مدل را درون یک Pipeline کنترل‌شده قرار دهد.

در این معماری:

  1. Schema منبع حقیقت است.
  2. مدل Query و Variables را پیشنهاد می‌دهد.
  3. Pydantic ساختار JSON را بررسی می‌کند.
  4. GraphQL Parser صحت Syntax را کنترل می‌کند.
  5. Schema Validator فیلدها، نوع‌ها و آرگومان‌ها را بررسی می‌کند.
  6. Allowlist دامنه دسترسی دستیار را محدود می‌کند.
  7. عمق و اندازه Query کنترل می‌شود.
  8. Mutation تنها با مجوز و تأیید اجرا می‌شود.
  9. پاسخ HTTP و GraphQL جداگانه بررسی می‌شوند.
  10. کیفیت سیستم با Dataset و Evals اندازه‌گیری می‌شود.

نتیجه، دستیاری است که می‌تواند درخواست فارسی را به یک GraphQL Operation واقعی تبدیل کند؛ بدون اینکه تصمیم نهایی اجرا صرفاً به مدل زبانی سپرده شود.

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

مقالات مرتبط

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

Read more

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

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

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

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

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

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