My chatbot answers well from its knowledge base, but it has no idea how hot it is right now. Ask it “how many degrees is it?” and the model happily makes up a number. To give it live data, I reached for LLM tool calling.
How LLM tool calling works
With LLM tool calling, the model does not fetch the weather itself. You give it a tool, and it decides to call it when the question is about the weather. The tool queries a real-time API and returns structured values: a forecast text, the air temperature, and a heat index (WBGT), which measures how hot it feels with sun, humidity and wind, not just the air temperature.
A short instruction tells the model to answer only from those values, and never to invent them. The tool fetches fresh data on every call, with no cache.
const weatherTool = tool(
async () => fetchWeather(),
{
name: 'get_current_weather',
description: `Returns the live weather: forecast, air temperature and heat index.
Use it whenever the user asks about the weather, the temperature or the heat.
Never make these values up, always call this tool.`,
schema: z.object({}),
},
);Where the tool plugs into the RAG
The key point: the knowledge-base search is itself a tool. You give the model a single list of tools that holds both the document search (the RAG) and the weather tool. The model is the one that picks which to call: the search for a background question, the weather tool for a live one.
You bind the list to the model with bindTools. The model does not reply with text, it returns the name of the tool to call and its arguments. The framework runs the tool with that name, gets the result, and the model writes the final answer from it.
// The knowledge-base search is just one tool among others. const tools = [ retrievalTool, // the RAG: embed then vector search weatherTool, // the live data ]; // The model sees both and calls the one that fits the question. const result = await llm.bindTools(tools).invoke(question);
Answering happens in two steps: first you fetch the info, then the model writes the answer. The tool only touches the first step, it just changes where you go to fetch the info. For a background question you look it up in the knowledge base; for the weather you ask the API. The model always writes the answer the same way, from whatever was fetched.
The little trap: reading a value by its position
The API returns the temperature and the heat index in the same array. The naive version reads them by position: the first element, then the second. The day the API swaps their order, the chatbot reports the heat index as the air temperature, with no visible error.
The fix: find each value by its label, never by its position.
async function fetchWeather() {
const { data } = await axios.get(WEATHER_ENDPOINT);
const measures = data.elements.measures;
return {
airTemperature: pickMeasure(measures, false),
heatIndex: pickMeasure(measures, true),
};
}
// Find the right value by its label, not by its position in the array.
function pickMeasure(measures, wantHeatIndex) {
const measure = measures.find((m) => isHeatIndex(m) === wantHeatIndex);
if (!measure || measure.is_valid === false) {
return null;
}
return { value: Math.round(measure.value * 10) / 10, unit: measure.unit };
}
function isHeatIndex(measure) {
return `${measure.title}`.toLowerCase().includes('wbgt');
}And when a value is missing, honour the is_valid flag from the API: return null and let the model say “no data right now” instead of a made-up zero. That is all it takes for a first, reliable piece of LLM tool calling.
