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

# Agent

> Use the Agentica SDK to build agents.

<Note>
  **This is detailed usage documentation.** New to the Agentica SDK? Start with the [Quickstart](/quickstart) or learn [when to use agents vs agentic functions](/concepts/agentic-vs-agents).
</Note>

**Agents** are stateful, long‑lived LLM workers you spawn and call repeatedly; they keep conversational history across invocations.

We expose two ways to instantiate an agent:

* **direct** instantiation via `Agent.__init__`
* **awaitable** instantiation via the `spawn` function
  Both return an instance of the `Agent` class.

<Tip>
  Direct instantiation is often useful in functions that **must be synchronous** e.g. setting attributes of objects in protected methods such as `__init__`. See [here](/references/python/agents) for the API reference of `Agent`.
</Tip>

## Using agents

Agents built with the Agentica SDK accomplish specific tasks using native libraries, code, APIs, and SDKs available in your programming language's runtime.
A single agent represents an **evolving history of invocations**, each of which may be provided a specific task and set of resources.

### When to use agents

Agents work best for **longer-running, multi-step tasks** where each action depends on prior outcomes,
state is preserved, and task-appropriate sets of resources need to be delegated.

For single, well-bounded tasks without cross-step context, see [Agentic functions](/concepts/agentic).

## The basics

Agents can be created with the Agentica SDK using `spawn` and later **called to perform tasks**. An agent's **history evolves across its invocations**, so you can follow up with tasks in the context of previous results.

In Python, provide a **return type** to receive a result of that runtime type (defaulting to `str`). In TypeScript, the return type is specified via the generic `<T>` type parameter.

<CodeGroup>
  ```python Python wrap theme={null}
  agent = await spawn(premise="You are a helpful assistant.")

  c: float = await agent.call(float, "What is the lattice constant of silicon in Ångströms?")
  print("Lattice constant of silicon:", c)

  derivation: str = await agent.call("And how is this constant derived?")
  print("Derivation of lattice constant:", derivation)
  ```

  ```typescript TypeScript wrap theme={null}
  await using agent = await spawn({ premise: "You are a helpful assistant." });

  const c = await agent.call<number>("What is the lattice constant of silicon in Ångströms?");
  console.log("Lattice constant of silicon:", c);

  const derivation = await agent.call<string>("And how is this constant derived?");
  console.log("Derivation of lattice constant:", derivation);
  ```
</CodeGroup>

See the API references: [Python](/references/python/agents) | [TypeScript](/references/ts/agents).

## Use your tools and types

**Any** function, object, method, or other runtime value can be directly exposed as resources your agent can interact with. No need to set up MCP servers.
Expose the full programmatic power of an SDK or API directly to your agent. Make them available when spawning the agent and/or pass per-invocation resources.

You can also **expose existing remote or local MCP tools** by passing an MCP configuration path. See [here](/concepts/unmcp) for more information.

<CodeGroup>
  ```python Python wrap theme={null}
  agent = await spawn(premise="You are a helpful researcher.")
  gdp: float = await agent.call(
      float,
      "What percentage of US GDP is from California?",
      web_search=web_search,
  )
  print(f"Percentage: {gdp:.1f}%")
  ```

  ```typescript TypeScript wrap theme={null}
  await using agent = await spawn({ premise: 'You are a helpful researcher.' });
  const gdp = await agent.call<number>(
    'What percentage of US GDP is from California?',
    { webSearch },
  );
  console.log(`Percentage: ${gdp.toFixed(2)}%`);
  ```
</CodeGroup>

<Warning>
  * Objects passed to `scope` or as arguments are presented **without** private methods or field names (fields with a leading `_`).
  * Async functions in `scope` are exposed to the REPL as synchronous functions returning `Future[T]`. The REPL includes a top-level event loop, so agents can `await` these futures directly and use standard patterns like `asyncio.gather()`.
</Warning>

## Multi-agent orchestration

Multi-agent orchestration becomes straightforward.
Agents can trigger sub-agents by passing `spawn` in `scope`, enabling completely dynamic agent delegation.

<CodeGroup>
  ```python Python wrap theme={null}
  agent = await spawn(premise="You are an agent orchestrator.", model="openai/gpt-5.2")
  result = await agent.call(
      tuple[int, int],
      "Use one sub-agent to compute 3**32 and another to compute 3**34, then return both results.",
      spawn=spawn,
  )
  assert result == (3**32, 3**34)
  print(result)
  ```

  ```typescript TypeScript wrap theme={null}
  await using agent = await spawn({
    premise: 'You are an agent orchestrator.',
    model: 'openai/gpt-5.2',
  });


  async function subAgent(task: string): Promise<number> {
    return await agentic(task, { pow: Math.pow });
  }

  const result = await agent.call<[number, number]>(
    'Use one sub-agent to compute 3**32 and another to compute 3**34, then return both results.',
    { subAgent },
  );

  console.log(result);
  ```
</CodeGroup>

## Streaming

Stream responses as they are being generated.

<CodeGroup>
  ```python Python wrap theme={null}
  import asyncio
  from agentica import spawn
  from agentica.logging.loggers import StreamLogger

  agent = await spawn(premise='You are a mathematician.', model='openai/gpt-5.2')

  stream = StreamLogger()
  with stream:
    root = asyncio.create_task(
      agent.call(float, 'Define a Newton–Raphson solver, and use it to solve for a root of a polynomial of your choice.')
    )

  role = None
  async for chunk in stream:
      if role is None and chunk.role == 'user':
          continue  # Skip first user message
      if role != chunk.role:
          print(f"\n\n--- {chunk.role} ---")
          role = chunk.role
      print(chunk, end='', flush=True)
  print('\n')

  print('root =', await root)
  ```

  ```typescript TypeScript wrap theme={null}
  await using agent = await spawn({
    premise: 'You are a mathematician.',
    model: 'openai/gpt-5.2'
  });

  let role: string | null = null;
  function print(iid: string, chunk: any) {
    if (role === null && chunk.role === 'user') {
      return; // Skip first user message
    }
    if (role !== chunk.role) {
      process.stdout.write(`\n\n--- ${chunk.role} ---\n`);
      role = chunk.role;
    }
    process.stdout.write(chunk.content);
  }

  const root = await agent.call<number>(
    'Define a Newton–Raphson solver, and use it to solve for a root of a polynomial of your choice.',
    { }, { listener: print }
  );

  console.log('\n');
  console.log('root =', root);
  ```
</CodeGroup>

<Accordion title="Example logs">
  ````
  --- agent ---
  ```python
  def newton_raphson(f, df, x0, tol=1e-8, max_iter=100):
      x = x0
      for _ in range(max_iter):
          fx = f(x)
          dfx = df(x)
          if abs(dfx) < 1e-12:
              break  # Avoid division by zero
          x_new = x - fx / dfx
          if abs(x_new - x) < tol:
              return float(x_new)
          x = x_new
      return float(x)

  # Polynomial: x^3 - x - 2 = 0
  def f(x):
      return x**3 - x - 2

  def df(x):
      return 3*x**2 - 1

  return newton_raphson(f, df, 1.5)
  ```

  --- user<execution> ---
  1.5213797068045676


  root = 1.5213797068045676
  ````
</Accordion>

## Chat with your agents

Create a simple chat loop using streaming. Consume the stream before awaiting the final result to see live generation.

<CodeGroup>
  ```python Python wrap theme={null}
  import asyncio
  from agentica import spawn
  from agentica.logging import set_default_agent_listener
  from agentica.logging.loggers import StreamLogger

  RED = "\033[91m"
  GREEN = "\033[92m"
  PURPLE = "\033[95m"
  RESET = "\033[0m"
  GREY = "\033[90m"

  set_default_agent_listener(None)

  async def chat():
      agent = await spawn(premise='You are a helpful assistant.', model='openai/gpt-5.2')

      while user_input := input(f"\n{PURPLE}User{RESET}: "):
          try:
              # Invoke agent against user prompt
              stream = StreamLogger()
              with stream:
                  result = asyncio.create_task(
                      agent.call(str, user_input)
                  )

              # Stream intermediate "thinking" to console
              print(GREY)
              async for chunk in stream:
                  if chunk.role == 'agent':
                      print(chunk, end="", flush=True)
              print(RESET)

              # Print final result
              print(f"\n{GREEN}Agent{RESET}: {await result}")

          except Exception as agent_error:
              print(f"\n{RED}Error: {agent_error}{RESET}")


  if __name__ == '__main__':
      asyncio.run(chat())
  ```

  ```typescript TypeScript theme={null}
  import { spawn } from '@symbolica/agentica';

  import * as readline from 'readline';

  const RED = '\x1b[91m';
  const GREEN = '\x1b[92m';
  const PURPLE = '\x1b[95m';
  const RESET = '\x1b[0m';
  const GREY = '\x1b[90m';

  async function chat() {
    await using agent = await spawn({
      premise: 'You are a helpful assistant.',
      model: 'openai/gpt-5.2'
    });

    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
    });

    const question = (prompt: string): Promise<string> => {
      return new Promise((resolve) => rl.question(prompt, resolve));
    };

    while (true) {
      const userInput = await question(`\n${PURPLE}User${RESET}: `);
      if (!userInput) break;

      try {
        // Invoke agent against user prompt
        process.stdout.write('\n' + GREY);
        const result = await agent.call<string>(userInput, {},
          {
            listener: (iid: string, chunk: any) => {
              if (chunk.role === 'agent' && chunk.content) {
                process.stdout.write(chunk.content);
              }
            }
          }
        );
        process.stdout.write(RESET + '\n');

        // Print final result
        console.log(`\n${GREEN}Agent${RESET}: ${result}`);
      } catch (error) {
        console.log(`\n${RED}Error: ${error}${RESET}`);
      }
    }

    rl.close();
  }

  chat();
  ```
</CodeGroup>

That's all it takes!

## Advanced

You can expose custom exceptions in scope so they can be raised from within execution (see [Advanced](/guides/agent-errors), including information on logging, retries, rate-limiting and prefix caching). For more examples, see [Examples](/guides/examples).
