Loading...
    • 開發者指南
    • API 參考
    • MCP
    • 資源
    • 發行說明
    Search...
    ⌘K
    入門
    Claude 簡介快速開始
    模型與定價
    模型概覽選擇模型Claude 4.6 新功能遷移指南模型棄用定價
    使用 Claude 構建
    功能概覽使用 Messages API處理停止原因提示詞最佳實踐
    上下文管理
    上下文視窗壓縮上下文編輯
    功能
    提示詞快取延伸思考自適應思考思考力度串流訊息批次處理引用多語言支援Token 計數嵌入視覺PDF 支援Files API搜尋結果結構化輸出
    工具
    概覽如何實作工具使用細粒度工具串流Bash 工具程式碼執行工具程式化工具呼叫電腦使用工具文字編輯器工具網頁擷取工具網頁搜尋工具記憶工具工具搜尋工具
    Agent Skills
    概覽快速開始最佳實踐企業級 Skills透過 API 使用 Skills
    Agent SDK
    概覽快速開始TypeScript SDKTypeScript V2(預覽版)Python SDK遷移指南
    串流輸入即時串流回應處理停止原因處理權限使用者核准與輸入使用 hooks 控制執行工作階段管理檔案檢查點SDK 中的結構化輸出託管 Agent SDK安全部署 AI 代理修改系統提示詞SDK 中的 MCP自訂工具SDK 中的子代理SDK 中的斜線命令SDK 中的 Agent Skills追蹤成本與用量待辦清單SDK 中的外掛
    API 中的 MCP
    MCP 連接器遠端 MCP 伺服器
    第三方平台上的 Claude
    Amazon BedrockMicrosoft FoundryVertex AI
    提示詞工程
    概覽提示詞產生器使用提示詞範本提示詞改進器清晰直接使用範例(多範例提示)讓 Claude 思考(CoT)使用 XML 標籤賦予 Claude 角色(系統提示詞)串聯複雜提示詞長上下文技巧延伸思考技巧
    測試與評估
    定義成功標準開發測試案例使用評估工具降低延遲
    強化防護機制
    減少幻覺提高輸出一致性防範越獄攻擊串流拒絕減少提示詞洩漏讓 Claude 保持角色
    管理與監控
    Admin API 概覽資料駐留工作區用量與成本 APIClaude Code Analytics API零資料保留
    Console
    Log in
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...

    Solutions

    • AI agents
    • Code modernization
    • Coding
    • Customer support
    • Education
    • Financial services
    • Government
    • Life sciences

    Partners

    • Amazon Bedrock
    • Google Cloud's Vertex AI

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Company

    • Anthropic
    • Careers
    • Economic Futures
    • Research
    • News
    • Responsible Scaling Policy
    • Security and compliance
    • Transparency

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Help and security

    • Availability
    • Status
    • Support
    • Discord

    Terms and policies

    • Privacy policy
    • Responsible disclosure policy
    • Terms of service: Commercial
    • Terms of service: Consumer
    • Usage policy
    指南

    處理審批和使用者輸入

    將 Claude 的審批請求和澄清問題呈現給使用者,然後將他們的決定回傳給 SDK。

    在處理任務時,Claude 有時需要與使用者確認。它可能需要在刪除檔案前獲得許可,或需要詢問新專案應使用哪個資料庫。您的應用程式需要將這些請求呈現給使用者,以便 Claude 能夠根據他們的輸入繼續工作。

    Claude 在兩種情況下請求使用者輸入:當它需要使用工具的許可(如刪除檔案或執行命令)時,以及當它有澄清問題(透過 AskUserQuestion 工具)時。兩者都會觸發您的 canUseTool 回呼,暫停執行直到您回傳回應。這與正常的對話輪次不同,在正常輪次中 Claude 完成後等待您的下一條訊息。

    對於澄清問題,Claude 會生成問題和選項。您的角色是將它們呈現給使用者並回傳他們的選擇。您無法在此流程中添加自己的問題;如果您需要自己向使用者詢問某些事情,請在您的應用程式邏輯中單獨處理。

    本指南向您展示如何偵測每種類型的請求並做出適當回應。

    偵測 Claude 何時需要輸入

    在您的查詢選項中傳入 canUseTool 回呼。每當 Claude 需要使用者輸入時,回呼就會觸發,接收工具名稱和輸入作為參數:

    async def handle_tool_request(tool_name, input_data, context):
        # Prompt user and return allow or deny
        ...
    
    options = ClaudeAgentOptions(can_use_tool=handle_tool_request)

    回呼在兩種情況下觸發:

    1. 工具需要審批:Claude 想要使用一個未被許可權規則或模式自動批准的工具。檢查 tool_name 以確定工具(例如 "Bash"、"Write")。
    2. Claude 提出問題:Claude 呼叫 AskUserQuestion 工具。檢查 tool_name == "AskUserQuestion" 以進行不同處理。如果您指定了 tools 陣列,請包含 AskUserQuestion 以使其正常運作。詳見處理澄清問題。

    要自動允許或拒絕工具而不提示使用者,請改用 hooks。Hooks 在 canUseTool 之前執行,可以根據您自己的邏輯允許、拒絕或修改請求。您也可以使用 PermissionRequest hook 在 Claude 等待審批時發送外部通知(Slack、電子郵件、推播)。

    處理工具審批請求

    一旦您在查詢選項中傳入了 canUseTool 回呼,當 Claude 想要使用未自動批准的工具時,它就會觸發。您的回呼接收兩個參數:

    參數描述
    toolNameClaude 想要使用的工具名稱(例如 "Bash"、"Write"、"Edit")
    inputClaude 傳遞給工具的參數。內容因工具而異。

    input 物件包含工具特定的參數。常見範例:

    工具輸入欄位
    Bashcommand、description、timeout
    Writefile_path、content
    Editfile_path、old_string、new_string
    Readfile_path、offset、limit

    完整的輸入結構請參閱 SDK 參考文件:Python | TypeScript。

    您可以將此資訊顯示給使用者,讓他們決定是否允許或拒絕該操作,然後回傳適當的回應。

    以下範例要求 Claude 建立並刪除一個測試檔案。當 Claude 嘗試每個操作時,回呼會將工具請求列印到終端機並提示 y/n 審批。

    import asyncio
    
    from claude_agent_sdk import ClaudeAgentOptions, query
    from claude_agent_sdk.types import (
        HookMatcher,
        PermissionResultAllow,
        PermissionResultDeny,
        ToolPermissionContext,
    )
    
    
    async def can_use_tool(
        tool_name: str, input_data: dict, context: ToolPermissionContext
    ) -> PermissionResultAllow | PermissionResultDeny:
        # Display the tool request
        print(f"\nTool: {tool_name}")
        if tool_name == "Bash":
            print(f"Command: {input_data.get('command')}")
            if input_data.get("description"):
                print(f"Description: {input_data.get('description')}")
        else:
            print(f"Input: {input_data}")
    
        # Get user approval
        response = input("Allow this action? (y/n): ")
    
        # Return allow or deny based on user's response
        if response.lower() == "y":
            # Allow: tool executes with the original (or modified) input
            return PermissionResultAllow(updated_input=input_data)
        else:
            # Deny: tool doesn't execute, Claude sees the message
            return PermissionResultDeny(message="User denied this action")
    
    
    # Required workaround: dummy hook keeps the stream open for can_use_tool
    async def dummy_hook(input_data, tool_use_id, context):
        return {"continue_": True}
    
    
    async def prompt_stream():
        yield {
            "type": "user",
            "message": {
                "role": "user",
                "content": "Create a test file in /tmp and then delete it",
            },
        }
    
    
    async def main():
        async for message in query(
            prompt=prompt_stream(),
            options=ClaudeAgentOptions(
                can_use_tool=can_use_tool,
                hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
            ),
        ):
            if hasattr(message, "result"):
                print(message.result)
    
    
    asyncio.run(main())

    在 Python 中,can_use_tool 需要串流模式和一個回傳 {"continue_": True} 的 PreToolUse hook 來保持串流開啟。沒有這個 hook,串流會在許可權回呼被呼叫之前關閉。

    此範例使用 y/n 流程,其中除 y 以外的任何輸入都被視為拒絕。在實際應用中,您可能會建構更豐富的 UI,讓使用者修改請求、提供回饋或完全重新導向 Claude。請參閱回應工具請求了解所有回應方式。

    回應工具請求

    您的回呼回傳兩種回應類型之一:

    回應PythonTypeScript
    允許PermissionResultAllow(updated_input=...){ behavior: "allow", updatedInput }
    拒絕PermissionResultDeny(message=...){ behavior: "deny", message }

    允許時,傳遞工具輸入(原始或修改後的)。拒絕時,提供說明原因的訊息。Claude 會看到此訊息並可能調整其方法。

    from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny
    
    # Allow the tool to execute
    return PermissionResultAllow(updated_input=input_data)
    
    # Block the tool
    return PermissionResultDeny(message="User rejected this action")

    除了允許或拒絕之外,您還可以修改工具的輸入或提供幫助 Claude 調整方法的上下文:

    • 批准:讓工具按照 Claude 的請求執行
    • 帶修改的批准:在執行前修改輸入(例如,清理路徑、添加約束)
    • 拒絕:阻止工具並告訴 Claude 原因
    • 建議替代方案:阻止但引導 Claude 朝使用者想要的方向前進
    • 完全重新導向:使用串流輸入向 Claude 發送全新的指令

    處理澄清問題

    當 Claude 在面對具有多種有效方法的任務時需要更多指引,它會呼叫 AskUserQuestion 工具。這會觸發您的 canUseTool 回呼,其中 toolName 設定為 AskUserQuestion。輸入包含 Claude 的問題作為多選選項,您將其顯示給使用者並回傳他們的選擇。

    澄清問題在 plan 模式中特別常見,在該模式下 Claude 會探索程式碼庫並在提出計劃之前提問。這使得 plan 模式非常適合互動式工作流程,在這些流程中您希望 Claude 在進行更改之前收集需求。

    以下步驟展示如何處理澄清問題:

    1. 1

      傳入 canUseTool 回呼

      在您的查詢選項中傳入 canUseTool 回呼。預設情況下,AskUserQuestion 是可用的。如果您指定了 tools 陣列來限制 Claude 的功能(例如,僅包含 Read、Glob 和 Grep 的唯讀代理),請在該陣列中包含 AskUserQuestion。否則,Claude 將無法提出澄清問題:

      async for message in query(
          prompt="Analyze this codebase",
          options=ClaudeAgentOptions(
              # Include AskUserQuestion in your tools list
              tools=["Read", "Glob", "Grep", "AskUserQuestion"],
              can_use_tool=can_use_tool,
          ),
      ):
          # ...
    2. 2

      偵測 AskUserQuestion

      在您的回呼中,檢查 toolName 是否等於 AskUserQuestion 以與其他工具進行不同處理:

      async def can_use_tool(tool_name: str, input_data: dict, context):
          if tool_name == "AskUserQuestion":
              # Your implementation to collect answers from the user
              return await handle_clarifying_questions(input_data)
          # Handle other tools normally
          return await prompt_for_approval(tool_name, input_data)
    3. 3

      解析問題輸入

      輸入在 questions 陣列中包含 Claude 的問題。每個問題都有 question(要顯示的文字)、options(選項)和 multiSelect(是否允許多選):

      {
        "questions": [
          {
            "question": "How should I format the output?",
            "header": "Format",
            "options": [
              { "label": "Summary", "description": "Brief overview" },
              { "label": "Detailed", "description": "Full explanation" }
            ],
            "multiSelect": false
          },
          {
            "question": "Which sections should I include?",
            "header": "Sections",
            "options": [
              { "label": "Introduction", "description": "Opening context" },
              { "label": "Conclusion", "description": "Final summary" }
            ],
            "multiSelect": true
          }
        ]
      }

      完整欄位描述請參閱問題格式。

    4. 4

      從使用者收集答案

      將問題呈現給使用者並收集他們的選擇。具體方式取決於您的應用程式:終端機提示、網頁表單、行動裝置對話框等。

    5. 5

      將答案回傳給 Claude

      將 answers 物件建構為一個記錄,其中每個鍵是 question 文字,每個值是所選選項的 label:

      來自問題物件用作
      question 欄位(例如 "How should I format the output?")鍵
      所選選項的 label 欄位(例如 "Summary")值

      對於多選問題,用 ", " 連接多個標籤。如果您支援自由文字輸入,請使用使用者的自訂文字作為值。

      return PermissionResultAllow(
          updated_input={
              "questions": input_data.get("questions", []),
              "answers": {
                  "How should I format the output?": "Summary",
                  "Which sections should I include?": "Introduction, Conclusion"
              }
          }
      )

    問題格式

    輸入在 questions 陣列中包含 Claude 生成的問題。每個問題都有以下欄位:

    欄位描述
    question要顯示的完整問題文字
    header問題的簡短標籤(最多 12 個字元)
    options2-4 個選項的陣列,每個都有 label 和 description
    multiSelect如果為 true,使用者可以選擇多個選項

    以下是您將收到的結構範例:

    {
      "questions": [
        {
          "question": "How should I format the output?",
          "header": "Format",
          "options": [
            { "label": "Summary", "description": "Brief overview of key points" },
            { "label": "Detailed", "description": "Full explanation with examples" }
          ],
          "multiSelect": false
        }
      ]
    }

    回應格式

    回傳一個 answers 物件,將每個問題的 question 欄位對應到所選選項的 label:

    欄位描述
    questions傳遞原始問題陣列(工具處理所需)
    answers物件,其中鍵是問題文字,值是所選標籤

    對於多選問題,用 ", " 連接多個標籤。對於自由文字輸入,直接使用使用者的自訂文字。

    {
      "questions": [...],
      "answers": {
        "How should I format the output?": "Summary",
        "Which sections should I include?": "Introduction, Conclusion"
      }
    }

    支援自由文字輸入

    Claude 的預定義選項不一定總能涵蓋使用者想要的內容。要讓使用者輸入自己的答案:

    • 在 Claude 的選項之後顯示一個額外的「其他」選項,接受文字輸入
    • 使用使用者的自訂文字作為答案值(而不是「其他」這個詞)

    完整實作請參閱下方的完整範例。

    完整範例

    當 Claude 需要使用者輸入才能繼續時,它會提出澄清問題。例如,當被要求幫助決定行動應用程式的技術堆疊時,Claude 可能會詢問跨平台與原生、後端偏好或目標平台。這些問題幫助 Claude 做出符合使用者偏好的決定,而不是猜測。

    此範例在終端機應用程式中處理這些問題。以下是每個步驟發生的事情:

    1. 路由請求:canUseTool 回呼檢查工具名稱是否為 "AskUserQuestion" 並路由到專用處理器
    2. 顯示問題:處理器遍歷 questions 陣列並列印每個問題及其編號選項
    3. 收集輸入:使用者可以輸入數字來選擇選項,或直接輸入自由文字(例如 "jquery"、"i don't know")
    4. 對應答案:程式碼檢查輸入是數字(使用選項的標籤)還是自由文字(直接使用文字)
    5. 回傳給 Claude:回應包含原始 questions 陣列和 answers 對應
    import asyncio
    
    from claude_agent_sdk import ClaudeAgentOptions, query
    from claude_agent_sdk.types import HookMatcher, PermissionResultAllow
    
    
    def parse_response(response: str, options: list) -> str:
        """Parse user input as option number(s) or free text."""
        try:
            indices = [int(s.strip()) - 1 for s in response.split(",")]
            labels = [options[i]["label"] for i in indices if 0 <= i < len(options)]
            return ", ".join(labels) if labels else response
        except ValueError:
            return response
    
    
    async def handle_ask_user_question(input_data: dict) -> PermissionResultAllow:
        """Display Claude's questions and collect user answers."""
        answers = {}
    
        for q in input_data.get("questions", []):
            print(f"\n{q['header']}: {q['question']}")
    
            options = q["options"]
            for i, opt in enumerate(options):
                print(f"  {i + 1}. {opt['label']} - {opt['description']}")
            if q.get("multiSelect"):
                print("  (Enter numbers separated by commas, or type your own answer)")
            else:
                print("  (Enter a number, or type your own answer)")
    
            response = input("Your choice: ").strip()
            answers[q["question"]] = parse_response(response, options)
    
        return PermissionResultAllow(
            updated_input={
                "questions": input_data.get("questions", []),
                "answers": answers,
            }
        )
    
    
    async def can_use_tool(tool_name: str, input_data: dict, context) -> PermissionResultAllow:
        # Route AskUserQuestion to our question handler
        if tool_name == "AskUserQuestion":
            return await handle_ask_user_question(input_data)
        # Auto-approve other tools for this example
        return PermissionResultAllow(updated_input=input_data)
    
    
    async def prompt_stream():
        yield {
            "type": "user",
            "message": {"role": "user", "content": "Help me decide on the tech stack for a new mobile app"},
        }
    
    
    # Required workaround: dummy hook keeps the stream open for can_use_tool
    async def dummy_hook(input_data, tool_use_id, context):
        return {"continue_": True}
    
    
    async def main():
        async for message in query(
            prompt=prompt_stream(),
            options=ClaudeAgentOptions(
                can_use_tool=can_use_tool,
                hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
            ),
        ):
            if hasattr(message, "result"):
                print(message.result)
    
    
    asyncio.run(main())

    限制

    • 子代理:AskUserQuestion 目前在透過 Task 工具產生的子代理中不可用
    • 問題限制:每次 AskUserQuestion 呼叫支援 1-4 個問題,每個問題 2-4 個選項

    其他獲取使用者輸入的方式

    canUseTool 回呼和 AskUserQuestion 工具涵蓋了大多數審批和澄清場景,但 SDK 提供了其他從使用者獲取輸入的方式:

    串流輸入

    當您需要以下功能時,使用串流輸入:

    • 在任務中途中斷代理:在 Claude 工作時發送取消信號或改變方向
    • 提供額外上下文:添加 Claude 需要的資訊,而無需等待它詢問
    • 建構聊天介面:讓使用者在長時間運行的操作期間發送後續訊息

    串流輸入非常適合對話式 UI,在這些 UI 中使用者在整個執行過程中與代理互動,而不僅僅是在審批檢查點。

    自訂工具

    當您需要以下功能時,使用自訂工具:

    • 收集結構化輸入:建構表單、精靈或多步驟工作流程,超越 AskUserQuestion 的多選格式
    • 整合外部審批系統:連接到現有的工單、工作流程或審批平台
    • 實作特定領域的互動:建立針對您應用程式需求量身定制的工具,如程式碼審查介面或部署檢查清單

    自訂工具讓您完全控制互動,但比使用內建的 canUseTool 回呼需要更多的實作工作。

    相關資源

    • 設定許可權:設定許可權模式和規則
    • 使用 hooks 控制執行:在代理生命週期的關鍵點執行自訂程式碼
    • TypeScript SDK 參考文件:完整的 canUseTool API 文件

    Was this page helpful?

    • 偵測 Claude 何時需要輸入