用FastAPI搭建图像诊断AI接口
教程以玉米叶片诊断为例,通过FastAPI接收图片、调用Gemma 4并返回结构化JSON。流程涵盖上下文文件、提示词管理、输入校验和测试,可迁移到其他图像分析任务。
AI 深度解读
45 分钟,把一张玉米叶片照片变成 JSON 诊断结果:这篇 AgriScan 工作坊用 Python、FastAPI 和 Gemma 4,演示了一条从模型调用到 API、测试和演示交付的完整路径。它真正值得借鉴的,是如何给 AI 应用安排清楚的代码边界。
但「接口能返回结果」离「诊断可信」还有距离。原文提供的是 Hack Day 原型教程,没有农业诊断准确率,也没有生产环境验证。读它时,可以带走工程组织方式,同时把模型效果和接口可靠性分开判断。
先把项目规则写清楚,AI 才知道往哪里改
AgriScan 的请求链路很短:用户上传照片,FastAPI 检查输入,app/gemma.py 带着 app/prompts.py 中的提示词调用模型,再解析 JSON 并返回。
作者要求在写代码之前,先创建 AGENTS.md。里面明确 Python 3.11、FastAPI、google-genai,指定模型为 gemma-4-26b-a4b-it,启动命令是 fastapi dev app/main.py,测试命令是 pytest。
更有用的是四条约束:密钥来自 GEMINI_API_KEY;模型调用集中在 app/gemma.py;提示词集中在 app/prompts.py;每条路由都要有不调用真实模型的测试。它们把容易散落在项目各处的东西,提前收到了固定位置。
上下文文件的价值,是让后续修改有明确边界。
作者据此建议,在 Copilot Chat 的 agent 模式中直接提出小任务,例如添加无框架的 HTML 上传页,调用 POST /diagnostic 并显示结果。需求可以短,前提是项目规则已经写清。至于 Copilot 是否按预期读取并遵守这些规则,仍要检查实际改动,原文没有提供遵循率之类的验证。
环境准备也很朴素:依赖只有 fastapi[standard]、google-genai 和 pytest,通过 python -m venv .venv 创建虚拟环境,再运行 pip install -r requirements.txt。密钥不能进仓库;如果使用 .env,就要确认它在 .gitignore 中。
从图片到 JSON,真正需要约定哪些东西
按原文说明,Gemma 4 是 Google 的开放权重模型,支持文本和图片;教程通过 Gemini API 调用,免去本地运行模型的准备。文中列出两个模型标识:gemma-4-26b-a4b-it 和 gemma-4-31b-it,实际使用前者。免费密钥和模型可用性是原文的描述,本文没有额外核验。
提示词定义了三件事:服务对象是多哥的小农户,输入是玉米叶片照片,输出必须是法语 JSON。字段固定为 maladie、confiance、conseil,分别表示疾病、置信程度和一句行动建议。置信程度只能取 faible、moyenne、forte,非玉米照片则返回 photo non valide。
这让业务需求变得具体:模型不只要「看图回答」,还要遵守字段、语言和异常输入约定。
下面这段代码把模型调用集中起来,图片作为内容的一部分发送,业务提示词放进 system_instruction:
import json
from google import genai
from google.genai import types
from app.prompts import DIAGNOSTIC_PROMPT
MODEL = "gemma-4-26b-a4b-it"
def analyser_image(image_bytes: bytes, mime_type: str) -> dict:
"""Envoie la photo à Gemma 4 et renvoie le diagnostic sous forme de dict."""
client = genai.Client() # créé ici pour que les tests n'aient pas besoin de clé
response = client.models.generate_content(
model=MODEL,
config=types.GenerateContentConfig(system_instruction=DIAGNOSTIC_PROMPT),
contents=[
types.Part.from_bytes(data=image_bytes, mime_type=mime_type),
"Analyse cette photo.",
],
)
return extraire_json(response.text)
要关注最后一行:拿到 response.text 之后,程序还要负责解析,模型回复并没有直接成为可靠的数据结构。
作者的 extraire_json 从第一个 { 截到最后一个 },再交给 json.loads。这能处理部分解释文字或 Markdown 围栏,却不保证只有一个 JSON 对象,也不校验字段是否齐全、枚举是否正确。
要求模型输出 JSON,与程序验证输出契约,是两件事。
FastAPI 路由只接收 image/jpeg 和 image/png,其他类型返回 415;文件上限是 5 * 1024 * 1024 字节,超出返回 413;解析触发 ValueError 时返回 502。作者还建议把 photo non valide 映射为 422,但这是后续练习,初版路由没有实现。
测试通过,究竟证明了哪一层正确
原文最适合日常 AI 编程借鉴的,是用 monkeypatch 替换模型调用。它让路由测试不用消耗配额,也不依赖网络或密钥。
下面两项测试分别验证正常返回和拒绝 PDF,代码保留原样:
from fastapi.testclient import TestClient
from app import gemma
from app.main import app
client = TestClient(app)
def test_diagnostic(monkeypatch):
faux = {"maladie": "rouille", "confiance": "moyenne", "conseil": "..."}
monkeypatch.setattr(gemma, "analyser_image", lambda data, mime: faux)
r = client.post("/diagnostic", files={"photo": ("feuille.jpg", b"fake", "image/jpeg")})
assert r.status_code == 200
assert r.json()["maladie"] == "rouille"
def test_refuse_un_pdf():
r = client.post("/diagnostic", files={"photo": ("doc.pdf", b"%PDF", "application/pdf")})
assert r.status_code == 415
要注意 monkeypatch.setattr(gemma, "analyser_image", ...)。路由采用 gemma.analyser_image(...) 调用,测试替换的是同一个模块属性,因此能拦住真实请求;如果换了导入方式,也要重新确认替换目标。
测试里的图片内容只是 b"fake"。它证明上传请求能进入路由、假结果能返回,无法证明接口识别了真实 JPEG,更无法证明玉米疾病判断正确。
原文另有一项 JSON 提取测试,并以「三个测试通过、不需要 API 密钥」描述预期结果。但提供的正文中,部分代码围栏和换行已经损坏,不能直接把整篇复制运行,也没有附测试日志。
作者安排的人工检查是:先用 python essai.py 验证连通性,再上传真实叶片和无关照片,最后在 http://127.0.0.1:8000/docs 操作 POST /diagnostic。每次修改后读 diff、跑 pytest、试接口、提交代码。自动测试与真实调用各负责一部分,这个安排是合理的。
我的判断
我赞同它对项目边界的处理。单独管理提示词、集中封装模型调用、用假响应测试路由,能让一个小原型保持可理解,也方便后续换模型或改提示词。
但「45 分钟」是教程目标,原文没有给出不同参与者的完成时间;「JSON 可靠」也说得偏满,当前实现缺少字段校验。提示词中出现 confiance,不代表这个置信程度经过校准,尤其非玉米照片还被要求固定返回 forte,前端不能把它直接展示成准确率。
接口还有明确的工程缺口:async def 路由中执行同步模型调用;整份文件读入后才检查大小;上传类型只检查声明的 MIME;网络失败、配额耗尽和超时没有完整处理。这些不妨碍工作坊演示,却限制了它直接上线的适用范围。
与当前的提示词约束加字符串截取相比,用 Pydantic 等工具校验解析后的字段,更方便约束 API 返回值;模型侧是否支持所需的结构化输出机制,则需要另行确认。原文没有做方案对比,也没有诊断数据集或专家复核,因此疾病识别效果只能保留判断。
另外,作者建议 AI 改错方向时运行 git restore .。这会丢弃工作区中尚未暂存的修改,使用前应确认改动范围,避免连自己的有效修改一起撤销。
能拿来做什么
- 做图片识别原型时,可以沿用「上传路由、模型适配层、提示词文件」的结构,先通过 FastAPI 的
/docs验证请求链路,再添加 HTML 页面。 - 给前端对接 AI 接口时,应明确三个字段的取值与错误状态,把 413、415、422、502 分别转成可理解的提示,并对模型输出补充字段校验。
- 编写测试时,可以先替换真实模型,覆盖路由和异常分支;再用少量真实照片检查模型表现,分别记录程序错误与识别错误。
- 使用 Copilot 扩展项目时,可以先写好
AGENTS.md,再按小任务推进,每次检查 diff 并运行测试,确认理解改动后再提交。