نستخدم ملفات تعريف الارتباط. تُبقيك ملفات تعريف الارتباط وتخزين المتصفح مسجّلًا للدخول وتتذكر تفضيلاتك. وبإذنك نقيس أيضًا كيفية استخدام الموقع. يمكنك في أي وقت. راجع سياسة ملفات تعريف الارتباط.

دليل: وكلاء استدعاء الأدوات

البدء

دليل: وكلاء استدعاء الأدوات

ابنِ حلقة وكيل بالذكاء الاصطناعي مع استدعاءات أدوات متوازية وحد للخطوات ومخرجات منظمة وضوابط للتكلفة باستخدام صيغة tools في OpenAI.

الوكيل (agent) حلقة: يقرأ النموذج المحادثة، ويقرر استدعاء أداة أو أكثر من أدواتك، فتنفذها وترسل النتائج إليه، ويواصل النموذج حتى يستطيع الإجابة. يدعم api14 ذلك بصيغة tools في OpenAI مع كل نموذج يدعم الأدوات، فيعمل الكود نفسه مع OpenAI وAnthropic وGoogle وغيرهم بتغيير معرّف النموذج فقط.

يبني هذا الدليل وكيلًا صغيرًا بحد للخطوات وتنفيذ متوازٍ للأدوات وإجابة نهائية منظمة وضوابط للتكلفة. يستخدم الكود حزمة openai الرسمية مع Base URL https://api14.com/api/v1.

تعريف الأدوات

كل أداة اسم ووصف يقرؤه النموذج ومخطط JSON Schema لمعاملاتها. اجعل الأوصاف محددة، فهي التوثيق الوحيد الذي يراه النموذج.

import json, os
from openai import OpenAI

client = OpenAI(base_url="https://api14.com/api/v1", api_key=os.environ["API14_API_KEY"])

def search_orders(customer_email: str) -> list:
    return [{"id": "A-1001", "status": "shipped"}]  # your database query

def get_shipment(order_id: str) -> dict:
    return {"order_id": order_id, "carrier": "UPS", "eta": "2026-10-09"}  # your API call

TOOLS = {"search_orders": search_orders, "get_shipment": get_shipment}

tools = [
    {"type": "function", "function": {
        "name": "search_orders",
        "description": "List a customer's orders with their status.",
        "parameters": {"type": "object", "properties": {"customer_email": {"type": "string"}},
                       "required": ["customer_email"]},
    }},
    {"type": "function", "function": {
        "name": "get_shipment",
        "description": "Carrier and estimated delivery date for one order.",
        "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
                       "required": ["order_id"]},
    }},
]

الحلقة مع حد للخطوات

كل تكرار طلب منفصل يُحاسَب عليه، لذا حدّد دائمًا عدد الخطوات. أعد أخطاء الأدوات إلى النموذج كمحتوى بدل أن ينهار البرنامج، فالنماذج عادةً تتعافى بتصحيح المعاملات.

MAX_STEPS = 8

def run_tool(call):
    try:
        args = json.loads(call.function.arguments)
        result = TOOLS[call.function.name](**args)
    except Exception as e:  # bad JSON, unknown tool, or your function failed
        result = {"error": str(e)}
    return {"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)}

def agent(task, model="openai/gpt-5-mini"):
    messages = [
        {"role": "system", "content": "You are a support agent. Use tools to look things up. Be brief."},
        {"role": "user", "content": task},
    ]
    spent = 0.0
    for step in range(MAX_STEPS):
        r = client.chat.completions.create(model=model, messages=messages, tools=tools, max_tokens=1000)
        spent += r.usage.cost
        msg = r.choices[0].message
        if not msg.tool_calls:
            return msg.content, spent
        messages.append(msg)
        messages.extend(run_tool(call) for call in msg.tool_calls)
    raise RuntimeError(f"No answer after {MAX_STEPS} steps (spent ${spent:.4f})")

print(agent("Where is my order? My email is ada@example.com"))

أرسل tools مع كل طلب في الحلقة. يرفض كثير من المزوّدين رسالة tool عندما يخلو الطلب من تعريفات الأدوات.

استدعاءات الأدوات المتوازية

مع parallel_tool_calls (مفعّل افتراضيًا حيث يكون مدعومًا) يستطيع النموذج طلب عدة استدعاءات في دورة واحدة، مثل get_shipment لثلاثة طلبات. نفّذها بالتزامن وأضف رسالة tool واحدة لكل استدعاء بأي ترتيب، فـ tool_call_id يربط كل نتيجة باستدعائها. في Node:

import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://api14.com/api/v1", apiKey: process.env.API14_API_KEY });
const MAX_STEPS = 8;

async function agent(messages, tools, impl, model = "openai/gpt-5-mini") {
  for (let step = 0; step < MAX_STEPS; step++) {
    const res = await client.chat.completions.create({ model, messages, tools, max_tokens: 1000 });
    const msg = res.choices[0].message;
    if (!msg.tool_calls?.length) return msg.content;

    messages.push(msg);
    const results = await Promise.all(msg.tool_calls.map(async (call) => {
      let content;
      try {
        content = JSON.stringify(await impl[call.function.name](JSON.parse(call.function.arguments)));
      } catch (err) {
        content = JSON.stringify({ error: String(err) });
      }
      return { role: "tool", tool_call_id: call.id, content };
    }));
    messages.push(...results);
  }
  throw new Error(`No answer after ${MAX_STEPS} steps`);
}

إذا كانت لأدواتك آثار جانبية يجب أن تحدث بترتيب (إنشاء ثم تحديث)، فاضبط parallel_tool_calls: false ليطلب النموذج استدعاءً واحدًا في كل دورة.

مخرجات نهائية منظمة

عندما تغذي إجابة الوكيل كودًا آخر، اطلب JSON يطابق مخططًا. نمط بسيط وقابل للنقل هو استدعاء إضافي واحد بعد أن تجمع الحلقة الحقائق (messages هنا هي المحادثة من الحلقة؛ أعدها من agent() مع الإجابة):

schema = {
    "type": "object",
    "properties": {
        "order_id": {"type": "string"},
        "status": {"type": "string", "enum": ["processing", "shipped", "delivered", "unknown"]},
        "eta": {"type": ["string", "null"]},
        "reply": {"type": "string"},
    },
    "required": ["order_id", "status", "eta", "reply"],
    "additionalProperties": False,
}

final = client.chat.completions.create(
    model="openai/gpt-5-mini",
    messages=messages + [{"role": "user", "content": "Summarize the result for our ticket system."}],
    tools=tools,
    tool_choice="none",
    response_format={"type": "json_schema", "json_schema": {"name": "ticket", "strict": True, "schema": schema}},
)
ticket = json.loads(final.choices[0].message.content)

يُبقي tool_choice: "none" الحقل tools في الطلب (وهو لازم لأن السجل يحوي رسائل أدوات) مع منع استدعاءات أخرى. تحقق دائمًا من JSON المحلَّل. راجع المخرجات المنظمة.

اختيار النموذج

  • القدرة: يجب أن يُدرج النموذج tools في supported_parameters. تحقق عبر GET /models، راجع النماذج. وللخطوة النهائية ابحث عن structured_outputs أو response_format.
  • التوجيه: أضف provider: { "require_parameters": true } (في Python عبر extra_body) لتذهب الطلبات فقط إلى المزوّدين الذين يدعمون كل معامل ترسله. راجع توجيه المزوّد.
  • الحجم: النماذج الصغيرة مثل openai/gpt-5-mini تتعامل بموثوقية وبتكلفة منخفضة مع عدد قليل من الأدوات الجيدة الوصف. انتقل إلى نموذج أكبر عندما يكون للوكيل أدوات كثيرة، أو خطط طويلة متعددة الخطوات، أو يواصل اختيار الأداة الخطأ.
  • اختبر بأدواتك: شغّل المهام الحقيقية نفسها (من 20 إلى 50) على نموذجين أو ثلاثة مرشحة وقارن معدل النجاح وعدد الخطوات لكل مهمة وإجمالي usage.cost.
curl -s "https://api14.com/api/v1/models?category=text" \
  | jq -r '.data[] | select(.supported_parameters | index("tools")) | "\(.id)  \(.pricing.prompt)"'

التحكم في التكلفة

قد يرسل الوكيل طلبات كثيرة للمهمة الواحدة، وتُعاد المحادثة كاملة في كل خطوة. حدّد الإنفاق على عدة مستويات:

  • حد الخطوات (MAX_STEPS) و**max_tokens** لكل طلب، كما في الأعلى.
  • ميزانية لكل مهمة: اجمع usage.cost عبر الخطوات وتوقف عندما يتجاوز عتبتك.
  • نتائج أدوات قصيرة: أعد فقط الحقول التي يحتاجها النموذج. مخرجات الأدوات الكبيرة تُدفع كرموز إدخال في كل خطوة لاحقة.
  • حد يومي بالدولار لكل مفتاح: أنشئ مفتاحًا مخصصًا للوكيل في مفاتيح API واضبط حدًا يوميًا. عند بلوغه تُعيد الطلبات 429 daily_limit_reached حتى يوم UTC التالي، فعالج هذا الرمز بإيقاف الوكيل لا بإعادة المحاولة.
  • قائمة النماذج المسموح بها: قيّد المفتاح بالنماذج التي اختبرتها، وأي نموذج آخر يُعيد 403 model_not_allowed.
  • التخزين المؤقت للمطالبات: أبقِ مطالبة النظام وتعريفات الأدوات متطابقة وفي أول كل طلب لتنطبق خصومات البادئة المخزنة. راجع التخزين المؤقت للمطالبات.

راجع استدعاء الأدوات وحدود الطلبات والأخطاء.