شمردنِ آنچه میفرستید
لیلا برای اولین بار mini را روی یک پروژهی واقعی اجرا میکند: چهل فایل، مجموعهای از آزمونها، و کاری که بیست دقیقه طول میکشد. کار انجام میشود. اما هزینهی همین یک کار از همهی اجراهای تمرینیاش روی هم بیشتر است، و نزدیکِ آخر انگار mini یادش رفته بود لیلا چه خواسته بود. لیلا پیش از اینکه چیزی را تغییر دهد، میخواهد ببیند: هر درخواستی که mini میفرستد چقدر بزرگ است، و از چه ساخته شده؟
لیلا میتوانم خودم بشمارم. نویسههای همهی چیزهای messages را جمع کنم و تقسیم بر چهار کنم.
امید میتوانی. چه چیزی را از قلم میاندازد؟
لیلا پرامپت سیستمی و توضیحِ ابزارها را، مگر یادم بماند. تازه آن چهار فقط یک قاعدهی سرانگشتی برای انگلیسی است. گزارشهای من جدول و عدد دارند.
امید پیامها هم فقط متن نیستند: فراخوانیِ ابزار، نتیجهها، فکر کردنِ مدل. چه کسی دقیقاً میداند همهی اینها چند توکن شد؟
لیلا خودِ API. باید تکتکشان را میخواند.
امید پس از خودش بپرس. هر پاسخ دقیقاً میگوید درخواست چند توکن داشت، و راهی هم هست که پیش از فرستادن بپرسی.
اندازهی هر درخواست
هر پاسخ یک شیءِ usage (مصرف) دارد: شمارشِ خودِ API از آنچه فرستادید و آنچه نوشت. آن را در درسِ «خواندنِ پاسخ» دیدید. برای مدیریتِ زمینه، سؤال این است: درخواستی که همین الان فرستادم چقدر بزرگ بود؟ جوابش جمعِ سه بخش است:
u = response.usage
size = u.input_tokens + u.cache_read_input_tokens + u.cache_creation_input_tokens
print(f"turn {turn + 1}: request {size:,} tokens, reply {u.output_tokens:,}")چرا سه بخش؟ به خاطرِ کش، که در پایانِ همین بخش به آن میرسیم. وقتی بخشی از درخواست از کش خوانده میشود، آن توکنها در cache_read_input_tokens شمرده میشوند، نه در input_tokens؛ توکنهایی که تازه در کش نوشته میشوند در cache_creation_input_tokens میروند. فقط باقیِ آن input_tokens است. هر سه را جمع کنید تا کلِ درخواست را داشته باشید، چه از کش آمده باشد چه نه. (اشتباهی رایج این است که فقط input_tokens را نگاه کنیم، ببینیم پس از راه افتادنِ کش کوچک مانده، و خیال کنیم زمینه بزرگ نمیشود.)
آن print را در حلقهی mini، درست بعد از رسیدنِ پاسخ بگذارید و یک کارِ واقعی را اجرا کنید. شکلی را که در پنجرهی زمینه دیدید، در عددهای خودتان خواهید دید: هر نوبت کمی بزرگتر از قبلی، با جهشهایی ناگهانی جایی که ابزاری چیزِ بزرگی برگردانده است.
شمردن پیش از فرستادن
گاهی عدد را پیش از خرج کردنِ هر چیزی میخواهید: این فایل جا میشود؟ این متنِ بلندی که میچسبانم چقدر اضافه میکند؟ API برای این کار یک endpoint جدا دارد: count_tokens. همان آرگومانهای یک درخواست را میگیرد و بیآنکه مدل را اجرا کند، شمارش را برمیگرداند:
def tokens_in(text):
"""How many tokens `text` would add, as a user message."""
count = client.messages.count_tokens(
model=MODEL, messages=[{"role": "user", "content": text}])
return count.input_tokens
print(tokens_in(Path("big_report.csv").read_text()))شمردن رایگان است، اما باز هم یک درخواست به API است، پس کمی طول میکشد و مثل هر درخواستِ دیگری محدودیتِ تعداد دارد. اگر system= و tools= را هم بدهید، اندازهی یک درخواستِ کامل را پیش از فرستادنش میگیرید. با همان مدلی بشمارید که واقعاً به آن میفرستید: توکنسازِ خانوادههای مختلفِ مدل فرق میکند، پس یک متن ممکن است جورِ دیگری شمرده شود.
توکنهای mini کجا میروند
چند جلسه را اندازه بگیرید، معمولاً همین تصویر را میبینید:
- آغازِ ثابت (پرامپت سیستمی و توضیحِ ابزارها) برای mini کوچک است: حدودِ هزار توکن. در بخشِ بعد که دستورها و مهارتها را اضافه میکنید، بزرگتر میشود.
- پیامهای خودِ مدل کوتاهاند: یک جمله، یک فراخوانیِ ابزار.
- نتیجههای ابزار تقریباً همهچیزند. یک
read_fileاز فایلی ۳٬۰۰۰ خطی میتواند ۳۰٬۰۰۰ توکن باشد، و از آن به بعد در همهی درخواستها میماند.
برای همین هوکِ cut_long_output در mini از هر نتیجه حداکثر ۱۰٬۰۰۰ نویسه نگه میدارد (حدودِ ۲٬۵۰۰ توکنِ انگلیسی). برای همین هم یک read_file بهتر بازهای از خطها را میگیرد، و یک ابزارِ جستوجو از خواندنِ کاملِ فایلها بهتر است. ارزانترین توکن آن است که هرگز وارد زمینه نشود.
چرا بودجهای خیلی پایینتر از پنجره
mini CONTEXT_BUDGET = 150_000 را تنظیم میکند، هرچند پنجرهی مدل یک میلیون توکن جا دارد. سه دلیل، که حالا میتوانید در عددهای خودتان ببینید:
- هر نوبت هزینهی کلِ درخواست را میدهد. یک درخواستِ ۶۰۰٬۰۰۰ توکنی، با قیمتِ ۴ دلار برای هر میلیون و پیش از کش، در هر نوبت حدودِ ۲٫۴۰ دلار هزینهی ورودی دارد.
- زمینهی شلوغ بدتر کار میکند. پاسخها معمولاً وقتی زمینه با نتیجههای قدیمی پر میشود مبهمتر میشوند، خیلی پیش از آنکه پنجره پر شود.
- پاسخ هم جا لازم دارد، در همان پنجره.
وقتی درخواستی از بودجه بگذرد، mini فشردهسازی میکند. این موضوعِ درسِ بعد است.
امتحان کنید
خطِ
درست به کار بردن
- وقتی عامل میسازید، اندازهی درخواست را در هر نوبت ثبت کنید. یک خط خروجی بیشتر از هر حدسی دربارهی زمینهی عاملتان میگوید.
- هر سه بخشِ ورودی را جمع بزنید. به محضِ اینکه کش کار کند،
input_tokensبهتنهایی دیگر «اندازهی درخواست» نیست. - پیش از افزودنِ چیزی بزرگ بشمارید.
count_tokensرایگان است؛ با آن تصمیم بگیرید فایلی کامل وارد شود، تکهای از آن، یا اصلاً نه. - نتیجههای ابزار را در همان سرچشمه کوچک کنید. یک سقف، یک بازهی خط یا یک ابزارِ جستوجو، زمینه را برای همهی نوبتهای بعد کوچک نگه میدارد.
واژههای این درس
usage(مصرف) - بخشی از هر پاسخ که توکنهای درخواست (input_tokens،cache_read_input_tokens،cache_creation_input_tokens) و پاسخ (output_tokens) را گزارش میکندcount_tokens- یک endpoint از API که توکنهای یک درخواست را بیآنکه مدل را اجرا کند میشمارد؛ رایگان- بودجهی زمینه (context budget) - سقفی که خودتان، خیلی پایینتر از پنجره، تعیین میکنید و عاملتان با رسیدن به آن زمینهاش را کوتاه میکند
ایدهی اصلی
لازم نیست زمینهی عاملتان را حدس بزنید: API اندازهی هر درخواست را گزارش میکند و میتواند یک درخواست را پیش از فرستادن بشمارد. در هر نوبت اندازه بگیرید تا ببینید نتیجههای ابزار پنجره را پر میکنند، پس همانجاست که باید برید.