0Pricing
AI Agents · Ders

Çok Belgeli Soru-Cevap Aracıları

Belge derlemesini dizinleme ve tüm belgeler genelinde soruları yanıtlama.

Çok Belgeli Soru-Cevap Aracıları, CoddyKit'te ücretsiz bir AI Agents dersidir. Bu, 4 dersinin 3. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, AI Agents öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. AI Agents kursu toplamda 4 dersten oluşur.

Çok Belgeli Soru-Cevap Genel Bakışı

Çok belgeli soru-cevap aracısı, N belgeden oluşan bir koleksiyondan ilgili içeriği getirerek, bir yanıt sentezleyerek ve her iddiayı kaynağına bağlayarak soruları yanıtlar.

Tek belgeli soru-cevap sistemlerinden farklı olarak, çok belgeli aracıların kaynaklar arasındaki çelişkili bilgileri ele alması ve hangi belgelerin soruyla en ilgili olduğunu değerlendirmesi gerekir.

Birden Çok Belgeyi Dizinleme

Soruları yanıtlamadan önce tüm belgeler dizine eklenmelidir: ayrıştırılmalı, parçalara ayrılmalı, gömülmeli ve bir vektör veritabanında saklanmalıdır. Her parça, onu kaynak belgesine bağlayan üst verilerle birlikte saklanır.

import chromadb
from chromadb.utils import embedding_functions
import os

client = chromadb.PersistentClient(path='./doc_index')
ef = embedding_functions.OpenAIEmbeddingFunction(
    api_key=os.getenv('OPENAI_API_KEY'),
    model_name='text-embedding-3-small'
)
collection = client.get_or_create_collection('documents', embedding_function=ef)

def index_document(doc_id, doc_path, doc_title):
    # Parse and chunk
    chunks = pdf_to_chunks(doc_path, chunk_size=800, overlap=150)
    for i, chunk in enumerate(chunks):
        chunk_id = f'{doc_id}_chunk_{i}'
        collection.add(
            ids=[chunk_id],
            documents=[chunk['text']],
            metadatas=[{
                'doc_id': doc_id,
                'title': doc_title,
                'page': chunk['page'],
                'source_file': doc_path
            }]
        )
    print(f'Indexed {len(chunks)} chunks from: {doc_title}')

İlgili Parçaları Getirme

Bir soru geldiğinde, dizine eklenmiş tüm belgeler arasındaki anlamsal açıdan en benzer parçaları bulmak için vektör deposunu sorgulayın. n_results parametresi kaç parça getirileceğini belirler.

def retrieve_relevant_chunks(question, n_results=8):
    results = collection.query(
        query_texts=[question],
        n_results=n_results,
        include=['documents', 'metadatas', 'distances']
    )

    chunks = []
    for i in range(len(results['documents'][0])):
        chunks.append({
            'text':      results['documents'][0][i],
            'metadata':  results['metadatas'][0][i],
            'distance':  results['distances'][0][i],
            'relevance': 1 - results['distances'][0][i]  # cosine similarity proxy
        })

    # Sort by relevance
    chunks.sort(key=lambda x: x['relevance'], reverse=True)
    return chunks

İstemde Kaynak Belirtme

Getirilen parçaları LLM'ye aktarırken her parçayı belge kaynağıyla etiketleyin. Böylece LLM, yanıtta numaralı göndermeler kullanarak kaynaklara atıfta bulunabilir.

def format_chunks_for_prompt(chunks, max_chars=4000):
    sections = []
    used_chars = 0

    for i, chunk in enumerate(chunks, 1):
        meta = chunk['metadata']
        header = f"[Source {i}: {meta['title']}, page {meta.get('page', '?')}]"
        content = chunk['text'][:600]
        entry = f'{header}\n{content}'

        if used_chars + len(entry) > max_chars:
            break

        sections.append(entry)
        used_chars += len(entry)

    return '\n\n'.join(sections)

QA_PROMPT = '''Answer the question based on the provided document excerpts.
Cite sources as [Source N]. If sources conflict, mention both views.

{context}

Question: {question}
Answer:'''

def answer_question(question):
    chunks = retrieve_relevant_chunks(question, n_results=6)
    context = format_chunks_for_prompt(chunks)
    return llm_call(QA_PROMPT.format(context=context, question=question))

Belgeler Arası Akıl Yürütme

Bazı sorular, yalnızca eşleşen tek bir parçayı bulmayı değil, birden çok belgedeki bilgileri sentezlemeyi gerektirir. Örneğin: "Üç sözleşmeden hangisinin ceza maddesi en düşük?"

İki adımlı bir yaklaşım kullanın: her belgeden ilgili parçaları getirin, ardından LLM'den bunları karşılaştırmasını ve aralarındaki bilgileri sentezlemesini isteyin.

def cross_document_compare(question, doc_ids):
    # Retrieve best chunks per document
    per_doc_chunks = {}
    for doc_id in doc_ids:
        results = collection.query(
            query_texts=[question],
            n_results=3,
            where={'doc_id': {'$eq': doc_id}}  # filter by document
        )
        if results['documents'][0]:
            per_doc_chunks[doc_id] = results['documents'][0]

    # Format with document labels
    context_parts = []
    for doc_id, texts in per_doc_chunks.items():
        doc_label = f'Document {doc_id}'
        combined = ' '.join(texts[:2])[:600]
        context_parts.append(f'== {doc_label} ==\n{combined}')

    comparison_context = '\n\n'.join(context_parts)
    return llm_call(f'Compare these documents to answer: {question}\n\n{comparison_context}')

Çelişkili Bilgileri İşleme

Farklı belgeler çelişkili gerçekler belirtebilir; bir sözleşme ödemenin 30 gün içinde, başka bir sözleşme ise 60 gün içinde yapılması gerektiğini söyleyebilir. Aracı, bu çelişkileri sessizce birini seçmek yerine algılamalı ve görünür hâle getirmelidir.

CONFLICT_PROMPT = '''You are analyzing multiple document sources.
Some may contain conflicting information.

For each factual claim you make:
1. Cite the source document
2. If another source contradicts it, explicitly note the conflict
3. Indicate which source you believe is more authoritative, if possible

Document excerpts:
{context}

Question: {question}

Answer (with conflict notes where applicable):'''

def answer_with_conflict_detection(question):
    chunks = retrieve_relevant_chunks(question, n_results=8)
    context = format_chunks_for_prompt(chunks)
    return llm_call(CONFLICT_PROMPT.format(
        context=context, question=question
    ))

İlgililik Eşiğine Göre Filtreleme

Getirilen parçaların tümü gerçekten ilgili değildir; vektör benzerliği, geri çağırma ile kesinlik arasında bir ödünleşim içerir. LLM'yi yanıltabilecek zayıf eşleşen parçaları dışlamak için asgari bir ilgililik eşiği belirleyin.

MIN_RELEVANCE = 0.72  # cosine similarity threshold

def retrieve_above_threshold(question, n_results=10, threshold=MIN_RELEVANCE):
    chunks = retrieve_relevant_chunks(question, n_results=n_results)
    relevant = [c for c in chunks if c['relevance'] >= threshold]

    print(f'Retrieved: {len(chunks)}, Above threshold: {len(relevant)}')

    if not relevant:
        # Fallback: use top 3 even if below threshold
        return chunks[:3]

    return relevant

def answer_with_threshold(question):
    chunks = retrieve_above_threshold(question)
    if not chunks:
        return 'I could not find relevant information in the indexed documents.'
    context = format_chunks_for_prompt(chunks)
    return llm_call(QA_PROMPT.format(context=context, question=question))

Kaynak Atıfları Oluşturma

Bir yanıt oluşturduktan sonra, hangi kaynak belgelere atıfta bulunulduğunu çıkarın ve bunları yapılandırılmış bir liste olarak döndürün. Bu, kullanıcıların doğrulama için özgün belgeleri bulmasına yardımcı olur.

import re

def extract_citations(answer_text, chunks):
    # Find all [Source N] references in the answer
    cited_nums = set(int(m) for m in re.findall(r'\[Source (\d+)\]', answer_text))

    citations = []
    for num in sorted(cited_nums):
        idx = num - 1
        if idx < len(chunks):
            meta = chunks[idx]['metadata']
            citations.append({
                'source_num': num,
                'title':     meta.get('title', 'Unknown'),
                'page':      meta.get('page', 'N/A'),
                'file':      meta.get('source_file', '')
            })

    return citations

def answer_with_citations(question):
    chunks = retrieve_above_threshold(question)
    context = format_chunks_for_prompt(chunks)
    answer = llm_call(QA_PROMPT.format(context=context, question=question))
    citations = extract_citations(answer, chunks)
    return {'answer': answer, 'citations': citations}

Çapraz Kodlayıcı ile Yeniden Sıralama

İlk vektör getirme işleminde çift kodlayıcılar kullanılır (hızlı ve yaklaşık). Bir çapraz kodlayıcı, her (sorgu, parça) çiftini birlikte puanlayarak en üstteki sonuçları yeniden sıralar; bu yöntem daha doğru, ancak daha yavaştır. Bu iki aşamalı yaklaşım, son yanıtın kalitesini artırır.

# pip install sentence-transformers
from sentence_transformers import CrossEncoder

reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

def rerank_chunks(question, chunks, top_k=4):
    # Score each chunk against the question
    pairs = [(question, c['text']) for c in chunks]
    scores = reranker.predict(pairs)

    # Attach scores and re-sort
    scored_chunks = list(zip(scores, chunks))
    scored_chunks.sort(key=lambda x: x[0], reverse=True)

    top_chunks = [chunk for _, chunk in scored_chunks[:top_k]]
    print(f'Re-ranked {len(chunks)} chunks -> kept top {top_k}')
    return top_chunks

def answer_with_reranking(question):
    # Retrieve more initially
    initial_chunks = retrieve_relevant_chunks(question, n_results=12)
    # Re-rank for precision
    top_chunks = rerank_chunks(question, initial_chunks, top_k=4)
    context = format_chunks_for_prompt(top_chunks)
    return llm_call(QA_PROMPT.format(context=context, question=question))

Belge Düzeyinde Üst Veri Filtreleme

Kullanıcı belirli bir belge veya tarih aralığı belirttiğinde, gömme aramasından önce üst veri düzeyinde filtreleme yapın. Bu, ilgisiz belgelerin sonuçlara karışmasını önler.

def retrieve_filtered(question, filters=None, n_results=8):
    query_kwargs = {
        'query_texts': [question],
        'n_results': n_results,
        'include': ['documents', 'metadatas', 'distances']
    }

    # ChromaDB metadata filters
    # Example: {'doc_id': 'contract_2024', 'year': {'$gte': 2023}}
    if filters:
        query_kwargs['where'] = filters

    results = collection.query(**query_kwargs)
    return [
        {'text': t, 'metadata': m, 'relevance': 1 - d}
        for t, m, d in zip(
            results['documents'][0],
            results['metadatas'][0],
            results['distances'][0]
        )
    ]

# Example usage
chunks = retrieve_filtered(
    'What are the payment terms?',
    filters={'doc_id': {'$in': ['contract_a', 'contract_b']}}
)

Dizini Yeni Belgelerle Güncelleme

Belge koleksiyonları zaman içinde değişir; yeni dosyalar eklenir, eski dosyalar güncellenir. Dizinleme işlem hattı artımlı güncellemeleri desteklemelidir: yeni belgeleri eklemeli, güncellenenleri yeniden dizine eklemeli ve silinenleri kaldırmalıdır.

import os
import hashlib

# Track indexed documents by file hash
index_registry = {}  # {filepath: {hash, doc_id, indexed_at}}

def file_hash(filepath):
    with open(filepath, 'rb') as f:
        return hashlib.md5(f.read()).hexdigest()

def index_if_new_or_changed(filepath, title):
    fhash = file_hash(filepath)
    existing = index_registry.get(filepath)

    if existing and existing['hash'] == fhash:
        print(f'Skipping unchanged: {title}')
        return existing['doc_id']

    if existing:
        # Remove old chunks from vector store
        collection.delete(where={'doc_id': {'': existing['doc_id']}})
        print(f'Re-indexing updated: {title}')
    else:
        print(f'Indexing new: {title}')

    doc_id = hashlib.md5(filepath.encode()).hexdigest()[:8]
    index_document(doc_id, filepath, title)
    index_registry[filepath] = {'hash': fhash, 'doc_id': doc_id}
    return doc_id

def sync_document_directory(directory):
    pdf_files = [f for f in os.listdir(directory) if f.endswith('.pdf')]
    for fname in pdf_files:
        fpath = os.path.join(directory, fname)
        title = fname.replace('.pdf', '').replace('_', ' ').title()
        index_if_new_or_changed(fpath, title)
    print(f'Sync complete: {len(pdf_files)} files processed')

Bilgi Kontrolü

Getirmeyle zenginleştirilmiş üretim kullanan çok belgeli bir soru-cevap sisteminde ilgililik eşiği neyin önlenmesini sağlar?

Tekrar: Çok Belgeli Soru-Cevap Aracıları

Çok belgeli soru-cevap: tüm belgeleri dizine ekleyin (ayrıştır → parçalara ayır → göm → üst verilerle sakla) → tüm belgeler arasından ilgili parçaları getir → kaynak etiketleriyle biçimlendir → atıflarla yanıtı sentezle.

İleri teknikler: karşılaştırmalı sorular için belgeler arası karşılaştırma, çelişki algılama istemleri, ilgililik eşiğine göre filtreleme, kesinlik için çapraz kodlayıcıyla yeniden sıralama ve sorguların kapsamını belirli belgelere veya tarih aralıklarına daraltmak için üst veri filtreleme.

Sıkça Sorulan Sorular

“Çok Belgeli Soru-Cevap Aracıları” dersi ücretsiz mi?

Evet — “Çok Belgeli Soru-Cevap Aracıları” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve AI Agents kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. AI Agents kursu toplamda 4 dersten oluşur.

“Çok Belgeli Soru-Cevap Aracıları” dersinde ne öğreneceğim?

Belge derlemesini dizinleme ve tüm belgeler genelinde soruları yanıtlama. AI Agents ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

AI Agents öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te AI Agents, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 3. dersidir.

“Çok Belgeli Soru-Cevap Aracıları” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu AI Agents dersinde kod yazıp çalıştırabilir miyim?

Evet. Her AI Agents dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. PyMuPDF ve pdfplumber ile PDF Ayrıştırma
  2. Taranmış Belgeler İçin OCR
  3. Çok Belgeli Soru-Cevap Aracıları
  4. Belge Sınıflandırma ve Yönlendirme
← AI Agents Sayfasına Dön