Jelajahi Sumber

打包为pyc

Sherlock1011 2 bulan lalu
induk
melakukan
5d9770764b
17 mengubah file dengan 322 tambahan dan 6 penghapusan
  1. 7 3
      agent/agent.py
  2. 3 1
      api/run_api.py
  3. 16 2
      config/__init__.py
  4. 23 0
      config/config.py
  5. 126 0
      ocr/README.md
  6. TEMPAT SAMPAH
      ocr/icon/GHS01.png
  7. TEMPAT SAMPAH
      ocr/icon/GHS02.png
  8. TEMPAT SAMPAH
      ocr/icon/GHS03.png
  9. TEMPAT SAMPAH
      ocr/icon/GHS04.png
  10. TEMPAT SAMPAH
      ocr/icon/GHS05.png
  11. TEMPAT SAMPAH
      ocr/icon/GHS06.png
  12. TEMPAT SAMPAH
      ocr/icon/GHS07.png
  13. TEMPAT SAMPAH
      ocr/icon/GHS08.png
  14. TEMPAT SAMPAH
      ocr/icon/GHS09.png
  15. 20 0
      ocr/ocr_config.yaml
  16. 23 0
      ocr/requirements_ocr.txt
  17. 104 0
      ocr/start.py

+ 7 - 3
agent/agent.py

@@ -1,4 +1,4 @@
-from config import MODEL_PATH, PROMPT_EXTRACT_NAME, PROMPT_EXTRACT_COMPONENTS, PROMPT_EXTRACT_KEYWORD, PROMPT_EXTRACT_PREVENTION,PROMPT_EXTRACT_SUPPLIER,PROMPT_EXTRACT_ICON
+from config import MODEL_PATH, INFERENCE_URL, INFERENCE_AUTH_TOKEN, INFERENCE_MODEL, PROMPT_EXTRACT_NAME, PROMPT_EXTRACT_COMPONENTS, PROMPT_EXTRACT_KEYWORD, PROMPT_EXTRACT_PREVENTION,PROMPT_EXTRACT_SUPPLIER,PROMPT_EXTRACT_ICON
 from model import QwenOcr
 
 from io import BytesIO
@@ -43,14 +43,18 @@ def resize_image(image, max_size=512):
 
 class OcrAgent:
     def __init__(self):
-        self._url = "http://127.0.0.1:8000/api/v1/ocr"
+        self._url = INFERENCE_URL
 
     def extract_single(self, image_base64: str, prompt: str, index: int):
         """单个任务请求,返回 (index, 结果文本)"""
         response = requests.post(
             self._url,
+            headers={
+                "Authorization": INFERENCE_AUTH_TOKEN,
+                "Content-Type": "application/json"
+            },
             json={
-                "model": "Qwen3-VL-32B-Instruct",
+                "model": INFERENCE_MODEL,
                 "messages": [
                     {"role": "system", "content": "You are a helpful assistant."},
                     {

+ 3 - 1
api/run_api.py

@@ -6,6 +6,7 @@ import asyncio
 import base64
 import io
 import logging
+import os
 import sys
 from contextlib import asynccontextmanager
 from typing import Optional, Dict, Any
@@ -214,7 +215,8 @@ async def lifespan(app: FastAPI):
     logger.info("应用启动中...")
     manager = await AgentManager.get_instance()
     try:
-        await manager.load_agent(max_concurrent=5)
+        max_concurrent = int(os.environ.get("MAX_CONCURRENT", 5))
+        await manager.load_agent(max_concurrent=max_concurrent)
         logger.info("应用启动完成")
     except Exception as e:
         logger.error(f"应用启动失败: {e}")

+ 16 - 2
config/__init__.py

@@ -1,11 +1,25 @@
-from .config import MODEL_PATH, PROMPT_EXTRACT_NAME, PROMPT_EXTRACT_COMPONENTS, PROMPT_EXTRACT_KEYWORD, PROMPT_EXTRACT_PREVENTION, PROMPT_EXTRACT_SUPPLIER, PROMPT_EXTRACT_ICON
+from .config import (
+    MODEL_PATH,
+    INFERENCE_URL,
+    INFERENCE_AUTH_TOKEN,
+    INFERENCE_MODEL,
+    PROMPT_EXTRACT_NAME,
+    PROMPT_EXTRACT_COMPONENTS,
+    PROMPT_EXTRACT_KEYWORD,
+    PROMPT_EXTRACT_PREVENTION,
+    PROMPT_EXTRACT_SUPPLIER,
+    PROMPT_EXTRACT_ICON,
+)
 
 __all__ = [
     "MODEL_PATH",
+    "INFERENCE_URL",
+    "INFERENCE_AUTH_TOKEN",
+    "INFERENCE_MODEL",
     "PROMPT_EXTRACT_NAME",
     "PROMPT_EXTRACT_COMPONENTS",
     "PROMPT_EXTRACT_KEYWORD",
     "PROMPT_EXTRACT_PREVENTION",
     "PROMPT_EXTRACT_SUPPLIER",
-    "PROMPT_EXTRACT_ICON"
+    "PROMPT_EXTRACT_ICON",
 ]

+ 23 - 0
config/config.py

@@ -3,6 +3,29 @@
 # 模型路径
 MODEL_PATH = "/root/autodl-tmp/llm/Qwen3-VL-32B-Instruct"
 
+# ========== Agent OCR 推理服务配置 ==========
+# 优先读取环境变量(由 start.py 在启动时注入),缺省使用下方默认值
+
+import os as _os
+
+# 推理服务 URL
+INFERENCE_URL = _os.environ.get(
+    "INFERENCE_URL",
+    "http://10.69.29.202:31277/inference-api/exp-api/inf-1480928240416935936/v1/chat/completions"
+)
+
+# 推理服务鉴权 Token
+INFERENCE_AUTH_TOKEN = _os.environ.get(
+    "INFERENCE_AUTH_TOKEN",
+    "Bearer QDiS42vR9EqP-j73zeeyWB8zSJ4juheflm6yDKUDz5c"
+)
+
+# 推理使用的模型名称
+INFERENCE_MODEL = _os.environ.get(
+    "INFERENCE_MODEL",
+    "Qwen3-VL-32B-Instruct"
+)
+
 
 # ========== OCR提示词 - 分步骤提取 ==========
 

+ 126 - 0
ocr/README.md

@@ -0,0 +1,126 @@
+# Agent OCR 服务
+
+基于大模型的化学品安全标签信息提取服务。
+
+## 目录结构
+
+```
+ocr/
+├── agent/              # OCR Agent 核心逻辑(.pyc)
+├── api/                # FastAPI 服务入口(.pyc)
+├── config/             # 配置模块(.pyc)
+├── icon/               # GHS 危险象形图(GHS01~GHS09.png)
+├── ocr_config.yaml     # 服务配置文件
+├── start.py            # 启动脚本
+└── requirements_ocr.txt
+```
+
+## 安装依赖
+
+```bash
+pip install -r requirements_ocr.txt
+```
+
+## 配置
+
+编辑 `ocr_config.yaml`,按实际环境修改以下参数:
+
+```yaml
+inference:
+  url: "http://<推理服务地址>/v1/chat/completions"
+  auth_token: "Bearer <your_token>"
+  model: "<模型名称>"
+
+server:
+  host: "0.0.0.0"
+  port: 6006
+  max_concurrent: 5
+```
+
+| 参数 | 说明 |
+|------|------|
+| `inference.url` | 推理服务的请求地址 |
+| `inference.auth_token` | 鉴权 Token,格式为 `Bearer <token>` |
+| `inference.model` | 调用的模型名称 |
+| `server.host` | API 服务监听地址,`0.0.0.0` 允许外部访问 |
+| `server.port` | API 服务监听端口 |
+| `server.max_concurrent` | 最大并发请求数 |
+
+## 启动服务
+
+```bash
+# 使用默认配置文件 ocr_config.yaml
+python start.py
+
+# 指定配置文件
+python start.py --config /path/to/your_config.yaml
+```
+
+服务启动后访问 `http://<host>:<port>/docs` 查看接口文档。
+
+## 接口说明
+
+### POST `/api/v1/agent_ocr`
+
+提取化学品安全标签信息。
+
+**请求体**
+```json
+{
+  "image": "<base64 编码的图像字符串>"
+}
+```
+
+**成功响应**
+```json
+{
+  "code": "200",
+  "message": "操作成功",
+  "data": {
+    "tag": {
+      "name_cn": "化学品中文名称",
+      "name_en": "化学品英文名称",
+      "cf_list": [
+        {
+          "cas_name": "成分名称",
+          "cas_cf": "化学式",
+          "true_rate": "实际浓度",
+          "rate": "浓度区间",
+          "cas_no": "CAS号"
+        }
+      ]
+    },
+    "tag_images": ["GHS02", "GHS07"],
+    "key_word": "警告",
+    "risk_notice": "危险性说明",
+    "pre_notice": {
+      "pre_method": "预防措施",
+      "acc_response": "事故响应",
+      "safe_keep": "安全存储",
+      "abandon_deal": "废弃处置"
+    },
+    "supplier": [
+      {
+        "name": "供应商名称",
+        "address": "供应商地址",
+        "tel": "供应商电话",
+        "post": "供应商邮编"
+      }
+    ],
+    "acc_tel": "应急咨询电话"
+  }
+}
+```
+
+**失败响应**
+```json
+{
+  "code": "500",
+  "message": "请求失败",
+  "data": {}
+}
+```
+
+### GET `/health`
+
+健康检查,返回服务状态及当前并发数。

TEMPAT SAMPAH
ocr/icon/GHS01.png


TEMPAT SAMPAH
ocr/icon/GHS02.png


TEMPAT SAMPAH
ocr/icon/GHS03.png


TEMPAT SAMPAH
ocr/icon/GHS04.png


TEMPAT SAMPAH
ocr/icon/GHS05.png


TEMPAT SAMPAH
ocr/icon/GHS06.png


TEMPAT SAMPAH
ocr/icon/GHS07.png


TEMPAT SAMPAH
ocr/icon/GHS08.png


TEMPAT SAMPAH
ocr/icon/GHS09.png


+ 20 - 0
ocr/ocr_config.yaml

@@ -0,0 +1,20 @@
+# ========== Agent OCR 服务配置文件 ==========
+# 修改此文件中的参数后,重新运行 start.py 即可生效
+
+# ---------- 推理服务配置 ----------
+inference:
+  # 推理服务请求地址
+  url: "http://10.69.29.202:31277/inference-api/exp-api/inf-1480928240416935936/v1/chat/completions"
+  # 鉴权 Token(格式:Bearer <token>)
+  auth_token: "Bearer QDiS42vR9EqP-j73zeeyWB8zSJ4juheflm6yDKUDz5c"
+  # 使用的模型名称
+  model: "Qwen3-VL-32B-Instruct"
+
+# ---------- API 服务配置 ----------
+server:
+  # 监听地址,0.0.0.0 表示允许外部访问
+  host: "0.0.0.0"
+  # 监听端口
+  port: 6006
+  # 最大并发请求数
+  max_concurrent: 5

+ 23 - 0
ocr/requirements_ocr.txt

@@ -0,0 +1,23 @@
+# Agent OCR 服务依赖包
+# 根据 agent/、api/、config/ 代码实际使用的库整理
+
+# Web 框架 & 服务
+fastapi==0.128.0
+uvicorn==0.40.0
+starlette==0.50.0
+pydantic==2.12.5
+
+# HTTP 请求
+requests
+
+# 图像处理
+Pillow==10.2.0
+
+# 异步支持
+anyio==4.7.0
+
+# 工具库(fastapi 依赖)
+python-dotenv==1.2.1
+typing_extensions==4.15.0
+annotated-types==0.7.0
+pydantic_core==2.41.5

+ 104 - 0
ocr/start.py

@@ -0,0 +1,104 @@
+"""
+Agent OCR 服务启动脚本
+用法:
+    python start.py                        # 使用默认配置文件 ocr_config.yaml
+    python start.py --config /path/to/cfg  # 指定配置文件
+"""
+import argparse
+import os
+import sys
+
+import yaml
+import uvicorn
+
+
+def load_config(config_path: str) -> dict:
+    """加载 yaml 配置文件"""
+    if not os.path.exists(config_path):
+        print(f"[ERROR] 配置文件不存在: {config_path}")
+        sys.exit(1)
+    with open(config_path, "r", encoding="utf-8") as f:
+        cfg = yaml.safe_load(f)
+    if not isinstance(cfg, dict):
+        print("[ERROR] 配置文件格式错误,请检查 yaml 格式")
+        sys.exit(1)
+    return cfg
+
+
+def apply_inference_config(cfg: dict):
+    """将推理服务配置写入环境变量,供 config/config.py 读取"""
+    inf = cfg.get("inference", {})
+    mapping = {
+        "INFERENCE_URL":        inf.get("url"),
+        "INFERENCE_AUTH_TOKEN": inf.get("auth_token"),
+        "INFERENCE_MODEL":      inf.get("model"),
+    }
+    for key, val in mapping.items():
+        if val:
+            os.environ[key] = str(val)
+
+
+def apply_server_config(cfg: dict) -> tuple:
+    """解析服务配置,返回 (host, port, max_concurrent)"""
+    srv = cfg.get("server", {})
+    host           = str(srv.get("host", "0.0.0.0"))
+    port           = int(srv.get("port", 6006))
+    max_concurrent = int(srv.get("max_concurrent", 5))
+    # 写入环境变量,供 run_api.py 的 lifespan 读取
+    os.environ["MAX_CONCURRENT"] = str(max_concurrent)
+    return host, port, max_concurrent
+
+
+def setup_path():
+    """将项目根目录(ocr/ 的上级)加入 sys.path,确保能找到 agent/api/config 包"""
+    ocr_dir  = os.path.dirname(os.path.abspath(__file__))
+    root_dir = os.path.dirname(ocr_dir)
+    if root_dir not in sys.path:
+        sys.path.insert(0, root_dir)
+
+
+def print_config(cfg: dict, host: str, port: int, max_concurrent: int):
+    inf = cfg.get("inference", {})
+    token = inf.get("auth_token", "")
+    # 只显示 token 末尾 6 位,避免泄露
+    masked = ("*" * max(0, len(token) - 6)) + token[-6:] if token else ""
+    print("=" * 50)
+    print("  Agent OCR 服务配置")
+    print("=" * 50)
+    print(f"  推理地址  : {inf.get('url', '')}")
+    print(f"  认证Token : {masked}")
+    print(f"  模型名称  : {inf.get('model', '')}")
+    print(f"  服务地址  : http://{host}:{port}")
+    print(f"  最大并发  : {max_concurrent}")
+    print("=" * 50)
+
+
+def main():
+    parser = argparse.ArgumentParser(description="启动 Agent OCR API 服务")
+    parser.add_argument(
+        "--config",
+        default=os.path.join(os.path.dirname(os.path.abspath(__file__)), "ocr_config.yaml"),
+        help="配置文件路径(默认:与 start.py 同目录的 ocr_config.yaml)",
+    )
+    args = parser.parse_args()
+
+    cfg = load_config(args.config)
+    apply_inference_config(cfg)
+    host, port, max_concurrent = apply_server_config(cfg)
+    setup_path()
+
+    print_config(cfg, host, port, max_concurrent)
+
+    uvicorn.run(
+        "api.run_api:app",
+        host=host,
+        port=port,
+        workers=1,
+        log_level="info",
+        access_log=True,
+        reload=False,
+    )
+
+
+if __name__ == "__main__":
+    main()