將前一篇程式邏輯,套用到 MAF
前一篇用 Google ADK 產生 VideoTimeline、透過 Jev 檢查每個分鏡應該要被修改的邏輯。今天換個目標:同樣的檢查,但是改用 Microsoft Agent Framework (MAF)。
以下是我採雷經驗分享跟成果:
FoundryChatClient 需要 Azure AD
Agent Framework 官方的 Azure client 是 agent_framework.foundry.FoundryChatClient。而 credential 參數的型別:
AzureCredentialTypes = TokenCredential | AsyncTokenCredential這表示 MAF 只能用 Azure AD 憑證(`az login` 或 service pncal),如果要使 API key 是不行的。
Workaround: MAF 有 agent_framework.openai.OpenAIChatClient,支援 api_key + base_url
另外 Azure endpoint 記得指到 https://<resource>.services.ai.azure.com/openai/v1
from agent_framework import Agent, ChatOptions
from agent_framework.openai import OpenAIChatClient
client = OpenAIChatClient(
model=os.environ.get(”FOUNDRY_MODEL”),
api_key=os.environ.get(”FOUNDRY_API_KEY”),
base_url=os.environ.get(”FOUNDRY_PROJECT_ENDPOINT”),
)
director_agent = Agent(
client=client,
instructions=INSTRUCTIONS,
name=”director_agent”,
default_options=ChatOptions(response_format=VideoTimelineAzure),
)Pydantic discriminated union 透過 Azure foundry structured output 回傳 400
昨天的 `VideoTimeline.segments` 用 Pydantic 的 discriminated union(`Field(discriminator=”tye”)`)。Gemini/ADK 吃得下,但如果將 schema 綁到 `rspnse_format`,Azure 會直接報:
openai.BadRequestError: Error code: 400 - {’error’: {’message’:
“Invalid schema for response_format ‘VideoTimeline’: In
context=(’properties’, ‘segments’, ‘items’), ‘oneOf’ is not permitted.”}}
Pydantic 的 discriminated union 會產生 JSON Schema 的 oneOf,但 OpenAI/Azure 的 structured output 嚴格模式 不允許 `oneOf`,只接受 `anyOf`。
# agent_framework_demo/schemas.py
class VideoTimelineAzure(BaseModel):
model_config = ConfigDict(extra=”forbid”)
title: str
target_platform: str
total_duration_sec: int
segments: list[Union[TitleCardSegment, PhotoSegment, VideoClipSegment, CreditsSegment]]解決方式:拿掉 discriminator,改成單純 `Unio` (發現不會影響後面 rebuild `VideoTimeline`)
response = await agent.run(prompt)
timeline = VideoTimeline.model_validate(response.value.model_dump())jev_client.py
`check_segment()` 直接使用昨天同一份,包括 `VideoTimeline`,透過 Jev 串接到後面的 LLM endpoint 達到可以互換 Gemini / Azure foundry endpoint。
from common.jev_client import check_segment
for seg in timeline.segments:
result = check_segment(seg.id, seg.duration_sec, seg.caption)
實際測試結果
🎬 同個 schema,用兩種 Agent framework libraries:Microsoft Agent Framework 重現 ADK 導演 Agent (20s, YouTube Shorts(9:16))
[seg-01-neon-hook] title_card 2.0s ✅ (jev confidence=0.50)
文字: 同一份 Agent 規格 能跨雲重現嗎?
[seg-02-adk-blueprint] video_clip 3.0s ✅ (jev confidence=0.48)
文字: 昨天:用 ADK 打造「導演 Agent」
[seg-03-spec-core] photo 3.0s ✅ (jev confidence=0.43)
文字: 保留核心規格:角色、流程、工具、輸出
[seg-04-framework-switch] video_clip 4.0s ✅ (jev confidence=0.41)
文字: 今天切換 Microsoft Agent Framework 重新接上模型與工具
[seg-05-orchestration] video_clip 3.0s ✅ (jev confidence=0.47)
文字: 讓 Agent 拆解任務、調度步驟、整合結果
[seg-06-result] photo 3.0s ✅ (jev confidence=0.41)
文字: 框架不同,導演邏輯依然可重現
[seg-07-credits-cta] credits 2.0s ⚠️ NEEDS REVIEW (jev confidence=0.70)
文字: 一份規格,兩套雲端。追蹤看完整實作 ⚡
共 7 個片段,1 個被 Jev 標記需要人工複查。把這七段直接餵給 `gemini-omni-1.1-flash` 生成、串接成一支完整影片:
{% youtube 7bvcEy-UHMU %}
Day 2 心得
換了雲端,`VideoTimeline` schema 跟 `check_segment` 這條檢查邏輯完全沒變,證明了 Jev 作為把關角色,可以與任一家 Agent framework + LLM vendor 無關。
下一篇會將通過 Jev 檢查、真的能唸完的版本,完整走一次生成流程,看看「文字跟秒數對得上」的結果實際長什麼樣。
---
完整程式碼:jimmyliao/jev-storyboard-lab - https://github.com/jimmyliao/jev-storyboard-lab



