
Wed Aug 26 2026

Merhaba! 👋
Hazırsan hemen konuya girelim.
Günlük hayatımızda “neden?” sorusu her an karşımıza çıkar.
Bu soruların cevapları, kararlarımızı şeffaf ve tekrarlanabilir kılar.
Peki ya yapay zeka ajanları? 🤖
Hadi başlayalım ve ajanlarımıza “neden” diye sorabileceğimiz bir hafıza verelim! 🚀
Hazırsan konuya girelim 🚀
Mevcut mimariler neden yetersiz kalıyor?
Bir hata ayıklama senaryosu düşün 👇
| Bileşen | Ne yapar? | Ne yapmaz? |
|---|---|---|
| Vector DB | Embedding saklar | Karar mantığını, filtreleri, skorları kaydetmez |
| RAG | İlgili metinleri getirir | Seçim nedenini, ağırlıkları, yeniden sıralama adımlarını tutmaz |
Sonuç: Sadece “ne” ve “nasıl” var, “neden” yok. Bu boşluk, hata ayıklamayı, denetimi ve güvenilirlik işini zorlaştırıyor.
Hadi bir sonraki bölümde bu “neden”i nasıl yakalayıp kaydedeceğimize bakalım 🎯
Merhaba! Bugün Reasoning Ledger (Gerekçe Defteri) kavramını bir arada inceleyeceğiz. Düşünün ki bir LLM ile yapılan her sohbet, bir günlük gibi kaydediliyor — ama bu günlük sadece "ne yazdı" değil, "neden öyle yazdı"yı da saklıyor 🎯.
| Veri Türü | Ne Anlama Gelir? |
|---|---|
| Prompt | Kullanıcının gönderdiği orijinal istek |
| Intermediate Reasoning | Modelin ara düşünce adımları (chain‑of‑thought) |
| Tool Calls | Harici API, veritabanı, hesaplama gibi çağrılar |
| Final Answer | Kullanıcıya dönen nihai cevap |
| Confidence Scores | Modelin her adım ve sonuca verdiği güven skorları |
Bu tabloyu bir günlük sayfası gibi düşünebilirsiniz: her satır bir "giriş", her sütun ise o girişin bir boyutu. Böylece sonradan "Bu cevap neden bu tool’u çağırdı?" veya "Confidence neden düşük?" sorularına tek bir yerden cevap verebilirsiniz ❗️.
Özetle: Reasoning Ledger, LLM tabanlı sistemlerin şeffaf, izlenebilir ve geliştirilebilir hale gelmesini sağlayan merkezi "karar günlüğü"dür. Bir sonraki bölümde bunu nasıl kodlayıp dağıtabileceğimize bakacağız ✅.
Hazırsan mimariyi adım adım inceleyelim.
Şu akışı takip ediyoruz:
acks=all ve replication factor ≥ 3 ile veri kaybı önlenir.Aşağıda Kafka, PostgreSQL ve basit bir Ledger Writer servisini ayağa kaldıran minimal bir docker-compose.yml var.
Gerçek prod ortamında replication, monitoring, TLS vb. eklemelisiniz.
version: "3.9"
services:
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
kafka:
image: confluentinc/cp-kafka:7.5.0
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
postgres:
image: postgres:16
environment:
POSTGRES_DB: ledger
POSTGRES_USER: ledger_user
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
ledger-writer:
build: ./ledger-writer # Dockerfile içeren klasör
depends_on:
- kafka
- postgres
environment:
KAFKA_BOOTSTRAP_SERVERS: kafka:9092
KAFKA_TOPIC: reasoning-events
PG_DSN: postgresql://ledger_user:secret@postgres:5432/ledger
# Başlangıçta migration çalıştırıp sonra writer’ı başlatabilirsiniz
command: >
sh -c "python migrate.py && python writer.py"
volumes:
pgdata:
Ne oluyor burada?
zookeeper + kafka → Event Store.postgres → Ledger DB (state’in materialized view’ı).ledger-writer → Kafka’dan event okur, PostgreSQL’e yazar.depends_on sayesinde servisler doğru sırada ayağa kalkar.| Bileşen | Öneri | Neden? |
|---|---|---|
| Kafka | Partition sayısını event türüne göre ayarlayın; key olarak agent_id kullanın. |
Aynı agent’in event’leri sıralı kalır, paralellik artar. |
| Kafka | min.insync.replicas=2, replication.factor=3. |
Broker çökse bile veri kaybı olmaz. |
| PostgreSQL | Read‑replica ekleyin, pgBouncer ile connection pooling. | Yazma tek node’da, okuma yatayda büyür. |
| ClickHouse | MergeTree tablolarında partition by toYYYYMM(event_time). |
Zaman bazlı sorgularda pruning hızlanır. |
| Query API | GraphQL için DataLoader / batching; REST için ETag + Cache‑Control. | İstemci tarafında gereksiz tekrar istekleri engeller. |
| Observability | Prometheus + Grafana + OpenTelemetry tracing. | End‑to‑end latency ve hata oranlarını anlık görürsünüz. |
Hadi şimdi bu compose dosyasını docker compose up -d ile çalıştırıp, kendi Ledger Writer’ını yazmaya başla! 🚀
Hazırsan başlayalım 🚀
Aşağıda tamamen kopyala‑yapıştır çalıştırılabilir bir ReasoningLedger sınıfı var.
İçinde şunlar var:
log_step(step_type, data, metadata) → bir adımı bellekte tutarflush() → birikmiş adımları batch halinde dosyaya yazarReasoningLedger sınıfıimport json
import time
import logging
from pathlib import Path
from typing import Any, Dict, List, Optional
logger = logging.getLogger(__name__)
class ReasoningLedger:
"""Basit bir *reasoning* ledger (kayıt defteri) uygulaması.
Her adım `log_step` ile bellekte toplanır ve `flush` çağrıldığında
JSON Lines formatında diske yazılır. Yazma hatalarında otomatik
yeniden deneme (retry) yapılır.
"""
def __init__(
self,
filepath: str | Path,
*,
max_retries: int = 3,
retry_delay: float = 0.5,
batch_size: int = 100,
) -> None:
"""
Args:
filepath: Kayıt dosyasının yolu.
max_retries: Yazma hatasında kaç kez tekrar deneneceği.
retry_delay: Tekrar denemeler arası bekleme süresi (saniye).
batch_size: `flush` çağrıldığında bir seferde yazılacak max satır.
"""
self.filepath = Path(filepath)
self.max_retries = max_retries
self.retry_delay = retry_delay
self.batch_size = batch_size
self._buffer: List[Dict[str, Any]] = []
def log_step(
self,
step_type: str,
data: Any,
metadata: Optional[Dict[str, Any]] = None,
) -> None:
"""Bir reasoning adını buffer'a ekler.
Args:
step_type: Adım türü (ör. "thought", "action", "observation").
data: Adımla ilgili ana veri (string, dict, list...).
metadata: Ekstra bilgiler (timestamp, model adı vb.).
"""
entry = {
"step_type": step_type,
"data": data,
"metadata": metadata or {},
}
self._buffer.append(entry)
# Buffer dolduğunda otomatik flush
if len(self._buffer) >= self.batch_size:
self.flush()
def flush(self) -> None:
"""Buffer'daki tüm entry'leri dosyaya yazar (batch)."""
if not self._buffer:
return
# Dosya yoksa oluştur, varsa append modunda aç
self.filepath.parent.mkdir(parents=True, exist_ok=True)
for attempt in range(1, self.max_retries + 1):
try:
with self.filepath.open("a", encoding="utf-8") as f:
for entry in self._buffer:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
logger.info("%d adım yazıldı → %s", len(self._buffer), self.filepath)
self._buffer.clear()
return
except OSError as exc:
logger.warning(
"Yazma hatası (deneme %d/%d): %s", attempt, self.max_retries, exc
)
if attempt < self.max_retries:
time.sleep(self.retry_delay)
else:
logger.error("Maksimum deneme sayısına ulaşıldı, veri kaybolabilir!")
raise
log_step çağrıldığında entry _buffer listesine eklenir.batch_size dolduğunda otomatik flush tetiklenir.flush dosyayı append modunda açar, her entry’yi JSON Lines olarak yazar.OSError (disk dolu, izin hatası vb.) yakalanır → retry döngüsü devreye girer.if __name__ == "__main__":
# Basit log ayarı
logging.basicConfig(level=logging.INFO, format="%(levelname)s | %(message)s")
ledger = ReasoningLedger(
filepath="logs/reasoning.jsonl",
max_retries=3,
retry_delay=0.5,
batch_size=5, # demo için küçük tutuldu
)
# 1️⃣ Birkaç adım ekle
ledger.log_step("thought", "Kullanıcı girişini analiz et", {"model": "gpt-4"})
ledger.log_step("action", {"tool": "search", "query": "Python retry pattern"})
ledger.log_step("observation", "Sonuçlar alındı", {"count": 12})
# 2️⃣ Manuel flush (buffer dolmadıysa bile)
ledger.flush()
print("✅ Ledger yazıldı, dosyayı kontrol edebilirsiniz.")
Ne oldu?
logs/reasoning.jsonl dosyası oluşturuldu (veya eklendi).İpucu: Gerçek projelerde flush’u bir background thread veya atexit handler ile otomatik çağırabilirsiniz.
Ayrıca metadata alanına timestamp=datetime.utcnow().isoformat() ekleyerek zaman serisi analizleri kolaylaşır 📈.
Hazırsan Ledger verilerini tam bir gözlemlenebilirlik zincirine bağlayalım 🎯
OpenTelemetry → Prometheus → Grafana akışıyla her işlem trace_id ve span_id ile izlenebilir hale gelir.
Collector, ledger’den gelen event’leri OTLP formatına çevirip Prometheus’a gönderir.
Aşağıda minimal bir yaml örneği var 👇
receivers:
otlp:
protocols:
grpc:
http:
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
namespace: "ledger"
const_labels:
service: "ledger-service"
logging:
loglevel: debug
processors:
batch:
timeout: 5s
send_batch_size: 1000
memory_limiter:
limit_mib: 512
check_interval: 1s
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus, logging]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus, logging]
Ne oluyor burada?
receivers.otlp → Uygulamanızdan gelen trace ve metric verilerini toplar.exporters.prometheus → Prometheus scrape edebileceği /metrics endpoint’i açar.processors.batch + memory_limiter → Yük altında bile stabil çalışır.trace_id / span_id Ekle// Örnek Go kodu (sadece mantık)
func WriteLedgerEntry(ctx context.Context, entry LedgerEntry) error {
span := trace.SpanFromContext(ctx)
entry.TraceID = span.SpanContext().TraceID().String()
entry.SpanID = span.SpanContext().SpanID().String()
return ledger.Insert(entry)
}
ledger_entry_total{trace_id="...", span_id="..."} metrikleri görünecektir.| Adım | Açıklama |
|---|---|
| Prometheus scrape config’ine collector endpoint’i ekle | scrape_interval: 15s yeterli |
| Grafana → Explore → Trace sekmesi | trace_id ile filtrele → ilgili span’leri gör |
| Dashboard oluştur | - Ledger Hata Oranı (counter) - Latency per Trace (histogram) - Top 10 Hatalı Trace (table) |
İpucu 🛠: Grafana’da “Trace to Logs” butonuyla ilgili log satırlarına anında atlayabilirsin.
ledger_error_total > 0) → Grafana alert manager size bildirir.SELECT * FROM ledger
WHERE trace_id = '01F4K...' AND span_id = 'A1B2C3...';
trace_id/span_id ekliyor mu?Bu adımları takip ederseniz, her hata anında ledger verisiyle dağıtık izleme arasında köprü kurmuş olursunuz.
Artık “Nerede hata?” sorusuna saniyeler içinde cevap verebilirsiniz 🚀
Hazırsan bu kısımda Reasoning Ledger'ın model uyum (alignment) testlerinde nasıl bir "karanlık odadan ışık" olduğunu anlatalım 💡. Ledger sadece log toplamaz; modelin neden öyle karar verdiğini, hangi adımları izlediğini ve nerede saptığını gösterir. İşte bu veriyi RLHF'den otomatik politika kontrolüne nasıl döktüğümüz:
Düşün ki bir model cevap üretti ama mantık zinciri (chain-of-thought) tuhaf bir yerde kopmuş. Ledger'da bu zincir adım adım durur. Etiketleme sürecini şöyle kurgularız:
reasoning_step için correct / partially_correct / incorrect etiketi.tool_call veya retrieval adımlarında kullanılan kaynak ground_truth ile uyuyor mu? → verified / unverified / hallucinated.final_answer ile reasoning_summary çelişiyor mu? → consistent / inconsistent.İpucu: Bu etiketleri bir Label Studio veya Argilla arayüzüne besleyip, anotatörlere "Bu adım neden yanlış?" diye sorarsan; modelin hata modunu (reasoning error vs retrieval error) ayırt edersin 🎯.
-- Örnek: Etiketleme kuyruğu için "insan incelemesi bekleyen" adımları çek
SELECT
ledger_id,
step_index,
step_type,
content,
metadata->>'model_confidence' AS confidence,
created_at
FROM reasoning_ledger
WHERE
session_id IN (
SELECT session_id FROM evaluation_sessions
WHERE status = 'pending_review'
)
AND step_type IN ('reasoning', 'tool_call', 'retrieval')
ORDER BY created_at DESC
LIMIT 100;
Ne oluyor burada? Annotatörlerin zamanını en belirsiz/yüksek riskli adımlarla harcamasını sağlıyoruz. model_confidence düşükse ama model "eminim" diyorsa → kalibrasyon sorunu var demektir ⚠️.
İnsan her şeyi gözden geçiremez. Ledger verisi SQL/GraphQL ile sorgulanabilir hale getirildiğinde, policy ihlallerini saniyelerde yakalarsın. Örnek kurallar:
| Kural | Ledger Alanı | Tespit Mantığı |
|---|---|---|
| Yasaklı veri sızıntısı | tool_call.output / retrieval.chunks |
PII regex / keyword match |
| Araç kötüye kullanımı | tool_call.name, arguments |
exec_sql içinde DROP, DELETE var mı? |
| Mantık atlaması | reasoning_step sayısı < eşik |
"Kısa devre" yapılmış mı? |
| Çelişkili kaynak | retrieval.source_id vs reasoning.cited_source |
Alıntı yapılmış ama kaynak çekilmemiş |
GraphQL örneği — son 1 saatte "PII içeren tool output" üreten oturumlar:
query RecentPIIViolations($since: DateTime!) {
reasoningLedgerEntries(
where: {
createdAt_gt: $since
stepType: TOOL_CALL
toolOutput_contains: "TC_KIMLIK_NO"
}
) {
sessionId
stepIndex
toolName
toolOutput
createdAt
}
}
Neden GraphQL? Frontend ekibin "İhlal Panosu"nu bu query ile real-time besler; filtreleme/sayfalama zaten built-in gelir 🚀.
İki metrik operasyonel hayatın kalbi olur:
"Aynı girdiye, aynı context'te model ne kadar benzer mantıkla cevap veriyor?"
consistency_score = 1 - (unique_reasoning_paths / total_runs)
unique_reasoning_paths: Ledger'da reasoning_step hash'leri (ör. SHA256) benzersiz sayısı."Model, ledger'da kanıtı olmayan iddialar üretti mi?"
Formül:
hallucination_rate = hallucinated_claims / total_claims
hallucinated_claims: retrieval/tool_call sonucu boş dönen ya da çelişen kaynaklarla desteklenen iddialar.total_claims: final_answer içindeki bağımsız iddia sayısı (NLP ile chunk'lanmış).İşte operasyonel dashboard'ına düşecek tek sorgu 🛠. Varsayıyorum ki:
claims tablosu: her iddiayı claim_text, supported_by_ledger_ids (ARRAY), is_hallucinated (BOOLEAN) ile tutar.reasoning_ledger tablosu: session_id, created_at, step_type, content.WITH recent_sessions AS (
SELECT DISTINCT session_id
FROM reasoning_ledger
WHERE created_at >= NOW() - INTERVAL '24 hours'
),
claim_stats AS (
SELECT
COUNT(*) AS total_claims,
COUNT(*) FILTER (WHERE is_hallucinated) AS hallucinated_claims
FROM claims
WHERE session_id IN (SELECT session_id FROM recent_sessions)
)
SELECT
total_claims,
hallucinated_claims,
ROUND(
CASE WHEN total_claims = 0 THEN 0
ELSE hallucinated_claims::numeric / total_claims
END, 4
) AS hallucination_rate
FROM claim_stats;
Bu sayede ne oluyor?
| Strateji | Ledger Rolü | Çıktı |
|---|---|---|
| RLHF Etiketleme | Adım bazlı inceleme yüzeyi | Yüksek kaliteli reward model verisi |
| Policy Detection | SQL/GraphQL sorgulanabilir audit trail | Saniyelerde ihlal yakalama |
| Güvenilirlik Metrikleri | reasoning_step + claims join |
Consistency & Hallucination KPI'ları |
Son söz: Ledger olmadan alignment "gözümüz kapalı sürüş"; ledger ile "kara kutu kaydedicili uçuş" olur. Veriyi topla, etiketle, sorgula, ölç — döngüyü kırma 🔄.
Hazırsan kapanışa geçelim 🚀
Ledger büyüdükçe şunları akılda tut:
Mikro hizmet mi, library mi?
Küçük ekipler için library başlangıçta pratik; büyüdükçe mikro hizmet mimarisi esneklik kazandırır.
Keyifli kodlamalar, neden sormaya devam edin! 🎉
Bu içerik tamamen yapay zeka destekli otomasyon sistemi ile üretilmiştir.
All rights reserved