Haystack Tutorial (TypeScript): adding tool use for intermediate developers
This tutorial shows you how to add tool use to a Haystack TypeScript pipeline so an LLM can call external functions instead of guessing. You need this when the model has to fetch live data, query internal systems, or trigger deterministic business logic like policy lookup or claim status checks.
What You'll Need
- •Node.js 18+ and npm
- •A TypeScript project with Haystack installed
- •An OpenAI API key in
OPENAI_API_KEY - •A working internet connection for model calls
- •Basic familiarity with Haystack components, pipelines, and generators
Install the packages first:
npm install @haystack-ai/core @haystack-ai/integrations dotenv
Step-by-Step
- •Start by creating a small tool function. Keep it deterministic and narrow; tools should do one thing well, like returning a policy status from a mock data source.
import "dotenv/config";
type PolicyRecord = {
policyId: string;
status: string;
premiumDue: string;
};
const policies: Record<string, PolicyRecord> = {
"POL-1001": { policyId: "POL-1001", status: "Active", premiumDue: "2026-02-01" },
"POL-1002": { policyId: "POL-1002", status: "Lapsed", premiumDue: "2025-12-15" },
};
function getPolicyStatus(policyId: string): string {
const record = policies[policyId];
if (!record) return `No policy found for ${policyId}`;
return JSON.stringify(record);
}
- •Wrap that function as a tool and expose it to the model. In Haystack, the tool description matters because the LLM uses it to decide when to call the function.
import { Tool } from "@haystack-ai/core";
const policyStatusTool = new Tool({
name: "get_policy_status",
description:
"Look up insurance policy details by policy ID. Use this when the user asks about status, due date, or whether a policy is active.",
parameters: {
type: "object",
properties: {
policyId: {
type: "string",
description: "The policy ID, for example POL-1001",
},
},
required: ["policyId"],
additionalProperties: false,
},
invoke: async ({ policyId }: { policyId: string }) => getPolicyStatus(policyId),
});
- •Create an OpenAI chat generator and attach the tool. This is the part that enables tool calling; without attaching tools, the model will only generate text.
import { OpenAIChatGenerator } from "@haystack-ai/integrations";
const generator = new OpenAIChatGenerator({
model: "gpt-4o-mini",
apiKey: process.env.OPENAI_API_KEY!,
tools: [policyStatusTool],
});
- •Build a small pipeline that sends user input into the generator and prints the response. For intermediate developers, this is the cleanest way to verify tool use before wiring it into a larger agent flow.
import { Pipeline } from "@haystack-ai/core";
const pipeline = new Pipeline();
pipeline.addComponent("generator", generator);
const result = await pipeline.run({
generator: {
messages: [
{
role: "user",
content: "Check policy POL-1001 and tell me if it's active.",
},
],
},
});
console.log(JSON.stringify(result, null, 2));
- •Put everything into one executable file so you can run it directly. This version keeps the setup simple and makes debugging easier because you can inspect both tool output and final model output.
import "dotenv/config";
import { Pipeline, Tool } from "@haystack-ai/core";
import { OpenAIChatGenerator } from "@haystack-ai/integrations";
const policies = {
"POL-1001": { policyId: "POL-1001", status: "Active", premiumDue: "2026-02-01" },
"POL-1002": { policyId: "POL-1002", status: "Lapsed", premiumDue: "2025-12-15" },
};
const getPolicyStatus = (policyId: string) =>
JSON.stringify(policies[policyId as keyof typeof policies] ?? `No policy found for ${policyId}`);
const policyStatusTool = new Tool({
name: "get_policy_status",
description:
"Look up insurance policy details by policy ID.",
parameters: {
type: "object",
properties: {
policyId: { type: "string" },
},
required: ["policyId"],
additionalProperties: false,
},
invoke: async ({ policyId }: { policyId?: string }) => getPolicyStatus(policyId ?? ""),
});
const generator = new OpenAIChatGenerator({
model: "gpt-4o-mini",
apiKey: process.env.OPENAI_API_KEY!,
tools: [policyStatusTool],
});
const pipeline = new Pipeline();
pipeline.addComponent("generator", generator);
const result = await pipeline.run({
generator: {
messages: [{ role: "user", content: "Is policy POL-1002 active?" }],
},
});
console.log(result);
Testing It
Run the script with npx tsx your-file.ts or compile it with tsc and execute the output with Node.js. If tool use is working, the model should call get_policy_status instead of inventing an answer.
Check that your console output includes both the tool invocation path and the final assistant response. If you ask about an unknown policy ID, you should see your fallback message returned through the tool rather than a hallucinated status.
Try at least three prompts:
- •“Is POL-1001 active?”
- •“When is POL-1002 due?”
- •“What’s the status of POL-9999?”
If all three behave consistently, your tool wiring is correct.
Next Steps
- •Add multiple tools and let the model choose between them based on intent
- •Replace mock data with real service calls to your CRM or claims system
- •Add guardrails around tool inputs so malformed IDs fail fast
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit