تصور کنید سناریویی را که در آن یک پاسخ استریم شده (Streamed AI Response) پاسخی کامل را به دست کاربر شما میرساند، اما لاگهای صورتحساب شما دقیقاً صفر توکن را ثبت میکنند. این شکست «تحویلشده اما صورتحسابنشده» (delivered-but-unbilled) زمانی رخ میدهد که فریم استفاده نهایی (terminal usage frame) — یعنی آخرین تکه از دادهها که حاوی اطلاعات هزینه است — در حین انتقال حذف، کوتاه (truncated) یا بدشکل (malformed) شود. نتیجه این اتفاق، یک «تراکنش شبح» است: شما خروجی را دریافت میکنید، اما داشبورد شما ادعا میکند که این فراخوانی رایگان بوده است.
این کورسویی در تلهمتری (Telemetry) یک شکاف خطرناک در اقتصاد واحد (UnitEconomics) برای سیستمهای هوش مصنوعی در محیط عملیاتی ایجاد میکند. دلیل این اتفاق این است که دلتاهای متنی (Text Deltas) ابتدا میرسند و حسابداری در آخرین لحظه ارسال میشود؛ بنابراین یک اتصال ناپایدار یا یک خطای پروکسی اغلب آخرین فریم را میبلعد. از آنجا که کاربر در همین لحظه پاسخ را مشاهده میکند، هیچ استثنایی (Exception) صادر نمیشود و کلاینت صرفاً مقدار اولیه یعنی صفر را ثبت میکند. برای بسیاری از کلاینتها، مقدار صفر یک انتخاب توسط کد است، نه یک قانون در استریمینگ. هیچ کرشی (Crash) رخ نمیدهد، اما پاسخی که هزینه توکنهای خروجی واقعی را داشته، به عنوان رایگان ثبت میشود.
در تاریخ ۷ ژوئیه، توسعهدهندهای که با نام kielltampubolon فعالیت میکرد، این حالت خاص از شکست را در یک اثبات مفهوم (Proof of Concept) با عنوان «کورسویی تلهمتری نامتقارن در کلاینتهای استریمینگ AI» در پلتفرم Dev.to برجسته کرد. آن نوشتار به عنوان یک اثبات مفهوم محلی عمل کرد و نشان داد که چگونه دلتاهای متنی یک پاسخ کامل را رندر میکنند، در حالی که حذف فریم نهایی، صورتحساب را روی صفر نگه میدارد. بر اساس آن یافته، ابزار جدیدی برای تطبیق آفلاین به نام stream_billing_gate.py توسعه یافته است تا این نشتها را از طریق بازسازی متن تحویل داده شده و مقایسه آن با تعداد توکنهای ثبتشده، شناسایی کند.
من stream_billing_gate.py را با کمک یک دستیار هوش مصنوعی نوشتم و آن را بهصورت آفلاین با استفاده از پایتون ۳.۱۳.۵ اجرا کردم، در حالی که تنها از کتابخانه استاندارد استفاده شده و هیچ دسترسی به شبکه وجود نداشت. هر عدد در خروجیهای ذکر شده در اینجا، از یک اجرای محلی واقعی کپی شده است. برای اطمینان از دقت، من کدهای خروج (۰، ۱ و ۲) را بررسی کردم، هر سناریو را دو بار اجرا کردم تا تأیید شود خروجی استاندارد (STDOUT) بایت-به-بایت یکسان است و از ابزار خواستم یک هش sha256 از گزارش خودش چاپ کند تا قابلیت بازتولیدپذیری (Reproducibility) تضمین شود.
مکانیزم تشخیص چگونه کار میکند
این ابزار به عنوان یک دروازه تطبیق پسازرویداد (Post-hoc Reconciliation Gate) برای استریمهای ثبتشده عمل میکند. این ابزار ترافیک زنده را رهگیری نمیکند، API را فراخوانی نمیکند و نیازی به کلید (Key) ندارد؛ در عوض، لاگها را به عنوان داده میخواند و آنها را در برابر یک کف منطقی ارزیابی میکند. این ابزار دو مقدار را برای هر استریم تطبیق میدهد: متن تحویل داده شده (که با اتصال تمام دلتاهای محتوا بازسازی میشود) و توکنهای خروجی ثبتشده (که از فریم استفاده نهایی استخراج میشود).
- کف متنی (The Text Floor): از آنجا که ابزار برای سبک ماندن و اکتفا به کتابخانه استاندارد، هیچ توکنایزری را همراه ندارد، نمیتواند تعداد دقیق توکنها را ارائه دهد. در عوض، یک «کف» (FLOOR) محاسبه میکند: تعداد کلمات جدا شده با فضای خالی (whitespace-delimited words). چون یک توکنایزر BPE که بر اساس فضای خالی کلید شده است، تقریباً همیشه برای هر کلمه حداقل یک توکن صادر میکند (و معمولاً به دلیل علائم نگارشی و تقسیمات زیر-کلمهای بیشتر است)، تعداد واقعی
output_tokensتقریباً همیشه بالاتر از این کف است. این کف یک رقم قابل خواندن «حداقل این مقدار» را برای هشدارها فراهم میکند بدون اینکه نیاز به وزنهای مدل-خاص داشته باشد. - محدودیت منطقی (The Logical Constraint): ادعای این گیت متکی به دقیق بودن کف نیست، بلکه بر یک حقیقت کوچکتر و نشکستنی استوار است: اگر متن تحویل داده شده خالی نباشد، تعداد واقعی توکنهای خروجی قطعاً بالای صفر است. بنابراین، یک مقدار صفر ثبتشده، صرفنظر از توکنایزر مورد استفاده ارائهدهنده، بهطور اثباتی غلط است.
- حکم نهایی (The Verdict): اگر متنی تحویل شده باشد اما استفاده ثبتشده صفر، غایب یا غیرقابل تجزیه (unparseable) باشد، ابزار حکم
BLINDرا صادر کرده و با کد خروج ۱ خارج میشود و خط لوله (Pipeline) را مسدود میکند.

چهار سطح تطبیق و تحلیل
این ابزار استریمها را به چهار حکم متمایز طبقهبندی میکند تا تفاوت بین لاگهای صادقانه، تلهمتریهای مشکوک و شکستهای کامل مشخص شود. منطق تصمیمگیری توسط یک تابع classify مدیریت میشود. تابع واقعی دیکشنریهایی با رشتههای دقیق برمیگرداند و شامل یک محافظ (Guard) است که در صورت مواجهه با شکلهای ناشناخته استریم، وضعیت را به صورت «بسته» (fail closed) خراب میکند.
def classify(delivered_text, logged, usage_seen, events, recognized):
# simplified for the post
floor = word_floor(delivered_text) # whitespace word count
if events and not recognized:
# valid JSON, but no shape the gate knows -> fail closed
return "UNRECOGNIZED", "unrecognized-stream", 2
if not delivered_text.strip():
return "EMPTY", "nothing-delivered", 0
if logged is None or logged == 0:
return "BLIND", "delivered-but-unbilled", 1
if logged < floor:
return "UNDERCOUNT", "partial-telemetry-loss", 2
return "OK", "usage-consistent", 0
- OK (usage-consistent): فریم استفاده موجود است و
logged >= floor. نتیجه: خروج ۰. - UNDERCOUNT (partial-telemetry-loss): فریم استفاده موجود است، اما
0 < logged < floor. این یک اکتشاف مشکوک است — نشان میدهد تلهمتری بخشی از استریم را گم کرده است — اما بیشتر جنبه هشدار دارد تا مسدود کردن. نتیجه: خروج ۲. - BLIND (delivered-but-unbilled): متن تحویل داده شده، اما مقدار ثبتشده ۰، غایب یا غیرقابل تجزیه است. این یک شکست منطقی سخت است. نتیجه: خروج ۱.
- EMPTY (nothing-delivered): هیچ متنی تحویل داده نشده، بنابراین چیزی برای تطبیق وجود ندارد. نتیجه: خروج ۰.
سازگاری با ارائهدهندگان مختلف
ارائهدهندگان مختلف، حسابداری را در مراحل متفاوتی از استریم مدیریت میکنند که معنای مقدار صفر را تغییر میدهد. stream_billing_gate.py بهطور خودکار شکل انتقال (wire shape) را برای چهار فرمت اصلی تشخیص میدهد:
- OpenAI Chat Completions: به دنبال
choices[].delta.contentبرای متن وusage.completion_tokensدر تکه (chunk) نهایی میگردد. این تکه تنها در صورتی ارسال میشود کهstream_options={"include_usage": true}تنظیم شده باشد؛ بدون آن، فریم استفاده غایب است که بهطور پیشفرض به عنوانdelivered-but-unbilledخوانده میشود. - Anthropic Messages: مقدار
content_block_delta.delta.textوmessage_delta.usage.output_tokensرا رصد میکند. چون آنتروپیک توکنها را بهصورت تجمعی (cumulatively) ارسال میکند، حذف فریم نهایی معمولاً منجر بهUNDERCOUNTمیشود، مگر اینکه کلاینت فقط عدد نهایی را ثبت کند. - OpenAI Responses: مقدار
response.output_text.deltaرا برای متن وresponse.completedو سپسresponse.usage.output_tokensرا برای حسابداری نگاشت میکند. - Generic Normalized: از یک شکل استاندارد استفاده میکند:
{"type":"content.delta","text":...}و{"type":"usage","output_tokens":...}.
امنیت و موارد خاص
این ابزار برای «شکست بسته» (fail closed) طراحی شده است. اگر با لاگی در شکلی مواجه شود که نمیتواند تشخیص دهد، به جای یک تأیید خاموش با عنوان EMPTY، مقدار unrecognized-stream (خروج ۲) را برمیگرداند. این تضمین میکند که یک آداپتور خراب نتواند نشت را پشت یک تیک سبز پنهان کند.
همچنین محتوای استریم را به عنوان ورودی غیرقابل اعتماد (untrusted input) در نظر میگیرد. در یک مورد تست خاص (fixture_injection.jsonl)، متن تحویل داده شده شامل خطی بود که مخاطب آن ابزار نظارتی بود و ادعا میکرد پاسخ رایگان است و به خواننده دستور میداد که وضعیت را usage-consistent گزارش کرده و با خروج ۰ خارج شود. چون این گیت لاگها را به عنوان داده میبیند و هرگز به عنوان دستور اجرا نمیکند، صرفاً متن تزریق شده را به تعداد کلمات اضافه کرد. از آنجا که هیچ فریم استفاده واقعی وجود نداشت، استریم بهدرستی مسدود شد (خروج ۱). این یک وضعیت امنیتی سختگیرانه را حفظ میکند: استریمهای ثبت شده میتوانند هر چیزی را حمل کنند، اما گیت دستورات موجود در محتوا را اجرا نخواهد کرد.
نمایش شکست: تفاوت در یک خط
برای اثبات دقت ابزار، دو فایل fixture_ok.jsonl و fixture_blind.jsonl را در نظر بگیرید. هر دو حاوی یک پاسخ عادی درباره محدود کردن هزینه عامل (agent spend) با ۱۳۷ کلمه (۷۵۰ کاراکتر) هستند.
در fixture_ok.jsonl استریم با یک فریم استفاده نهایی به پایان میرسد که ۱۹۰ توکن خروجی را گزارش میکند. ابزار گزارش میدهد:delivered : 137 words, 750 chars (floor 137 tokens)logged : output_tokens=190verdict : OK (usage-consistent) exit 0detail : logged 190 >= floor 137report-sha256: f6b8ce0b2b7a09f57a589a862b53024a81ca618cfecd34f82fa6231c670486a7
در fixture_blind.jsonl آن یک خط استفاده حذف شده است. هر بایت دیگر از متن یکسان است. ابزار اکنون گزارش میدهد:delivered : 137 words, 750 chars (floor 137 tokens)logged : noneverdict : BLIND (delivered-but-unbilled) exit 1detail : delivered >= 137 tokens of text; no usage frame at allreport-sha256: 318dff2585a7874bd04cd06a1430a9d3b303d93535910bd46be1d5987f81a4ce
این نشان میدهد که شکست در تنها یک خط رخ داده است. تفاوت (diff) بین این دو فایل صرفاً حذف {"type":"usage","input_tokens":523,"output_tokens":190} است. در محیط عملیاتی، این یک حذف دستی نیست؛ بلکه یک نوسان در اتصال یا یک برش توسط پروکسی است که باعث میشود داشبورد شما ادعا کند فراخوانی رایگان بوده است.
تست جامع روی نمونهها (Fixtures)
یک اجرای کامل روی هفت نمونه (stream_billing_gate.py fixtures/*.jsonl) تمام حالتهای ممکن، از جمله فریمهای استفاده خراب را پوشش میدهد:
fixture_broken_usage.jsonl: ۴۶ کلمه تحویل داده شد، اما فریم استفاده موجود است و غیرقابل تجزیه است $\rightarrow$ BLIND (خروج ۱).fixture_zero_usage.jsonl: ۵۵ کلمه تحویل داده شد، فریم استفاده صراحتاًoutput_tokens=0را گزارش میکند $\rightarrow$ BLIND (خروج ۱).fixture_undercount.jsonl: ۷۲ کلمه تحویل داده شد، ۱۲ توکن ثبت شد $\rightarrow$ UNDERCOUNT (خروج ۲).fixture_empty.jsonl: ۰ کلمه تحویل داده شد، چیزی برای تطبیق نیست $\rightarrow$ EMPTY (خروج ۰).fixture_injection.jsonl: ۶۰ کلمه تحویل داده شد شامل یک دستور جعلی «رایگان»، بدون فریم استفاده $\rightarrow$ BLIND (خروج ۱).
هنگامی که ابزار در برابر لاگهای واقعی ارائهدهندگان در fixtures/providers/ تست شد، بهدرستی anthropic_blind.jsonl (که در آن output_tokens صفر بود)، openai_chat_blind.jsonl (که در آن include_usage خاموش بود) و responses_blind.jsonl (که ۰ را در response.completed گزارش میکرد) را مسدود کرد. همچنین بهدرستی برای unknown_schema.jsonl حکم UNRECOGNIZED (خروج ۲) را با این جزئیات صادر کرد: 4 event(s) parsed, none matched a known stream shape. این تأیید میکند که ابزار اجازه نمیدهد یک فرمت لاگ ناشناخته بهطور خاموش عبور کند.
مخاطرات اقتصادی
این شکست با سختتر شدن حاشیه سود AI بسیار حیاتی است. در هفته ۷ ژوئیه، یک رشتهبحث برجسته در Hacker News با عنوان «GLM 5.2 و سقوط قریبالوقوع حاشیه سود AI» — که تقریباً ۶۸۰ امتیاز و ۴۶۵ نظر داشت — بر اقتصاد سختگیرانهی سرویسدهی مدلها تأکید کرد. در چنین محیطی، تفاوت بین هزینه واقعی و هزینه ثبتشده دیگر یک خطای گرد کردن نیست؛ بلکه یک ریسک سیستمی برای اقتصاد واحد است. این موضوع در کنار بررسی هزینههای واقعی عملیاتی در محیطهای تولیدی اهمیت بیشتری مییابد، جایی که فاصله بین بنچمارکهای تئوری و واقعیت مالی مشهود است.
ردیابی یک عدد با کنترل چیزی که آن عدد توصیف میکند متفاوت است. داشبورد شما دروغ نمیگوید؛ بلکه صادقانه عددی را گزارش میکند که در منبعش غلط است. هدف این نیست که صورتحساب را تا آخرین سنت محاسبه کنیم — این ابزار سیاستهای قیمتگذاری یا نرخهای مدل-خاص را مدیریت نمیکند — بلکه هدف این است که دیگر تظاهر نکنیم یک فراخوانی رایگان بوده است، در حالی که پاسخ روی صفحه نمایش داده شده است.
شفافسازی محدوده ابزار
برای جلوگیری از سردرگمی، تعریف اینکه این ابزار چه چیزی «نیست» اهمیت دارد:
- صورتحسابکن (Biller) نیست: دلار محاسبه نمیکند. کف متنی یک شمارش کلمه محافظهکارانه است، نه یک فاکتور فروشنده. نمیتواند مبلغ دقیق اضافهپرداخت را بگوید، زیرا این کار نیاز به سیاستهای قیمت و شناسایی مدل دارد.
- توکنایزر نیست: برای اجتناب از وابستگیها، از تقسیم بر اساس فضای خالی استفاده میکند. برای متون خاص (مثلاً کاراکترهای چینی یا ژاپنی بدون فاصله)، کف متنی به سمت ۱ سقوط میکند. در این موارد،
UNDERCOUNTغیرفعال میشود، اماBLINDهمچنان برقرار است زیرا هر متن غیرخالی به معنای تعداد توکن بالای صفر است. - ابزار تشخیصی نیست: نمیتواند بگوید چرا یک فریم غایب است. خطای فروشنده و افت شبکه هر دو شواهد یکسانی تولید میکنند: متن تحویل داده شده بدون تطبیق استفاده.
- تجزیه کننده اعشاری (Float Parser) نیست: یک شمارش غیر صحیح (مانند
190.0) به عنوان غیرقابل تجزیه تلقی شده و منجر بهBLINDمیشود. اگرچه این یک مثبت کاذب نادر برای کلاینتهایی است که اعداد اعشاری ثبت میکنند، اما وضعیت شکست محافظهکارانه را حفظ میکند.
این دروازه تطبیق در کنار سایر بررسیهای حیاتی، مانند نظارت بر «مالیات توکنی» اضافه شده توسط سرورهای MCP یا پیشبینی هزینههای حلقه (loop costs) قرار میگیرد. این چالشها مشابه بحرانهای هزینهای در حلقههای تکرار توکنها است که میتواند در مدت کوتاهی منجر به زیانهای مالی جدی شود. این ابزار هزینهای را اندازهگیری میکند که اتفاق افتاده و سپس از سوابق محو شده است. برای حل این مشکل، توسعهدهندگان باید چنین گیتهایی را در خط لولههای CI ادغام کنند تا تأیید شود لاگهای ثبتشده با واقعیت فیزیکی متن تحویل داده شده مطابقت دارند. سؤال واقعی باقی میماند: در لاگهای شما، چگونه یک فریم استفاده واقعاً گمشده را از پاسخی که بهطور قانونی هیچ خروجی قابل صورتحسابکنندهای تولید نکرده است، تشخیص میدهید؟
گام بعدی شما
- بررسی لاگهای فعلی خود برای شناسایی پاسخهایی که متن دارند اما توکن خروجی آنها صفر ثبت شده است.
- فعالسازی گزینه
include_usage: trueدر درخواستهای استریم OpenAI برای دریافت بسته هزینه در تکه نهایی. - ادغام مکانیزم تطبیق متن و توکن در خط لوله تست (CI) برای شناسایی نقاط ضعف در لایههای پروکسی و شبکه.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو