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

# Build an Agent

> Create an AI agent, assign tools and knowledge bases, then chat with it via streaming. Agents run a ReAct loop: they reason, call tools, and respond.

Agents are the conversational interface to your AI features. This guide creates an agent, gives it a tool and a knowledge base, then runs a streaming multi-turn conversation against it.

<Note>
  **Prerequisites:**

  * Authentication configured (see Authentication guide)
  * Optional: A knowledge base for RAG (see Create a Knowledge Base guide)
</Note>

<Steps>
  <Step title="Create the agent">
    Define the agent with a name, LLM model, and system prompt that describes its behavior.

    **Endpoint:** `POST /api/agents`

    <Note>
      `model` is a LiteLLM model ID. Bare IDs (`gpt-4o`, `claude-sonnet-4-6`) route to OpenAI/Anthropic; other providers are prefixed (`gemini/...`, `openrouter/...`). The field is not limited to the Studio model picker, but a provider you haven't [registered a key](/api-reference/ai-provider-keys) for (and that isn't AI-on-us) returns `402 provider_key_decrypt_failed`. [Bring your own LLM](/guides/byollm) covers the formats and an OpenRouter/DeepSeek example.
    </Note>

    <CodeGroup>
      ```python Python theme={null}
      response = requests.post(
          f"{BASE_URL}/api/agents",
          headers=headers,
          json={
              "name": "Support Bot",
              "model": "gpt-4o",
              "system_prompt": "You are a helpful support assistant. Answer questions using the knowledge base when available.",
              "settings": {"temperature": 0.7},
          },
      )
      agent = response.json()
      agent_id = agent["id"]
      print(f"Agent created: {agent_id}")
      ```

      ```typescript TypeScript theme={null}
      const response = await fetch(`${BASE_URL}/api/agents`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          name: "Support Bot",
          model: "gpt-4o",
          system_prompt: "You are a helpful support assistant. Answer questions using the knowledge base when available.",
          settings: { temperature: 0.7 },
        }),
      });
      const agent = await response.json();
      console.log("Agent created:", agent.id);
      ```

      ```bash cURL theme={null}
      curl -X POST '{BASE_URL}/api/agents' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "Support Bot",
          "model": "gpt-4o",
          "system_prompt": "You are a helpful support assistant.",
          "settings": {"temperature": 0.7}
        }'
      ```
    </CodeGroup>
  </Step>

  <Step title="Assign a builtin tool">
    Enable the agent to use builtin tools like database\_query, http\_request, or code\_execute.

    **Endpoint:** `POST /api/agents/{id}/tools`

    <CodeGroup>
      ```python Python theme={null}
      response = requests.post(
          f"{BASE_URL}/api/agents/{agent_id}/tools",
          headers=headers,
          json={"tool_name": "database_query"},
      )
      print(response.json())
      ```

      ```typescript TypeScript theme={null}
      await fetch(`${BASE_URL}/api/agents/${agentId}/tools`, {
        method: "POST",
        headers,
        body: JSON.stringify({ tool_name: "database_query" }),
      });
      ```

      ```bash cURL theme={null}
      curl -X POST '{BASE_URL}/api/agents/{agent_id}/tools' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -H "Content-Type: application/json" \
        -d '{"tool_name": "database_query"}'
      ```
    </CodeGroup>
  </Step>

  <Step title="Link a knowledge base">
    Assign a knowledge base and the agent automatically gets a search tool for it. During a conversation it can search the KB to ground its responses.

    **Endpoint:** `POST /api/agents/{id}/knowledge-bases`

    <CodeGroup>
      ```python Python theme={null}
      response = requests.post(
          f"{BASE_URL}/api/agents/{agent_id}/knowledge-bases",
          headers=headers,
          json={"knowledge_base_id": kb_id},
      )
      print(response.json())
      ```

      ```typescript TypeScript theme={null}
      await fetch(`${BASE_URL}/api/agents/${agentId}/knowledge-bases`, {
        method: "POST",
        headers,
        body: JSON.stringify({ knowledge_base_id: kbId }),
      });
      ```

      ```bash cURL theme={null}
      curl -X POST '{BASE_URL}/api/agents/{agent_id}/knowledge-bases' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -H "Content-Type: application/json" \
        -d '{"knowledge_base_id": "{kb_id}"}'
      ```
    </CodeGroup>
  </Step>

  <Step title="Chat with the agent (streaming)">
    Send a message and receive a Server-Sent Events (SSE) stream. Events include tool calls, tool results, and the final response.

    **Endpoint:** `POST /api/agents/{id}/run/stream`

    <Note>
      SSE events: start, chunk, step\_started, tool\_call, tool\_result, step\_completed, approval\_requested, complete, error.
    </Note>

    <CodeGroup>
      ```python Python theme={null}
      response = requests.post(
          f"{BASE_URL}/api/agents/{agent_id}/run/stream",
          headers=headers,
          json={"message": "What does the product documentation say about getting started?"},
          stream=True,
      )

      session_id = None
      for line in response.iter_lines():
          if not line:
              continue
          text = line.decode("utf-8")
          if text.startswith("data: "):
              import json
              event = json.loads(text[6:])
              if event["event"] == "start":
                  session_id = event["session_id"]
              elif event["event"] == "chunk":
                  print(event["content"], end="")
              elif event["event"] == "tool_call":
                  print(f"\n[Tool: {event['tool_name']}]")
              elif event["event"] == "complete":
                  print(f"\n\nDone. Session: {session_id}")
      ```

      ```typescript TypeScript theme={null}
      const response = await fetch(`${BASE_URL}/api/agents/${agentId}/run/stream`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          message: "What does the product documentation say about getting started?",
        }),
      });

      const reader = response.body!.getReader();
      const decoder = new TextDecoder();
      let sessionId: string | null = null;

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        const text = decoder.decode(value);
        for (const line of text.split("\n")) {
          if (line.startsWith("data: ")) {
            const event = JSON.parse(line.slice(6));
            if (event.event === "start") sessionId = event.session_id;
            if (event.event === "chunk") process.stdout.write(event.content);
            if (event.event === "tool_call") console.log(`\n[Tool: ${event.tool_name}]`);
            if (event.event === "complete") console.log(`\nDone. Session: ${sessionId}`);
          }
        }
      }
      ```

      ```bash cURL theme={null}
      curl -N -X POST '{BASE_URL}/api/agents/{agent_id}/run/stream' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -H "Content-Type: application/json" \
        -d '{"message": "What does the product documentation say about getting started?"}'
      ```
    </CodeGroup>
  </Step>

  <Step title="Continue the conversation">
    Pass the session\_id from the previous run to continue the multi-turn conversation. The agent retains full message history within the session.

    **Endpoint:** `POST /api/agents/{id}/run/stream`

    <CodeGroup>
      ```python Python theme={null}
      response = requests.post(
          f"{BASE_URL}/api/agents/{agent_id}/run/stream",
          headers=headers,
          json={
              "message": "Can you summarize that in bullet points?",
              "session_id": session_id,
          },
          stream=True,
      )

      for line in response.iter_lines():
          if not line:
              continue
          text = line.decode("utf-8")
          if text.startswith("data: "):
              event = json.loads(text[6:])
              if event["event"] == "chunk":
                  print(event["content"], end="")
      ```

      ```typescript TypeScript theme={null}
      const response = await fetch(`${BASE_URL}/api/agents/${agentId}/run/stream`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          message: "Can you summarize that in bullet points?",
          session_id: sessionId,
        }),
      });
      // ... parse SSE stream same as above
      ```

      ```bash cURL theme={null}
      curl -N -X POST '{BASE_URL}/api/agents/{agent_id}/run/stream' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -H "Content-Type: application/json" \
        -d '{"message": "Can you summarize that in bullet points?", "session_id": "{session_id}"}'
      ```
    </CodeGroup>
  </Step>
</Steps>

## What's Next

<CardGroup cols={2}>
  <Card title="Streaming Responses" href="/guides/streaming-guide">
    Deep dive into SSE event handling.
  </Card>

  <Card title="Advanced Agent Config" href="/guides/advanced-agent-config">
    Add MCP servers, hooks, and approval flows.
  </Card>

  <Card title="Agents & Tools" href="/concepts/agents-tools">
    Understand the ReAct loop and tool system.
  </Card>
</CardGroup>
