Skip to content

Agent Guide

This guide walks you through creating an agent that answers questions from a document bucket and a custom tool. By the end you will have a working TypeScript agent that searches documents, calls a tool, and emits events you can observe.

  • Node.js 18 or later
  • A Schift workspace API key (sch_...)
  • A configured API origin for your workspace
  • The Schift CLI installed (only needed to create and populate the bucket)

Note: If you have not yet created a bucket and verified search, follow the Quickstart first. Proving the retrieval path before adding an agent makes debugging much easier.

Terminal window
npm install @schift-io/sdk

Create a bucket and upload at least one document:

Terminal window
export SCHIFT_API_KEY=sch_...
export SCHIFT_API_URL=https://api.example.com/v1
schift db create support-docs
schift upload ./handbook.pdf --bucket support-docs
schift search "How do I reset my password?" --bucket support-docs --top-k 5

Confirm that search returns relevant chunks. Once it does, the same bucket can be attached to an agent.

Create a file named agent.ts:

import { Schift, Agent, RAG } from "@schift-io/sdk";
const schift = new Schift({
apiKey: process.env.SCHIFT_API_KEY,
});
const rag = new RAG({ bucket: "support-docs" }, schift.transport);
const agent = new Agent({
name: "Support Bot",
instructions:
"You are a support assistant. Answer questions using the knowledge base. " +
"If the answer is not in the documents, say so clearly.",
rag,
model: "gpt-4o-mini",
transport: schift.transport,
maxSteps: 10,
});

The RAG instance is automatically registered as a tool named rag_search, so the agent can search the bucket whenever the question requires it.

const result = await agent.run("How do I reset my password?");
console.log(result.output);
console.log(`Completed in ${result.totalDurationMs}ms`);
console.log(`Steps: ${result.steps.length}`);

Each step in result.steps has a type such as think, tool_call, tool_result, or final_answer. Inspecting the steps is the fastest way to understand why an agent produced a particular answer.

Agents become more useful when they can call your own APIs. Add a weather tool:

import type { AgentTool } from "@schift-io/sdk";
const getWeather: AgentTool = {
name: "get_weather",
description: "Get the current weather for a city",
parameters: {
type: "object",
properties: {
city: {
type: "string",
description: "City name, for example Seoul",
},
},
required: ["city"],
},
handler: async (args) => {
const city = String(args.city);
const temperature = await fetchWeatherAPI(city); // your implementation
return { success: true, data: { city, temperature } };
},
};
const agent = new Agent({
name: "Support Bot",
instructions: "Answer support questions and report weather when asked.",
rag,
tools: [getWeather],
transport: schift.transport,
});

Tool names must be unique within an agent, use snake_case, and match /^[a-zA-Z_][a-zA-Z0-9_]*$/.

Subscribe to events to show progress or log execution:

agent.on("tool_call", (event) => {
console.log(`Calling tool: ${event.toolName}`);
});
agent.on("tool_result", (event) => {
console.log(`Tool result:`, event.result);
});
agent.on("agent_end", (event) => {
console.log(`Run finished in ${event.totalDurationMs}ms`);
});
const result = await agent.run("What is the weather in Seoul?");

The returned cleanup function removes the listener when you no longer need it:

const unsub = agent.on("tool_call", handler);
unsub();

Wrap runs in try/catch to handle agent-level failures:

import { AgentError, MaxStepsError } from "@schift-io/sdk";
try {
const result = await agent.run("A very complex question");
console.log(result.output);
} catch (err) {
if (err instanceof MaxStepsError) {
console.log("The agent used too many steps. Try a simpler question or increase maxSteps.");
} else if (err instanceof AgentError) {
console.log(`Agent error: ${err.message}`);
}
}

If the agent hits maxSteps, agent.run() returns normally and the final step has type error. Check result.steps to see where the loop ended.

  • Add web search for questions that need current information.
  • Read the Agent concept for a deeper explanation of the ReAct loop and configuration options.
  • Explore Tools and Skills to extend what your agent can do.
  • Review Error Handling for production-ready patterns.