अध्याय ३ · खण्ड II · 28 मिनेट
कुराकानी: टर्नमा स्थिति राख्ने
Multi-turn च्याटको व्यवहारिक कला। Reusable Conversation class, लामो histories का लागि sliding-window र summarisation रणनीतिहरू, र क्लासिक 'मेरो chatbot bill विस्फोट भयो' कथा रोक्ने साना बानीहरू।
अघिल्लो खण्डले memory client-side हो — तपाईंले इतिहास पठाउनुहुन्छ, मोडेलले प्रयोग गर्छ — भन्ने establish गर्यो। यो खण्ड त्यसलाई राम्रोसँग गर्ने कोड हो। Reusable Conversation class, कुराकानी लामो हुँदै जाँदा context window नियन्त्रणमा राख्ने रणनीति, र तपाईंको मासिक bill लाई पूर्वानुमानयोग्य राख्ने साना operational बानीहरू। अन्त्यसम्म तपाईंसँग कुनै पनि chatbot मा निर्माण गर्न सक्ने foundation हुनेछ।
Strategy A — पूरा इतिहास
सबैभन्दा सरल सही implementation: हरेक सन्देश राख्नुहोस्, हरेक कल सबै पठाउनुहोस्।
import anthropic
client = anthropic.Anthropic()
class Conversation:
def __init__(self, system_prompt: str, model: str = "claude-haiku-4-5-20251001"):
self.system = system_prompt
self.model = model
self.messages: list[dict] = []
def ask(self, user_message: str, max_tokens: int = 800) -> str:
self.messages.append({"role": "user", "content": user_message})
response = client.messages.create(
model=self.model,
max_tokens=max_tokens,
temperature=0.7,
system=self.system,
messages=self.messages,
)
assistant_text = response.content[0].text
self.messages.append({"role": "assistant", "content": assistant_text})
return assistant_text
Usage:
conv = Conversation(
system_prompt=(
"You are a friendly Nepali-speaking assistant. Respond in the "
"same language the user writes in. Keep responses under three "
"sentences unless asked otherwise."
)
)
print(conv.ask("नमस्ते! मेरो नाम निश्चल हो।"))
print(conv.ask("मेरो नाम याद छ?"))
print(conv.ask("के मैले खल्ती अलर्ट स्क्रिप्ट बनाउन सक्छु?"))
पच्चीस लाइन। काम गर्छ। छोटा कुराकानीका लागि (20 turns मुनि), यो तपाईंलाई चाहिने सबै हो।
जब पूरा इतिहास break हुन्छ
Scale मा दुई समस्याहरू देखा पर्छन्:
लागत। हरेक turn ले सबै अघिल्ला input tokens पुन: पठाउँछ। 30-turn कुराकानी जहाँ हरेक turn 200 tokens छ ले कुराकानीको अवधिमा 90,000 input tokens (200 + 400 + 600 + … + 6000 = 90k) पठाउँछ। Haiku मूल्यमा ($0.25/M) यो कुराकानीका लागि लगभग $0.023 हो — विनाशकारी होइन, तर धेरै users र धेरै लामो sessions मा गुणित हुँदा जम्मा हुन्छ।
Context window। धेरै लामो कुराकानीहरू (300+ turns, वा ठूला uploaded documents सँग) का लागि, तपाईं eventually 200k token सीमा हिट गर्नुहुन्छ र API ले error फर्काउँछ।
दुवै समस्याहरूको समाधान उही छ: पूरा इतिहास नपठाउनुहोस्। यसलाई trim गर्नुहोस्।
Strategy B — sliding window
केवल अन्तिम N turns राख्नुहोस्:
class Conversation:
def __init__(self, system_prompt: str, window: int = 10, model: str = "claude-haiku-4-5-20251001"):
self.system = system_prompt
self.window = window
self.model = model
self.messages: list[dict] = []
def ask(self, user_message: str, max_tokens: int = 800) -> str:
self.messages.append({"role": "user", "content": user_message})
# Only send the most recent `window` messages (each user/assistant pair is 2)
recent = self.messages[-(self.window * 2):]
response = client.messages.create(
model=self.model,
max_tokens=max_tokens,
temperature=0.7,
system=self.system,
messages=recent,
)
text = response.content[0].text
self.messages.append({"role": "assistant", "content": text})
return text
window=10 सँग, मोडेलले सधैँ user+assistant सन्देशहरूको अन्तिम 10 pairs देख्छ, कुराकानी कति लामो भए पनि। पुराना सन्देशहरू चुपचाप बिर्सिन्छन्।
यो सुन्दर रूपमा काम गर्छ:
- Customer support (हालैको context महत्त्व राख्छ, पुरातन context राख्दैन)।
- Q&A bots (हरेक प्रश्न अधिकांश एक्लै उभिन्छ)।
- Task-focused chatbots जहाँ वर्तमान task महत्त्वपूर्ण छ।
यो असफल हुन्छ:
- कुनै पनि कुरा जहाँ user ले 20 turns पहिलाको केही सन्दर्भ गर्न सक्छ (“जस्तै मैले पहिले भनेको थिएँ…”)।
- लामो chats जहाँ personality/preferences तपाईंले पहिले सेट अप गर्नुभयो मा महत्त्व राख्न जारी रहन्छ।
ती का लागि, तपाईंलाई summarisation चाहिन्छ।
Strategy C — summarised इतिहास
ढाँचा: कुराकानी बढ्दै जाँदा, पुरानो turns लाई periodically compact block मा summarise गर्नुहोस्, र त्यो summary लाई हालैको window मा prepend गर्नुहोस्।
class SummarisingConversation:
def __init__(
self,
system_prompt: str,
keep_recent: int = 6,
summarise_after: int = 12,
model: str = "claude-haiku-4-5-20251001",
):
self.system = system_prompt
self.keep_recent = keep_recent # last N turns to keep verbatim
self.summarise_after = summarise_after # when to trigger summarisation
self.model = model
self.messages: list[dict] = []
self.summary: str = ""
def _summarise_old_messages(self):
"""Compress everything older than the recent window into a summary."""
to_summarise = self.messages[: -(self.keep_recent * 2)]
if not to_summarise:
return
summary_prompt = (
"Summarise the following conversation history in 3-5 sentences. "
"Focus on facts about the user, their goals, and any decisions made. "
"Preserve names, preferences, and constraints.\n\n"
+ "\n".join(f"{m['role'].upper()}: {m['content']}" for m in to_summarise)
)
response = client.messages.create(
model=self.model,
max_tokens=400,
temperature=0.0,
messages=[{"role": "user", "content": summary_prompt}],
)
new_summary = response.content[0].text.strip()
# Merge with previous summary if present
if self.summary:
self.summary = f"{self.summary}\n\nMore recent: {new_summary}"
else:
self.summary = new_summary
# Drop the summarised messages from the live history
self.messages = self.messages[-(self.keep_recent * 2):]
def ask(self, user_message: str, max_tokens: int = 800) -> str:
self.messages.append({"role": "user", "content": user_message})
# Summarise if we've grown too long
if len(self.messages) > self.summarise_after * 2:
self._summarise_old_messages()
# Build the system prompt with any summary prepended
system = self.system
if self.summary:
system = f"{system}\n\nCONVERSATION SO FAR (summary):\n{self.summary}"
response = client.messages.create(
model=self.model,
max_tokens=max_tokens,
temperature=0.7,
system=system,
messages=self.messages,
)
text = response.content[0].text
self.messages.append({"role": "assistant", "content": text})
return text
ढाँचा:
summarise_afterturns मुनि: सबै पठाउनुहोस्।- यसभन्दा माथि: पुरानो turns लाई compact block मा summarise गर्नुहोस्, अन्तिम
keep_recentलाई verbatim राख्नुहोस्, summary लाई system prompt मा prepend गर्नुहोस्। - Summary समयसँगै बढ्छ जब नयाँ segments summarise हुन्छन्।
सयौँ turns चल्ने chats का लागि, यो ढाँचाले लागतलाई bounded र context सान्दर्भिक राख्छ। अधिकांश गम्भीर उत्पादन chatbots ले प्रयोग गर्ने recipe यही हो।
Token accounting
कुनै पनि वास्तविक chat अनुप्रयोगका लागि, प्रति कुराकानी tokens र लागत ट्र्याक गर्नुहोस्:
class Conversation:
# ... previous code ...
def __init__(self, ...):
# ...
self.total_input_tokens = 0
self.total_output_tokens = 0
def ask(self, user_message: str, max_tokens: int = 800) -> str:
# ... build messages, call API ...
self.total_input_tokens += response.usage.input_tokens
self.total_output_tokens += response.usage.output_tokens
return text
def cost_so_far_haiku(self) -> float:
return (
self.total_input_tokens / 1_000_000 * 0.25 +
self.total_output_tokens / 1_000_000 * 1.25
)
एक-लाइन conv.cost_so_far_haiku() ले तपाईंलाई ठ्याक्कै कुराकानीको लागत के हो भन्छ। उत्पादनमा, यसलाई प्रति session log गर्नुहोस्। साप्ताहिक distribution हेर्नुहोस् — यदि 95th-percentile कुराकानीको median भन्दा 20x लागत छ भने, तपाईंसँग सुधार गर्न लायक runaway-user समस्या छ।
System prompt पनि memory हो
Underused प्रविधि: message history मा होइन, system prompt मा stable facts राख्नुहोस्।
system = f"""You are a Nepali-speaking assistant.
Known facts about this user:
- Name: {user.name}
- Location: {user.city}
- Preferred language: {user.language}
- Signed up: {user.created_at}
- Interests: {', '.join(user.interests)}
Guidelines: [...]
"""
यहाँ inject गरिएका facts मोडेलले सधैँ देख्न सक्ने हुन्छ, sliding windows द्वारा trim हुँदैनन्, र दोहोर्याइन आवश्यक हुँदैन। तिनी यो user को मोडेलको “long-term memory” हुन्।
यसरी वास्तविक chatbots ले sessions भर users लाई सम्झन्छन्: app ले user द्वारा keyed database मा facts store गर्छ, हरेक session को सुरुमा system prompt मा inject गर्छ, र LLM ले तिनलाई user को यसको पहिचानको part रूपमा व्यवहार गर्छ।
सानो इमानदार टिप्पणी
यीमध्ये कुनै पनि रणनीति पूर्ण छैन। Sliding windows ले साँच्चै पुरानो context बिर्सन्छन्। Summaries ले विवरण गुमाउँछन्। 200k-token पूर्ण-इतिहास chats मा पनि कहिलेकाहीँ बीचमा उल्लेख गरिएका कुराहरूको track गुमाउँछन्। राम्रो chatbot memory निर्माण गर्नु के गुमाउने रोज्ने engineering अनुशासन हो — किनकि तपाईं सधैँ केही गुमाउनुहुन्छ। यो खण्डका रणनीतिहरू इमानदार, राम्रोसँग-परीक्षण गरिएका defaults हुन्; उत्पादन अनुप्रयोगहरूले सामान्यतया तिनलाई blend गर्छन्।
आफ्नो बुझाइ जाँच्नुहोस्
Quick check
—एक साथीको customer-support chatbot ले पहिलो 10 turns का लागि राम्रोसँग काम गर्छ, त्यसपछि नाम hallucinate गर्न र users लाई confuse गर्न थाल्छ। छानबिनले तिनी full-history strategy प्रयोग गर्दै छन् र कुराकानीहरू प्रायः 30+ turns चल्छन् देखाउँछ। सम्भावित कारण के हो?
Quick check
—उत्पादन नेपाली chatbot लाई sessions (हप्ता वा महिना टाढा) भर user preferences सम्झनुपर्छ। कुन architectural component ले यो समाधान गर्छ, यो खण्डका अनुसार?
अब के आउँछ
तपाईंसँग stateful chat निर्माण गर्ने औजार छ। अध्याय 3 को अन्तिम खण्डले तिनलाई सँगै राख्छ: system prompt, sliding-window memory, cost tracking, र काम गर्ने command-line UI सहितको पूर्ण, इमानदार नेपाली chatbot। खण्ड 3 को अन्त्यसम्म तपाईंसँग तपाईंले आफ्नो terminal मा वास्तवमै run गर्न र नेपालीमा chat गर्न सक्ने केही हुनेछ।