iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0
Build on Google AI

AI 策展人:用 Google ADK 打造會思考、會介紹的 3D 展示平台系列 第 19 篇

Day 19|`view_renderer` 與 `vision_judge`:6 張照片、一條「只能降級」的規則

  • 分享至 

  • xImage
  •  

品管員(qa-kit,Day 15)靠規則量數字。有些毛病數字看不出來,要用眼睛看:圓柱被切成多邊形、小零件被瘦身瘦到消失、該亮的地方變黑。所以接力隊(expo_director)的第三棒 vision_qa_loop 裡,有兩位成員專門處理「看圖」:先拍照的 view_renderer,再看照的 vision_judge。

比喻→術語對照表

比喻 術語 白話
拍照的人 view_renderer(LlmAgent,只掛一支工具) 拿成品拍 6 張照片
看照片的人 vision_judge(LlmAgent,掛兩支工具) 看照片、判斷有沒有缺陷、寫一筆紀錄
相機 render_views(工具)→ qa_kit.render_views → Blender Workbench(headless) 後台 3D 軟體,一次啟動、換 6 個鏡頭
照片放進共用相簿 artifact(tool_context.save_artifact) ADK 的檔案暫存空間,工具存、別的工具取
看照片的人把照片「調出來看」 load_artifacts(ADK 內建工具) 把相簿裡的圖加進這次要問模型的請求裡
判讀紀錄單 record_vision_verdict(工具)→ asset.qa.vision_qa 寫「看了沒、合不合格、一句話」
一圈:拍照 → 看照片 → 自癒 vision_qa_loop(LoopAgent,max_iterations=3) 今天講前兩棒,自癒下一篇

接力隊裡的位置

vision_qa_loop = LoopAgent(
    name="vision_qa_loop",
    description="算圖 → 視覺判讀 → 自癒,最多三圈",
    max_iterations=3,
    sub_agents=[view_renderer, vision_judge, healer],
)

render_views 原本在規劃裡是「迴圈裡的一個工具」,但 LoopAgent.sub_agents 只能放 agent,所以包成 view_renderer 這個只拿一支工具的 LlmAgent(agents/expo_director/CONTRACT.md 記錄的三點差異之一)。

拍照:render_views

async def render_views(asset_id: str, profile: str, size_px: int = 512, tool_context=None):
    """Blender Workbench headless 出 6 個視角的 PNG 到 reports/views/,並存成 artifacts 供視覺判讀。"""

它是 17 支工具裡唯一的 async 工具(要 await tool_context.save_artifact(...))。流程:

  1. 找出要拍的檔案:geometry/exhibit_kiosk_qa.glb(_qa_source_uri:Y-up、公尺、未經 Draco 壓縮,因為 trimesh 讀不了 Draco 幾何,見 Day 15)。
  2. 呼叫 qa_kit.render_views(...),產出 6 張 PNG 到 reports/views/。
  3. 每張 PNG 同時存成 artifact(mime_type="image/png")。
  4. 把 {attempt, views: [...], size_px} 寫進 state["views"]。

失敗(Blender 不在、渲染出錯)一律回 envelope.fail("qa.render_views", "render_failed", ...),不炸管線——程式註解寫得很直白:「Blender 不在、渲染失敗都只是降級」。

qa_kit.render_views 本身(packages/qa-kit/qa_kit/render.py + _blender_render.py):

  • 用 contracts_py.kernels.require("blender") 取得 Blender 路徑,subprocess 以 --background --factory-startup 執行腳本。
  • 一次啟動、換 6 次鏡頭:front/back/left/right/top/iso,正交相機,Workbench 引擎,STUDIO 打光,材質顏色,背景透明(RGBA)。
  • 逾時 120 秒。

實測(ast_gearbox_demo 的 QA 檔):render-views 約 2.6 秒(含 uv run 啟動)。6 張 PNG 各約 105–124 KB(合計約 650 KB),尺寸 512×512,RGBA。

看照片:vision_judge

vision_judge = LlmAgent(
    name="vision_judge",
    model=MODEL,
    description="看多視角圖判讀破洞、法線反轉、過度減面",
    instruction=_STATE_BRIEF + """
先用 `load_artifacts` 取出剛才算的 PNG,再判讀:有沒有破洞、法線反轉(該亮的地方變黑)、
過度減面(圓柱變多邊形、小零件消失)。然後呼叫一次 `record_vision_verdict`:
- ran=true;passed=true 表示「看起來沒問題」,passed=false 表示「看得出缺陷」。
- issue_description 用一句中文描述你看到的問題,看不出問題就寫「無明顯缺陷」。

**你只能降級**:qa-kit 的規則判 fail 就是 fail,你判 pass 也不會把它改回來;
你唯一能做的是「規則沒抓到但眼睛看得出來的問題」。看不到圖就 ran=false,不要用猜的。
""",
    tools=[catalog.record_vision_verdict, load_artifacts],
    ...
)

load_artifacts 是 ADK 內建工具,參數是 artifact_names(字串陣列);它把相簿裡的檔案加進這次送給模型的請求,模型才真的「看得到」圖。

判讀紀錄單:record_vision_verdict

寫進 asset.qa.vision_qa(契約的 VisionQA 物件):

vision = {"ran": bool(ran), "model": None if not ran else str(state.get("model") or "gemini"),
          "pass": bool(passed) if ran else None,
          "issue_description": issue_description or None,
          "views": list((state.get("views") or {}).get("views") or [])}

「降級」的程式:

if ran and not passed:  # 只能降級
    qa["status"] = "fail" if qa.get("status") in (None, "pass", "warn") else qa["status"]

我用 ast_day19(QA 原本是 pass)直接呼叫工具,實測三種情況:

呼叫 之前的 qa.status 之後
ran=True, passed=False pass fail
ran=True, passed=True fail fail(不改回去)
ran=False fail fail(不動),pass/model 都是 None

這支工具不會動 score。

沒有 API key:離線劇本

def plan_judge(state):
    ...
    return [("record_vision_verdict", {"asset_id": ..., "ran": False, "passed": False,
        "issue_description": "沒有模型憑證,視覺判讀未執行(qa-kit 規則結果不受影響)。"})]

離線走的是 ran=False:記下「沒看」,不降級任何東西。離線整條管線實跑,這一段事件是:

→ [view_renderer] render_views({"asset_id": "ast_day18", "profile": "exhibit.kiosk", "size_px": 512})
← [view_renderer] render_views: ok=True
[view_renderer] (離線腳本)第 0 次算圖完成,共 6 張。
→ [vision_judge] record_vision_verdict({... "ran": false, "passed": false, ...})
← [vision_judge] record_vision_verdict: ok=True
[vision_judge] (離線腳本)沒有 API key,vision_judge 未執行;視覺 QA 只能降級,不會把 qa-kit 的結果改成通過。

離線劇本沒有呼叫 load_artifacts(整條 14 個工具呼叫裡沒有它):沒有模型,就沒有人要看那些圖。

評測題:vision_qa_can_only_downgrade

agents/evals/expo_director.evalset.json 的第 5 題問:「視覺判讀看起來沒問題的話,可以把 qa-kit 判定的 fail 改成 pass 嗎?」參考答案是「不行」並說明原因。軌跡比對離線能跑;中文回答比對需要 GOOGLE_API_KEY(Day 25 細講)。

還沒做、還沒驗證的事

  • 真模型看 6 張圖、判斷缺陷:沒有 GOOGLE_API_KEY,完全沒實測;load_artifacts 在真模型下是否正確把圖送進請求,也沒測。
  • 512×512、正交、Workbench 打光的照片,模型看不看得出小零件消失或細微破洞:沒驗證。
  • 契約的 suggested_ratio_adjustment 欄位,工具沒有寫入。
  • ran=True 時 model 欄位用 state.get("model") or "gemini";沒有 state["model"] 就寫字串 gemini,不是實際的模型型號。
  • 「不合格」之後怎麼辦(自癒):下一篇。

下一篇:healer 與自癒迴圈。


上一篇
Day 18|開工前,先問一句:這批貨要磨多細?
下一篇
Day 20|【里程碑】自癒迴圈:說不合格之後,團隊自己調參重做
系列文
AI 策展人:用 Google ADK 打造會思考、會介紹的 3D 展示平台 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言