Skip to content

Agents & Tools

ThinkLang's agentic runtime is fully available from the library. Define tools that the LLM can call, then run an agent loop that orchestrates tool use automatically.

Defining Tools

Use defineTool() to create tools. Accepts Zod schemas or raw JSON Schema for input.

typescript
import { defineTool } from "thinklang";
import { z } from "zod";

const searchDocs = defineTool({
  name: "searchDocs",
  description: "Search internal documentation for relevant info",
  input: z.object({ query: z.string() }),
  execute: async ({ query }) => {
    const results = await docsIndex.search(query);
    return results.map(r => r.title).join("\n");
  },
});

Running an Agent

Use agent() to start an agentic loop. The LLM calls tools as needed until it produces a final answer.

typescript
import { agent, defineTool, zodSchema } from "thinklang";
import { z } from "zod";

const getWeather = defineTool({
  name: "getWeather",
  description: "Get weather for a city",
  input: z.object({ city: z.string() }),
  execute: async ({ city }) => {
    const res = await fetch(`https://api.weather.example/v1/${city}`);
    return res.text();
  },
});

const Report = z.object({
  city: z.string(),
  temperature: z.number(),
  conditions: z.string(),
  recommendation: z.string(),
});

const result = await agent<z.infer<typeof Report>>({
  prompt: "What is the weather in Tokyo? Recommend what to wear.",
  tools: [getWeather],
  ...zodSchema(Report),
  maxTurns: 5,
});

console.log(result.data);             // the Report object
console.log(result.turns);            // how many loop iterations
console.log(result.toolCallHistory);  // full history of tool calls

Agent Options

OptionTypeDefaultDescription
promptstringrequiredThe goal for the agent
toolsTool[]requiredTools the agent can call
jsonSchemaobjectJSON Schema for the final output
maxTurnsnumber10Maximum loop iterations
guardsGuardRule[]Validate the final output
retryCountnumberRetry the entire loop on failure
fallback() => TFallback if all retries fail
onToolCall(call) => voidCalled before each tool executes
onToolResult(result) => voidCalled after each tool executes
abortSignalAbortSignalCancel the agent loop
contextobjectContext data for the agent
modelstringOverride the default model

How the Loop Works

  1. The prompt is sent to the LLM along with tool definitions.
  2. The LLM either calls one or more tools or returns a final answer.
  3. If tools were called, their results are fed back to the LLM.
  4. Steps 2-3 repeat until the LLM produces a final answer or the turn limit is reached.
  5. The final answer is parsed into the specified type and returned.
  6. Throws AgentMaxTurnsError if the turn limit is reached without a final answer.

Built-in Tools

ThinkLang ships with opt-in built-in tools:

typescript
import { agent, fetchUrl, readFile, writeFile, runCommand } from "thinklang";

const result = await agent({
  prompt: "Read the README and summarize it",
  tools: [readFile],
  jsonSchema: { type: "string" },
  maxTurns: 3,
});
ToolDescription
fetchUrlFetch a URL via HTTP GET
readFileRead a local file
writeFileWrite content to a local file
runCommandRun a shell command

Observability Hooks

Track what the agent is doing in real time:

typescript
const result = await agent({
  prompt: "Research this topic",
  tools: [searchDocs],
  jsonSchema: { type: "string" },
  onToolCall: (call) => {
    console.log(`Calling tool: ${call.name}`, call.input);
  },
  onToolResult: (result) => {
    console.log(`Tool ${result.toolName}:`, result.isError ? "ERROR" : "OK");
  },
});

Cancellation

Use AbortSignal to cancel a running agent:

typescript
const controller = new AbortController();
setTimeout(() => controller.abort(), 30000); // 30s timeout

const result = await agent({
  prompt: "Complex research task",
  tools: [searchDocs],
  jsonSchema: { type: "string" },
  abortSignal: controller.signal,
});

Multi-Agent Orchestration

A coordinator agent can delegate to specialized sub-agents. Each sub-agent runs its own agentic loop with its own tools, prompt, and turn limit.

typescript
import { agent, defineTool, type SubAgent } from "thinklang";
import { z } from "zod";

const searchWeb = defineTool({
  name: "searchWeb",
  description: "Search the web for information",
  input: z.object({ query: z.string() }),
  execute: async ({ query }) => `Results for: ${query}`,
});

// Define sub-agents
const researcher: SubAgent = {
  name: "researcher",
  description: "Research a topic thoroughly using web search",
  prompt: "You are a research assistant. Use the search tool to find relevant information.",
  tools: [searchWeb],
  maxTurns: 3,
};

const writer: SubAgent = {
  name: "writer",
  description: "Write well-structured content based on research findings",
  prompt: "You are a technical writer. Create clear, well-organized content.",
};

// Run the coordinator
const result = await agent<string>({
  prompt: "Create a brief report on TypeScript trends",
  tools: [],
  agents: [researcher, writer],
  maxTurns: 10,
  jsonSchema: { type: "string" },
  onAgentCall: (name, input) => console.log(`→ Delegating to ${name}`),
  onAgentResult: (name) => console.log(`← ${name} completed`),
});

console.log(result.data);

How It Works

  1. Sub-agents are automatically wrapped as tools that the coordinator can call.
  2. When the coordinator calls a sub-agent, a nested agentic loop runs with the sub-agent's configuration.
  3. The sub-agent's result is returned to the coordinator as a tool result.
  4. Token usage from all sub-agents is aggregated into the coordinator's totalUsage.

SubAgent Options

OptionTypeDefaultDescription
namestringrequiredName used to identify the sub-agent
descriptionstringrequiredDescription shown to the coordinator
promptstringSystem prompt for the sub-agent
toolsTool[]Tools the sub-agent can use
maxTurnsnumber10Maximum turns for the sub-agent's loop
modelstringOverride the model for this sub-agent
jsonSchemaobjectJSON Schema for the sub-agent's output

ThinkLang Language Syntax

In .tl files, you can declare sub-agents and use them with the with agents: clause:

agent researcher("Research topics") with tools: search
agent writer("Write content")

let report = agent<Report>("Create a report")
  with tools: search
  with agents: researcher, writer
  max turns: 10

Next Steps