RAG ve veri rehberleri · 2026-08-28

Qwen-VL-OCR ile fatura ve tablo okuma: eksik alanları doğrulama

Qwen-VL-OCR ile fatura alanlarını çıkarırken okunamayan değerleri, satır ve sütun eşleşmesini, aritmetik tutarlılığı ve insan onayını birlikte yönetin.

Qwen-VL-OCR ile fatura ve tablo okuma: eksik alanları doğrulama konusu için üç ayrı bilgi kutusunu karşılaştıran veya sıralayan açıklayıcı LLMTR rehber şeması.

Faturayı okumak ile faturayı doğrulamak farklı adımlardır

Qwen-VL-OCR, fatura görselindeki metni ve tablo alanlarını çıkarmak için kullanılabilir; çıkan değerler doğrudan muhasebe kaydı veya ödeme talimatı sayılmamalıdır. Güvenli akış, okunamayan alanları bilinmeyen olarak bırakır, her tutarı doğru satır ve sütunla eşler, hesapları uygulama koduyla yeniden yapar ve belgeyi insan kontrolüne sunar.

LLMTR isteğinde qwen/qwen-vl-ocr kimliğini ve POST /v1/chat/completions yolunu kullanın. Görsel, user mesajının content dizisindeki image_url alanına girer. Alibaba tarafındaki model adı qwen-vl-ocr biçimindedir. Bölge ve sürüme bağlı görüntü, bağlam ve çıktı sınırlarını seçili hizmet için kontrol edin; başka bir OCR sürümünün değerlerini genellemeyin.

Kişisel veri içermeyen bir deneme faturası hazırlayın

Üzerine EĞİTİM BELGESİ yazılmış bir PNG hazırlayın. Belge numarası EGT-0001, tarih 2026-08-28 ve para birimi TRY olsun. Gerçek kişi, şirket, vergi numarası, adres veya banka hesabı eklemeyin. Aşağıdaki tablo giriş görselinin içeriğidir; modelden alınmış bir sonuç değildir.

Altına ara toplam 400,00, indirim 0,00, vergi tutarı 40,00 ve genel toplam 440,00 yazın. Vergi tutarı yalnızca aritmetik egzersiz içindir; geçerli bir vergi oranını temsil etmez. Aynı belgenin ikinci kopyasında birim fiyat hücresini bulanıklaştırarak bilinmeyen değer davranışını ayrıca sınayın.

Sentetik giriş belgesindeki iki ürün satırı; tutarlar TRY
SatırÜrünAdetBirim fiyatSatır tutarı
1Kablo2125,00250,00
2Defter350,00150,00

Eksik alan ile okunamayan alanı ayırın

Her alan için value ve status isteyin. Alan görünür ve okunuyorsa read, belgede bulunmuyorsa missing, mevcut fakat çözülemiyorsa unreadable kullanın. Son iki durumda value null olmalıdır. Bu etiketler uygulamanızın istediği çıktı düzenidir; modelin kendiliğinden sağladığı doğrulanmış durum kodları değildir.

Örneğin bulanık birim fiyatı, satır toplamını adede bölerek OCR sonucu gibi doldurmayın. Hesaplanan olası değer ayrı bir öneridir ve özgün okumayı değiştirmemelidir. Tarihte gün ile ay, tutarda virgül ile nokta belirsizse düzeltmeyi tahminle yapmayın. Modelin yazdığı güven yüzdesini de ölçülmüş doğruluk olasılığı kabul etmeyin.

Görseli gerçek Chat Completions sözleşmesiyle gönderin

Aşağıdaki örnek, hazırladığınız synthetic-invoice.png dosyasını gönderir. LLMTR_BASE_URL tam API taban adresi, LLMTR_API_KEY ise LLMTR anahtarıdır; bunları doğrudan Alibaba erişim bilgileriyle karıştırmayın. Kod çalıştırılmadı ve canlı yanıt doğrulanmadı.

JSON istemek JSON Schema garantisi sağlamaz. Örnek yalnızca yanıtın tamamlanmasını, JSON sözdizimini ve temel nesne biçimini kontrol eder. Alanların tamamını ve türlerini uygulamanız ayrıca doğrulamalıdır. Belge içeriği veya model yanıtı loglanmaz; bir inceleme durumu üretilir.

Sentetik PNG için tek istek; sonuç otomatik onaylanmaz

import base64
import json
import os
from pathlib import Path
from urllib.error import HTTPError
from urllib.request import Request, urlopen

image = Path("synthetic-invoice.png").read_bytes()
if not image.startswith(b"\x89PNG\r\n\x1a\n"):
    raise ValueError("Gerçek bir PNG dosyası kullanın.")
prompt = (
    "Bu eğitim faturasındaki alanları oku. Yalnızca JSON nesnesi döndür. "
    "Üst alanlar: invoice_number, date, currency, subtotal, discount, tax, total. "
    "lines dizisindeki her satırda row_index, description, quantity, "
    "unit_price, line_total bulunsun. row_index 1'den başlayan tam sayı olsun. "
    "Diğer her alan {value: metin veya null, status: read|missing|unreadable} "
    "nesnesi olsun. Eksik veya okunamayan değer null olsun. Görünen sayı "
    "biçimini koru. Satırları birleştirme, hiçbir değeri hesaplayıp tamamlama."
)
payload = {
    "model": "qwen/qwen-vl-ocr",
    "messages": [{
        "role": "user",
        "content": [
            {"type": "image_url", "image_url": {
                "url": "data:image/png;base64," + base64.b64encode(image).decode("ascii")
            }},
            {"type": "text", "text": prompt},
        ],
    }],
    "stream": False,
    "max_tokens": 2048,
}
request = Request(
    os.environ["LLMTR_BASE_URL"].rstrip("/") + "/chat/completions",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": "Bearer " + os.environ["LLMTR_API_KEY"],
        "Content-Type": "application/json",
    },
    method="POST",
)
try:
    with urlopen(request, timeout=90) as response:
        envelope = json.load(response)
except HTTPError as error:
    raise RuntimeError(f"Gateway HTTP {error.code}") from None

choices = envelope.get("choices") or []
if not choices or choices[0].get("finish_reason") != "stop":
    raise ValueError("Tamamlanmamış çıktı; insan kontrolü gerekli.")
content = choices[0].get("message", {}).get("content") or ""
try:
    extracted = json.loads(content)
except json.JSONDecodeError:
    raise ValueError("Geçerli JSON yok; insan kontrolü gerekli.") from None
if not isinstance(extracted, dict) or not isinstance(extracted.get("lines"), list):
    raise ValueError("Beklenen nesne biçimi yok.")
print({"parsed": True, "review_required": True})

Satır eşleşmesini ve aritmetiği bağımsız denetleyin

Her ürün için adedi, birim fiyatı ve satır tutarını aynı görsel satırında karşılaştırın. Çok satırlı açıklama yeni ürün sayılmamalı; tekrar eden sayfa başlığı ürün listesine girmemelidir. Kırpma yapıyorsanız sütun başlıklarını koruyun ve her parçayı kaynak sayfa ile ilişkilendirin. İki kırpmada görünen aynı satırı iki kez toplamayın.

Örnekte 2 × 125,00 = 250,00 ve 3 × 50,00 = 150,00; satırlar 400,00 eder. Ardından 400,00 − 0,00 + 40,00 = 440,00 kontrol edilir. Gerçek belgede indirim ve verginin hangi aşamada uygulandığını okumadan bu denklemi zorlamayın. Sayı biçimini kesinleştirdikten sonra Decimal veya para biriminin alt birimini kullanın; yuvarlama kuralını açıkça tanımlayın.

İnsan incelemesini iş akışının son kapısı yapın

İnceleme ekranı özgün görseli, çıkarılan alanı ve hangi kontrolün başarısız olduğunu birlikte göstermelidir. Örneğin satır toplamı uyuşmazlığı ile okunamayan tarih farklı düzeltme gerektirir. Eksik alanı sessizce sıfır yapmayın; insan yeni bir görüntü isteyebilmeli veya belgeyi reddedebilmelidir.

Aritmetiğin tutması belgenin gerçekliğini, doğru tedarikçiye ait olduğunu veya daha önce işlenmediğini kanıtlamaz. Aynı belge numarası tek başına evrensel kimlik değildir. Mükerrer belge ve tedarikçi kontrollerini yetkili kayıtlarla ayrıca yapın. OCR çıktısından otomatik ödeme başlatmayın; veri inceleme onayı ile ödeme yetkisini ayrı tutun.

Sık sorulan sorular

JSON çıktısı alanların doğru olduğunu gösterir mi?

Hayır. Ayrıştırılabilir JSON yalnızca sözdizimini doğrular. Zorunlu alanlar, türler, satır eşleşmesi ve belgeye uygunluk ayrı kontrol edilir; null değerler inceleme gerektirebilir.

Okunamayan fiyat toplamdan bulunabilir mi?

Hesaplanmış aday üretilebilir, ancak OCR okuması olarak yazılamaz. İndirim, vergi veya ölçü birimi varsayımı yanlış olabilir; alan insan doğrulamasına kadar bilinmeyen kalmalıdır.

PDF dosyasını image_url içine koyabilir miyim?

Bu örnek gerçek PNG görüntüsü içindir. PDF'nin ilgili sayfasını görsele dönüştürün; PDF baytlarını PNG diye etiketlemeyin. Sayfa sırasını ve tablo devamlılığını koruyun.

İlgili yazılar