Araç Kullanım Desenleri: Güvenilir Ajan-Araç Arayüzleri Oluşturma
Ajanın bir araç çağırdı ve 40 satırlık JSON blob aldı — ham API yanıtı, iç içe nesneler, status alanında gömülü hata kodları. Model onu okudu, makul görünen bir değer seçti ve devam etti. Değer yanlıştı. Üç adım sonra ajan, hatalı verilere dayanarak güvenle bir rapor yazdı.
Araç çalıştı. Arayüz başarısız oldu.
Araç kullanımı, bir dil modelini ajana dönüştüren mekanizmadır. Ajanının her yeteneği — veritabanı arama, dosya yazma, API çağırma, servis sorgulama — bir araç arayüzü üzerinden gelir. Arayüz kötü tasarlanmışsa, alttaki servis doğru çalışsa bile model daha kötü kararlar alır. Bu kılavuz, hassas, güvenilir ve üretime hazır araç arayüzleri oluşturmak için beş desen kapsar.
Ön koşullar: Python ve Claude API’ye hakim olmak. MCP’yi araç taşıma katmanı olarak kullanmak için İlk MCP Sunucunu Oluşturma bölümüne bak.
Arayüz Tasarımı Neden Önemlidir
Bir ajan bir araç seçip kullandığında iki karar alır:
- Hangi aracı çağıracağı — aracın
namevedescription’ı tarafından yönlendirilir - Hangi argümanları geçeceği — aracın
input_schema’sı tarafından yönlendirilir
Belirsiz açıklamalar yanlış araç seçimine yol açar. Gevşek şemalar, modelin hatalı biçimli girişler iletmesine izin verir. Yapılandırılmamış sonuçlar, modelin ne olduğunu tahmin etmesine neden olur. Ajan hatalarının çoğu akıl yürütmede değil — araç sınırında yaşar.
Desen 1: Şema Öncelikli Tasarım
Uygulamayı yazmadan önce JSON şemasını yaz. Sıkı bir şema, herhangi bir şey yürütülmeden önce giriş aşamasında model davranışını kısıtlar.
import anthropic
client = anthropic.Anthropic()
tools = [ { "name": "search_products", "description": ( "Ürün kataloğunu anahtar kelimeyle arar. " "Kimlik, ad ve fiyatlarla eşleşen ürünlerin listesini döndürür. " "Kullanıcı ürün bulmak veya göz atmak istediğinde kullan." ), "input_schema": { "type": "object", "properties": { "query": { "type": "string", "description": "Aranacak anahtar kelimeler" }, "category": { "type": "string", "enum": ["electronics", "clothing", "food", "home", "all"], "description": "Filtrelenecek ürün kategorisi. Belirtilmemişse 'all' kullan." }, "max_results": { "type": "integer", "minimum": 1, "maximum": 20, "description": "Döndürülecek sonuç sayısı. Varsayılan: 5" } }, "required": ["query", "category"] } }]
response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=[{"role": "user", "content": "1000 TL altında elektronik bul"}])Hataları azaltan şema kuralları:
- Sabit geçerli değer kümesi olan herhangi bir alan için
enumkullan - Aralık dışı girişleri önlemek için sayısal alanlara
minimum/maximumkoy - Alanları
requiredolarak işaretle ancak araç gerçekten onlar olmadan çalışamıyorsa - Açıklamaları modelin bakış açısından yaz: “Kullan…” modele ne zaman aracı çağıracağını söyler
Ne zaman kullanılır: Her araç tanımında.
Desen 2: Yapılandırılmış Araç Sonuçları
Yazımlı, makine tarafından okunabilir sonuçlar döndür. Asla ham API yanıtları döndürme.
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))
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"Veritabanı kullanılamıyor: {e}") except Exception as e: return ToolResult(success=False, error=f"Arama başarısız: {type(e).__name__}: {e}")
def _query_database(query, category, limit): return []Tutarlı {success, data, error} zarfı, modelin her zaman nereye bakacağını bilmesi anlamına gelir. Hataları zarif şekilde ele almak için Ajan Hata Kurtarma Desenleri bölümüne bak.
Desen 3: Paralel Araç Çağrıları
Claude tek bir yanıtta birden fazla araç talep edebilir. Bunları sıralı yerine paralel işle.
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"Bilinmeyen araç: {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 ""Her biri araç çağıran birden fazla ajanı yönetmek için Çok Ajan Desenleri bölümüne bak.
Desen 4: Güvenli Araç Çağrısı Sarmalayıcısı
Araç istisnalarının ajan döngüsüne işlenmeden ulaşmasına asla izin verme.
import signal
def timeout_handler(signum, frame): raise TimeoutError("Araç yürütme zaman aşımına uğradı")
def safe_tool_call( name: str, inputs: dict, timeout_seconds: int = 30,) -> ToolResult: """ Zaman aşımıyla araç çalıştır, tüm istisnaları yakala. Her zaman ToolResult döndürür — asla raise yapmaz. """ 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}' aracı {timeout_seconds}s sonra zaman aşımına uğradı" ) except Exception as e: return ToolResult( success=False, error=f"'{name}' aracı {type(e).__name__} fırlattı: {e}" ) finally: signal.alarm(0)Bir araç success: false döndürdüğünde, model yeniden denemeye, bir alternatif denemeye veya başarısızlığı bildirmeye karar verebilir. Daha geniş yeniden deneme stratejileri için Hata Kurtarma Desenleri bölümüne bak.
Desen 5: Sonuç Doğrulama ve Kısaltma
Araç sonuçlarını modele döndürmeden önce doğrula.
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"Araç yanıtında beklenen alanlar eksik: {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="Sonuç kısaltıldı — bağlam penceresi için çok büyük", )
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Üretimdeki bu hataları gözlemlemek ve hata ayıklamak için Hata Ayıklama ve Gözlemlenebilirlik bölümüne bak.
Yaygın Hatalar
Hata 1: Ham API Yanıtları Döndürmek
Model 30 alanlı iç içe bir nesne alır, çoğu alakasız. Yanlış olanı seçer.
Çözüm: Döndürmeden önce yanıtı şekillendir. Yalnızca modelin bir sonraki kararı için ihtiyaç duyduğunu döndür.
Hata 2: Onay Olmaksızın Yan Etkili Araçlar
E-posta gönderen, kayıt silen veya ücret alan bir araç sessizce çalışmamalıdır.
Çözüm: Geri alınamaz işlemler için iki araç deseni kullan: plan_email önizleme döndürür, send_email gerçekten gönderir.
Hata 3: Örtüşen Araç Sorumlulukları
Benzer şeyler yapan iki araç, modeli hangisini kullanacağını tahmin etmeye zorlar.
Çözüm: Her aracın benzersiz, örtüşmeyen bir amacı olmalıdır.
Hata 4: Harici Araçlarda Zaman Aşımı Olmaması
Yavaş bir üçüncü taraf API çağrısı, tüm ajan döngüsünü süresiz olarak engeller.
Çözüm: Her zaman zaman aşımı ayarla (Desen 4).
Üretim Kontrol Listesi
- Her araç modelin bakış açısından açıklamaya sahip (“Kullan…”)
- Tüm sabit değerli girişler için enum alanları
- Her araç
{success, data, error}döndürür — asla ham yanıtlar değil - Araç çağrıları
safe_tool_calliçine sarılmış - Çok araçlı yanıtlar için paralel yürütme
- Her harici araçta zaman aşımı ayarlanmış
- Değişken boyutlu yükler için sonuç kısaltma
- Harici API çağıran araçlar için doğrulama
Sonraki Adımlar
- Şemayla başla — fonksiyon gövdesinden önce
input_schemayaz ToolResultsarmalayıcısını her mevcut araca eklesafe_tool_callentegre et ajan döngüsünü sağlamlaştırmak için- Kontrol etmediğin API’leri çağıran araçlar için sonuç doğrulaması ayarla
İlgili kılavuzlar:
- İlk MCP Sunucunu Oluşturma
- Çok Ajan Desenleri
- Ajan Bellek Sistemleri
- Ajan Hata Kurtarma Desenleri
- Hata Ayıklama ve Gözlemlenebilirlik