1> ## Documentation Index
2> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3> Use this file to discover all available pages before exploring further.
4
5# Gestire approvazioni e input dell'utente
6
7> Presenta le richieste di approvazione e le domande di chiarimento di Claude agli utenti, quindi restituisci le loro decisioni all'SDK.
8
9Durante il lavoro su un'attività, Claude a volte ha bisogno di verificare con gli utenti. Potrebbe aver bisogno di autorizzazione prima di eliminare file, oppure potrebbe aver bisogno di chiedere quale database utilizzare per un nuovo progetto. La tua applicazione deve presentare queste richieste agli utenti in modo che Claude possa continuare con il loro input.
10
11Claude richiede input dell'utente in due situazioni: quando ha bisogno di **autorizzazione per utilizzare uno strumento** (come eliminare file o eseguire comandi), e quando ha **domande di chiarimento** (tramite lo strumento `AskUserQuestion`). Entrambi attivano il tuo callback `canUseTool`, che mette in pausa l'esecuzione fino a quando non restituisci una risposta. Questo è diverso dai normali turni di conversazione in cui Claude finisce e attende il tuo prossimo messaggio.
12
13Per le domande di chiarimento, Claude genera le domande e le opzioni. Il tuo ruolo è presentarle agli utenti e restituire le loro selezioni. Non puoi aggiungere le tue domande a questo flusso; se hai bisogno di chiedere qualcosa agli utenti tu stesso, fallo separatamente nella logica dell'applicazione.
14
15Il callback può rimanere in sospeso indefinitamente. L'esecuzione rimane in pausa fino a quando il callback non restituisce, e l'SDK annulla l'attesa solo quando la query stessa viene annullata. Se un utente potrebbe impiegare più tempo per rispondere di quanto il tuo processo possa ragionevolmente rimanere in esecuzione, l'SDK TypeScript supporta il [hook `defer` decision](/it/hooks#defer-a-tool-call-for-later), che consente al processo di uscire e riprendere in seguito dalla sessione persistente; questa opzione non è disponibile nell'SDK Python.
16
17Questa guida ti mostra come rilevare ogni tipo di richiesta e rispondere in modo appropriato.
18
19## Rilevare quando Claude ha bisogno di input
20
21Passa un callback `canUseTool` nelle opzioni della query. Il callback si attiva ogni volta che Claude ha bisogno di input dell'utente, ricevendo il nome dello strumento e l'input come argomenti:
22
23<CodeGroup>
24 ```python Python theme={null}
25 async def handle_tool_request(tool_name, input_data, context):
26 # Chiedi all'utente e restituisci allow o deny
27 ...
28
29
30 options = ClaudeAgentOptions(can_use_tool=handle_tool_request)
31 ```
32
33 ```typescript TypeScript theme={null}
34 async function handleToolRequest(toolName, input, options) {
35 // options includes { signal: AbortSignal, suggestions?: PermissionUpdate[] }
36 // Chiedi all'utente e restituisci allow o deny
37 }
38
39 const options = { canUseTool: handleToolRequest };
40 ```
41</CodeGroup>
42
43Il callback si attiva in due casi:
44
451. **Lo strumento ha bisogno di approvazione**: Claude vuole utilizzare uno strumento che non è approvato automaticamente dalle [regole di autorizzazione](/it/agent-sdk/permissions) o dalle modalità. Controlla `tool_name` per lo strumento (ad es. `"Bash"`, `"Write"`).
462. **Claude pone una domanda**: Claude chiama lo strumento `AskUserQuestion`. Controlla se `tool_name == "AskUserQuestion"` per gestirlo diversamente. Se specifichi un array `tools`, includi `AskUserQuestion` affinché funzioni. Vedi [Gestire domande di chiarimento](#handle-clarifying-questions) per i dettagli.
47
48<Note>
49 Per consentire o negare automaticamente gli strumenti senza chiedere agli utenti, utilizza invece gli [hook](/it/agent-sdk/hooks). Gli hook vengono eseguiti prima di `canUseTool` e possono consentire, negare o modificare le richieste in base alla tua logica. Puoi anche utilizzare l'[hook `PermissionRequest`](/it/agent-sdk/hooks#available-hooks) per inviare notifiche esterne (Slack, email, push) quando Claude è in attesa di approvazione.
50</Note>
51
52## Gestire le richieste di approvazione dello strumento
53
54Una volta passato un callback `canUseTool` nelle opzioni della query, si attiva quando Claude vuole utilizzare uno strumento che non è approvato automaticamente. Il tuo callback riceve tre argomenti:
55
56| Argomento | Descrizione |
57| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
58| `toolName` | Il nome dello strumento che Claude vuole utilizzare (ad es. `"Bash"`, `"Write"`, `"Edit"`) |
59| `input` | I parametri che Claude sta passando allo strumento. Il contenuto varia a seconda dello strumento. |
60| `options` (TS) / `context` (Python) | Contesto aggiuntivo incluso `suggestions` opzionale (voci `PermissionUpdate` proposte per evitare di ripetere le domande) e un segnale di annullamento. In TypeScript, `signal` è un `AbortSignal`; in Python, il campo signal è riservato per uso futuro. Vedi [`ToolPermissionContext`](/it/agent-sdk/python#toolpermissioncontext) per Python. |
61
62L'oggetto `input` contiene parametri specifici dello strumento. Esempi comuni:
63
64| Strumento | Campi di input |
65| --------- | --------------------------------------- |
66| `Bash` | `command`, `description`, `timeout` |
67| `Write` | `file_path`, `content` |
68| `Edit` | `file_path`, `old_string`, `new_string` |
69| `Read` | `file_path`, `offset`, `limit` |
70
71Vedi il riferimento SDK per gli schemi di input completi: [Python](/it/agent-sdk/python#tool-input%2Foutput-types) | [TypeScript](/it/agent-sdk/typescript#tool-input-types).
72
73Puoi visualizzare queste informazioni all'utente in modo che possa decidere se consentire o rifiutare l'azione, quindi restituire la risposta appropriata.
74
75L'esempio seguente chiede a Claude di creare ed eliminare un file di test. Quando Claude tenta ogni operazione, il callback stampa la richiesta dello strumento nel terminale e chiede l'approvazione s/n.
76
77<CodeGroup>
78 ```python Python theme={null}
79 import asyncio
80
81 from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
82 from claude_agent_sdk.types import (
83 HookMatcher,
84 PermissionResultAllow,
85 PermissionResultDeny,
86 ToolPermissionContext,
87 )
88
89
90 async def can_use_tool(
91 tool_name: str, input_data: dict, context: ToolPermissionContext
92 ) -> PermissionResultAllow | PermissionResultDeny:
93 # Visualizza la richiesta dello strumento
94 print(f"\nTool: {tool_name}")
95 if tool_name == "Bash":
96 print(f"Command: {input_data.get('command')}")
97 if input_data.get("description"):
98 print(f"Description: {input_data.get('description')}")
99 else:
100 print(f"Input: {input_data}")
101
102 # Ottieni l'approvazione dell'utente
103 response = input("Allow this action? (y/n): ")
104
105 # Restituisci allow o deny in base alla risposta dell'utente
106 if response.lower() == "y":
107 # Allow: lo strumento viene eseguito con l'input originale (o modificato)
108 return PermissionResultAllow(updated_input=input_data)
109 else:
110 # Deny: lo strumento non viene eseguito, Claude vede il messaggio
111 return PermissionResultDeny(message="User denied this action")
112
113
114 # Workaround richiesto: dummy hook mantiene il flusso aperto per can_use_tool
115 async def dummy_hook(input_data, tool_use_id, context):
116 return {"continue_": True}
117
118
119 async def prompt_stream():
120 yield {
121 "type": "user",
122 "message": {
123 "role": "user",
124 "content": "Create a test file in /tmp and then delete it",
125 },
126 }
127
128
129 async def main():
130 async for message in query(
131 prompt=prompt_stream(),
132 options=ClaudeAgentOptions(
133 can_use_tool=can_use_tool,
134 hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
135 ),
136 ):
137 if isinstance(message, ResultMessage) and message.subtype == "success":
138 print(message.result)
139
140
141 asyncio.run(main())
142 ```
143
144 ```typescript TypeScript theme={null}
145 import { query } from "@anthropic-ai/claude-agent-sdk";
146 import * as readline from "readline";
147
148 // Helper per chiedere all'utente l'input nel terminale
149 function prompt(question: string): Promise<string> {
150 const rl = readline.createInterface({
151 input: process.stdin,
152 output: process.stdout
153 });
154 return new Promise((resolve) =>
155 rl.question(question, (answer) => {
156 rl.close();
157 resolve(answer);
158 })
159 );
160 }
161
162 for await (const message of query({
163 prompt: "Create a test file in /tmp and then delete it",
164 options: {
165 canUseTool: async (toolName, input) => {
166 // Visualizza la richiesta dello strumento
167 console.log(`\nTool: ${toolName}`);
168 if (toolName === "Bash") {
169 console.log(`Command: ${input.command}`);
170 if (input.description) console.log(`Description: ${input.description}`);
171 } else {
172 console.log(`Input: ${JSON.stringify(input, null, 2)}`);
173 }
174
175 // Ottieni l'approvazione dell'utente
176 const response = await prompt("Allow this action? (y/n): ");
177
178 // Restituisci allow o deny in base alla risposta dell'utente
179 if (response.toLowerCase() === "y") {
180 // Allow: lo strumento viene eseguito con l'input originale (o modificato)
181 return { behavior: "allow", updatedInput: input };
182 } else {
183 // Deny: lo strumento non viene eseguito, Claude vede il messaggio
184 return { behavior: "deny", message: "User denied this action" };
185 }
186 }
187 }
188 })) {
189 if ("result" in message) console.log(message.result);
190 }
191 ```
192</CodeGroup>
193
194<Note>
195 In Python, `can_use_tool` richiede la [modalità streaming](/it/agent-sdk/streaming-vs-single-mode) e un hook `PreToolUse` che restituisce `{"continue_": True}` per mantenere il flusso aperto. Senza questo hook, il flusso si chiude prima che il callback di autorizzazione possa essere invocato.
196</Note>
197
198Questo esempio utilizza un flusso s/n in cui qualsiasi input diverso da `y` viene trattato come un rifiuto. In pratica, potresti creare un'interfaccia utente più ricca che consenta agli utenti di modificare la richiesta, fornire feedback o reindirizzare completamente Claude. Vedi [Rispondere alle richieste dello strumento](#respond-to-tool-requests) per tutti i modi in cui puoi rispondere.
199
200### Rispondere alle richieste dello strumento
201
202Il tuo callback restituisce uno di due tipi di risposta:
203
204| Risposta | Python | TypeScript |
205| --------- | ------------------------------------------ | ------------------------------------- |
206| **Allow** | `PermissionResultAllow(updated_input=...)` | `{ behavior: "allow", updatedInput }` |
207| **Deny** | `PermissionResultDeny(message=...)` | `{ behavior: "deny", message }` |
208
209Quando consenti, passa l'input dello strumento (originale o modificato). Quando neghi, fornisci un messaggio che spiega il motivo. Claude vede questo messaggio e potrebbe adattare il suo approccio.
210
211<CodeGroup>
212 ```python Python theme={null}
213 from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny
214
215 # Consenti l'esecuzione dello strumento
216 return PermissionResultAllow(updated_input=input_data)
217
218 # Blocca lo strumento
219 return PermissionResultDeny(message="User rejected this action")
220 ```
221
222 ```typescript TypeScript theme={null}
223 // Consenti l'esecuzione dello strumento
224 return { behavior: "allow", updatedInput: input };
225
226 // Blocca lo strumento
227 return { behavior: "deny", message: "User rejected this action" };
228 ```
229</CodeGroup>
230
231Oltre a consentire o negare, puoi modificare l'input dello strumento o fornire contesto che aiuta Claude ad adattare il suo approccio:
232
233* **Approva**: consenti l'esecuzione dello strumento come richiesto da Claude
234* **Approva con modifiche**: modifica l'input prima dell'esecuzione (ad es. sanitizza i percorsi, aggiungi vincoli)
235* **Rifiuta**: blocca lo strumento e spiega a Claude il motivo
236* **Suggerisci alternativa**: blocca ma guida Claude verso ciò che l'utente vuole invece
237* **Reindirizza completamente**: utilizza [input streaming](/it/agent-sdk/streaming-vs-single-mode) per inviare a Claude un'istruzione completamente nuova
238
239<Tabs>
240 <Tab title="Approva">
241 L'utente approva l'azione così com'è. Passa l'`input` dal tuo callback invariato e lo strumento viene eseguito esattamente come richiesto da Claude.
242
243 <CodeGroup>
244 ```python Python theme={null}
245 async def can_use_tool(tool_name, input_data, context):
246 print(f"Claude wants to use {tool_name}")
247 approved = await ask_user("Allow this action?")
248
249 if approved:
250 return PermissionResultAllow(updated_input=input_data)
251 return PermissionResultDeny(message="User declined")
252 ```
253
254 ```typescript TypeScript theme={null}
255 canUseTool: async (toolName, input) => {
256 console.log(`Claude wants to use ${toolName}`);
257 const approved = await askUser("Allow this action?");
258
259 if (approved) {
260 return { behavior: "allow", updatedInput: input };
261 }
262 return { behavior: "deny", message: "User declined" };
263 };
264 ```
265 </CodeGroup>
266 </Tab>
267
268 <Tab title="Approva con modifiche">
269 L'utente approva ma vuole modificare la richiesta prima. Puoi cambiare l'input prima che lo strumento venga eseguito. Claude vede il risultato ma non gli viene detto che hai cambiato qualcosa. Utile per sanitizzare i parametri, aggiungere vincoli o limitare l'accesso.
270
271 <CodeGroup>
272 ```python Python theme={null}
273 async def can_use_tool(tool_name, input_data, context):
274 if tool_name == "Bash":
275 # L'utente ha approvato, ma limita tutti i comandi alla sandbox
276 sandboxed_input = {**input_data}
277 sandboxed_input["command"] = input_data["command"].replace(
278 "/tmp", "/tmp/sandbox"
279 )
280 return PermissionResultAllow(updated_input=sandboxed_input)
281 return PermissionResultAllow(updated_input=input_data)
282 ```
283
284 ```typescript TypeScript theme={null}
285 canUseTool: async (toolName, input) => {
286 if (toolName === "Bash") {
287 // L'utente ha approvato, ma limita tutti i comandi alla sandbox
288 const sandboxedInput = {
289 ...input,
290 command: input.command.replace("/tmp", "/tmp/sandbox")
291 };
292 return { behavior: "allow", updatedInput: sandboxedInput };
293 }
294 return { behavior: "allow", updatedInput: input };
295 };
296 ```
297 </CodeGroup>
298 </Tab>
299
300 <Tab title="Rifiuta">
301 L'utente non vuole che questa azione accada. Blocca lo strumento e fornisci un messaggio che spiega il motivo. Claude vede questo messaggio e potrebbe provare un approccio diverso.
302
303 <CodeGroup>
304 ```python Python theme={null}
305 async def can_use_tool(tool_name, input_data, context):
306 approved = await ask_user(f"Allow {tool_name}?")
307
308 if not approved:
309 return PermissionResultDeny(message="User rejected this action")
310 return PermissionResultAllow(updated_input=input_data)
311 ```
312
313 ```typescript TypeScript theme={null}
314 canUseTool: async (toolName, input) => {
315 const approved = await askUser(`Allow ${toolName}?`);
316
317 if (!approved) {
318 return {
319 behavior: "deny",
320 message: "User rejected this action"
321 };
322 }
323 return { behavior: "allow", updatedInput: input };
324 };
325 ```
326 </CodeGroup>
327 </Tab>
328
329 <Tab title="Suggerisci alternativa">
330 L'utente non vuole questa azione specifica, ma ha un'idea diversa. Blocca lo strumento e includi una guida nel tuo messaggio. Claude leggerà questo e deciderà come procedere in base al tuo feedback.
331
332 <CodeGroup>
333 ```python Python theme={null}
334 async def can_use_tool(tool_name, input_data, context):
335 if tool_name == "Bash" and "rm" in input_data.get("command", ""):
336 # L'utente non vuole eliminare, suggerisci di comprimere invece
337 return PermissionResultDeny(
338 message="User doesn't want to delete files. They asked if you could compress them into an archive instead."
339 )
340 return PermissionResultAllow(updated_input=input_data)
341 ```
342
343 ```typescript TypeScript theme={null}
344 canUseTool: async (toolName, input) => {
345 if (toolName === "Bash" && input.command.includes("rm")) {
346 // L'utente non vuole eliminare, suggerisci di comprimere invece
347 return {
348 behavior: "deny",
349 message:
350 "User doesn't want to delete files. They asked if you could compress them into an archive instead."
351 };
352 }
353 return { behavior: "allow", updatedInput: input };
354 };
355 ```
356 </CodeGroup>
357 </Tab>
358
359 <Tab title="Reindirizza completamente">
360 Per un cambio di direzione completo (non solo una spinta), utilizza [input streaming](/it/agent-sdk/streaming-vs-single-mode) per inviare a Claude una nuova istruzione direttamente. Questo bypassa la richiesta dello strumento corrente e dà a Claude istruzioni completamente nuove da seguire.
361 </Tab>
362</Tabs>
363
364## Gestire domande di chiarimento
365
366Quando Claude ha bisogno di più direzione su un'attività con più approcci validi, chiama lo strumento `AskUserQuestion`. Questo attiva il tuo callback `canUseTool` con `toolName` impostato su `AskUserQuestion`. L'input contiene le domande di Claude come opzioni a scelta multipla, che visualizzi all'utente e restituisci le sue selezioni.
367
368<Tip>
369 Le domande di chiarimento sono particolarmente comuni nella [modalità `plan`](/it/agent-sdk/permissions#plan-mode-plan), dove Claude esplora la base di codice e pone domande prima di proporre un piano. Questo rende la modalità plan ideale per flussi di lavoro interattivi in cui vuoi che Claude raccolga i requisiti prima di apportare modifiche.
370</Tip>
371
372I seguenti passaggi mostrano come gestire le domande di chiarimento:
373
374<Steps>
375 <Step title="Passa un callback canUseTool">
376 Passa un callback `canUseTool` nelle opzioni della query. Per impostazione predefinita, `AskUserQuestion` è disponibile. Se specifichi un array `tools` per limitare le capacità di Claude (ad esempio, un agente di sola lettura con solo `Read`, `Glob` e `Grep`), includi `AskUserQuestion` in quell'array. Altrimenti, Claude non sarà in grado di porre domande di chiarimento:
377
378 <CodeGroup>
379 ```python Python theme={null}
380 async for message in query(
381 prompt="Analyze this codebase",
382 options=ClaudeAgentOptions(
383 # Includi AskUserQuestion nella tua lista di strumenti
384 tools=["Read", "Glob", "Grep", "AskUserQuestion"],
385 can_use_tool=can_use_tool,
386 ),
387 ):
388 print(message)
389 ```
390
391 ```typescript TypeScript theme={null}
392 for await (const message of query({
393 prompt: "Analyze this codebase",
394 options: {
395 // Includi AskUserQuestion nella tua lista di strumenti
396 tools: ["Read", "Glob", "Grep", "AskUserQuestion"],
397 canUseTool: async (toolName, input) => {
398 // Gestisci le domande di chiarimento qui
399 }
400 }
401 })) {
402 console.log(message);
403 }
404 ```
405 </CodeGroup>
406 </Step>
407
408 <Step title="Rileva AskUserQuestion">
409 Nel tuo callback, controlla se `toolName` è uguale a `AskUserQuestion` per gestirlo diversamente da altri strumenti:
410
411 <CodeGroup>
412 ```python Python theme={null}
413 async def can_use_tool(tool_name: str, input_data: dict, context):
414 if tool_name == "AskUserQuestion":
415 # La tua implementazione per raccogliere risposte dall'utente
416 return await handle_clarifying_questions(input_data)
417 # Gestisci altri strumenti normalmente
418 return await prompt_for_approval(tool_name, input_data)
419 ```
420
421 ```typescript TypeScript theme={null}
422 canUseTool: async (toolName, input) => {
423 if (toolName === "AskUserQuestion") {
424 // La tua implementazione per raccogliere risposte dall'utente
425 return handleClarifyingQuestions(input);
426 }
427 // Gestisci altri strumenti normalmente
428 return promptForApproval(toolName, input);
429 };
430 ```
431 </CodeGroup>
432 </Step>
433
434 <Step title="Analizza l'input della domanda">
435 L'input contiene le domande di Claude in un array `questions`. Ogni domanda ha una `question` (il testo da visualizzare), `options` (le scelte) e `multiSelect` (se sono consentite più selezioni):
436
437 ```json theme={null}
438 {
439 "questions": [
440 {
441 "question": "How should I format the output?",
442 "header": "Format",
443 "options": [
444 { "label": "Summary", "description": "Brief overview" },
445 { "label": "Detailed", "description": "Full explanation" }
446 ],
447 "multiSelect": false
448 },
449 {
450 "question": "Which sections should I include?",
451 "header": "Sections",
452 "options": [
453 { "label": "Introduction", "description": "Opening context" },
454 { "label": "Conclusion", "description": "Final summary" }
455 ],
456 "multiSelect": true
457 }
458 ]
459 }
460 ```
461
462 Vedi [Formato della domanda](#question-format) per le descrizioni complete dei campi.
463 </Step>
464
465 <Step title="Raccogli risposte dall'utente">
466 Presenta le domande all'utente e raccogli le sue selezioni. Come lo fai dipende dalla tua applicazione: un prompt del terminale, un modulo web, una finestra di dialogo mobile, ecc.
467 </Step>
468
469 <Step title="Restituisci le risposte a Claude">
470 Costruisci l'oggetto `answers` come un record in cui ogni chiave è il testo `question` e ogni valore è l'`label` dell'opzione selezionata:
471
472 | Dall'oggetto domanda | Usa come |
473 | ------------------------------------------------------------- | -------- |
474 | Campo `question` (ad es. `"How should I format the output?"`) | Chiave |
475 | Campo `label` dell'opzione selezionata (ad es. `"Summary"`) | Valore |
476
477 Per le domande a selezione multipla, passa un array di etichette o uniscile con `", "`. Se [supporti input di testo libero](#support-free-text-input), utilizza il testo personalizzato dell'utente come valore.
478
479 <CodeGroup>
480 ```python Python theme={null}
481 return PermissionResultAllow(
482 updated_input={
483 "questions": input_data.get("questions", []),
484 "answers": {
485 "How should I format the output?": "Summary",
486 "Which sections should I include?": ["Introduction", "Conclusion"],
487 },
488 }
489 )
490 ```
491
492 ```typescript TypeScript theme={null}
493 return {
494 behavior: "allow",
495 updatedInput: {
496 questions: input.questions,
497 answers: {
498 "How should I format the output?": "Summary",
499 "Which sections should I include?": "Introduction, Conclusion"
500 }
501 }
502 };
503 ```
504 </CodeGroup>
505 </Step>
506</Steps>
507
508### Formato della domanda
509
510L'input contiene le domande generate da Claude in un array `questions`. Ogni domanda ha questi campi:
511
512| Campo | Descrizione |
513| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
514| `question` | Il testo completo della domanda da visualizzare |
515| `header` | Etichetta breve per la domanda (max 12 caratteri) |
516| `options` | Array di 2-4 scelte, ognuna con `label` e `description`. TypeScript: opzionalmente `preview` (vedi [sotto](#option-previews-type-script)) |
517| `multiSelect` | Se `true`, gli utenti possono selezionare più opzioni |
518
519La struttura che il tuo callback riceve:
520
521```json theme={null}
522{
523 "questions": [
524 {
525 "question": "How should I format the output?",
526 "header": "Format",
527 "options": [
528 { "label": "Summary", "description": "Brief overview of key points" },
529 { "label": "Detailed", "description": "Full explanation with examples" }
530 ],
531 "multiSelect": false
532 }
533 ]
534}
535```
536
537#### Anteprime delle opzioni (TypeScript)
538
539`toolConfig.askUserQuestion.previewFormat` aggiunge un campo `preview` a ogni opzione in modo che la tua app possa mostrare un mockup visivo insieme all'etichetta. Senza questa impostazione, Claude non genera anteprime e il campo è assente.
540
541| `previewFormat` | `preview` contiene |
542| :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------- |
543| non impostato (predefinito) | Il campo è assente. Claude non genera anteprime. |
544| `"markdown"` | ASCII art e blocchi di codice recintati |
545| `"html"` | Un frammento `<div>` stilizzato (l'SDK rifiuta `<script>`, `<style>` e `<!DOCTYPE>` prima che il tuo callback venga eseguito) |
546
547Il formato si applica a tutte le domande nella sessione. Claude include `preview` sulle opzioni in cui un confronto visivo aiuta (scelte di layout, schemi di colori) e lo omette dove non lo farebbe (conferme sì/no, scelte solo testo). Controlla `undefined` prima di eseguire il rendering.
548
549```typescript theme={null}
550import { query } from "@anthropic-ai/claude-agent-sdk";
551
552for await (const message of query({
553 prompt: "Help me choose a card layout",
554 options: {
555 toolConfig: {
556 askUserQuestion: { previewFormat: "html" }
557 },
558 canUseTool: async (toolName, input) => {
559 // input.questions[].options[].preview è una stringa HTML o undefined
560 return { behavior: "allow", updatedInput: input };
561 }
562 }
563})) {
564 // ...
565}
566```
567
568Un'opzione con un'anteprima HTML:
569
570```json theme={null}
571{
572 "label": "Compact",
573 "description": "Title and metric value only",
574 "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>"
575}
576```
577
578### Formato della risposta
579
580Restituisci un oggetto `answers` che mappa il campo `question` di ogni domanda all'`label` dell'opzione selezionata:
581
582| Campo | Descrizione |
583| ----------- | --------------------------------------------------------------------------------------------- |
584| `questions` | Passa l'array di domande originale (obbligatorio per l'elaborazione dello strumento) |
585| `answers` | Oggetto in cui le chiavi sono il testo della domanda e i valori sono le etichette selezionate |
586
587Per le domande a selezione multipla, passa un array di etichette o uniscile con `", "`. Per l'input di testo libero, utilizza il testo personalizzato dell'utente direttamente.
588
589```json theme={null}
590{
591 "questions": [
592 // ...
593 ],
594 "answers": {
595 "How should I format the output?": "Summary",
596 "Which sections should I include?": ["Introduction", "Conclusion"]
597 }
598}
599```
600
601#### Supporta input di testo libero
602
603Le opzioni predefinite di Claude non copriranno sempre ciò che gli utenti vogliono. Per consentire agli utenti di digitare la propria risposta:
604
605* Visualizza una scelta "Other" aggiuntiva dopo le opzioni di Claude che accetta input di testo
606* Utilizza il testo personalizzato dell'utente come valore della risposta (non la parola "Other")
607
608Vedi l'[esempio completo](#complete-example) di seguito per un'implementazione completa.
609
610### Esempio completo
611
612Claude pone domande di chiarimento quando ha bisogno di input dell'utente per procedere. Ad esempio, quando gli viene chiesto di aiutare a decidere su uno stack tecnologico per un'app mobile, Claude potrebbe chiedere informazioni su cross-platform vs nativo, preferenze di backend o piattaforme di destinazione. Queste domande aiutano Claude a prendere decisioni che corrispondono alle preferenze dell'utente piuttosto che indovinare.
613
614Questo esempio gestisce quelle domande in un'applicazione terminale. Ecco cosa accade ad ogni passaggio:
615
6161. **Instrada la richiesta**: Il callback `canUseTool` controlla se il nome dello strumento è `"AskUserQuestion"` e instrada a un gestore dedicato
6172. **Visualizza le domande**: Il gestore scorre l'array `questions` e stampa ogni domanda con opzioni numerate
6183. **Raccogli input**: L'utente può inserire un numero per selezionare un'opzione, o digitare testo libero direttamente (ad es. "jquery", "i don't know")
6194. **Mappa le risposte**: Il codice controlla se l'input è numerico (utilizza l'etichetta dell'opzione) o testo libero (utilizza il testo direttamente)
6205. **Restituisci a Claude**: La risposta include sia l'array `questions` originale che la mappatura `answers`
621
622<CodeGroup>
623 ```python Python theme={null}
624 import asyncio
625
626 from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
627 from claude_agent_sdk.types import HookMatcher, PermissionResultAllow
628
629
630 def parse_response(response: str, options: list) -> str:
631 """Analizza l'input dell'utente come numero(i) di opzione o testo libero."""
632 try:
633 indices = [int(s.strip()) - 1 for s in response.split(",")]
634 labels = [options[i]["label"] for i in indices if 0 <= i < len(options)]
635 return ", ".join(labels) if labels else response
636 except ValueError:
637 return response
638
639
640 async def handle_ask_user_question(input_data: dict) -> PermissionResultAllow:
641 """Visualizza le domande di Claude e raccogli le risposte dell'utente."""
642 answers = {}
643
644 for q in input_data.get("questions", []):
645 print(f"\n{q['header']}: {q['question']}")
646
647 options = q["options"]
648 for i, opt in enumerate(options):
649 print(f" {i + 1}. {opt['label']} - {opt['description']}")
650 if q.get("multiSelect"):
651 print(" (Enter numbers separated by commas, or type your own answer)")
652 else:
653 print(" (Enter a number, or type your own answer)")
654
655 response = input("Your choice: ").strip()
656 answers[q["question"]] = parse_response(response, options)
657
658 return PermissionResultAllow(
659 updated_input={
660 "questions": input_data.get("questions", []),
661 "answers": answers,
662 }
663 )
664
665
666 async def can_use_tool(
667 tool_name: str, input_data: dict, context
668 ) -> PermissionResultAllow:
669 # Instrada AskUserQuestion al nostro gestore di domande
670 if tool_name == "AskUserQuestion":
671 return await handle_ask_user_question(input_data)
672 # Auto-approva altri strumenti per questo esempio
673 return PermissionResultAllow(updated_input=input_data)
674
675
676 async def prompt_stream():
677 yield {
678 "type": "user",
679 "message": {
680 "role": "user",
681 "content": "Help me decide on the tech stack for a new mobile app",
682 },
683 }
684
685
686 # Workaround richiesto: dummy hook mantiene il flusso aperto per can_use_tool
687 async def dummy_hook(input_data, tool_use_id, context):
688 return {"continue_": True}
689
690
691 async def main():
692 async for message in query(
693 prompt=prompt_stream(),
694 options=ClaudeAgentOptions(
695 can_use_tool=can_use_tool,
696 hooks={"PreToolUse": [HookMatcher(matcher=None, hooks=[dummy_hook])]},
697 ),
698 ):
699 if isinstance(message, ResultMessage) and message.subtype == "success":
700 print(message.result)
701
702
703 asyncio.run(main())
704 ```
705
706 ```typescript TypeScript theme={null}
707 import { query } from "@anthropic-ai/claude-agent-sdk";
708 import * as readline from "readline/promises";
709
710 // Helper per chiedere all'utente l'input nel terminale
711 async function prompt(question: string): Promise<string> {
712 const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
713 const answer = await rl.question(question);
714 rl.close();
715 return answer;
716 }
717
718 // Analizza l'input dell'utente come numero(i) di opzione o testo libero
719 function parseResponse(response: string, options: any[]): string {
720 const indices = response.split(",").map((s) => parseInt(s.trim()) - 1);
721 const labels = indices
722 .filter((i) => !isNaN(i) && i >= 0 && i < options.length)
723 .map((i) => options[i].label);
724 return labels.length > 0 ? labels.join(", ") : response;
725 }
726
727 // Visualizza le domande di Claude e raccogli le risposte dell'utente
728 async function handleAskUserQuestion(input: any) {
729 const answers: Record<string, string> = {};
730
731 for (const q of input.questions) {
732 console.log(`\n${q.header}: ${q.question}`);
733
734 const options = q.options;
735 options.forEach((opt: any, i: number) => {
736 console.log(` ${i + 1}. ${opt.label} - ${opt.description}`);
737 });
738 if (q.multiSelect) {
739 console.log(" (Enter numbers separated by commas, or type your own answer)");
740 } else {
741 console.log(" (Enter a number, or type your own answer)");
742 }
743
744 const response = (await prompt("Your choice: ")).trim();
745 answers[q.question] = parseResponse(response, options);
746 }
747
748 // Restituisci le risposte a Claude (deve includere le domande originali)
749 return {
750 behavior: "allow",
751 updatedInput: { questions: input.questions, answers }
752 };
753 }
754
755 async function main() {
756 for await (const message of query({
757 prompt: "Help me decide on the tech stack for a new mobile app",
758 options: {
759 canUseTool: async (toolName, input) => {
760 // Instrada AskUserQuestion al nostro gestore di domande
761 if (toolName === "AskUserQuestion") {
762 return handleAskUserQuestion(input);
763 }
764 // Auto-approva altri strumenti per questo esempio
765 return { behavior: "allow", updatedInput: input };
766 }
767 }
768 })) {
769 if ("result" in message) console.log(message.result);
770 }
771 }
772
773 main();
774 ```
775</CodeGroup>
776
777## Limitazioni
778
779* **Subagenti**: `AskUserQuestion` non è attualmente disponibile nei subagenti generati tramite lo strumento Agent
780* **Limiti delle domande**: ogni chiamata `AskUserQuestion` supporta 1-4 domande con 2-4 opzioni ciascuna
781
782## Altri modi per ottenere input dall'utente
783
784Il callback `canUseTool` e lo strumento `AskUserQuestion` coprono la maggior parte degli scenari di approvazione e chiarimento, ma l'SDK offre altri modi per ottenere input dagli utenti:
785
786### Input streaming
787
788Utilizza [input streaming](/it/agent-sdk/streaming-vs-single-mode) quando hai bisogno di:
789
790* **Interrompere l'agente a metà attività**: invia un segnale di annullamento o cambia direzione mentre Claude sta lavorando
791* **Fornire contesto aggiuntivo**: aggiungi informazioni di cui Claude ha bisogno senza aspettare che le chieda
792* **Costruire interfacce di chat**: consenti agli utenti di inviare messaggi di follow-up durante operazioni di lunga durata
793
794L'input streaming è ideale per interfacce conversazionali in cui gli utenti interagiscono con l'agente durante l'esecuzione, non solo nei checkpoint di approvazione.
795
796### Strumenti personalizzati
797
798Utilizza [strumenti personalizzati](/it/agent-sdk/custom-tools) quando hai bisogno di:
799
800* **Raccogliere input strutturato**: costruisci moduli, procedure guidate o flussi di lavoro multi-step che vanno oltre il formato a scelta multipla di `AskUserQuestion`
801* **Integrare sistemi di approvazione esterni**: connettiti a piattaforme di ticketing, flusso di lavoro o approvazione esistenti
802* **Implementare interazioni specifiche del dominio**: crea strumenti personalizzati per le esigenze della tua applicazione, come interfacce di revisione del codice o elenchi di controllo di distribuzione
803
804Gli strumenti personalizzati ti danno il controllo completo sull'interazione, ma richiedono più lavoro di implementazione rispetto all'utilizzo del callback `canUseTool` integrato.
805
806## Risorse correlate
807
808* [Configura autorizzazioni](/it/agent-sdk/permissions): configura modalità e regole di autorizzazione
809* [Controlla l'esecuzione con hook](/it/agent-sdk/hooks): esegui codice personalizzato nei punti chiave del ciclo di vita dell'agente
810* [Riferimento SDK TypeScript](/it/agent-sdk/typescript#canusetool): documentazione API completa di canUseTool