> ## Documentation Index
> Fetch the complete documentation index at: https://polos.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

Agents in Polos use LLMs to autonomously reason about tasks and decide which actions to take. This guide covers the fundamentals of creating and running agents.

## Defining an agent

Create an agent by specifying a model provider, system prompt, and optional tools:

<CodeGroup>
  ```python Python theme={null}
  from polos import Agent, tool, WorkflowContext
  from pydantic import BaseModel

  class WeatherInput(BaseModel):
      city: str

  class WeatherOutput(BaseModel):
      temperature: float
      condition: str
      city: str

  @tool(description="Get current weather for a city")
  async def get_weather(ctx: WorkflowContext, input: WeatherInput) -> WeatherOutput:
      # Call weather API
      data = await weather_api.get(input.city)
      return WeatherOutput(
          city=input.city,
          temperature=data.temp,
          condition=data.conditions
      )

  weather_agent = Agent(
      id="weather-agent",
      provider="openai",
      model="gpt-4o",
      system_prompt="You are a helpful weather assistant. Use the get_weather tool to answer questions about weather.",
      tools=[get_weather]
  )
  ```

  ```typescript TypeScript theme={null}
  import { defineAgent, defineTool, WorkflowContext } from "@polos/sdk";
  import { openai } from "@ai-sdk/openai";
  import { z } from "zod";

  const WeatherInput = z.object({
    city: z.string(),
  });

  const WeatherOutput = z.object({
    temperature: z.number(),
    condition: z.string(),
    city: z.string(),
  });

  const getWeather = defineTool(
    {
      id: "get-weather",
      description: "Get current weather for a city",
      inputSchema: WeatherInput,
      outputSchema: WeatherOutput,
    },
    async (ctx: WorkflowContext, input) => {
      // Call weather API
      const data = await weatherApi.get(input.city);
      return {
        city: input.city,
        temperature: data.temp,
        condition: data.conditions,
      };
    }
  );

  const weatherAgent = defineAgent({
    id: "weather-agent",
    model: openai("gpt-4o"),
    systemPrompt:
      "You are a helpful weather assistant. Use the get_weather tool to answer questions about weather.",
    tools: [getWeather],
  });
  ```
</CodeGroup>

## Agent configuration

**Required parameters:**

* `id` - Unique identifier for the agent
* `provider` - LLM provider (see supported providers below)
* `model` - Model name (e.g., "gpt-4o", "claude-sonnet-4")

**Optional parameters:**

* `system_prompt` - Instructions that guide the agent's behavior
* `tools` - List of tools the agent can call
* `output_schema` - Pydantic model for structured outputs
* `stop_conditions` - Conditions that terminate agent execution

### Supported providers

* `openai` - OpenAI (GPT-5, GPT-5-mini, GPT-4o, etc.)
* `anthropic` - Anthropic (Claude Sonnet, Claude Opus, etc.)
* `gemini` - Google Gemini
* `groq` - Groq
* `fireworks` - Fireworks AI
* `together` - Together AI
* `azure` - Azure OpenAI

## Running agents

### Synchronous execution with `agent.run()`

Use `agent.run()` for complete execution:

<CodeGroup>
  ```python Python theme={null}
  import asyncio
  from polos import PolosClient

  async def main():
      client = PolosClient()
      response = await weather_agent.run(
          client,
          "What's the weather like in New York and London? Compare them."
      )

      print(response.result)
      # Agent automatically:
      # 1. Calls get_weather("New York")
      # 2. Calls get_weather("London")
      # 3. Generates comparison
      # 4. Returns final answer

  if __name__ == "__main__":
      asyncio.run(main())
  ```

  ```typescript TypeScript theme={null}
  import { PolosClient } from "@polos/sdk";

  async function main() {
    const client = PolosClient.fromEnv();
    const response = await weatherAgent.run(
      client,
      "What's the weather like in New York and London? Compare them."
    );

    console.log(response.result);
    // Agent automatically:
    // 1. Calls getWeather("New York")
    // 2. Calls getWeather("London")
    // 3. Generates comparison
    // 4. Returns final answer
  }

  main();
  ```
</CodeGroup>

**How it works:**

1. Agent receives your message
2. LLM analyzes the request and decides if tools are needed
3. If tools are needed, agent executes them. If multiple tool calls are needed, they are executed in parallel.
4. Agent feeds tool results back to the LLM
5. Process repeats until the agent has a final answer or hits a stop condition
6. Returns the complete response

### Provider-specific parameters

You can pass any keyword argument supported by your provider:

<CodeGroup>
  ```python Python theme={null}
  # Anthropic extended thinking
  response = await anthropic_agent.run(
      client,
      "What's the weather in New York? How did it impact my day?",
      thinking={
          "type": "enabled",
          "budget_tokens": 4000
      }
  )

  # OpenAI reasoning
  response = await openai_agent.run(
      client,
      "Analyze the weather patterns",
      reasoning_effort="high"  # OpenAI o1 models
  )

  # Temperature and other common parameters
  response = await weather_agent.run(
      client,
      "What's the weather?",
      temperature=0.7,
      max_tokens=1000,
      top_p=0.9
  )
  ```

  ```typescript TypeScript theme={null}
  // Anthropic extended thinking
  const response1 = await anthropicAgent.run(
    client,
    "What's the weather in New York? How did it impact my day?",
    {
      thinking: {
        type: "enabled",
        budget_tokens: 4000,
      },
    }
  );

  // OpenAI reasoning
  const response2 = await openaiAgent.run(
    client,
    "Analyze the weather patterns",
    {
      reasoning_effort: "high", // OpenAI o1 models
    }
  );

  // Temperature and other common parameters
  const response3 = await weatherAgent.run(
    client,
    "What's the weather?",
    {
      temperature: 0.7,
      max_tokens: 1000,
      top_p: 0.9,
    }
  );
  ```
</CodeGroup>

These parameters are passed directly to the underlying provider API.

### Streaming with `agent.stream()`

Stream responses for real-time feedback:

<CodeGroup>
  ```python Python theme={null}
  async def main():
      client = PolosClient()
      stream = await weather_agent.stream(
          client, "What's the weather in Tokyo?"
      )

      # Stream text chunks as they arrive
      async for chunk in stream.text_chunks:
          print(chunk, end="", flush=True)

      print("\n")

  if __name__ == "__main__":
      asyncio.run(main())
  ```

  ```typescript TypeScript theme={null}
  import { PolosClient } from "@polos/sdk";

  async function main() {
    const client = PolosClient.fromEnv();
    const stream = await weatherAgent.stream(
      client,
      "What's the weather in Tokyo?"
    );

    // Stream text chunks as they arrive
    for await (const chunk of stream.textChunks) {
      process.stdout.write(chunk);
    }

    console.log("\n");
  }

  main();
  ```
</CodeGroup>

**Stream features:**

* Real-time token streaming
* Access to intermediate tool calls
* Progress tracking

You can also pass provider-specific parameters to `agent.stream()`:

<CodeGroup>
  ```python Python theme={null}
  stream = await weather_agent.stream(
      client,
      "Compare weather across multiple cities",
      temperature=0.8,
      max_tokens=2000
  )
  ```

  ```typescript TypeScript theme={null}
  const stream = await weatherAgent.stream(
    client,
    "Compare weather across multiple cities",
    {
      temperature: 0.8,
      max_tokens: 2000,
    }
  );
  ```
</CodeGroup>

See [Streaming](/docs/agents/streaming) for advanced streaming patterns.

## Agent responses

Both `agent.run()` and `agent.stream()` return response objects with useful information:

<CodeGroup>
  ```python Python theme={null}
  response = await weather_agent.run("What's the weather in SF?")

  # Access the final text result
  print(response.result)

  # View tool results
  for tool_result in response.tool_results:
      print(f"Tool: {tool_result.tool_name}")
      print(f"Input: {tool_result.result}")

  # Check token usage
  print(f"Total tokens: {response.usage.total_tokens}")
  print(f"Input tokens: {response.usage.input_tokens}")
  print(f"Output tokens: {response.usage.output_tokens}")
  ```

  ```typescript TypeScript theme={null}
  const response = await weatherAgent.run("What's the weather in SF?");

  // Access the final text result
  console.log(response.result);

  // View tool results
  for (const toolResult of response.toolResults) {
    console.log(`Tool: ${toolResult.toolName}`);
    console.log(`Input: ${toolResult.result}`);
  }

  // Check token usage
  console.log(`Total tokens: ${response.usage.totalTokens}`);
  console.log(`Input tokens: ${response.usage.inputTokens}`);
  console.log(`Output tokens: ${response.usage.outputTokens}`);
  ```
</CodeGroup>

## Triggering agents from Slack

You can trigger agents directly from Slack by @mentioning your bot. The agent's output streams back to the originating Slack thread.

```
@polos @sales-agent What's the latest on Acme?
```

`@sales-agent` is the ID of the agent you want to trigger. The bot routes the message to the right agent and streams the response back to the same thread.

To set this up, create a Slack app, configure event subscriptions, and register the app with your orchestrator. See [Slack Integration](/docs/agents/slack-integration) for the full setup guide.

## Sandbox tools

Give agents the ability to write code, run shell commands, and explore a codebase inside a controlled environment. A single call to `sandboxTools()` creates six tools (`exec`, `read`, `write`, `edit`, `glob`, `grep`) that share an execution environment.

<CodeGroup>
  ```python Python theme={null}
  from polos import Agent, sandbox_tools, SandboxToolsConfig, DockerEnvironmentConfig

  tools = sandbox_tools(SandboxToolsConfig(
      env="docker",
      scope="session",
      docker=DockerEnvironmentConfig(
          image="node:20-slim",
      ),
  ))

  coding_agent = Agent(
      id="coding_agent",
      provider="anthropic",
      model="claude-opus-4-5",
      system_prompt="You are a coding assistant.",
      tools=tools,
  )
  ```

  ```typescript TypeScript theme={null}
  import { defineAgent, sandboxTools } from '@polos/sdk';
  import { anthropic } from '@ai-sdk/anthropic';

  const tools = sandboxTools({
    env: 'docker',
    scope: 'session',
    docker: {
      image: 'node:20-slim',
    },
  });

  const codingAgent = defineAgent({
    id: 'coding_agent',
    model: anthropic('claude-opus-4-5'),
    systemPrompt: 'You are a coding assistant.',
    tools: [...tools],
  });
  ```
</CodeGroup>

Polos supports three execution environments:

| Environment         | Isolation           | Best for                    |
| ------------------- | ------------------- | --------------------------- |
| `docker`            | Container           | Production, untrusted code  |
| `local`             | None (host machine) | Development, trusted agents |
| `e2b` (coming soon) | Cloud sandbox       | Cloud-native deployments    |

The `exec` tool supports security modes to control which commands run without approval: `allow-always` (default for Docker), `allowlist` (pattern-based), and `approval-always` (default for local). See [Sandbox](/docs/agents/sandbox) for the full configuration reference.

## Agent durability

Agents in Polos are durable - they survive failures and resume from the last completed step.

**What gets persisted:**

* Tool call inputs and outputs
* LLM reasoning steps
* Conversation history
* Agent state

**Example recovery scenario:**

<CodeGroup>
  ```python Python theme={null}
  # Agent execution starts
  response = await weather_agent.run(client, "Compare weather in 5 cities")

  # Agent calls get_weather("New York") ✓ - cached
  # Agent calls get_weather("London") ✓ - cached
  # Worker crashes ❌

  # On retry:
  # Agent resumes from last completed step
  # get_weather("New York") - returns cached result (no API call)
  # get_weather("London") - returns cached result (no API call)
  # Continues with remaining cities
  ```

  ```typescript TypeScript theme={null}
  // Agent execution starts
  const response = await weatherAgent.run(client, "Compare weather in 5 cities");

  // Agent calls getWeather("New York") ✓ - cached
  // Agent calls getWeather("London") ✓ - cached
  // Worker crashes ❌

  // On retry:
  // Agent resumes from last completed step
  // getWeather("New York") - returns cached result (no API call)
  // getWeather("London") - returns cached result (no API call)
  // Continues with remaining cities
  ```
</CodeGroup>

**Benefits:**

* No duplicate tool calls
* No wasted API tokens
* No lost progress

See [Durable Execution](/docs/fundamentals/durable-execution) for how this works under the hood.

## Using agents in workflows

Agents are workflows, so you can compose them with other workflow steps:

<CodeGroup>
  ```python Python theme={null}
  from polos import workflow, WorkflowContext

  @workflow
  async def customer_support_workflow(ctx: WorkflowContext, input: CustomerSupportInput):
      # Run agent to handle customer query
      agent_response = await ctx.step.agent_invoke_and_wait(
          "weather_agent",
          weather_agent.with_input(input.question)
      )

      # Log the interaction
      await ctx.step.run("log", log_interaction, agent_response)

      # Send follow-up email
      await ctx.step.run(
          "send_email",
          send_email,
          to=input.customer_email,
          body=agent_response.result
      )

      return agent_response
  ```

  ```typescript TypeScript theme={null}
  import { defineWorkflow, WorkflowContext } from "@polos/sdk";

  const customerSupportWorkflow = defineWorkflow(
    async (ctx: WorkflowContext, input: CustomerSupportInput) => {
      // Run agent to handle customer query
      const agentResponse = await ctx.step.agentInvokeAndWait(
        "weather_agent",
        weatherAgent.withInput(input.question)
      );

      // Log the interaction
      await ctx.step.run("log", logInteraction, agentResponse);

      // Send follow-up email
      await ctx.step.run(
        "send_email",
        sendEmail,
        { to: input.customerEmail, body: agentResponse.result }
      );

      return agentResponse;
    }
  );
  ```
</CodeGroup>

This combines agent autonomy with workflow orchestration for complex multi-step processes.

## Next steps

**Core features:**

* **[Tools](/docs/agents/tools)** - Give agents the ability to take actions
* **[Sandbox](/docs/agents/sandbox)** - Isolated execution environments for code and shell commands
* **[Streaming](/docs/agents/streaming)** - Real-time response streaming
* **[Structured Outputs](/docs/agents/structured-outputs)** - Extract structured data
* **[Slack Integration](/docs/agents/slack-integration)** - Trigger agents from Slack

**Advanced patterns:**

* **[Stop Conditions](/docs/agents/stop-conditions)** - Control when agents stop
* **[Conversation Memory](/docs/agents/conversation-memory)** - Multi-turn conversations
* **[Human-in-the-Loop](/docs/agents/human-in-the-loop)** - Approval workflows
* **[Guardrails](/docs/agents/guardrails)** - Safety and compliance
* **[Lifecycle Hooks](/docs/agents/lifecycle-hooks)** - Customize agent behavior
