
在 Agent 開發中,instruction 往往是我們投注最多心力的地方。角色設定、行為規範、步驟拆解、注意事項 —— 一段成熟的 System Instruction 動輒數百字,而它確實能大幅提升模型的表現。
然而,這裡有一個容易被忽略的事實:Instruction 並不是「設定」,而是「每一輪都要重新說服模型一次」的輸入。
模型不會「記住」規則。它在每一次推理時,都要重新閱讀那段文字、重新判斷該不該遵守。這意味著遵守率會受到對話長度、使用者措辭、甚至工具回傳內容的影響 —— 它本質上是一個機率問題,而不是一個布林值。
期待在這篇文章中,能夠讓大家把第一個 Agent 真的跑起來,並且理解為什麼 Instruction 寫得再好,也只能把遵循率推到某個天花板。
Google ADK 的 CLI 是透過目錄結構來定位 Agent 的,因此專案的組織方式必須遵循固定慣例:
leave_copilot/
├── __init__.py # from . import agent
├── agent.py # 必須定義 root_agent
└── .env # Google ADK 會自動載入
.env 至少要有金鑰,否則第一次呼叫模型就會失敗:
GOOGLE_API_KEY=your-key-here
agent.py 裡必須有一個名為 root_agent 的變數,這是 Google ADK 的約定:
from google.adk import Agent
root_agent = Agent(
name="leave_copilot",
model="gemini-3.8-flash",
instruction="你是差勤助手,協助同仁查詢與處理請假申請。",
)
然後:
.venv/bin/adk run leave_copilot # 互動式 CLI
.venv/bin/adk web . # Web UI,可挑選目錄下的多個 agent

instruction 是 Agent 的核心設定。它不只是「您是誰」,更重要的是告訴模型工具該怎麼用。
官方範例展示的結構是這樣:
instruction="""You are an agent that provides the capital city of a country.
When a user asks for the capital of a country:
1. Identify the country name from the user's query.
2. Use the `get_capital_city` tool to find the capital.
3. Respond clearly to the user, stating the capital city."""
注意它用編號步驟描述流程。這不是文體偏好,而是有實際作用——模型對編號清單的遵循度通常高於一段散文。
套用到 Leave Copilot,難點 1 和難點 3 的規則就寫在這裡:
instruction="""你是差勤助手,協助同仁查詢與處理請假申請。
處理假單修改請求時,必須依下列步驟:
1. 先用 `search_leaves` 找出目標假單,取得真實的 leave_id。
絕對不要自行推測或編造 leave_id。
2. 確認假單目前狀態。
3. 呼叫 `update_leave_status` 時,狀態只能依
draft → submitted → approved → taken 逐級推進,不可跳級。
執行破壞性操作(withdraw_leave、cancel_approved_leave)時:
- 系統會要求使用者確認,這是必要流程。
- 若使用者拒絕(decline),立即停止,不得改用其他工具達成相同目的。
- 若使用者取消(cancel),詢問使用者的意向,不要直接重試。
"""
**先講結論:這段 Instruction 寫得再仔細,模型也不會完全照做。**評測階段的基準線測試會量出它的遵循率。這正是整個系列要證明的事——Prompt 有極限,而權重可以內化。
原因可以從上面那張循環圖看出來:模型每一輪都要重新讀這段文字、重新判斷。它不是「記住」了規則,而是「每次都要被說服一次」。而說服的成功率受對話長度、使用者措辭、甚至工具回傳內容的影響。
微調改變的是這件事——規則進到權重裡,就不必每次重新說服。
但這段還是得寫好。評測階段會先把 Prompt 調到極限,再談微調;否則「微調比 Prompt 好」的結論會不公平。
instruction 支援 {var} 語法,從 session state 取值:
instruction="""你是差勤助手,目前服務的工程師是 {engineer_name},
負責的系統是 {system_scope}。"""
這在多租戶或多環境場景很實用——同一個 Agent 定義,不同 session 帶不同上下文。
不過有一個地方要先知道:{var} 在 state 裡找不到對應的 key 時會直接丟 KeyError,不是留白。想讓它可以缺席,在變數名後面加問號:
instruction="…目前服務的工程師是 {engineer_name?}。"
加了問號之後找不到就代換成空字串,並在 log 留一行記錄。這個差別在 session 剛建立、state 還是空的那一輪特別容易踩到。
output_key 會把 Agent 的最終文字回應自動寫進 session state:
triage_agent = Agent(
name="triage_agent",
model="gemini-3.8-flash",
instruction="分析假單內容,判斷嚴重程度,只回答 P1 / P2 / P3。",
output_key="severity",
)
之後 session.state['severity'] 就有值了,下一個 Agent 可以用 {severity} 讀它。這個階段後面做 multi-agent 交接時,這是最輕量的傳遞方式。
Python 版 Google ADK 會自動把函式包成工具,型別提示與 docstring 直接轉成 schema:
def get_capital_city(country: str) -> str:
"""Retrieves the capital city for a given country."""
capitals = {"france": "Paris", "japan": "Tokyo"}
return capitals.get(country.lower(), f"Sorry, I don't know...")
capital_agent = Agent(
name="capital_agent",
model="gemini-3.8-flash",
tools=[get_capital_city],
)
docstring 就是模型看到的工具說明。 這件事的份量比它看起來重——評測階段優化 tool description 時,改的就是這段文字,而它能帶來的改善幅度會讓人意外。
這是今天最重要的一個習慣,而且很容易被跳過。
Google ADK 的每次執行都會產生一串 event,裡面完整記錄了:模型的思考、工具呼叫(名稱與參數)、工具回傳結果、最終回應。
透過 adk web 可以在瀏覽器中檢視這些 Event。而我要特別建議的是:從今天起,每次測試都把 Event Stream 保存下來。
理由有兩個,都在後面才會顯現:
Content / Part 結構會被轉成訓練樣本。也就是說,您今天隨手測試的那些對話,就是訓練資料階段的原料。 現在存下來,比之後再重跑一遍便宜得多。
一個最小的記錄做法是在 adk web 裡手動導出,但更可靠的是直接走 API:
.venv/bin/adk api_server &
curl -X POST http://localhost:8000/apps/leave_copilot/users/u_dev/sessions/s_001 \
-H "Content-Type: application/json" -d '{}'
curl -X POST http://localhost:8000/run \
-H "Content-Type: application/json" \
-d '{
"appName": "leave_copilot",
"userId": "u_dev",
"sessionId": "s_001",
"newMessage": {"role": "user", "parts": [{"text": "有哪些未處理的假單?"}]}
}' > traces/run_001.json
/run 會把所有 event 收集成一個陣列回傳,存成檔案就是一筆完整軌跡。評測階段用的工具走的是同一組 endpoint——現在熟悉它,之後會省事。
今天建立的 Agent 在功能上還相當基礎,但它讓我們第一次觀察到 LLM 驅動的執行路徑 —— 模型如何理解 Instruction、如何選擇工具、以及如何在同樣的輸入下產生不同的行為。
總結來說,今天提到三個重點:
明天,我將會提到如何把先前建立的 Leave Copilot MCP Server 正式接上這個 Agent,並且比較兩條人機確認的路線該怎麼選。屆時模型會開始面對那四個刻意植入的難點,而它犯的錯,正是這個系列真正的起點,再請大家回來閱讀後續章節囉!

instruction 寫法、{var} 狀態插值、output_key、tools 自動包裝/run 請求格式Agent 基本用法查證日期:2026-09-03。本篇的 API、CLI 指令與 {var?} 的行為,都在 google-adk 2.7.1 / Python 3.13 的環境確認過。
大家好,我是 Simon 劉育維,是一位 AI 領域解決方案專家,目前也擔任 Google Cloud AI 領域開發者專家 (GDE),期待能夠幫助企業導入人工智慧相關技術解決問題。如果這篇文章對您有幫助,歡迎在我的 Linkedin 上留言提供意見,並與我一起討論有關人工智慧的主題,期待能夠對大家有所幫助!
我的個人部落格資訊:https://medium.com/@simon3458