पिछले अध्याय में हमने देखा कि AI एजेंट दिमाग (LLM), औज़ार (टूल), याददाश्त (मेमोरी) और लूप (नियंत्रण) — इन 4 घटकों से बनता है। इस अध्याय में अब हम हाथ चलाएँगे। लक्ष्य सीधा है — 4 घटकों को न्यूनतम ढाँचे में जोड़कर एक "चलता हुआ एजेंट" बनाना, और उसे सचमुच चलाकर उसका बर्ताव देखना। भड़कीले फ़ीचर अभी नहीं जोड़ेंगे। पहले "LLM से सोचवाना, टूल बुलाना, नतीजा लौटाना, फिर सोचवाना, और पूरा होने पर रुकना" — यह एजेंट का दिल, अपने हाथों से चला पाना सीखेंगे।
"एक न्यूनतम एजेंट, खुद जोड़कर चला पाना"
न्यूनतम एजेंट क्या है
"न्यूनतम एजेंट" यानी पिछले अध्याय के 4 घटकों को हर एक के सबसे छोटे रूप में जोड़ा हुआ। शानदार मेमोरी, ढेरों टूल, या कई एजेंट — इनमें से कुछ नहीं चाहिए। नीचे दी गई तालिका की तरह, पहले जितना कम किया जा सके उतने कम ढाँचे से शुरू करते हैं। यहाँ समझ आ गया तो आगे बस हर घटक को फैलाते जाना बाकी है।
Claude जैसा एक मॉडल बस एक बार बुलाना। प्रॉम्प्ट में "तुम औज़ार इस्तेमाल कर सकने वाले सहायक हो" — इतनी एक पंक्ति में भूमिका लिख देना।
टूल सिर्फ़ एक। खोज हो या गणना, कोई भी चले। पहले तो एक ऐसा सरल फ़ंक्शन लें जो पक्का चले।
बातचीत का इतिहास रखने वाली एक ऐरे ही काफ़ी है। आदान-प्रदान को क्रम से जमा करते जाना बस। स्थायी सहेजना अभी मत सोचें।
एक while स्टेटमेंट। "टूल का अनुरोध हो तो चलाओ, न हो तो पूरा" पर घूमे। बस अधिकतम कितनी बार, यह तय कर रखें।
💡 "एक बार की API कॉल" से अंतर सिर्फ़ लूप का है। LLM से एक बार पूछकर जवाब ले लेना, तो वह सिर्फ़ सामान्य API उपयोग है। जिस पल उसमें "टूल चलाकर नतीजा लौटाओ, और एक बार फिर सोचवाओ" वाली पुनरावृत्ति जुड़ती है, वह एजेंट बन जाता है। बढ़ता तो असल में यही एक लूप है। इसीलिए, पहले लूप को पूरी तरह समझ लेना ही नज़दीकी रास्ता है।
एजेंट लूप के अंदर क्या है
एजेंट का दिल है एक अकेला लूप। इंसान जैसे "पता लगाओ, सोचो, हाथ चलाओ, नतीजा देखो, और अभी बाकी हो तो दोहराओ" करता है, वही चीज़ प्रोग्राम से घुमाई जाती है। पहले एक चक्कर के बहाव को चित्र से पकड़ें।
अब तक का बातचीत इतिहास और टूल की सूची LLM को देना। LLM तय करता है कि "जवाब देना" है या "टूल इस्तेमाल करना"।
LLM "इस टूल को इन आर्ग्युमेंट के साथ इस्तेमाल करना है" लौटाए, तो हम सचमुच वह फ़ंक्शन चलाते हैं।
चलाने का नतीजा (टूल का रिटर्न वैल्यू) बातचीत इतिहास में जोड़कर, फिर से LLM को देना। यहाँ से STEP 1 पर लौटते हैं।
LLM टूल इस्तेमाल किए बिना अंतिम जवाब लौटा दे, तो लक्ष्य पूरा मानकर लूप से बाहर निकल जाते हैं।
अहम बात यह है कि STEP 1~3 घूमते रहते हैं, और STEP 4 पर ही बाहर निकलते हैं। LLM का काम एक चक्कर में शायद पूरा न हो। "पहले खोजो, उसका नतीजा देखकर फिर से खोजो" की तरह, कई चक्कर लगाकर लक्ष्य के करीब पहुँचता है। स्यूडोकोड में लिखें तो यह हैरान कर देने वाला छोटा होगा।
# न्यूनतम एजेंट लूप (स्यूडोकोड) messages = [ user("पिछले महीने का औसत तापमान पता करके सार बनाओ") ] while True: reply = llm.think(messages, tools=[search_tool]) # STEP1 सोचना if reply.wants_tool: # टूल का अनुरोध है? result = run_tool(reply.tool_name, reply.args) # STEP2 बुलाना messages.append(assistant(reply)) messages.append(tool_result(result)) # STEP3 लौटाना else: return reply.text # STEP4 पूरा होने पर रुकना
⚠️ अधिकतम चक्कर की सीमा ज़रूर डालें। ऊपर का स्यूडोकोड समझाने के लिए while True रखा है, पर असली में हमेशा "ज़्यादा से ज़्यादा N चक्कर तक" का ब्रेक डालते हैं। LLM का लगातार टूल बुलाते रहने वाला अनंत लूप, सचमुच अक्सर होने वाली दुर्घटना है। इस पर विस्तार से अटकने के बिंदु में देखेंगे।
एक टूल देना
अब LLM को एक औज़ार थमाते हैं। यहाँ अहम यह है कि LLM खुद कोड नहीं चला सकता। LLM बस "इस टूल को, इन आर्ग्युमेंट के साथ इस्तेमाल करना है" — इतना शब्दों में अनुरोध कर सकता है। सचमुच फ़ंक्शन चलाकर नतीजा लौटाना, हमेशा हमारा (आपके प्रोग्राम का) काम रहता है।
इसके लिए, पहले टूल डेफ़िनिशन LLM को देते हैं। टूल डेफ़िनिशन यानी "यह टूल क्या कर सकता है, और कैसे आर्ग्युमेंट लेता है" — इसका बयान (मैनुअल)। LLM इसे पढ़कर तय करता है कि कब और कैसे इस्तेमाल करना है।
जैसे: search_weather "जगह और महीना देने पर औसत तापमान लौटाता है"। विवरण ही LLM के निर्णय का आधार बनता है, इसलिए ध्यान से लिखें।
कौन-से आर्ग्युमेंट, किस टाइप में लेने हैं — यह स्कीमा (JSON रूप की डेफ़िनिशन) से बताते हैं। जैसे: city (स्ट्रिंग) और month (नंबर)।
सचमुच चलने वाला कोड। LLM के लौटाए आर्ग्युमेंट लेकर, नतीजा लौटाता है। यह तो बस एक सामान्य फ़ंक्शन ही है।
इस तीन-हिस्सों के सेट में से, ①② को LLM को देना, और ③ को अपने पास रखना — यही तरीका है। LLM "search_weather(city=\"पेरिस\", month=6) इस्तेमाल करना है" कहकर अनुरोध करे, तो उस नाम और आर्ग्युमेंट को हम लेते हैं, ③ वाला फ़ंक्शन बुलाते हैं, और रिटर्न वैल्यू को टूल-नतीजे के रूप में बातचीत में लौटाते हैं। स्यूडोकोड में देखें।
# ① और ② … LLM को दी जाने वाली टूल डेफ़िनिशन search_tool = { "name": "search_weather", "description": "शहर और महीना देने पर औसत तापमान(℃) लौटाता है", "input_schema": { "city": "string", "month": "number", }, } # ③ … अपने पास रखा असली हिस्सा। बस एक फ़ंक्शन def run_tool(name, args): if name == "search_weather": return weather_db.lookup(args["city"], args["month"]) return "unknown tool"
📊 विवरण, प्रॉम्प्ट का ही हिस्सा है। टूल का description ही वह इकलौता सुराग़ है जिससे LLM "कब इस्तेमाल करना है" तय करता है। धुँधला हो तो शायद इस्तेमाल ही न करे, या अजीब मौके पर कर बैठे। "क्या लौटाता है, कब इस्तेमाल करना चाहिए" — किसी इंसान को समझाने के अंदाज़ में लिखना ही सही तरीका है। टूल डिज़ाइन को अगले अध्याय के MCP में और गहराई से देखेंगे।
क्रियान्वयन की झलक — हाथ से लिखा लूप और SDK
यहाँ तक के पुर्ज़ों को जोड़ दें तो पहला एजेंट चलने लगता है। क्रियान्वयन के मोटे तौर पर 2 तरीके हैं। कार्यप्रणाली सीखनी हो तो हाथ से लिखो, असल काम में सहूलियत चाहिए तो SDK — दोनों का भीतरी लूप एक ही है। पहले अंतर समझ लें।
API कॉल और टूल क्रियान्वयन को, अपने while स्टेटमेंट से घुमाना। भीतर क्या हो रहा है यह सब दिखता है, इसलिए सीखने के लिए बेहतरीन। नियंत्रण भी पूरी तरह अपने हाथ में।
कब उपयुक्त: कार्यप्रणाली समझनी हो / बारीकी से नियंत्रण चाहिए।
Claude जैसे SDK जो टूल क्रियान्वयन लूप देते हैं, उसमें टूल डेफ़िनिशन और फ़ंक्शन रजिस्टर करके सौंप देना। रटा-रटाया हिस्सा लिखना नहीं पड़ता, और मज़बूत भी रहता है।
कब उपयुक्त: जल्दी बनाना हो / प्रोडक्शन का तयशुदा काम सौंपना हो।
A. हाथ से लिखा लूप का पूरा खाका, अब तक के पुर्ज़ों को सीधे जोड़ने भर से बनता है। बातचीत इतिहास रखो, LLM को बुलाओ, टूल का अनुरोध हो तो चलाकर नतीजा लौटाओ, न हो तो बाहर निकल जाओ — बस इतना।
# A. हाथ से लिखा लूप (स्यूडोकोड / Claude के मैसेज-अंदाज़ में) messages = [ user(goal) ] for step in range(MAX_STEPS): # ← सीमा से ब्रेक reply = client.messages.create( model="claude-…", messages=messages, tools=[search_tool], ) log(step, reply) # ← निरीक्षण के लिए ज़रूर रिकॉर्ड if reply.stop_reason == "tool_use": out = run_tool(reply.tool_name, reply.tool_args) messages += [ assistant(reply), tool_result(out) ] else: break # अंतिम जवाब आ गया → समाप्त
B. SDK का टूल रनर इस्तेमाल करें तो यह for लूप खुद लाइब्रेरी सँभाल लेती है। आपको बस "टूल डेफ़िनिशन", "असली फ़ंक्शन" और "शुरुआती लक्ष्य" लिखना है। लूप, इतिहास प्रबंधन और टूल-नतीजे को वापस डालना — यह सब भीतर ही निपट जाता है।
# B. SDK के टूल रनर को सौंपना (स्यूडोकोड) runner = ToolRunner( model="claude-…", tools=[search_tool], # डेफ़िनिशन handlers={"search_weather": run_tool}, # असली हिस्सा ) answer = runner.run(goal) # लूप भीतर घूमता है
💡 सटीक मेथड नाम आधिकारिक दस्तावेज़ में देखें। यहाँ के client.messages.create और ToolRunner सिर्फ़ माहौल बताने वाला स्यूडोकोड हैं। असली आर्ग्युमेंट नाम, रिटर्न वैल्यू का रूप और मॉडल ID हर कंपनी के SDK में अलग हैं, और अपडेट भी अक्सर होते हैं, इसलिए आधिकारिक दस्तावेज़ में नवीनतम संस्करण ज़रूर देखें। पहले अवधारणा पकड़ें, बारीकियाँ दस्तावेज़ से भरें — यही बिना घुमाव वाला तरीका है। चरण दर चरण परिचय के लिए AI एजेंट कैसे बनाएँ (परिचय) भी देखें।
सुझाव है "पहले A से एक बार हाथ से लिखो, फिर से आगे B से सहूलियत लो"। एक बार खुद लूप घुमा लेने पर, SDK कोई ब्लैक बॉक्स नहीं रह जाता, और अटकने पर भीतर क्या हो रहा है इसकी कल्पना कर पाते हैं।
चलाकर देखना और निरीक्षण
जुड़ जाने पर चलाते हैं। यहाँ अहम यह है कि बस चलाना नहीं, बल्कि "कहाँ क्या सोचा, और कहाँ नाकाम हुआ" इसका निरीक्षण करना। एजेंट डेवलपमेंट में, इसी निरीक्षण की गुणवत्ता नतीजा तय करती है। इसीलिए शुरू से ही लॉग बिठा देते हैं।
कम से कम, हर चक्कर पर नीचे की चीज़ें रिकॉर्ड करें। हाथ से लिखे लूप में log(step, reply) की एक पंक्ति काफ़ी है।
उस चक्कर में LLM ने "टूल इस्तेमाल करूँ" तय किया या अंतिम जवाब पर आया। निर्णय का मोड़।
टूल का नाम और आर्ग्युमेंट। यहाँ गड़बड़ हो तो नतीजा भी बिगड़ता है। LLM की समझ का फ़र्क़ सबसे ज़्यादा यहीं दिखता है।
टूल का रिटर्न वैल्यू। बहुत बड़ा, खाली, या एरर जैसी अगली सोच को बिगाड़ने वाली चीज़ें यहीं छिपी होती हैं।
सचमुच चलाने पर, दिलचस्प (और झंझट भरे) बर्ताव दिखने लगते हैं। जैसे — LLM ज़रूरत न होते हुए भी टूल बुला लेता है। उल्टा, जहाँ बुलाना चाहिए वहाँ न बुलाकर, बस याददाश्त से ही जवाब दे देता है। आर्ग्युमेंट में शहर का नाम ज़रा-सा गलत कर देता है। एक बार में काम बन जाना चाहिए वहाँ वही खोज बार-बार दोहराता है। ऐसी "निर्णय की आदतें", लॉग देखकर ही पता चलती हैं।
✅ लॉग कोई "बड़ी सुविधा" नहीं, शुरू से ज़रूरी है। एजेंट का भीतर कम दिखता है, इसलिए लॉग न हो तो "ऐसा क्यों हुआ" बिलकुल पता नहीं चलता। अभी print की एक पंक्ति भी काफ़ी है। इस निरीक्षण को व्यवस्थित करने वाले मूल्यांकन और ऑब्ज़र्वेबिलिटी को अध्याय 5 में विस्तार से देखेंगे। पहले "देखने की आदत" यहीं डाल लें।
अटकने के बिंदु
न्यूनतम ढाँचे में भी, चलाने पर कुछ दीवारों से ज़रूर टकराते हैं। ये सब एजेंट डेवलपमेंट की जानी-पहचानी अड़चनें हैं, जिन्हें आगे के अध्यायों में हल करते जाएँगे। यहाँ "असल स्वरूप" और "अभी की गई तात्कालिक मरहम" पकड़ लें, पूरा इलाज आगे के अध्यायों पर छोड़ें।
वही टूल लगातार बुलाता रहता है, और कभी पूरा नहीं होता। लक्ष्य पूरा हुआ यह न पहचान पाकर खाली घूमता रहता है।
तात्कालिक मरहम: अधिकतम चक्कर की सीमा ज़रूर रखें। एक ही आर्ग्युमेंट से लगातार कॉल का पता लगाकर रोकें।
टूल विशाल JSON या पूरे पेज का टेक्स्ट लौटाता है, और उसे ज्यों का त्यों बातचीत में डालकर लागत और देरी दोनों फूल जाती हैं।
तात्कालिक मरहम: टूल की तरफ़ से बस ज़रूरी फ़ील्ड तक सीमित करें / सार बनाकर लौटाएँ। पूरा टेक्स्ट बिना छेड़े मत दें।
जितने चक्कर बढ़ते हैं, बातचीत इतिहास उतना जमा होता जाता है, सीमा के करीब पहुँचकर धीमा, महँगा और अस्थिर हो जाता है।
तात्कालिक मरहम: पुराने आदान-प्रदान का सार बनाकर सिकोड़ें / गैर-ज़रूरी टूल-नतीजे हटा दें। पूरा इलाज अगले अध्यायों में।
📊 तीनों ही "कॉन्टेक्स्ट के डिज़ाइन" की समस्या हैं। अनंत लूप, रिटर्न वैल्यू का फूलना और कॉन्टेक्स्ट फूलना — इन सबकी जड़ "LLM को क्या, और कितना देना है" वाले कॉन्टेक्स्ट डिज़ाइन तक जाकर मिलती है। यह सोच कॉन्टेक्स्ट इंजीनियरिंग के रूप में व्यवस्थित है, और एजेंट डेवलपमेंट की रीढ़ बनती है। पहले "ज़रूरत से ज़्यादा मत दो, ज़रूरत से ज़्यादा मत जमाओ" को मंत्र बना लें।
- न्यूनतम एजेंट = 4 घटकों को सबसे छोटे रूप में जोड़ा हुआ। एक बार के API उपयोग से अंतर सिर्फ़ लूप का।
- लूप "सोचो → टूल बुलाओ → नतीजा लौटाओ → फिर सोचो → पूरा होने पर रुको" वाले एक चक्कर को दोहराता है। अधिकतम चक्कर का ब्रेक ज़रूरी।
- टूल की डेफ़िनिशन (नाम, विवरण, आर्ग्युमेंट) LLM को दी जाती है, और असली फ़ंक्शन अपने पास चलाया जाता है। LLM बस अनुरोध करता है।
- क्रियान्वयन के हाथ से लिखा लूप (सीखने के लिए) और SDK का टूल रनर (असल काम के लिए) — 2 तरीके। भीतर एक ही है।
- शुरू से ही लॉग से निरीक्षण करें, और अनंत लूप, रिटर्न वैल्यू का फूलना, कॉन्टेक्स्ट फूलना जैसी जानी-पहचानी अड़चनें पकड़ें।
एक एजेंट चला लेने पर, अब बारी है औज़ार असल में बढ़ाने की। अगले अध्याय 3 "MCP और टूल कनेक्शन" में, टूल को मानक तरीके (MCP) से बाहरी सेवाओं और डेटा से जोड़कर, एजेंट के हाथ एकदम से फैलाएँगे। पिछले अध्याय का पूरा खाका देखना हो तो अध्याय 1 पर लौटें।