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
jsonSchemaobject—JSON Schema for the final output
maxTurnsnumber10Maximum loop iterations
guardsGuardRule[]—Validate the final output
retryCountnumber—Retry the entire loop on failure
fallback() => T—Fallback if all retries fail
onToolCall(call) => void—Called before each tool executes
onToolResult(result) => void—Called after each tool executes
abortSignalAbortSignal—Cancel the agent loop
contextobject—Context data for the agent
modelstring—Override 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
promptstring—System prompt for the sub-agent
toolsTool[]—Tools the sub-agent can use
maxTurnsnumber10Maximum turns for the sub-agent's loop
modelstring—Override the model for this sub-agent
jsonSchemaobject—JSON 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 ​