BİLGİSAYAR ERİŞİM SİSTEMİ

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:

  1. Hangi aracı çağıracağı — aracın name ve description’ı tarafından yönlendirilir
  2. 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 enum kullan
  • Aralık dışı girişleri önlemek için sayısal alanlara minimum/maximum koy
  • Alanları required olarak 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 json
from dataclasses import dataclass, asdict
from typing import Any, Optional
@dataclass
class 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_call iç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

  1. Şemayla başla — fonksiyon gövdesinden önce input_schema yaz
  2. ToolResult sarmalayıcısını her mevcut araca ekle
  3. safe_tool_call entegre et ajan döngüsünü sağlamlaştırmak için
  4. Kontrol etmediğin API’leri çağıran araçlar için sonuç doğrulaması ayarla

İlgili kılavuzlar: