iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0
Build on Google AI

給藥袋裝一張嘴:30 天用 Android 與 Google VLM 實作高齡語音用藥助手系列 第 29 篇

Day 29|開放生態與對接標準!實作 Flask-RESTX OpenAPI / Swagger 互動式 API 文件與規範

  • 分享至 

  • xImage
  •  

✏️【本日實作紀錄:OpenAPI / Swagger 互動式 API 文件自動化】

在 Day 28 完成 OpenCV 影像品質防衛 與 Chain-of-Thought 多模態提示詞工程 後,PrescriptionVLM 系統已具備後端穩定度、AI 識別精準度與邊緣異常防衛的能力。

隨著系統準備邁入營運,未來可能需要與第三方長照關懷平台、社工管理系統或醫院藥局系統進行 API 對接。如果僅依靠口頭說明或手動維護的文件,容易因為欄位型別不一致、漏傳 Header 或錯誤碼定義不明確而增加溝通成本。

今天我們將為 PrescriptionVLM 系統導入 Flask-RESTX(Swagger / OpenAPI 3.0),自動化生成互動式 API 測試文件。完成項目包含:

  • Flask-RESTX 模組整合 — 將傳統的 Flask @app.route 升級為 RESTX Resource 與 Namespace 架構。
  • Swagger UI 互動式測試文件 — 自動生成線上記錄文件,支援線上檔案上傳與 X-Request-ID Header 帶入測試。
  • API 模型 Schema 規範定義 — 精確定義請求與回應的 JSON 結構。

一、OpenAPI / Swagger 標準化的設計

在現代 Web API 開發與跨系統對接中,導入 OpenAPI 規範有以下好處:

  • 文件即程式碼:API 文件直接由程式碼註解與 Schema 產生,當程式碼重構時文件會同步更新,避免出現文件與程式碼不符的情況。
  • 降低跨團隊對接門檻:前端工程師或第三方合作夥伴可以直接在 /doc 互動介面測試,檢視 HTTP 回應狀態碼與 JSON 欄位格式。
  • 自動化代碼生成:標準的 Swagger JSON 規格可直接供前端或 Mobile App 產生強型別 API 用戶端代碼。

二、升級專案依賴(requirements.txt)

請開啟專案根目錄的 requirements.txt,加入 Flask-RESTX 套件:

flask-restx==1.3.0

三、建構 Swagger Data Models 與 Namespace(schemas_restx.py)

在專案根目錄建立 schemas_restx.py,定義 API 請求與回應的資料模型結構:

from flask_restx import Api, fields

def init_swagger_models(api: Api):
    """定義 Swagger / OpenAPI 回應結構 Schema"""

    medicine_model = api.model('MedicineItem', {
        'name': fields.String(required=True, description='藥品名稱', example='阿斯匹靈 (Aspirin)'),
        'dosage': fields.String(required=True, description='單位劑量', example='100mg / 1 錠'),
        'timing': fields.String(required=True, description='服用時間與頻率', example='每日一次,早餐後')
    })

    metrics_model = api.model('PerformanceMetrics', {
        'vlm_latency_ms': fields.Float(description='Gemini VLM 耗時 (ms)', example=1250.5),
        'line_latency_ms': fields.Float(description='LINE 推播耗時 (ms)', example=180.2)
    })

    prescription_response_model = api.model('PrescriptionAnalysisResponse', {
        'status': fields.String(required=True, description='執行狀態', example='success'),
        'cached': fields.Boolean(required=True, description='是否命中 Redis 快取', example=False),
        'request_id': fields.String(required=True, description='全域請求 Trace ID', example='req-uuid-12345'),
        'metrics': fields.Nested(metrics_model, description='分段效能耗時'),
        'data': fields.Nested(api.model('PrescriptionData', {
            'is_prescription': fields.Boolean(description='是否為有效藥袋照片', example=True),
            'confidence_score': fields.Float(description='模型辨識信心度', example=0.98),
            'medicines': fields.List(fields.Nested(medicine_model), description='解析藥品清單'),
            'safety_warnings': fields.List(fields.String, description='注意事項與警語', example=['服藥後請勿駕駛車輛']),
            'spoken_summary': fields.String(description='語音摘要簡述', example='這是您的止痛藥,請記得早餐後配水服用一錠。')
        }))
    })

    return prescription_response_model

四、重構 Flask 主程式掛載 Swagger UI(app.py)

開啟 app.py,掛載 Flask-RESTX 介面並配置 /doc 路徑:

from flask import Flask, request, jsonify, g
from flask_restx import Api, Resource, reqparse
from werkzeug.datastructures import FileStorage
from schemas_restx import init_swagger_models
from logger import logger

app = Flask(__name__)

# 初始化 Flask-RESTX API 物件
api = Api(
    app,
    version='1.0.0',
    title='PrescriptionVLM API 服務文件',
    description='整合 Gemini 多模態 VLM、LINE Messaging API 與高齡化無障礙體驗之藥單解析後端系統',
    doc='/doc', # 指定 Swagger UI 存取路徑為 /doc
    prefix='/api/v1'
)

ns = api.namespace('prescription', description='藥單解析與語音關懷相關 API')
prescription_model = init_swagger_models(api)

# 設定檔案上傳 Request Parser
upload_parser = reqparse.RequestParser()
upload_parser.add_argument('image', location='files', type=FileStorage, required=True, help='藥袋照片檔案 (JPG/PNG)')
upload_parser.add_argument('X-Request-ID', location='headers', type=str, required=False, help='全域 Trace ID')

@ns.route('/analyze-prescription')
class PrescriptionAnalysisResource(Resource):
    
    @ns.expect(upload_parser)
    @ns.response(200, '解析成功', prescription_model)
    @ns.response(400, '圖片品質不佳或參數錯誤')
    @ns.response(429, '已達流量限制')
    @ns.response(500, '系統內部錯誤')
    def post(self):
        """上傳藥袋照片進行 AI 多模態辨識與關懷推播"""
        req_id = getattr(g, "request_id", "N/A")
        logger.info(f"收到 Swagger/Client 藥單解析請求 [ReqID: {req_id}]")

        # 呼叫原本 Day 28 優化後的完整解析邏輯 ...
        return {
            "status": "success",
            "cached": False,
            "request_id": req_id,
            "metrics": {"vlm_latency_ms": 1200.0, "line_latency_ms": 150.0},
            "data": {
                "is_prescription": True,
                "confidence_score": 0.98,
                "medicines": [{"name": "阿斯匹靈", "dosage": "1 錠", "timing": "早餐後"}],
                "safety_warnings": ["請遵照醫囑服用"],
                "spoken_summary": "這是您的阿斯匹靈,早餐後服用一錠。"
            }
        }, 200

五、測試與成果驗證

1. 啟動服務並訪問 Swagger UI

在 Terminal 啟動服務:

docker-compose up --build -d

開啟瀏覽器連線至:
http://localhost:5000/doc

  • 預期成果:
    1. 瀏覽器成功載入 Swagger UI 互動介面,頂部呈現 PrescriptionVLM API 服務文件 v1.0.0。
    2. 可以展開 /api/v1/prescription/analyze-prescription 端點,看到完整清晰的 Response Schema。

2. 在 Swagger UI 執行線上 API 測試

  1. 在 Swagger UI 點擊 Try it out。
  2. 在 image 欄位選擇一張本地藥袋照片。
  3. 填寫 optional header X-Request-ID: swagger-test-1234。
  4. 點擊 Execute 按鈕。
  5. 預期成果:下方直接回傳 HTTP 200 與格式齊全的 JSON 結果。

六、版本控制與提交 GitHub

測試通過後,將 Day 29 的修改提交至 GitHub:

git add requirements.txt schemas_restx.py app.py
git commit -m "保留雙引號 改填寫自己要記錄的標記 ex.鐵人賽第二十九天"
git push

七、本日小結與明日預告

今天我們透過 Flask-RESTX 為 PrescriptionVLM 建立了符合 OpenAPI 規範的互動式文件,完成了系統邁向開放生態與跨系統對接的基礎建設。

明天(Day 30),我們將迎來 30 天的最終完結篇!進行 PrescriptionVLM 系統整體架構回顧、30 天成果總結與未來展望!


上一篇
Day 28|準確度與防禦力升級!多模態 VLM 提示詞工程調優與邊緣異常防衛
下一篇
Day 30|AI 照護的最後一哩路!PrescriptionVLM 系統總覽、30 天挑戰回顧與未來展望
系列文
給藥袋裝一張嘴:30 天用 Android 與 Google VLM 實作高齡語音用藥助手 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言