How the agent loop works
기준일: 2026-07-26
공식 기준: How the agent loop works
How the agent loop works 문서는 Claude Code 공식 문서(agent-sdk/agent-loop)를 한국어로 정리한 가이드입니다. Understand the message lifecycle, tool execution, context window, and architecture that power your SDK agents. 명령·설정 키·코드 예시는 공식 문서를 그대로 보존하며, 해석과 절차 안내는 한국어로 제공합니다. 최종 동작은 설치 버전과 공식 원문을 확인하세요.
핵심 요약
Understand the message lifecycle, tool execution, context window, and architecture that power your SDK agents.
한국어 가이드 범위: agent-sdk/agent-loop 경로의 설정·명령·제약·예시를 학습용으로 재구성합니다.
문서 구성
공식 문서의 주요 섹션은 다음과 같습니다.
- How the agent loop works
- The loop at a glance
- Turns and messages
- Message types
- Handle messages
- Tool execution
- Built-in tools
- Tool permissions
- Parallel tool execution
- Control how the loop runs
- Turns and budget
- Effort level
- Permission mode
- Model
- The context window
- What consumes context
- Automatic compaction
- Keep context efficient
- Sessions and continuity
- Handle the result
- Hooks
- Put it all together
- 다음 단계
상세 내용
How the agent loop works
Understand the message lifecycle, tool execution, context window, and architecture that power your SDK agents.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
The loop at a glance
Every agent session follows the same cycle:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
Turns and messages
A turn is one round trip inside the loop: Claude produces output that includes tool calls, the SDK executes those tools, and the results feed back to Claude automatically. This happens without yielding control back to your code. Turns continue until Claude produces output with no tool calls, at which point the loop ends and the final result is delivered.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
Message types
As the loop runs, the SDK yields a stream of messages. Each message carries a type that tells you what stage of the loop it came from. The five core types are:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
SystemMessage: session lifecycle events. Thesubtypefield distinguishes them:"init": session metadata for the run. When aSessionStartorSetuphook runs during session startup, its hook lifecycle messages arrive before theinitmessage"compact_boundary": fires after compaction"informational": plain-text status banners from the loop"worker_shutting_down": the loop will end after the current turn because the host is exiting or Remote Control disconnectedAssistantMessage: emitted after each Claude response, including the final text-only one. Contains text content blocks and tool call blocks from that turn.UserMessage: emitted after each tool execution with the tool result content sent back to Claude. Also emitted for any user inputs you stream mid-loop.StreamEvent: only emitted when partial messages are enabled. Contains raw API streaming events (text deltas, tool input chunks). See Stream responses.ResultMessage: marks the end of the agent loop. Contains the final text result, token usage, cost, and session ID. Check thesubtypefield to determine whether the task succeeded or hit a limit. A small number of trailing system events, such asprompt_suggestion, can arrive after it, so iterate the stream to completion rather than breaking on the result. See Handle the result.
Handle messages
Which messages you handle depends on what you're building:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
- Final results only: handle
ResultMessageto get the output, cost, and whether the task succeeded or hit a limit. - Progress updates: handle
AssistantMessageto see what Claude is doing each turn, including which tools it called. - Live streaming: enable partial messages (
include_partial_messagesin Python,includePartialMessagesin TypeScript) to getStreamEventmessages in real time. See Stream responses in real-time. - Python: check message types with
isinstance()against classes imported fromclaude_agent_sdk(예를 들어,isinstance(message, ResultMessage)). - TypeScript: check the
typestring field (예를 들어,message.type === "result").AssistantMessageandUserMessagewrap the raw API message in a.messagefield, so content blocks are atmessage.message.content, notmessage.content.
Tool execution
Tools give your agent the ability to take action. Without tools, Claude can only respond with text. With tools, Claude can read files, run commands, search code, and interact with external services.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
Built-in tools
The SDK includes the same tools that power Claude Code:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
- Connect external services with MCP servers (databases, browsers, APIs)
- Define custom tools with custom tool handlers
- Load project skills via setting sources for reusable workflows
| Category | Tools | What they do |
|---|---|---|
| File operations | Read, Edit, Write |
Read, modify, and create files |
| Search | Glob, Grep |
Find files by pattern, search content with regex |
| Execution | Bash |
Run shell commands, scripts, git operations |
| Web | WebSearch, WebFetch |
Search the web, fetch and parse pages |
| Discovery | ToolSearch |
Dynamically find and load tools on-demand instead of preloading all of them |
| Orchestration | Agent, Skill, AskUserQuestion, TaskCreate, TaskUpdate |
Spawn subagents, invoke skills, ask the user, track tasks |
Tool permissions
Claude determines which tools to call based on the task, but you control whether those calls are allowed to execute. You can auto-approve specific tools, block others entirely, or require approval for everything. Three options work together to determine what runs:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
allowed_tools/allowedToolsauto-approves listed tools. A read-only agent with["Read", "Glob", "Grep"]in its allowed tools list runs those tools without prompting. Tools not listed are still available but require permission.disallowed_tools/disallowedToolsblocks listed tools, regardless of other settings. See Permissions for the order that rules are checked before a tool runs.permission_mode/permissionModecontrols what happens to tools that aren't covered by allow or deny rules. See Permission mode for available modes.
Parallel tool execution
When Claude requests multiple tool calls in a single turn, both SDKs can run them concurrently or sequentially depending on the tool. Read-only tools (like Read, Glob, Grep, and MCP tools marked as read-only) can run concurrently. Tools that modify state (like Edit, Write, and Bash) run sequentially to avoid conflicts.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
Control how the loop runs
You can limit how many turns the loop takes, how much it costs, how deeply Claude reasons, and whether tools require approval before running. All of these are fields on ClaudeAgentOptions (Python) / Options (TypeScript).
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
Turns and budget
When either limit is hit, the SDK returns a ResultMessage with a corresponding error subtype (error_max_turns or error_max_budget_usd). See Handle the result for how to check these subtypes and ClaudeAgentOptions / Options for syntax.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
| Option | What it controls | Default |
|---|---|---|
Max turns (max_turns / maxTurns) |
Maximum tool-use round trips | No limit |
Max budget (max_budget_usd / maxBudgetUsd) |
Maximum cost before stopping | No limit |
Effort level
The effort option controls how much reasoning Claude applies. Lower effort levels use fewer tokens per turn and reduce cost. Not all models support the effort parameter. See Effort for which models support it.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
| Level | Behavior | Good for |
|---|---|---|
"low" |
Minimal reasoning, fast responses | File lookups, listing directories |
"medium" |
Balanced reasoning | Routine edits, standard tasks |
"high" |
Thorough analysis | Refactors, debugging |
"xhigh" |
Extended reasoning depth | Coding and agentic tasks; recommended on Fable 5, Opus 4.7+, and Sonnet 5 |
"max" |
Maximum reasoning depth | Multi-step problems requiring deep analysis |
Permission mode
The permission mode option (permission_mode in Python, permissionMode in TypeScript) controls whether the agent asks for approval before using tools:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
| Mode | Behavior |
|---|---|
"default" |
Tools not covered by allow rules trigger your approval callback; no callback means deny |
"acceptEdits" |
Auto-approves file edits and common filesystem commands (mkdir, touch, mv, cp, etc.); other Bash commands follow default rules |
"plan" |
Claude explores and plans without editing your source files; file edits are never auto-approved and prompt through your canUseTool callback |
"dontAsk" |
Never prompts. Tools pre-approved by permission rules run; everything else is denied. AskUserQuestion, connector tools your organization set to ask, and MCP tools marked requiresUserInteraction are denied even if you've allowed them |
"auto" |
Uses a model classifier to approve or deny permission prompts. See Auto mode for availability and behavior |
"bypassPermissions" |
Runs all allowed tools without asking, except tools matched by an explicit ask rule, connector tools your organization set to ask, and tools that require user interaction; see How permissions are evaluated for the precedence order. Cannot be used when running as root on Unix. Use only in isolated environments where the agent's actions cannot affect systems you care about |
Model
If you don't set model, the SDK uses Claude Code's default, which depends on your authentication method and subscription. Set it explicitly (예를 들어, model="claude-sonnet-5") to pin a specific model or to use a smaller model for faster, cheaper agents. See models for available IDs.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
The context window
The context window is the total amount of information available to Claude during a session. It does not reset between turns within a session. Everything accumulates: the system prompt, tool definitions, conversation history, tool inputs, and tool outputs. Content that stays the same across turns (system prompt, tool definitions, CLAUDE.md) is automatically prompt cached, which reduces cost and latency for repeated prefixes. For how a custom system prompt or append text affects cache reuse across sessions, see Modifying system prompts.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
What consumes context
Here's how each component affects context in the SDK:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
| Source | When it loads | Impact |
|---|---|---|
| System prompt | Every request | Small fixed cost, always present |
| CLAUDE.md files | Session start, via settingSources |
Full content in every request (but prompt-cached, so only the first request pays full cost) |
| Tool definitions | Every request; MCP schemas deferred by default | Built-in tool schemas load every request. Tool search defers MCP tool schemas by default, falling back to upfront loading on Google Cloud's Agent Platform or a non-first-party ANTHROPIC_BASE_URL. See Configure tool search for the full matrix |
| Conversation history | Accumulates over turns | Grows with each turn: prompts, responses, tool inputs, tool outputs |
| Skill descriptions | Session start, via setting sources | Short summaries; full content loads only when invoked |
Automatic compaction
When the context window approaches its limit, the SDK automatically compacts the conversation: it summarizes older history to free space, keeping your most recent exchanges and key decisions intact. The SDK emits a message with type: "system" and subtype: "compact_boundary" in the stream when this happens (in Python this is a SystemMessage; in TypeScript it is a separate SDKCompactBoundaryMessage type).
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
- Summarization instructions in CLAUDE.md: The compactor reads your CLAUDE.md like any other context, so you can include a section telling it what to preserve when summarizing. The section header is free-form (not a magic string); the compactor matches on intent.
PreCompacthook: Run custom logic before compaction occurs, for example to archive the full transcript. The hook receives atriggerfield (manualorauto). See hooks.- Manual compaction: Send
/compactas a prompt string to trigger compaction on demand. Commands sent this way are SDK inputs, not CLI-only shortcuts. See commands in the SDK. - The current task objective and acceptance criteria
- File paths that have been read or modified
- Test results and error messages
- Decisions made and the reasoning behind them
Keep context efficient
A few strategies for long-running agents:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
- Use subagents for subtasks. Each subagent starts with a fresh conversation (no prior message history, though it does load its own system prompt and project-level context like CLAUDE.md). It does not see the parent's turns, and only its final response returns to the parent as a tool result. The main agent's context grows by that summary, not by the full subtask transcript. See What subagents inherit for details.
- Be selective with tools. Every tool definition takes context space. Use the
toolsfield onAgentDefinitionto scope subagents to the minimum set they need. - Watch MCP server costs. MCP tool search defers MCP tool schemas by default and loads them on demand. When tool search is off, on Google Cloud's Agent Platform, or behind a non-first-party
ANTHROPIC_BASE_URL, each MCP server adds all its tool schemas to every request, so a few servers with many tools can consume significant context before the agent does any work. - Use lower effort for routine tasks. Set effort to
"low"for agents that only need to read files or list directories. This reduces token usage and cost.
Sessions and continuity
Each interaction with the SDK creates or continues a session. Capture the session ID from ResultMessage.session_id (available in both SDKs) to resume later. The TypeScript SDK also exposes it as a direct field on the init SystemMessage; in Python it's nested in SystemMessage.data.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
Handle the result
When the loop ends, the ResultMessage tells you what happened and gives you the output. The subtype field (available in both SDKs) is the primary way to check termination state.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
- A single-shot
query()call yields the final result message, then raises an error that includes the failure text, such asReached maximum number of turns. The raise is intentional — wrap the loop in a try block if your code needs to continue past it. The underlying Claude Code process also exits with a nonzero code. - A streaming input session stays alive, and you can keep sending messages.
| Result subtype | What happened | result field available? |
|---|---|---|
success |
Claude finished the task normally | Yes |
error_max_turns |
Hit the maxTurns limit before finishing |
No |
error_max_budget_usd |
Hit the maxBudgetUsd limit before finishing |
No |
error_during_execution |
An error interrupted the loop (for example, an API failure or cancelled request) | No |
error_max_structured_output_retries |
No valid structured output was produced within the configured retry limit: every attempt failed validation, or a model fallback retracted the completed output with no successful retry | No |
Hooks
Hooks are callbacks that fire at specific points in the loop: before a tool runs, after it returns, when the agent finishes, and so on. Some commonly used hooks are:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
| Hook | When it fires | Common uses |
|---|---|---|
PreToolUse |
Before a tool executes | Validate inputs, block dangerous commands |
PostToolUse |
After a tool returns | Audit outputs, trigger side effects |
UserPromptSubmit |
When a prompt is sent | Inject additional context into prompts |
Stop |
When the agent finishes | Validate the result, save session state |
SubagentStart / SubagentStop |
When a subagent spawns or completes | Track and aggregate parallel task results |
PreCompact |
Before context compaction | Archive full transcript before summarizing |
Put it all together
This example combines the key concepts from this page into a single agent that fixes failing tests. It configures the agent with allowed tools (auto-approved so the agent runs autonomously), project settings, and safety limits on turns and reasoning effort. As the loop runs, it captures the session ID for potential resumption, handles the final result, and prints the total cost.
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
다음 단계
Now that you understand the loop, here's where to go depending on what you're building:
위 내용은 공식 문서의 해당 섹션 요지입니다. 세부 플래그·기본값은 원문을 확인하세요.
주요 항목:
- Haven't run an agent yet? Start with the quickstart to get the SDK installed and see a full example running end to end.
- Ready to hook into your project? Load CLAUDE.md, skills, and filesystem hooks so the agent follows your project conventions automatically.
- Building an interactive UI? Enable streaming to show live text and tool calls as the loop runs.
- Need tighter control over what the agent can do? Lock down tool access with permissions, and use hooks to audit, block, or transform tool calls before they execute.
- Running long or expensive tasks? Offload isolated work to subagents to keep your main context lean.
- Deploying as a service? See Hosting the Agent SDK for container and serverless guidance, and Session storage to persist sessions to your own backend.
실습 체크리스트
- 공식 문서와 로컬/SDK 버전을 대조합니다.
- 관련 CLI·SDK 옵션은 공식 페이지와
--help로 교차 확인합니다. - Agent SDK 예시는 TypeScript/Python 패키지 최신 API를 우선합니다.
- 권한·호스팅·보안 설정 변경 후 통합 테스트를 실행합니다.
자주 쓰는 명령·설정 예시
## Tool execution
Tools give your agent the ability to take action. Without tools, Claude can only respond with text. With tools, Claude can read files, run commands, search code, and interact with external services.
### Built-in tools
The SDK includes the same tools that power Claude Code:
| Category | Tools | What they do |
| :------------------ | :-------------------------------------------------------------- | :-------------------------------------------------------------------------- |
| **File operations** | `Read`, `Edit`, `Write` | Read, modify, and create files |
| **Search** | `Glob`, `Grep` | Find files by pattern, search content with regex |
| **Execution** | `Bash` | Run shell commands, scripts, git operations |
| **Web** | `WebSearch`, `WebFetch` | Search the web, fetch and parse pages |
| **Discovery** | `ToolSearch` | Dynamically find and load tools on-demand instead of preloading all of them |
| **Orchestration** | `Agent`, `Skill`, `AskUserQuestion`, `TaskCreate`, `TaskUpdate` | Spawn subagents, invoke skills, ask the user, track tasks |
Beyond built-in tools, you can:
* **Connect external services** with [MCP servers](/docs/en/agent-sdk/mcp) (databases, browsers, APIs)
* **Define custom tools** with [custom tool handlers](/docs/en/agent-sdk/custom-tools)
* **Load project skills** via [setting sources](/docs/en/agent-sdk/claude-code-features) for reusable workflows
### Tool permissions
Claude determines which tools to call based on the task, but you control whether those calls are allowed to execute. You can auto-approve specific tools, block others entirely, or require approval for everything. Three options work together to determine what runs:
* **`allowed_tools` / `allowedTools`** auto-approves listed tools. A read-only agent with `["Read", "Glob", "Grep"]` in its allowed tools list runs those tools without prompting. Tools not listed are still available but require permission.
* **`disallowed_tools` / `disallowedTools`** blocks listed tools, regardless of other settings. See [Permissions](/docs/en/agent-sdk/permissions) for the order that rules are checked before a tool runs.
* **`permission_mode` / `permissionMode`** controls what happens to tools that aren't covered by allow or deny rules. See [Permission mode](#permission-mode) for available modes.
You can also scope individual tools with rules like `"Bash(npm *)"` to allow only specific commands. See [Permissions](/docs/en/agent-sdk/permissions) for the full rule syntax.
When a tool is denied, Claude receives a rejection message as the tool result and typically attempts a different approach or reports that it couldn't proceed.
### Parallel tool execution
When Claude requests multiple tool calls in a single turn, both SDKs can run them concurrently or sequentially depending on the tool. Read-only tools (like `Read`, `Glob`, `Grep`, and MCP tools marked as read-only) can run concurrently. Tools that modify state (like `Edit`, `Write`, and `Bash`) run sequentially to avoid conflicts.
Custom tools default to sequential execution. To enable parallel execution for a custom tool, set `readOnlyHint` in its annotations. Both the [TypeScript](/docs/en/agent-sdk/typescript#tool) and [Python](/docs/en/agent-sdk/python#tool) SDKs use this field name from the MCP SDK.
## Control how the loop runs
You can limit how many turns the loop takes, how much it costs, how deeply Claude reasons, and whether tools require approval before running. All of these are fields on [`ClaudeAgentOptions`](/docs/en/agent-sdk/python#claudeagentoptions) (Python) / [`Options`](/docs/en/agent-sdk/typescript#options) (TypeScript).
### Turns and budget
| Option | What it controls | Default |
| :--------------------------------------------- | :--------------------------- | :------- |
| Max turns (`max_turns` / `maxTurns`) | Maximum tool-use round trips | No limit |
| Max budget (`max_budget_usd` / `maxBudgetUsd`) | Maximum cost before stopping | No limit |
When either limit is hit, the SDK returns a `ResultMessage` with a corresponding error subtype (`error_max_turns` or `error_max_budget_usd`). See [Handle the result](#handle-the-result) for how to check these subtypes and [`ClaudeAgentOptions`](/docs/en/agent-sdk/python#claudeagentoptions) / [`Options`](/docs/en/agent-sdk/typescript#options) for syntax.
The budget cap covers [subagents](/docs/en/agent-sdk/subagents): their spend counts toward the total. {/* min-version: 2.1.217 */}Once spend reaches the cap, spawning another subagent fails with `Budget limit reached`, and Claude Code stops any background subagents still running. The cap-enforcement behaviors require Claude Code v2.1.217 or later.
With [streaming input](/docs/en/agent-sdk/streaming-vs-single-mode), a message you send while a turn is still running stays queued when that turn ends at the max-turns limit, and it starts its own turn with its own max-turns limit. Before v2.1.205, a message that arrived on the turn's final iteration could be consumed into the ending turn and lost without ever reaching the model.
### Effort level
The `effort` option controls how much reasoning Claude applies. Lower effort levels use fewer tokens per turn and reduce cost. Not all models support the effort parameter. See [Effort](https://platform.claude.com/docs/en/build-with-claude/effort) for which models support it.
| Level | Behavior | Good for |
| :--------- | :-------------------------------- | :------------------------------------------------------------------------ |
| `"low"` | Minimal reasoning, fast responses | File lookups, listing directories |
| `"medium"` | Balanced reasoning | Routine edits, standard tasks |
| `"high"` | Thorough analysis | Refactors, debugging |
| `"xhigh"` | Extended reasoning depth | Coding and agentic tasks; recommended on Fable 5, Opus 4.7+, and Sonnet 5 |
| `"max"` | Maximum reasoning depth | Multi-step problems requiring deep analysis |
If you don't set `effort`, both SDKs leave the parameter unset and defer to the model's default behavior.
`effort` trades latency and token cost for reasoning depth within each response. [Extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) is a separate feature that produces visible chain-of-thought blocks in the output. They are independent: you can set `effort: "low"` with extended thinking enabled, or `effort: "max"` without it.
Use lower effort for agents doing simple, well-scoped tasks (like listing files or running a single grep) to reduce cost and latency. Set `effort` in the top-level `query()` options for the whole session, or per subagent with the `effort` field on [`AgentDefinition`](/docs/en/agent-sdk/subagents#agentdefinition-configuration) to override the session level.
### Permission mode
The permission mode option (`permission_mode` in Python, `permissionMode` in TypeScript) controls whether the agent asks for approval before using tools:
| Mode | Behavior |
| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"default"` | Tools not covered by allow rules trigger your approval callback; no callback means deny |
| `"acceptEdits"` | Auto-approves file edits and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.); other Bash commands follow default rules |
| `"plan"` | Claude explores and plans without editing your source files; file edits are never auto-approved and prompt through your `canUseTool` callback |
| `"dontAsk"` | Never prompts. Tools pre-approved by [permission rules](/docs/en/settings#permission-settings) run; everything else is denied. `AskUserQuestion`, connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and MCP tools marked [`requiresUserInteraction`](/docs/en/mcp#require-approval-for-a-specific-tool) are denied even if you've allowed them |
| `"auto"` | Uses a model classifier to approve or deny permission prompts. See [Auto mode](/docs/en/permission-modes#eliminate-prompts-with-auto-mode) for availability and behavior |
| `"bypassPermissions"` | Runs all allowed tools without asking, except tools matched by an explicit [`ask` rule](/docs/en/settings#permission-settings), connector tools [your organization set to `ask`](/docs/en/mcp#organization-controls-on-connector-tools), and tools that require user interaction; see [How permissions are evaluated](/docs/en/agent-sdk/permissions#how-permissions-are-evaluated) for the precedence order. Cannot be used when running as root on Unix. Use only in isolated environments where the agent's actions cannot affect systems you care about |
For interactive applications, use `"default"` with a tool approval callback to surface approval prompts. For autonomous agents on a dev machine, `"acceptEdits"` auto-approves file edits and common filesystem commands (`mkdir`, `touch`, `mv`, `cp`, etc.) while still gating other `Bash` commands behind allow rules. Reserve `"bypassPermissions"` for CI, containers, or other isolated environments. See [Permissions](/docs/en/agent-sdk/permissions) for full details.
### Model
If you don't set `model`, the SDK uses Claude Code's default, which depends on your authentication method and subscription. Set it explicitly (for example, `model="claude-sonnet-5"`) to pin a specific model or to use a smaller model for faster, cheaper agents. See [models](https://platform.claude.com/docs/en/about-claude/models) for available IDs.
## The context window
The context window is the total amount of information available to Claude during a session. It does not reset between turns within a session. Everything accumulates: the system prompt, tool definitions, conversation history, tool inputs, and tool outputs. Content that stays the same across turns (system prompt, tool definitions, CLAUDE.md) is automatically [prompt cached](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), which reduces cost and latency for repeated prefixes. For how a custom system prompt or `append` text affects cache reuse across sessions, see [Modifying system prompts](/docs/en/agent-sdk/modifying-system-prompts#improve-prompt-caching-across-users-and-machines).
### What consumes context
Here's how each component affects context in the SDK:
| Source | When it loads | Impact |
| :----------------------- | :------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **System prompt** | Every request | Small fixed cost, always present |
| **CLAUDE.md files** | Session start, via [`settingSources`](/docs/en/agent-sdk/claude-code-features) | Full content in every request (but prompt-cached, so only the first request pays full cost) |
| **Tool definitions** | Every request; MCP schemas deferred by default | Built-in tool schemas load every request. [Tool search](/docs/en/agent-sdk/mcp#mcp-tool-search) defers MCP tool schemas by default, falling back to upfront loading on Google Cloud's Agent Platform or a non-first-party `ANTHROPIC_BASE_URL`. See [Configure tool search](/docs/en/agent-sdk/tool-search#configure-tool-search) for the full matrix |
| **Conversation history** | Accumulates over turns | Grows with each turn: prompts, responses, tool inputs, tool outputs |
| **Skill descriptions** | Session start, via setting sources | Short summaries; full content loads only when invoked |
Large tool outputs consume significant context. Reading a big file or running a command with verbose output can use thousands of tokens in a single turn. Context accumulates across turns, so longer sessions with many tool calls build up significantly more context than short ones.
### Automatic compaction
When the context window approaches its limit, the SDK automatically compacts the conversation: it summarizes older history to free space, keeping your most recent exchanges and key decisions intact. The SDK emits a message with `type: "system"` and `subtype: "compact_boundary"` in the stream when this happens (in Python this is a `SystemMessage`; in TypeScript it is a separate `SDKCompactBoundaryMessage` type).
Compaction replaces older messages with a summary, so specific instructions from early in the conversation may not be preserved. Persistent rules belong in CLAUDE.md (loaded via [`settingSources`](/docs/en/agent-sdk/claude-code-features)) rather than in the initial prompt, because CLAUDE.md content is re-injected on every request.
You can customize compaction behavior in several ways:
* **Summarization instructions in CLAUDE.md:** The compactor reads your CLAUDE.md like any other context, so you can include a section telling it what to preserve when summarizing. The section header is free-form (not a magic string); the compactor matches on intent.
* **`PreCompact` hook:** Run custom logic before compaction occurs, for example to archive the full transcript. The hook receives a `trigger` field (`manual` or `auto`). See [hooks](/docs/en/agent-sdk/hooks).
* **Manual compaction:** Send `/compact` as a prompt string to trigger compaction on demand. Commands sent this way are SDK inputs, not CLI-only shortcuts. See [commands in the SDK](/docs/en/agent-sdk/slash-commands).
Add a section to your project's CLAUDE.md telling the compactor what to preserve. The header name isn't special; use any clear label.
### Keep context efficient
A few strategies for long-running agents:
* **Use subagents for subtasks.** Each subagent starts with a fresh conversation (no prior message history, though it does load its own system prompt and project-level context like CLAUDE.md). It does not see the parent's turns, and only its final response returns to the parent as a tool result. The main agent's context grows by that summary, not by the full subtask transcript. See [What subagents inherit](/docs/en/agent-sdk/subagents#what-subagents-inherit) for details.
* **Be selective with tools.** Every tool definition takes context space. Use the `tools` field on [`AgentDefinition`](/docs/en/agent-sdk/subagents#agentdefinition-configuration) to scope subagents to the minimum set they need.
* **Watch MCP server costs.** [MCP tool search](/docs/en/agent-sdk/mcp#mcp-tool-search) defers MCP tool schemas by default and loads them on demand. When tool search is off, on Google Cloud's Agent Platform, or behind a non-first-party `ANTHROPIC_BASE_URL`, each MCP server adds all its tool schemas to every request, so a few servers with many tools can consume significant context before the agent does any work.
* **Use lower effort for routine tasks.** Set [effort](#effort-level) to `"low"` for agents that only need to read files or list directories. This reduces token usage and cost.
For a detailed breakdown of per-feature context costs, see [Understand context costs](/docs/en/features-overview#understand-context-costs).
## Sessions and continuity
Each interaction with the SDK creates or continues a session. Capture the session ID from `ResultMessage.session_id` (available in both SDKs) to resume later. The TypeScript SDK also exposes it as a direct field on the init `SystemMessage`; in Python it's nested in `SystemMessage.data`.
When you resume, the full context from previous turns is restored: files that were read, analysis that was performed, and actions that were taken. You can also fork a session to branch into a different approach without modifying the original.
See [Session management](/docs/en/agent-sdk/sessions) for the full guide on resume, continue, and fork patterns. To resume sessions across stateless containers or serverless hosts, pass a [`session_store` / `sessionStore` adapter](/docs/en/agent-sdk/session-storage) so transcripts are mirrored to your own backend and any host can resume them. The Claude Code subprocess still writes to local disk first; point `CLAUDE_CONFIG_DIR` at a temp directory in `options.env` if the local copy needs to be ephemeral.
In Python, `ClaudeSDKClient` handles session IDs automatically across multiple calls. See the [Python SDK reference](/docs/en/agent-sdk/python#choosing-between-query-and-claudesdkclient) for details.
## Handle the result
When the loop ends, the `ResultMessage` tells you what happened and gives you the output. The `subtype` field (available in both SDKs) is the primary way to check termination state.
| Result subtype | What happened | `result` field available? |
| :------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------------------: |
| `success` | Claude finished the task normally | Yes |
| `error_max_turns` | Hit the `maxTurns` limit before finishing | No |
| `error_max_budget_usd` | Hit the `maxBudgetUsd` limit before finishing | No |
| `error_during_execution` | An error interrupted the loop (for example, an API failure or cancelled request) | No |
| `error_max_structured_output_retries` | No valid structured output was produced within the configured retry limit: every attempt failed validation, or a model fallback retracted the completed output with no successful retry | No |
The `result` field (the final text output) is only present on the `success` variant, so always check the subtype before reading it. All result subtypes carry `total_cost_usd`, `usage`, `num_turns`, and `session_id` so you can track cost and resume even after errors. In Python, `total_cost_usd` and `usage` are typed as optional and may be `None` on some error paths, so guard before formatting them. See [Tracking costs and usage](/docs/en/agent-sdk/cost-tracking) for details on interpreting the `usage` fields.
When a query ends on an error result:
* A single-shot `query()` call yields the final result message, then raises an error that includes the failure text, such as `Reached maximum number of turns`. The raise is intentional — wrap the loop in a try block if your code needs to continue past it. The underlying Claude Code process also exits with a nonzero code.
* A streaming input session stays alive, and you can keep sending messages.
The result also includes a `stop_reason` field (`string | null` in TypeScript, `str | None` in Python) indicating why the model stopped generating on its final turn. Common values are `end_turn` (model finished normally), `max_tokens` (hit the output token limit), and `refusal` (the model declined the request). On error result subtypes, `stop_reason` carries the value from the last assistant response before the loop ended. To detect refusals, check `stop_reason === "refusal"` (TypeScript) or `stop_reason == "refusal"` (Python). See [`SDKResultMessage`](/docs/en/agent-sdk/typescript#sdkresultmessage) (TypeScript) or [`ResultMessage`](/docs/en/agent-sdk/python#resultmessage) (Python) for the full type.
## Hooks
[Hooks](/docs/en/agent-sdk/hooks) are callbacks that fire at specific points in the loop: before a tool runs, after it returns, when the agent finishes, and so on. Some commonly used hooks are:
| Hook | When it fires | Common uses |
| :------------------------------- | :---------------------------------- | :----------------------------------------- |
| `PreToolUse` | Before a tool executes | Validate inputs, block dangerous commands |
| `PostToolUse` | After a tool returns | Audit outputs, trigger side effects |
| `UserPromptSubmit` | When a prompt is sent | Inject additional context into prompts |
| `Stop` | When the agent finishes | Validate the result, save session state |
| `SubagentStart` / `SubagentStop` | When a subagent spawns or completes | Track and aggregate parallel task results |
| `PreCompact` | Before context compaction | Archive full transcript before summarizing |
Hooks run in your application process, not inside the agent's context window, so they don't consume context. Hooks can also short-circuit the loop: a `PreToolUse` hook that rejects a tool call prevents it from executing, and Claude receives the rejection message instead.
Both SDKs support all the events above. The TypeScript SDK includes additional events that Python does not yet support. See [Control execution with hooks](/docs/en/agent-sdk/hooks) for the complete event list, per-SDK availability, and the full callback API.
## Put it all together
This example combines the key concepts from this page into a single agent that fixes failing tests. It configures the agent with allowed tools (auto-approved so the agent runs autonomously), project settings, and safety limits on turns and reasoning effort. As the loop runs, it captures the session ID for potential resumption, handles the final result, and prints the total cost.
Because a single-shot `query()` call raises after yielding an error result, the loop is wrapped in a try block so the script exits cleanly when a limit is hit.
관련 링크
- 공식 원문: agent-sdk/agent-loop
- 문서 홈
이 가이드는 공식 문서를 한국어 학습용으로 재구성한 것입니다. 옵션 기본값·API 이름은 설치 버전에 따라 달라질 수 있습니다.