Getting Started with Agents

Build an AI agent that can call tools, track its own progress, and respond with results. This guide walks you through a complete agent from scratch.

1. Install dependencies

npm install @allem-sdk/agents @allem-sdk/ai ai @ai-sdk/react @ai-sdk/google zod

You can swap @ai-sdk/google for @ai-sdk/anthropic or @ai-sdk/openai.

2. Set your API key

Create a .env.local file in your project root:

GOOGLE_GENERATIVE_AI_API_KEY=your-api-key-here

# Or for other providers:
# ANTHROPIC_API_KEY=your-api-key-here
# OPENAI_API_KEY=your-api-key-here

3. Define your tools (server)

Tools are functions your agent can call. Each tool has a description (so the AI knows when to use it), a zod schema (for typed parameters), and an execute function.

// lib/tools.ts
import { createAllemTool } from "@allem-sdk/agents";
import { z } from "zod";

export const weatherTool = createAllemTool({
  description: "Get the current weather for a city",
  parameters: z.object({
    city: z.string().describe("City name, e.g. 'San Francisco'"),
  }),
  execute: async ({ city }) => {
    // Replace with a real API call
    const data = await fetch(
      `https://api.weatherapi.com/v1/current.json?q=${city}`
    ).then((r) => r.json());
    return {
      city,
      temperature: data.current.temp_f,
      condition: data.current.condition.text,
    };
  },
});

export const calculatorTool = createAllemTool({
  description: "Perform a math calculation",
  parameters: z.object({
    expression: z.string().describe("Math expression, e.g. '2 + 2'"),
  }),
  execute: async ({ expression }) => {
    const result = Function(`"use strict"; return (${expression})`)();
    return { expression, result };
  },
});

4. Create the API route (server)

The agent handler receives messages from the client, passes them to the AI model with your tools, and streams the response back.

// app/api/agent/route.ts
import { createAllemAgentHandler } from "@allem-sdk/agents";
import { google } from "@ai-sdk/google";
import { weatherTool, calculatorTool } from "@/lib/tools";

export const POST = createAllemAgentHandler({
  providers: {
    google: (model) => google(model ?? "gemini-2.0-flash"),
  },
  tools: {
    weather: weatherTool,
    calculator: calculatorTool,
  },
  systemPrompt: "You are a helpful assistant. Use the available tools when needed to answer questions accurately.",
});

5. Build the agent UI (client)

The useAllemAgent hook gives you everything you need: messages, agent status, tool call tracking, and step history.

// app/page.tsx
"use client";

import { useState } from "react";
import { AllemAIProvider } from "@allem-sdk/ai";
import { useAllemAgent } from "@allem-sdk/agents";

export default function Home() {
  return (
    <AllemAIProvider api="/api/agent" provider="google">
      <Agent />
    </AllemAIProvider>
  );
}

function Agent() {
  const [input, setInput] = useState("");
  const {
    messages,
    sendMessage,
    agentStatus,
    steps,
    currentToolCalls,
    isAgentRunning,
  } = useAllemAgent();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!input.trim() || isAgentRunning) return;
    const text = input;
    setInput("");
    await sendMessage({ text });
  };

  return (
    <div style={{ maxWidth: 600, margin: "0 auto", padding: 24 }}>
      <h1>AI Agent</h1>

      {/* Status indicator */}
      <AgentStatusBadge status={agentStatus} toolCalls={currentToolCalls} />

      {/* Messages */}
      <div style={{ marginTop: 16 }}>
        {messages.map((m) => (
          <div
            key={m.id}
            style={{
              padding: 12,
              marginBottom: 8,
              borderRadius: 8,
              background: m.role === "user" ? "#e8f0fe" : "#f8f9fa",
            }}
          >
            <strong>{m.role === "user" ? "You" : "Agent"}</strong>
            <div>
              {m.parts
                ?.filter((p) => p.type === "text")
                .map((p, i) => <p key={i}>{p.text}</p>)}
            </div>
          </div>
        ))}
      </div>

      {/* Step history */}
      {steps.length > 0 && (
        <div style={{ marginTop: 16, fontSize: 13, color: "#666" }}>
          <strong>Steps taken: {steps.length}</strong>
          <ul>
            {steps.map((step) => (
              <li key={step.stepIndex}>
                Step {step.stepIndex + 1}:{" "}
                {step.toolCalls.map((tc) => tc.toolName).join(", ")}
              </li>
            ))}
          </ul>
        </div>
      )}

      {/* Input */}
      <form onSubmit={handleSubmit} style={{ marginTop: 16, display: "flex", gap: 8 }}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask something..."
          disabled={isAgentRunning}
          style={{ flex: 1, padding: 8, borderRadius: 6, border: "1px solid #ddd" }}
        />
        <button
          type="submit"
          disabled={isAgentRunning}
          style={{ padding: "8px 16px", borderRadius: 6, background: "#4f46e5", color: "#fff", border: "none" }}
        >
          Send
        </button>
      </form>
    </div>
  );
}

6. Show agent status

The agentStatus property tracks the agent lifecycle. Use it to show contextual feedback to the user.

StatusWhenWhat to show
idleNo conversation yet, or agent finishedInput field, ready state
thinkingAI is generating a responseLoading spinner, "Thinking..."
calling-toolAI decided to call a toolTool name, "Looking up weather..."
doneAgent finished its responseFinal answer, step summary
function AgentStatusBadge({
  status,
  toolCalls,
}: {
  status: "idle" | "thinking" | "calling-tool" | "done";
  toolCalls: { toolName: string }[];
}) {
  if (status === "idle") return null;

  const labels = {
    thinking: "Thinking...",
    "calling-tool": `Using tool: ${toolCalls[0]?.toolName ?? "..."}`,
    done: "Done",
  };

  return (
    <div style={{
      padding: "6px 12px",
      borderRadius: 6,
      fontSize: 13,
      background: status === "calling-tool" ? "#fef3c7" : "#e0e7ff",
      color: status === "calling-tool" ? "#92400e" : "#3730a3",
    }}>
      {labels[status]}
    </div>
  );
}

7. Try it out

Start your dev server and try these prompts:

  • "What's the weather in Tokyo?" → agent calls the weather tool, returns the result
  • "What's 1847 * 293?" → agent calls the calculator tool
  • "What's the weather in Paris and London?" → agent may call the tool twice in separate steps
  • "Tell me a joke" → agent responds directly without calling any tools

What's happening under the hood

  1. User sends a message via sendMessage()
  2. The message is sent to your /api/agent route
  3. createAllemAgentHandler passes it to the AI model with your tools
  4. The AI model decides whether to respond directly or call a tool
  5. If it calls a tool, the execute function runs and the result is sent back to the model
  6. The model generates a final response incorporating the tool result
  7. On the client, useAllemAgent tracks each step: thinking → calling-tool → done

Next steps