| name | langchain-core-workflow-b |
| description | Build LangChain agents with tool calling for autonomous task execution. Use when this capability is needed. |
| metadata | {"author":"flight505"} |
LangChain Core Workflow B: Agents & Tools
Overview
Build autonomous agents that use tools, make decisions, and execute multi-step tasks. Covers tool definition with Zod schemas, createToolCallingAgent, AgentExecutor, streaming agent output, and conversation memory.
Prerequisites
- Completed
langchain-core-workflow-a (chains)
npm install langchain @langchain/core @langchain/openai zod
Step 1: Define Tools (TypeScript)
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const calculator = tool(
async ({ expression }) => {
try {
const result = Function(`"use strict"; return (${expression})`)();
return String(result);
} catch (e) {
return `Error: invalid expression "${expression}"`;
}
},
{
name: "calculator",
description: "Evaluate a mathematical expression. Input: a math expression string.",
schema: z.object({
expression: z.string().describe("Math expression like '2 + 2' or '100 * 0.15'"),
}),
}
);
const weatherLookup = tool(
async ({ city }) => {
const data: Record<string, string> = {
"New York": "72F, sunny",
"London": "58F, cloudy",
"Tokyo": "80F, humid",
};
return data[city] ?? `No weather data for ${city}`;
},
{
name: "weather",
description: "Get current weather for a city.",
schema: z.object({
city: z.string().describe("City name"),
}),
}
);
const tools = [calculator, weatherLookup];
Step 2: Create Agent with AgentExecutor
import { ChatOpenAI } from "@langchain/openai";
import { createToolCallingAgent, AgentExecutor } from "langchain/agents";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
const llm = new ChatOpenAI({ model: "gpt-4o-mini" });
const prompt = ChatPromptTemplate.fromMessages([
["system", "You are a helpful assistant. Use tools when needed."],
new MessagesPlaceholder("chat_history"),
["human", "{input}"],
new MessagesPlaceholder("agent_scratchpad"),
]);
const agent = createToolCallingAgent({
llm,
tools,
prompt,
});
const executor = new AgentExecutor({
agent,
tools,
verbose: true,
maxIterations: 10,
returnIntermediateSteps: true,
});
Step 3: Run the Agent
const result = await executor.invoke({
input: "What's 25 * 4, and what's the weather in Tokyo?",
chat_history: [],
});
console.log(result.output);
console.log(result.intermediateSteps);
Step 4: Agent with Conversation Memory
import { ChatMessageHistory } from "@langchain/community/stores/message/in_memory";
import { RunnableWithMessageHistory } from "@langchain/core/runnables";
const messageHistory = new ChatMessageHistory();
const agentWithHistory = new RunnableWithMessageHistory({
runnable: executor,
getMessageHistory: (_sessionId) => messageHistory,
inputMessagesKey: "input",
historyMessagesKey: "chat_history",
});
await agentWithHistory.invoke(
{ input: "My name is Alice" },
{ configurable: { sessionId: "user-1" } }
);
const res = await agentWithHistory.invoke(
{ input: "What's my name?" },
{ configurable: { sessionId: "user-1" } }
);
console.log(res.output);
Step 5: Stream Agent Events
const eventStream = executor.streamEvents(
{ input: "Calculate 15% tip on $85", chat_history: [] },
{ version: "v2" }
);
for await (const event of eventStream) {
if (event.event === "on_chat_model_stream") {
process.stdout.write(event.data.chunk.content ?? "");
} else if (event.event === "on_tool_start") {
console.log(`\n[Calling tool: ${event.name}]`);
} else if (event.event === "on_tool_end") {
console.log(`[Tool result: ${event.data.output}]`);
}
}
Step 6: Bind Tools Directly (Without AgentExecutor)
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
const model = new ChatOpenAI({ model: "gpt-4o-mini" });
const modelWithTools = model.bindTools(tools);
const response = await modelWithTools.invoke([
new HumanMessage("What's 42 * 17?"),
]);
if (response.tool_calls && response.tool_calls.length > 0) {
for (const tc of response.tool_calls) {
console.log(`Tool: ${tc.name}, Args: ${JSON.stringify(tc.args)}`);
const toolResult = await tools
.find((t) => t.name === tc.name)!
.invoke(tc.args);
console.log(`Result: ${toolResult}`);
}
}
Python Equivalent
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.tools import tool
@tool
def calculator(expression: str) -> str:
"""Evaluate a math expression."""
return str(eval(expression))
tools = [calculator]
llm = ChatOpenAI(model="gpt-4o-mini")
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
MessagesPlaceholder("chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
result = executor.invoke({"input": "What is 25 * 4?", "chat_history": []})
Error Handling
| Error | Cause | Fix |
|---|
Max iterations reached | Agent stuck in loop | Increase maxIterations or improve system prompt |
Tool not found | Tool name mismatch | Verify tools array passed to both createToolCallingAgent and AgentExecutor |
Missing agent_scratchpad | Prompt missing placeholder | Add new MessagesPlaceholder("agent_scratchpad") |
| Tool execution error | Tool throws exception | Wrap tool body in try/catch, return error string |
Resources
Next Steps
Proceed to langchain-common-errors for debugging guidance.
Source: flight505/skill-forge — distributed by TomeVault.