टूल उपयोग पैटर्न: विश्वसनीय एजेंट-टूल इंटरफेस बनाना
आपके एजेंट ने एक टूल को कॉल किया और 40 लाइनों का JSON ब्लॉब मिला — कच्ची API प्रतिक्रिया, नेस्टेड ऑब्जेक्ट, status फ़ील्ड में दबे एरर कोड। मॉडल ने इसे पढ़ा, एक प्रशंसनीय दिखने वाला मान चुना और आगे बढ़ा। मान गलत था। तीन चरणों के बाद, एजेंट ने गलत डेटा के आधार पर आत्मविश्वास से एक रिपोर्ट लिखी।
टूल काम किया। इंटरफेस विफल हो गया।
टूल उपयोग वह तंत्र है जो एक भाषा मॉडल को एजेंट में बदलता है। आपके एजेंट की हर क्षमता — डेटाबेस खोजना, फाइलें लिखना, API कॉल करना, सेवाएं क्वेरी करना — एक टूल इंटरफेस के माध्यम से आती है। यदि इंटरफेस खराब तरीके से डिज़ाइन किया गया है, तो मॉडल खराब निर्णय लेता है, भले ही अंतर्निहित सेवा सही ढंग से काम कर रही हो। यह गाइड सटीक, विश्वसनीय और प्रोडक्शन-तैयार टूल इंटरफेस बनाने के पांच पैटर्न कवर करती है।
पूर्वापेक्षाएं: Python और Claude API से परिचित होना। MCP के बारे में पृष्ठभूमि के लिए, अपना पहला MCP सर्वर बनाना देखें।
इंटरफेस डिज़ाइन क्यों मायने रखता है
जब एजेंट एक टूल चुनता और उपयोग करता है, तो वह दो निर्णय लेता है:
- कौन सा टूल कॉल करना है — टूल के
nameऔरdescriptionद्वारा संचालित - कौन से आर्गुमेंट पास करने हैं — टूल के
input_schemaद्वारा संचालित
अस्पष्ट विवरण गलत टूल चयन की ओर ले जाते हैं। ढीले स्कीमा मॉडल को खराब-स्वरूपित इनपुट पास करने देते हैं। असंरचित परिणाम मॉडल को अनुमान लगाने के लिए मजबूर करते हैं। अधिकांश एजेंट बग तर्क में नहीं — टूल सीमा पर रहते हैं।
पैटर्न 1: स्कीमा-फर्स्ट डिज़ाइन
इम्प्लीमेंटेशन लिखने से पहले JSON स्कीमा लिखें। एक कड़ा स्कीमा इनपुट चरण पर मॉडल के व्यवहार को प्रतिबंधित करता है — इससे पहले कि कुछ भी निष्पादित हो।
import anthropic
client = anthropic.Anthropic()
tools = [ { "name": "search_products", "description": ( "कीवर्ड द्वारा उत्पाद कैटलॉग खोजें। " "ID, नाम और कीमतों के साथ मिलान करने वाले उत्पादों की सूची लौटाता है। " "जब उपयोगकर्ता उत्पाद खोजना या ब्राउज़ करना चाहे तब उपयोग करें।" ), "input_schema": { "type": "object", "properties": { "query": { "type": "string", "description": "खोजने के लिए कीवर्ड" }, "category": { "type": "string", "enum": ["electronics", "clothing", "food", "home", "all"], "description": "फ़िल्टर करने के लिए उत्पाद श्रेणी। अनिर्दिष्ट होने पर 'all' उपयोग करें।" }, "max_results": { "type": "integer", "minimum": 1, "maximum": 20, "description": "वापस करने के लिए परिणामों की संख्या। डिफ़ॉल्ट: 5" } }, "required": ["query", "category"] } }]
response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=[{"role": "user", "content": "₹1000 से कम के इलेक्ट्रॉनिक्स खोजें"}])एरर कम करने वाले स्कीमा नियम:
- वैध मानों के एक निश्चित सेट वाले किसी भी फ़ील्ड के लिए
enumका उपयोग करें - सीमा से बाहर के इनपुट को रोकने के लिए संख्यात्मक फ़ील्ड पर
minimum/maximumसेट करें - फ़ील्ड को
requiredकेवल तब चिह्नित करें जब टूल वास्तव में उनके बिना नहीं चल सकता - मॉडल के दृष्टिकोण से विवरण लिखें: “जब … उपयोग करें”
कब उपयोग करें: हर टूल परिभाषा में।
पैटर्न 2: संरचित टूल परिणाम
टाइप किए गए, मशीन-पठनीय परिणाम वापस करें। कभी भी कच्ची API प्रतिक्रियाएं या गद्य विवरण न लौटाएं।
import jsonfrom dataclasses import dataclass, asdictfrom typing import Any, Optional
@dataclassclass ToolResult: success: bool data: Optional[Any] = None error: Optional[str] = None
def to_content(self) -> str: return json.dumps(asdict(self), ensure_ascii=False)
def search_products( query: str, category: str, max_results: int = 5,) -> ToolResult: try: raw_results = _query_database(query, category, limit=max_results) products = [ {"id": r["product_id"], "name": r["title"], "price": r["price_usd"]} for r in raw_results ] return ToolResult(success=True, data={"products": products, "count": len(products)}) except ConnectionError as e: return ToolResult(success=False, error=f"डेटाबेस अनुपलब्ध: {e}") except Exception as e: return ToolResult(success=False, error=f"खोज विफल: {type(e).__name__}: {e}")
def _query_database(query, category, limit): return []सुसंगत {success, data, error} एनवेलप का अर्थ है कि मॉडल हमेशा जानता है कि कहां देखना है। विफलताओं को सुंदर ढंग से संभालने के लिए, एजेंट एरर रिकवरी पैटर्न देखें।
पैटर्न 3: समानांतर टूल कॉल
Claude एकल प्रतिक्रिया में एकाधिक टूल अनुरोध कर सकता है। उन्हें क्रमिक रूप से नहीं, समानांतर में प्रोसेस करें।
from concurrent.futures import ThreadPoolExecutor, as_completed
TOOL_REGISTRY = { "search_products": search_products,}
def dispatch_tool(name: str, inputs: dict) -> ToolResult: handler = TOOL_REGISTRY.get(name) if not handler: return ToolResult(success=False, error=f"अज्ञात टूल: {name}") return handler(**inputs)
def process_tool_calls(response: anthropic.types.Message) -> list[dict]: tool_uses = [ block for block in response.content if block.type == "tool_use" ] if not tool_uses: return []
def execute(tool_use): result = dispatch_tool(tool_use.name, tool_use.input) return { "type": "tool_result", "tool_use_id": tool_use.id, "content": result.to_content(), }
with ThreadPoolExecutor(max_workers=len(tool_uses)) as executor: futures = {executor.submit(execute, tu): tu for tu in tool_uses} results = [] for future in as_completed(futures): results.append(future.result())
return results
def run_agent(user_message: str) -> str: messages = [{"role": "user", "content": user_message}]
while True: response = client.messages.create( model="claude-sonnet-4-6", max_tokens=4096, tools=tools, messages=messages, )
if response.stop_reason == "end_turn": for block in response.content: if hasattr(block, "text"): return block.text return ""
if response.stop_reason == "tool_use": tool_results = process_tool_calls(response) messages.append({"role": "assistant", "content": response.content}) messages.append({"role": "user", "content": tool_results}) else: break
return ""टूल कॉल करने वाले कई एजेंटों को ऑर्केस्ट्रेट करने के लिए, मल्टी-एजेंट पैटर्न देखें।
पैटर्न 4: सुरक्षित टूल कॉल रैपर
कभी भी टूल अपवादों को एजेंट लूप तक बिना हैंडल किए पहुंचने न दें।
import signal
def timeout_handler(signum, frame): raise TimeoutError("टूल निष्पादन टाइमआउट")
def safe_tool_call( name: str, inputs: dict, timeout_seconds: int = 30,) -> ToolResult: """ टाइमआउट के साथ टूल निष्पादित करें, सभी अपवादों को पकड़ते हुए। हमेशा ToolResult लौटाता है — कभी raise नहीं करता। """ signal.signal(signal.SIGALRM, timeout_handler) signal.alarm(timeout_seconds) try: return dispatch_tool(name, inputs) except TimeoutError: return ToolResult( success=False, error=f"टूल '{name}' {timeout_seconds}s के बाद टाइमआउट" ) except Exception as e: return ToolResult( success=False, error=f"टूल '{name}' ने {type(e).__name__} उठाया: {e}" ) finally: signal.alarm(0)जब कोई टूल success: false लौटाता है, तो मॉडल तय कर सकता है कि पुनः प्रयास करना है, कोई विकल्प आजमाना है, या विफलता रिपोर्ट करनी है। व्यापक रिट्राई रणनीतियों के लिए, एरर रिकवरी पैटर्न देखें।
पैटर्न 5: परिणाम सत्यापन और ट्रंकेशन
टूल परिणामों को मॉडल को लौटाने से पहले सत्यापित करें।
MAX_TOOL_RESULT_CHARS = 8000
def validate_result(result: ToolResult, expected_keys: list[str]) -> ToolResult: if not result.success or not isinstance(result.data, dict): return result missing = [k for k in expected_keys if k not in result.data] if missing: return ToolResult( success=False, error=f"टूल प्रतिक्रिया में अपेक्षित फ़ील्ड गायब: {missing}" ) return result
def truncate_result(result: ToolResult) -> ToolResult: content = result.to_content() if len(content) <= MAX_TOOL_RESULT_CHARS: return result
truncated_data = { "truncated": True, "chars_omitted": len(content) - MAX_TOOL_RESULT_CHARS, "content": content[:MAX_TOOL_RESULT_CHARS], } return ToolResult( success=result.success, data=truncated_data, error="परिणाम ट्रंकेट किया गया — संदर्भ विंडो के लिए बहुत बड़ा", )
def safe_tool_call_validated( name: str, inputs: dict, expected_keys: list[str] | None = None,) -> ToolResult: result = safe_tool_call(name, inputs) if expected_keys: result = validate_result(result, expected_keys) result = truncate_result(result) return resultप्रोडक्शन में इन विफलताओं को देखने और डीबग करने के लिए, डीबगिंग और अवलोकनीयता देखें।
सामान्य गलतियां
गलती 1: कच्ची API प्रतिक्रियाएं लौटाना
मॉडल को 30 फ़ील्ड वाला नेस्टेड ऑब्जेक्ट मिलता है, अधिकांश अप्रासंगिक। वह गलत चुनता है।
समाधान: लौटाने से पहले प्रतिक्रिया आकार दें। केवल वही लौटाएं जो मॉडल को अपना अगला निर्णय लेने की जरूरत है।
गलती 2: पुष्टि के बिना साइड इफेक्ट वाले टूल
एक टूल जो ईमेल भेजता है, रिकॉर्ड हटाता है, या शुल्क लेता है, उसे चुपचाप निष्पादित नहीं होना चाहिए।
समाधान: अपरिवर्तनीय कार्यों के लिए, दो-टूल पैटर्न का उपयोग करें: plan_email प्रीव्यू लौटाता है, send_email वास्तव में भेजता है।
गलती 3: ओवरलैपिंग टूल जिम्मेदारियां
समान काम करने वाले दो टूल मॉडल को अनुमान लगाने के लिए मजबूर करते हैं।
समाधान: प्रत्येक टूल का एक विशिष्ट, गैर-ओवरलैपिंग उद्देश्य होना चाहिए।
गलती 4: बाहरी टूल पर कोई टाइमआउट नहीं
धीमा तृतीय-पक्ष API कॉल पूरे एजेंट लूप को अनिश्चित काल के लिए ब्लॉक करता है।
समाधान: हमेशा टाइमआउट सेट करें (पैटर्न 4)।
प्रोडक्शन चेकलिस्ट
- प्रत्येक टूल का मॉडल के दृष्टिकोण से विवरण है (“जब … उपयोग करें”)
- सभी फिक्स्ड-वैल्यू इनपुट के लिए enum फ़ील्ड
- प्रत्येक टूल
{success, data, error}लौटाता है — कभी कच्ची प्रतिक्रियाएं नहीं - टूल कॉल
safe_tool_callमें रैप किए गए - मल्टी-टूल प्रतिक्रियाओं के लिए समानांतर निष्पादन
- प्रत्येक बाहरी टूल पर टाइमआउट सेट
- परिवर्तनीय आकार के पेलोड के लिए परिणाम ट्रंकेशन
- बाहरी API कॉल करने वाले टूल के लिए सत्यापन
अगले चरण
- स्कीमा से शुरू करें — फ़ंक्शन बॉडी से पहले अपना
input_schemaलिखें - हर मौजूदा टूल में
ToolResultरैपर जोड़ें safe_tool_callअपनाएं अपने एजेंट लूप को मजबूत करने के लिए- बाहरी API कॉल करने वाले टूल के लिए परिणाम सत्यापन सेट करें
संबंधित गाइड:
- अपना पहला MCP सर्वर बनाना
- मल्टी-एजेंट पैटर्न
- एजेंट मेमोरी सिस्टम
- एजेंट एरर रिकवरी पैटर्न
- डीबगिंग और अवलोकनीयता