🤖Ajan Tasarım Prensipleri
Komtaş CoPilot'ta ajan geliştirirken bu bölümdeki prensipler ve desenler temel referans kaynağıdır. Bir ajan, LLM'nin araçları (tool) kullanarak çok adımlı görevleri tamamlayabildiği özerk bir sistemdir. Geri alınamaz aksiyonlarda HITL deseni zorunludur.
2.1 Ajan Tasarım Desenleri
Ne zaman kullanılır: Çevresel geri bildirimin önemli olduğu, adım adım karar verilen görevler. Web araması, veritabanı sorgusu, API çağrısı içeren senaryolar.
Nasıl çalışır: Model Thought → Action → Observation döngüsünü tekrarlar. Her gözlem bir sonraki düşünceyi besler.
from anthropic import Anthropic
client = Anthropic()
tools = [
{
"name": "search_database",
"description": "Şirket veritabanında bilgi arar",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Arama sorgusu"},
"table": {"type": "string", "enum": ["contracts", "customers", "products"]}
},
"required": ["query", "table"]
}
}
]
def react_agent(user_query: str, max_iterations: int = 10):
messages = [{"role": "user", "content": user_query}]
for i in range(max_iterations):
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=4096,
tools=tools,
messages=messages
)
# Thought: model'in iç akıl yürütmesi
# Action: tool_use bloğu
if response.stop_reason == "tool_use":
tool_result = execute_tool(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_result})
else:
# Final answer
return extract_text(response.content)
return "Maksimum iterasyon aşıldı"
- Tool (Araç) Tanımı: Modelin çağırabileceği dış fonksiyonu tanımlayan JSON şemasıdır.
input_schemamodele hangi parametreleri nasıl göndereceğini öğretir; net ve kısa tanımlar daha doğru araç seçimi sağlar. - ReAct döngüsü: Reason (Düşün) → Act (Araç çağır) → Observe (Sonucu gözlemle) döngüsü; ajan bir görevi tamamlayana kadar bu adımları tekrarlar.
- stop_reason:
stop_reason == 'tool_use'modelin bir araç çağrısı istediği anlamına gelir. Kod bu durumu yakalamalı, aracı çalıştırmalı ve sonucutool_resultmesajı olarak geri göndermelidir. - Güvenlik notu: Araç fonksiyonları LLM'nin doğrudan çalıştırdığı koddur. Parametre doğrulama (Pydantic), izin kontrolü ve timeout eklenmesi zorunludur.
Ne zaman kullanılır: Uzun vadeli, çok adımlı görevler. Paralel yürütme imkânı olan işler. Görevin başında tam planın görünmesi istenen durumlar.
Nasıl çalışır: İlk LLM çağrısı bir plan üretir (step listesi). Sonraki çağrılar her adımı sırayla veya paralel olarak çalıştırır.
from google.adk.agents import SequentialAgent, LlmAgent
from google.adk.tools import FunctionTool
# Planlayıcı ajan
planner = LlmAgent(
name="Planner",
model="gemini-2.0-flash",
instruction="""Kullanıcı görevini analiz et ve adım adım çalıştırılabilir bir plan oluştur.
Her adım için: action, input, expected_output belirt.
JSON formatında çıktı ver.""",
output_key="execution_plan"
)
# Uygulayıcı ajan
executor = LlmAgent(
name="Executor",
model="gemini-2.0-flash",
instruction="""Verilen planı adım adım uygula.
Her adımı tamamladıktan sonra sonucu kaydet.""",
tools=[search_tool, write_tool, calculate_tool]
)
# Sequential pipeline
pipeline = SequentialAgent(
name="PlanExecutePipeline",
sub_agents=[planner, executor]
)
- SequentialAgent: Görevleri sıralı olarak çalıştırır; her adımın çıktısı bir sonraki adımın bağlamına eklenir. Bağımlı adımlar (özetle → analiz et → raporla) için idealdir.
- LlmAgent: ADK'da temel LLM çağrısını gerçekleştiren birimdir.
instructionparametresi sistem promptu gibi davranır. - output_key: Her ajanın çıktısını paylaşılan sözlüğe (session state) kaydeder. Sonraki ajanlar bu anahtara
{key}sözdizimi ile erişir. - Model seçimi: ADK, farklı alt görevler için farklı modeller kullanılmasını destekler: hız gerektiren adımlarda Flash, kritik adımlarda Pro tercih edilebilir.
Ne zaman kullanılır: Token maliyeti kritik olduğunda. Araç çağrılarının bağımsız olduğu (birbirinin sonucuna ihtiyaç duymayan) durumlarda. Hız öncelikliyse.
Nasıl çalışır: Model, araçlara erişmeden önce TÜM araç çağrılarını planlar. Çağrılar paralel yürütülür. Sonuçlar bir kez modele gönderilir.
# ReWOO: Paralel tool planlama ve yürütme
import asyncio
from anthropic import AsyncAnthropic
async def rewoo_agent(task: str):
client = AsyncAnthropic()
# Adım 1: Tüm tool çağrılarını planla
plan_response = await client.messages.create(
model="claude-opus-4-6",
max_tokens=2048,
system="""Sen bir planlama ajanısın. Görevi analiz et ve
tüm gerekli tool çağrılarını LİSTELE. Çağrıları ŞİMDİ YAPMA.
Her çağrı için: tool_name, params, reason belirt.""",
messages=[{"role": "user", "content": task}]
)
# Adım 2: Tüm çağrıları paralel yürüt
tool_calls = parse_plan(plan_response)
results = await asyncio.gather(*[
execute_tool_async(tc) for tc in tool_calls
])
# Adım 3: Tek seferlik sentez
final = await client.messages.create(
model="claude-opus-4-6",
max_tokens=4096,
messages=[
{"role": "user", "content": task},
{"role": "assistant", "content": f"Araç sonuçları: {results}"},
{"role": "user", "content": "Sonuçları sentezleyerek cevapla."}
]
)
return final
- ReWOO Deseni: Planlama (Reason) ve Yürütme (Act) fazlarını ayırır. Ajan önce tüm araç çağrılarını planlar, ardından hepsini paralel çalıştırır. Bağımsız araçlar için ReAct'a göre çok daha hızlıdır.
- asyncio.gather(): Python'da eş zamanlı asenkron görev çalıştırmanın standart yöntemidir. I/O yoğun (API çağrıları) görevlerde toplam süreyi N kat kısaltır; CPU yoğun görevlerde etkisi sınırlıdır.
- Plan formatı: Planı yapılandırılmış (JSON/YAML) formatta üretmek, ayrıştırmayı güvenilir kılar. Serbest metin planlar parse hatalarına neden olabilir.
Token Tasarrufu: ReAct'e kıyasla ortalama %40-60 token tasarrufu sağlar çünkü her observation için model yeniden çalışmaz.
Ne zaman kullanılır: Yüksek kalite çıktı gerektiğinde (kod, yazı, analiz). Hataların maliyetli olduğu kritik kararlar. Doğrulama döngüsü istenen durumlar.
from anthropic import Anthropic
def reflection_agent(task: str, max_rounds: int = 3):
client = Anthropic()
# İlk üretim
draft = generate_draft(client, task)
for round in range(max_rounds):
# Eleştiri üret
critique = client.messages.create(
model="claude-opus-4-6",
max_tokens=2048,
system="""Sen kalite kontrol uzmanısın. Verilen çıktıyı şu kriterlerle değerlendir:
1. Doğruluk ve tutarlılık
2. Eksik bilgiler
3. Mantıksal hatalar
4. İyileştirme önerileri
Kısa ve eyleme geçirilebilir geri bildirim ver.""",
messages=[
{"role": "user", "content": f"Görev: {task}\n\nÇıktı:\n{draft}"}
]
)
critique_text = extract_text(critique.content)
# Eleştiri olumluya dönüştüyse dur
if "yeterli" in critique_text.lower() or "geliştirme gerekmez" in critique_text.lower():
break
# Revize et
draft = revise_with_critique(client, task, draft, critique_text)
return draft
- Reflection Deseni: Modelin kendi çıktısını eleştirmesi ve iyileştirmesidir. Tek bir büyük model yerine daha küçük modeli çoklu turda çalıştırmak genellikle daha iyi maliyet/kalite dengesi sağlar.
- max_rounds: Sonsuz döngüyü önler. Pratikte 2–3 tur çoğu görev için yeterlidir; daha fazlası marjinal iyileştirme sağlarken maliyeti artırır.
- Critique prompt: Eleştiri promptu ne kadar spesifik olursa iyileştirme o kadar odaklı olur. 'Daha iyi yap' yerine 'KVKK uyumluluğunu kontrol et ve eksikleri listele' şeklinde yazın.
- Convergence testi: Ardışık iki yanıt arasındaki benzerliği ölçün (cosine similarity). Yüksek benzerlik ise model platoya ulaşmıştır, döngüyü erken kesin.
Ne zaman kullanılır: Optimizasyon problemleri, karmaşık bulmacalar, en iyi çözümü bulmak için birden fazla yolun keşfedilmesi gerektiğinde.
Nasıl çalışır: Ağaç arama (BFS/DFS) stratejisi ile paralel düşünce dalları oluşturulur, her dal değerlendirilir, en umut verici dal takip edilir.
# ToT - Beam Search yaklaşımı
def tree_of_thoughts(problem: str, beam_width: int = 3, depth: int = 3):
# Kök node
thoughts = [{"thought": problem, "score": 1.0, "history": []}]
for level in range(depth):
candidates = []
for node in thoughts:
# Her node'dan beam_width kadar düşünce üret
new_thoughts = generate_thoughts(node["thought"], k=beam_width)
for thought in new_thoughts:
# Her düşünceyi değerlendir (0-1 arası skor)
score = evaluate_thought(problem, node["history"] + [thought])
candidates.append({
"thought": thought,
"score": score,
"history": node["history"] + [thought]
})
# En iyi beam_width kadar düşünceyi seç
thoughts = sorted(candidates, key=lambda x: x["score"], reverse=True)[:beam_width]
# En yüksek skorlu yol
best = max(thoughts, key=lambda x: x["score"])
return synthesize_solution(problem, best["history"])
- Tree of Thoughts (ToT): Tek bir düşünce zinciri yerine birden fazla çözüm dalını paralel keşfeder ve en iyisini seçer. Beam search varyantı en umut verici
beam_widthkadar dalı her adımda korur. - Maliyet uyarısı: ToT, token maliyetini
beam_width × depthfaktörüyle artırır. 3 genişlik, 4 derinlik = 12 ek LLM çağrısı. Yalnızca gerçekten kritik optimizasyon problemlerinde kullanın. - Alternatif: Çoğu iş senaryosunda ReAct veya Plan-and-Execute ToT'dan daha pratiktir. ToT'u tercih edin: çok adımlı matematik, strateji geliştirme, kaynak tahsisi optimizasyonu.
Dikkat: ToT token maliyeti yüksektir. Sadece gerçekten kritik optimizasyon problemlerinde kullanın. Çoğu iş senaryosunda ReAct veya Plan-and-Execute yeterlidir.
Ne zaman kullanılır: Ajanın aksiyon (e-posta gönderme, kayıt değiştirme, ödeme onaylama, müşteriye yanıt) yapacağı her durumda. Yanlış aksiyonun maliyetli olduğu kritik kararlar.
Nasıl çalışır: Ajan tool'u ÇAĞIRMAZ — bunun yerine "onay isteği" sıraya girer. İnsan Slack/Teams/UI üzerinden onaylar veya düzenler. Onaydan sonra tool çalışır.
# HITL — Slack Block Kit ile interaktif onay
import asyncio
from slack_sdk.web.async_client import AsyncWebClient
slack = AsyncWebClient(token=os.environ["SLACK_BOT_TOKEN"])
pending_approvals = {} # request_id -> Future
async def request_human_approval(action: str, payload: dict,
approver_user_id: str,
timeout_sec: int = 3600) -> dict:
"""Aksiyon için Slack üzerinden onay iste, cevabı bekle."""
request_id = str(uuid.uuid4())
fut = asyncio.get_event_loop().create_future()
pending_approvals[request_id] = fut
blocks = [
{"type": "section", "text": {"type": "mrkdwn",
"text": f"*Onay isteği*\n```{action}```\n```{json.dumps(payload, ensure_ascii=False, indent=2)}```"}},
{"type": "actions", "elements": [
{"type": "button", "text": {"type": "plain_text", "text": "✅ Onayla"},
"style": "primary", "action_id": "approve",
"value": request_id},
{"type": "button", "text": {"type": "plain_text", "text": "✏️ Düzenle"},
"action_id": "edit", "value": request_id},
{"type": "button", "text": {"type": "plain_text", "text": "❌ Reddet"},
"style": "danger", "action_id": "reject",
"value": request_id},
]},
]
await slack.chat_postMessage(channel=approver_user_id, blocks=blocks)
try:
decision = await asyncio.wait_for(fut, timeout=timeout_sec)
return decision # {"status": "approved"|"edited"|"rejected", "payload": {...}}
except asyncio.TimeoutError:
return {"status": "timeout"}
# Slack interaction handler bu future'ı resolve eder
async def on_slack_action(payload):
action = payload["actions"][0]
request_id = action["value"]
if request_id in pending_approvals:
decision = {
"approve": {"status": "approved"},
"reject": {"status": "rejected"},
"edit": {"status": "edited"}, # düzenleme dialogu açılır
}[action["action_id"]]
pending_approvals.pop(request_id).set_result(decision)
# Ajanın tool çağrısını HITL ile sarmala
HITL_REQUIRED_TOOLS = {"send_external_email", "create_invoice", "approve_payment"}
async def execute_tool_with_hitl(tool_name: str, tool_input: dict, user: str):
if tool_name in HITL_REQUIRED_TOOLS:
decision = await request_human_approval(
action=tool_name, payload=tool_input,
approver_user_id=user,
)
if decision["status"] != "approved":
return {"error": f"İnsan onayı reddedildi: {decision['status']}"}
tool_input = decision.get("payload", tool_input)
return await actually_run(tool_name, tool_input)
- HITL ne zaman ZORUNLU: Geri alınamaz aksiyonlar (e-posta gönderme, ödeme, müşteri kaydı oluşturma, dış API'ye yazma) için asla tam otomatik bırakmayın. Komtaş güvenlik politikası gereği bu aksiyonlar HITL gate arkasında olmalıdır.
- "Düzenle" butonu: Kullanıcı onaylamadan önce taslağı düzenleyebilmeli. Bu, prompt iyileşmesinin de en iyi feedback kaynağıdır — düzeltilen taslakları RAGAS test setine ekleyin.
- Timeout: Onay 1 saat içinde gelmezse aksiyon iptal edilir; ajan bunu kullanıcıya bildirir. "Sessiz onay" varsayılanı asla kullanılmaz.
- Audit izi: Her HITL kararı (kim, ne zaman, hangi karar, payload diff) Langfuse trace'ine eklenir. KVKK ve iç denetim için kritik.