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# Genehmigungen und Benutzereingaben verarbeiten
6
7> Zeigen Sie Claudes Genehmigungsanfragen und Klärungsfragen den Benutzern an und geben Sie deren Entscheidungen an das SDK zurück.
8
9Während Claude an einer Aufgabe arbeitet, muss er manchmal mit Benutzern Rücksprache halten. Er könnte eine Genehmigung benötigen, bevor er Dateien löscht, oder fragen müssen, welche Datenbank für ein neues Projekt verwendet werden soll. Ihre Anwendung muss diese Anfragen den Benutzern anzeigen, damit Claude mit deren Eingabe fortfahren kann.
10
11Claude fordert Benutzereingaben in zwei Situationen an: wenn er **Genehmigung zur Verwendung eines Tools benötigt** (wie das Löschen von Dateien oder das Ausführen von Befehlen) und wenn er **Klärungsfragen hat** (über das `AskUserQuestion`-Tool). Beide lösen Ihren `canUseTool`-Callback aus, der die Ausführung pausiert, bis Sie eine Antwort zurückgeben. Dies unterscheidet sich von normalen Gesprächsrunden, bei denen Claude fertig ist und auf Ihre nächste Nachricht wartet.
12
13Bei Klärungsfragen generiert Claude die Fragen und Optionen. Ihre Aufgabe besteht darin, sie den Benutzern zu präsentieren und ihre Auswahl zurückzugeben. Sie können diesem Ablauf keine eigenen Fragen hinzufügen; wenn Sie Benutzer selbst etwas fragen müssen, tun Sie dies separat in Ihrer Anwendungslogik.
14
15Der Callback kann unbegrenzt ausstehend bleiben. Die Ausführung bleibt pausiert, bis Ihr Callback zurückkommt, und das SDK bricht das Warten nur ab, wenn die Abfrage selbst abgebrochen wird. Wenn ein Benutzer länger braucht, um zu antworten, als Ihr Prozess vernünftigerweise laufen kann, unterstützt das TypeScript SDK den [`defer`-Hook-Entscheidung](/de/hooks#defer-a-tool-call-for-later), mit dem der Prozess beendet und später aus der persistierten Sitzung fortgesetzt werden kann; diese Option ist im Python SDK nicht verfügbar.
16
17Diese Anleitung zeigt Ihnen, wie Sie jeden Anforderungstyp erkennen und angemessen reagieren.
18
19## Erkennen Sie, wenn Claude Eingaben benötigt
20
21Übergeben Sie einen `canUseTool`-Callback in Ihren Abfrageoptionen. Der Callback wird ausgelöst, wenn Claude Benutzereingaben benötigt, und erhält den Tool-Namen und die Eingabe als Argumente:
22
23<CodeGroup>
24 ```python Python theme={null}
25 async def handle_tool_request(tool_name, input_data, context):
26 # Benutzer auffordern und Zulassung oder Ablehnung zurückgeben
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 // Benutzer auffordern und Zulassung oder Ablehnung zurückgeben
37 }
38
39 const options = { canUseTool: handleToolRequest };
40 ```
41</CodeGroup>
42
43Der Callback wird in zwei Fällen ausgelöst:
44
451. **Tool benötigt Genehmigung**: Claude möchte ein Tool verwenden, das nicht durch [Berechtigungsregeln](/de/agent-sdk/permissions) oder Modi automatisch genehmigt wird. Überprüfen Sie `tool_name` auf das Tool (z. B. `"Bash"`, `"Write"`).
462. **Claude stellt eine Frage**: Claude ruft das `AskUserQuestion`-Tool auf. Überprüfen Sie, ob `tool_name == "AskUserQuestion"`, um es anders zu behandeln. Wenn Sie ein `tools`-Array angeben, fügen Sie `AskUserQuestion` ein, damit dies funktioniert. Siehe [Klärungsfragen verarbeiten](#handle-clarifying-questions) für Details.
47
48<Note>
49 Um Tools automatisch zuzulassen oder abzulehnen, ohne Benutzer zu fragen, verwenden Sie stattdessen [Hooks](/de/agent-sdk/hooks). Hooks werden vor `canUseTool` ausgeführt und können Anfragen basierend auf Ihrer eigenen Logik zulassen, ablehnen oder ändern. Sie können auch den [`PermissionRequest`-Hook](/de/agent-sdk/hooks#available-hooks) verwenden, um externe Benachrichtigungen (Slack, E-Mail, Push) zu senden, wenn Claude auf Genehmigung wartet.
50</Note>
51
52## Tool-Genehmigungsanfragen verarbeiten
53
54Nachdem Sie einen `canUseTool`-Callback in Ihren Abfrageoptionen übergeben haben, wird er ausgelöst, wenn Claude ein Tool verwenden möchte, das nicht automatisch genehmigt ist. Ihr Callback erhält drei Argumente:
55
56| Argument | Beschreibung |
57| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
58| `toolName` | Der Name des Tools, das Claude verwenden möchte (z. B. `"Bash"`, `"Write"`, `"Edit"`) |
59| `input` | Die Parameter, die Claude an das Tool übergibt. Der Inhalt variiert je nach Tool. |
60| `options` (TS) / `context` (Python) | Zusätzlicher Kontext, einschließlich optionaler `suggestions` (vorgeschlagene `PermissionUpdate`-Einträge, um erneute Aufforderungen zu vermeiden) und eines Abbruchsignals. In TypeScript ist `signal` ein `AbortSignal`; in Python ist das Signalfeld für zukünftige Verwendung reserviert. Siehe [`ToolPermissionContext`](/de/agent-sdk/python#toolpermissioncontext) für Python. |
61
62Das `input`-Objekt enthält Tool-spezifische Parameter. Häufige Beispiele:
63
64| Tool | Eingabefelder |
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
71Siehe die SDK-Referenz für vollständige Eingabeschemas: [Python](/de/agent-sdk/python#tool-input%2Foutput-types) | [TypeScript](/de/agent-sdk/typescript#tool-input-types).
72
73Sie können diese Informationen dem Benutzer anzeigen, damit er entscheiden kann, ob er die Aktion zulässt oder ablehnt, und dann die entsprechende Antwort zurückgeben.
74
75Das folgende Beispiel fordert Claude auf, eine Testdatei zu erstellen und zu löschen. Wenn Claude jeden Vorgang versucht, druckt der Callback die Tool-Anfrage auf dem Terminal aus und fordert zur y/n-Genehmigung auf.
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 # Tool-Anfrage anzeigen
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 # Benutzergenehmigung abrufen
103 response = input("Allow this action? (y/n): ")
104
105 # Zulassung oder Ablehnung basierend auf der Antwort des Benutzers zurückgeben
106 if response.lower() == "y":
107 # Zulassen: Tool wird mit der ursprünglichen (oder geänderten) Eingabe ausgeführt
108 return PermissionResultAllow(updated_input=input_data)
109 else:
110 # Ablehnen: Tool wird nicht ausgeführt, Claude sieht die Nachricht
111 return PermissionResultDeny(message="User denied this action")
112
113
114 # Erforderliche Umgehung: Dummy-Hook hält den Stream für can_use_tool offen
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 // Hilfsfunktion zum Auffordern von Benutzereingaben im Terminal
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 // Tool-Anfrage anzeigen
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 // Benutzergenehmigung abrufen
176 const response = await prompt("Allow this action? (y/n): ");
177
178 // Zulassung oder Ablehnung basierend auf der Antwort des Benutzers zurückgeben
179 if (response.toLowerCase() === "y") {
180 // Zulassen: Tool wird mit der ursprünglichen (oder geänderten) Eingabe ausgeführt
181 return { behavior: "allow", updatedInput: input };
182 } else {
183 // Ablehnen: Tool wird nicht ausgeführt, Claude sieht die Nachricht
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 erfordert `can_use_tool` den [Streaming-Modus](/de/agent-sdk/streaming-vs-single-mode) und einen `PreToolUse`-Hook, der `{"continue_": True}` zurückgibt, um den Stream offen zu halten. Ohne diesen Hook wird der Stream geschlossen, bevor der Berechtigungscallback aufgerufen werden kann.
196</Note>
197
198Dieses Beispiel verwendet einen `y/n`-Ablauf, bei dem jede Eingabe außer `y` als Ablehnung behandelt wird. In der Praxis könnten Sie eine umfangreichere Benutzeroberfläche erstellen, die es Benutzern ermöglicht, die Anfrage zu ändern, Feedback zu geben oder Claude vollständig umzuleiten. Siehe [Auf Tool-Anfragen reagieren](#respond-to-tool-requests) für alle Möglichkeiten, wie Sie reagieren können.
199
200### Auf Tool-Anfragen reagieren
201
202Ihr Callback gibt einen von zwei Antworttypen zurück:
203
204| Antwort | Python | TypeScript |
205| ------------ | ------------------------------------------ | ------------------------------------- |
206| **Zulassen** | `PermissionResultAllow(updated_input=...)` | `{ behavior: "allow", updatedInput }` |
207| **Ablehnen** | `PermissionResultDeny(message=...)` | `{ behavior: "deny", message }` |
208
209Beim Zulassen übergeben Sie die Tool-Eingabe (original oder geändert). Beim Ablehnen geben Sie eine Nachricht an, die erklärt, warum. Claude sieht diese Nachricht und kann seinen Ansatz anpassen.
210
211<CodeGroup>
212 ```python Python theme={null}
213 from claude_agent_sdk.types import PermissionResultAllow, PermissionResultDeny
214
215 # Tool-Ausführung zulassen
216 return PermissionResultAllow(updated_input=input_data)
217
218 # Tool blockieren
219 return PermissionResultDeny(message="User rejected this action")
220 ```
221
222 ```typescript TypeScript theme={null}
223 // Tool-Ausführung zulassen
224 return { behavior: "allow", updatedInput: input };
225
226 // Tool blockieren
227 return { behavior: "deny", message: "User rejected this action" };
228 ```
229</CodeGroup>
230
231Über das Zulassen oder Ablehnen hinaus können Sie die Eingabe des Tools ändern oder Kontext bereitstellen, der Claude hilft, seinen Ansatz anzupassen:
232
233* **Genehmigen**: Lassen Sie das Tool genau wie von Claude angefordert ausführen
234* **Mit Änderungen genehmigen**: Ändern Sie die Eingabe vor der Ausführung (z. B. Pfade bereinigen, Einschränkungen hinzufügen)
235* **Ablehnen**: Blockieren Sie das Tool und teilen Sie Claude mit, warum
236* **Alternative vorschlagen**: Blockieren Sie, aber leiten Sie Claude zu dem hin, was der Benutzer stattdessen möchte
237* **Vollständig umleiten**: Verwenden Sie [Streaming-Eingabe](/de/agent-sdk/streaming-vs-single-mode), um Claude eine völlig neue Anweisung zu senden
238
239<Tabs>
240 <Tab title="Genehmigen">
241 Der Benutzer genehmigt die Aktion unverändert. Geben Sie die `input` aus Ihrem Callback unverändert durch und das Tool wird genau wie von Claude angefordert ausgeführt.
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="Mit Änderungen genehmigen">
269 Der Benutzer genehmigt, möchte aber die Anfrage zuerst ändern. Sie können die Eingabe vor der Tool-Ausführung ändern. Claude sieht das Ergebnis, wird aber nicht darüber informiert, dass Sie etwas geändert haben. Nützlich zum Bereinigen von Parametern, zum Hinzufügen von Einschränkungen oder zum Einschränken des Zugriffs.
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 # Benutzer genehmigt, aber alle Befehle auf Sandbox beschränken
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 // Benutzer genehmigt, aber alle Befehle auf Sandbox beschränken
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="Ablehnen">
301 Der Benutzer möchte nicht, dass diese Aktion stattfindet. Blockieren Sie das Tool und geben Sie eine Nachricht an, die erklärt, warum. Claude sieht diese Nachricht und kann einen anderen Ansatz versuchen.
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="Alternative vorschlagen">
330 Der Benutzer möchte diese spezifische Aktion nicht, hat aber eine andere Idee. Blockieren Sie das Tool und fügen Sie Anleitung in Ihre Nachricht ein. Claude wird dies lesen und basierend auf Ihrem Feedback entscheiden, wie er vorgehen soll.
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 # Benutzer möchte nicht löschen, schlagen Sie stattdessen Archivierung vor
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 // Benutzer möchte nicht löschen, schlagen Sie stattdessen Archivierung vor
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="Vollständig umleiten">
360 Für eine vollständige Richtungsänderung (nicht nur einen Anstoß) verwenden Sie [Streaming-Eingabe](/de/agent-sdk/streaming-vs-single-mode), um Claude eine neue Anweisung direkt zu senden. Dies umgeht die aktuelle Tool-Anfrage und gibt Claude völlig neue Anweisungen zum Befolgen.
361 </Tab>
362</Tabs>
363
364## Klärungsfragen verarbeiten
365
366Wenn Claude mehr Anleitung zu einer Aufgabe mit mehreren gültigen Ansätzen benötigt, ruft es das `AskUserQuestion`-Tool auf. Dies löst Ihren `canUseTool`-Callback mit `toolName` auf `AskUserQuestion` aus. Die Eingabe enthält Claudes Fragen als Multiple-Choice-Optionen, die Sie dem Benutzer anzeigen und deren Auswahl zurückgeben.
367
368<Tip>
369 Klärungsfragen sind besonders häufig im [`plan`-Modus](/de/agent-sdk/permissions#plan-mode-plan), in dem Claude die Codebasis erkundet und Fragen stellt, bevor er einen Plan vorschlägt. Dies macht den Plan-Modus ideal für interaktive Workflows, bei denen Claude Anforderungen sammeln soll, bevor Änderungen vorgenommen werden.
370</Tip>
371
372Die folgenden Schritte zeigen, wie Sie Klärungsfragen verarbeiten:
373
374<Steps>
375 <Step title="Übergeben Sie einen canUseTool-Callback">
376 Übergeben Sie einen `canUseTool`-Callback in Ihren Abfrageoptionen. Standardmäßig ist `AskUserQuestion` verfügbar. Wenn Sie ein `tools`-Array angeben, um Claudes Funktionen einzuschränken (z. B. einen schreibgeschützten Agent mit nur `Read`, `Glob` und `Grep`), fügen Sie `AskUserQuestion` in dieses Array ein. Andernfalls kann Claude keine Klärungsfragen stellen:
377
378 <CodeGroup>
379 ```python Python theme={null}
380 async for message in query(
381 prompt="Analyze this codebase",
382 options=ClaudeAgentOptions(
383 # Fügen Sie AskUserQuestion in Ihre Tools-Liste ein
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 // Fügen Sie AskUserQuestion in Ihre Tools-Liste ein
396 tools: ["Read", "Glob", "Grep", "AskUserQuestion"],
397 canUseTool: async (toolName, input) => {
398 // Klärungsfragen hier verarbeiten
399 }
400 }
401 })) {
402 console.log(message);
403 }
404 ```
405 </CodeGroup>
406 </Step>
407
408 <Step title="Erkennen Sie AskUserQuestion">
409 Überprüfen Sie in Ihrem Callback, ob `toolName` gleich `AskUserQuestion` ist, um es anders als andere Tools zu behandeln:
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 # Ihre Implementierung zum Sammeln von Antworten vom Benutzer
416 return await handle_clarifying_questions(input_data)
417 # Andere Tools normal verarbeiten
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 // Ihre Implementierung zum Sammeln von Antworten vom Benutzer
425 return handleClarifyingQuestions(input);
426 }
427 // Andere Tools normal verarbeiten
428 return promptForApproval(toolName, input);
429 };
430 ```
431 </CodeGroup>
432 </Step>
433
434 <Step title="Analysieren Sie die Frageneingabe">
435 Die Eingabe enthält Claudes Fragen in einem `questions`-Array. Jede Frage hat eine `question` (der anzuzeigende Text), `options` (die Auswahlmöglichkeiten) und `multiSelect` (ob mehrere Auswahlen zulässig sind):
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 Siehe [Frageformat](#question-format) für vollständige Feldbeschreibungen.
463 </Step>
464
465 <Step title="Sammeln Sie Antworten vom Benutzer">
466 Präsentieren Sie die Fragen dem Benutzer und sammeln Sie deren Auswahl. Wie Sie dies tun, hängt von Ihrer Anwendung ab: ein Terminal-Prompt, ein Web-Formular, ein mobiler Dialog usw.
467 </Step>
468
469 <Step title="Geben Sie Antworten an Claude zurück">
470 Erstellen Sie das `answers`-Objekt als Datensatz, wobei jeder Schlüssel der `question`-Text ist und jeder Wert das `label` der ausgewählten Option ist:
471
472 | Aus dem Frageobjekt | Verwenden Sie als |
473 | ----------------------------------------------------------- | ----------------- |
474 | `question`-Feld (z. B. `"How should I format the output?"`) | Schlüssel |
475 | `label`-Feld der ausgewählten Option (z. B. `"Summary"`) | Wert |
476
477 Für Multi-Select-Fragen übergeben Sie ein Array von Labels oder verbinden Sie sie mit `", "`. Wenn Sie [freie Texteingabe unterstützen](#support-free-text-input), verwenden Sie den benutzerdefinierten Text des Benutzers als Wert.
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### Frageformat
509
510Die Eingabe enthält Claudes generierte Fragen in einem `questions`-Array. Jede Frage hat diese Felder:
511
512| Feld | Beschreibung |
513| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
514| `question` | Der vollständige Fragetext zum Anzeigen |
515| `header` | Kurzes Label für die Frage (max. 12 Zeichen) |
516| `options` | Array von 2-4 Auswahlmöglichkeiten, jeweils mit `label` und `description`. TypeScript: optional `preview` (siehe [unten](#option-previews-type-script)) |
517| `multiSelect` | Wenn `true`, können Benutzer mehrere Optionen auswählen |
518
519Die Struktur, die Ihr Callback erhält:
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#### Optionsvorschau (TypeScript)
538
539`toolConfig.askUserQuestion.previewFormat` fügt jedem Option ein `preview`-Feld hinzu, damit Ihre App ein visuelles Mockup neben dem Label anzeigen kann. Ohne diese Einstellung generiert Claude keine Vorschau und das Feld ist nicht vorhanden.
540
541| `previewFormat` | `preview` enthält |
542| :----------------------- | :--------------------------------------------------------------------------------------------------------------------------- |
543| nicht gesetzt (Standard) | Feld ist nicht vorhanden. Claude generiert keine Vorschau. |
544| `"markdown"` | ASCII-Art und eingezäunte Code-Blöcke |
545| `"html"` | Ein gestyltes `<div>`-Fragment (das SDK lehnt `<script>`, `<style>` und `<!DOCTYPE>` ab, bevor Ihr Callback ausgeführt wird) |
546
547Das Format gilt für alle Fragen in der Sitzung. Claude fügt `preview` bei Optionen ein, bei denen ein visueller Vergleich hilfreich ist (Layout-Auswahlmöglichkeiten, Farbschemas) und lässt es weg, wo nicht (Ja/Nein-Bestätigungen, nur Text-Auswahlmöglichkeiten). Überprüfen Sie auf `undefined`, bevor Sie rendern.
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 ist ein HTML-String oder undefined
560 return { behavior: "allow", updatedInput: input };
561 }
562 }
563})) {
564 // ...
565}
566```
567
568Eine Option mit HTML-Vorschau:
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### Antwortformat
579
580Geben Sie ein `answers`-Objekt zurück, das jedes `question`-Feld der Frage dem `label` der ausgewählten Option zuordnet:
581
582| Feld | Beschreibung |
583| ----------- | ------------------------------------------------------------------------------------------ |
584| `questions` | Geben Sie das ursprüngliche Questions-Array durch (erforderlich für die Tool-Verarbeitung) |
585| `answers` | Objekt, bei dem Schlüssel Fragetext und Werte ausgewählte Labels sind |
586
587Für Multi-Select-Fragen übergeben Sie ein Array von Labels oder verbinden Sie sie mit `", "`. Für freie Texteingabe verwenden Sie den benutzerdefinierten Text des Benutzers direkt.
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#### Unterstützen Sie freie Texteingabe
602
603Claudes vordefinierte Optionen decken nicht immer ab, was Benutzer möchten. Um Benutzern zu ermöglichen, ihre eigene Antwort einzugeben:
604
605* Zeigen Sie nach Claudes Optionen eine zusätzliche "Other"-Auswahlmöglichkeit an, die Texteingabe akzeptiert
606* Verwenden Sie den benutzerdefinierten Text des Benutzers als Antwortwert (nicht das Wort "Other")
607
608Siehe das [vollständige Beispiel](#complete-example) unten für eine vollständige Implementierung.
609
610### Vollständiges Beispiel
611
612Claude stellt Klärungsfragen, wenn er Benutzereingaben benötigt, um fortzufahren. Wenn Claude beispielsweise aufgefordert wird, bei der Entscheidung über einen Tech-Stack für eine mobile App zu helfen, könnte Claude Fragen zu Cross-Platform vs. Native, Backend-Vorlieben oder Zielplattformen stellen. Diese Fragen helfen Claude, Entscheidungen zu treffen, die den Vorlieben des Benutzers entsprechen, anstatt zu raten.
613
614Dieses Beispiel verarbeitet diese Fragen in einer Terminal-Anwendung. Hier ist, was bei jedem Schritt passiert:
615
6161. **Leiten Sie die Anfrage weiter**: Der `canUseTool`-Callback überprüft, ob der Tool-Name `"AskUserQuestion"` ist, und leitet zu einem dedizierten Handler weiter
6172. **Zeigen Sie Fragen an**: Der Handler durchläuft das `questions`-Array und druckt jede Frage mit nummerierten Optionen
6183. **Sammeln Sie Eingaben**: Der Benutzer kann eine Nummer eingeben, um eine Option auszuwählen, oder direkt freien Text eingeben (z. B. "jquery", "i don't know")
6194. **Ordnen Sie Antworten zu**: Der Code überprüft, ob die Eingabe numerisch ist (verwendet das Label der Option) oder freier Text (verwendet den Text direkt)
6205. **Geben Sie an Claude zurück**: Die Antwort enthält sowohl das ursprüngliche `questions`-Array als auch die `answers`-Zuordnung
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 """Analysieren Sie Benutzereingaben als Optionsnummer(n) oder freien Text."""
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 """Zeigen Sie Claudes Fragen an und sammeln Sie Benutzerantworten."""
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 # Leiten Sie AskUserQuestion zu unserem Frage-Handler weiter
670 if tool_name == "AskUserQuestion":
671 return await handle_ask_user_question(input_data)
672 # Auto-Genehmigung anderer Tools für dieses Beispiel
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 # Erforderliche Umgehung: Dummy-Hook hält den Stream für can_use_tool offen
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 // Hilfsfunktion zum Auffordern von Benutzereingaben im Terminal
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 // Analysieren Sie Benutzereingaben als Optionsnummer(n) oder freien Text
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 // Zeigen Sie Claudes Fragen an und sammeln Sie Benutzerantworten
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 // Geben Sie die Antworten an Claude zurück (muss ursprüngliche Fragen enthalten)
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 // Leiten Sie AskUserQuestion zu unserem Frage-Handler weiter
761 if (toolName === "AskUserQuestion") {
762 return handleAskUserQuestion(input);
763 }
764 // Auto-Genehmigung anderer Tools für dieses Beispiel
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## Einschränkungen
778
779* **Subagenten**: `AskUserQuestion` ist derzeit nicht in Subagenten verfügbar, die über das Agent-Tool erzeugt werden
780* **Fragenlimits**: Jeder `AskUserQuestion`-Aufruf unterstützt 1-4 Fragen mit jeweils 2-4 Optionen
781
782## Andere Möglichkeiten, Benutzereingaben zu erhalten
783
784Der `canUseTool`-Callback und das `AskUserQuestion`-Tool decken die meisten Genehmigungs- und Klärungsszenarien ab, aber das SDK bietet andere Möglichkeiten, Eingaben von Benutzern zu erhalten:
785
786### Streaming-Eingabe
787
788Verwenden Sie [Streaming-Eingabe](/de/agent-sdk/streaming-vs-single-mode), wenn Sie:
789
790* **Den Agent mitten in der Aufgabe unterbrechen**: Senden Sie ein Abbruchsignal oder ändern Sie die Richtung, während Claude arbeitet
791* **Zusätzlichen Kontext bereitstellen**: Fügen Sie Informationen hinzu, die Claude benötigt, ohne darauf zu warten, dass es fragt
792* **Chat-Schnittstellen erstellen**: Lassen Sie Benutzer Folgenachrichten während langwieriger Operationen senden
793
794Streaming-Eingabe ist ideal für Konversations-UIs, bei denen Benutzer während der Ausführung mit dem Agent interagieren, nicht nur bei Genehmigungsprüfpunkten.
795
796### Benutzerdefinierte Tools
797
798Verwenden Sie [benutzerdefinierte Tools](/de/agent-sdk/custom-tools), wenn Sie:
799
800* **Strukturierte Eingaben sammeln**: Erstellen Sie Formulare, Assistenten oder mehrstufige Workflows, die über das Multiple-Choice-Format von `AskUserQuestion` hinausgehen
801* **Externe Genehmigungssysteme integrieren**: Verbinden Sie sich mit bestehenden Ticketing-, Workflow- oder Genehmigungsplattformen
802* **Domänenspezifische Interaktionen implementieren**: Erstellen Sie Tools, die auf die Anforderungen Ihrer Anwendung zugeschnitten sind, wie Code-Review-Schnittstellen oder Bereitstellungs-Checklisten
803
804Benutzerdefinierte Tools geben Ihnen vollständige Kontrolle über die Interaktion, erfordern aber mehr Implementierungsarbeit als die Verwendung des integrierten `canUseTool`-Callbacks.
805
806## Verwandte Ressourcen
807
808* [Berechtigungen konfigurieren](/de/agent-sdk/permissions): Richten Sie Berechtigungsmodi und -regeln ein
809* [Ausführung mit Hooks steuern](/de/agent-sdk/hooks): Führen Sie benutzerdefinierten Code an Schlüsselpunkten im Agent-Lebenszyklus aus
810* [TypeScript SDK-Referenz](/de/agent-sdk/typescript#canusetool): Vollständige canUseTool-API-Dokumentation