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

Özerk Ajan Sistemlerinde Hata Ayıklama ve Gözlemlenebilirlik


Sessizce başarısız olan özerk bir ajan, hiç ajan olmamasından daha kötüdür. Geleneksel bir fonksiyon istisna fırlattığında yığın izini alırsınız. Bir ajan yirmi araç çağrısı ve üç model çağrısı boyunca yanlış yolu izlediğinde yanlış bir cevap alırsınız — ve belirgin bir açıklama yoktur.

Ajanların hata ayıklaması farklı bir zihinsel model gerektirir. Sistem deterministik bir yol yürütmüyor; bir dizi karar veriyor. Gözlemlenebilirlik bu kararları yakalamak anlamına gelir — yalnızca girdi ve çıktıları değil, onları birbirine bağlayan akıl yürütmeyi de.

Geleneksel Hata Ayıklama Neden Ajanlar İçin Başarısız Olur

Standart loglama ne olduğunu yakalar. Ajan gözlemlenebilirliği neden olduğunu yakalamayı gerektirir — modelin ne sonuca vardığı, hangi aracı neden seçtiği ve hangi ara durumdan çalıştığı.

Hata modları da farklıdır:

  • Sessiz halüsinasyon: Ajan belirsizliği sinyal vermeden güvenle yanlış cevap üretir.
  • Karar kayması: Her adım yerel olarak makul görünür, ancak dizi hedeften uzaklaşır.
  • Araç kötüye kullanımı: Ajan doğru aracı ince biçimde yanlış parametrelerle çağırır.
  • Sonsuz döngüler: Ajan başarısız bir yaklaşımı yeniden denemekte sıkışıp kalır.
  • Bağlam zehirlenmesi: Erken bir adımdan gelen kötü çıktı sonraki tüm akıl yürütmeyi bozar.

Bunların hiçbiri istisna üretmez. Yanlış davranış üretirler; bu davranış yalnızca tam yürütme izini yeniden oluşturduğunuzda görünür olur.

Ajan Kararları için Yapılandırılmış Loglama

İlk adım her ajan etkileşimini yapılandırılmış loglar içinde sarmaktır. Ham API yanıtlarını loglamayın — semantik olayları loglayın.

import json
import time
import uuid
from dataclasses import dataclass, asdict
from typing import Any
import anthropic
client = anthropic.Anthropic()
@dataclass
class AgentEvent:
trace_id: str
step: int
event_type: str # "llm_call", "tool_call", "tool_result", "decision", "error"
model: str | None
input_tokens: int | None
output_tokens: int | None
latency_ms: float | None
content: dict[str, Any]
timestamp: float
def log_event(event: AgentEvent):
print(json.dumps(asdict(event))) # Log hedefinizle değiştirin
class TracedAgent:
def __init__(self, trace_id: str | None = None):
self.trace_id = trace_id or str(uuid.uuid4())
self.step = 0
self.tools = []
def add_tool(self, name: str, description: str, input_schema: dict):
self.tools.append({
"name": name,
"description": description,
"input_schema": input_schema
})
def call(self, messages: list[dict], system: str = "") -> str:
self.step += 1
start = time.monotonic()
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=4096,
system=system,
tools=self.tools,
messages=messages
)
latency_ms = (time.monotonic() - start) * 1000
log_event(AgentEvent(
trace_id=self.trace_id,
step=self.step,
event_type="llm_call",
model="claude-opus-4-6",
input_tokens=response.usage.input_tokens,
output_tokens=response.usage.output_tokens,
latency_ms=latency_ms,
content={
"stop_reason": response.stop_reason,
"text_blocks": [b.text for b in response.content if b.type == "text"],
"tool_calls": [
{"name": b.name, "input": b.input}
for b in response.content if b.type == "tool_use"
]
},
timestamp=time.time()
))
return response

Tam İz Oluşturma

Tek bir log satırı yeterli değil — her kararı sonucuyla bağlayan tam yürütme izine ihtiyacınız var:

from typing import Callable
def run_traced_agent(
task: str,
tools: dict[str, Callable],
tool_schemas: list[dict],
system: str,
max_steps: int = 20,
) -> dict:
agent = TracedAgent()
for schema in tool_schemas:
agent.add_tool(**schema)
messages = [{"role": "user", "content": task}]
trace = {"trace_id": agent.trace_id, "task": task, "steps": []}
step_count = 0
while step_count < max_steps:
step_count += 1
response = agent.call(messages, system=system)
step_record = {
"step": step_count,
"stop_reason": response.stop_reason,
"model_output": [],
"tool_results": []
}
if response.stop_reason == "end_turn":
for block in response.content:
if block.type == "text":
step_record["model_output"].append(block.text)
trace["steps"].append(step_record)
trace["final_answer"] = step_record["model_output"][-1] if step_record["model_output"] else ""
break
tool_results = []
for block in response.content:
if block.type == "tool_use":
step_record["model_output"].append({
"tool": block.name,
"input": block.input
})
tool_fn = tools.get(block.name)
if not tool_fn:
result = f"Hata: bilinmeyen araç '{block.name}'"
else:
try:
result = tool_fn(**block.input)
except Exception as e:
result = f"Araç hatası: {e}"
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result)
})
step_record["tool_results"].append({
"tool": block.name,
"result_preview": str(result)[:200]
})
trace["steps"].append(step_record)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
trace["error"] = f"maksimum adım sayısı aşıldı ({max_steps})"
return trace

Döngü Tespiti

Sonsuz döngüler yaygın bir hata modudur. Her LLM çağrısının araç çağırma deseninin parmak izini alarak tespit edin:

def detect_loop(trace: dict, window: int = 4) -> bool:
steps = trace["steps"]
if len(steps) < window:
return False
def step_signature(step: dict) -> str:
tools_called = sorted(
t["tool"] if isinstance(t, dict) else t
for t in step.get("model_output", [])
if isinstance(t, dict) and "tool" in t
)
return "|".join(tools_called)
recent = [step_signature(s) for s in steps[-window:]]
if len(set(recent)) == 1 and recent[0]:
return True
if len(steps) >= 4:
pattern = [step_signature(s) for s in steps[-4:]]
if pattern[0] == pattern[2] and pattern[1] == pattern[3]:
return True
return False

Üretimde Takip Edilecek Metrikler

from collections import Counter
def compute_trace_metrics(trace: dict) -> dict:
steps = trace["steps"]
errors = [s for s in steps if "error" in s]
tool_calls_by_name: Counter = Counter()
for step in steps:
for output in step.get("model_output", []):
if isinstance(output, dict) and "tool" in output:
tool_calls_by_name[output["tool"]] += 1
return {
"trace_id": trace["trace_id"],
"total_steps": len(steps),
"error_steps": len(errors),
"tool_call_distribution": dict(tool_calls_by_name),
"completed": "final_answer" in trace,
"loop_detected": detect_loop(trace),
}

Uyarı için temel sinyaller:

  • Döngü oranı > %5 — ajan sıkışıyor
  • Araç başına hata oranı > eşik — bir araç bozuk
  • Ortalama adım sayısı artış eğilimi — görevler zorlaşıyor
  • p99 gecikme ani artışı — model uç noktası yavaş

OpenTelemetry Entegrasyonu

Zaten OpenTelemetry kullanan ekipler için ajan izlerini span olarak yayın:

from opentelemetry import trace as otel_trace
tracer = otel_trace.get_tracer("agent")
def run_with_otel(task: str, tools: dict, tool_schemas: list, system: str):
with tracer.start_as_current_span("agent.run") as root_span:
root_span.set_attribute("agent.task", task[:200])
agent = TracedAgent()
for schema in tool_schemas:
agent.add_tool(**schema)
messages = [{"role": "user", "content": task}]
for step in range(20):
with tracer.start_as_current_span(f"agent.step.{step}") as step_span:
response = agent.call(messages, system=system)
step_span.set_attribute("llm.stop_reason", response.stop_reason)
step_span.set_attribute("llm.input_tokens", response.usage.input_tokens)
if response.stop_reason == "end_turn":
break

Loglarda KKB Redaksiyonu

Ajan logları genellikle hassas veri içerir. Herhangi bir harici sisteme göndermeden önce redaksiyon uygulayın:

import re
PII_PATTERNS = [
(re.compile(r'\b[\w.+-]+@[\w-]+\.[a-z]{2,}\b'), '[EPOSTA]'),
(re.compile(r'\b0?\d{3}[-\s]?\d{3}[-\s]?\d{2}[-\s]?\d{2}\b'), '[TELEFON]'),
(re.compile(r'\bsk-[a-zA-Z0-9]{20,}\b'), '[API_ANAHTARI]'),
]
def redact(text: str) -> str:
for pattern, replacement in PII_PATTERNS:
text = pattern.sub(replacement, text)
return text

En Önemli Üç Metrik

Görev tamamlama oranı — çalışmaların ne kadarı final_answer’a ulaşıyor, ne kadarı max_steps veya hatayla sonuçlanıyor. Görev türüne göre temel çizgi belirleyin.

Görev başına token maliyeti — tüm adımlardaki input_tokens + output_tokens toplamı. Tamamlama oranı değişmeden %20 maliyet artışı genellikle prompt bozulmasını gösterir.

Araç hata oranıerror_steps / total_steps. Bu metrikte ani artışlar doğrudan bozuk bir araca veya API’ye işaret eder.


Ajan sistemlerinde gözlemlenebilirlik isteğe bağlı değildir — iterasyon yapabileceğiniz bir sistem ile bozulduğunda yalnızca yeniden başlatabileceğiniz bir sistem arasındaki farktır. Yapılandırılmış olaylar ve iz kimlikleriyle başlayın. Döngü tespiti ekleyin. Metrikleri yayımlayın. Bu yatırımın karşılığını, üretimde ilk arıza yaşadığınızda ve ne olduğunu tahmin etmek yerine tam olarak yeniden oluşturabildiğinizde alırsınız.


İlgili Makaleler