العودة إلى المدونة

API وSDKدليل

ولّد أداة HTML من طلب واحد باستخدام API (Python)

سكربت Python يحوّل طلبًا واحدًا إلى tool.html عامل، ويلتقط إجابة مقطوعة عبر finish_reason، ويطبع الرموز والتكلفة.

بواسطة CheapTokens Teamنُشر في 6 دقيقة قراءة

مقتطف كود يتحقق من finish_reason ويحفظ tool.html بجوار العنوان 'طلب واحد، أداة HTML واحدة'
في هذه الصفحة
  1. ما الذي يحتاجه طلب "ابنِ لي أداة"
  2. السكربت: أرسل، تحقق، احفظ
  3. لماذا يحدد max_tokens ما إذا كانت الصفحة تعمل
  4. افتحها وانظر إلى الصفحة
  5. كم تكلفة توليد واحد
  6. إذا لم يعمل

يمكنك الحصول على أداة عاملة في ملف واحد من استدعاء API واحد: أرسل طلبًا (prompt) جيد البنية، وتأكد من أن max_tokens كبير بما يكفي لصفحة كاملة، وتحقق من أن finish_reason هو stop، واحفظ الرد باسم tool.html. فيما يلي السكربت الذي يفعل ذلك بالضبط، وقالب طلب (prompt) يمكنك تعديله لأداتك الخاصة.

الفكرة بسيطة، وقد أظهرها DeepSeek للتو. نشر مجموعة من سبعة طلبات تجريبية لوضع Creator Mode في DeepSeek Harness (الصفحة، تم التحقق منها في 3 نوفمبر 2026): معرض معالم ثلاثي الأبعاد، وطاولة عمل للملصقات، وطاولة عمل لتحرير الفيديو، وقارئ أوراق بحثية، ولوحة إحصاءات محادثة، وغيرها. يعيش Creator Mode داخل تطبيق Harness. إذا أردت نفس تدفق "طلب يدخل، أداة تخرج" في سكربتك الخاص وبمفتاحك الخاص، فإن إكمال محادثة عاديًا يكفي لحالة الملف الواحد. وإذا استخدمت Harness نفسه، فإن ربطه بـ CheapTokens دليل قصير منفصل.

ما الذي يحتاجه طلب "ابنِ لي أداة"#

اقرأ بعضًا من تلك الطلبات التجريبية وسيظهر نمط. كل واحد منها يحدد ما هي الأداة، وما الموجود على الشاشة، وما الذي تفعله عناصر التحكم، وما هي الحالة الابتدائية، وما الذي يجب ألا تفعله. إذا أغفلت واحدًا منها، سيملأ النموذج الفراغ بتخمينه الخاص، وهو عادةً ليس تخمينك.

إليك القالب، خمسة أجزاء:

text
What it is: <one sentence: the tool and who uses it>
Layout: <what is on the screen, top to bottom>
Controls: <every input and button, and what each one does>
Defaults: <what the page shows before the user touches anything>
Constraints: <single HTML file, no external requests, no libraries, size of the page>

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

text
What it is: a unit converter for cooking, for someone standing in a kitchen with a phone.
Layout: a title, one number field, two dropdowns (from, to), a large result line under them.
Controls: the dropdowns hold cup, tablespoon, teaspoon, ml, oz, g (flour) and g (sugar). Typing a number updates the result immediately. A swap button exchanges the two dropdowns.
Defaults: 1 cup to ml, result already shown.
Constraints: one HTML file with inline CSS and JavaScript, no external libraries, fonts or network requests, works on a narrow phone screen, dark text on a light background.

احفظ ذلك باسم prompt.txt بجوار السكربت. سطر "Constraints" يحمل معظم الثقل: بدون "no external requests" يحب النموذج جلب مكتبة من CDN، فتنكسر الصفحة دون اتصال.

قبل وبعد: طلب من سطر واحد بجوار نفس الطلب بخمسة أجزاء
نفس الطلب كسطر واحد وكخمسة أجزاء: ما هو، والتخطيط، وعناصر التحكم، والقيم الافتراضية، والقيود

السكربت: أرسل، تحقق، احفظ#

واجهة CheapTokens API متوافقة مع OpenAI Chat Completions على https://api.cheaptokens.dev/v1، لذا تعمل حزمة openai بتغييرين: عنوان URL الأساسي والمفتاح. لصفحة كاملة نستخدم deepseek-ai/DeepSeek-V4-Flash-0731: يسمح بما يصل إلى 32,768 رمز إخراج لكل إجابة ولديه نافذة سياق 380K. تفاصيل API الكاملة في وثائق CheapTokens.

ثبّت الحزمة، وضع مفتاحك في متغير بيئة، واضبط السعرين من جدول الأسعار (يستخدمهما السكربت لطباعة التكلفة):

bash
pip install openai
export CHEAPTOKENS_API_KEY="your-key"
export PRICE_IN_PER_M="<input price per 1M from the table>"
export PRICE_OUT_PER_M="<output price per 1M from the table>"

ثم make_tool.py:

python
import os
import re
import sys

from openai import OpenAI

MODEL = 'deepseek-ai/DeepSeek-V4-Flash-0731'
PRICE_IN = float(os.environ['PRICE_IN_PER_M'])    # USD per 1M input tokens (from the table)
PRICE_OUT = float(os.environ['PRICE_OUT_PER_M'])  # USD per 1M output tokens (from the table)
FENCE = '`' * 3

client = OpenAI(
    base_url='https://api.cheaptokens.dev/v1',
    api_key=os.environ['CHEAPTOKENS_API_KEY'],
)

prompt = open('prompt.txt', encoding='utf-8').read()

resp = client.chat.completions.create(
    model=MODEL,
    max_tokens=16000,
    messages=[
        {'role': 'system', 'content': 'You write one self-contained HTML file. Reply with the file only, no commentary.'},
        {'role': 'user', 'content': prompt},
    ],
)

choice = resp.choices[0]
if choice.finish_reason == 'length':
    sys.exit('Cut off at max_tokens: the page is unfinished. Raise max_tokens or shrink the request.')

text = choice.message.content or ''
m = re.search(FENCE + r'(?:html)?\s*(.*?)' + FENCE, text, re.DOTALL)
html = (m.group(1) if m else text).strip()
if '</html>' not in html.lower():
    sys.exit('The reply has no closing </html>; not saving it.')

with open('tool.html', 'w', encoding='utf-8') as f:
    f.write(html)

u = resp.usage
cost = (u.prompt_tokens * PRICE_IN + u.completion_tokens * PRICE_OUT) / 1_000_000
print(f'saved tool.html, {u.prompt_tokens} in / {u.completion_tokens} out, about ${cost:.6f}')

ترتيب الخطوات مهم، ومن السهل الخطأ فيه:

رتّب بالترتيب

رتّب خطوات السكربت بالترتيب الذي ينفذها به

  1. أرسل الطلب مع max_tokens سخي
  2. تحقق من finish_reason قبل لمس النص
  3. استخرج HTML من سياج الكود، إن وُجد
  4. تأكد من وجود </html> الختامي
  5. احفظ tool.html واطبع الاستخدام
خط الأنابيب: الطلب، استدعاء API، فحص finish_reason، tool.html
ما يفعله السكربت، من الطلب إلى الملف المحفوظ

لماذا يحدد max_tokens ما إذا كانت الصفحة تعمل#

الصفحة التفاعلية الكاملة إجابة طويلة. تتراكم HTML وCSS وJavaScript بسرعة، ويكتبها النموذج بهذا الترتيب: إذا قُطعت الإجابة، تفقد نهاية السكربت أولًا، وهي بالضبط الجزء الذي يجعل الأزرار تعمل. قد يفتح المتصفح الصفحة مع ذلك. لكنها لا تفعل شيئًا.

لهذا يتحقق السكربت من finish_reason قبل أن ينظر إلى النص. stop يعني أن النموذج انتهى من تلقاء نفسه. length يعني أنه اصطدم بسقف max_tokens في منتصف الملف. النسخة العامة من هذه المشكلة مغطاة في هل قُطعت إجابة الذكاء الاصطناعي؟ إصلاح finish_reason length؛ هنا القاعدة قصيرة: لا تحفظ أبدًا صفحة يكون فيها finish_reason هو length.

إذا رأيته فعلًا:

  • ارفع max_tokens (يسمح DeepSeek-V4-Flash بما يصل إلى 32,768 لكل إجابة)؛
  • أو اطلب أقل: عناصر تحكم أقل، شاشة واحدة، بلا حركات؛
  • على GLM-5.3-Flash (zai-org/GLM-5.3-Flash) السقف هو 8,192 رمز إخراج، لذا يناسب ذلك النموذج الأدوات الصغيرة، مثل المحوّل أعلاه.

الفحص الثاني، </html> في النص، يلتقط الفشل الأهدأ حيث ينهي النموذج الرد بلطف دون إكمال الملف. كما أن ضبط max_tokens لا يجعل النموذج يكتب أكثر: إنه مجرد سقف، وتدفع مقابل الرموز المنتجة فعليًا (المزيد عن ذلك في تقليل رموز الإخراج).

فحص سريع

طبع السكربت finish_reason = length. ماذا تفعل؟

إظهار الإجابة

الإجابة: لا تحفظه؛ ارفع max_tokens أو قلّص الطلب

length يعني أن الإجابة قُطعت عند السقف، لذا نهاية السكربت مفقودة. السقف الأقل يجعل القطع يأتي أبكر.

افتحها وانظر إلى الصفحة#

افتح tool.html بالنقر المزدوج عليه، أو من الطرفية: open tool.html على macOS، وxdg-open tool.html على Linux، وstart tool.html على Windows.

هذا كود كتبه نموذج لك، لذا خصص دقيقة قبل أن تثق به. افتح أدوات مطوّر المتصفح (F12)، وانظر إلى تبويب Network وأعد التحميل: مع قيد "no external requests" يجب أن يبقى فارغًا. ثم انقر كل عنصر تحكم من جزء "Controls" في طلبك مرة واحدة.

إذا كان هناك خلل، لا تُعِد التوليد من الصفر. أضف جملة واحدة إلى الطلب عن الشيء المحدد ("the swap button must also swap the two values") وشغّل مرة أخرى. تشير صفحة DeepSeek نفسها إلى أن بعض عروضها التجريبية صُقلت عبر عدة جولات محادثة، لذا فالجولة الثانية أمر طبيعي.

جرّب بنفسكجرّب: ماذا تتوقع أن ترى في تبويب Network للمحوّل؟إظهار الإجابة

لا شيء: الصفحة ملف واحد بـ CSS وJavaScript مضمّنين، لذا بعد التحميل الأول لا توجد طلبات أخرى. إذا رأيت طلبًا إلى CDN، فقد تجاهل النموذج القيد؛ أضفه إلى الطلب بأحرف كبيرة وشغّل مرة أخرى.

كم تكلفة توليد واحد#

لنفترض أن الطلب نحو 600 رمز إدخال والصفحة النهائية نحو 6,000 رمز إخراج (افتراض لأداة متوسطة الحجم؛ سطر usage الخاص بك سيُظهر الأرقام الحقيقية). تشغيل واحد على DeepSeek-V4-Flash هو $0.00061، وعلى GLM-5.3-Flash هو $0.00025. كلاهما أرقام حية من قائمة الأسعار الحالية، وإجمالًا، مع احتساب الإدخال والإخراج معًا، كلاهما أرخص من OpenRouter.

النموذج التكلفة OpenRouter
GLM-5.3-Flash $0.00025 $0.00052
DeepSeek-V4-Flash $0.00061 $0.00088
MiniMax-M2.7 $0.00093 $0.0112

600 رمز إدخال و6,000 رمز إخراج إجمالًا. الأسعار لكل مليون رمز حالية وتتغير مع تعريفاتنا؛ يعرض عمود OpenRouter أسعارًا مرجعية لنفس حجم العمل.

عمليًا لن تحصل على الأداة صحيحة من المحاولة الأولى، لذا فكّر بالتكرارات: عشر تعديلات على الطلب هي عشرة أضعاف ذلك السطر، وما زال مبلغًا صغيرًا لأداة كنت ستكتبها يدويًا لولا ذلك. معظمه تقريبًا من جهة الإخراج، أي الصفحة نفسها، ولهذا فإن تقليص الطلب (عناصر تحكم أقل) يقصّر الإجابة والفاتورة.

إذا لم يعمل#

ما تراه السبب المحتمل ما تفعله
401 أو 404 من API مفتاح أو عنوان URL أساسي أو معرّف نموذج خاطئ تحقق من الثلاثة، انظر خطأ API 401 أو 404
Cut off at max_tokens الصفحة أطول من السقف ارفع max_tokens أو اطلب أقل
The reply has no closing </html> توقف النموذج مبكرًا أو أضاف نصًا شغّل مرة أخرى؛ أضف "end with </html>" إلى الطلب
تُفتح الصفحة لكن الأزرار لا تفعل شيئًا خطأ JavaScript افتح الطرفية (F12)، والصق الخطأ الأحمر في طلب متابعة

إذا أردت تحليل رد النموذج كبيانات بدلًا من صفحة، فحيلة الاستخراج هي نفسها كما في json.JSONDecodeError على رد LLM. جديد على API؟ ابدأ بـ أول طلب API لك.