This repository has been archived on 2026-03-05. You can view files and clone it, but cannot push or open issues or pull requests.
AccountingBot/bot/vlm.py
2026-01-21 10:45:30 +08:00

68 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
import httpx
import base64
import os
import json
import re
from datetime import datetime, timedelta, timezone
gpu_sem = asyncio.Semaphore(int(os.getenv("MAX_CONCURRENT_INFERENCE", 1)))
async def call_qwen_vlm(content, is_image=True):
async with gpu_sem:
# 获取北京时间 (UTC+8)
tz_beijing = timezone(timedelta(hours=8))
now_beijing = datetime.now(tz_beijing).strftime("%Y-%m-%d %H:%M:%S")
system_prompt = (
"你是一个财务账单解析助手。请提取以下字段并返回 JSON\n"
"1. amount (浮点数)\n"
"2. category (餐饮, 交通, 购物, 娱乐, 医疗, 运动 , 住宿, 人情, 其它)\n"
f"3. transaction_time (格式YYYY-MM-DD HH:MM:SS。当前北京时间为{now_beijing})\n\n"
"时间提取准则:\n"
"- 优先寻找账单截图或文字中明确提到的交易时间。\n"
"- 若信息不全(如只有月日),请结合当前北京时间年份进行补全。\n"
"- 若完全没有时间信息,请直接使用上述提供的北京时间作为默认值。\n"
"注意:\n"
"- 严禁输出任何思维过程,只返回纯 JSON 字典,确保数据完整性。\n"
"- 忽略任何货币符号以及正负号,仅提取数字部分作为 amount。\n"
)
base_url = os.getenv('OLLAMA_BASE_URL', 'http://vlm-service:11434').rstrip('/')
payload = {
"model": os.getenv("VLM_MODEL"),
"prompt": system_prompt,
"stream": False,
"format": "json",
"options": {"temperature": 0.1}
}
if is_image:
payload["images"] = [base64.b64encode(content).decode('utf-8')]
else:
payload["prompt"] += f"\n用户输入: {content}"
try:
async with httpx.AsyncClient(timeout=300.0, trust_env=False) as client:
resp = await client.post(f"{base_url}/api/generate", json=payload)
if resp.status_code == 200:
res_json = resp.json()
# 兼容 Qwen3-VL 可能将结果放在 thinking 字段的情况
raw_res = res_json.get("response", "") or res_json.get("thinking", "")
# 提取并验证 JSON
match = re.search(r'(\{.*\})', raw_res, re.DOTALL)
clean_json = match.group(1) if match else "{}"
# 检查必要字段是否存在,不存在则补齐
data = json.loads(clean_json)
data.setdefault("amount", 0.0)
data.setdefault("category", "其它")
data.setdefault("transaction_time", now_beijing)
return json.dumps(data)
return "{}"
except Exception as e:
print(f"VLM Error: {e}")
return "{}"