احسب الحدود بوضوح أولاً
ثلاثة أرقام تحدد كل شيء:
- إجمالي السياق 100,000 رمز، ⟨الموجّه والإكمال معاً.
max_tokensافتراضيًا 2048، والحد الأقصى للطلب الواحد 16,000.- حجم جسم الطلب لا يتجاوز 8 MB؛ هذا الحد نادر الوصول له للنصوص، فالرموز هي التي تصل للحد أولاً.
معادلة الميزانية بسيطة: ⟨الموجّه المتاح = 100000 - max_tokens⟩. إذا أردت 4000 رمز، يتبقى 60,000 للموجّه. تجاوز الحد يعيد 400 ولا يقطع تلقائياً.
فهم خاطئ شائع آخر: كلما كان max_tokens أكبر، كان ذلك أكثر أماناً. في الواقع، هو مساحة مخصصة للمخرجات، فإعداد 16,000 يعني أن الـ prompt لن يتجاوز 48,000. اضبطه حسب الحاجة، ولا ترفعه للحد الأقصى بلا تفكير.
مثال على الحساب: افترض أن لديك نص مقابلة من 300,000 كلمة تريد تلخيصه. بتقدير خشن بـ 1.3 رمز (token) لكل كلمة، يصبح المجموع حوالي 390,000 رمز (token)، وهو أكثر من ستة أضعاف الحد الأقصى، لذا لا يمكن إرساله كاملاً. افترض أيضاً أن التلخيص الفردي يتطلب 800 رمز (token) من المخرجات، فيكون الحد الأقصى لكل كتلة إدخال 63,000 رمز (token). مع ترك هامش أمان بنسبة 10٪، يتبقى حوالي 50,000 رمز (token) فقط. لكن هذا ليس الحل الأمثل؛ فعندما تكون الكتل كبيرة، يضعف تركيز النموذج على المحتوى الأوسط. لذا، الحل أدناه هو تقسيم الكتل إلى أحجام أصغر، حتى لو تطلّب ذلك استدعاءات أكثر.
تقدير الرموز: خوارزمية تقريبية
بدون مُجزئ محلي، استخدم هذه القيم التقريبية. ⟨القيم تقريبية وليست دقيقة، وقد يختلف النص بنسبة 10-20%.
| نوع النص | التحويل التقريبي |
|---|---|
| نص صيني | حوالي 1 إلى 1.5 رمز لكل كلمة |
| الإنجليزية | حوالي 1 رمز لكل 4 أحرف، أو 1.3 رمز لكل كلمة |
| الكود، JSON، والنصوص كثيفة الرموز | تستهلك رموزًا أكثر من النص العادي؛ يُنصح بالتقدير بقيم أعلى تحفظًا. |
دالة تقديرية كافية مع حساب الميزانية مع الاحتياطي:
CONTEXT = 100000
def est_tokens(text: str) -> int:
"""粗估:中文按每字 1.3 token,其余字符按每 4 个字符 1 token。仅为近似。"""
zh = sum(1 for c in text if "\u4e00" <= c <= "\u9fff")
other = len(text) - zh
return int(zh * 1.3 + other / 4) + 1
def max_prompt_budget(max_tokens: int, margin: float = 0.1) -> int:
"""给定计划的 max_tokens,返回 prompt 最多能用多少 token(留 margin 余量)。"""
return int((CONTEXT - max_tokens) * (1 - margin))
# 例:计划让模型写 3000 token,prompt 预算
print(max_prompt_budget(3000)) # 54900طريقة المعايرة: أرسل طلبًا بجزء نموذجي، اقرأ usage.prompt_tokens، قارنه بتقديرك، احسب معاملك الخاص، ثم عدّل الدالة. التقدير يحدد "هل أقسم؟"، أما المحاسبة فتعتمد على usage.
تلخيص المستندات الطويلة: التقسيم والدمج
عند تجاوز الميزانية، الطريقة القياسية هي map-reduce: قسم النص وخلص كل جزء، ثم ادمج الملخصات. انتبه:
- اقطع عند حدود الفقرات، لا تقطع حسب عدد الأحرف الثابت لتجنب قطع الجمل.
- خصص ميزانية لكل جزء، يُنصح بألا يتجاوز ثلث الحد الأقصى، واترك مساحة للتعليمات والمخرجات.
- اطلب من الملخصات الحفاظ على الأسماء والأرقام والأحداث الرئيسية، وإلا ستضيع المعلومات عند الدمج.
- إذا كان ملخص الملخص لا يزال طويلاً، كرر العملية.
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.wushenchaapi.com/v1", api_key=os.environ["API_KEY"])
def split_paragraphs(text, budget):
"""按段落切块,每块估算不超过 budget token。"""
chunks, cur, used = [], [], 0
for p in text.split("\n"):
t = int(len(p) * 1.3) + 1 # 中文粗估
if used + t > budget and cur:
chunks.append("\n".join(cur))
cur, used = [], 0
cur.append(p)
used += t
if cur:
chunks.append("\n".join(cur))
return chunks
def ask(prompt, max_tokens=800):
r = client.chat.completions.create(
model="uncensored",
temperature=0.3,
max_tokens=max_tokens,
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content
def summarize_long(text, budget=12000):
parts = split_paragraphs(text, budget)
partial = [ask("用不超过 200 字概括下面这段,保留人名和关键事件:\n\n" + p) for p in parts]
merged = "\n".join(f"第{i+1}部分:{s}" for i, s in enumerate(partial))
return ask("下面是分段摘要,请合并成一份 400 字以内的整体摘要:\n\n" + merged, max_tokens=900)استخدم temperature بقيمة 0.3 للتثبيت. الاستدعاءات مستقلة ويمكن أن تكون parallel requests، لكن احذر من حد المعدل 300 طلب/دقيقة لكل مفتاح.
لا يوجد حجم قياسي للكتل. تجريبياً، 10,000 إلى 15,000 رمز يوفر توازنًا بين التفاصيل وعدد الاستدعاءات. جرب أحجام 5000، 12000، و25000 على عينة، وقارن عدد الحقائق المفقودة. الوثائق عالية الكثافة تحتاج كتلًا أصغر، والسجلات الحوارية ذات الحشو تسمح بكتل أكبر.
متابعة الروايات: النافذة المنزلقة والملخص المتحرك
عند الوصول للفصل العاشر، لن يتسع النص الكامل ولا يحتاج لذلك. استخدم ذاكرة على مستويين:
- المشهد القريب: آخر فصلين أو ثلاثة لضمان استمرارية الأسلوب والحوار.
- المشهد البعيد: فصول سابقة مضغوطة في ملخص، مع الحفاظ على العلاقات والخيوط غير المحلولة.
بعد كتابة كل فصل، اطلب من النموذج تلخيص هذا الفصل في ثلاث إلى خمس جمل وإضافتها إلى الـ outline؛ وإذا تجاوز الـ outline نفسه 3000 رمز (token)، قم بضغطه بالكامل مرة أخرى.
def continue_story(chapters, outline, new_hint, keep_last=3):
"""chapters: 已写章节列表。只带最近 keep_last 章原文,更早的用 outline(滚动大纲)代替。"""
recent = "\n\n".join(chapters[-keep_last:])
prompt = (
f"【全书大纲(早期章节摘要)】\n{outline}\n\n"
f"【最近章节原文】\n{recent}\n\n"
f"【下一章要求】\n{new_hint}\n\n请写下一章,约 2000 字。"
)
return ask(prompt, max_tokens=4000)أكثر ما يضيع هو الخيوط المتشابكة (الحواف). نوصي بإدراج عمون "الحواف غير المستغلة" بشكل منفصل في الـ outline، ومتابعة طلب معالجة واحدة منها في كل فصل. كما يجب إدراج أسماء الشخصيات والأماكن في قائمة ثابتة في system لتجنب أن يغيّر النموذج الأسماء تلقائياً عند خروجها من نافذة السياق.
مشكلة أخرى: انحراف الأسلوب. النموذج يميل لأسلوب الفصول الأخيرة. لحل ذلك، أضف "عينة أسلوب" في system كنقطة ثبات لا تتحرك مع النافذة.
max_tokens والقطع
يحدث قطع الإجابة في حالتين:
finish_reasonهوlength: وصلت لحد max_tokens. الحل: زيادة max_tokens أو طلب الكتابة على مراحل.- الطلب يعود 400: مجموع الموجّه و max_tokens تجاوز 100,000. الحل هو تقليل المدخلات أو تقليل max_tokens.
جدول ميزانية عملي:
| المهمة | max_tokens المقترح | الموجّه متاح (تقريباً) |
|---|---|---|
| التلخيص، الاستخراج | 800 | 63,200 |
| استكمال الفصل (حوالي 2000 كلمة) | 4000 | 60,000 |
| إطالة المخرجات لمستوى الآلاف من الكلمات | 16000 (الحد الأقصى) | 48,000 |
تتأثر المخرجات الطويلة أيضاً بـ«وقت الاستجابة»، لذا يُنصح باستخدام البث المتدفق لعرض المخرجات أثناء توليدها.
عند انقطاع الإكمال، لا تقم بإلقاء النص المقطوع على النموذج وقل "استمر". الطريقة الأكثر استقراراً هي إعادة الجزء المُولّد كرسالة assistant في messages، ثم إضافة رسالة user تقول "استمر من الجملة الأخيرة دون تكرار"، مما يضمن أكثر سلاسة. انتبه: هذه الخطوة تطيل الموجّه، لذا أعد حساب الميزانية.
قائمة مراجعة سياق طويل
- استخدم دالة التقدير لحساب حجم الموجّه قبل كل طلب، وإذا تجاوزت الميزانية فانتقل إلى مسار التقسيم.
- سجل usage في كل استجابة للمعايرة المستمرة لمعامل التقدير.
- ضع رسالة system والخطة في بداية المحتوى، وأعد التأكيد على المتطلبات الأساسية في نهاية التعليمات.
- تحقق من finish_reason؛ إذا كان length فعّل التنبيه أو الاستكمال.
- حدد حداً أقصى لرسائل المحادثة التاريخية، وحذف الأقدم عند تجاوز الحد، ولا تنتظر حتى يعيد الواجهة خطأ 400.
محتوى ذو صلة: هيكل الموجّه في كتابة الموجّه، معالجة الأخطاء في دليل أكواد الأخطاء، التسعير في صفحة الأسعار.
تذكير أخير: السياق الطويل لا يعني الذاكرة الطويلة. يقرأ النموذج ما تقدمه من محتوى من الصفر في كل طلب، ولا يتذكر أي شيء من الاستدعاء السابق؛ لذا يجب عليك توفير التاريخ لضمان الاستمرارية.