• 25.08.2026 05:10:11
  • Admin Admin

Bir yazılım eğitimi asistanında LLM tool calling akışını şema doğrulama, sunucu tarafı yetkilendirme, idempotency ve OpenTelemetry metrikleriyle üretime uygun şekilde tasarlamayı ele alır.

Yazılım Eğitimi Asistanlarında Tool Calling Güvenliği ve İzlenebilirlik

Yazılım eğitimi asistanında araç sözleşmesini daraltmak

Bir yazılım eğitimi asistanında modelin ürettiği tool argümanını güvenilir girdi kabul etmeyin. Model, sistem mesajındaki yönergeyi doğru takip etse bile prompt injection ile geniş kapsamlı bir course_id, beklenmeyen alan veya aşırı uzun bir sorgu üretebilir. Her araç için Pydantic ile kapalı şema tanımlayın; extra='forbid', modelin uydurduğu include_private_notes gibi alanları doğrulama aşamasında reddeder, strict=True ise '10' değerinin sessizce integer'a dönüşmesini engeller.

from uuid import UUID
from pydantic import BaseModel, ConfigDict, Field

class SearchLessonArgs(BaseModel):
    model_config = ConfigDict(extra='forbid', strict=True)

    course_id: UUID
    query: str = Field(min_length=3, max_length=240)
    limit: int = Field(default=5, ge=1, le=10)

def parse_search_args(raw: dict) -> SearchLessonArgs:
    return SearchLessonArgs.model_validate(raw)

Şema, yetkilendirme mekanizması değildir: geçerli bir UUID, kullanıcının o kursa erişebileceği anlamına gelmez. Ayrıca tool açıklamalarına SQL, URL şablonu veya dosya yolu gibi yürütme ayrıntıları koymayın; bunlar modelin çıktısına taşınan saldırı yüzeyini büyütür. Araç tanımını search_course_lessons gibi iş niyeti düzeyinde tutun ve dışarı açılan şemayı CI içinde pytest -q tests/tools/test_schemas.py ile sınayın. Özellikle bilinmeyen alan, 241 karakterlik sorgu, negatif limit ve UUID olmayan course_id için negatif test ekleyin.

Tool çağrılarını kimlik ve yetki bağlamına bağlamak

Tool dispatch katmanında modelin verdiği course_id değerini doğrudan SQL filtresine geçirmek yerine, oturumdan gelen kullanıcı kimliğiyle sahiplik kontrolü yapın. Aşağıdaki örnekte model yalnızca arama niyetini ve sorguyu belirler; kurs kaydı hem course_id hem de ctx.user_id ile sorgulandığı için başka bir kiracıya ait UUID tahmin edilse bile sonuç dönmez.

def search_course_lessons(ctx, args, db):
    enrolled = db.fetch_one(
        '''
        select 1
        from enrollments
        where user_id = %s and course_id = %s
        ''',
        (ctx.user_id, args.course_id),
    )
    if enrolled is None:
        raise PermissionError('course access denied')

    return db.fetch_all(
        '''
        select id, title, excerpt
        from lessons
        where course_id = %s
          and search_vector @@ websearch_to_tsquery(%s)
        order by ts_rank(search_vector, websearch_to_tsquery(%s)) desc
        limit %s
        ''',
        (args.course_id, args.query, args.query, args.limit),
    )

Tool adı için de açık allowlist kullanın. getattr(tools, tool_name) veya kullanıcıdan gelen URL'ye HTTP isteği atan genel bir fetch aracı, model çıktısını yetenek yükseltmeye dönüştürür. Dispatcher yalnızca tanımlı fonksiyonları kabul etmelidir: TOOLS = {'search_course_lessons': search_course_lessons, 'get_progress': get_progress}. HTTP kullanan araçlarda ek olarak DNS çözümlemesinden sonra hedef IP'nin RFC1918, loopback ve link-local aralıklarda olmadığını kontrol edin; yalnızca URL metnini https:// ile başlatmak SSRF için yeterli filtre değildir.

Tool orchestration gecikmesini profil ile ölçmek

Gecikme optimizasyonuna, kullanıcıdan görülen ölçümü tanımlayarak başlayın: streaming arayüzde time_to_first_token, tool gerektiren turlarda tool_round_trip_ms, uçtan uca turn_duration_ms ve tur başına tool sayısı ayrı histogramlar olmalıdır. OpenTelemetry span'ları model çağrısı, tool doğrulaması ve her downstream isteği ayrı gösterecek şekilde eklenirse p95 değerinin modelden mi, PostgreSQL'den mi, yoksa seri çalışan bağımsız araçlardan mı geldiği trace üzerinde görülebilir.

from opentelemetry import trace
from time import perf_counter

tracer = trace.get_tracer('academy.assistant')

def run_tool(ctx, name, args):
    started = perf_counter()
    with tracer.start_as_current_span('assistant.tool') as span:
        span.set_attribute('tool.name', name)
        span.set_attribute('tool.course_id', str(args.course_id))
        try:
            result = TOOLS[name](ctx, args)
            span.set_attribute('tool.result_count', len(result))
            return result
        finally:
            span.set_attribute(
                'tool.duration_ms', round((perf_counter() - started) * 1000, 2)
            )

Önce sabit bir konuşma veri setiyle mevcut akışın p50, p95, hata oranı ve ortalama tool tur sayısını kaydedin. Ardından yalnızca birbirinden bağımsız salt-okunur çağrıları paralelleştirin; örneğin kurs ilerlemesi ile ders araması aynı veriyi yazmıyorsa asyncio.TaskGroup kullanın. Aynı kaynağa yazan mark_lesson_complete ve ardından çalışan get_progress çağrılarını paralelleştirmek, read-after-write tutarsızlığı üretir.

import asyncio

async def load_turn_context(ctx, search_args):
    async with asyncio.TaskGroup() as group:
        lessons_task = group.create_task(search_lessons_async(ctx, search_args))
        progress_task = group.create_task(get_progress_async(ctx))
    return {'lessons': lessons_task.result(), 'progress': progress_task.result()}
Değişiklikten sonra aynı veri seti ve aynı eşzamanlılıkla k6 run -e BASE_URL=https://staging.example.com scripts/assistant_turn.js çalıştırın. Sadece p95 düşerken hata oranı veya tool sonuç sayısı değişmişse karşılaştırma geçerli değildir; paralellik eksik sonuç, timeout veya rate limit üretiyor olabilir.

Tekrarlanan side effect çağrılarında idempotency tasarımı

Ağ bağlantısı, tool sonucu veritabanına yazıldıktan sonra koparsa orkestratör aynı tool çağrısını yeniden deneyebilir. Bu durum create_submission, rozet verme veya e-posta gönderme gibi araçlarda çift işlem üretir. Tool çağrısına istemci oturumundan gelen, işlem kapsamına bağlı bir idempotency_key ekleyin ve PostgreSQL tarafında benzersiz kısıtla ilk sonucu tekrar kullanın.

create table tool_executions (
  user_id uuid not null,
  tool_name text not null,
  idempotency_key uuid not null,
  status text not null,
  result_json jsonb,
  created_at timestamptz not null default now(),
  primary key (user_id, tool_name, idempotency_key)
);

Yaygın hata, her LLM tool çağrısı için yeni rastgele anahtar üretmektir; retry yeni anahtarla geldiğinde benzersiz kısıt hiçbir işe yaramaz. Anahtar, kullanıcı isteği oluşturulurken üretilip tüm retry zincirinde taşınmalıdır. insert ... on conflict ile kaydı önce running durumunda oluşturun, işi tamamlayınca sonucu yazın; aynı anahtarla gelen ikinci istek completed sonucu döndürebilir. Uzun süren running kayıtlarını körlemesine yeniden çalıştırmak yerine lease süresi, worker heartbeat'i ve hedef sistemdeki işlem durumu kontrolü ekleyin.

Araç çağrılarını denetlenebilir ancak veri sızdırmaz tutmak

İzlenebilirlik kaydında ham prompt, erişim belirteci veya ders metninin tamamını saklamak yerine korelasyon için trace_id, araç adı, şema doğrulama sonucu, gecikme, kullanıcı kiracısı ve argüman özetini saklayın. Örneğin sorgu metnini loglamak yerine SHA-256 özeti ve uzunluğunu kaydedin; böylece aynı sorgunun tekrarını tespit ederken öğrenci verisini log sistemine taşımamış olursunuz.

import hashlib

def audit_tool_call(trace_id, ctx, args, tool_name):
    return {
        'trace_id': trace_id,
        'tenant_id': str(ctx.tenant_id),
        'tool_name': tool_name,
        'query_sha256': hashlib.sha256(args.query.encode()).hexdigest(),
        'query_length': len(args.query),
        'course_id': str(args.course_id),
    }

OpenTelemetry Collector içinde span attribute'larını dış sisteme göndermeden önce redaksiyon uygulayın ve authorization, cookie, prompt, tool.arguments gibi alanları allowlist dışı bırakın. Üretim doğrulaması için kontrollü bir canary isteğinde benzersiz test token'ı kullanın, ardından log arama aracında token'ın bulunmadığını doğrulayın. Bu kontrolü CI'da entegrasyon testi olarak çalıştırmak, bir geliştiricinin debug amacıyla ham argüman eklemesini yalnızca kod incelemesine bırakmaktan daha güvenilirdir.

Sık Sorulan Sorular

Yazılım eğitimi asistanında LLM tool argümanları nasıl doğrulanır?

Her tool için Pydantic veya Zod ile kapalı JSON şeması oluşturun. Bilinmeyen alanları reddedin, string uzunluğu ve sayısal sınır koyun, UUID gibi kimlikleri tip olarak doğrulayın. Doğrulama başarılı olsa bile kullanıcı-kurs sahiplik kontrolünü tool sunucusunda ayrı SQL sorgusuyla yapın.

Yazılım eğitimi uygulamasında tool calling gecikmesi nasıl ölçülür?

OpenTelemetry ile model çağrısı, argüman doğrulama, her tool ve veritabanı sorgusu için ayrı span üretin. Değişiklik öncesi ve sonrası aynı konuşma veri setinde p50, p95, hata oranı ve tur başına tool çağrısı ölçün; k6 ile aynı eşzamanlılık seviyesinde yük testi çalıştırın.

Yazılım eğitimi asistanında tekrar eden tool çağrıları nasıl engellenir?

Side effect içeren araçlarda kullanıcı ve tool adıyla birlikte benzersiz bir idempotency key saklayın. Anahtarı LLM çağrısı sırasında değil, ilk kullanıcı isteğinde üretin ve retry zincirinde koruyun. PostgreSQL unique constraint ile ikinci isteğin önceki sonucu döndürmesini sağlayın.

AI / LLM Discovery

Bu makale Opendart Akademi Yapay Zeka eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt