SpyBara
Go Premium

agent-sdk/user-input.md 2026-05-08 22:00 UTC to 2026-05-09 04:57 UTC

810 added, 0 removed.

2026
Sun 31 06:39 Sat 30 06:23 Fri 29 06:38 Thu 28 06:37 Wed 27 06:42 Tue 26 06:33 Sun 24 06:25 Sat 23 06:18 Fri 22 06:33 Thu 21 06:36 Wed 20 06:35 Tue 19 06:34 Mon 18 23:59 Sun 17 01:01 Fri 15 22:58 Thu 14 17:02 Wed 13 23:01 Tue 12 22:57 Mon 11 23:00 Sun 10 23:03 Sat 9 04:57 Fri 8 22:00 Thu 7 22:59 Tue 5 23:00 Mon 4 22:58 Sat 2 18:14 Fri 1 18:19

Lidar com aprovações e entrada do usuário

Apresente as solicitações de aprovação e perguntas de esclarecimento do Claude aos usuários e retorne suas decisões ao SDK.

Ao trabalhar em uma tarefa, Claude às vezes precisa verificar com os usuários. Pode precisar de permissão antes de excluir arquivos ou precisar perguntar qual banco de dados usar para um novo projeto. Seu aplicativo precisa apresentar essas solicitações aos usuários para que Claude possa continuar com sua entrada.

Claude solicita entrada do usuário em duas situações: quando precisa de permissão para usar uma ferramenta (como excluir arquivos ou executar comandos) e quando tem perguntas de esclarecimento (por meio da ferramenta AskUserQuestion). Ambas acionam seu callback canUseTool, que pausa a execução até que você retorne uma resposta. Isso é diferente dos turnos de conversa normais, onde Claude termina e aguarda sua próxima mensagem.

Para perguntas de esclarecimento, Claude gera as perguntas e opções. Seu papel é apresentá-las aos usuários e retornar suas seleções. Você não pode adicionar suas próprias perguntas a este fluxo; se precisar perguntar algo aos usuários, faça isso separadamente na lógica do seu aplicativo.

O callback pode permanecer pendente indefinidamente. A execução permanece pausada até que seu callback retorne, e o SDK apenas cancela a espera quando a própria consulta é cancelada. Se um usuário puder levar mais tempo para responder do que seu processo pode razoavelmente permanecer em execução, o SDK TypeScript suporta o hook defer, que permite que o processo saia e retome mais tarde a partir da sessão persistida; esta opção não está disponível no SDK Python.

Este guia mostra como detectar cada tipo de solicitação e responder apropriadamente.

Detectar quando Claude precisa de entrada

Passe um callback canUseTool nas opções de sua consulta. O callback é acionado sempre que Claude precisa de entrada do usuário, recebendo o nome da ferramenta e a entrada como argumentos:

async def handle_tool_request(tool_name, input_data, context):
# Solicite ao usuário e retorne permitir ou negar
...


options = ClaudeAgentOptions(can_use_tool=handle_tool_request)

O callback é acionado em dois casos:

  1. Ferramenta precisa de aprovação: Claude quer usar uma ferramenta que não é aprovada automaticamente por regras de permissão ou modos. Verifique tool_name para a ferramenta (por exemplo, "Bash", "Write").
  2. Claude faz uma pergunta: Claude chama a ferramenta AskUserQuestion. Verifique se tool_name == "AskUserQuestion" para tratá-la diferentemente. Se você especificar um array tools, inclua AskUserQuestion para que isso funcione. Veja Lidar com perguntas de esclarecimento para detalhes.

Lidar com solicitações de aprovação de ferramentas

Depois de passar um callback canUseTool nas opções de sua consulta, ele é acionado quando Claude quer usar uma ferramenta que não é aprovada automaticamente. Seu callback recebe três argumentos:

Argumento Descrição
toolName O nome da ferramenta que Claude quer usar (por exemplo, "Bash", "Write", "Edit")
input Os parâmetros que Claude está passando para a ferramenta. O conteúdo varia por ferramenta.
options (TS) / context (Python) Contexto adicional incluindo suggestions opcional (entradas PermissionUpdate propostas para evitar re-solicitação) e um sinal de cancelamento. Em TypeScript, signal é um AbortSignal; em Python, o campo de sinal é reservado para uso futuro. Veja ToolPermissionContext para Python.

O objeto input contém parâmetros específicos da ferramenta. Exemplos comuns:

Ferramenta Campos de entrada
Bash command, description, timeout
Write file_path, content
Edit file_path, old_string, new_string
Read file_path, offset, limit

Veja a referência do SDK para esquemas de entrada completos: Python | TypeScript.

Você pode exibir essas informações ao usuário para que ele possa decidir se permite ou rejeita a ação, e então retornar a resposta apropriada.

O exemplo a seguir pede ao Claude para criar e excluir um arquivo de teste. Quando Claude tenta cada operação, o callback imprime a solicitação de ferramenta no terminal e solicita aprovação s/n.

import asyncio

from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, 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:
# Exiba a solicitação de ferramenta
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}")

# Obtenha aprovação do usuário
response = input("Allow this action? (y/n): ")

# Retorne permitir ou negar com base na resposta do usuário
if response.lower() == "y":
# Permitir: ferramenta executa com a entrada original (ou modificada)
return PermissionResultAllow(updated_input=input_data)
else:
# Negar: ferramenta não executa, Claude vê a mensagem
return PermissionResultDeny(message="User denied this action")


# Solução alternativa necessária: hook fictício mantém o fluxo aberto para 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 isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)


asyncio.run(main())

Este exemplo usa um fluxo s/n onde qualquer entrada diferente de s é tratada como uma negação. Na prática, você pode construir uma interface de usuário mais rica que permite aos usuários modificar a solicitação, fornecer feedback ou redirecionar Claude completamente. Veja Responder a solicitações de ferramentas para todas as maneiras que você pode responder.

Responder a solicitações de ferramentas

Seu callback retorna um de dois tipos de resposta:

Resposta Python TypeScript
Permitir PermissionResultAllow(updated_input=...) { behavior: "allow", updatedInput }
Negar PermissionResultDeny(message=...) { behavior: "deny", message }

Ao permitir, passe a entrada da ferramenta (original ou modificada). Ao negar, forneça uma mensagem explicando por quê. Claude vê esta mensagem e pode ajustar sua abordagem.

from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny

# Permita que a ferramenta execute
return PermissionResultAllow(updated_input=input_data)

# Bloqueie a ferramenta
return PermissionResultDeny(message="User rejected this action")

Além de permitir ou negar, você pode modificar a entrada da ferramenta ou fornecer contexto que ajude Claude a ajustar sua abordagem:

  • Aprovar: deixe a ferramenta executar conforme Claude solicitou
  • Aprovar com alterações: modifique a entrada antes da execução (por exemplo, sanitize caminhos, adicione restrições)
  • Rejeitar: bloqueie a ferramenta e diga ao Claude por quê
  • Sugerir alternativa: bloqueie mas guie Claude para o que o usuário quer em vez disso
  • Redirecionar completamente: use entrada de streaming para enviar ao Claude uma instrução completamente nova

O usuário aprova a ação como está. Passe a input do seu callback inalterada e a ferramenta executa exatamente como Claude solicitou.

async def can_use_tool(tool_name, input_data, context):
print(f"Claude wants to use {tool_name}")
approved = await ask_user("Allow this action?")

if approved:
return PermissionResultAllow(updated_input=input_data)
return PermissionResultDeny(message="User declined")

Lidar com perguntas de esclarecimento

Quando Claude precisa de mais direção em uma tarefa com múltiplas abordagens válidas, ele chama a ferramenta AskUserQuestion. Isso aciona seu callback canUseTool com toolName definido como AskUserQuestion. A entrada contém as perguntas do Claude como opções de múltipla escolha, que você exibe ao usuário e retorna suas seleções.

Os passos a seguir mostram como lidar com perguntas de esclarecimento:

1

Passe um callback canUseTool

Passe um callback canUseTool nas opções de sua consulta. Por padrão, AskUserQuestion está disponível. Se você especificar um array tools para restringir as capacidades do Claude (por exemplo, um agente somente leitura com apenas Read, Glob e Grep), inclua AskUserQuestion nesse array. Caso contrário, Claude não será capaz de fazer perguntas de esclarecimento:

async for message in query(
prompt="Analyze this codebase",
options=ClaudeAgentOptions(
# Inclua AskUserQuestion em sua lista de ferramentas
tools=["Read", "Glob", "Grep", "AskUserQuestion"],
can_use_tool=can_use_tool,
),
):
print(message)
2

Detecte AskUserQuestion

Em seu callback, verifique se toolName é igual a AskUserQuestion para tratá-lo diferentemente de outras ferramentas:

async def can_use_tool(tool_name: str, input_data: dict, context):
if tool_name == "AskUserQuestion":
# Sua implementação para coletar respostas do usuário
return await handle_clarifying_questions(input_data)
# Lidar com outras ferramentas normalmente
return await prompt_for_approval(tool_name, input_data)
3

Analise a entrada da pergunta

A entrada contém as perguntas do Claude em um array questions. Cada pergunta tem uma question (o texto a exibir), options (as escolhas) e multiSelect (se múltiplas seleções são permitidas):

{
"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
}
]
}

Veja Formato de pergunta para descrições completas de campos.

4

Colete respostas do usuário

Apresente as perguntas ao usuário e colete suas seleções. Como você faz isso depende de seu aplicativo: um prompt de terminal, um formulário web, um diálogo móvel, etc.

5

Retorne respostas ao Claude

Construa o objeto answers como um registro onde cada chave é o texto question e cada valor é o label da opção selecionada:

Do objeto de pergunta Use como
Campo question (por exemplo, "How should I format the output?") Chave
Campo label da opção selecionada (por exemplo, "Summary") Valor

Para perguntas de seleção múltipla, passe um array de labels ou junte-os com ", ". Se você suportar entrada de texto livre, use o texto personalizado do usuário como o valor.

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

Formato de pergunta

A entrada contém as perguntas geradas pelo Claude em um array questions. Cada pergunta tem estes campos:

Campo Descrição
question O texto completo da pergunta a exibir
header Rótulo curto para a pergunta (máximo 12 caracteres)
options Array de 2-4 escolhas, cada uma com label e description. TypeScript: opcionalmente preview (veja abaixo)
multiSelect Se true, os usuários podem selecionar múltiplas opções

A estrutura que seu callback recebe:

{
  "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
    }
  ]
}

Visualizações de opção (TypeScript)

toolConfig.askUserQuestion.previewFormat adiciona um campo preview a cada opção para que seu aplicativo possa mostrar uma simulação visual ao lado do rótulo. Sem esta configuração, Claude não gera visualizações e o campo está ausente.

previewFormat preview contém
não definido (padrão) Campo está ausente. Claude não gera visualizações.
"markdown" Arte ASCII e blocos de código cercados
"html" Um fragmento <div> estilizado (o SDK rejeita <script>, <style> e <!DOCTYPE> antes que seu callback seja executado)

O formato se aplica a todas as perguntas na sessão. Claude inclui preview em opções onde uma comparação visual ajuda (escolhas de layout, esquemas de cores) e a omite onde não ajudaria (confirmações sim/não, escolhas apenas de texto). Verifique se há undefined antes de renderizar.

import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Help me choose a card layout",
  options: {
    toolConfig: {
      askUserQuestion: { previewFormat: "html" }
    },
    canUseTool: async (toolName, input) => {
      // input.questions[].options[].preview é uma string HTML ou undefined
      return { behavior: "allow", updatedInput: input };
    }
  }
})) {
  // ...
}

Uma opção com uma visualização HTML:

{
  "label": "Compact",
  "description": "Title and metric value only",
  "preview": "<div style=\"padding:12px;border:1px solid #ddd;border-radius:8px\"><div style=\"font-size:12px;color:#666\">Active users</div><div style=\"font-size:28px;font-weight:600\">1,284</div></div>"
}

Formato de resposta

Retorne um objeto answers mapeando cada campo question da pergunta para o label da opção selecionada:

Campo Descrição
questions Passe o array de perguntas original (obrigatório para processamento de ferramentas)
answers Objeto onde as chaves são texto de pergunta e os valores são labels selecionados

Para perguntas de seleção múltipla, passe um array de labels ou junte-os com ", ". Para entrada de texto livre, use o texto personalizado do usuário diretamente.

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

Suporte para entrada de texto livre

As opções predefinidas do Claude nem sempre cobrirão o que os usuários querem. Para permitir que os usuários digitem sua própria resposta:

  • Exiba uma escolha "Outro" adicional após as opções do Claude que aceita entrada de texto
  • Use o texto personalizado do usuário como o valor da resposta (não a palavra "Outro")

Veja o exemplo completo abaixo para uma implementação completa.

Exemplo completo

Claude faz perguntas de esclarecimento quando precisa de entrada do usuário para prosseguir. Por exemplo, quando solicitado a ajudar a decidir sobre uma pilha de tecnologia para um aplicativo móvel, Claude pode perguntar sobre cross-platform vs nativo, preferências de backend ou plataformas alvo. Essas perguntas ajudam Claude a tomar decisões que correspondem às preferências do usuário em vez de adivinhar.

Este exemplo lida com essas perguntas em um aplicativo de terminal. Aqui está o que acontece em cada etapa:

  1. Rotear a solicitação: O callback canUseTool verifica se o nome da ferramenta é "AskUserQuestion" e roteia para um manipulador dedicado
  2. Exibir perguntas: O manipulador percorre o array questions e imprime cada pergunta com opções numeradas
  3. Coletar entrada: O usuário pode inserir um número para selecionar uma opção ou digitar texto livre diretamente (por exemplo, "jquery", "não sei")
  4. Mapear respostas: O código verifica se a entrada é numérica (usa o label da opção) ou texto livre (usa o texto diretamente)
  5. Retornar ao Claude: A resposta inclui tanto o array questions original quanto o mapeamento answers
import asyncio

from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
from claude_agent_sdk.types import HookMatcher, PermissionResultAllow


def parse_response(response: str, options: list) -> str:
"""Analise a entrada do usuário como número(s) de opção ou texto livre."""
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:
"""Exiba as perguntas do Claude e colete respostas do usuário."""
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:
# Rotear AskUserQuestion para nosso manipulador de perguntas
if tool_name == "AskUserQuestion":
return await handle_ask_user_question(input_data)
# Auto-aprovar outras ferramentas para este exemplo
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",
},
}


# Solução alternativa necessária: hook fictício mantém o fluxo aberto para 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 isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)


asyncio.run(main())

Limitações

  • Subagentes: AskUserQuestion não está disponível em subagentes gerados por meio da ferramenta Agent
  • Limites de perguntas: cada chamada AskUserQuestion suporta 1-4 perguntas com 2-4 opções cada

Outras maneiras de obter entrada do usuário

O callback canUseTool e a ferramenta AskUserQuestion cobrem a maioria dos cenários de aprovação e esclarecimento, mas o SDK oferece outras maneiras de obter entrada dos usuários:

Entrada de streaming

Use entrada de streaming quando você precisar:

  • Interromper o agente no meio da tarefa: enviar um sinal de cancelamento ou mudar de direção enquanto Claude está trabalhando
  • Fornecer contexto adicional: adicionar informações que Claude precisa sem esperar que ele pergunte
  • Construir interfaces de chat: permitir que os usuários enviem mensagens de acompanhamento durante operações de longa duração

A entrada de streaming é ideal para interfaces conversacionais onde os usuários interagem com o agente durante toda a execução, não apenas em pontos de aprovação.

Ferramentas personalizadas

Use ferramentas personalizadas quando você precisar:

  • Coletar entrada estruturada: construir formulários, assistentes ou fluxos de trabalho de várias etapas que vão além do formato de múltipla escolha do AskUserQuestion
  • Integrar sistemas de aprovação externos: conectar a plataformas de ticketing, fluxo de trabalho ou aprovação existentes
  • Implementar interações específicas do domínio: criar ferramentas adaptadas às necessidades do seu aplicativo, como interfaces de revisão de código ou listas de verificação de implantação

As ferramentas personalizadas lhe dão controle total sobre a interação, mas requerem mais trabalho de implementação do que usar o callback canUseTool integrado.

Recursos relacionados