ব্লগে ফিরে যান

শিক্ষানবিসদের জন্য AIগাইড

Python-এ একটি LLM ব্যাচের খরচ সীমাবদ্ধ করুন: একটি লেজার ও একটি কঠোর বাজেট

প্রতিটি API কলের টোকেন ও খরচ একটি JSONL ফাইলে লগ করুন, বাজেটে ব্যাচ থামান, এবং চালানোর আগেই বিল অনুমান করুন। Python কোড অন্তর্ভুক্ত।

CheapTokens Team দ্বারাপ্রকাশিত হয়েছে 10 মিনিট পড়ার সময়

একটি ব্যাচ রান দেখানো টার্মিনাল যা বাজেটে পৌঁছালে থামে
এই পৃষ্ঠায়
  1. Clocktower রান যা করে, আপনি তা কপি করতে পারেন
  2. লেজার: একটি ফাইল, প্রতি কলে একটি লাইন
  3. ব্যাচ লুপ: যা হয়ে গেছে তা বাদ দিন, ক্যাপে থামুন
  4. রান চাপার আগে বিলটা অনুমান করুন
  5. বিল প্রত্যাশার চেয়ে বড় হলে: লেজার পড়ুন
  6. লঞ্চের আগে

আপনার স্ক্রিপ্ট ২০০টি প্রম্পটের ওপর লুপ চালায়, আপনি ল্যাপটপের ঢাকনা বন্ধ করেন, আর সকালে জানতে পারেন রাতটা কত খরচ করেছে। দুটি ছোট অভ্যাসই এটি ঠিক করে দেয়। প্রতিটি API কলের পরে, টোকেন ও খরচের একটি লাইন একটি লেজার ফাইলে যোগ করুন। প্রতিটি কলের আগে, চলমান মোটকে বাজেটের সাথে তুলনা করুন এবং পৌঁছে গেলে থামুন। এটি ৫০ লাইনেরও কম Python, আর ৫-প্রম্পটের একটি ট্রায়াল রান আসল রান শুরুর আগেই আপনাকে বিলটা জানিয়ে দেয়।

Clocktower রান যা করে, আপনি তা কপি করতে পারেন#

Dwarkesh Podcast-এর হোস্ট Dwarkesh Patel, Blood on the Clocktower-এ ১১টি মডেলকে একে অপরের বিরুদ্ধে চালিয়েছেন, README অনুযায়ী ৩৮৬টি গেম, এবং কোড, লগ ও রেটিং প্রকাশ করেছেন (github.com/dwarkeshsp/clocktower, ৮ নভেম্বর ২০২৬-এ যাচাই করা)। রিপো থেকে দুটি বিবরণ চুরি করার মতো। প্রতিটি গেম ডিরেক্টরি টোকেন ও খরচসহ প্রতি-কলের একটি লগ রাখে, আর খরচ games/ledger.jsonl-এ যায়। একটি --budget ফ্ল্যাগ মোট খরচ সীমাবদ্ধ করে; ডিফল্ট $1,000। বেঞ্চমার্ক সাইটটি তৃতীয় একটি সুরক্ষা যোগ করে: প্রতিটি মডেল প্রতি টার্নে একই ৮,০০০-টোকেন সিলিং পায় (clocktowerbench.com)।

তাহলে: প্রতিটি কলে একটি সিলিং, প্রতিটি কলের একটি লগ, মোটের ওপর একটি ক্যাপ। একটি টুর্নামেন্টের তিনটিই দরকার, আর আপনার রাতভর রানেরও, শুধু ছোট পরিসরে।

লেজারকে ভাবুন দোকানের ক্যাশ রেজিস্টারের রসিদের রোলের মতো। প্রতিটি বিক্রি একটি লাইন ছাপে; ম্যানেজার যেকোনো মুহূর্তে রোলটা যোগ করতে পারেন এবং জানেন কখন দিনের ভাঙতি শেষ।

লেজার: একটি ফাইল, প্রতি কলে একটি লাইন#

পুরো জিনিসটা এখানে। এটি openai প্যাকেজ ব্যবহার করে যা CheapTokens-এ পয়েন্ট করা (https://api.cheaptokens.dev/v1, কী ও মডেল আইডির জন্য ডকস দেখুন)।

python
import json
import os
from pathlib import Path

from openai import OpenAI

client = OpenAI(
    base_url='https://api.cheaptokens.dev/v1',
    api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'deepseek-ai/DeepSeek-V4-Flash-0731'
PRICE_IN, PRICE_OUT = 1.00, 2.00  # USD per 1M tokens: example, substitute current ones


class BudgetExceeded(Exception):
    pass


class Ledger:
    def __init__(self, path, budget_usd):
        self.path = Path(path)
        self.budget = budget_usd
        self.rows = []
        if self.path.exists():
            with self.path.open() as f:
                self.rows = [json.loads(line) for line in f if line.strip()]

    @property
    def spent(self):
        return sum(r['cost_usd'] for r in self.rows)

    def done(self):
        return {r['unit'] for r in self.rows}

    def check(self, unit):
        if self.spent >= self.budget:
            raise BudgetExceeded(
                f'stopped before {unit}: spent {self.spent:.4f} of {self.budget} USD'
            )

    def add(self, unit, usage, finish_reason):
        cost = (usage.prompt_tokens * PRICE_IN
                + usage.completion_tokens * PRICE_OUT) / 1_000_000
        row = {
            'unit': unit,
            'model': MODEL,
            'prompt_tokens': usage.prompt_tokens,
            'completion_tokens': usage.completion_tokens,
            'finish_reason': finish_reason,
            'cost_usd': round(cost, 6),
        }
        self.rows.append(row)
        with self.path.open('a') as f:
            f.write(json.dumps(row) + '\n')


def ask(ledger, unit, prompt, max_tokens=400):
    ledger.check(unit)
    resp = client.chat.completions.create(
        model=MODEL,
        messages=[{'role': 'user', 'content': prompt}],
        max_tokens=max_tokens,
    )
    ledger.add(unit, resp.usage, resp.choices[0].finish_reason)
    return resp.choices[0].message.content

প্রতিটি কল একই চারটি ধাপের মধ্য দিয়ে যায়:

প্রতিটি API কলের চারপাশে চারটি ধাপ: বাজেট যাচাই, কল, ব্যবহার পড়া, লেজারে যোগ
র্যাপারের প্রতিটি কলের পেছনের চক্র

সেখানে তিনটি সিদ্ধান্ত ইচ্ছাকৃত।

  • প্রতি লাইনে একটি JSON অবজেক্ট (JSONL), অ্যাপেন্ড মোডে খোলা। স্ক্রিপ্ট যদি ১৩০ নম্বর কলে ক্র্যাশ করে, ১ থেকে ১২৯ নম্বর কল ইতিমধ্যে ডিস্কে আছে।
  • মোট আসে ফাইল থেকে, কোনো ভেরিয়েবল থেকে নয়। স্ক্রিপ্ট রিস্টার্ট করুন, ক্যাপ তবুও টিকে থাকে, কারণ লেজার মনে রাখে আগের রান কত খরচ করেছে।
  • finish_reason টোকেনের পাশে লগ হয়। যে সারিতে length লেখা, তার মানে উত্তরটি আপনার max_tokens-এ পৌঁছে কেটে গেছে। সেটি পরে জানতে চাইবেন।

দুটি PRICE_ ধ্রুবক প্লেসহোল্ডার। বর্তমান সংখ্যাগুলো প্রাইসিং টেবিল থেকে নিন। আপনি যখন এটি পড়ছেন তখন DeepSeek-V4-Flash-এর জন্য, তা হলো প্রতি ১M ইনপুট টোকেনে $0.015 এবং প্রতি ১M আউটপুট টোকেনে $0.10। MODEL-কে zai-org/GLM-5.3-Flash বা MiniMaxAI/MiniMax-M2.7 এবং দুটি ধ্রুবক বদলান, একই লেজার তাদের জন্যও কাজ করবে।

ব্যাচ লুপ: যা হয়ে গেছে তা বাদ দিন, ক্যাপে থামুন#

লুপটি ছোট। ধরুন আপনার প্রম্পটগুলো একটি dict-এ আছে যা একটি ইউনিট আইডি দিয়ে কী করা, কাজের একটি টুকরোর নাম, যেমন doc-001:

python
prompts = {
    'doc-001': 'Summarize this ticket in one sentence: ...',
    'doc-002': 'Summarize this ticket in one sentence: ...',
    # ... your 200 prompts
}

ledger = Ledger('ledger.jsonl', budget_usd=0.50)
already = ledger.done()

for unit, prompt in prompts.items():
    if unit in already:
        continue
    try:
        answer = ask(ledger, unit, prompt)
    except BudgetExceeded as e:
        print(e)
        break
    Path(f'out-{unit}.txt').write_text(answer)

এটি চালান, মাঝপথে Ctrl+C চাপুন, আবার চালান: শেষ হওয়া ইউনিটগুলো বাদ পড়ে, তাই লেজারই আপনার অগ্রগতি ফাইল। বাজেট শেষ হতে দিন, আর অবাক হওয়ার বদলে একটি লাইন পাবেন, যেমন stopped before doc-142: spent 0.5003 of 0.5 USD।

আগে শব্দটি খেয়াল করুন। যাচাই প্রতিটি কলের আগে ঘটে, তাই শেষ কলটি মোটকে বাজেটের সামান্য ওপরে ঠেলে দিতে পারে। কতটা ওপরে, তা সীমিত: একটি কল আপনার দীর্ঘতম প্রম্পট যোগ max_tokens-এর বেশি খরচ করতে পারে না। ১,৫০০-টোকেনের প্রম্পট এবং max_tokens=400-এর জন্য তা সর্বোচ্চ $0.000063। এজন্যই র্যাপারের প্রতিটি কলে একটি max_tokens থাকে: এটি একটি খোলা-প্রান্তের ঝুঁকিকে এমন একটি সংখ্যায় পরিণত করে যা নিয়ে আপনি পরিকল্পনা করতে পারেন।

একটি লেজার ফাইলের সারি, প্রতি কলে টোকেন এবং শেষে একটি বাজেট স্টপ
উদাহরণ লেজার সারি ও স্টপ বার্তা (দৃষ্টান্তমূলক মান)

রান চাপার আগে বিলটা অনুমান করুন#

এখন বাকি অর্ধেক: লঞ্চের আগেই খরচ জানা। প্রথম পাঁচটি প্রম্পট একটি ছোট বাজেট দিয়ে ask()-এর মধ্য দিয়ে চালান এবং লেজার পড়ুন। ধরুন পাঁচটি সারি গড়ে ১,৫০০ ইনপুট টোকেন ও ৪০০ আউটপুট টোকেন। এগুলোই পুরো ব্যাচের জন্য আপনার সংখ্যা।

২০০ প্রম্পটের জন্য তা হলো ২০০ × ১,৫০০ = ৩,০০,০০০ ইনপুট টোকেন এবং ২০০ × ৪০০ = ৮০,০০০ আউটপুট টোকেন। এখানে লাইভ দামে সেই কাজের চাপ:

মডেল প্রতি অনুরোধে ২০০টি অনুরোধ OpenRouter, ২০০টি অনুরোধ
GLM-5.3-Flash $0.000046 $0.0092 $0.0167
DeepSeek-V4-Flash $0.000063 $0.0125 $0.0322
MiniMax-M2.7 $0.00014 $0.027 $0.324

প্রতি অনুরোধে ১,৫০০ ইনপুট এবং ৪০০ আউটপুট টোকেন। প্রতি 1M টোকেনের দাম বর্তমান এবং আমাদের ট্যারিফের সাথে পরিবর্তিত হয়; OpenRouter কলামটি একই ওয়ার্কলোডের জন্য রেফারেন্স মূল্য দেখায়।

টেবিলে একটি OpenRouter কলামও আছে। ইনপুট ও আউটপুট একসাথে গুনলে, এই লোডের জন্য DeepSeek-V4-Flash OpenRouter-এর চেয়ে সস্তা পড়ে, আর সেই যোগফলই ব্যাচ আসলে দেয়।

প্রম্পটের দৈর্ঘ্য ভিন্ন, তাই পাঁচটি নমুনা একটি অনুমান, প্রতিশ্রুতি নয়। নিরাপদ ঊর্ধ্বসীমা চাইলে পাঁচটির মধ্যে দীর্ঘতমটি ব্যবহার করুন, তারপর বাকিটা বাজেট ক্যাপ ধরুক। একই ২০০ প্রম্পটে দুটি মডেল তুলনা করতে চান? প্রতিটি মডেলকে নিজস্ব লেজার ফাইল দিন, যেমন Ledger(f'ledger-{MODEL.split("/")[-1]}.jsonl', 0.50), আর পরে দুটি মোট পাশাপাশি থাকবে।

এখানে আপনার নিজের ব্যাচ চেষ্টা করুন: আপনার নমুনা থেকে প্রতি রিকোয়েস্টে টোকেন এবং আপনি কত রিকোয়েস্টের পরিকল্পনা করছেন তা সেট করুন।

খরচ ক্যালকুলেটর

আপনার ব্যাচ: আপনার ৫-প্রম্পট নমুনা থেকে টোকেন

প্রতি অনুরোধে ইনপুট টোকেন: ১,৫০০ · প্রতি অনুরোধে আউটপুট টোকেন: ৪০০ · প্রতিদিন অনুরোধ: ২০০

মডেলপ্রতিদিনপ্রতি মাসে
GLM-5.3-Flash$0.0092$0.276
DeepSeek-V4-Flash$0.0125$0.375
MiniMax-M2.7$0.027$0.81

পাঠানোর আগে টোকেন গোনার আরও জন্য, tiktoken দিয়ে টোকেন গোনা দেখুন।

বিল প্রত্যাশার চেয়ে বড় হলে: লেজার পড়ুন#

তৃতীয় পাঠকের পরিস্থিতিটি হলো খারাপটি। একটি এজেন্ট রান ভুল হয়ে লুপে পড়ে, আর ড্যাশবোর্ড শুধু একটি মোট দেখায়। লেজার দেখায় কোথায় গেল। সারিগুলো খরচ অনুযায়ী সাজান:

python
rows = [json.loads(line) for line in open('ledger.jsonl')]
for r in sorted(rows, key=lambda r: r['cost_usd'], reverse=True)[:5]:
    print(r['unit'], r['prompt_tokens'], r['completion_tokens'], r['finish_reason'])

এজেন্টের জন্য, ধাপের নামে ইউনিটের নাম দিন (step-17) যাতে রানের আকৃতি দেখতে পারেন। কয়েকটি প্যাটার্ন বারবার দেখা যায়; প্রতিটিকে তার স্বাভাবিক কারণের সাথে মেলান:

জোড়া মেলান

প্রতিটি লেজার প্যাটার্নকে তার স্বাভাবিক কারণের সাথে মেলান

  • প্রতিটি সারিতে prompt_tokens একটু বাড়েলুপ প্রতিটি ধাপে পুরো হিস্ট্রি আবার পাঠায়
  • completion_tokens সমান max_tokens এবং finish_reason হলো lengthউত্তর আপনার ক্যাপে কেটে যায়, আর কাটা টেক্সটের জন্য আপনি দেন
  • একই ইউনিট পরপর বহুবার আসেএকটি রিট্রাই লুপ, বা একটি ধাপ বারবার পুনরাবৃত্তি করা এজেন্ট
  • একটি সারির খরচ বাকি সবগুলোর চেয়ে অনেক বেশিএকটি অতিরিক্ত বড় ইনপুট, যেমন প্রম্পটে পুরো একটি ফাইল পেস্ট করা

প্রতিটি প্যাটার্ন ইনভয়েসের অনেক আগেই লেজারে দৃশ্যমান। প্রথমটি কেন একটি এজেন্ট তার হিস্ট্রি আবার পড়ে-তে আলোচনা করা হয়েছে।

স্ক্রিপ্টের বাইরে প্রতি-প্রজেক্ট খরচ ও অ্যালার্টও চাইলে, প্রতি-প্রজেক্ট বাজেট অ্যালার্টের এই গাইড সেই দিকটি কভার করে। এখানকার লেজার হলো সেই অংশ যা আপনার কোডে থাকে এবং 'থামুন' বলতে পারে।

লঞ্চের আগে#

চেকলিস্ট

প্রথম আসল রানের আগে

DeepSeek-V4-Flash-এর কনটেক্সট সাইজ ও উত্তরের সীমা, max_tokens বাছাই করার সময় কাজে লাগবে, তার পেজে আছে: CheapTokens-এ DeepSeek-V4-Flash।