Нервсіз Карго-Трекер: Қытай жүкқұжаттарын (1688 / Taobao / WeChat) фотодан Document AI + Gemini 2.5 Flash арқылы парсинг жасау
«Брат, жүк Қорғаста!» — логисттің WhatsApp-ына WeChat-тан жүктелген, иероглифі бар 15 мыжылған скриншот, SF Express-тің кірлеген жүкқұжаттарының фотолары және термопринтерден шыққан, жартысы өшіп қалған штрих-кодтар түседі. Қазақстандағы карго-бизнес — бұл миллиардтаған теңге айналымы бар, бірақ операциялық жағынан тас ғасырында қалған сала. Барахолка мен Баян-Ауылдағы жүздеген логистер күн сайын мониторға телміріп, 4-5 сағат бойы қытайлық трек-кодтарды, салмақ пен клиент белгілерін (мысалы, «ALM-882») Excel-дің алып кестелеріне қолмен терумен әлек.
Қолмен енгізудің мәселесі — тек уақыт жоғалту ғана емес. Бұл — жоғалған сәлемдемелер. ZTO Express-тің 15 таңбалы кодында бір сан қате кетсе болды — iPhone тыстары бар қорап Алматының орнына Шымкентке кетіп қалады, ал ашулы клиент телефонды үзбей соғады. Классикалық мәтін тану жүйелері (OCR) бұл жерде дәрменсіз: олар байерлердің сленгтік қысқартуларын түсінбейді, иероглифтерді ұқсас сандармен шатастырады және фото сәл қисық болса, қоқыс деректер береді. Бұл ауыртпалықты шешу үшін біз OZAT-та гибридті AI-конвейер әзірледік. Ол тіпті ең «өлген» қытай жүкқұжаттарынан 2 секунд ішінде мінсіз деректерді суырып алады.
Неліктен бұл Қазақстанның e-commerce саласы үшін үлкен мәселе?
- • Форматтар хаосы: Қытайлық курьерлік қызметтердің (SF Express, YTO, ZTO, STO, Yunda) жүкқұжаттарының (快递单) ондаған түрлі шаблондары бар. Трек-нөмірді регуляркамен (RegEx) іздеу пайдасыз — ол үнемі әр жерде тұрады.
- • Бастапқы фото сапасы: Taobao мен 1688-ден алынған скриншоттар WeChat арқылы қысылып, сосын WhatsApp-қа жіберілгенде пиксельді ботқаға айналады.
- • Тілдік кедергі: Кедендік ресімдеу үшін тауар санатын түсіну үшін логист қытай тіліндегі сипаттаманы (мысалы, «女装夏季连衣裙») аударуы керек. Google Translate Excel-дегі мыңдаған жолдарға масштабталмайды.
- • Карго-компаниялардың белгілері: Әр клиенттің өз коды бар (мысалы, KZ-01-A немесе Bishkek-99). Байерлер оны штрих-кодтың үстінен тікелей маркермен жаза салады.
Біз түсіндік: бізге суретті тірі қытайтанушы-логист сияқты оқитын, бірақ машинаның жылдамдығымен жұмыс істейтін жүйе қажет.
1. Архитектура: Document AI және Gemini 2.5 Flash тандемі
Көптеген адамдар бұл тапсырманы суретті мультимодальды (Vision) LLM-ге лақтыра салу арқылы шешуге тырысады. Бірақ мәтін тым ұсақ және фото мыжылған болса, LLM жиі галлюцинацияға ұшырап, цифрларды шатастырады (мысалы, '8' және 'B'). Дұрыс Enterprise-тәсіл — бұл гибридті пайплайн:
- 1-қадам: Google Cloud Document AI (OCR Processor). Бұл мамандандырылған оптикалық модель. Ол «ойлануға» тырыспайды, тіпті өте нашар скандар мен термочектерден шикі мәтінді, координаттарды және иероглифтерді тамаша шығарып алады.
- 2-қадам: Gemini 2.5 Flash (Semantic Parser). Нейрожелі осы шикі мәтіннің «ақпаратын» қабылдайды, контекстті талдайды, қай жерде трек-код, қай жерде салмақ тұрғанын табады, қытайша сипаттаманы орыс/қазақ тіліне аударады және бәрін әдемі JSON етіп жинайды.
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ CARGO TRACKER AI PIPELINE (OZAT HUB) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
[ Кладовщик на складе в Гуанчжоу / Иу ]
│ (Скидывает мятую накладную SF Express или скрин из WeChat)
▼
┌─────────────────────────────────────────────────────────┐
│ Telegram / WeChat Webhook Endpoint (FastAPI) │
└────────────────────────────┬────────────────────────────┘
│ Ужатое фото (JPEG)
▼
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ЭТАП 1: Google Cloud Document AI (Pretrained OCR Processor) │
│ • Вытягивает плотный китайский текст, цифры и иероглифы │
│ • Устойчив к перекосам (Skew), теням, размытию и мелкому шрифту │
└────────────────────────────┬──────────────────────────────────────────────────────────────────────────────┘
│
▼ (Сырой текст с артефактами: "运单号882371 KZ-09...")
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ЭТАП 2: Gemini 2.5 Flash (Semantic Extraction) │
│ • Распознает контекст: где трек-код, где телефон, где код клиента │
│ • Переводит описание ("手机壳" -> "Чехлы для телефона") │
│ • Возвращает строгий JSON │
└────────────────────────────┬──────────────────────────────────────────────────────────────────────────────┘
│
▼ { "tracking": "882...", "client": "KZ-09", "weight": 2.5 }
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Firestore Database & Web Frontend │
│ • Статус: "Поступило на склад в Китае" │
│ • Клиенту отправляется Push / SMS уведомление │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────┘2. 1-листинг: Python-дағы парсинг конвейері (Document AI + Gemini)
Төменде сервистің коды берілген. Fallback паттерніне назар аударыңыз: егер Document AI кенеттен мәтін таппаса (мысалы, кескіннің ерекше форматына байланысты), біз кескін байттарын Gemini 2.5 Flash-тің кіріктірілген Vision мүмкіндіктерін пайдаланып тікелей жібереміз.
import os
import json
import logging
from typing import Dict, Any, Optional
from google.api_core.client_options import ClientOptions
from google.cloud import documentai
from google import genai
from google.genai import types
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Настройки Document AI (OCR)
PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT")
LOCATION = os.environ.get("DOCAI_LOCATION", "eu")
PROCESSOR_ID = os.environ.get("DOCAI_PROCESSOR_ID")
# Инициализация клиентов
docai_client = documentai.DocumentProcessorServiceClient(
client_options=ClientOptions(api_endpoint=f"{LOCATION}-documentai.googleapis.com")
)
ai_client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))
SYSTEM_PROMPT = """Ты — экспертный логистический ассистент карго-компании (Казахстан-Китай).
На вход поступает сырой распознанный текст (OCR) с китайской накладной (快递单) от курьеров ZTO, YTO, SF Express, STO или скриншот из 1688/Taobao/WeChat.
Твоя задача — извлечь следующие данные, игнорируя визуальный мусор и нерелевантный текст:
1. Идентификатор/Код клиента (часто содержит латиницу и цифры, например "ALM-882", "KZ-01-M", "K89").
2. Трек-номер посылки (快递单号 / 运单号) — обычно длинный цифровой или буквенно-цифровой код (12-15 символов).
3. Курьерская служба (ZTO/中通, YTO/圆通, SF/顺丰 и т.д.).
4. Вес в кг (重量), если указан.
5. Описание товара (переведи с китайского на русский).
Верни строгий JSON:
{
"client_code": "string или null",
"tracking_number": "string",
"courier": "string",
"weight_kg": number или null,
"description_ru": "string"
}"""
async def process_cargo_waybill(image_bytes: bytes, mime_type: str = "image/jpeg") -> Dict[str, Any]:
"""
Двухэтапный парсинг:
1. Document AI для высокоточного OCR мелкого/мятого китайского текста.
2. Gemini 2.5 Flash для семантического извлечения сущностей из текста OCR.
"""
# ЭТАП 1: OCR через Document AI (Pretrained OCR Processor)
name = docai_client.processor_path(PROJECT_ID, LOCATION, PROCESSOR_ID)
raw_document = documentai.RawDocument(content=image_bytes, mime_type=mime_type)
request = documentai.ProcessRequest(name=name, raw_document=raw_document)
logger.info("Отправка в Document AI...")
result = docai_client.process_document(request=request)
document = result.document
raw_text = document.text
logger.info(f"Document AI извлек {len(raw_text)} символов текста.")
# Если OCR не нашел текст, пробуем отдать картинку напрямую в Gemini (Fallback)
if len(raw_text.strip()) < 10:
logger.warning("Document AI не нашел текст. Используем Gemini Multimodal Fallback.")
prompt_content = [
types.Part.from_bytes(data=image_bytes, mime_type=mime_type),
"Проанализируй накладную и верни JSON."
]
else:
# ЭТАП 2: Gemini 2.5 Flash для структурирования
logger.info("Передача сырого текста в Gemini 2.5 Flash...")
prompt_content = [
f"Вот сырой текст с накладной, полученный через OCR:\n\n{raw_text}\n\n",
"Извлеки из него данные и верни JSON согласно системной инструкции."
]
response = ai_client.models.generate_content(
model="gemini-2.5-flash",
contents=prompt_content,
config=types.GenerateContentConfig(
system_instruction=SYSTEM_PROMPT,
response_mime_type="application/json",
temperature=0.0
)
)
try:
parsed_data = json.loads(response.text)
return parsed_data
except json.JSONDecodeError:
logger.error("Ошибка парсинга JSON от Gemini.")
return {"error": "Failed to parse"}GitHub-тағы кодты көру (OZAT-kz)
3. 2-листинг: Боттар мен CRM интеграциясына арналған FastAPI Webhook
Бұл эндпоинтті қоймашыларға арналған Telegram-ботқа қосуға болады. Иу немесе Гуанчжоудағы қоймашы қорапты суретке түсіріп, чатқа жібереді, деректер бірден Firestore базасына түседі, ал Қазақстандағы клиент: «Сіздің жүгіңіз Қытайдағы қоймаға келді» деген пуш-хабарлама алады.
from fastapi import FastAPI, UploadFile, File, HTTPException
import os
from google.cloud import firestore
from cargo_parser_service import process_cargo_waybill
app = FastAPI(title="OZAT Cargo Tracker API")
db = firestore.Client(database=os.environ.get("FIRESTORE_DATABASE", "(default)"))
@app.post("/api/v1/parse-waybill")
async def parse_waybill_endpoint(file: UploadFile = File(...)):
if not file.content_type.startswith("image/"):
raise HTTPException(status_code=400, detail="Only images are supported")
image_bytes = await file.read()
# Вызов конвейера Document AI + Gemini
try:
parsed_data = await process_cargo_waybill(image_bytes, file.content_type)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Processing failed: {str(e)}")
if "error" in parsed_data:
raise HTTPException(status_code=422, detail="AI could not extract structured data")
tracking_number = parsed_data.get("tracking_number")
if not tracking_number:
raise HTTPException(status_code=400, detail="Could not find tracking number on image")
# Сохраняем результат в Firestore
doc_ref = db.collection("cargo_waybills").document(tracking_number)
doc_ref.set({
"tracking_number": tracking_number,
"client_code": parsed_data.get("client_code"),
"courier": parsed_data.get("courier"),
"weight_kg": parsed_data.get("weight_kg"),
"description": parsed_data.get("description_ru"),
"status": "ARRIVED_AT_WAREHOUSE",
"created_at": firestore.SERVER_TIMESTAMP
})
return {
"status": "success",
"data": parsed_data,
"message": f"Waybill {tracking_number} saved to database."
}GitHub-тағы кодты көру (OZAT-kz)
4. Енгізу нәтижелері: Шешуші метрикалар
Біз бұл байланысты Иу — Қорғас — Алматы бағыты бойынша жұмыс істейтін карго-компаниясының 2 500 жүкқұжатынан тұратын нақты датасетінде сынақтан өткіздік.
Точность распознавания трек-кодов и иероглифов (%)
Время обработки одной накладной в секундах (p50)
Бизнес-көрсеткіштер (Айлық қорытынды):
- • Уақытты босату: Әр оператор күніне 4.2 сағат үнемдейді (клиенттерге қызмет көрсетуге ауыстыруға болады).
- • Жүк жоғалуын азайту (Lost Packages): 89%-ға (15 таңбалы трек-кодтардағы қателерді болдырмау есебінен).
- • Өңдеу жылдамдығы (p50): Бір жүкқұжатқа 1.8 секунд.
- • FinOps (Бұлт құны): 10 000 жүкқұжатты парсинг жасау шамамен $25-30 тұрады (Document AI + Gemini Flash токендері). Бұл логисттің бір жұмыс күнінен де арзан.
5. 3-листинг: Инфрақұрылым код ретінде (Terraform)
Конвейердің жұмыс істеуі үшін біз Cloud Run сервисін орналастырамыз және Document AI API-ін автоматты түрде іске қосамыз. Құпиялар Secret Manager-де қауіпсіз сақталады.
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
variable "project_id" { type = string }
variable "region" { default = "europe-west1" }
provider "google" {
project = var.project_id
region = var.region
}
# Активация API Document AI
resource "google_project_service" "documentai" {
service = "documentai.googleapis.com"
disable_on_destroy = false
}
# Создание Document AI Processor (OCR)
resource "google_document_ai_processor" "ocr_processor" {
display_name = "cargo-ocr-processor"
location = "eu"
type = "OCR_PROCESSOR"
depends_on = [google_project_service.documentai]
}
# Секрет Gemini API
resource "google_secret_manager_secret" "gemini_key" {
secret_id = "gemini-api-key"
replication { auto {} }
}
# Cloud Run Service
resource "google_cloud_run_v2_service" "cargo_parser" {
name = "cargo-waybill-parser"
location = var.region
ingress = "INGRESS_TRAFFIC_ALL"
template {
scaling { max_instance_count = 10 }
containers {
image = "${var.region}-docker.pkg.dev/${var.project_id}/apps/cargo-parser:latest"
env {
name = "DOCAI_PROCESSOR_ID"
value = google_document_ai_processor.ocr_processor.id
}
env {
name = "DOCAI_LOCATION"
value = "eu"
}
env {
name = "GEMINI_API_KEY"
value_source { secret_key_ref { secret = google_secret_manager_secret.gemini_key.secret_id; version = "latest" } }
}
}
}
}GitHub-тағы кодты көру (OZAT-kz)
6. 4-листинг: Микросервисті деплой жасау
Парсерді жылдам жинауға және жариялауға арналған gcloud командалары.
# 1. Авторизация и настройка
gcloud auth login
gcloud config set project ozat-cloud-kz
# 2. Создание процессора Document AI (если через CLI)
# Альтернативно используется Terraform (см. листинг выше)
# 3. Сборка образа
gcloud builds submit --tag europe-west1-docker.pkg.dev/ozat-cloud-kz/apps/cargo-parser:latest
# 4. Деплой Cloud Run
gcloud run deploy cargo-waybill-parser \
--image europe-west1-docker.pkg.dev/ozat-cloud-kz/apps/cargo-parser:latest \
--region europe-west1 \
--platform managed \
--allow-unauthenticated \
--set-secrets="GEMINI_API_KEY=gemini-api-key:latest" \
--set-env-vars="DOCAI_LOCATION=eu,FIRESTORE_DATABASE=(default)" \
--memory=512Mi \
--cpu=1GitHub-тағы кодты көру (OZAT-kz)
7. OZAT Скауттарына арналған нұсқаулық: Cargo Tracker-ді қалай сату керек
Қазақстандағы Карго нарығы орасан зор. Алматының әрбір ірі сауда орталығында, базарларда, бизнес-орталықтарда ондаған форвардинг компаниялары отыр. Олардың басты ауыртпалығы — операциялық хаос және сәлемдеме статусының жаңаруын тәуліктеп күтетін көңілі толмаған клиенттер.
Карго иесіне жазыңыз: «Сіздің қызметкерлеріңіз WeChat-тан Excel-ге трек-кодтарды көшіруге қанша сағат жұмсайды? Ал бір әріптен қате кеткені үшін қанша сәлемдеме жоғалады?». Сурет жіберсе, базаға өзі түсетін Telegram-ботты ұсыныңыз.
Бір реттік төлем (Setup): 350 000-нан 600 000 ₸-ге дейін (парсерді олардың ағымдағы CRM/Excel/Google Sheets-пен біріктіру).
Қолдау (MRR): 40 000 – 70 000 ₸/ай.
Демо-бот жасаңыз. Клиенттен WeChat-тан ең нашар, мыжылған жүкқұжатты жіберуін сұраңыз. Бот 2 секундта аудармасы бар мінсіз JSON береді. Сатылымға кепілдік беріледі.
8. Шешімнің шынайы шектеулері мен компромисстері
- 1. Штрих-код үстіндегі қолжазба маркер: Иу қоймаларында жұмысшылар көбінесе штрих-кодтың үстіне қалың маркермен клиент кодын (мысалы, KZ-77) жазып тастайды. Document AI жабылған мәтінді танымауы мүмкін. Мұндай жағдайларда қолмен тексеру қажет.
- 2. Document AI құны: Өте арзан Gemini Flash-пен салыстырғанда, Document AI беттер бойынша тарифтеледі (1000 бетке шамамен $1.5). Өте үлкен көлемдер үшін (күніне жүздеген мың) бұл бюджетке соққы болуы мүмкін. Егер фото сапасы әрқашан мінсіз болса, DocAI-ді өшіріп, тек Gemini Vision қалдыруға болады.
- 3. Иероглиф-омоглифтер: Кейбір қытай таңбалары термопринтердің басып шығару сапасы нашар болған кезде сандарға ұқсас болып көрінеді. OCR сенімділігі (Confidence Score) төмендейді.
- 4. Арнайы аударма сленгі: Gemini «充电宝» сөзін «Пауэрбанк» деп аударуы мүмкін, ал бухгалтер кедендік декларация үшін «Портативті зарядтау құрылғысы» деп күтеді. СЭҚ синонимдер сөздігі арқылы постөңдеуді қажет етеді.
💡 ОЗАТ кеңесі: Енгізуге дайынсыз ба? Архитектура мен бюджетті Scope Builder арқылы есептеңіз немесе тегін ЖИ-аудиттен өтіңіз.

Рустам Шарафутдинов
Google Cloud архитектурасы саласындағы сарапшы және 15 жылдан астам тәжірибесі бар Senior Full-Stack әзірлеушісі. Ақауға төзімді архитектураларға, жоғары жүктемелі жобаларды оңтайландыруға және AI (Vertex AI) интеграциясына маманданған.