跳轉到內容

實作課程 05 — 工作階段

目標: 為對話指定穩定的識別碼,使其能在處理程序重新啟動後延續,並了解工作階段永續保存能做到哪些事,以及無法涵蓋哪些事。

時間: ~20分鐘

必要條件: 實作課程 04 完成。

第1步 — 問題

到目前為止,所有操作都是無狀態的。每次執行都會建立全新的工作階段、傳送提示,然後捨棄上下文。重新啟動處理程序後,模型將完全不知道先前談過什麼。

這很好用於一次性的範例,但對於真正的助理來說是無用的。一個零售分析師,問了三個後續問題,期望第四次仍然與同一個客戶相關。

第2步 — 執行工作階段範例

cd src/AgentOrchestrator-python
uv run python -m sdk_labs sessions

新增 --model <id> 覆蓋模型:

uv run python -m sdk_labs sessions --model gpt-5-mini

已驗證輸出:

== Lab 05: sessions ==

Model: claude-haiku-4.5
Session id: sdklabs-75e6b8f216d0451b

--- Turn 1 (new session) ---
You: Remember this: my favourite retail segment is 'At Risk'. Reply with just OK.
Assistant: OK

Session closed.

--- Turn 2 (resumed session) ---
You: Which retail segment did I say was my favourite?
Assistant: Your favourite retail segment is 'At Risk'.

--- Session metadata ---
  id=sdklabs-75e6b8f216d0451b metadata retrieved

工作階段ID是每次執行隨機的,所以你的工作階段ID會不同。重要的證明不是ID值——而是那輪2記住。 'At Risk' 在第一個工作階段被關閉之後.

這個執行證明了工作階段恢復在同一個程序內。它本身並不證明跨裝置移交或重啟恢復。

第3步 — 給工作階段一個ID

開啟 sessions_sample.py. 第一個輪次透過顯式 session_id:

session_id = f"sdklabs-{uuid.uuid4().hex}"[:24]

session = await client.create_session(
    session_id=session_id,
    model=model_id,
    streaming=False,
)
async with session:
    await send_and_print(
        session,
        "Remember this: my favourite retail segment is 'At Risk'. Reply with just OK.",
    )

這個 ID 是本實作課程的關鍵。沒有它,SDK 仍會建立工作階段,但你將無法再次回到該工作階段。

事件 async with session: 然後關閉工作階段。下一個輪次不是即時物件的延續,而是關閉物件的真正恢復。

第4步 — 恢復工作階段

第二個輪次使用相同的id和不同的SDK呼叫:

resumed = await client.resume_session(session_id, model=model_id, streaming=False)
async with resumed:
    await send_and_print(resumed, "Which retail segment did I say was my favourite?")

💡 這比.NET等效體更簡單。 在C#中,你必須建置一個 ResumeSessionConfig 和作為必需的第二個引數傳遞它;不傳遞它會導致編譯錯誤。Python沒有 ResumeSessionConfig;它使用與普通關鍵字引數相同的設定, session_id 是唯一的位置引數:

await client.resume_session(session_id)                       # valid
await client.resume_session(session_id, model="gpt-5")        # valid
await client.resume_session(session_id, streaming=False)      # valid

⚠️ session_id 是位置引數,但在建立時是關鍵字引數。 注意不對稱性: create_session(session_id=...)resume_session(session_id). 工作階段ID之後的所有內容是關鍵字引數。 resume_session 為了使證明更強,使用工作階段ID在第二個程序中執行重播路徑:

以另一個機器上重新開始工作階段需要訪問相同的工作階段永續性儲存,相容的執行時和適當的授權。 使用列印的ID在第二個程序中執行重播路徑: 那確實跨越了程序邊界。從另一個機器上重新開始工作階段還需要訪問相同的工作階段永續性儲存,相容的執行時和適當的授權。

uv run python -m sdk_labs sessions --resume sdklabs-75e6b8f216d0451b

這個範例還要求SDK詢問中繼資料:

第5步 — 發現儲存的工作階段

它傳回

metadata = await client.get_session_metadata(session_id)

當沒有儲存的工作階段匹配時,所以 SessionMetadata | NoneNone 在使用它之前檢查:

print(
    "  (no metadata returned)"
    if metadata is None
    else f"  id={session_id} metadata retrieved"
)

要瀏覽已儲存的工作階段而不是從已知ID開始,使用 list_sessions:

sessions = await client.list_sessions()          # -> list[SessionMetadata]

常見的模式是:

  1. 列出使用者的工作階段
  2. 讓使用者選擇一個,或者選擇最近的
  3. 將此ID傳遞給 resume_session(id, ...)

第6步 — 將其連線到架構

工作階段永續保存使:

  • 在裝置和使用者端之間切換 — 在一個地方開始,繼續在另一個地方
  • 恢復失敗 — 在程序重啟後恢復而不是從頭開始重建上下文
  • 稽核性 — 穩定的ID使更容易檢查、組織和追蹤對話

今天在示範應用中,瀏覽器保留聊天曆史 localStorage (見 app.js). 這適用於一個瀏覽器在一個裝置上,但無法將對話移動到其他地方。開啟應用在手機上,歷史記錄將消失。

SDK工作階段是解決方案。使用者介面可以儲存工作階段ID而不是整個對話,使用相同工作階段儲存的授權使用者端可以繼續使用相同的伺服器端工作階段。

⚠️ 工作階段ID不是訪問控制。 將ID視為識別符號,而不是金鑰或能力。您的應用在恢復已儲存的對話之前仍然需要正常的使用者認證和授權。

Python學習路線使用PyPI github-copilot-sdk 1.0.9,匯入名稱為 copilot, 並要求Python 3.11或更高版本。您可以在 pyproject.toml.

⚠️ 警告

  • ID碰撞: 工作階段ID是您的鍵。重複使用ID會恢復舊對話而不是建立一個乾淨的對話。
  • 沒有語意的 ID: 隨機 ID 適用於示範,但實際系統應能將 ID 對應回使用者、案例或工作流程。
  • ID不是權限: 知道或猜測ID是不夠的,以訪問對話;單獨實施授權。
  • ID中的機密資料: 從工作階段ID中不要放入令牌、電子郵件地址或客戶詳情。
  • 假設存在中繼資料: get_session_metadata 傳回 None 對於一個 未知的id; 在解引用之前,先進行一個防護。
  • 忘記關閉: 範例使用 async with 所以,1號斷言在1號斷言之前關閉,2號斷言在2號斷言之後重新開始。跳過這一點,你沒有證明任何東西。

💡 延伸挑戰

  1. 在中繼資料呼叫之後新增第三個斷言,再次恢復相同的id,並詢問關於原始問題的另一個問題 'At Risk' 訊息
  2. 執行 --resume 從第二個終端路徑證明跨程序恢復
  3. 列出儲存的工作階段並恢復最近的一個:
sessions = await client.list_sessions()
if sessions:
    resumed = await client.resume_session(sessions[0].id, model=model_id)
  1. 將id更改為穩定字串,例如 "analyst-demo" 並觀察,現在重新執行範例工作階段繼續一個長生工作階段

✅ 檢查點

現在你可以解釋:

  • [x] 為什麼程序重啟失去上下文,沒有永續性
  • [x] 如何描述Pydantic create_session(session_id=...) 為你的應用提供一個穩定的鍵
  • ✓ 那 resume_session(id, ...) 在Python中,使用普通的關鍵詞引數,沒有單獨的設定物件作為.NET需要的
  • [x] 如何描述Pydantic get_session_metadatalist_sessions 幫助發現儲存的工作階段,以及中繼資料可以 None
  • [x] 為什麼SDK工作階段是跨裝置聊天曆史的正確基礎
  • [x] 為什麼跨程序和跨裝置恢復也相依於共享儲存、相容執行時行為和授權