
用 Python 的 yield 為執行中的 AI 智能體串流輸入 — 與 return 的差異,以及 Node.js 的實作
Python 的 yield 與 return 有何不同,以及如何把非同步產生器當作提示詞傳入,為執行中的長時間 AI 智能體串流輸入,並附上 Node.js 的對應寫法。
本頁目錄
引言
長時間執行的 AI 智能體有時需要在執行過程中注入新的資訊(人類的核准、外部事件、後續指示),而不必重新建立工作階段並重送整段歷史紀錄。Python 的 yield 正是這個模式背後的機制:一個簡單到容易被忽略的關鍵字,卻直接連結到智能體的架構設計。這個模式是把一個非同步產生器當作輸入串流、保持開啟:yield 會暫停函式並保留其狀態,讓智能體的工作階段能維持在「熱」的狀態,同時一次接收一則新訊息。 本文從 yield 與 return 的差異談起,接著介紹這個串流輸入模式,最後說明 Node.js 中相同的實作方式。
return 與 yield — 控制流程的根本差異
return「立即退出」
return 會就地結束函式。 函式內的所有區域變數都會被捨棄,下次呼叫時會從第一行重新開始。
def get_greeting(): name = "Alice" return f"Hello, {name}" # 函式在這裡完全結束,name 也一併被捨棄
result = get_greeting() # 每次都從頭執行yield「暫停並恢復」
使用 yield 會讓函式變成產生器。它會一邊回傳值一邊暫停,函式的狀態(變數、執行位置)全部保留。 當下一個值被要求時,執行會從暫停的那一行恢復。
def count_up(): n = 1 while True: yield n # 回傳 n 並暫停 n += 1 # 恢復時從這裡繼續
counter = count_up()print(next(counter)) # 1print(next(counter)) # 2(記得 n=1 的狀態)print(next(counter)) # 3return 每次都從頭開始;yield 從中斷處繼續。比較摘要
| 特性 | return |
yield |
|---|---|---|
| 函式結束 | 立即結束 | 暫停(可恢復) |
| 狀態保留 | 捨棄 | 保留 |
| 呼叫模式 | 每次都從頭執行 | 從上次暫停處恢復 |
| 類比 | 寄一封信(一次性) | 打一通電話(保持線路開啟) |
為智能體串流輸入 — 非同步迭代器模式
yield 那種「一邊保留狀態一邊逐一回傳值」的特性,直接連結到長時間執行的 AI 智能體的輸入模式。
問題:單次請求的限制
在一般的 API 呼叫中,所有輸入都必須在請求時就準備好。
# 傳統做法:一次把所有輸入都傳入response = await client.query(prompt="Analyse this data")但在長時間執行的智能體中,有些情況需要在執行過程中注入新資訊。每次重新建立工作階段都得重送整段過去的對話歷史,效率不佳。
解法:用非同步產生器串流輸入
把 Python 的 async 產生器當作提示詞傳入,能讓工作階段保持「開啟」的狀態, 動態餵入之後才抵達的訊息。
import asyncio
async def message_streamer(): # 傳送初始指示 yield "Starting the agent. Please analyse the S3 logs."
# 等待外部佇列(例如 SQS)的事件,隨到隨注入 while True: event = await queue.get() yield event.message if event.is_last: break
# 把迭代器傳給 SDK — 工作階段維持存活(虛擬碼)response = await client.query(prompt=message_streamer())重點在於,多虧了 yield,函式不會結束,而是進入「等待狀態」,直到佇列中有事件抵達。事件一到,函式就會恢復,並把新訊息送進智能體。
這裡展示的 SDK 進入點只是示意。接收訊息非同步可迭代物件的方法名稱,各 SDK 不盡相同(query、run、chat 等),而且這些 API 演進得很快,需要查閱所使用 SDK 目前的簽章。各家 SDK 之間穩定不變的是這個模式本身:把一個非同步產生器當作輸入串流保持開啟。
注意與 streaming=True 的差異
許多 SDK 中常見的 streaming=True,是用來串流輸出(回應)的設定。相對地,把非同步迭代器當作提示詞傳入,則是串流輸入的機制。方向相反,需留意不要混淆。
| 設定 | 方向 | 用途 |
|---|---|---|
streaming=True |
輸出串流 | 逐一 token 接收回應 |
| 把非同步迭代器當作提示詞傳入 | 輸入串流 | 動態把訊息注入執行中的智能體 |
使用情境 — 何時需要串流輸入
1. Human-in-the-Loop(HITL)
當智能體詢問確認,例如「可以刪除這個檔案嗎?」,工作階段能在保持存活的同時等待人類的回應。
async def hitl_streamer(): yield "Please start cleaning up the production database."
# 智能體要求確認時,從 UI 接收人類的回答 approval = await wait_for_human_approval() yield f"Approval: {approval}"好處在於能一邊讓智能體保持「熱」的狀態,一邊插入人類的判斷。
2. 即時注入外部事件
支援機器人正在分析同一位顧客的問題時,注入「顧客升級了方案」這類系統事件的情境。
async def event_injector(): yield "Please analyse the support ticket for customer #12345."
async for event in system_event_stream: yield f"[SYSTEM EVENT: {event.description}]"智能體接收事件後,能即時調整回答的方向。
3. 逐步加入指示
分析一份 100 頁文件時,被注入「其實再加上這個角度」這類指示的情境。因為不需要重新建立工作階段,先前累積的分析脈絡得以保留。
在 Node.js 中實作 — async function*
同樣的模式不只適用於 Python,Node.js 也能用。語法是 async function*(帶星號)搭配 yield。
// Node.js 的非同步產生器async function* messageStreamer() { yield "Initializing agent...";
while (true) { const event = await waitForNextEvent(); yield event.text;
if (event.type === 'close') break; }}
// 把迭代器傳給 SDK(虛擬碼)const client = new AgentClient();await client.query({ prompt: messageStreamer() });Python 與 Node.js 比較
| 項目 | Python | Node.js |
|---|---|---|
| 語法 | async def func(): + yield |
async function* func() + yield |
| 消費方式 | async for item in gen: |
for await (const item of gen) |
| 產生器判定 | 由 yield 的存在自動判定 |
由 function* 的星號明確標示 |
| 生態系 | 以 asyncio 為基礎 | 以 EventEmitter / Promise 為基礎 |
Node.js 與 Python 的差異在於函式宣告需要加上 *,但概念完全相同。無論哪種語言,行為都是一樣的:「不結束函式,一邊保留狀態一邊逐一送出值」。
總結
yield會在不結束函式的情況下回傳值。 如果return是一封「信」,yield就是一通保持線路開啟、可以交換資訊的「電話」。- 它直接連結到長時間執行智能體的串流輸入。 把非同步迭代器當作提示詞傳入,能把訊息動態注入執行中的智能體。
- 它實現了實務上常見的模式,包括 HITL、外部事件注入、逐步指示。 每一種都能避免重新建立工作階段的額外開銷。
- 同樣的概念在 Python 與 Node.js 中都適用。 語法有差異,但設計理念是共通的。
yield 不只是一項語言特性,更是 AI 智能體設計模式的基礎,而在長時間執行的智能體需要它的場合,這個串流輸入模式值得採用。



