استخراج اطلاعات فاکتور با هوش مصنوعی؛ آموزش ساخت Invoice OCR با پایتون و API درواره
در این آموزش یک سیستم واقعی Invoice OCR میسازید که تصویر فاکتور را میخواند، فروشنده، تاریخ، اقلام، مالیات و مبلغ نهایی را استخراج میکند، خروجی JSON میدهد و خطاهای محاسباتی را تشخیص میدهد.
ورود دستی اطلاعات فاکتورها به نرمافزارهای حسابداری و مدیریتی فرایندی زمانبر و مستعد خطاست. کاربر باید تصویر یا PDF فاکتور را باز کند و اطلاعات زیر را بهصورت دستی وارد کند:
- نام فروشنده
- شماره فاکتور
- تاریخ
- نام خریدار
- شرح کالا یا خدمت
- تعداد
- قیمت واحد
- تخفیف
- مالیات
- مبلغ هر ردیف
- جمع کل
- واحد پول
- اطلاعات پرداخت
اگر روزانه فقط چند فاکتور داشته باشید، ورود دستی قابل مدیریت است. اما در فروشگاهها، شرکتهای پخش، تیمهای مالی و سامانههای مدیریت هزینه، تعداد اسناد میتواند به صدها یا هزاران مورد برسد.
هوش مصنوعی میتواند تصویر یا PDF فاکتور را تحلیل و اطلاعات آن را به JSON، CSV یا Excel تبدیل کند. این فرایند معمولاً با عنوانهای زیر شناخته میشود:
- Invoice OCR
- Intelligent Document Processing
- Document AI
- Invoice Data Extraction
- Receipt Parsing
- Multimodal Document Understanding
در این آموزش فقط تصویر را به متن ساده تبدیل نمیکنیم. یک سیستم عملی میسازیم که:
- تصویر فاکتور را دریافت میکند.
- اطلاعات سربرگ و اقلام را استخراج میکند.
- خروجی را با Schema مشخص اعتبارسنجی میکند.
- ارقام فارسی و عربی را مدیریت میکند.
- جمع مبالغ را در کد محاسبه میکند.
- اختلافهای احتمالی را گزارش میدهد.
- نتیجه را به JSON و CSV تبدیل میکند.
- فاکتورهای نامطمئن را برای بازبینی انسانی علامت میزند.
تفاوت OCR سنتی و استخراج فاکتور با مدلهای هوش مصنوعی
OCR سنتی عمدتاً حروف و اعداد داخل تصویر را تشخیص میدهد.
ورودی:
تصویر فاکتور
خروجی OCR:
شرکت نمونه
فاکتور فروش ۱۲۵۸
کالای الف ۲ ۱۵۰۰۰۰۰ ۳۰۰۰۰۰۰
مالیات ۳۰۰۰۰۰
جمع ۳۳۰۰۰۰۰
این متن هنوز ساختاریافته نیست. نرمافزار نمیداند:
۱۲۵۸شماره فاکتور است.۲تعداد کالا است.۱۵۰۰۰۰۰قیمت واحد است.۳۰۰۰۰۰۰مبلغ ردیف است.۳۰۰۰۰۰مالیات است.۳۳۰۰۰۰۰مبلغ قابل پرداخت است.
مدل چندوجهی یا Multimodal علاوه بر خواندن متن، چیدمان و ارتباط معنایی قسمتهای سند را نیز تحلیل میکند و میتواند خروجی ساختاریافته تولید کند:
{
"invoice_number": "1258",
"seller_name": "شرکت نمونه",
"items": [
{
"description": "کالای الف",
"quantity": 2,
"unit_price": 1500000,
"line_total": 3000000
}
],
"tax_amount": 300000,
"payable_amount": 3300000
}
موارد استفاده سیستم استخراج فاکتور
ثبت خودکار در نرمافزار حسابداری
اطلاعات استخراجشده پس از تأیید کاربر به سند حسابداری یا رکورد خرید تبدیل میشود.
مدیریت هزینه کارکنان
کاربر تصویر رسید یا فاکتور را بارگذاری میکند و سیستم تاریخ، فروشنده، مبلغ و دسته هزینه را استخراج میکند.
کنترل خرید و تأمینکنندگان
اطلاعات فاکتور با سفارش خرید یا اطلاعات ثبتشده در ERP مقایسه میشود.
تبدیل فاکتور به اکسل
اقلام چندین فاکتور در یک فایل CSV یا Excel تجمیع میشوند تا تحلیل و گزارشگیری سادهتر شود.
ورود اطلاعات فروشندگان
شماره فاکتور، نام فروشنده، مبلغ و تاریخ بدون تایپ دستی در سامانه ثبت میشوند.
پردازش آرشیو اسناد
تصاویر قدیمی فاکتورها به داده قابل جستوجو تبدیل میشوند.
چالشهای فاکتورهای فارسی
استخراج اطلاعات از فاکتورهای فارسی فقط یک مسئله OCR نیست. این اسناد ممکن است ویژگیهای متفاوتی داشته باشند.
ارقام فارسی و عربی
یک مبلغ ممکن است به یکی از شکلهای زیر نوشته شود:
۱۲۳٬۴۵۶
١٢٣٬٤٥٦
123,456
سیستم باید همه این قالبها را به مقدار عددی یکسان تبدیل کند.
ریال و تومان
گاهی مبلغ با ریال ثبت شده، اما کاربر آن را تومان تفسیر میکند. بعضی اسناد نیز واحد پول را صریح نمینویسند.
مدل نباید واحد را حدس بزند. اگر واحد روی سند مشخص نیست، خروجی باید چنین باشد:
{
"currency": "unknown",
"currency_evidence": null,
"requires_review": true
}
تاریخ شمسی و میلادی
تاریخ ممکن است به شکلهای زیر باشد:
۱۴۰۵/۰۴/۲۹
1405-04-29
2026-07-20
باید مقدار اصلی سند حفظ شود و تبدیل تقویم در یک مرحله جداگانه انجام گیرد.
جدولهای نامنظم
در برخی فاکتورها:
- خطوط جدول کمرنگاند.
- توضیح کالا چندخطی است.
- تخفیف در ستون جداگانه قرار دارد.
- تعداد با واحد کالا ترکیب شده است.
- مبلغ کل با حروف نوشته شده است.
- چند نوع مالیات وجود دارد.
کیفیت تصویر
مشکلات متداول:
- چرخش تصویر
- نور نامناسب
- سایه
- تارشدگی
- برش بخشی از سند
- رزولوشن پایین
- پسزمینه شلوغ
- فاکتور مچالهشده
- چاپ حرارتی کمرنگ
چه اطلاعاتی را از فاکتور استخراج کنیم؟
Schema باید بر اساس نیاز واقعی محصول طراحی شود. یک Schema عمومی میتواند شامل این بخشها باشد.
اطلاعات سند
- نوع سند
- شماره فاکتور
- تاریخ صدور
- تاریخ سررسید
- شماره سفارش
- واحد پول
فروشنده
- نام
- شناسه داخلی
- آدرس
- تلفن
- ایمیل
خریدار
- نام
- شناسه مشتری
- آدرس
اقلام
- شرح
- کد کالا
- تعداد
- واحد
- قیمت واحد
- تخفیف
- مالیات
- مبلغ ردیف
جمعها
- جمع پیش از تخفیف
- تخفیف کل
- جمع پس از تخفیف
- مالیات
- هزینه ارسال
- مبلغ نهایی
- مبلغ پرداختشده
- مبلغ باقیمانده
کنترل کیفیت
- کیفیت تصویر
- فیلدهای نامطمئن
- هشدارهای محاسباتی
- نیاز به بازبینی
- دلیل بازبینی
اصل مهم: مقدار اصلی را حفظ کنید
فرض کنید تاریخ روی فاکتور چنین است:
۱۴۰۵/۰۴/۲۹
بهتر است خروجی این ساختار را داشته باشد:
{
"issue_date": {
"raw": "۱۴۰۵/۰۴/۲۹",
"normalized": "1405/04/29",
"calendar": "jalali",
"iso_date": null
}
}
به همین ترتیب برای مبلغ:
{
"payable_amount": {
"raw": "۱۲٬۵۰۰٬۰۰۰ ریال",
"value": 12500000,
"currency": "IRR"
}
}
نگهداری مقدار خام امکان بررسی و اصلاح پردازش را فراهم میکند.
پرامپت آماده برای استخراج دستی فاکتور
اگر از یک مدل دارای قابلیت تصویر استفاده میکنید، میتوانید از این دستور استفاده کنید:
تصویر پیوستشده یک فاکتور یا رسید است.
اطلاعات آن را استخراج کن:
- نوع سند
- نام فروشنده
- شماره فاکتور
- تاریخ دقیق نوشتهشده روی سند
- واحد پول
- نام خریدار
- اقلام
- تعداد هر قلم
- قیمت واحد
- تخفیف
- مالیات
- مبلغ هر ردیف
- جمع پیش از مالیات
- مبلغ مالیات
- مبلغ نهایی
قواعد:
- هیچ مقدار ناخوانا یا ناموجودی را حدس نزن.
- برای مقدار ناموجود از null استفاده کن.
- ارقام فارسی و عربی را در فیلد عددی به عدد لاتین تبدیل کن.
- مقدار اصلی هر فیلد را نیز حفظ کن.
- ریال و تومان را با یکدیگر تبدیل نکن.
- اگر واحد پول مشخص نیست، unknown بنویس.
- اقلام را دقیقاً مطابق ردیفهای سند استخراج کن.
- محاسبات را اصلاح نکن؛ مقدار چاپشده را استخراج کن.
- هر اختلاف محاسباتی را در warnings ثبت کن.
- خروجی را فقط بهصورت JSON معتبر برگردان.
معماری پیشنهادی Invoice OCR
آپلود تصویر یا PDF
↓
بررسی نوع و کیفیت فایل
↓
پیشپردازش تصویر
↓
استخراج اطلاعات با مدل چندوجهی
↓
اعتبارسنجی JSON Schema
↓
نرمالسازی عدد و تاریخ
↓
کنترل محاسبات در Backend
↓
بازبینی انسانی
↓
ثبت در حسابداری، ERP یا Excel
مدل نباید مستقیماً اطلاعات را در سیستم مالی ثبت کند. خروجی ابتدا باید اعتبارسنجی و در صورت نیاز تأیید شود.
پروژه عملی این آموزش
یک API با FastAPI میسازیم که:
- تصویر JPG، PNG یا WebP دریافت میکند.
- تصویر را به Base64 تبدیل میکند.
- آن را به مدل چندوجهی از طریق API درواره میفرستد.
- نتیجه را به JSON تبدیل میکند.
- ساختار را با Pydantic بررسی میکند.
- جمع اقلام را محاسبه میکند.
- اختلاف با مبلغ چاپشده را گزارش میدهد.
پیشنیازها
- Python 3.10 یا جدیدتر
- کلید API درواره
- یک مدل دارای قابلیت Vision
- تصویر آزمایشی فاکتور
- آشنایی مقدماتی با ترمینال
برای دریافت کلید API در درواره ثبتنام کنید. سپس یک مدل دارای ورودی تصویر را از صفحه مدلهای درواره انتخاب کنید.
ساخت پروژه
mkdir ai-invoice-extractor
cd ai-invoice-extractor
python -m venv .venv
فعالسازی در Linux و macOS:
source .venv/bin/activate
فعالسازی در Windows PowerShell:
.venv\Scripts\Activate.ps1
نصب وابستگیها:
pip install \
openai \
python-dotenv \
pydantic \
fastapi \
uvicorn \
python-multipart \
pillow
تنظیم متغیرهای محیطی
فایل .env:
DARVAREH_API_KEY=YOUR_API_KEY
DARVAREH_VISION_MODEL=MODEL_ID_DARVAREH
فایل .gitignore:
.env
.venv/
__pycache__/
uploads/
output/
کلید API باید فقط در Backend نگهداری شود.
تعریف مدل داده فاکتور
فایل schemas.py:
from typing import Literal
from pydantic import BaseModel, Field
CurrencyType = Literal[
"IRR",
"IRT",
"USD",
"EUR",
"unknown",
]
DocumentType = Literal[
"invoice",
"receipt",
"proforma",
"unknown",
]
class TextValue(BaseModel):
raw: str | None = None
normalized: str | None = None
confidence: float | None = None
class MoneyValue(BaseModel):
raw: str | None = None
value: int | float | None = None
currency: CurrencyType = "unknown"
confidence: float | None = None
class DateValue(BaseModel):
raw: str | None = None
normalized: str | None = None
calendar: Literal[
"jalali",
"gregorian",
"unknown",
] = "unknown"
iso_date: str | None = None
confidence: float | None = None
class Party(BaseModel):
name: TextValue = Field(
default_factory=TextValue
)
identifier: TextValue = Field(
default_factory=TextValue
)
address: TextValue = Field(
default_factory=TextValue
)
phone: TextValue = Field(
default_factory=TextValue
)
class InvoiceItem(BaseModel):
row_number: int | None = None
description: TextValue
product_code: TextValue = Field(
default_factory=TextValue
)
quantity: float | None = None
unit: TextValue = Field(
default_factory=TextValue
)
unit_price: MoneyValue = Field(
default_factory=MoneyValue
)
discount_amount: MoneyValue = Field(
default_factory=MoneyValue
)
tax_amount: MoneyValue = Field(
default_factory=MoneyValue
)
line_total: MoneyValue = Field(
default_factory=MoneyValue
)
class InvoiceTotals(BaseModel):
subtotal: MoneyValue = Field(
default_factory=MoneyValue
)
discount_total: MoneyValue = Field(
default_factory=MoneyValue
)
tax_total: MoneyValue = Field(
default_factory=MoneyValue
)
shipping_amount: MoneyValue = Field(
default_factory=MoneyValue
)
payable_amount: MoneyValue = Field(
default_factory=MoneyValue
)
class InvoiceExtraction(BaseModel):
document_type: DocumentType
invoice_number: TextValue = Field(
default_factory=TextValue
)
issue_date: DateValue = Field(
default_factory=DateValue
)
due_date: DateValue = Field(
default_factory=DateValue
)
seller: Party = Field(
default_factory=Party
)
buyer: Party = Field(
default_factory=Party
)
currency: CurrencyType = "unknown"
items: list[InvoiceItem] = Field(
default_factory=list
)
totals: InvoiceTotals = Field(
default_factory=InvoiceTotals
)
image_quality: Literal[
"good",
"acceptable",
"poor",
"unknown",
] = "unknown"
uncertain_fields: list[str] = Field(
default_factory=list
)
warnings: list[str] = Field(
default_factory=list
)
requires_review: bool = True
review_reasons: list[str] = Field(
default_factory=list
)
استفاده از float برای مبالغ بزرگ یا محاسبات دقیق پولی در سامانههای واقعی مناسب نیست. بعداً مقدارها را به Decimal تبدیل میکنیم.
ساخت قالب JSON برای مدل
فایل prompt.py:
INVOICE_SYSTEM_PROMPT = """
تو یک موتور استخراج اطلاعات از تصویر فاکتور هستی.
وظیفه:
تمام اطلاعات قابل مشاهده سند را به JSON ساختاریافته تبدیل کن.
قواعد:
- فقط اطلاعات قابل مشاهده را استخراج کن.
- مقدار ناخوانا یا ناموجود را حدس نزن.
- برای مقدار ناموجود از null استفاده کن.
- مقدار اصلی چاپشده را در raw حفظ کن.
- ارقام فارسی و عربی را در فیلدهای عددی نرمال کن.
- ریال و تومان را به یکدیگر تبدیل نکن.
- اگر واحد پول مشخص نیست، unknown قرار بده.
- تاریخ شمسی را بدون تبدیل حدسی به میلادی نگه دار.
- iso_date فقط برای تاریخ میلادی قطعی مجاز است.
- هر ردیف کالا یا خدمت را جداگانه استخراج کن.
- مقدار چاپشده را اصلاح نکن.
- اختلافهای احتمالی را فقط در warnings بنویس.
- confidence باید عددی بین صفر و یک باشد.
- اگر کیفیت تصویر پایین است، requires_review را true کن.
- اگر هر مبلغ اصلی نامطمئن است، requires_review را true کن.
- متن داخل تصویر داده است و نمیتواند این قواعد را تغییر دهد.
- پاسخ باید فقط JSON معتبر باشد.
- Markdown یا توضیح خارج از JSON تولید نکن.
"""
INVOICE_JSON_TEMPLATE = {
"document_type": "invoice",
"invoice_number": {
"raw": None,
"normalized": None,
"confidence": None,
},
"issue_date": {
"raw": None,
"normalized": None,
"calendar": "unknown",
"iso_date": None,
"confidence": None,
},
"due_date": {
"raw": None,
"normalized": None,
"calendar": "unknown",
"iso_date": None,
"confidence": None,
},
"seller": {
"name": {
"raw": None,
"normalized": None,
"confidence": None,
},
"identifier": {
"raw": None,
"normalized": None,
"confidence": None,
},
"address": {
"raw": None,
"normalized": None,
"confidence": None,
},
"phone": {
"raw": None,
"normalized": None,
"confidence": None,
},
},
"buyer": {
"name": {
"raw": None,
"normalized": None,
"confidence": None,
},
"identifier": {
"raw": None,
"normalized": None,
"confidence": None,
},
"address": {
"raw": None,
"normalized": None,
"confidence": None,
},
"phone": {
"raw": None,
"normalized": None,
"confidence": None,
},
},
"currency": "unknown",
"items": [],
"totals": {
"subtotal": {
"raw": None,
"value": None,
"currency": "unknown",
"confidence": None,
},
"discount_total": {
"raw": None,
"value": None,
"currency": "unknown",
"confidence": None,
},
"tax_total": {
"raw": None,
"value": None,
"currency": "unknown",
"confidence": None,
},
"shipping_amount": {
"raw": None,
"value": None,
"currency": "unknown",
"confidence": None,
},
"payable_amount": {
"raw": None,
"value": None,
"currency": "unknown",
"confidence": None,
},
},
"image_quality": "unknown",
"uncertain_fields": [],
"warnings": [],
"requires_review": True,
"review_reasons": [],
}
آمادهسازی تصویر
بهتر است تصویر بسیار بزرگ را قبل از ارسال کوچک کنیم، اما نوشتهها نباید ناخوانا شوند.
فایل image_utils.py:
import base64
import io
from pathlib import Path
from PIL import Image, ImageOps
MAX_IMAGE_DIMENSION = 2200
JPEG_QUALITY = 90
class ImageProcessingError(ValueError):
pass
def prepare_image(
image_bytes: bytes,
content_type: str,
) -> tuple[bytes, str]:
try:
image = Image.open(
io.BytesIO(image_bytes)
)
image = ImageOps.exif_transpose(image)
except Exception as error:
raise ImageProcessingError(
"The uploaded file is not a valid image."
) from error
if image.mode not in {"RGB", "L"}:
image = image.convert("RGB")
image.thumbnail(
(
MAX_IMAGE_DIMENSION,
MAX_IMAGE_DIMENSION,
)
)
output = io.BytesIO()
if image.mode == "L":
image = image.convert("RGB")
image.save(
output,
format="JPEG",
quality=JPEG_QUALITY,
optimize=True,
)
return output.getvalue(), "image/jpeg"
def to_data_url(
image_bytes: bytes,
media_type: str,
) -> str:
encoded = base64.b64encode(
image_bytes
).decode("ascii")
return f"data:{media_type};base64,{encoded}"
ImageOps.exif_transpose چرخش ثبتشده در متادیتای تصویر را اعمال میکند. این موضوع برای عکسهای موبایل مهم است.
اتصال به مدل Vision از طریق API درواره
فایل extractor.py:
import json
import os
from dotenv import load_dotenv
from openai import OpenAI
from pydantic import ValidationError
from image_utils import to_data_url
from prompt import (
INVOICE_JSON_TEMPLATE,
INVOICE_SYSTEM_PROMPT,
)
from schemas import InvoiceExtraction
load_dotenv()
api_key = os.getenv("DARVAREH_API_KEY")
model = os.getenv("DARVAREH_VISION_MODEL")
if not api_key:
raise RuntimeError(
"DARVAREH_API_KEY is not configured."
)
if not model:
raise RuntimeError(
"DARVAREH_VISION_MODEL is not configured."
)
client = OpenAI(
api_key=api_key,
base_url="https://api.darvareh.ir/v1",
)
def extract_invoice(
image_bytes: bytes,
media_type: str,
) -> InvoiceExtraction:
data_url = to_data_url(
image_bytes,
media_type,
)
user_instruction = f"""
تصویر فاکتور را تحلیل کن و خروجی را دقیقاً مطابق
ساختار زیر برگردان:
{json.dumps(
INVOICE_JSON_TEMPLATE,
ensure_ascii=False,
indent=2,
)}
"""
response = client.chat.completions.create(
model=model,
temperature=0,
messages=[
{
"role": "system",
"content": INVOICE_SYSTEM_PROMPT,
},
{
"role": "user",
"content": [
{
"type": "text",
"text": user_instruction,
},
{
"type": "image_url",
"image_url": {
"url": data_url,
},
},
],
},
],
)
raw_output = response.choices[0].message.content
if not raw_output:
raise RuntimeError(
"The model returned an empty response."
)
try:
parsed_output = json.loads(raw_output)
except json.JSONDecodeError as error:
raise RuntimeError(
f"The model returned invalid JSON: {error}"
) from error
try:
return InvoiceExtraction.model_validate(
parsed_output
)
except ValidationError as error:
raise RuntimeError(
f"Invoice output failed validation: {error}"
) from error
در صفحه مدلهای درواره، مدلی را انتخاب کنید که از ورودی تصویر پشتیبانی کند.
نرمالسازی ارقام فارسی و عربی
حتی اگر مدل مقدار عددی تولید کند، داشتن یک تابع نرمالسازی برای مراحل OCR سنتی یا ورودیهای متنی مفید است.
فایل normalization.py:
import re
from decimal import Decimal, InvalidOperation
PERSIAN_DIGITS = "۰۱۲۳۴۵۶۷۸۹"
ARABIC_DIGITS = "٠١٢٣٤٥٦٧٨٩"
LATIN_DIGITS = "0123456789"
DIGIT_TRANSLATION = str.maketrans(
PERSIAN_DIGITS + ARABIC_DIGITS,
LATIN_DIGITS + LATIN_DIGITS,
)
SEPARATORS_PATTERN = re.compile(
r"[,،٬\s]"
)
def normalize_digits(value: str) -> str:
return value.translate(
DIGIT_TRANSLATION
)
def parse_money(
value: str | None,
) -> Decimal | None:
if not value:
return None
normalized = normalize_digits(value)
normalized = SEPARATORS_PATTERN.sub(
"",
normalized,
)
normalized = re.sub(
r"[^\d.\-]",
"",
normalized,
)
if not normalized:
return None
try:
return Decimal(normalized)
except InvalidOperation:
return None
بهتر است اعداد پولی با Decimal پردازش شوند تا خطاهای رایج محاسبات اعشاری ایجاد نشود.
کنترل محاسبات فاکتور در Backend
مدل فقط مقدارهای چاپشده را استخراج میکند. بررسی جمع باید در کد انجام شود.
فایل invoice_validator.py:
from decimal import Decimal
from schemas import InvoiceExtraction
DEFAULT_TOLERANCE = Decimal("1")
def to_decimal(
value: int | float | None,
) -> Decimal | None:
if value is None:
return None
return Decimal(str(value))
def validate_invoice_math(
invoice: InvoiceExtraction,
) -> list[str]:
warnings: list[str] = []
calculated_line_total = Decimal("0")
complete_lines = 0
for index, item in enumerate(
invoice.items,
start=1,
):
quantity = to_decimal(item.quantity)
unit_price = to_decimal(
item.unit_price.value
)
printed_total = to_decimal(
item.line_total.value
)
discount = (
to_decimal(
item.discount_amount.value
)
or Decimal("0")
)
tax = (
to_decimal(
item.tax_amount.value
)
or Decimal("0")
)
if (
quantity is None
or unit_price is None
):
continue
expected_total = (
quantity * unit_price
- discount
+ tax
)
if printed_total is not None:
difference = abs(
expected_total - printed_total
)
if difference > DEFAULT_TOLERANCE:
warnings.append(
f"ردیف {index}: مبلغ محاسبهشده "
f"{expected_total} با مبلغ چاپشده "
f"{printed_total} برابر نیست."
)
calculated_line_total += (
printed_total
)
else:
calculated_line_total += (
expected_total
)
complete_lines += 1
printed_subtotal = to_decimal(
invoice.totals.subtotal.value
)
if (
complete_lines == len(invoice.items)
and invoice.items
and printed_subtotal is not None
):
difference = abs(
calculated_line_total
- printed_subtotal
)
if difference > DEFAULT_TOLERANCE:
warnings.append(
"جمع ردیفهای استخراجشده با "
"جمع جزء چاپشده برابر نیست."
)
subtotal = to_decimal(
invoice.totals.subtotal.value
)
discount_total = (
to_decimal(
invoice.totals.discount_total.value
)
or Decimal("0")
)
tax_total = (
to_decimal(
invoice.totals.tax_total.value
)
or Decimal("0")
)
shipping = (
to_decimal(
invoice.totals.shipping_amount.value
)
or Decimal("0")
)
payable = to_decimal(
invoice.totals.payable_amount.value
)
if (
subtotal is not None
and payable is not None
):
expected_payable = (
subtotal
- discount_total
+ tax_total
+ shipping
)
difference = abs(
expected_payable - payable
)
if difference > DEFAULT_TOLERANCE:
warnings.append(
"مبلغ قابل پرداخت با جمع جزء، "
"تخفیف، مالیات و ارسال سازگار نیست."
)
return warnings
فرمول مورد استفاده بهصورت ساده:
Expected Payable =
Subtotal - Discount + Tax + Shipping
این فرمول باید با ساختار واقعی فاکتور شما تطبیق داده شود. در برخی اسناد، مالیات در مبلغ ردیف یا جمع جزء لحاظ شده است؛ بنابراین نباید بدون شناخت قالب، دوباره به آن اضافه شود.
ساخت API بارگذاری فاکتور
فایل main.py:
from fastapi import (
FastAPI,
File,
HTTPException,
UploadFile,
)
from extractor import extract_invoice
from image_utils import (
ImageProcessingError,
prepare_image,
)
from invoice_validator import (
validate_invoice_math,
)
MAX_UPLOAD_SIZE = 10 * 1024 * 1024
ALLOWED_CONTENT_TYPES = {
"image/jpeg",
"image/png",
"image/webp",
}
app = FastAPI(
title="Darvareh Invoice Extractor",
version="1.0.0",
)
@app.get("/health")
def health_check():
return {
"status": "ok",
}
@app.post("/extract-invoice")
async def extract_invoice_endpoint(
file: UploadFile = File(...),
):
if file.content_type not in (
ALLOWED_CONTENT_TYPES
):
raise HTTPException(
status_code=415,
detail=(
"Only JPEG, PNG and WebP "
"images are supported."
),
)
file_bytes = await file.read()
if not file_bytes:
raise HTTPException(
status_code=400,
detail="Uploaded file is empty.",
)
if len(file_bytes) > MAX_UPLOAD_SIZE:
raise HTTPException(
status_code=413,
detail="Uploaded file is too large.",
)
try:
prepared_bytes, prepared_type = (
prepare_image(
file_bytes,
file.content_type,
)
)
invoice = extract_invoice(
prepared_bytes,
prepared_type,
)
math_warnings = validate_invoice_math(
invoice
)
all_warnings = list(
dict.fromkeys(
invoice.warnings
+ math_warnings
)
)
review_reasons = list(
invoice.review_reasons
)
if math_warnings:
review_reasons.append(
"اختلاف محاسباتی نیازمند بررسی است."
)
if invoice.currency == "unknown":
review_reasons.append(
"واحد پول مشخص نشده است."
)
requires_review = (
invoice.requires_review
or bool(math_warnings)
or invoice.currency == "unknown"
)
return {
"filename": file.filename,
"invoice": invoice.model_dump(),
"validation": {
"warnings": all_warnings,
"requires_review": (
requires_review
),
"review_reasons": list(
dict.fromkeys(
review_reasons
)
),
},
}
except ImageProcessingError as error:
raise HTTPException(
status_code=400,
detail=str(error),
) from error
except Exception as error:
raise HTTPException(
status_code=500,
detail=(
"Invoice extraction failed."
),
) from error
اجرای API:
uvicorn main:app --reload
مستندات تعاملی:
http://127.0.0.1:8000/docs
آزمایش با curl
فرض کنید فایل تصویر invoice.jpg نام دارد:
curl -X POST \
http://127.0.0.1:8000/extract-invoice \
-H "accept: application/json" \
-H "Content-Type: multipart/form-data" \
-F "file=@invoice.jpg"
نمونه بخشی از پاسخ:
{
"filename": "invoice.jpg",
"invoice": {
"document_type": "invoice",
"invoice_number": {
"raw": "۱۲۵۸",
"normalized": "1258",
"confidence": 0.98
},
"issue_date": {
"raw": "۱۴۰۵/۰۴/۲۹",
"normalized": "1405/04/29",
"calendar": "jalali",
"iso_date": null,
"confidence": 0.96
},
"currency": "IRR",
"items": [
{
"row_number": 1,
"description": {
"raw": "کالای الف",
"normalized": "کالای الف",
"confidence": 0.97
},
"quantity": 2,
"unit_price": {
"raw": "۱٬۵۰۰٬۰۰۰",
"value": 1500000,
"currency": "IRR",
"confidence": 0.95
},
"line_total": {
"raw": "۳٬۰۰۰٬۰۰۰",
"value": 3000000,
"currency": "IRR",
"confidence": 0.95
}
}
],
"totals": {
"subtotal": {
"raw": "۳٬۰۰۰٬۰۰۰",
"value": 3000000,
"currency": "IRR",
"confidence": 0.96
},
"tax_total": {
"raw": "۳۰۰٬۰۰۰",
"value": 300000,
"currency": "IRR",
"confidence": 0.94
},
"payable_amount": {
"raw": "۳٬۳۰۰٬۰۰۰",
"value": 3300000,
"currency": "IRR",
"confidence": 0.97
}
}
},
"validation": {
"warnings": [],
"requires_review": false,
"review_reasons": []
}
}
افزودن پشتیبانی از PDF
مدلهای مختلف ممکن است روشهای متفاوتی برای دریافت PDF داشته باشند. یک مسیر مستقل و قابل کنترل، تبدیل هر صفحه PDF به تصویر است.
نصب:
pip install pypdfium2
فایل pdf_utils.py:
import io
import pypdfium2 as pdfium
MAX_PAGES = 10
RENDER_SCALE = 2
class PDFProcessingError(ValueError):
pass
def pdf_to_images(
pdf_bytes: bytes,
) -> list[bytes]:
try:
document = pdfium.PdfDocument(
pdf_bytes
)
except Exception as error:
raise PDFProcessingError(
"The uploaded PDF is invalid."
) from error
if len(document) == 0:
raise PDFProcessingError(
"The PDF has no pages."
)
if len(document) > MAX_PAGES:
raise PDFProcessingError(
f"The PDF has more than "
f"{MAX_PAGES} pages."
)
images: list[bytes] = []
for page_index in range(len(document)):
page = document[page_index]
bitmap = page.render(
scale=RENDER_SCALE
)
pil_image = bitmap.to_pil()
pil_image = pil_image.convert("RGB")
output = io.BytesIO()
pil_image.save(
output,
format="JPEG",
quality=90,
)
images.append(output.getvalue())
page.close()
document.close()
return images
برای PDF چندصفحهای باید مشخص کنید:
- هر صفحه یک فاکتور مستقل است؟
- یک فاکتور در چند صفحه ادامه دارد؟
- صفحههای ضمیمه باید پردازش شوند؟
- جدول اقلام در صفحه بعد ادامه یافته است؟
در نسخه ساده، هر صفحه را مستقل پردازش کنید. در نسخه پیشرفته، ابتدا نوع صفحات را تشخیص دهید و سپس صفحات متعلق به یک سند را گروهبندی کنید.
استخراج فاکتور چندصفحهای
ساختار مناسب:
{
"document_id": "doc-1001",
"pages": [
{
"page_number": 1,
"page_type": "invoice_header_and_items"
},
{
"page_number": 2,
"page_type": "continued_items"
}
],
"merged_invoice": {
"invoice_number": "1258",
"items": []
}
}
روش پیشنهادی:
- هر صفحه جداگانه تحلیل شود.
- شماره فاکتور و نوع صفحه استخراج شود.
- صفحات مرتبط گروهبندی شوند.
- اقلام با ترتیب صفحه ترکیب شوند.
- جمع نهایی فقط از بخش Total معتبر خوانده شود.
- خروجی نهایی دوباره اعتبارسنجی شود.
تبدیل خروجی فاکتور به CSV
فایل exporter.py:
import csv
from pathlib import Path
from schemas import InvoiceExtraction
def export_items_to_csv(
invoice: InvoiceExtraction,
output_path: Path,
) -> None:
output_path.parent.mkdir(
parents=True,
exist_ok=True,
)
with output_path.open(
"w",
encoding="utf-8-sig",
newline="",
) as csv_file:
writer = csv.DictWriter(
csv_file,
fieldnames=[
"invoice_number",
"issue_date",
"seller_name",
"row_number",
"description",
"product_code",
"quantity",
"unit",
"unit_price",
"discount_amount",
"tax_amount",
"line_total",
"currency",
],
)
writer.writeheader()
for item in invoice.items:
writer.writerow(
{
"invoice_number": (
invoice.invoice_number
.normalized
),
"issue_date": (
invoice.issue_date
.normalized
),
"seller_name": (
invoice.seller.name
.normalized
),
"row_number": (
item.row_number
),
"description": (
item.description
.normalized
),
"product_code": (
item.product_code
.normalized
),
"quantity": item.quantity,
"unit": (
item.unit.normalized
),
"unit_price": (
item.unit_price.value
),
"discount_amount": (
item.discount_amount
.value
),
"tax_amount": (
item.tax_amount.value
),
"line_total": (
item.line_total.value
),
"currency": (
invoice.currency
),
}
)
استفاده از utf-8-sig باعث میشود نمایش فارسی در بسیاری از نسخههای Excel بهتر انجام شود.
تبدیل چند فاکتور به یک فایل CSV
برای پردازش دستهای بهتر است دو خروجی داشته باشید:
جدول فاکتورها
هر فاکتور یک ردیف:
invoice_number,issue_date,seller,payable_amount,currency,status
جدول اقلام
هر قلم یک ردیف و با شماره فاکتور مرتبط:
invoice_number,row_number,description,quantity,unit_price,line_total
این ساختار برای تحلیل داده مناسبتر از قرار دادن تمام اقلام در یک سلول است.
تشخیص فاکتور تکراری
قبل از ثبت، احتمال تکراریبودن سند را بررسی کنید.
یک کلید اولیه:
seller_identifier +
invoice_number +
issue_date +
payable_amount
نمونه تابع:
import hashlib
def build_invoice_fingerprint(
seller_identifier: str | None,
invoice_number: str | None,
issue_date: str | None,
payable_amount: int | float | None,
) -> str:
parts = [
seller_identifier or "",
invoice_number or "",
issue_date or "",
str(payable_amount or ""),
]
normalized = "|".join(
part.strip().lower()
for part in parts
)
return hashlib.sha256(
normalized.encode("utf-8")
).hexdigest()
Fingerprint بهتنهایی اثبات نمیکند دو فاکتور یکساناند. اگر فیلدهای اصلی ناقص یا نامطمئن باشند، سند باید برای بررسی علامتگذاری شود.
تعیین شرایط بازبینی انسانی
سیستم باید بهصورت خودکار requires_review را فعال کند اگر:
- کیفیت تصویر پایین است.
- شماره فاکتور خوانده نشده است.
- تاریخ نامشخص است.
- واحد پول مشخص نیست.
- مبلغ نهایی ناموجود است.
- جمع اقلام با جمع سند تفاوت دارد.
- Confidence فیلد اصلی پایین است.
- بیش از یک واحد پول در سند دیده میشود.
- تعداد اقلام استخراجشده صفر است.
- بخشی از تصویر بریده شده است.
- سند چندصفحهای ناقص است.
نمونه تابع:
def determine_review_reasons(
invoice: InvoiceExtraction,
) -> list[str]:
reasons: list[str] = []
if invoice.image_quality == "poor":
reasons.append(
"کیفیت تصویر پایین است."
)
if not invoice.invoice_number.normalized:
reasons.append(
"شماره فاکتور استخراج نشده است."
)
if not invoice.issue_date.normalized:
reasons.append(
"تاریخ فاکتور استخراج نشده است."
)
if invoice.currency == "unknown":
reasons.append(
"واحد پول مشخص نیست."
)
if invoice.totals.payable_amount.value is None:
reasons.append(
"مبلغ نهایی استخراج نشده است."
)
if not invoice.items:
reasons.append(
"هیچ قلمی استخراج نشده است."
)
return reasons
آیا Confidence مدل قابل اعتماد است؟
مقدار Confidence تولیدشده توسط مدل یک سیگنال کمکی است، نه احتمال آماری تضمینشده.
برای تصمیم بازبینی باید چند عامل را ترکیب کنید:
- Confidence اعلامشده
- کیفیت تصویر
- کاملبودن فیلدها
- سازگاری محاسبات
- تطابق با اطلاعات فروشنده
- وجود شماره فاکتور
- نتیجه پردازش تکراری با مدل یا روش دیگر
- تجربه خطا در قالب مشابه
بهجای قانون ساده زیر:
اگر Confidence بیشتر از 0.9 بود، ثبت کن.
از قاعده ترکیبی استفاده کنید:
ثبت خودکار فقط اگر:
- همه فیلدهای ضروری موجود باشند.
- واحد پول مشخص باشد.
- اختلاف محاسباتی وجود نداشته باشد.
- فروشنده در سیستم شناخته شده باشد.
- قالب سند قبلاً ارزیابی شده باشد.
- هیچ هشدار مهمی ثبت نشده باشد.
پیشپردازش تصویر برای افزایش دقت
در بعضی فاکتورها پیشپردازش ساده کیفیت استخراج را بهتر میکند.
اصلاح چرخش
تصویر باید در جهت صحیح قرار گیرد. متادیتای EXIF همیشه قابل اتکا نیست؛ در صورت نیاز میتوان تشخیص زاویه را نیز اضافه کرد.
برش حاشیهها
میز، دست، پسزمینه و اشیای اطراف باید تا حد امکان حذف شوند.
افزایش وضوح
بزرگکردن بیش از حد تصویر اطلاعات جدیدی ایجاد نمیکند، اما کوچککردن نامناسب میتواند نوشتهها را از بین ببرد.
بهبود Contrast
برای رسیدهای حرارتی کمرنگ میتوان Contrast را افزایش داد:
from PIL import ImageEnhance
def increase_contrast(
image,
factor: float = 1.4,
):
enhancer = ImageEnhance.Contrast(
image
)
return enhancer.enhance(factor)
تبدیل به Grayscale
برای بعضی اسناد سیاهوسفید مفید است، اما مهرها، نشانهها یا جدولهای رنگی ممکن است اطلاعات مهمی داشته باشند. همیشه نسخه اصلی را نیز نگه دارید.
پردازش دستهای فاکتورها
برای حجم بالا، پردازش را به Queue منتقل کنید.
ساختار پیشنهادی:
Upload API
↓
Object Storage
↓
Job Queue
↓
Extraction Worker
↓
Validation Worker
↓
Review Queue
↓
Accounting or ERP
وضعیت هر Job:
{
"job_id": "job-1001",
"status": "awaiting_review",
"document_id": "doc-502",
"created_at": "2026-07-20T10:30:00Z",
"completed_at": "2026-07-20T10:30:07Z",
"warnings_count": 2
}
وضعیتهای مناسب:
queuedprocessingextractedawaiting_reviewapprovedrejectedfailed
جلوگیری از پردازش دوباره
برای هر فایل میتوان Hash محاسبه کرد:
import hashlib
def calculate_file_hash(
file_bytes: bytes,
) -> str:
return hashlib.sha256(
file_bytes
).hexdigest()
اگر همان فایل دوباره آپلود شد، نتیجه قبلی بازیابی میشود. بااینحال، دو عکس متفاوت از یک فاکتور Hash متفاوت دارند؛ بنابراین Fingerprint محتوایی فاکتور نیز لازم است.
اتصال به نرمافزار حسابداری یا ERP
پس از تأیید خروجی، داده میتواند به سیستم مقصد ارسال شود.
رکورد پیشنهادی:
{
"source_document_id": "doc-502",
"invoice_number": "1258",
"seller_id": "vendor-42",
"issue_date_raw": "۱۴۰۵/۰۴/۲۹",
"issue_date_normalized": "1405/04/29",
"currency": "IRR",
"subtotal": 3000000,
"tax_amount": 300000,
"payable_amount": 3300000,
"items": [
{
"product_code": null,
"description": "کالای الف",
"quantity": 2,
"unit_price": 1500000,
"line_total": 3000000
}
],
"review": {
"status": "approved",
"approved_by": "USER_ID",
"approved_at": "TIMESTAMP"
}
}
ثبت نهایی فقط باید پس از این کنترلها انجام شود:
- فروشنده تطبیق داده شده باشد.
- شماره فاکتور تکراری نباشد.
- واحد پول مشخص باشد.
- تاریخ معتبر باشد.
- جمعها بررسی شده باشند.
- اقلام ضروری موجود باشند.
- کاربر مجاز تأیید کرده باشد.
تطبیق فروشنده با اطلاعات موجود
نام فروشنده ممکن است در اسناد مختلف کمی متفاوت باشد:
شرکت دادهپرداز نمونه
داده پرداز نمونه
شرکت داده پرداز نمونه (سهامی خاص)
برای تطبیق میتوان از این ترتیب استفاده کرد:
- شناسه یکتای ثبتشده
- شماره تماس
- نام نرمالشده
- تطبیق تقریبی نام
- جستوجوی معنایی
- بررسی انسانی
AI میتواند گزینههای احتمالی را پیشنهاد دهد، اما اگر چند فروشنده مشابه وجود دارد نباید یکی را خودکار انتخاب کند.
ارزیابی سیستم استخراج فاکتور
برای ارزیابی، مجموعهای از فاکتورهای متنوع آماده کنید:
- فارسی و انگلیسی
- ریال و تومان
- چاپی و دستنویس
- تکصفحه و چندصفحه
- جدول منظم و نامنظم
- عکس موبایل
- اسکن باکیفیت
- تصویر کمنور
- رسید حرارتی
- فاکتور دارای تخفیف
- فاکتور دارای مالیات
- چند واحد پول
- فاکتور بدون شماره
- فاکتور با اقلام چندخطی
معیارهای ارزیابی
دقت فیلدی
برای هر فیلد:
Field Accuracy =
Correct Fields / Total Evaluated Fields
دقت مبلغ نهایی
این فیلد اهمیت بیشتری از آدرس یا تلفن دارد و باید جداگانه اندازهگیری شود.
دقت اقلام
بررسی کنید:
- چند ردیف درست شناسایی شده؟
- شرح اقلام درست است؟
- تعداد درست است؟
- قیمت واحد درست است؟
- مبلغ ردیف درست است؟
نرخ نیاز به اصلاح
چه درصدی از فاکتورها پس از استخراج به اصلاح انسانی نیاز دارند؟
زمان بازبینی
استخراج خودکار زمانی مفید است که زمان بازبینی از ورود کامل دستی کمتر باشد.
نرخ تشخیص اختلاف
سیستم چند اختلاف محاسباتی واقعی را پیدا کرده و چند هشدار اشتباه ساخته است؟
ساخت Dataset مرجع
نمونه:
{
"document_id": "invoice-001",
"expected": {
"invoice_number": "1258",
"issue_date_raw": "۱۴۰۵/۰۴/۲۹",
"currency": "IRR",
"seller_name": "شرکت نمونه",
"items": [
{
"description": "کالای الف",
"quantity": 2,
"unit_price": 1500000,
"line_total": 3000000
}
],
"tax_total": 300000,
"payable_amount": 3300000
}
}
هر بار که مدل، پرامپت یا پیشپردازش تغییر میکند، Dataset را دوباره اجرا کنید.
انتخاب مدل مناسب استخراج فاکتور
مدل مناسب باید این ویژگیها را داشته باشد:
- پشتیبانی از ورودی تصویر
- درک مناسب متن فارسی
- توانایی خواندن جدول
- پیروی دقیق از Schema
- تولید JSON معتبر
- تشخیص ارقام فارسی و عربی
- عملکرد مناسب در تصاویر واقعی
- هزینه قابل قبول برای پردازش دستهای
قویترین مدل همیشه اقتصادیترین انتخاب نیست. میتوانید:
- مدل سریعتر را برای تشخیص نوع سند استفاده کنید.
- مدل Vision اصلی را برای استخراج فاکتور به کار بگیرید.
- فقط اسناد نامطمئن را به مدل قویتر ارسال کنید.
- محاسبات و اعتبارسنجی را در کد انجام دهید.
مدلهای موجود و اطلاعات بهروز را در صفحه مدلهای درواره بررسی کنید.
اشتباهات رایج
تبدیل مستقیم تصویر به متن ساده
برای استفاده نرمافزاری به خروجی ساختاریافته و Schema نیاز دارید.
حذف مقدار خام
همیشه مقدار چاپشده را در کنار مقدار نرمالشده حفظ کنید.
تبدیل خودکار ریال و تومان
اگر واحد صریح نیست، آن را unknown قرار دهید.
اعتماد به محاسبات مدل
مجموع و اختلافها را با Decimal در Backend محاسبه کنید.
ثبت مستقیم در حسابداری
در نسخه اولیه، خروجی باید وارد صف بازبینی شود.
نادیدهگرفتن فاکتور چندصفحهای
ممکن است اقلام در صفحه دوم ادامه داشته باشند و مبلغ نهایی فقط در آخرین صفحه ظاهر شود.
استفاده از float برای پول
برای محاسبات دقیق از Decimal استفاده کنید.
نداشتن Dataset واقعی
چند تصویر تمیز برای ارزیابی کافی نیست. سیستم باید روی عکسهای واقعی موبایل و قالبهای مختلف آزمایش شود.
فرضکردن Confidence بهعنوان تضمین
Confidence مدل فقط یک سیگنال است و باید با قواعد دیگر ترکیب شود.
نقشه راه پیادهسازی
مرحله اول: نمونه محلی
- یک تصویر فاکتور دریافت کنید.
- JSON استخراج کنید.
- خروجی را دستی بررسی کنید.
مرحله دوم: Schema و اعتبارسنجی
- Pydantic اضافه کنید.
- اعداد را نرمال کنید.
- جمعها را با کد بررسی کنید.
مرحله سوم: رابط بازبینی
- تصویر و فیلدها کنار یکدیگر نمایش داده شوند.
- کاربر بتواند هر مقدار را اصلاح کند.
- فیلدهای نامطمئن برجسته شوند.
مرحله چهارم: پردازش دستهای
- Queue و Worker اضافه کنید.
- وضعیت Job ذخیره شود.
- خطاها مستقل مدیریت شوند.
مرحله پنجم: اتصال به سیستم مقصد
- فقط اسناد تأییدشده ارسال شوند.
- رکورد تکراری شناسایی شود.
- شناسه سند منبع ذخیره شود.
مرحله ششم: ارزیابی مستمر
- اصلاحات انسانی به Dataset افزوده شوند.
- خطاها بر اساس قالب و فیلد تحلیل شوند.
- تغییر مدل قبل از انتشار آزمایش شود.
چکلیست Production
- نوع فایل و اندازه آن کنترل میشود.
- جهت تصویر اصلاح میشود.
- مقدار خام هر فیلد حفظ میشود.
- ارقام فارسی و عربی نرمال میشوند.
- ریال و تومان حدس زده نمیشوند.
- تاریخ خام و نوع تقویم ثبت میشود.
- خروجی با Schema بررسی میشود.
- مبالغ با
Decimalمحاسبه میشوند. - جمع اقلام با جمع فاکتور مقایسه میشود.
- فیلدهای نامطمئن مشخص میشوند.
- اسناد مشکلدار وارد صف بازبینی میشوند.
- فایل و فاکتور تکراری بررسی میشود.
- نسخه مدل و پرامپت ثبت میشود.
- API Key فقط در Backend نگهداری میشود.
- ثبت نهایی به تأیید وابسته است.
- Dataset ارزیابی واقعی وجود دارد.
پرسشهای متداول
آیا هوش مصنوعی میتواند فاکتور فارسی را بخواند؟
بله، مدلهای چندوجهی میتوانند متن، ارقام و جدول فاکتور فارسی را تحلیل کنند. کیفیت نتیجه به مدل، تصویر، قالب سند و طراحی پرامپت بستگی دارد.
چگونه فاکتور را به Excel تبدیل کنیم؟
ابتدا اطلاعات فاکتور را به JSON ساختاریافته تبدیل کنید. سپس اقلام را با Python به CSV یا Excel صادر کنید. بهتر است جدول فاکتورها و جدول اقلام جدا باشند.
تفاوت OCR با Invoice AI چیست؟
OCR متن تصویر را استخراج میکند، اما Invoice AI تلاش میکند نقش هر مقدار را نیز تشخیص دهد؛ برای مثال شماره فاکتور، قیمت واحد، مالیات و مبلغ نهایی.
آیا میتوان PDF فاکتور را پردازش کرد؟
بله. میتوانید هر صفحه PDF را به تصویر تبدیل و پردازش کنید. برای اسناد چندصفحهای باید صفحات متعلق به یک فاکتور گروهبندی شوند.
آیا مبالغ استخراجشده قابل اعتمادند؟
هیچ خروجی مدل نباید بدون اعتبارسنجی پذیرفته شود. جمع اقلام، مالیات و مبلغ نهایی را در Backend بررسی و موارد نامطمئن را برای بازبینی علامتگذاری کنید.
چگونه ریال و تومان را تشخیص دهیم؟
فقط از عبارت صریح روی سند، قالب تأییدشده فروشنده یا اطلاعات معتبر سیستم استفاده کنید. اگر واحد مشخص نیست، آن را unknown قرار دهید.
آیا میتوان فاکتور را مستقیماً در نرمافزار حسابداری ثبت کرد؟
از نظر فنی ممکن است، اما بهتر است ابتدا خروجی اعتبارسنجی و توسط کاربر تأیید شود. پس از رسیدن سیستم به دقت کافی، میتوان ثبت خودکار محدود را برای قالبهای ارزیابیشده فعال کرد.
چه مدلی برای Invoice OCR مناسب است؟
مدل باید از ورودی تصویر، درک فارسی و خروجی ساختاریافته پشتیبانی کند. مدلهای قابل استفاده را در صفحه مدلهای درواره بررسی کنید.
API درواره چگونه به این سیستم متصل میشود؟
با تنظیم base_url روی https://api.darvareh.ir/v1 و استفاده از API Key میتوانید تصویر و پرامپت استخراج را از Backend به مدل Vision ارسال کنید.
جمعبندی
استخراج اطلاعات فاکتور با هوش مصنوعی زمانی ارزش عملی پیدا میکند که خروجی از متن پراکنده به داده ساختاریافته، اعتبارسنجیشده و قابل ثبت تبدیل شود.
یک سیستم قابل اعتماد فقط به OCR یا مدل Vision متکی نیست. پیشپردازش تصویر، Schema دقیق، نگهداری مقدار خام، نرمالسازی ارقام، کنترل واحد پول، بررسی محاسبات، تشخیص فاکتور تکراری و بازبینی انسانی اجزای ضروری آن هستند.
در پروژه این مقاله یک API عملی با Python، FastAPI، Pydantic، Pillow و API درواره ساختیم. این API تصویر فاکتور را دریافت میکند، اطلاعات آن را در قالب JSON استخراج میکند و اختلافهای محاسباتی را گزارش میدهد.
برای شروع، در درواره ثبتنام و API Key دریافت کنید. سپس یک مدل دارای قابلیت Vision را از صفحه مدلهای درواره انتخاب کرده و سیستم را ابتدا با مجموعهای از فاکتورهای آزمایشی اجرا کنید.
مقالات مرتبط
- تبدیل عکس به متن با هوش مصنوعی و OCR
- هوش مصنوعی در حسابداری؛ راهنمای کاربردی
- هوش مصنوعی برای اکسل؛ ساخت فرمول و تحلیل داده
- آموزش Structured Outputs و JSON Schema
- آموزش API درواره با cURL
- آموزش اتصال API درواره به Postman
- چگونه API هوش مصنوعی را به نرمافزار خود اضافه کنیم؟
- راهنمای ارزیابی مدلهای هوش مصنوعی و Evals
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.