অধ্যায় 3 · টেমপ্লেট, মাল্টিমোডাল ও সেটিংস
প্রম্পট ক্যাশিং ও খরচ: স্থির প্রিফিক্স, ব্যবহারের ডেটা ও আনুমানিক হিসাব
- পৃষ্ঠা 14 / 24
- 14 মিনিট পড়া
অনেক AI অনুরোধের শুরুটা বারবার একই থাকে: একই নির্দেশ, একই উদাহরণ, একই ডকুমেন্ট, তারপর একটা ছোট নতুন প্রশ্ন। প্রোভাইডাররা একই শুরুর অংশে আগে করা কাজ আবার ব্যবহার করতে পারে; একে বলে প্রম্পট ক্যাশিং (prompt caching)। এতে খরচ ও দেরি কমতে পারে, কিন্তু তখনই, যখন প্রম্পট এমনভাবে বানাবেন যাতে শুরুটা সত্যিই হুবহু এক থাকে, এবং ইউজেজ ডেটা দেখে যাচাই করবেন কী ঘটেছে। এই পাতায় দেখবেন ক্যাশিংয়ের জন্য প্রম্পট কীভাবে সাজাবেন, ক্যাশিং কী নয়, ইউজেজ ডেটা থেকে হিট কীভাবে পড়বেন, এবং সেটিংসের ওপর নিয়ন্ত্রিত এক্সপেরিমেন্ট কীভাবে চালাবেন।
কোডে স্ট্যান্ডার্ড লাইব্রেরি আর promptlab ব্যবহার হয়েছে। এখানকার ক্যাশ একটা টয় প্রোভাইডার (toy provider): হুবহু-প্রিফিক্স ক্যাশের সিমুলেশন, ধারণাটার একটা মডেল, আসল সার্ভিস নয়। এটা Usage-এর cached_input_tokens ফিল্ডে হিট জানায়। সব দাম উদাহরণ দাম, আসল নয় (example prices, not real): হিসাবের অনুশীলনের জন্য বানানো একক। আসল প্রোভাইডারদের নিয়ম, ন্যূনতম দৈর্ঘ্য, মেয়াদ ও দাম আলাদা, তাই নিজে দেখে নিন।
যা শিখবেন
- স্থির প্রিফিক্স আর পরিবর্তনশীল ইনপুট কীভাবে আলাদা করবেন, এবং ক্রম কেন গুরুত্বপূর্ণ।
- প্রম্পট ক্যাশিং কী আর কী নয়: মেমরি নয়, রিট্রিভাল নয়, ফাইন-টিউনিং নয়।
- যে ইউজেজ ডেটা ক্যাশ হিট জানায় শুধু তা থেকে হিট কীভাবে মাপবেন।
- প্রোডাকশনের আগে ল্যাটেন্সি, খরচ, রিটেনশন ও দাম নিয়ে কীভাবে ভাববেন।
- নিয়ন্ত্রিত এক্সপেরিমেন্টে সেটিং বদলের আগে-পরের মান কীভাবে তুলনা করবেন।
স্থির প্রিফিক্স বনাম পরিবর্তনশীল ইনপুট
প্রতিটা প্রম্পট দুই ভাগে ভাগ করুন। স্থির প্রিফিক্স (stable prefix) হলো যা প্রতিবার এক: স্থায়ী নির্দেশ, আউটপুটের নিয়ম, উদাহরণ, রেফারেন্স ডকুমেন্ট। পরিবর্তনশীল ইনপুট (variable input) হলো যা বদলায়: এই ব্যবহারকারীর প্রশ্ন, আজকের রেকর্ড। ক্যাশিং সেই প্রম্পটকে পুরস্কৃত করে যাতে সব স্থির উপাদান আগে, একই ক্রমে, বাইটে-বাইটে এক, আর পরিবর্তনশীল ইনপুট শেষে। লং-কনটেক্সট মূল্যায়ন: চাংকিং, অবস্থান-পরীক্ষা ও হারানো তথ্য পাতার পরামর্শের (লম্বা উপাদান আগে, প্রশ্ন শেষে) সঙ্গে এটা মেলে, এবং ক্যাশিংয়ের জন্য প্রোভাইডাররাও এটাই বলে।
দুর্বল প্রম্পট:
Today is 2026-10-12. The customer is Mateo. Question: what is the refund window?
You answer questions about the shop policy. Use only the policy. Be brief.
### POLICY
(several pages of policy text)ভালো প্রম্পট:
You answer questions about the shop policy. Use only the policy. Be brief.
### POLICY
(several pages of policy text)
### REQUEST
Today is 2026-10-12. The customer is Mateo.
Question: what is the refund window?কী বদলাল: তারিখ আর গ্রাহকের নাম, যা প্রতি কলে বদলায়, শেষে সরে গেল। দুর্বল সংস্করণে প্রথম লাইন প্রতি কলে আলাদা, তাই তার পরের কিছুই আগের কলের সঙ্গে মেলে না।
ক্যাশিং মানে পুনরাবৃত্ত কনটেক্সটের পুনর্ব্যবহার
ক্যাশিং প্রোভাইডারের আগেই-প্রসেস-করা প্রিফিক্সের হিসাব আবার ব্যবহার করে। এতে মডেল কী জানে বা কীভাবে উত্তর দেয় তা বদলায় না; হিট হোক বা না হোক উত্তরের মান একই থাকার কথা। এটা খরচ ও দেরির অপ্টিমাইজেশন, আর হিট কখনো নিশ্চিত নয়।
ক্যাশিং কথোপকথনের মেমরি নয়
মেমরি মানে অ্যাসিস্ট্যান্ট আগের চ্যাটের তথ্য নতুন চ্যাটে নিয়ে যায়। ক্যাশে এমন কিছু জমা থাকে না যা আপনি জিজ্ঞেস করতে পারেন। ক্যাশ হিটের মানে শুধু এই অনুরোধের শুরু কোনো সাম্প্রতিক অনুরোধের শুরুর সমান, তাই প্রোভাইডারকে আবার প্রসেস করতে হয়নি। আপনি প্রতিবার পুরো প্রম্পটই পাঠান, আর মডেল শুধু তা-ই দেখে যা আপনি পাঠান।
ক্যাশিং রিট্রিভাল বা ফাইন-টিউনিং নয়
| ধারণা | কী করে | কী করে না |
|---|---|---|
| প্রম্পট ক্যাশিং | হুবহু প্রিফিক্সের কাজ আবার ব্যবহার করে খরচ ও দেরি বাঁচায় | জ্ঞান যোগ করে না, প্রাসঙ্গিক অংশ বাছে না, উত্তর বদলায় না |
| রিট্রিভাল | বড় উৎসের কোন টুকরো প্রম্পটে যাবে তা বাছে | নিজে থেকে পুনরাবৃত্ত লেখাকে সস্তা করে না |
| ফাইন-টিউনিং | বাড়তি ট্রেনিংয়ে মডেলের আচরণ বদলায় | প্রম্পট-বিন্যাসের সমস্যার দ্রুত সমাধান নয় |
এরা আলাদা সমস্যা সমাধান করে এবং একসঙ্গে ব্যবহার করা যায়। রিট্রিভাল ও ফাইন-টিউনিং AI Engineer পথের বিষয়।
প্রোভাইডারভেদে প্রিফিক্সের ক্রম
প্রোভাইডার নোট (২০২৬-১০-১২ অনুযায়ী): OpenAI-এর ডকুমেন্টেশন বলে ক্যাশ পুনর্ব্যবহারে পুরো রেন্ডার-করা প্রিফিক্স মিলতে হয়, এবং পুনর্ব্যবহার হিটের নিশ্চয়তা দেয় না। Anthropic-এর ডকুমেন্টেশন বলে ক্যাশ একটা ব্রেকপয়েন্ট পর্যন্ত
tools, তারপরsystem, তারপরmessagesঢাকে; হিটের জন্য ১০০% অভিন্ন অংশ লাগে, এবং এক স্তরে বদল হলে পরের স্তরগুলো অকার্যকর হয়। Google-এর ডকুমেন্টেশন বড় ও সাধারণ উপাদান শুরুতে রাখতে এবং একই রকম প্রিফিক্সের অনুরোধ কাছাকাছি সময়ে পাঠাতে বলে। ক্যাশিং ইমপ্লিসিট (প্রোভাইডার ঠিক করে) বা এক্সপ্লিসিট (আপনি ব্রেকপয়েন্ট চিহ্নিত করেন বা ক্যাশ অবজেক্ট পরিচালনা করেন) হতে পারে, এবং প্রাপ্যতা প্রোভাইডার ও মডেলভেদে আলাদা। প্রতিটা প্রোভাইডারের মডেল-ভিত্তিক ন্যূনতম প্রম্পট দৈর্ঘ্যও আছে; তার নিচে কিছুই ক্যাশ হয় না, আর Anthropic বলে সেটা কোনো এরর ছাড়াই হয়। আপনার মডেলের জন্য বর্তমান ডকুমেন্টেশন দেখুন।
সাধারণ নিয়ম হুবহু-প্রিফিক্সের পুনর্ব্যবহার। হিটের জন্য শুরুটা অভিন্ন হতে হয়, তাই আগের দিকের একটা অক্ষর বদলালে তার পরের সব নষ্ট হয়। প্রথম কোড ব্লকে ন্যূনতম দৈর্ঘ্য, ব্লক-মাপের পুনর্ব্যবহার ও মেয়াদসহ একটা টয় ক্যাশড মডেল বানানো হয়েছে, এবং একই প্রশ্ন নিয়ে স্থির-আগে ও পরিবর্তনশীল-আগে বিন্যাস চালানো হয়েছে।
import re
from promptlab import Client, estimate_tokens
POLICY = {"refund window": "14 days", "warranty": "24 months", "shipping fee": "6.50", "return label": "free",
"support hours": "08:00-18:00", "gift wrap": "2.00", "price match": "7 days", "repair time": "10 days"}
INSTRUCTIONS = "You answer questions about the shop policy below. Use only the policy. Be brief.\n"
DOC = "### POLICY\n" + "\n".join(f"- {k}: {v}. " + "Applies to all orders placed on the web shop. " * 3 for k, v in POLICY.items())
class ToyCachedModel:
"""Stand-in provider with an exact-prefix cache. It models the idea; it is not a real service."""
BLOCK, MINIMUM, TTL = 16, 64, 5 # toy numbers: reuse in blocks, a minimum length, entries expire after 5 ticks
def __init__(self):
self.seen, self.clock = {}, 0 # prompt text -> tick when last used
def __call__(self, messages, max_tokens=200, idle=1, **settings):
self.clock += idle
self.seen = {p: t for p, t in self.seen.items() if self.clock - t <= self.TTL}
prompt = "\n".join(m["content"] for m in messages)
best = 0
for old in self.seen: # longest shared beginning with any recent prompt
n = 0
while n < min(len(old), len(prompt)) and old[n] == prompt[n]:
n += 1
best = max(best, n)
cached = best // 4 // self.BLOCK * self.BLOCK
cached = cached if cached >= self.MINIMUM else 0
self.seen[prompt] = self.clock
topic = re.search(r"### QUESTION\nWhat is the (.*?)\?", prompt).group(1)
text = f"The policy covers this topic in one line, and the line applies to all orders. Answer: {POLICY[topic]}"
return {"text": text[: max_tokens * 4], "cached_input_tokens": cached}
QUESTIONS = [f"What is the {k}?" for k in POLICY]
def layout(kind, q):
if kind == "stable_first":
return [{"role": "system", "content": INSTRUCTIONS + DOC}, {"role": "user", "content": "### QUESTION\n" + q}]
return [{"role": "user", "content": "### QUESTION\n" + q + "\n" + INSTRUCTIONS + DOC}]
client = Client(ToyCachedModel())
for i, q in enumerate(QUESTIONS[:3], 1):
u = client.complete(layout("stable_first", q)).usage
print(f"stable-first call {i}: input {u.input_tokens}, cached {u.cached_input_tokens}")
client = Client(ToyCachedModel())
for i, q in enumerate(QUESTIONS[:3], 1):
u = client.complete(layout("variable_first", q)).usage
print(f"variable-first call {i}: input {u.input_tokens}, cached {u.cached_input_tokens}")stable-first call 1: input 358, cached 0
stable-first call 2: input 356, cached 352
stable-first call 3: input 358, cached 352
variable-first call 1: input 358, cached 0
variable-first call 2: input 357, cached 0
variable-first call 3: input 358, cached 0প্রথম কল কখনো হিট হতে পারে না, কারণ তার আগে কিছু ছিল না। স্থির-আগে বিন্যাসে পরের কলগুলো প্রম্পটের বেশিরভাগ পুনর্ব্যবহার করে। পরিবর্তনশীল-আগে বিন্যাসে প্রশ্ন আগে আসে, শুরুগুলো সঙ্গে সঙ্গে আলাদা হয়ে যায়, আর পরের লেখা অভিন্ন হলেও কিছু পুনর্ব্যবহৃত হয় না। একই শব্দ, ভিন্ন ক্রম, ভিন্ন ফল।
শুরুর একটা বদল বাকিটা নষ্ট করে
টিমগুলো প্রায়ই না জেনে ক্যাশ ভাঙে: প্রথম লাইনে টাইমস্ট্যাম্প, বদলানো উদাহরণ, ক্রম-বদলানো তালিকা। নিজের টেমপ্লেটে একটা জায়গা বদলে ইউজেজ ডেটা দেখে পরীক্ষা করুন।
def cached_after(edit):
model = Client(ToyCachedModel())
model.complete(layout("stable_first", QUESTIONS[0]))
messages = layout("stable_first", QUESTIONS[1])
messages[0] = {"role": "system", "content": edit(messages[0]["content"])}
return model.complete(messages).usage.cached_input_tokens
print("no edit :", cached_after(lambda s: s))
print("edit at the end :", cached_after(lambda s: s[:-12] + "CHANGED-END."))
print("edit at the start :", cached_after(lambda s: s.replace("You answer", "You respond", 1)))no edit : 352
edit at the end : 336
edit at the start : 0শেষের কাছে বদল আগের পুনর্ব্যবহারের বেশিরভাগ রেখে দেয়; শুরুতে বদল সবটুকু সরিয়ে দেয়। যা সবচেয়ে কম বদলায় তা আগে রাখুন, যা সবচেয়ে বেশি বদলায় তা শেষে।
ক্যাশ হিট মাপুন শুধু যেখানে ইউজেজ ডেটা তা জানায়
ক্যাশ চোখে দেখা যায় না; প্রোভাইডার যা জানায় শুধু তা পড়া যায়। "ইউজেজ ডেটায় ক্যাশ-করা টোকেন দেখা যাচ্ছে" কেবল এটাকেই হিটের প্রমাণ ধরুন। প্রোভাইডার ক্যাশ ফিল্ড না জানালে "শূন্য" মানে অজানা, মিস নয়; আর শুধু ল্যাটেন্সি কমাও কিছু প্রমাণ করে না, কারণ লোড ও আউটপুটের দৈর্ঘ্যও ল্যাটেন্সি বদলায়।
প্রোভাইডার নোট (২০২৬-১০-১২ অনুযায়ী): OpenAI ইউজেজ ডিটেইলসে ক্যাশড রিড জানায়। Anthropic জানায়
cache_creation_input_tokens,cache_read_input_tokensওinput_tokens, যার যোগফল মোট ইনপুট। Google ইউজেজ মেটাডেটায় ক্যাশড-টোকেন সংখ্যা জানায়; তাদের Interactions API-র জন্য ক্যাশিং পাতাusage.total_cached_tokensনাম বলে, আর অন্য API-গুলোর ফিল্ডের নাম আমি নিশ্চিত করতে পারিনি। দুটো ক্যাশ সংখ্যাই শূন্য হলে কিছুই ক্যাশ হয়নি। বর্তমান ডকুমেন্টেশন দেখুন।
def toy_no_usage(messages, **settings):
return "ok" # a provider that reports no cache field at all
def cache_status(response, exposed):
if not exposed:
return "unknown (no cache field reported)"
return "hit" if response.usage.cached_input_tokens else "miss"
cached_client, plain_client = Client(ToyCachedModel()), Client(toy_no_usage)
for _ in range(2):
r1 = cached_client.complete(layout("stable_first", QUESTIONS[0]))
r2 = plain_client.complete(layout("stable_first", QUESTIONS[0]))
print("toy cache :", cache_status(r1, exposed=True))
print("no field :", cache_status(r2, exposed=False), "| raw value:", r2.usage.cached_input_tokens)toy cache : hit
no field : unknown (no cache field reported) | raw value: 0দ্বিতীয় লাইনের কাঁচা মান 0, কিন্তু স্ট্যাটাস "unknown"। লগে প্রতিটা কলের পুরো ইউজেজ অবজেক্ট রাখুন, সঙ্গে নিজের ওয়াল-ক্লক সময়, কারণ এই প্রোভাইডারদের ইউজেজ ডেটায় ল্যাটেন্সি কোনো ফিল্ড নয়।
ল্যাটেন্সি ও খরচে ক্যাশের প্রভাব
হিট প্রথম আউটপুটের আগের দেরি এবং পুনরাবৃত্ত ইনপুটের দাম কমাতে পারে। এটা আউটপুটের খরচ কমায় না, আর যে প্রম্পট প্রোভাইডারের ন্যূনতম দৈর্ঘ্যের নিচে বা কখনো পুনরাবৃত্ত হয় না, তাকে সাহায্য করে না। কিছু প্রোভাইডারের ডকুমেন্টেশনে দামের আকৃতি এমন বর্ণনা করা আছে: প্রথম ব্যবহার (ক্যাশ রাইট) সাধারণ ইনপুটের চেয়ে বেশি পড়তে পারে, আর পরের রিড খরচের সামান্য অংশ। অন্য পাতায় কাঠামোটা বলা নেই, তাই প্রতিটা প্রাইস শিট পড়ুন। টয়ে সাধারণ ইনপুট, ক্যাশড ইনপুট ও আউটপুটের জন্য প্রতি মিলিয়ন টোকেনে উদাহরণ দাম, আসল নয় ধরা হয়েছে; রাইট-প্রিমিয়াম বাদ দেওয়া হয়েছে। টয় মডেলে ল্যাটেন্সি একটা নকল সংখ্যা, তাই তা পড়বেন না: আসল প্রোভাইডারে অনেক কলে ল্যাটেন্সি নিজে মাপুন।
প্রোডাকশনের আগে রিটেনশন ও দাম যাচাই
ক্যাশ এন্ট্রি চিরকাল থাকে না। প্রোভাইডার ও সেটিংসভেদে মেয়াদ কয়েক মিনিট থেকে কয়েক ঘণ্টা; দীর্ঘ মেয়াদের নিজস্ব দাম থাকতে পারে। আপনার ট্র্যাফিক মাঝেমধ্যে হলে কলের মাঝখানে এন্ট্রি মেয়াদ শেষ হয়ে যেতে পারে, আর আপনি পুরো দাম দেবেন। নিচের কনফিগারেশন C-তে টয়ের ছোট মেয়াদ দেখা যায়। প্রোডাকশনের আগে প্রোভাইডারের বর্তমান পাতায় দেখুন: আপনার মডেলের ন্যূনতম ক্যাশযোগ্য দৈর্ঘ্য, রিটেনশন ও তা কীভাবে নবায়ন হয়, রাইট ও রিডের দাম, ক্যাশিং ইমপ্লিসিট না মার্কার লাগে, আর আপনার ডেটা-নীতি তা অনুমতি দেয় কি না। তারপর ডেমো থেকে নয়, নিজের ট্র্যাফিক থেকে খরচের হিসাব করুন।
সেটিং বদলের আগে-পরের মানের তুলনা
যেকোনো বদল, বিন্যাস, সীমা বা মডেল সেটিং, একই পরীক্ষা চায়: একই কেস, একবারে একটা বদল, একই স্কোর। ক্যাশিংয়ে মান অপরিবর্তিত আর খরচ কম হওয়ার কথা; ভিন্ন আউটপুট সীমা দুটোই বদলাতে পারে। নিচের হারনেস একই আটটা প্রশ্নে চারটা কনফিগারেশন চালায় এবং সঠিক উত্তর, টোকেন, হিটের ভাগ, আর সেট ১,০০০ বার চালালে উদাহরণ-খরচ (উদাহরণ একক) ছাপে।
PRICE = {"input": 1.00, "cached": 0.10, "output": 4.00} # example prices per million tokens, not real
def run_experiment(kind, max_tokens=200, idle=1):
client = Client(ToyCachedModel())
ok = cached = total_in = out = 0
for q in QUESTIONS:
r = client.complete(layout(kind, q), max_tokens=max_tokens, idle=idle)
ok += POLICY[q[12:-1]] in r.text
cached += r.usage.cached_input_tokens
total_in += r.usage.input_tokens
out += r.usage.output_tokens
cost = ((total_in - cached) * PRICE["input"] + cached * PRICE["cached"] + out * PRICE["output"]) / 1e6
return ok, total_in, cached, cost
print(f"{'config':24} {'right':>5} {'input':>6} {'cached':>6} {'hit':>4} {'example cost x1000':>18}")
for name, kind, mt, idle in [("A stable first", "stable_first", 200, 1), ("B variable first", "variable_first", 200, 1),
("C stable, slow traffic", "stable_first", 200, 10), ("D stable, small cap", "stable_first", 20, 1)]:
ok, tin, ca, cost = run_experiment(kind, mt, idle)
print(f"{name:24} {ok:>3}/8 {tin:>6} {ca:>6} {ca / tin:4.0%} {cost * 1000:18.2f}")config right input cached hit example cost x1000
A stable first 8/8 2859 2464 86% 1.37
B variable first 8/8 2862 0 0% 3.59
C stable, slow traffic 8/8 2859 0 0% 3.59
D stable, small cap 0/8 2859 2464 86% 1.28এক্সপেরিমেন্ট হিসেবে পড়ুন। A হলো বেসলাইন। B-র মান একই কিন্তু খরচ বেশি, কারণ বিন্যাস ক্যাশ নষ্ট করছে। C-র বিন্যাস A-র মতো, কিন্তু কলগুলো এত দূরে দূরে যে এন্ট্রি মেয়াদ হারায়: ক্যাশ কিছুই বাঁচায়নি। D প্রতি কলে সবচেয়ে সস্তা দেখায় আর প্রতিটা প্রশ্নে ভুল, কারণ আউটপুট সীমা উত্তর কেটে দিয়েছে; মান-পরীক্ষায় ফেল করা সস্তা রান উন্নতি নয়। দুটো কলাম একসঙ্গে দেখে সিদ্ধান্ত নিন। টয় নির্ধারিত, তাই এখানে একবার চালানোই যথেষ্ট। আসল প্রোভাইডারে প্রতিটা কনফিগারেশন কয়েকবার চালান, কারণ মান ও ল্যাটেন্সি প্রতি রানে বদলায় (দেখুন স্যাম্পলিং ও রিজনিং কন্ট্রোল: temperature, top-p, effort ও পুনরাবৃত্তিযোগ্যতা)।
কাজের জায়গায় এটা কোথায় দেখা যায়
সাপোর্ট অ্যাসিস্ট্যান্ট প্রতিটা প্রশ্নের সঙ্গে একই পলিসি লেখা পাঠায়। ডকুমেন্ট-রিভিউ টুল একটা চুক্তি নিয়ে অনেক প্রশ্ন করে। মূল্যায়ন-রানে শত শত টেস্ট কেসের সঙ্গে একটা লম্বা নির্দেশ-ব্লক যায়। সবাই স্থির প্রিফিক্সে লাভ করে, এবং বাজেট ধরার আগে সবারই নিজের প্রোভাইডারের ইউজেজ ডেটায় সেই লাভ যাচাই করা উচিত। লং-কনটেক্সট মূল্যায়ন: চাংকিং, অবস্থান-পরীক্ষা ও হারানো তথ্য রানের আগে সাইজ আন্দাজ করা দেখিয়েছিল; এই পাতা যোগ করে প্রিফিক্স পুনরাবৃত্ত হলে রানের খরচ কেমন হওয়া উচিত।
সাধারণ ভুল
- তারিখ, ব্যবহারকারীর নাম বা রিকোয়েস্ট আইডি সবার আগে রাখা। তখন প্রতি কলের শুরু আলাদা। পরিবর্তনশীল অংশ শেষে রাখুন।
- ক্যাশ চ্যাট মনে রাখে, এমন ভাবা। এটা অভিন্ন প্রিফিক্সের হিসাব পুনর্ব্যবহার করে। আপনি তবু সবই পাঠান।
- ইউজেজ ডেটা ছাড়া হিটের দাবি করা। ক্যাশড-টোকেন ফিল্ড পড়ুন; না থাকলে "অজানা" বলুন।
- ডেমো থেকে বাজেট বানানো। ন্যূনতম দৈর্ঘ্য, মেয়াদ ও দাম আলাদা। প্রাইস শিট ও আসল ট্র্যাফিক ধরনটা দেখুন।
- মান না দেখে সস্তা সেটিং মেনে নেওয়া। একই কেসে আগে-পরে তুলনা করুন।
নিজে চেষ্টা করুন
পাতার ব্লকগুলো ক্রমানুসারে একটা Python ফোল্ডারে চালান, পাশে promptlab.py রেখে।
- সহজ। একটা প্রম্পট শুরু হয় "Request id 8841, 2026-10-12 09:15." দিয়ে, তারপর ৩,০০০ টোকেনের স্থির নিয়ম। হুবহু-প্রিফিক্স ক্যাশিং কেন কমই কাজে লাগবে, আর কীভাবে ঠিক করবেন?
- মাঝারি।
cached_afterদিয়ে সিস্টেম লেখার মাঝখানে (যেমন gift wrap লাইনে) বদল করুন এবং ক্যাশড টোকেন জানান। শুরু ও শেষের বদলের তুলনায় ক্যাশড পরিমাণ কোথায় পড়ে? - কঠিন। একটা নিয়ন্ত্রিত এক্সপেরিমেন্ট চালান: কনফিগারেশন E যোগ করুন, স্থির-আগে,
max_tokens=200ওidle=3, এবং একটা সিদ্ধান্ত-নিয়ম, যা কনফিগারেশন তখনই গ্রহণ করে যখন আটটাই ঠিক এবং উদাহরণ-খরচ A-র চেয়ে বেশি নয়। B, C, D ও E-র মধ্যে কোনগুলো পাস করে, জানান।
উত্তর
সহজ। প্রথম লাইন প্রতি কলে বদলায়, তাই শুরু কখনো আগের অনুরোধের সঙ্গে মেলে না, আর তার পরের ৩,০০০ টোকেন পুনর্ব্যবহৃত হয় না। রিকোয়েস্ট আইডি ও সময় স্থির নিয়মের পরে, শেষে নিন। পাস চেকলিস্ট: বদলাতে-থাকা প্রথম লাইন চিহ্নিত; পরিবর্তনশীল ডেটা শেষে সরানো; পরে ইউজেজ ডেটা যাচাইয়ের কথা বলা।
মাঝারি।
print("edit in the middle:", cached_after(lambda s: s.replace("gift wrap: 2.00", "gift wrap: 3.00", 1)))edit in the middle: 224ক্যাশড পরিমাণ আগের দুই ফলের মাঝখানে পড়ে: বদলের আগের লেখাটুকু ঢাকে, পরেরটা হারায়। পাস চেকলিস্ট: বদলের অবস্থান ব্যাখ্যা করা হয়েছে "মিল প্রথম পার্থক্যে থামে" দিয়ে; তিনটা ফল তুলনা করা; টয় থেকে আসল প্রোভাইডার নিয়ে দাবি নেই।
কঠিন।
base = run_experiment("stable_first")
for name, args in [("B", ("variable_first", 200, 1)), ("C", ("stable_first", 200, 10)),
("D", ("stable_first", 20, 1)), ("E", ("stable_first", 200, 3))]:
ok, tin, ca, cost = run_experiment(*args)
print(name, "accept" if ok == 8 and cost <= base[3] else "reject", f"(right {ok}/8, hit {ca / tin:.0%})")B reject (right 8/8, hit 0%)
C reject (right 8/8, hit 0%)
D reject (right 0/8, hit 86%)
E accept (right 8/8, hit 86%)A নিজেই বেসলাইন, তাই লুপে নেই। B ও C-র খরচ বেশি, আর D মান-নিয়মে ফেল করে। E গৃহীত হয় কারণ তিন টিক ব্যবধানের কল এখনও টয়ের মেয়াদের মধ্যে পড়ে, তাই তার খরচ A-র সমান। পাস চেকলিস্ট: নিয়মে মান ও খরচ দুটোই আছে; বেসলাইনের সঙ্গে ন্যায্য তুলনা; উত্তরে বলা আছে E-র ফল টয়ের বানানো মেয়াদের ওপর নির্ভর করে, আসল প্রোভাইডারের রিটেনশন দেখে নিতে হবে।
সারসংক্ষেপ
- স্থির উপাদান আগে, পরিবর্তনশীল ইনপুট শেষে, প্রতিবার একই ক্রমে রাখুন।
- প্রম্পট ক্যাশিং অভিন্ন প্রিফিক্স পুনর্ব্যবহার করে; এটা মেমরি, রিট্রিভাল বা ফাইন-টিউনিং নয়, আর উত্তর বদলায় না।
- হিটের দাবি করুন শুধু ক্যাশড-টোকেন জানানো ইউজেজ ডেটা থেকে; ফিল্ড না থাকলে মানে অজানা।
- বাজেট ধরার আগে বর্তমান ডকুমেন্টেশনে ন্যূনতম দৈর্ঘ্য, রিটেনশন ও দামের আকৃতি দেখুন।
- মান ও খরচ একসঙ্গে তুলনা করুন, একই কেসে, একবারে একটা জিনিস বদলে।
এরপর: Python প্রম্পট এক্সপেরিমেন্ট: টেস্ট কেস, রানার, JSONL লগ ও মক মোড, যেখানে এই একবারের এক্সপেরিমেন্টগুলোকে লগসহ পুনর্ব্যবহারযোগ্য রানারে বদলাবেন।