From b6a99ba5678d358d9103ab70f033f3001d6e57b0 Mon Sep 17 00:00:00 2001 From: wsb1224 Date: Sat, 4 Jul 2026 10:16:58 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=89=A9=E6=B5=81=E8=A1=A8=E6=A0=BC?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E4=BC=98=E5=85=88=E7=94=A8=E9=98=BF=E9=87=8C?= =?UTF-8?q?=E4=BA=91OCR=EF=BC=8CSDK=E4=B8=8D=E5=8F=AF=E7=94=A8=E6=97=B6?= =?UTF-8?q?=E9=99=8D=E7=BA=A7LLM=E8=A7=86=E8=A7=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 拆分为三种方法:_parse_table_via_ocr(OCR+LLM)、 _parse_table_via_llm_vision(LLM视觉直读), 自动检测SDK可用性选择路径。 Co-Authored-By: Claude Sonnet 4.6 --- backend/app/services/order_service.py | 59 +++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/backend/app/services/order_service.py b/backend/app/services/order_service.py index 32abd7f..ea64e7f 100644 --- a/backend/app/services/order_service.py +++ b/backend/app/services/order_service.py @@ -2400,15 +2400,61 @@ class OrderService: def parse_logistics_table_images(self, image_urls, session): - """识别物流表格截图,用 LLM 视觉能力直接从图片中提取快递单号和收件人。""" + """识别物流表格截图。优先用阿里云OCR+LLM,SDK不可用时降级为LLM视觉直接识别。""" + import logging as _log + _logger = _log.getLogger(__name__) + + # 检测阿里云 OCR SDK 是否可用 + try: + from alibabacloud_ocr_api20210707.models import RecognizeGeneralRequest # noqa: F401 + ocr_available = True + except ImportError: + ocr_available = False + _logger.info("[parse_logistics_table_images] 阿里云OCR SDK未安装,将使用LLM视觉识别") + + if ocr_available: + return self._parse_table_via_ocr(image_urls, session, _logger) + return self._parse_table_via_llm_vision(image_urls, _logger) + + def _parse_table_via_ocr(self, image_urls, session, _logger): + """阿里云OCR + LLM提取。""" + from backend.app.services.ai_service import ai_service + from backend.app.core.config import get_settings + settings = get_settings() + results = [] + seen_tracking = set() + for url in image_urls: + try: + _logger.info("[parse_logistics_table_images] OCR处理图片: %s", url) + ctx = ai_service._ocr_and_parse_image(url) + raw_text = ctx.get("raw_text", "") + _logger.info("[parse_logistics_table_images] OCR文本长度: %d", len(raw_text)) + if not raw_text.strip(): + continue + llm_items = self._llm_extract_logistics(raw_text, settings) + for item in llm_items: + tn = (item.get("tracking_number") or "").strip() + if not tn or len(tn) < 10 or tn in seen_tracking: + continue + seen_tracking.add(tn) + results.append({ + "tracking_number": tn, + "express_company": item.get("express_company") or None, + "recipient_name": item.get("recipient_name") or None, + }) + except Exception as exc: + _logger.warning("[parse_logistics_table_images] OCR识别失败: %s", exc, exc_info=True) + _logger.info("[parse_logistics_table_images] OCR模式最终结果: %d条", len(results)) + return results + + def _parse_table_via_llm_vision(self, image_urls, _logger): + """LLM视觉直接识别表格图片。""" import base64 as _b64 import json as _json - import logging as _log from urllib import request as urllib_request from backend.app.services.ai_service import read_image_bytes from backend.app.core.config import get_settings - _logger = _log.getLogger(__name__) settings = get_settings() api_key = settings.llm_parse_api_key or settings.aliyun_ai_access_key_id @@ -2425,7 +2471,7 @@ class OrderService: seen_tracking = set() for url in image_urls: try: - _logger.info("[parse_logistics_table_images] 处理图片: %s", url) + _logger.info("[parse_logistics_table_images] LLM视觉处理图片: %s", url) image_bytes = read_image_bytes(url) image_b64 = _b64.b64encode(image_bytes).decode("utf-8") suffix = url.rsplit(".", 1)[-1].lower() if "." in url else "jpeg" @@ -2455,7 +2501,6 @@ class OrderService: content = result["choices"][0]["message"]["content"] _logger.info("[parse_logistics_table_images] LLM响应: %s", content[:500]) llm_items = self._extract_json_array(content) - _logger.info("[parse_logistics_table_images] 提取结果: %d条", len(llm_items)) for item in llm_items: tn = (item.get("tracking_number") or "").strip() @@ -2468,8 +2513,8 @@ class OrderService: "recipient_name": item.get("recipient_name") or None, }) except Exception as exc: - _logger.warning("[parse_logistics_table_images] 识别失败: %s", exc, exc_info=True) - _logger.info("[parse_logistics_table_images] 最终结果: %d条", len(results)) + _logger.warning("[parse_logistics_table_images] LLM视觉识别失败: %s", exc, exc_info=True) + _logger.info("[parse_logistics_table_images] LLM视觉模式最终结果: %d条", len(results)) return results def _llm_extract_logistics(self, text, settings):