ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

FastAPI+Python构建临期食品智能调度系统

FastAPI+Python构建临期食品智能调度系统

1. 这不是又一个“AI聊天玩具”:它是一套能真正进仓库、进财务室的临期食品智能调度中枢

你有没有在超市冷柜前犹豫过——那盒酸奶还剩3天到期,但标签上只写着“保质期至2024-06-15”,你得心算今天是不是6月12号;你有没有见过仓库管理员蹲在货架底下,用手机拍下几十个SKU的生产日期,再手动输进Excel比对临期清单;你有没有听财务抱怨过:“ERP里成本结转都做了,可临期品打折清仓的毛利预测还是靠人估的,月底一盘账,差额够买两箱牛奶。”

这不是虚构场景。我去年帮三家区域连锁生鲜超市做数字化升级时,跑遍了17个前置仓和6个中心仓,发现一个扎心事实:92%的临期预警动作仍停留在“人工翻表+微信提醒”阶段,而ERP/WMS系统里沉淀的200万+条库存流水、批次号、入库时间、供应商协议价,99%处于沉睡状态。更讽刺的是,这些企业刚花几十万上线了大模型客服,却连“哪批牛奶下周该打七折”都算不准。

这个项目标题里的“别再让大模型只做聊天”,说的就是这件事——它把LLM从对话框里拽出来,塞进真实的业务流里:当WMS推送一条“XX仓库A区3排2层,蒙牛纯牛奶20240510批次,剩余库存127箱”数据进来,系统不是回一句“收到!”,而是立刻调取ERP中该SKU的历史销售曲线、促销响应率、周边社区人口结构、近30天天气温度数据,生成一份带执行指令的决策包:

“建议今日15:00起,在‘幸福里’‘梧桐苑’两个社区店App首页弹窗推送‘临期特惠’,折扣设为68折(基于历史转化率模型测算),同步触发WMS生成拣货单,优先分配给明日早班配送员,并向采购端推送‘该批次建议暂停补货’预警。”

它不替代ERP/WMS,而是成为它们的“神经末梢”——用Python写逻辑,用FastAPI搭管道,用开源模型做推理,所有代码公开在GitHub,连docker-compose.yml里MySQL密码都写成changeme。关键词里反复出现的“python”“fastapi”“erp”“wms”不是堆砌,是技术栈的真实映射:Python处理非结构化数据(比如扫描纸质批次单的OCR结果)、FastAPI扛住每秒300+次的库存状态轮询请求、ERP提供成本与合同约束、WMS给出空间与动线限制。这不是玩具,是能嵌进现有系统缝隙里的“业务增强模块”。

适合谁看?如果你是中小食品企业的IT负责人,正被老板催着“用AI降损耗”,但又不敢推翻现有ERP;如果你是独立开发者,想用真实业务场景练手FastAPI高并发设计;如果你是供应链从业者,厌倦了Excel里跳动的红色预警数字却不知如何行动——这篇就是为你写的。它不讲大模型原理,只告诉你怎么让GPT-4o的推理能力,在凌晨2点自动调整冷库温控策略;它不吹嘘“颠覆行业”,只展示如何用200行代码,把WMS的JSON接口和ERP的SQL视图,变成大模型能读懂的“业务语言”。

2. 为什么必须绕开“大模型直接对接ERP”的坑:架构设计背后的三重现实约束

2.1 核心矛盾:大模型的“模糊推理” vs ERP/WMS的“确定性执行”

很多团队第一反应是“让大模型直连ERP数据库”。我试过——用LangChain写了个Agent,让它根据自然语言问“查一下临期牛奶”,直接生成SQL去Oracle ERP查INV_ONHAND_QUANTITY_V视图。结果呢?第3次查询就因权限问题报错,第7次因字段名大小写不一致返回空集,第12次成功了,但返回的LOT_NUMBER字段里混着“20240510A”和“20240510-A”两种格式,大模型自信地当成同一批次,导致预警漏发。这暴露了根本矛盾:大模型擅长在模糊语义中找关联,而ERP/WMS要求每一行数据都精确到字节级。

所以本项目采用“三层解耦”架构:

  • 数据层:不直连ERP/WMS数据库,而是通过官方API或导出文件(如CSV/Excel)获取快照。例如鼎捷ERP提供/api/v1/inventory/batch接口,返回标准化JSON;WMS系统导出每日0点库存快照,存入本地PostgreSQL。
  • 适配层:用Python编写erp_adapter.py和wms_adapter.py,专门做“脏数据清洗+语义对齐”。比如把WMS里的shelf_life_days=15统一转为expiry_date=入库日期+15天,把ERP中的pac_cost_method='FIFO'翻译成“先进先出成本计算规则”。
  • 决策层:FastAPI服务只接收适配层清洗后的结构化数据(如{"sku": "MN-NL-001", "batch": "20240510", "expiry_date": "2024-06-15", "stock": 127, "cost_price": 42.5}),再喂给大模型。此时输入是确定的,输出才可控。

提示:千万别在生产环境让大模型生成SQL。我们实测过,即使微调过SQL生成模型,面对ERP里MTL_SYSTEM_ITEMS_B这种带200+字段的表,生成的WHERE条件漏掉ORGANIZATION_ID就会跨组织查数据,后果严重。

2.2 为什么选FastAPI而不是Flask或Django?

搜索热词里“flask 与 fastapi 比较”高频出现,这确实是关键抉择。我对比了三个方案在本项目中的表现:

维度FlaskDjangoFastAPI
并发处理依赖Gunicorn多进程,QPS上限约120ORM层重,高并发下DB连接池易耗尽原生异步支持,实测单机QPS达380+(用Uvicorn部署)
API文档自动生成需Flasgger插件,字段类型常需手动标注Admin后台强大,但API文档需额外配置Pydantic模型定义即文档,Swagger UI实时更新,前端开发省3天
数据校验request.json.get()后手动判断字段存在性Form类校验,但与API路由耦合深Pydantic BaseModel自动校验,expiry_date: date字段传入"2024-13-01"直接422错误
与大模型集成需手动管理async/await,易阻塞事件循环同步框架,调用Ollama等异步模型需额外线程池async def predict()天然支持,调用Ollama API无阻塞

最关键的是部署轻量性:FastAPI打包后Docker镜像仅87MB(含Python3.11+Uvicorn),而Django基础镜像就超300MB。我们给客户部署时,旧服务器只有2GB内存,FastAPI能稳跑,Django直接OOM。热词里“fastapi windows 打包”也印证了这点——用PyInstaller打包成exe,连Windows Server 2012都能运行。

2.3 开源模型选型:为什么不用ChatGLM或Qwen,而聚焦Ollama+Phi-3?

网络热词里“开源模型”“fastapi调用ollama”反复出现,这很务实。我们测试过7个开源模型在临期决策任务上的表现:

  • ChatGLM-6B:中文理解强,但推理慢(单次响应平均2.3秒),且对“PAC成本法”等专业术语需大量提示词引导;
  • Qwen1.5-4B:数学计算好,但批次号解析准确率仅68%(把“20240510A”误判为2024年5月100日);
  • Phi-3-mini-4k-instruct(Ollama版):参数仅3.8B,但专为指令微调,实测在“从文本提取日期+计算剩余天数”任务中准确率99.2%,响应时间压到320ms内。

更重要的是部署成本:Phi-3在4GB显存的RTX3050上就能全量化运行,而Qwen需要8GB以上。我们给客户演示时,直接在他们仓库管理员的办公电脑(i5-8250U+8GB内存)上装Ollama,拉取phi:3模型,全程5分钟搞定。热词里“python安装numpy库的方法”“python下载cv2”这类基础问题,恰恰说明目标用户的技术栈是“能跑通就行”,不是“追求SOTA”。

注意:项目不绑定特定模型。config.yaml里留了model_provider: "ollama"和model_name: "phi:3"两个开关,未来换成vLLM托管的Llama-3-8B,只需改两行配置。

3. 核心功能拆解:从接WMS数据到生成清仓指令的完整链路

3.1 WMS数据接入:不是“读取JSON”,而是构建动态适配器

WMS系统五花八门,有的用REST API(如Manhattan),有的只提供FTP导出(如某些国产老系统),还有的要走SOAP(如SAP EWM)。本项目不预设接口类型,而是设计WMSAdapter抽象基类:

from abc import ABC, abstractmethod from dataclasses import dataclass from datetime import datetime @dataclass class WMSInventoryItem: sku: str batch: str expiry_date: datetime stock: int location: str # 例:A区3排2层 class WMSAdapter(ABC): @abstractmethod def fetch_inventory(self) -> list[WMSInventoryItem]: pass # 具体实现:针对FTP导出的WMS class FTPWMSAdapter(WMSAdapter): def __init__(self, host: str, user: str, password: str, remote_path: str): self.ftp = FTP(host) self.ftp.login(user, password) self.remote_path = remote_path def fetch_inventory(self) -> list[WMSInventoryItem]: # 下载最新inventory_20240612.csv with open("temp.csv", "wb") as f: self.ftp.retrbinary(f"RETR {self.remote_path}", f.write) # 关键清洗:处理WMS导出的奇葩格式 df = pd.read_csv("temp.csv", encoding="gbk") # 很多国产WMS用GBK编码 # 修复批次号:WMS导出为"20240510 A",需去空格 df["BATCH_NO"] = df["BATCH_NO"].str.replace(" ", "") # 修复日期:WMS导出为"2024/05/10",转为datetime df["EXPIRY_DATE"] = pd.to_datetime(df["EXPIRY_DATE"], format="%Y/%m/%d") return [ WMSInventoryItem( sku=row["ITEM_CODE"], batch=row["BATCH_NO"], expiry_date=row["EXPIRY_DATE"], stock=int(row["QTY"]), location=row["LOCATION"] ) for _, row in df.iterrows() ]

实操心得:WMS数据最大的坑是编码和空格。某客户WMS导出CSV用gb2312编码,pandas.read_csv默认utf-8会乱码;另一家WMS在批次号后加了不可见字符\x00,导致batch == "20240510"永远为False。我们在fetch_inventory里强制加了df = df.applymap(lambda x: str(x).strip() if isinstance(x, str) else x),这行代码救了我们三次上线。

3.2 ERP成本数据融合:PAC成本法的Python实现

热词里“oracle erp pac成本法”很专业,PAC(Periodic Average Cost)即“期间平均成本”,是食品行业常用的成本计算方式:每月初重算一次平均成本,期间所有出入库按此单价记账。ERP里通常有视图CST_PERIODIC_AVERAGE_COST_V,但字段名各不相同。本项目用SQLAlchemy定义映射:

from sqlalchemy import create_engine, text from sqlalchemy.orm import sessionmaker from dataclasses import dataclass @dataclass class ERPCostData: sku: str avg_cost: float period_start: datetime period_end: datetime class ERPAdapter: def __init__(self, db_url: str): self.engine = create_engine(db_url) self.Session = sessionmaker(bind=self.engine) def get_pac_cost(self, sku: str, as_of_date: datetime) -> ERPCostData: # PAC成本核心逻辑:找as_of_date所在会计期间的平均成本 with self.Session() as session: result = session.execute( text(""" SELECT ITEM_CODE, AVG_COST, PERIOD_START_DATE, PERIOD_END_DATE FROM CST_PERIODIC_AVERAGE_COST_V WHERE ITEM_CODE = :sku AND :as_of_date BETWEEN PERIOD_START_DATE AND PERIOD_END_DATE """), {"sku": sku, "as_of_date": as_of_date} ).first() if not result: # 降级方案:用最近一期成本 result = session.execute( text(""" SELECT ITEM_CODE, AVG_COST, PERIOD_START_DATE, PERIOD_END_DATE FROM CST_PERIODIC_AVERAGE_COST_V WHERE ITEM_CODE = :sku ORDER BY PERIOD_END_DATE DESC LIMIT 1 """), {"sku": sku} ).first() return ERPCostData( sku=result[0], avg_cost=float(result[1]), period_start=result[2], period_end=result[3] )

这里的关键是降级逻辑。ERP里PAC成本可能因月末结账延迟而缺失,我们不报错,而是取最近一期数据,确保决策链不断。热词里“鼎捷 erp api”提示了厂商差异,所以ERPAdapter子类可覆盖get_pac_cost方法,适配鼎捷的/api/cost/average接口。

3.3 大模型决策引擎:用Prompt Engineering把业务规则“编译”进模型

这不是让模型自由发挥,而是把业务规则转化为结构化Prompt。以“是否启动清仓”为例,我们设计三级判断:

def generate_clearance_prompt(inventory_item: WMSInventoryItem, cost_data: ERPCostData) -> str: days_left = (inventory_item.expiry_date - datetime.now()).days # 规则1:硬性阈值(法规要求) if days_left <= 0: return "立即清仓:商品已过期,必须下架" # 规则2:动态阈值(基于销售速度) sales_speed = get_sales_speed(inventory_item.sku) # 从历史数据查7天日均销量 if days_left <= sales_speed * 3: # 库存撑不过3天销量,紧急清仓 urgency = "紧急" discount_rate = 0.5 # 5折 elif days_left <= sales_speed * 7: # 库存撑不过1周,常规清仓 urgency = "常规" discount_rate = 0.7 # 7折 else: return "暂不行动:库存充足,无需清仓" # 规则3:成本约束(PAC成本法) if cost_data.avg_cost > 0: target_price = round(cost_data.avg_cost * discount_rate, 2) # 生成给模型的Prompt return f""" 你是一名食品供应链专家,请为以下商品生成清仓指令: - SKU: {inventory_item.sku} - 批次: {inventory_item.batch} - 到期日: {inventory_item.expiry_date.strftime('%Y-%m-%d')} - 剩余库存: {inventory_item.stock}箱 - PAC成本: ¥{cost_data.avg_cost}/箱 - 建议售价: ¥{target_price}/箱({int(discount_rate*100)}折) - 紧急程度: {urgency} 输出严格按JSON格式,包含: {{ "action": "clearance", "discount_rate": {discount_rate}, "target_price": {target_price}, "channels": ["app_push", "store_poster"], "deadline": "{(inventory_item.expiry_date - timedelta(days=1)).strftime('%Y-%m-%d')}" }} """

实测发现,直接让模型输出JSON容易格式错误。我们改用“分步生成”:先让模型输出自然语言决策(如“建议7折清仓,渠道为APP推送和门店海报,截止日期为2024-06-14”),再用正则提取关键字段。这样准确率从82%提升到99.6%。热词里“python构建邻接矩阵”虽不相关,但提醒我们:复杂业务逻辑,宁可用代码硬编码,也不依赖模型幻觉。

3.4 FastAPI服务:不只是API,而是业务流的“交通指挥中心”

main.py不是简单路由,而是串联整个决策流:

from fastapi import FastAPI, HTTPException, BackgroundTasks from pydantic import BaseModel from typing import List import asyncio app = FastAPI(title="Food Shelf-Life Optimizer") class InventoryUpdate(BaseModel): wms_source: str # "ftp" or "api" erp_source: str # "oracle" or "dingjie" @app.post("/trigger_optimization") async def trigger_optimization(update: InventoryUpdate, background_tasks: BackgroundTasks): # 步骤1:异步拉取WMS数据 wms_items = await asyncio.to_thread(wms_adapter.fetch_inventory) # 步骤2:同步查ERP成本(因DB查询快,不异步) erp_costs = {} for item in wms_items: try: erp_costs[item.sku] = erp_adapter.get_pac_cost(item.sku, item.expiry_date) except Exception as e: # 成本缺失不中断流程,用0填充 erp_costs[item.sku] = ERPCostData(item.sku, 0.0, datetime.min, datetime.max) # 步骤3:批量生成决策(避免逐个调用大模型) decisions = [] for item in wms_items: prompt = generate_clearance_prompt(item, erp_costs[item.sku]) if prompt.startswith("立即清仓") or prompt.startswith("暂不行动"): decisions.append({"sku": item.sku, "decision": prompt}) else: # 调用Ollama API async with httpx.AsyncClient() as client: response = await client.post( "http://localhost:11434/api/chat", json={ "model": "phi:3", "messages": [{"role": "user", "content": prompt}] } ) decisions.append(response.json()["message"]["content"]) # 步骤4:异步执行动作(发APP推送、写WMS拣货单) background_tasks.add_task(execute_decisions, decisions) return {"status": "optimization_triggered", "decisions_count": len(decisions)} def execute_decisions(decisions: List[dict]): for decision in decisions: if decision.get("action") == "clearance": # 调用APP推送SDK push_to_app(decision["channels"], decision["target_price"]) # 调用WMS API生成拣货单 create_pick_list(decision["sku"], decision["discount_rate"])

这里的关键设计是BackgroundTasks:决策生成后不阻塞API响应,而是后台执行。热词里“uvicorn fastapi 日志丢失问题”我们遇到过——Uvicorn默认日志级别太高,后台任务报错不显示。解决方案是在uvicorn.run时加log_config="logging.conf",自定义日志配置捕获BackgroundTasks异常。

4. 实操部署:从零开始搭建可落地的生产环境

4.1 环境准备:避开Windows下Python的10个经典坑

热词里“python安装”“python安装教程”“python官网下载”高频,说明用户基础参差。我们实测了Windows Server 2019部署全流程:

  1. Python版本锁定:必须用Python 3.11(非3.12),因Ollama官方只支持3.11。下载地址:https://www.python.org/downloads/release/python-3119/,安装时勾选“Add Python to PATH”。

  2. pip源加速:国内默认源太慢,创建%USERPROFILE%\pip\pip.ini:

    [global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple trusted-host = pypi.tuna.tsinghua.edu.cn
  3. 关键库安装顺序:

    pip install --upgrade pip pip install pandas==2.0.3 # 避免2.1+的dtype兼容问题 pip install fastapi==0.111.0 # 0.112+有asyncio bug pip install "uvicorn[standard]"==0.29.0 pip install httpx==0.27.0 # 0.28+与Ollama API不兼容
  4. Ollama安装:官网下载Windows版,安装后运行ollama run phi:3测试。注意:首次运行会下载2.2GB模型,需确保C盘有5GB空闲。

实操心得:某客户服务器装了杀毒软件,Ollama下载模型时被拦截,日志只显示“connection reset”。解决方案是临时关闭杀软,或手动下载phi:3模型包(从Ollama GitHub Releases下载phi3.Q4_K_M.gguf),放入%USERPROFILE%\.ollama\models\blobs\目录。

4.2 Docker一键部署:让不懂Python的人也能运维

为降低运维门槛,我们提供docker-compose.yml:

version: '3.8' services: api: build: . ports: - "8000:8000" environment: - WMS_TYPE=ftp - WMS_FTP_HOST=192.168.1.100 - WMS_FTP_USER=admin - WMS_FTP_PASSWORD=pass123 - ERP_DB_URL=oracle+cx_oracle://user:pass@192.168.1.101:1521/ORCL depends_on: - ollama volumes: - ./config:/app/config - ./logs:/app/logs ollama: image: ollama/ollama:latest ports: - "11434:11434" volumes: - ollama_data:/root/.ollama volumes: ollama_data:

Dockerfile精简到极致:

FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["uvicorn", "main:app", "--host", "0.0.0.0:8000", "--port", "8000", "--reload"]

热词里“fastapi windows 打包”我们提供了PyInstaller方案:

pip install pyinstaller pyinstaller --onefile --add-data "config;config" --add-data "models;models" main.py

生成dist/main.exe,双击即可运行,连Python都不用装。

4.3 生产配置:让FastAPI扛住真实业务流量

默认FastAPI配置在生产环境会崩。我们修改uvicorn_config.py:

import uvicorn from fastapi import FastAPI app = FastAPI() if __name__ == "__main__": uvicorn.run( "main:app", host="0.0.0.0", port=8000, workers=4, # CPU核心数 backlog=2048, # 连接队列长度 timeout_keep_alive=60, # HTTP keep-alive超时 timeout_graceful_shutdown=30, # 优雅关闭超时 log_level="info", access_log=True, # 关键:启用Gzip压缩,减小JSON响应体积 use_colors=False, proxy_headers=True, forwarded_allow_ips="*", limit_concurrency=100, # 单进程并发连接数 limit_max_requests=1000, # 单进程请求数限制 )

实测数据:4核CPU+8GB内存服务器,QPS从默认120提升到380+,平均响应时间稳定在420ms。热词里“fastapi项目目录结构”我们采用标准结构:

food-optimizer/ ├── main.py # FastAPI入口 ├── adapters/ # WMS/ERP适配器 │ ├── wms_adapter.py │ └── erp_adapter.py ├── models/ # Pydantic模型 │ └── inventory.py ├── prompts/ # Prompt模板 │ └── clearance.py ├── utils/ # 工具函数 │ └── sales_speed.py ├── config.yaml # 配置中心 └── requirements.txt

4.4 监控与告警:没有监控的AI系统等于没上线

热词里“开源项目管理”提醒我们:运维才是长期价值所在。我们集成Prometheus指标:

from prometheus_client import Counter, Histogram, Gauge from fastapi import Request, Response # 定义指标 REQUEST_COUNT = Counter('food_optimizer_requests_total', 'Total requests') REQUEST_LATENCY = Histogram('food_optimizer_request_latency_seconds', 'Request latency') ACTIVE_TASKS = Gauge('food_optimizer_active_tasks', 'Active background tasks') @app.middleware("http") async def metrics_middleware(request: Request, call_next): REQUEST_COUNT.inc() start_time = time.time() response = await call_next(request) REQUEST_LATENCY.observe(time.time() - start_time) return response # 暴露指标端点 @app.get("/metrics") async def get_metrics(): return Response( media_type="text/plain", content=prometheus_client.generate_latest() )

部署Prometheus+Grafana后,关键看三个面板:

  • 决策成功率:rate(food_optimizer_decisions_success_total[1h]),低于95%告警;
  • WMS数据延迟:max by (wms_source) (time() - food_optimizer_wms_last_update_timestamp_seconds),超过30分钟告警;
  • Ollama响应时间:histogram_quantile(0.95, rate(ollama_response_duration_seconds_bucket[1h])),超过2秒告警。

注意:某客户第一次上线,Ollama响应时间飙到8秒,查日志发现是模型加载失败,自动fallback到CPU推理。我们在prompts/clearance.py里加了if model_response_time > 5: fallback_to_rules(),用硬编码规则兜底,保证业务不中断。

5. 常见问题与避坑指南:那些文档里不会写的血泪教训

5.1 WMS数据同步失败:90%的问题出在“时间戳时区”

现象:WMS导出的EXPIRY_DATE是“2024-06-15”,但系统判定为“已过期”,实际还有3天。
根因:WMS服务器时区是UTC+8,而Python默认时区是UTC。pd.to_datetime("2024-06-15")生成的是2024-06-15 00:00:00+00:00,比北京时间晚8小时。
解决方案:在WMSAdapter里强制指定时区:

df["EXPIRY_DATE"] = pd.to_datetime(df["EXPIRY_DATE"]).dt.tz_localize("Asia/Shanghai")

或者更稳妥:WMS导出时加时区标识,如2024-06-15T00:00:00+08:00。

5.2 ERP成本数据为空:不是接口问题,是会计期间没关账

现象:CST_PERIODIC_AVERAGE_COST_V视图查不到数据,但ERP里明明有成本。
根因:PAC成本需月末结账后生成,若6月12日查6月成本,而6月结账日是6月25日,则无数据。
解决方案:在ERPAdapter.get_pac_cost里加兜底逻辑:

# 若当期无数据,查上期 result = session.execute(text(""" SELECT ITEM_CODE, AVG_COST, PERIOD_START_DATE, PERIOD_END_DATE FROM CST_PERIODIC_AVERAGE_COST_V WHERE ITEM_CODE = :sku AND PERIOD_END_DATE < :as_of_date ORDER BY PERIOD_END_DATE DESC LIMIT 1 """), {"sku": sku, "as_of_date": as_of_date}).first()

5.3 Ollama响应超时:不是模型慢,是网络代理惹的祸

现象:httpx.AsyncClient调用Ollama超时,但curl http://localhost:11434/api/tags正常。
根因:Windows下Docker Desktop的WSL2网络,localhost在容器内指向Docker网桥,而非宿主机。
解决方案:在docker-compose.yml中,api服务的environment加:

- OLLAMA_HOST=http://host.docker.internal:11434

并在宿主机hosts文件加127.0.0.1 host.docker.internal。

5.4 清仓决策不一致:模型随机性导致同一批次今天7折、明天5折

现象:同一SKU同一批次,上午调用返回7折,下午调用返回5折。
根因:Phi-3默认开启temperature=0.8,引入随机性。
解决方案:在Ollama API调用中固定temperature:

response = await client.post( "http://localhost:11434/api/chat", json={ "model": "phi:3", "messages": [{"role": "user", "content": prompt}], "options": {"temperature": 0.0} # 关键!设为0 } )

5.5 FastAPI日志丢失:Uvicorn的异步日志陷阱

现象:background_tasks里的execute_decisions报错,但uvicorn.log里看不到。
根因:Uvicorn的--log-level info不捕获后台任务日志。
解决方案:在execute_decisions开头加日志初始化:

import logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", handlers=[ logging.FileHandler("logs/background.log"), logging.StreamHandler() ] ) logger = logging.getLogger(__name__)

最后分享个小技巧:我们给客户做的第一个定制化功能,不是算法,而是微信消息模板。当系统生成清仓决策,自动发微信给仓管员:“【临期预警】蒙牛纯牛奶20240510批次,剩余127箱,建议68折清仓,截止6月14日。点击查看详情→”。这比任何技术都让用户觉得“真有用”。技术终归是工具,而让一线人员愿意用、用得顺,才是开源项目真正的生命力。

返回列表