1. 總覽
AI 和科技領域的發展速度非常快,每天都有新模型、論文和產品推出。如果每天早上都能取得當日新聞摘要、精簡的總結,並產生 PDF,就能解決這個問題。但過去要建構這類摘要代理程式,就得挑選框架、在 Python 中定義工具、編寫自動化調度管理迴圈、封裝容器,然後部署至 Cloud Run。在代理程式發出任何網頁要求之前,所有這些作業都已完成。
Gemini API 中的受管理代理改變了這個等式。您會編寫兩個 Markdown 設定檔和一個預先建構的算繪器指令碼,發出一個 API 呼叫,然後啟動實際的 Ubuntu 沙箱、瀏覽網頁、撰寫摘要並產生 PDF。沒有容器。無需部署。完全不需要自動化調度管理程式碼。
在本程式碼研究室中,您將從空白函式開始,逐步建構出可運作的每日摘要代理程式。
建構目標
- 在實際的 Linux 沙箱中建立及執行第一個受管理代理
- 提供詳細指令來自訂代理程式
- 下載代理程式的 PDF 輸出內容
- 繼續對話,進一步修正摘要,不必重新擷取網頁內容
- 儲存代理程式設定,並在日後執行時依 ID 叫用
軟硬體需求
- Python 3.10 以上版本
- 已啟用計費功能的 Gemini API 金鑰:aistudio.google.com/api-keys
- 約$1 美元的 API 抵免額 (每次完整執行費用為 $0.30 美元至 $1.30 美元)
2. 什麼是 Gemini API 中的受管理代理?
三種層級的 AI 系統
開始編寫程式碼前,請先瞭解 Managed Agents 相對其他兩種替代方案的適用情況:
等級 | 簡介 | 誰負責管理基礎架構? |
標準 LLM | 你輸入提示,它會以文字回覆。不需動手、不需記憶、不需網路。 | 不適用:無法自行執行任何動作 |
自行代管的代理程式 | 您需要將 ADK/LangChain/AutoGen + Docker + 工具 + 記憶體連接起來。 | 您:所有項目 (或 Agent Engine 等代管平台) |
代管代理程式 | 你為其設定目標,Google 會提供安全沙箱。代理會編寫程式碼、執行程式碼、讀取錯誤、搜尋網路,並自主修正錯誤。 | Google:所有內容 |
本程式碼研究室將介紹第三列。您提供工作和設定檔,其餘作業則由 Google 負責。
您可使用 ADK + Cloud Run 建構的項目
如要建構新聞摘要代理,瀏覽網路、執行 Python 並產生 PDF,您需要 ADK + Cloud Run 才能完成所有這些作業:
# agent.py: define tools and wire up the agent
from google.adk.agents import LlmAgent
from google.adk.tools import google_search, built_in_code_execution
agent = LlmAgent(
name="digest-agent",
model=MODEL,
instruction=AGENTS_MD, # your editorial voice and rules
tools=[google_search, built_in_code_execution],
)
# app.py: serve the agent over HTTP
from google.adk.runners import FastApiRunner
runner = FastApiRunner(agent=agent)
app = runner.app
# pdf_tool.py: custom tool, install reportlab, render PDF
# scraper.py: custom tool, fetch each news source
# streaming.py: wire agent events to your SSE endpoint
# Dockerfile: package everything
FROM python:3.12
COPY . /app
RUN pip install google-adk reportlab requests
CMD ["uvicorn", "app:app", "--host", "0.0.0.0"]
# Deploy to Cloud Run
gcloud run deploy digest-agent \
--image gcr.io/your-project/digest-agent \
--set-secrets GEMINI_API_KEY=gemini-key:latest \
--memory 2Gi
也就是在代理程式執行一次之前。您仍擁有沙箱隔離功能 (因此代理程式不會損壞伺服器)、套件安裝、工具呼叫之間的狀態管理,以及將事件傳送至用戶端的串流基礎架構。
Managed Agents 的替代方案
from google import genai
client = genai.Client()
stream = client.interactions.create(
agent="antigravity-preview-05-2026",
input="",
stream=True,
environment={
"type": "remote",
"sources": [ # your config files, mounted at startup
{
"type": "inline",
"target": ".agents/AGENTS.md",
"content": AGENTS_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/SKILL.md",
"content": SKILL_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/scripts/generate_pdf.py",
"content": GENERATE_PDF_PY,
},
],
},
)
ADK + Cloud Run 的必要條件 | Managed Agents 為您處理的事項 |
容器映像檔 + Dockerfile + CI/CD | 全代管 Ubuntu 沙箱 (Python 3.12、Node 22、4 個 CPU / 16 GB RAM) |
Cloud Run 部署作業 + 擴縮 | 每次互動都會提供,閒置 7 天後自動失效 |
沙箱隔離 | 每次互動都獨立運作 |
自訂 PDF 工具 + | 代理程式會在沙箱內安裝套件 |
SSE 串流基礎架構 |
|
Python 中的工具定義 | 內建工具:網頁瀏覽、執行程式碼、檔案系統 |
工具呼叫之間的狀態管理 | 內建於代理推論迴圈 |
您只需編寫設定檔 (AGENTS.md、SKILL.md、預先建構的指令碼),並發出一次 API 呼叫,其餘作業則由 Google 負責。
沙箱的運作方式
interactions.create() call
│
▼
Google provisions Ubuntu sandbox (Python 3.12, Node 22, 4 CPU / 16 GB RAM)
│
▼
Agent reasoning loop:
plan → fetch URLs → run Python → write files → reason → repeat
│
▼
Events stream back in real time: tool calls, text chunks, completion
│
▼
interaction.completed → environment_id + interaction_id
沙箱會在閒置 7 天後失效。你可以使用 environment_id 繼續對輸出內容進行微調、執行後續工作,或將其分叉到已儲存的具名代理。
3. 設定
選項 A:Cloud Shell (建議)
點選下方按鈕,在 Google Cloud Shell 中開啟這個程式碼研究室。所有依附元件都已預先安裝。
選項 B:本機設定
git clone https://github.com/Saoussen-CH/tech-digest-managed-agent.git
cd tech-digest-managed-agent
視需要安裝 uv:
curl -LsSf https://astral.sh/uv/install.sh | sh
設定 API 金鑰
cp .env.example .env
cloudshell edit .env
設定金鑰:
GEMINI_API_KEY=your-key-here
安裝依附元件
uv sync
4. 撥打第一通代理電話
開啟範例檔案
cloudshell edit run_digest.py
run_digest() 有一個 TODO 現在要填寫,下一個步驟還有三個。系統已在上方預先填入兩個輔助程式:
load_source(path):從相對於指令碼的.agents/讀取檔案。您會在下一個練習中使用這項資訊,將編輯觀點、PDF 教戰手冊和轉譯器掛接到沙箱。run_stream(stream):處理事件串流並傳回(environment_id, interaction_id)。您不需要自行編寫事件迴圈。
應加入的資訊
TODO 1:將 pass 替換為 (暫時忽略 TODO 3 和 4,這些是下一個步驟要處理的):
from google import genai
client = genai.Client()
stream = client.interactions.create(
agent=BASE_AGENT,
input="Fetch the Hacker News front page and list the top 5 stories.",
stream=True,
environment="remote",
)
environment_id, interaction_id = run_stream(stream)
print(f"\nDone. environment_id={environment_id}")
各部分的用途
genai.Client() 會從環境中讀取 GEMINI_API_KEY。其他所有項目都會透過這個用戶端傳送。
interactions.create() 是核心呼叫。這項功能需要四個參數才能運作:
agent=BASE_AGENT:選取 Antigravity 代理程式 (antigravity-preview-05-2026),這是由 Gemini 3.5 Flash 支援的通用型受管理代理程式。這項功能預設啟用三種內建工具:code_execution(執行 Bash、Python、Node.js)、google_search和url_context(擷取及讀取網頁)。傳遞environment參數時,系統會自動啟用檔案系統工具 (read_file、write_file、list_files)。只要呼叫一次,即可佈建全面代管的 Ubuntu 環境,並預先安裝 Python 3.12、Node.js 22、git、pip 和 curl。無需建構容器,也不必執行部署作業。input:本次執行的工作。代理會瀏覽 Hacker News,並推論結果。environment="remote":為這項互動佈建新的雲端沙箱。stream=True:傳回事件的可疊代項目,而非封鎖。如果沒有,呼叫會等待 30 到 90 秒,然後以interaction.output_text形式一次傳回所有輸出內容。使用串流功能時,您可以即時查看代理的推理和行動。串流並非進階功能,而是正確的預設值,因為 90 秒的黑畫面無法提供服務專員是否正常運作或卡住的信號。
您剛才佈建的內容:每次 interactions.create() 呼叫都會啟動專屬沙箱:
元件 | 規格 |
作業系統 | 隔離的 Ubuntu Linux 環境 |
預先安裝的執行階段 | Python 3.12、Node.js 22、Bash |
運算 | 4 個 CPU 核心,16 GB RAM |
情境脈絡管理 | 在約 135,000 個權杖時觸發自動壓縮 |
網路 | 透過 Egress Proxy 存取外部網站 |
代理程式可以使用 pip 或 npm 安裝任何套件、讀取及寫入檔案,以及發出外送網頁要求。我們絕不會接觸您的電腦和憑證。
environment_id 是剛執行的沙箱控制代碼。interaction.completed後,沙箱不會關閉,最多可存留 7 天。environment_id 是返回該頁面的方式。將其傳遞至第二次 interactions.create() 呼叫,代理程式就會在相同檔案系統中繼續執行,並使用相同檔案和已安裝的套件,就像從未離開一樣。下一個步驟會使用這個 ID 下載 PDF,不必重新執行代理程式,再下一個步驟則會使用這個 ID 繼續對話。
interaction_id 是剛完成的對話回合控點。在下一次呼叫中,將其做為 previous_interaction_id 傳遞,代理程式就會完整記憶在此回合中說過和做過的事。
驗證
uv run python run_digest.py
代理程式運作時,您應該會看到即時輸出內容:
[agent started]
[tool] run_code
Here are the top 5 stories currently on the Hacker News front page, retrieved via the official Hacker News API:
1. **Qwen 3.6 27B is the sweet spot for local development** — 471 points
2. **.self: A new top-level domain designed to support self-hosting** — 116 points
...
Done. environment_id=e3de58774073f75a6ef42924c6ce2e88
即使有 environment="remote",API 仍會傳回實際的 environment_id。沙箱已執行。缺少設定:沒有語音、技能或 PDF 產生器。代理程式只會以文字形式列印故事,然後停止運作。下一個步驟會新增這些項目。
每行輸出內容都會對應至 run_stream() 的事件:
| 簡介 |
|
| 代理程式擷取網址 |
|
| 代理在沙箱中執行程式碼 |
|
| 代理程式搜尋網路 |
|
| 檔案工具和其他 |
|
| 服務專員正在輸入文字 | 直接串流至 stdout |
5. 自訂代理程式
這個代理程式沒有任何指令:沒有語音、沒有技能,也沒有 PDF 產生器。在這個步驟中,您會從 .agents/ 載入設定檔,並將其掛接到沙箱。
要變更的內容
對 run_digest.py 進行四項變更:
TODO 2:在 load_source() 下方新增三個模組層級常數 (這些常數位於 run_digest() 外部,在檔案頂端):
AGENTS_MD = load_source(".agents/AGENTS.md")
SKILL_MD = load_source(".agents/skills/digest-pdf/SKILL.md")
GENERATE_PDF_PY = load_source(".agents/skills/digest-pdf/scripts/generate_pdf.py")
開啟每個檔案,瞭解載入的內容:AGENTS.md 設定編輯觀點和工作流程規則;SKILL.md 是逐步 PDF 教戰手冊;generate_pdf.py 是代理程式將執行的預先建構的算繪器。
現在,請在 run_digest() 中再進行兩項變更:
TODO 3:將 environment 從 "remote" 變更為來源字典,並將 input 設為 "":
environment={
"type": "remote",
"sources": [
{
"type": "inline",
"target": ".agents/AGENTS.md",
"content": AGENTS_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/SKILL.md",
"content": SKILL_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/scripts/generate_pdf.py",
"content": GENERATE_PDF_PY,
},
],
},
TODO 4:在 print(f"\nDone. environment_id={environment_id}") 之後新增以下幾行:
set_key(".env", "ENVIRONMENT_ID", environment_id)
set_key(".env", "INTERACTION_ID", interaction_id)
(set_key 已在 run_digest.py 頂端匯入)。
這會在執行期間將兩個 ID 寫入 .env,因此下一個步驟可以下載 PDF,不必重新執行代理。
各來源的功能
每個來源都是在代理程式執行前,於啟動時掛接到沙箱檔案系統的檔案。target 路徑與 Antigravity 測試架構預期找到這些路徑的位置相符:
.agents/
├── AGENTS.md ← auto-loaded as global instructions
└── skills/
└── digest-pdf/
├── SKILL.md ← auto-discovered and registered as a skill
└── scripts/
└── generate_pdf.py ← pre-built renderer the agent can run
| 變數 | 安全帶如何與安全座椅搭配使用 |
|
| 自動載入為持續性指令:編輯觀點、工作流程、執行規則 |
|
| 自動探索並註冊為具名技能;代理程式會依名稱叫用該技能 |
|
| 預建的 PDF 轉譯器;代理程式會寫入 |
驗證
uv run python run_digest.py
現在執行作業只需 1 到 3 分鐘。您應該會看到代理程式讀取設定檔、撰寫摘要,以及儲存 PDF:
[agent started]
[tool] read_file (/.agents/skills/digest-pdf/SKILL.md)
[tool] list_files (/.agents/skills/digest-pdf/scripts)
[tool] read_file (/.agents/skills/digest-pdf/scripts/generate_pdf.py)
[tool] run_code
[tool] write_file (/workspace/summaries.json)
[tool] run_code
[tool] delete_file (/tmp/test_scrape.py)
I have successfully generated today's tech news digest and saved the formatted document to /workspace/digest.pdf.
Done. environment_id=4129ffd75574e308748e9425d7ec828f
environment_id 現在是實際值:沙箱已使用設定檔執行,且代理程式已建立 digest.pdf。下一步會下載該檔案。
6. 下載 PDF
代理程式在沙箱中將 digest.pdf 寫入 /workspace/digest.pdf。環境快照會透過 Gemini Files API 以 tar 封存檔的形式提供。
視需要安裝 requests:
uv pip install requests
填寫內容
開啟 download_pdf.py。其中有兩個 TODO。
TODO 1:填寫 requests.get() 呼叫:
r = requests.get(
f"https://generativelanguage.googleapis.com/v1beta/files/environment-{environment_id}:download",
params={"alt": "media"},
headers={"x-goog-api-key": api_key},
allow_redirects=True,
)
r.raise_for_status()
網址會指向沙箱快照。params={"alt": "media"} 會傳回原始位元組,而非中繼資料。現有的 GEMINI_API_KEY 也會驗證 Files API。
TODO 2:從 tar 封存檔找出並擷取 PDF:
member = next(m for m in tar.getmembers() if m.name.endswith("workspace/digest.pdf"))
tar.extract(member, path=tmp, filter="data")
tar 路徑前置碼在不同執行階段會有所不同,因此請依後置碼搜尋,而非以硬式編碼方式指定確切路徑。filter="data" 會抑制 Python 3.13 針對不安全 tar 檔案解壓縮作業發出的淘汰警告。
驗證
uv run python download_pdf.py
Saved digest.pdf (48,231 bytes)
開啟相同目錄中的 digest.pdf。其中包含代理程式從即時網頁產生的格式化摘要。
7. 繼續對話
你已擁有 digest.pdf。如果只是想取得檔案,這樣就完成了。這個步驟與上述不同,是要要求代理程式變更摘要,而不重新擷取網頁。
沙箱仍有效。代理程式仍有 /workspace/digest.pdf,並會記住摘要的所有報導。第二次 interactions.create() 呼叫會將後續訊息傳送至同一個沙箱。在這裡,您要求在每則報導下方新增「重要性」附註,系統會直接更新 PDF,不會重新擷取或重新摘要。
填寫內容
開啟 refine_digest.py。其中有三個 TODO。
TODO 1 和 2:在 interactions.create() 中填寫兩個多輪對話參數:
environment=environment_id,
previous_interaction_id=interaction_id,
environment=environment_id 會繼續使用相同的沙箱,並保留檔案和套件。previous_interaction_id=interaction_id 會提供代理程式對話記錄。第一次通話後,其他設定不會變更。
TODO 3:在事件迴圈後,將新的 interaction_id 持續傳回 .env:
set_key(".env", "INTERACTION_ID", interaction_id)
每次呼叫 interactions.create() 都會產生新的 interaction_id。寫回表示下次執行時,會將這項修正內容當做 previous_interaction_id 傳遞,正確地串連回合。沙箱 ID 不會變更,因此不需要更新 ENVIRONMENT_ID。
讓多輪對話運作的兩個參數
ID | 保留內容 | 比喻 |
| 檔案、已安裝的套件、系統狀態:Linux 檔案系統中的所有內容 | 在會議之間使用同一張辦公桌 |
| 對話記錄:代理在先前回合中說過和做過的事 | 回顧上次會議的討論內容 |
您可以單獨傳遞任一 ID:
environment_id:重複使用檔案和套件,但發起新對話。適用於同一工作區中的新工作。previous_interaction_id:繼續對話,但會在新沙箱中進行 (檔案會消失)。- 兩者:完整連續性,也就是這個步驟使用的項目。
沒有 environment_id:空白沙箱,沒有 PDF。沒有 previous_interaction_id:沒有脈絡,服務專員無法修正特定章節。
驗證
uv run python refine_digest.py
串流應快速進行,代理程式不會重新擷取任何內容。完成後:
Refinement done.
Saved digest_v2.pdf (52,418 bytes)
開啟 digest_v2.pdf 並與 digest.pdf 比較。每則報導現在都應新增「重要性」一行。
8. 保存受管理的代理程式設定
目前為止,每次呼叫都會傳遞 AGENTS.md、SKILL.md 和 generate_pdf.py 內嵌。這樣做沒問題,但每次執行時,呼叫程式碼都會攜帶完整檔案內容。agents.create() 會將設定烘焙到 Google 端的已儲存具名代理程式中。下一個呼叫只會傳遞代理程式 ID:
Inline calls: send sources on every call
Named agent: bake once → invoke by ID, no sources
填寫內容
開啟 save_agent.py。其中包含一個 TODO (TODO 1)。
請注意,常數是直接從 run_digest.py 匯入 (不會重複):
from run_digest import BASE_AGENT, AGENTS_MD, SKILL_MD, GENERATE_PDF_PY
TODO 1:填寫 agents.create() 呼叫:
agent = client.agents.create(
id="my-digest",
base_agent=BASE_AGENT,
description="Daily tech digest with editorial voice and PDF generation.",
base_environment={
"type": "remote",
"sources": [
{
"type": "inline",
"target": ".agents/AGENTS.md",
"content": AGENTS_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/SKILL.md",
"content": SKILL_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/scripts/generate_pdf.py",
"content": GENERATE_PDF_PY,
},
],
},
)
base_environment (不是 environment) 是與上一個步驟中內嵌呼叫的主要差異:來源會儲存在 Google 端,並在每次後續叫用時自動掛接。執行一次即可,不必每次摘要執行時都執行。
驗證:儲存代理程式
uv run python save_agent.py
Saved: my-digest
my-digest: Daily tech digest with editorial voice and PDF generation.
叫用已儲存的代理
開啟 invoke_agent.py。它會透過 ID 呼叫已儲存的代理程式,且沒有來源:
stream = client.interactions.create(
agent="my-digest",
input="",
stream=True,
environment="remote",
)
與內嵌呼叫比較:agent=BASE_AGENT 會替換為 "my-digest",而包含三個內嵌來源的完整 environment 區塊會替換為 environment="remote"。Google 端已內建設定。
驗證:叫用已儲存的代理程式
uv run python invoke_agent.py
您應該會看到與內嵌執行相同的即時串流,但呼叫不攜帶任何來源檔案。執行完畢後,.env 中的 ENVIRONMENT_ID 和 INTERACTION_ID 會更新,因此您可以照常繼續使用 refine_digest.py。
[agent started]
[tool] read_file
[tool] write_file
[tool] run_code
I have successfully created today's tech news digest.
Done. environment_id=9a1c3e02-...
9. 清除
沙箱會在閒置 7 天後自動過期。沒有要停止的伺服器。沒有要刪除的容器。
如果已儲存代理程式設定,請刪除:
uv run python delete_agent.py
10. 摘要
您從頭開始建構受管理代理程式,一次一個概念。以下是各項練習的教學內容:
運動 | 概念 | Key API |
撥打第一通電話 | 佈建實際的 Linux 沙箱,並即時串流其事件 |
|
自訂代理 | 掛接設定檔;在同一次執行中將 ID 保留在 |
|
下載 PDF | 下載 PDF,不必重新執行代理程式 | Gemini Files API |
延續對話 | 繼續對話,不必重新擷取網頁內容 |
|
保留代理程式設定 | 保存代理設定;依 ID 叫用,無須來源 |
|
鍵模式
- 一次呼叫,一個沙箱:
interactions.create()會處理所有基礎架構 (無需部署容器,也無須在本機安裝套件) - 漸進式串流:
stream=True將 90 秒的黑方塊轉換為工具呼叫和文字區塊的現行動態饋給 - 內嵌來源:將
AGENTS.md、SKILL.md和預先建構的指令碼掛接到沙箱,不必上傳或部署 - 自動探索 Harness:系統會自動挑選
.agents/中的檔案 (無須設定 SDK) - 二維狀態:
environment_id追蹤檔案和套件;previous_interaction_id追蹤對話脈絡;兩者皆可獨立傳遞 - 下載快照:環境是完整的檔案系統 tar,可透過 Gemini Files API 存取
- 具名代理:
agents.create()會永久烘焙設定;日後呼叫時只會傳遞代理 ID 和environment="remote",不會傳遞來源
ADK + Cloud Run 與代管型代理:差異一覽
功能 | ADK + Cloud Run | Gemini API 中的受管理代理 |
佈建沙箱 |
|
|
定義工具 | 向代理程式註冊的 Python 函式 | 內建:網頁瀏覽、執行程式碼、檔案系統 |
安裝套件 | Dockerfile 中的 | 代理程式在沙箱中執行 |
串流事件 | 自訂 SSE 基礎架構 |
|
繼續執行工作階段 | 工作階段資料庫 + 注入脈絡 |
|
設定檔 | 在代理程式中硬式編碼,或在啟動時插入 | 透過「 |
要管理的基礎架構 | 容器、Cloud Run、IAM、密鑰 | 無 |