View a markdown version of this page

搭配 AgentCore 閘道使用抽樣 - Amazon Bedrock AgentCore

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

搭配 AgentCore 閘道使用抽樣

取樣是一種 MCP 功能,可讓 MCP 伺服器在工具呼叫期間向用戶端請求 LLM 完成。這可讓伺服器利用 AI 功能,而無需直接存取語言模型 — 用戶端會處理模型調用並傳回結果。AgentCore Gateway 會將 MCP 伺服器目標的取樣請求轉送到您的用戶端,id以閘道產生的識別符取代請求。

先決條件

若要搭配閘道使用抽樣:

  • 工作階段已啟用 (2025-11-25 版及更早版本) — 取樣需要工作階段支援。請參閱搭配閘道使用 MCP 工作階段。對於版本 2026-07-28和更新版本,您不需要將 sessionConfiguration 新增至閘道,因為這些版本是無狀態的。

  • 已啟用回應串流 (2025-11-25 版及更早版本) — 在開放連線期間,取樣請求會以 SSE 區塊的形式傳送。在閘道的 true中streamingConfiguration.enableResponseStreaming將 設定為 protocolConfiguration.mcp。對於版本 2026-07-28和更新版本,您不需要啟用回應串流。這些版本透過多往返請求 (MRTR) 模式提供取樣,而不是回應串流上的伺服器起始請求。如需詳細資訊,請參閱模型內容通訊協定文件中的多重往返請求。

  • MCP 伺服器目標類型 — 抽樣請求源自 MCP 伺服器目標。

  • Client 宣告取樣功能 — 用戶端必須宣告對取樣的支援,閘道才能轉送取樣請求。對於版本 2025-11-25和更早版本,用戶端會在initialize請求中宣告此支援。對於版本 2026-07-28和更新版本,用戶端會為 _meta 欄位 () 中的每個請求宣告它io.modelcontextprotocol/clientCapabilities。

抽樣的運作方式

當 MCP 伺服器目標在工具執行期間需要完成 LLM 時,它會傳送sampling/createMessage請求。閘道會將此請求轉送給用戶端做為 SSE 事件,取代請求 id。用戶端會叫用其語言模型,並將結果傳回至閘道,再將其轉送至目標。

注意

此處所述的流程適用於版本 2025-11-25和更早版本,其中伺服器在開啟的 SSE 串流上sampling/createMessage以伺服器起始的請求傳送。對於 版本 2026-07-28 和更新版本,抽樣會改用多來回請求 (MRTR) 模式。伺服器會傳回將 resultType設為 的臨時結果input_required。然後,用戶端會在重試原始請求時提供完成。如需詳細資訊,請參閱模型內容通訊協定文件中的多重往返請求。

抽樣請求包括:

  • messages — 要傳送至模型的對話訊息。

  • modelPreferences — 有關所需模型功能的選用提示 (智慧、速度、成本)。

  • systemPrompt — 模型的選用系統提示。

  • maxTokens — 要產生的字符數目上限。

用戶端會以下列方式回應:

  • model — 使用的模型。

  • role — 一律為 assistant。

  • content — 產生的內容 (文字或影像)。

注意

用戶端可完全控制要使用的模型,以及如何處理請求。伺服器modelPreferences是提示,而不是要求。用戶端也可以根據自己的政策修改或拒絕請求。

取樣流程

  1. 用戶端使用 Mcp-Session-Id標頭傳送tools/call請求。

  2. Gateway 會將工具呼叫轉送至 MCP 伺服器目標。

  3. 目標會開啟 SSE 串流並傳送sampling/createMessage請求。

  4. Gateway 會將取樣請求轉送給用戶端做為 SSE 事件,取代請求 id。

  5. 用戶端會使用提供的訊息叫用其語言模型。

  6. 用戶端會使用相同的 Mcp-Session-Id和id來自閘道請求的 ,傳送具有取樣結果的新請求。

  7. Gateway 會將結果轉送至 MCP 伺服器目標。

  8. 目標會繼續處理並傳回最終工具結果。

  9. Gateway 會將最終結果轉送至用戶端並關閉串流。

MCP 伺服器目標開發人員指南

重要

傳送取樣請求的 MCP 伺服器目標應將取樣呼叫包裝在 try-catch 區塊中,並處理用戶端不支援取樣的情況。如果閘道的用戶端未宣告取樣功能,閘道不會將其宣告至目標。如果目標仍然傳送取樣請求,閘道會將 -32601(找不到方法) 錯誤傳回目標。

當無法使用取樣時,伺服器應實作備用路徑 (例如使用內建模型或略過 AI 輔助步驟)。

保護請求狀態 (2026-07-28 版及更新版本)

在 版本 2026-07-28 和更新版本上,抽樣使用多往返請求 (MRTR) 模式,在用戶端和 MCP 伺服器目標requestState之間具有不透明。保護該值是共同的責任:閘道在不儲存的情況下授權和轉送它,而您的 MCP 伺服器目標必須驗證它,並防止一個使用者重播另一個使用者的請求狀態。如需 MCP 伺服器必須遵循的完整共同責任模型和保護指引,請參閱 MCP 伺服器目標考量中的保護引出和取樣的請求狀態。

錯誤處理

案例 錯誤 說明

當沒有等待取樣請求時,用戶端會傳送取樣回應

JSON-RPC -32600(無效的請求)

找不到此工作階段的相符取樣請求。

用戶端使用id不符合待定請求的 傳送抽樣回應

JSON-RPC -32600(無效的請求)

id 必須與sampling/createMessage請求中閘道傳送的 相符。

MCP 伺服器會傳送抽樣請求,但閘道未宣告支援

JSON-RPC -32601(找不到方法)

已傳回 MCP 伺服器目標。請參閱故障診斷。

疑難排解

錯誤:「錯誤呼叫工具 'sample_tool':找不到方法: sampling/createMessage」

當 MCP 伺服器目標傳送取樣請求,但閘道的用戶端未宣告取樣功能時,就會發生此錯誤。對於版本 2025-11-25和更早版本,用戶端會在 期間宣告此功能initialize。對於版本 2026-07-28和更新版本,用戶端會針對 _meta 欄位中的每個請求宣告。閘道會將 -32601(找不到方法) 錯誤傳回目標。目標可能會將此作為工具執行錯誤傳回給用戶端。

若要解決問題:

  • 如果您是 MCP 伺服器開發人員:新增取樣呼叫的錯誤處理。不支援取樣時實作備用路徑:

    重要

    您必須在create_message通話related_request_id=ctx.request_context.request_id中包含 。這是閘道正確將抽樣請求與原始工具呼叫建立關聯的必要項目。如果沒有它,抽樣將無法運作。

    try: result = await ctx.session.create_message( messages=[{"role": "user", "content": {"type": "text", "text": "Summarize this document"}}], max_tokens=500, related_request_id=ctx.request_context.request_id, ) except Exception as e: # Fallback when client doesn't support sampling logger.warning(f"Sampling not supported: {e}") result = fallback_summarization(document)
  • 如果您是閘道用戶端開發人員:對於 2025-11-25和更早版本,請確保您的用戶端在 期間宣告取樣功能initialize。對於 版本 2026-07-28和更新版本,請在 _meta 欄位 (io.modelcontextprotocol/clientCapabilities) 中宣告每個請求。下列範例顯示initialize宣告:

    { "capabilities": { "sampling": {} } }

程式碼範例

注意

LangGraph MCP 用戶端 (langchain-mcp-adapters) 和 Strands MCP 用戶端目前不支援取樣。使用如下所示的 MCP 用戶端方法來處理來自閘道的取樣請求。

範例
Python requests package (2025-11-25 and earlier)

在這些版本上,用戶端會在 期間宣告取樣功能initialize,而取樣請求會以sampling/createMessage請求的形式送達開啟的 SSE 串流。將 MCP-Protocol-Version標頭設定為閘道支援的版本。

import requests import json import sseclient gateway_url = "https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp" headers = { "Content-Type": "application/json", "Accept": "text/event-stream", "Authorization": "Bearer YOUR_ACCESS_TOKEN" } # Step 1: Initialize with sampling capability init_response = requests.post(gateway_url, headers=headers, json={ "jsonrpc": "2.0", "id": "init-request", "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {"sampling": {}}, "clientInfo": {"name": "my-agent", "version": "1.0.0"} } }) session_id = init_response.headers["Mcp-Session-Id"] headers["Mcp-Session-Id"] = session_id headers["MCP-Protocol-Version"] = "2025-06-18" # Step 2: Call tool (streaming response) response = requests.post(gateway_url, headers=headers, json={ "jsonrpc": "2.0", "id": "tool-call-1", "method": "tools/call", "params": { "name": "summarizeDocument", "arguments": {"documentId": "doc-789"} } }, stream=True) # Step 3: Process SSE events client = sseclient.SSEClient(response) for event in client.events(): data = json.loads(event.data) if data.get("method") == "sampling/createMessage": sampling_id = data["id"] print(f"Sampling request: {data['params']['messages']}") # Step 4: Invoke your LLM and send result llm_result = invoke_your_model(data["params"]) # Your LLM invocation requests.post(gateway_url, headers=headers, json={ "jsonrpc": "2.0", "id": sampling_id, "result": { "model": "claude-sonnet-4-20250514", "role": "assistant", "content": {"type": "text", "text": llm_result} } }) elif "result" in data: print(f"Tool result: {data['result']}") break
Python requests package (2026-07-28)

在版本 上2026-07-28,取樣會使用多往返請求模式,而不是 SSE 串流上的伺服器起始請求。用戶端會_meta在每次請求時宣告 中的取樣功能。如果工具需要完成,回應是包含 中的sampling/createMessage請求inputRequests和不透明 input_required的結果requestState。用戶端會叫用其模型,並使用新的 id、 inputResponses和未修改的 重試原始請求requestState。工作階段和initialize交握不會使用。閘道的 supportedVersions 必須包含 2026-07-28。

import requests gateway_url = "https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp" META = { "io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientInfo": {"name": "my-agent", "version": "1.0.0"}, "io.modelcontextprotocol/clientCapabilities": {"sampling": {}} } headers = { "Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Bearer YOUR_ACCESS_TOKEN", "MCP-Protocol-Version": "2026-07-28", "Mcp-Method": "tools/call", "Mcp-Name": "summarizeDocument" } arguments = {"documentId": "doc-789"} # Step 1: Call the tool, declaring the sampling capability in _meta response = requests.post(gateway_url, headers=headers, json={ "jsonrpc": "2.0", "id": "tool-call-1", "method": "tools/call", "params": {"name": "summarizeDocument", "arguments": arguments, "_meta": META} }).json() result = response["result"] if result.get("resultType") == "input_required": # Step 2: Fulfill each sampling request by invoking your model input_responses = {} for key, input_request in result.get("inputRequests", {}).items(): params = input_request["params"] print(f"Sampling request: {params['messages']}") llm_result = invoke_your_model(params) # Your LLM invocation input_responses[key] = { "model": "claude-sonnet-4-20250514", "role": "assistant", "content": {"type": "text", "text": llm_result} } # Step 3: Retry the tool call with a new id, the input responses, # and the requestState echoed back unmodified retry_params = {"name": "summarizeDocument", "arguments": arguments, "_meta": META, "inputResponses": input_responses} if "requestState" in result: retry_params["requestState"] = result["requestState"] response = requests.post(gateway_url, headers=headers, json={ "jsonrpc": "2.0", "id": "tool-call-2", "method": "tools/call", "params": retry_params }).json() result = response["result"] print(f"Tool result: {result}")
MCP Client
from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client import asyncio async def sampling_handler(request): """Handle sampling requests from the server by invoking an LLM.""" messages = request.params.messages llm_response = await invoke_your_model(messages, max_tokens=request.params.maxTokens) return { "model": "claude-sonnet-4-20250514", "role": "assistant", "content": {"type": "text", "text": llm_response} } async def use_sampling(url, token): headers = {"Authorization": f"Bearer {token}"} async with streamablehttp_client(url=url, headers=headers) as ( read_stream, write_stream, _ ): async with ClientSession( read_stream, write_stream, sampling_handler=sampling_handler ) as session: await session.initialize() result = await session.call_tool( name="summarizeDocument", arguments={"documentId": "doc-789"} ) print(f"Tool result: {result}") return result asyncio.run(use_sampling( url="https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp", token="YOUR_ACCESS_TOKEN" ))