<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://www.intellinode.ai/articles</id>
    <title>IntelliNode Articles</title>
    <updated>2026-09-30T12:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://www.intellinode.ai/articles"/>
    <subtitle>Guides and use cases for building AI agents with Intelli and IntelliNode.</subtitle>
    <icon>https://www.intellinode.ai/img/favicon.ico</icon>
    <rights>Copyright © 2026 IntelliNode.</rights>
    <entry>
        <title type="html"><![CDATA[How to Choose a Node.js LLM Library in 2026]]></title>
        <id>https://www.intellinode.ai/articles/nodejs-llm-library</id>
        <link href="https://www.intellinode.ai/articles/nodejs-llm-library"/>
        <updated>2026-09-30T12:00:00.000Z</updated>
        <summary type="html"><![CDATA[How to choose a Node.js LLM library in 2026: provider SDKs, the Vercel AI SDK, LangChain.js, Mastra and IntelliNode compared, with code and trade-offs.]]></summary>
        <content type="html"><![CDATA[<p>Choosing a Node.js LLM library in 2026 is mostly a question of which trade-offs your team can carry for the next two years. The realistic shortlist is the provider SDKs (<code>openai</code>, <code>@anthropic-ai/sdk</code>, <code>@google/genai</code>), the Vercel AI SDK, LangChain.js with LangGraph.js, Mastra, the OpenAI Agents SDK, and lighter multi-provider libraries such as IntelliNode.</p>
<p>Streaming chat inside a React or Next.js product points to the Vercel AI SDK. Stateful agent workflows with tracing point to LangGraph.js or Mastra. A vendor you will never change points to its own SDK. One small backend API across OpenAI, Claude, Gemini and local models, with a Python twin, is where IntelliNode fits. Below are the numbers, the code, and where each option falls short.</p>
<p><img decoding="async" loading="lazy" alt="Glowing prisms of different heights along an arc, one circled by a lens, like picking one Node.js LLM library" src="https://www.intellinode.ai/assets/images/nodejs-llm-library-34f19d6f3b021f57037fe612989d1437.jpg" width="1200" height="630" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-a-nodejs-llm-library-should-do-for-you">What a Node.js LLM library should do for you<a href="https://www.intellinode.ai/articles/nodejs-llm-library#what-a-nodejs-llm-library-should-do-for-you" class="hash-link" aria-label="Direct link to What a Node.js LLM library should do for you" title="Direct link to What a Node.js LLM library should do for you">​</a></h2>
<p>Every option can send a prompt and return text. The differences show up in the second month, when you add tools, need JSON you can store, or hit a 429 at peak traffic. A useful library gives you:</p>
<ul>
<li><strong>One interface across models</strong>, so switching vendor means changing a provider name and a key, not call sites.</li>
<li><strong>Tool calling with a bounded loop</strong>: the model asks for a function, your code runs it, and a step limit stops runaway loops.</li>
<li><strong>Structured output</strong> that follows a JSON Schema, using each provider's native mode.</li>
<li><strong>Streaming</strong> for chat interfaces, with cancellation.</li>
<li><strong>Errors you can branch on</strong>: timeouts, retries with backoff, and the HTTP status on the error, so a 429 can fall back to another model while a 401 fails loudly.</li>
</ul>
<p>Then the business questions: how much code is tied to one vendor's format, whether a workload can move to a cheaper model without a sprint of refactoring, how long until the first feature ships, and who maintains it when its champion changes teams. With Python services too, ask whether prompts and tool schemas can be shared or will live twice.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-options-at-a-glance">The options at a glance<a href="https://www.intellinode.ai/articles/nodejs-llm-library#the-options-at-a-glance" class="hash-link" aria-label="Direct link to The options at a glance" title="Direct link to The options at a glance">​</a></h2>
<p>The download figures come from the npm registry API for the week of 2026-09-22 to 2026-09-28 (<code>api.npmjs.org/downloads/point/last-week/&lt;package&gt;</code>), pulled on 2026-09-30. They include CI installs and transitive dependencies, so read them as ecosystem size, not quality.</p>
<table><thead><tr><th>Option</th><th>npm package</th><th>Weekly downloads</th><th>Best when</th><th>Watch out for</th></tr></thead><tbody><tr><td>Provider SDKs</td><td><code>openai</code>, <code>@anthropic-ai/sdk</code>, <code>@google/genai</code></td><td>46.2M, 47.9M, 27.3M</td><td>One vendor, and you want its new features on day one</td><td>Each vendor has its own message, tool and stream format</td></tr><tr><td>Vercel AI SDK</td><td><code>ai</code></td><td>31.6M</td><td>Web apps that stream chat into a React, Next.js, Vue or Svelte UI</td><td>Backend-only services use a fraction of it</td></tr><tr><td>LangChain.js and LangGraph.js</td><td><code>langchain</code>, <code>@langchain/langgraph</code></td><td>3.40M, 4.02M</td><td>Many integrations, stateful graph agents, LangSmith tracing</td><td>More layers to learn and debug</td></tr><tr><td>Mastra</td><td><code>@mastra/core</code></td><td>2.07M</td><td>A TypeScript agent framework with workflows, memory and evals in one place</td><td>Framework conventions your team has to adopt</td></tr><tr><td>OpenAI Agents SDK</td><td><code>@openai/agents</code></td><td>2.19M</td><td>Several agents with handoffs, guardrails and tracing</td><td>Other providers go through an adapter; Node.js 22 or later</td></tr><tr><td>IntelliNode</td><td><code>intellinode</code></td><td>405</td><td>One small API across cloud and local models, plus MCP and a Python twin</td><td>Small community, no UI hooks, no graph engine</td></tr></tbody></table>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="provider-sdks-the-baseline">Provider SDKs: the baseline<a href="https://www.intellinode.ai/articles/nodejs-llm-library#provider-sdks-the-baseline" class="hash-link" aria-label="Direct link to Provider SDKs: the baseline" title="Direct link to Provider SDKs: the baseline">​</a></h3>
<p>The official SDKs are typed, maintained by the vendor, and get new features first. If you use one model family and will not change, they are the right call.</p>
<p>The cost shows up later. Each vendor has its own message format, tool schema and streaming events, so a tool loop written against <code>openai</code> has to be rewritten to run on Claude, tests included. Teams usually find this out the week procurement asks for a price comparison.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="vercel-ai-sdk">Vercel AI SDK<a href="https://www.intellinode.ai/articles/nodejs-llm-library#vercel-ai-sdk" class="hash-link" aria-label="Direct link to Vercel AI SDK" title="Direct link to Vercel AI SDK">​</a></h3>
<p>The <a href="https://ai-sdk.dev/docs" target="_blank" rel="noopener noreferrer">AI SDK</a> is a TypeScript toolkit for AI apps and agents. AI SDK Core gives one API for text, structured objects, tool calls and agents. <a href="https://ai-sdk.dev/docs/ai-sdk-ui/overview" target="_blank" rel="noopener noreferrer">AI SDK UI</a> adds <code>useChat</code>, <code>useCompletion</code> and <code>useObject</code> hooks for React, Svelte, Vue.js and Angular. Providers ship as packages such as <code>@ai-sdk/openai</code>, and Ollama is covered by a community provider.</p>
<p>It is the strongest choice when your product streams chat into a web UI. Check two things: a backend-only service uses just the Core half, and a Python team next to you will need a different library. Teams looking for a Vercel AI SDK alternative are usually in one of those two spots.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="langchainjs-and-langgraphjs">LangChain.js and LangGraph.js<a href="https://www.intellinode.ai/articles/nodejs-llm-library#langchainjs-and-langgraphjs" class="hash-link" aria-label="Direct link to LangChain.js and LangGraph.js" title="Direct link to LangChain.js and LangGraph.js">​</a></h3>
<p><a href="https://github.com/langchain-ai/langchainjs" target="_blank" rel="noopener noreferrer">LangChain.js</a> has a very large integration catalog: model providers, tools, vector stores and retrievers. LangGraph.js adds agents and controllable workflows built as graphs, and LangSmith covers testing and monitoring. It runs in Node.js, browsers, Deno, Bun and Cloudflare Workers.</p>
<p>Pick it for many integrations, stateful multi-step agents, or a tracing product your ops team already uses. The price is abstraction: for one prompt and two tools, there are several layers between your code and the HTTP request, and that is where debugging time goes.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="mastra-and-the-openai-agents-sdk">Mastra and the OpenAI Agents SDK<a href="https://www.intellinode.ai/articles/nodejs-llm-library#mastra-and-the-openai-agents-sdk" class="hash-link" aria-label="Direct link to Mastra and the OpenAI Agents SDK" title="Direct link to Mastra and the OpenAI Agents SDK">​</a></h3>
<p><a href="https://github.com/mastra-ai/mastra" target="_blank" rel="noopener noreferrer">Mastra</a> is a TypeScript framework with agents, a graph-based workflow engine (<code>.then()</code>, <code>.branch()</code>, <code>.parallel()</code>), memory, human-in-the-loop suspend and resume, MCP, evals, observability and routing to 40+ providers. The <a href="https://github.com/openai/openai-agents-js" target="_blank" rel="noopener noreferrer">OpenAI Agents SDK</a> centers on agents, handoffs, guardrails, sessions and tracing, plus realtime voice agents. It calls itself provider agnostic and reaches other models through an AI SDK integration.</p>
<p>A TypeScript AI agent framework earns its structure when you have several agents, approval steps, and evals that gate a release. For a summarize endpoint, it is more framework than the job needs.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="where-intellinode-fits">Where IntelliNode fits<a href="https://www.intellinode.ai/articles/nodejs-llm-library#where-intellinode-fits" class="hash-link" aria-label="Direct link to Where IntelliNode fits" title="Direct link to Where IntelliNode fits">​</a></h2>
<p>IntelliNode (<code>intellinode</code> on npm) sits at the light end: a unified LLM SDK, not a framework. One <code>Chatbot</code> class covers OpenAI, Anthropic, Gemini, Mistral, Cohere, NVIDIA and vLLM, plus presets for OpenAI-compatible services (OpenRouter, Groq, DeepSeek, xAI, Together, Ollama, LM Studio). It adds a tool loop, schema-based JSON, streaming, MCP and TypeScript types.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="one-call-shape-for-openai-claude-and-a-local-model">One call shape for OpenAI, Claude and a local model<a href="https://www.intellinode.ai/articles/nodejs-llm-library#one-call-shape-for-openai-claude-and-a-local-model" class="hash-link" aria-label="Direct link to One call shape for OpenAI, Claude and a local model" title="Direct link to One call shape for OpenAI, Claude and a local model">​</a></h3>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">ChatGPTInput</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">AnthropicInput</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">OpenAICompatibleInput</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> system </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'You are a billing support assistant. Answer in two sentences.'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> question </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'A customer was charged twice this month. What should we check first?'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ask</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">bot</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> input</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addUserMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">answer</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">chat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// chat() resolves to an array of replies</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> answer</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> openai </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OPENAI_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'openai'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic">// gpt-5.5, Responses API</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> claude </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ANTHROPIC_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'anthropic'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// claude-sonnet-5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> local </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'qwen2.5:0.5b'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// no key, runs on your machine</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ask</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">openai</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">ChatGPTInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">system</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">effort</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'low'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ask</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">claude</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">AnthropicInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">system</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">maxTokens</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4096</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// Claude 5 thinking counts toward maxTokens</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ask</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">local</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">OpenAICompatibleInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">system</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Only the key, the provider name and the input class change. Claude 5 counts thinking toward <code>maxTokens</code>, so the 2048 default is raised. The local line needs no key, which makes it a free way to test the plumbing in CI. <code>Chatbot.createInput(bot.provider, system)</code> can pick the input class for you, as the seam example below shows.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="a-tool-calling-agent-in-about-20-lines">A tool calling agent in about 20 lines<a href="https://www.intellinode.ai/articles/nodejs-llm-library#a-tool-calling-agent-in-about-20-lines" class="hash-link" aria-label="Direct link to A tool calling agent in about 20 lines" title="Direct link to A tool calling agent in about 20 lines">​</a></h3>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">AnthropicInput</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> tools </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'get_invoice'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Look up an invoice by id and return its amount and payment status'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">parameters</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'object'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">properties</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">invoiceId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'string'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">required</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'invoiceId'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">handler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> invoiceId </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> invoiceId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">amount</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">129</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">currency</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'USD'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">status</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'charged twice'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ANTHROPIC_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'anthropic'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> input </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">AnthropicInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'You are a billing assistant. Always call get_invoice before answering.'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">maxTokens</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4096</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addUserMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'What is the status of invoice INV-2041?'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> steps </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">runTools</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tools</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">maxSteps</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">steps</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">step</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> step</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// [ 'get_invoice' ]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p><code>runTools</code> calls the model, runs every tool it asks for, sends the results back, and repeats until the model answers in text. <code>maxSteps</code> caps the tool rounds and the call throws after that, so a confused model cannot loop forever. The same call works with <code>ChatGPTInput</code>, <code>GeminiInput</code> and <code>OpenAICompatibleInput</code>. It appends tool turns to the input, so build a fresh input per request.</p>
<p>On local models: with <code>qwen2.5:0.5b</code> on Ollama, this agent called the tool in 3 of 6 runs and answered without it in the others. Use a capable model for agents. The <a href="https://www.intellinode.ai/docs/npm/chatbot/tool-calling">tool calling docs</a> cover handler errors and handling tool calls yourself, and <a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs">Build AI Agents in Node.js With Tools and MCP</a> goes further.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="json-that-matches-a-schema">JSON that matches a schema<a href="https://www.intellinode.ai/articles/nodejs-llm-library#json-that-matches-a-schema" class="hash-link" aria-label="Direct link to JSON that matches a schema" title="Direct link to JSON that matches a schema">​</a></h3>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">ChatGPTInput</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> ticketSchema </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'object'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">properties</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">category</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'string'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">enum</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'billing'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'bug'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'account'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'other'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">urgent</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'boolean'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">summary</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'string'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">required</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'category'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'urgent'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'summary'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OPENAI_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'openai'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> input </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">ChatGPTInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'Classify the support ticket.'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">responseSchema</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ticketSchema</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">strictSchema</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addUserMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'I was charged twice for my March invoice and need a refund today.'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> ticket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">chatJson</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// a parsed object, not a string</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ticket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">category</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ticket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">urgent</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ticket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">summary</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>With <code>responseSchema</code> set, <code>chatJson</code> uses each provider's native structured output (OpenAI, Anthropic, Gemini, and <code>response_format</code> on compatible providers) and returns a parsed object. <code>strictSchema: true</code> turns on OpenAI strict mode.</p>
<p><code>chatJson</code> itself only confirms the reply is an object, or an array if the schema says so. Required fields and types come from the provider's structured output mode, so validate business data with a validator such as ajv before it reaches your database. A schema also guarantees shape, not truth: on the local 0.5B model, the double-charge ticket sometimes came back as <code>account</code> instead of <code>billing</code>. More in the <a href="https://www.intellinode.ai/docs/npm/chatbot/structured-output">structured output guide</a>.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="streaming-mcp-and-the-browser">Streaming, MCP and the browser<a href="https://www.intellinode.ai/articles/nodejs-llm-library#streaming-mcp-and-the-browser" class="hash-link" aria-label="Direct link to Streaming, MCP and the browser" title="Direct link to Streaming, MCP and the browser">​</a></h3>
<ul>
<li><strong>Streaming:</strong> <code>for await (const chunk of bot.stream(input))</code> works for OpenAI, Anthropic, Mistral, Cohere, NVIDIA, vLLM and every OpenAI-compatible provider, local Ollama included. Gemini, Replicate and SageMaker need <code>chat()</code>, and tool calls are not surfaced through <code>stream()</code>.</li>
<li><strong>MCP:</strong> pass an <code>MCPClient</code> straight into <code>runTools</code> and the model uses that server's tools. <code>MCPServer</code> exposes your own functions over stdio or Streamable HTTP, and <code>npx -y intellinode mcp</code> gives Claude Code, Cursor or VS Code 15 cross-provider tools. See <a href="https://www.intellinode.ai/docs/npm/mcp/get-started">MCP in Node.js</a>.</li>
<li><strong>Browser:</strong> a script-tag bundle exposes a global <code>IntelliNode</code>, fine for local models and demos but not for paid keys.</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="what-intellinode-does-not-do">What IntelliNode does not do<a href="https://www.intellinode.ai/articles/nodejs-llm-library#what-intellinode-does-not-do" class="hash-link" aria-label="Direct link to What IntelliNode does not do" title="Direct link to What IntelliNode does not do">​</a></h3>
<ul>
<li><strong>No UI hooks.</strong> For <code>useChat</code>-style state in React, the AI SDK is the better fit.</li>
<li><strong>No graph workflow engine, memory store or tracing UI.</strong> Orchestration is plain JavaScript: fine for three steps, tedious for thirty.</li>
<li><strong>No tool calls on Cohere.</strong> <code>runTools</code> there returns the model's text with zero steps and no error.</li>
<li><strong>Node-only pieces.</strong> <code>CodingAgent</code> and <code>MCPServer</code> are not in the browser bundle.</li>
<li><strong>A much smaller community.</strong> 405 downloads that week against millions for the leaders means fewer forum answers and more reading of the source.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-to-choose-a-nodejs-llm-library-a-decision-checklist">How to choose a Node.js LLM library: a decision checklist<a href="https://www.intellinode.ai/articles/nodejs-llm-library#how-to-choose-a-nodejs-llm-library-a-decision-checklist" class="hash-link" aria-label="Direct link to How to choose a Node.js LLM library: a decision checklist" title="Direct link to How to choose a Node.js LLM library: a decision checklist">​</a></h2>
<p>Go through these in order. The first yes usually decides it.</p>
<ol>
<li><strong>Do you stream chat into a React, Next.js, Vue, Svelte or Angular UI?</strong> Start with the Vercel AI SDK.</li>
<li><strong>Is one vendor fixed for the next year</strong> by contract, committed spend or a compliance review? Use its SDK and keep the calls behind one module.</li>
<li><strong>Do you need stateful multi-step workflows, approval pauses and tracing?</strong> LangGraph.js or Mastra, or the OpenAI Agents SDK for several cooperating agents on OpenAI models.</li>
<li><strong>Must some data stay on your own hardware?</strong> Pick a library where local models share the cloud code path, like IntelliNode's <code>ollama</code> and <code>lmstudio</code> presets.</li>
<li><strong>Is there a Python side?</strong> Look for a Python counterpart with the same concepts, so prompts and tool schemas carry over. IntelliNode's is Intelli, with the same <code>Chatbot</code> idea and JSON Schema <a href="https://www.intellinode.ai/docs/python/chatbot/tool-calling">tool calling</a>, though there you run the tool loop yourself.</li>
<li><strong>Who maintains it?</strong> Two backend engineers do better with fewer abstractions. A platform team serving ten product teams can justify a framework.</li>
</ol>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="a-scenario-most-saas-teams-will-recognize">A scenario most SaaS teams will recognize<a href="https://www.intellinode.ai/articles/nodejs-llm-library#a-scenario-most-saas-teams-will-recognize" class="hash-link" aria-label="Direct link to A scenario most SaaS teams will recognize" title="Direct link to A scenario most SaaS teams will recognize">​</a></h3>
<p>A 40-person B2B SaaS company runs a Node.js API and a two-person Python data team. Support wants AI ticket triage: classify, pull the invoice, draft a reply. Then two constraints land: an enterprise contract says ticket text may not go to an outside model provider, and finance wants cost per ticket tracked before the feature reaches every tenant.</p>
<p>A provider SDK ships the first version fastest, but the enterprise tenant then needs a second code path. LangGraph.js or Mastra would work, though a three-step flow is small for a graph. The AI SDK fits if the agent assist panel lives in their Next.js dashboard. With IntelliNode the team keeps one <code>createBot()</code> function: Claude for most tenants, Ollama for the enterprise one, the same <code>chatJson</code> schema for both, and prompts the data team can reuse in Python with Intelli. Several options would work. What decided it was the on-premises tenant and the size of the team.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="switching-later-without-a-rewrite">Switching later without a rewrite<a href="https://www.intellinode.ai/articles/nodejs-llm-library#switching-later-without-a-rewrite" class="hash-link" aria-label="Direct link to Switching later without a rewrite" title="Direct link to Switching later without a rewrite">​</a></h2>
<p>Whatever you pick, keep one file that knows the vendor. The rest of the codebase calls <code>answer()</code> and never imports a provider package.</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// llm.js: the only file that knows which vendor serves a request</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">KEYS</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">openai</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'OPENAI_API_KEY'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">anthropic</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ANTHROPIC_API_KEY'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">gemini</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'GEMINI_API_KEY'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">REQUEST</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">timeout</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">30000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">retries</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createBot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">provider </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">LLM_PROVIDER</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'openai'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">provider </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token constant" style="color:#36acaa">REQUEST</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OLLAMA_MODEL</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'qwen2.5:0.5b'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">[</span><span class="token constant" style="color:#36acaa">KEYS</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">provider</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> provider</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">REQUEST</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">answer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">system</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">createBot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> input </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">provider</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> system</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">maxTokens</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4096</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addUserMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">reply</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">chat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> reply</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> createBot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> answer </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Moving the app to Gemini, or one tenant to a local model, is now an environment variable. <code>timeout</code> applies per attempt, retries back off on 429, most 5xx responses and network errors, and the thrown error carries <code>status</code> (or <code>code</code> on a timeout), so fallback logic can tell a rate limit from a bad key.</p>
<p>OpenAI-compatible endpoints widen the choice without new code. <code>new Chatbot(process.env.OPENROUTER_API_KEY, 'openrouter')</code> reaches the models OpenRouter hosts, and the groq, deepseek, xai, together, ollama and lmstudio presets work the same way (all except deepseek need a <code>model</code> in the options). Details in <a href="https://www.intellinode.ai/docs/npm/chatbot/openai-compatible">OpenAI-compatible providers</a>.</p>
<p>Before you move traffic to a cheaper model, measure it on your own prompts:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">LLMEvaluation</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// the embedding provider that scores the answers: openai, cohere, gemini, replicate or openrouter</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> evaluation </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">LLMEvaluation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OPENAI_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'openai'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> results </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> evaluation</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">compareModels</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'A customer was charged twice. What should support do first?'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'Confirm the duplicate charge with the payment provider, refund it, and tell the customer when the money will arrive.'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">apiKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ANTHROPIC_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">provider</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'anthropic'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'chat'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'claude-sonnet-5'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">maxTokens</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2048</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">apiKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OPENAI_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">provider</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'openai'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'chat'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'gpt-5.5'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">apiKey</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">provider</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'chat'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'qwen2.5:0.5b'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">maxTokens</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">300</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">lane</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> runs</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">of</span><span class="token plain"> </span><span class="token known-class-name class-name">Object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">entries</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">results</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">lane </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'lookup'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">lane</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> runs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">stop_reason</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> runs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">score_cosine_similarity</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p><code>compareModels</code> sends the same prompt to each lane, embeds every answer, and scores it against your reference answers (cosine similarity, Euclidean and Manhattan distance). A failing lane is recorded with <code>stop_reason: 'error'</code> instead of stopping the run. Treat the score as a measure of semantic closeness rather than correctness, and run it over 50 real tickets before you trust it. See <a href="https://www.intellinode.ai/docs/npm/functions/llm-evaluation">LLM evaluation</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="faq">FAQ<a href="https://www.intellinode.ai/articles/nodejs-llm-library#faq" class="hash-link" aria-label="Direct link to FAQ" title="Direct link to FAQ">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="what-is-the-best-nodejs-llm-library-in-2026">What is the best Node.js LLM library in 2026?<a href="https://www.intellinode.ai/articles/nodejs-llm-library#what-is-the-best-nodejs-llm-library-in-2026" class="hash-link" aria-label="Direct link to What is the best Node.js LLM library in 2026?" title="Direct link to What is the best Node.js LLM library in 2026?">​</a></h3>
<p>There is no single winner. The Vercel AI SDK leads for web apps with streaming UI, LangGraph.js and Mastra for stateful agent workflows, provider SDKs for single-vendor apps, and IntelliNode for a small multi-provider backend with local models. The checklist above usually settles it.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="is-intellinode-a-langchainjs-alternative">Is IntelliNode a LangChain.js alternative?<a href="https://www.intellinode.ai/articles/nodejs-llm-library#is-intellinode-a-langchainjs-alternative" class="hash-link" aria-label="Direct link to Is IntelliNode a LangChain.js alternative?" title="Direct link to Is IntelliNode a LangChain.js alternative?">​</a></h3>
<p>For many backend features, yes: chat across providers, a tool loop, schema-based JSON, streaming and MCP, with fewer layers. It does not replace LangChain's integration catalog, LangGraph's stateful graphs or LangSmith tracing, so teams that depend on those should stay.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="does-intellinode-support-typescript">Does IntelliNode support TypeScript?<a href="https://www.intellinode.ai/articles/nodejs-llm-library#does-intellinode-support-typescript" class="hash-link" aria-label="Direct link to Does IntelliNode support TypeScript?" title="Direct link to Does IntelliNode support TypeScript?">​</a></h3>
<p>Yes. The package ships <code>index.d.ts</code> covering <code>Chatbot</code>, every input class, <code>runTools</code>, <code>chatJson</code> and the MCP classes, so no <code>@types</code> package is needed. The build is CommonJS and ESM named imports work too. It is not TypeScript-first the way Mastra is.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="can-a-javascript-llm-library-run-in-the-browser">Can a JavaScript LLM library run in the browser?<a href="https://www.intellinode.ai/articles/nodejs-llm-library#can-a-javascript-llm-library-run-in-the-browser" class="hash-link" aria-label="Direct link to Can a JavaScript LLM library run in the browser?" title="Direct link to Can a JavaScript LLM library run in the browser?">​</a></h3>
<p>Several can. LangChain.js lists browsers among its environments, and IntelliNode ships a script tag bundle with <code>Chatbot</code>, <code>runTools</code>, <code>chatJson</code>, <code>stream</code> and <code>MCPClient</code> over HTTP. The hard part is the key: anything in the page is public, so paid keys belong on a server.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="does-intellinode-work-with-openrouter-groq-and-ollama">Does IntelliNode work with OpenRouter, Groq and Ollama?<a href="https://www.intellinode.ai/articles/nodejs-llm-library#does-intellinode-work-with-openrouter-groq-and-ollama" class="hash-link" aria-label="Direct link to Does IntelliNode work with OpenRouter, Groq and Ollama?" title="Direct link to Does IntelliNode work with OpenRouter, Groq and Ollama?">​</a></h3>
<p>Yes. They are built-in presets of the OpenAI-compatible provider. <code>openrouter</code> defaults to <code>openai/gpt-5.5</code>, while <code>groq</code> and <code>ollama</code> need a model name. Local presets need no key, and <code>listModels()</code> returns the models the server offers.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="try-it-on-your-own-prompts">Try it on your own prompts<a href="https://www.intellinode.ai/articles/nodejs-llm-library#try-it-on-your-own-prompts" class="hash-link" aria-label="Direct link to Try it on your own prompts" title="Direct link to Try it on your own prompts">​</a></h2>
<p>Start with the local model, since it needs no key and no budget approval:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm i intellinode</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ollama pull qwen2.5:0.5b</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Run the local line from the first example, then point <code>createBot()</code> at your hosted provider and try a real prompt from your backlog. The <a href="https://www.intellinode.ai/docs/npm/get-started/installation">installation guide</a> covers Node.js versions, TypeScript imports and the browser bundle.</p>]]></content>
        <author>
            <name>IntelliNode Team</name>
            <uri>https://github.com/intelligentnode</uri>
        </author>
        <category label="Node.js" term="Node.js"/>
        <category label="LLM Libraries" term="LLM Libraries"/>
        <category label="Comparison" term="Comparison"/>
        <category label="TypeScript" term="TypeScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Build AI Agents in Node.js With Tools and MCP]]></title>
        <id>https://www.intellinode.ai/articles/build-ai-agents-nodejs</id>
        <link href="https://www.intellinode.ai/articles/build-ai-agents-nodejs"/>
        <updated>2026-09-30T11:00:00.000Z</updated>
        <summary type="html"><![CDATA[Build AI agents in Node.js with a tool calling loop, MCP servers and a local Ollama model, then move to OpenAI, Claude or Gemini by changing one line.]]></summary>
        <content type="html"><![CDATA[<p>To build AI agents in Node.js you need three parts: tools the model can call, a loop that runs those tools and feeds the results back, and a model that is good at deciding when to call them. Memory stores, graphs and dashboards are optional, and most first agents skip them.</p>
<p>This guide builds one support agent end to end with the <code>intellinode</code> npm package. It answers order questions with a plain JavaScript tool, gets the same tool from an MCP server, waits for a person before it issues a refund, and falls back to another provider when one is down. You develop on a local Ollama model with no API key, then move to OpenAI, Claude or Gemini by changing one line.</p>
<p>Every snippet was run on Node.js 20 against Ollama with <code>qwen2.5:0.5b</code>. Where the tiny model got things wrong, we say so: those failures are worth seeing before you ship.</p>
<p><img decoding="async" loading="lazy" alt="Glowing hub with thin spokes to round and square endpoints, like one Node.js AI agent wired to many tools" src="https://www.intellinode.ai/assets/images/build-ai-agents-nodejs-d66ce9321b409aeb10bf5e59dc26a4af.jpg" width="1200" height="630" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-you-need-to-build-ai-agents-in-nodejs">What you need to build AI agents in Node.js<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#what-you-need-to-build-ai-agents-in-nodejs" class="hash-link" aria-label="Direct link to What you need to build AI agents in Node.js" title="Direct link to What you need to build AI agents in Node.js">​</a></h2>
<p>Picture an online store with a small support team. Much of the inbox is some version of "where is my order", and every answer means opening the order system and copying a status into a reply. That is a good first agent: the data lives in your system, the question is narrow, and a wrong answer is easy to catch. Refunds are different. They move money, so a person approves them.</p>
<p>You will build it in small files: tools, a loop, a provider switch, an MCP server and client, a refund approval step and a production wrapper. This follows Anthropic's advice in <a href="https://www.anthropic.com/engineering/building-effective-agents" target="_blank" rel="noopener noreferrer">Building effective agents</a>: start with direct LLM calls and add layers only when the simple version falls short.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="install-intellinode-and-pick-a-model">Install IntelliNode and pick a model<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#install-intellinode-and-pick-a-model" class="hash-link" aria-label="Direct link to Install IntelliNode and pick a model" title="Direct link to Install IntelliNode and pick a model">​</a></h2>
<p>You need Node.js 18 or newer. The package has three runtime dependencies and its own TypeScript types (see the <a href="https://www.intellinode.ai/docs/npm/get-started/installation">installation page</a>).</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm i intellinode</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># local models for development, no key needed</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ollama pull qwen2.5:0.5b   # about 400 MB, fine for wiring things up</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ollama pull qwen3:8b       # 5.2 GB, a better fit for real tool use</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>The 0.5B model runs on any laptop, which is why we tested with it. It is also poor at deciding when to call a tool, so you meet the failure modes early. For anything a customer reads, use a larger local model such as <a href="https://ollama.com/library/qwen3" target="_blank" rel="noopener noreferrer">qwen3</a>, which Ollama lists with tool support, or a cloud model.</p>
<p>Cloud keys go in environment variables on your server: <code>OPENAI_API_KEY</code>, <code>ANTHROPIC_API_KEY</code> and <code>GEMINI_API_KEY</code>. Never put them in a browser bundle, where anyone can read them.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="define-the-tools-the-model-can-call">Define the tools the model can call<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#define-the-tools-the-model-can-call" class="hash-link" aria-label="Direct link to Define the tools the model can call" title="Direct link to Define the tools the model can call">​</a></h2>
<p>A tool is a name, a description, a JSON Schema for its arguments and a handler. The model never sees the handler. The name, description and schema alone decide whether it calls the tool and with what arguments.</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// order-tools.js: plain functions the agent can call</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">ORDERS</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">'A-1001'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">status</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'shipped'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">carrier</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'DHL'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">eta</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'2026-10-02'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string-property property" style="color:#36acaa">'A-1002'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">status</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'processing'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">carrier</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">eta</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'2026-10-06'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> tools </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'get_order_status'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'Look up the shipping status, carrier and expected delivery date of a customer order. '</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">'Call it whenever the customer gives an order id such as A-1001.'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">parameters</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'object'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">properties</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">orderId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'string'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'The order id, for example A-1001'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">required</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'orderId'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">handler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> orderId </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> order </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">ORDERS</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">orderId</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">order</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">No order found with id </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">orderId</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> orderId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">order </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> tools </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>The description says when to call the tool, not only what it does, and the <code>orderId</code> property shows the id format. The handler throws on an unknown id instead of returning <code>null</code>, because a thrown error reaches the model as text, so it can tell the customer instead of guessing.</p>
<p>Wording matters more than you would expect. With <code>qwen2.5:0.5b</code>, "What is the status of order A-1001?" triggered the tool in 16 of 19 runs. "Where is my order A-1001?" triggered it in 1 of 15 runs across the prompts we tried, and most other runs asked the customer for the id they had just typed. Test the phrasings your customers actually use on the model you plan to ship.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="run-the-llm-tool-calling-loop-with-runtools">Run the LLM tool calling loop with runTools<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#run-the-llm-tool-calling-loop-with-runtools" class="hash-link" aria-label="Direct link to Run the LLM tool calling loop with runTools" title="Direct link to Run the LLM tool calling loop with runTools">​</a></h2>
<p>By hand, the loop is: send the messages and tool definitions, run the tools the reply asks for, append calls and results in the provider's format, and repeat until the model answers with text. Every provider formats tool calls differently, and the loop needs a step cap. <code>runTools</code> handles both.</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// agent.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">OpenAICompatibleInput</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> tools </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./order-tools'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">SYSTEM</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'You are a support agent for an online store. '</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'Always call get_order_status before you answer a question about an order. Keep answers short.'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OLLAMA_MODEL</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'qwen2.5:0.5b'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> input </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">OpenAICompatibleInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token constant" style="color:#36acaa">SYSTEM</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addUserMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'What is the status of order A-1001?'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> steps</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> toolCalls </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">runTools</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tools</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">maxSteps</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">onToolCall</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">name</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'calling'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">onToolResult</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">name</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> result</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> isError</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'result'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> isError </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'error'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ok'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">toolCalls</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> steps</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">step</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> step</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// calling get_order_status { orderId: 'A-1001' }</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// result get_order_status ok</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// The order A-1001 has been shipped. The carrier is DHL and the expected delivery date is 2026-10-02.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 1 [ 'get_order_status' ]</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>What to know about the result:</p>
<ul>
<li><code>text</code> is the answer, <code>steps</code> lists each executed tool with its arguments, result and <code>isError</code>, and <code>toolCalls</code> is the count.</li>
<li><code>maxSteps</code> counts tool rounds (default 5), and the model gets one more call after the last round. So <code>maxSteps: 4</code> means at most five model calls per question, which is your cost ceiling per request. If the model still wants tools, <code>runTools</code> throws "runTools stopped after 4 tool rounds without a final answer".</li>
<li>A throwing handler does not crash the loop. Asked about order A-9999, the step came back as <code>Error: No order found with id A-9999</code> with <code>isError: true</code>, and the model told the customer it could not find it.</li>
<li><code>onToolCall</code> and <code>onToolResult</code> are where logging and audit records go.</li>
</ul>
<p>One caveat: <code>runTools</code> appends the tool calls and results to the input you pass in. Create a fresh input per customer request, or one customer's order data leaks into the next conversation.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="typescript">TypeScript<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#typescript" class="hash-link" aria-label="Direct link to TypeScript" title="Direct link to TypeScript">​</a></h3>
<p>Top-level await needs an ES module, so use an <code>.mts</code> file or set <code>"type": "module"</code> in <code>package.json</code>.</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// agent.mts</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Chatbot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> OpenAICompatibleInput</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">RunToolsResult</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OLLAMA_MODEL</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'qwen2.5:0.5b'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> input </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">OpenAICompatibleInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'You are a support agent for an online store. Always call get_order_status before you answer a question about an order.'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">addUserMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'What is the status of order A-1001?'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> result</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> RunToolsResult </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">runTools</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'get_order_status'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Look up the shipping status of an order by id'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  parameters</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'object'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> properties</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> orderId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'string'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> required</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'orderId'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">handler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> orderId </span><span class="token punctuation" style="color:#393A34">}</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> orderId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> orderId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> status</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'shipped'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> eta</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'2026-10-02'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> maxSteps</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">toolCalls</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="switch-from-ollama-to-openai-claude-or-gemini">Switch from Ollama to OpenAI, Claude or Gemini<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#switch-from-ollama-to-openai-claude-or-gemini" class="hash-link" aria-label="Direct link to Switch from Ollama to OpenAI, Claude or Gemini" title="Direct link to Switch from Ollama to OpenAI, Claude or Gemini">​</a></h2>
<p>The agent above is tied to Ollama by one line and one input class. Put both behind a small factory and the provider becomes a config value:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// switch.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> tools </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./order-tools'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">SYSTEM</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'You are a support agent for an online store. '</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'Always call get_order_status before you answer a question about an order. Keep answers short.'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> bots </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">ollama</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OLLAMA_MODEL</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'qwen2.5:0.5b'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">openai</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OPENAI_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'openai'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">anthropic</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ANTHROPIC_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'anthropic'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">gemini</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">GEMINI_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'gemini'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">answer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> bots</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">LLM_PROVIDER</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> input </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">provider</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">SYSTEM</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// ChatGPTInput, AnthropicInput, GeminiInput...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addUserMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> text </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">runTools</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tools</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">maxSteps</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">answer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'What is the status of order A-1002?'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">then</span><span class="token punctuation" style="color:#393A34">(</span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">log</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p><code>Chatbot.createInput</code> picks the matching input class. The same tool definition is converted for each API, and so are the calls and results: function call items for the OpenAI Responses API, <code>tool_use</code> blocks for Claude and <code>functionCall</code> parts for Gemini. <code>LLM_PROVIDER=anthropic node switch.js</code> is the whole migration.</p>
<p>The current defaults are <code>gpt-5.5</code>, <code>claude-sonnet-5</code> and <code>gemini-3.6-flash</code>. Pin a model in production with <code>model</code> in the third argument of <code>createInput</code>, so a library upgrade never changes your bill. On Claude, <code>maxTokens</code> defaults to 2048 and thinking counts toward it, so raise it if answers get cut.</p>
<p>The business reason is lock-in. Most Node.js tutorials on OpenAI function calling are written against one vendor's SDK, so moving means rewriting the loop. Here the loop, tools and tests stay put, and you can price one workload on three vendors in an afternoon. The same code reaches Groq, OpenRouter or LM Studio through the <a href="https://www.intellinode.ai/docs/npm/chatbot/openai-compatible">OpenAI-compatible providers</a>. One exception: Cohere's input never sends tools, so <code>runTools</code> on Cohere returns plain text with zero steps and no error.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="use-mcp-servers-as-agent-tools-in-nodejs">Use MCP servers as agent tools in Node.js<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#use-mcp-servers-as-agent-tools-in-nodejs" class="hash-link" aria-label="Direct link to Use MCP servers as agent tools in Node.js" title="Direct link to Use MCP servers as agent tools in Node.js">​</a></h2>
<p>Plain functions work while the agent and tools share a repo. Once another team owns the order service, or Claude Code and Cursor should use the same tools, put them behind an MCP server. <code>MCPServer</code> takes almost the same shape, with <code>inputSchema</code> in place of <code>parameters</code>:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// order-server.js: the same tools, served over MCP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">MCPServer</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> tools </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./order-tools'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> server </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">MCPServer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'order-tools'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">version</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'1.0.0'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">instructions</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Tools for looking up customer orders.'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">tools</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> tools</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> name</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> description</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> parameters</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> handler </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    description</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">inputSchema</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    handler</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">server</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">startStdio</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// stdout carries the protocol, so log to stderr</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>The agent passes an <code>MCPClient</code> where the tools array used to be:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// mcp-agent.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> path </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'path'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">OpenAICompatibleInput</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">MCPClient</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">SYSTEM</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'You are a support agent for an online store. '</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'Always call get_order_status before you answer a question about an order. Keep answers short.'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> orders </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">MCPClient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">command</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'node'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">args</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">path</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">__dirname</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'order-server.js'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OLLAMA_MODEL</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'qwen2.5:0.5b'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> input </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">OpenAICompatibleInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token constant" style="color:#36acaa">SYSTEM</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addUserMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'What is the status of order A-1001?'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// runTools lists the server's tools on first use, no connect() needed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> steps </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">runTools</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> orders</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">maxSteps</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">steps</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">step</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> step</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> orders</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// stops the server subprocess</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Before you rely on it:</p>
<ul>
<li>The client runs <code>order-server.js</code> as a stdio subprocess. For a remote server, use <code>new MCPClient({ url, headers })</code> over Streamable HTTP. It handles both the 2026-07-28 protocol and the older initialize handshake.</li>
<li>Always call <code>close()</code>. A test script that skipped it was still running six seconds after its last line, held open by the subprocess.</li>
<li>Community servers plug in the same way: <code>command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem', './kb']</code> gives the agent file tools limited to one folder.</li>
<li><code>runTools</code> takes one tool source per call, so an agent that needs both kinds should get all its tools from one MCP server.</li>
<li><code>MCPServer</code> serves tools only, checks only top-level <code>required</code>, <code>type</code> and <code>enum</code>, and has no authentication. Over HTTP it binds to 127.0.0.1 by default; add your own auth before you expose it.</li>
</ul>
<p>Over MCP the small model used the tool in 12 of 18 runs, and in one miss it invented a "pending confirmation" status that exists nowhere in the data, a case the production section handles.</p>
<p>The official <a href="https://modelcontextprotocol.io/docs/2026-07-28/develop/build-client" target="_blank" rel="noopener noreferrer">Build an MCP client</a> tutorial writes this loop by hand against the Anthropic SDK, a good way to learn the protocol. The <a href="https://www.intellinode.ai/docs/npm/mcp/client">MCP client</a> and <a href="https://www.intellinode.ai/docs/npm/mcp/server">MCP server</a> pages list every option, plus <code>npx -y intellinode mcp</code>, a ready server of cross-provider tools for Claude Code and Cursor.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="keep-a-human-in-the-loop-for-risky-actions">Keep a human in the loop for risky actions<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#keep-a-human-in-the-loop-for-risky-actions" class="hash-link" aria-label="Direct link to Keep a human in the loop for risky actions" title="Direct link to Keep a human in the loop for risky actions">​</a></h2>
<p><code>runTools</code> runs every tool it is given as soon as the model asks. That is right for reads and wrong for refunds. For actions that move money, keep the tool out of the loop: put the definition on the input, read <code>tool_calls</code> from <code>chat()</code>, and continue only after someone approves.</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// refund.js: the model proposes, a person approves</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> readline </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'readline/promises'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token maybe-class-name">OpenAICompatibleInput</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> issueRefund </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'function'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">function</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'issue_refund'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Refund a customer order. Use it only when the customer asks for a refund.'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">parameters</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'object'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">properties</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">orderId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'string'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">amount</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'number'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">description</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Amount in USD'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">required</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'orderId'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'amount'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OLLAMA_MODEL</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'qwen2.5:0.5b'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> input </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">OpenAICompatibleInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'You are a support agent for an online store. Always call issue_refund when a customer asks for a refund.'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">tools</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">issueRefund</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">toolChoice</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'auto'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// 'auto' | 'none' | 'required'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addUserMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'Order A-1002 arrived broken. Please refund the 49.90 USD I paid.'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">reply</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">chat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">reply </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!</span><span class="token plain">reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">tool_calls</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">reply</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> rl </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> readline</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createInterface</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">input</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">stdin</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">output</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">stdout</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> results </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> call </span><span class="token keyword" style="color:#00009f">of</span><span class="token plain"> reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">tool_calls</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> args </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">parse</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">call</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">function</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">arguments</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// arguments arrive as a JSON string</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> ok </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> call</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">function</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">name</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'issue_refund'</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// small models sometimes invent tool names</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> rl</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">question</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">Approve </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">call</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">function</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">name</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c"> </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation known-class-name class-name">JSON</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation method function property-access" style="color:#d73a49">stringify</span><span class="token template-string interpolation punctuation" style="color:#393A34">(</span><span class="token template-string interpolation">args</span><span class="token template-string interpolation punctuation" style="color:#393A34">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">? (y/n) </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'n'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> content </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">trim</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'y'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">refunded</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">orderId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">orderId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">amount</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">amount</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// call your payments API here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">refunded</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">reason</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'A support lead declined the refund.'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    results</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">push</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">id</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> call</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> call</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">function</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> content </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  rl</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">close</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addToolCalls</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">tool_calls</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">content</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addToolResults</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">results</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">answer</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">chat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">answer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>In about half our runs the model proposed <code>issue_refund</code>, most often as <code>{"amount":49.9,"orderId":"A-1002"}</code>; in the rest it skipped the tool, usually asking for the order number again. <code>addToolCalls</code> records that request, <code>addToolResults</code> records the outcome, and the second <code>chat()</code> writes the reply. A declined refund is a normal result: with "n", the model apologized and pointed the customer to the support team.</p>
<p>The small model also invented things. After an approval it added that the refund was "sent to your billing address", which no tool returned. Some proposals asked for 51 or -49.9 USD instead of 49.90, and once it called a made-up <code>getOrderId</code> tool. That is why the approver sees the exact arguments and only <code>issue_refund</code> reaches the prompt. That argues for a larger model, and for letting the approver see the final message while you build trust. In a real service the approval is a queue in your admin tool, not a terminal prompt. Store the pending call id, name and arguments with the ticket, and rebuild the input with <code>addToolCalls</code> when the decision arrives.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="production-checklist-for-a-nodejs-ai-agent">Production checklist for a Node.js AI agent<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#production-checklist-for-a-nodejs-ai-agent" class="hash-link" aria-label="Direct link to Production checklist for a Node.js AI agent" title="Direct link to Production checklist for a Node.js AI agent">​</a></h2>
<p>Here is the agent from <code>switch.js</code> with request limits and fallback lanes added:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// support-agent.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'intellinode'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> tools </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./order-tools'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">SYSTEM</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'You are a support agent for an online store. '</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'Always call get_order_status before you answer a question about an order. Keep answers short.'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">REQUEST</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">timeout</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">30000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">retries</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">retryDelay</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">500</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> lanes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">primary</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">signal</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OPENAI_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'openai'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token constant" style="color:#36acaa">REQUEST</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> signal </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">backup</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">signal</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ANTHROPIC_API_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'anthropic'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token constant" style="color:#36acaa">REQUEST</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> signal </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">local</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">signal</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ollama'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token spread operator" style="color:#393A34">...</span><span class="token constant" style="color:#36acaa">REQUEST</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">timeout</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">120000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> signal</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">model</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OLLAMA_MODEL</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'qwen2.5:0.5b'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// provider-side problems: move to the next lane</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">isProviderProblem</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'ETIMEDOUT'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ECONNREFUSED'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ENOTFOUND'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">includes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  error</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">status</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">429</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">status</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">500</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">answer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">question</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> signal</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> order </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'primary'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'backup'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'local'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> lastError</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> lane </span><span class="token keyword" style="color:#00009f">of</span><span class="token plain"> order</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> lanes</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">lane</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">signal</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> input </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">Chatbot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">provider</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">SYSTEM</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// a fresh input for every attempt</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addUserMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">runTools</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tools</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">maxSteps</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> lane</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">result </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// AbortError, a 401 bad key or the maxSteps error should surface, not fall through</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token function" style="color:#d73a49">isProviderProblem</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">throw</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">warn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">lane </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">lane</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c"> failed: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">error</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">code</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation operator" style="color:#393A34">||</span><span class="token template-string interpolation"> error</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">status</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      lastError </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">throw</span><span class="token plain"> lastError</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> answer </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>And a small endpoint, so the keys stay on the server:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// server.js: the agent behind your own endpoint, keys stay on the server</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> http </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'http'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> answer </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./support-agent'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">http</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createServer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">req</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> controller </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">AbortController</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'close'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">writableEnded</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> controller</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">abort</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// the customer left</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> chunks </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> chunk </span><span class="token keyword" style="color:#00009f">of</span><span class="token plain"> req</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> chunks</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">push</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">chunk</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> question </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">''</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">parse</span><span class="token punctuation" style="color:#393A34">(</span><span class="token maybe-class-name">Buffer</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">concat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">chunks</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">toString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'{}'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// in the try, so bad JSON cannot crash the server</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> lane</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> toolCalls </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">answer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">question</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">signal</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> controller</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">signal</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> needsOrderData </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex anchor function" style="color:#d73a49">\b</span><span class="token regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:#393A34">[</span><span class="token regex regex-source language-regex char-class range" style="color:#36acaa">A</span><span class="token regex regex-source language-regex char-class range range-punctuation operator" style="color:#393A34">-</span><span class="token regex regex-source language-regex char-class range" style="color:#36acaa">Z</span><span class="token regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:#393A34">]</span><span class="token regex regex-source language-regex" style="color:#36acaa">-</span><span class="token regex regex-source language-regex char-set class-name" style="color:#36acaa">\d</span><span class="token regex regex-source language-regex quantifier number" style="color:#36acaa">{4}</span><span class="token regex regex-source language-regex anchor function" style="color:#d73a49">\b</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">test</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> escalate </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> needsOrderData </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> toolCalls </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// answered without looking anything up</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">end</span><span class="token punctuation" style="color:#393A34">(</span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> lane</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> escalate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">text</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> escalate </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> text </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">name</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'AbortError'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">writableEnded</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">writeHead</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">502</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">end</span><span class="token punctuation" style="color:#393A34">(</span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">escalate</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">listen</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>With the first two lanes pointed at dead endpoints, the log showed <code>lane primary failed: ECONNREFUSED</code>, then <code>lane backup failed: ENOTFOUND</code>, and the local lane answered. The checklist behind these files:</p>
<ul>
<li><strong>Cap the steps.</strong> The maxSteps error has no status or code, so the fallback rethrows it. Treat it as a bug in your prompt or tools.</li>
<li><strong>Do the timeout math.</strong> <code>timeout</code> applies per attempt and per model call, and retries cover 429, the common 5xx codes and network errors. Five calls times three attempts times 30 seconds is over seven minutes in the worst case, far too long for live chat, so tune the <a href="https://www.intellinode.ai/docs/npm/chatbot/request-options">request options</a> per channel.</li>
<li><strong>Cancel abandoned work.</strong> The <code>res.on('close')</code> line aborts the call through an <a href="https://nodejs.org/api/globals.html#class-abortcontroller" target="_blank" rel="noopener noreferrer">AbortController</a> when the customer leaves, so you stop paying for answers nobody reads.</li>
<li><strong>Catch unreachable hosts.</strong> Network failures have no HTTP status, so a check on <code>status</code> alone never falls back when a host is down. Hence <code>ECONNREFUSED</code> and <code>ENOTFOUND</code>. A 401 is your bug, so it surfaces.</li>
<li><strong>Use a fresh input per attempt.</strong> A half-finished tool conversation from OpenAI is not valid input for Claude.</li>
<li><strong>Distrust answers that skipped the tools.</strong> The <code>toolCalls === 0</code> check sends those to a person.</li>
<li><strong>Check authorization in the handler.</strong> A customer can type anyone's order id. Look the order up with the customer id from your session. A system prompt is not access control.</li>
<li><strong>Keep the automatic loop read-only.</strong> Fallback reruns the loop on the next lane, so a tool can run twice.</li>
</ul>
<p>The <a href="https://www.intellinode.ai/docs/npm/use-cases/model-routing">model routing page</a> extends the lanes idea to quality, cost and private data.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="when-a-bigger-agent-framework-fits-better">When a bigger agent framework fits better<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#when-a-bigger-agent-framework-fits-better" class="hash-link" aria-label="Direct link to When a bigger agent framework fits better" title="Direct link to When a bigger agent framework fits better">​</a></h2>
<p>For agents, IntelliNode is deliberately small: a <code>Chatbot</code> class with one tool loop, an MCP client and server, and a ready coding agent. It has no memory store, tracing UI, React hooks or durable execution, and when you need those, other tools fit better. From their official docs, checked on September 30, 2026:</p>
<ul>
<li><strong><a href="https://openai.github.io/openai-agents-js/" target="_blank" rel="noopener noreferrer">OpenAI Agents SDK</a></strong> (<code>@openai/agents</code>): handoffs, guardrails, sessions, tracing and MCP tools, with other models through an AI SDK extension. Good for multi-agent handoffs with tracing built in.</li>
<li><strong>Vercel AI SDK</strong>: a <a href="https://ai-sdk.dev/docs/agents/overview" target="_blank" rel="noopener noreferrer"><code>ToolLoopAgent</code></a> with <code>stopWhen</code> loop control, an <a href="https://ai-sdk.dev/docs/ai-sdk-core/mcp-tools" target="_blank" rel="noopener noreferrer">MCP client</a> (<code>createMCPClient</code> in <code>@ai-sdk/mcp</code>) and <a href="https://ai-sdk.dev/docs/ai-sdk-ui/overview" target="_blank" rel="noopener noreferrer">UI hooks</a> such as <code>useChat</code> for React, Vue, Svelte and Angular. Pick it when the agent streams into a web UI.</li>
<li><strong><a href="https://github.com/mastra-ai/mastra" target="_blank" rel="noopener noreferrer">Mastra</a></strong>: a TypeScript framework with agents, workflows that can suspend and resume, memory backed by a storage provider, and an interactive Studio UI. Pick it when you want those pieces included.</li>
<li><strong><a href="https://docs.langchain.com/oss/javascript/langgraph/overview" target="_blank" rel="noopener noreferrer">LangGraph.js</a></strong>: low-level orchestration for long-running, stateful agents, with durable execution, checkpointing and human-in-the-loop support. Pick it when an agent must resume after a crash or a day-long wait.</li>
</ul>
<p>Choose IntelliNode for a backend agent that swaps providers by config, runs locally on Ollama, and stays small enough for a new teammate to read in an hour. For a wider comparison, see <a href="https://www.intellinode.ai/articles/nodejs-llm-library">How to Choose a Node.js LLM Library in 2026</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="faq">FAQ<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#faq" class="hash-link" aria-label="Direct link to FAQ" title="Direct link to FAQ">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="what-is-the-best-model-for-an-ai-agent-in-nodejs">What is the best model for an AI agent in Node.js?<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#what-is-the-best-model-for-an-ai-agent-in-nodejs" class="hash-link" aria-label="Direct link to What is the best model for an AI agent in Node.js?" title="Direct link to What is the best model for an AI agent in Node.js?">​</a></h3>
<p>Use the model that picks the right tool for your own questions, not the best benchmark score. Run 20 or so real customer questions through a cloud model such as <code>gpt-5.5</code> or <code>claude-sonnet-5</code> and a local model of about 8B parameters. A 0.5B model is fine for wiring code, but even with our best prompt it skipped the tool in 3 of 19 direct runs and 6 of 18 over MCP.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="can-a-nodejs-ai-agent-run-fully-offline-with-ollama">Can a Node.js AI agent run fully offline with Ollama?<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#can-a-nodejs-ai-agent-run-fully-offline-with-ollama" class="hash-link" aria-label="Direct link to Can a Node.js AI agent run fully offline with Ollama?" title="Direct link to Can a Node.js AI agent run fully offline with Ollama?">​</a></h3>
<p>Yes. <code>new Chatbot(null, 'ollama', null, { model })</code> needs no key, and the same <code>runTools</code> and <code>MCPClient</code> code runs with no internet connection. The limit is model quality on your hardware, not the code.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="how-many-tool-steps-should-an-ai-agent-be-allowed">How many tool steps should an AI agent be allowed?<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#how-many-tool-steps-should-an-ai-agent-be-allowed" class="hash-link" aria-label="Direct link to How many tool steps should an AI agent be allowed?" title="Direct link to How many tool steps should an AI agent be allowed?">​</a></h3>
<p>Allow the tool rounds a correct answer needs, plus a little room. A lookup agent like this needs one or two, so <code>maxSteps: 4</code> leaves slack without letting a confused model loop. If you hit the maxSteps error often, fix the tool descriptions before raising the limit.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="can-i-build-the-agent-in-typescript">Can I build the agent in TypeScript?<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#can-i-build-the-agent-in-typescript" class="hash-link" aria-label="Direct link to Can I build the agent in TypeScript?" title="Direct link to Can I build the agent in TypeScript?">​</a></h3>
<p>Yes. The package ships <code>index.d.ts</code>, so <code>Chatbot</code>, the input classes and <code>RunToolsResult</code> are typed with no <code>@types</code> package. The code is CommonJS, and ESM named imports work, as <code>agent.mts</code> shows. Import <code>RunToolsResult</code> with the <code>type</code> modifier: it exists only as a type, so a plain import fails under <code>verbatimModuleSyntax</code> and under Node's type stripping.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="can-claude-code-or-cursor-use-the-same-tools">Can Claude Code or Cursor use the same tools?<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#can-claude-code-or-cursor-use-the-same-tools" class="hash-link" aria-label="Direct link to Can Claude Code or Cursor use the same tools?" title="Direct link to Can Claude Code or Cursor use the same tools?">​</a></h3>
<p>Yes. <code>order-server.js</code> is a standard stdio MCP server, so you can register it with <code>claude mcp add order-tools -- node /path/to/order-server.js</code> or add it to <code>.cursor/mcp.json</code>. Your agent and your coding assistant then share one implementation of the tools.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="next-step">Next step<a href="https://www.intellinode.ai/articles/build-ai-agents-nodejs#next-step" class="hash-link" aria-label="Direct link to Next step" title="Direct link to Next step">​</a></h2>
<p>Install the package, pull a local model, and run <code>agent.js</code> from this guide:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm i intellinode</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ollama pull qwen2.5:0.5b</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">node agent.js</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Then read the <a href="https://www.intellinode.ai/docs/npm/chatbot/tool-calling">tool calling guide</a> for the other tool formats and the manual tool call API. For a cloud model, run <code>switch.js</code> with <code>LLM_PROVIDER</code> and your key set.</p>]]></content>
        <author>
            <name>IntelliNode Team</name>
            <uri>https://github.com/intelligentnode</uri>
        </author>
        <category label="Node.js" term="Node.js"/>
        <category label="AI Agents" term="AI Agents"/>
        <category label="Tool Calling" term="Tool Calling"/>
        <category label="MCP" term="MCP"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Agentic Workflows in Python Without LangGraph]]></title>
        <id>https://www.intellinode.ai/articles/agentic-workflow-python</id>
        <link href="https://www.intellinode.ai/articles/agentic-workflow-python"/>
        <updated>2026-09-30T10:00:00.000Z</updated>
        <summary type="html"><![CDATA[Build an agentic workflow in Python with chain, parallel, router, loop and MCP tool patterns, then compare Intelli flows with LangGraph and CrewAI.]]></summary>
        <content type="html"><![CDATA[<p>An agentic workflow in Python is a set of model calls, tools and plain functions wired into a fixed shape: a chain, a fan-out, a router or a loop. The model makes decisions inside each step, but your code decides which steps exist. You do not need LangGraph for that. You need a graph runner, a clean handoff between steps, and a way to call different providers.</p>
<p>This guide builds each common AI agent orchestration pattern with Intelli, an Apache 2.0 Python library for multi-model agent flows. The examples mix OpenAI, Claude and Gemini in one flow and can run on a local Ollama model. It closes by comparing Intelli with LangGraph, CrewAI and plain code, including where each of them wins.</p>
<p><img decoding="async" loading="lazy" alt="Blue and lavender ribbons of light crossing and merging from left to right, like steps in an agentic workflow" src="https://www.intellinode.ai/assets/images/agentic-workflow-python-7c0d4b7a23a42f8a1da61c92676a7f8b.jpg" width="1200" height="630" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="workflow-or-agent-how-much-autonomy-you-need">Workflow or agent: how much autonomy you need<a href="https://www.intellinode.ai/articles/agentic-workflow-python#workflow-or-agent-how-much-autonomy-you-need" class="hash-link" aria-label="Direct link to Workflow or agent: how much autonomy you need" title="Direct link to Workflow or agent: how much autonomy you need">​</a></h2>
<p>Anthropic's engineering team draws a useful line in <a href="https://www.anthropic.com/engineering/building-effective-agents" target="_blank" rel="noopener noreferrer">Building effective agents</a>. In a workflow, models and tools follow predefined code paths. In an agent, the model directs its own process and chooses its tools. The post recommends adding autonomy only when simpler setups fall short.</p>
<p>For most business processes the workflow wins on cost and audits. Take a SaaS support team handling 12,000 tickets a month. A workflow that triages, extracts facts and risks in parallel, and drafts a reply makes four model calls per ticket, every time. That is 48,000 calls a month, a number finance can budget and QA can test. An autonomous agent might use 3 calls on one ticket and 25 on the next.</p>
<p>Fix the known steps in code and give the model freedom only inside a step. If you have not built a single tool calling agent yet, start with <a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python">How to Build an AI Agent in Python</a> and come back when one step is not enough.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-building-blocks-of-an-agentic-workflow-in-python">The building blocks of an agentic workflow in Python<a href="https://www.intellinode.ai/articles/agentic-workflow-python#the-building-blocks-of-an-agentic-workflow-in-python" class="hash-link" aria-label="Direct link to The building blocks of an agentic workflow in Python" title="Direct link to The building blocks of an agentic workflow in Python">​</a></h2>
<p>For AI agent orchestration in Python, Intelli gives you four pieces:</p>
<ul>
<li><code>Agent</code>: one model or tool, with a mission (the system prompt), a provider and model settings.</li>
<li><code>Task</code>: one unit of work for an agent, with optional <code>pre_process</code> and <code>post_process</code> hooks.</li>
<li><code>SequenceFlow</code>: runs tasks in a straight line.</li>
<li><code>Flow</code>: runs a graph of tasks. <code>map_paths</code> says which task feeds which, independent tasks run in parallel, and connectors add routing.</li>
</ul>
<p>Each agent picks its own provider, so a cheap fast model can classify while a stronger one writes. Install with <code>pip install intelli</code>, then put the provider setup in one file so the pattern examples stay short:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># providers.py</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> os</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flow </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Agent</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">gpt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">mission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"openai"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                 </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"key"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"OPENAI_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"model"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"gpt-5.5"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">claude</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">mission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"anthropic"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                 </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"key"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"ANTHROPIC_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"model"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"claude-sonnet-5"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">gemini</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">mission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"gemini"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                 </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"key"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"GEMINI_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"model"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"gemini-2.5-flash"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">local</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">mission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># Ollama or vLLM on your own machine: no API key, no per-token bill</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vllm"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"model"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"qwen2.5:0.5b"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                 options</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"baseUrl"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"http://localhost:11434"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>This file is also your insurance against vendor lock-in: moving a step from Claude to Gemini is a one-word change.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="pattern-1-the-sequential-chain">Pattern 1: the sequential chain<a href="https://www.intellinode.ai/articles/agentic-workflow-python#pattern-1-the-sequential-chain" class="hash-link" aria-label="Direct link to Pattern 1: the sequential chain" title="Direct link to Pattern 1: the sequential chain">​</a></h2>
<p>Anthropic's post calls this prompt chaining: each step works on the previous step's output. Here GPT writes release notes from commit messages, and Claude turns them into a two-sentence banner for the app.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flow </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> SequenceFlow</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TextTaskInput</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TextProcessor</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> providers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> gpt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> claude</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">commits </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token triple-quoted-string string" style="color:#e3116c">"""feat: export invoices as CSV</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">fix: weekly report used the wrong timezone</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">perf: dashboard loads 40% faster"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">notes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Write release notes for these commits, one short paragraph each."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             gpt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You write clear release notes for B2B customers."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">banner </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Summarize the release notes in two sentences for an in-app banner."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              claude</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You write short product announcements."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              pre_process</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">TextProcessor</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">text_head</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># hand off the first 800 characters only</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> SequenceFlow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">notes</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> banner</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> log</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">initial_input</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">commits</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"task2"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Two details matter. <code>TextProcessor.text_head</code> trims the handoff to 800 characters, which caps what the second call costs. And <code>SequenceFlow</code> returns positional keys (<code>task1</code>, <code>task2</code>) with raw outputs as values, so update the code that reads them if you reorder tasks.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="pattern-2-parallel-fan-out-and-merge">Pattern 2: parallel fan-out and merge<a href="https://www.intellinode.ai/articles/agentic-workflow-python#pattern-2-parallel-fan-out-and-merge" class="hash-link" aria-label="Direct link to Pattern 2: parallel fan-out and merge" title="Direct link to Pattern 2: parallel fan-out and merge">​</a></h2>
<p>When two steps do not depend on each other, run them at the same time. This support flow extracts facts with Gemini and churn risks with GPT in parallel, then Claude writes the reply from both.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flow </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TextTaskInput</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> providers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> gpt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> claude</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gemini</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ticket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Order #1001 arrived with a cracked screen. This is our second damaged "</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token string" style="color:#e3116c">"delivery this quarter, and our 40 seats are up for renewal in May."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tasks </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"facts"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"List the facts in this ticket as short bullets."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  gemini</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You extract facts from support tickets."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"risks"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"List the churn risks in this ticket."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  gpt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You are a customer success analyst."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"reply"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Write a short, calm reply to the customer."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  claude</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You synthesize analyst notes into customer replies."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">flow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tasks</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">tasks</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            map_paths</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"facts"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"reply"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"risks"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"reply"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            output_memory_map</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"reply"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"final_reply"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">max_workers</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> initial_input</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">ticket</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"reply"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"output"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">errors</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">                        </span><span class="token comment" style="color:#999988;font-style:italic"># {} when every step worked</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">saved </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">memory</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">retrieve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"final_reply"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p><code>initial_input</code> goes to every root task, so both analysts see the ticket. The reply step waits for both parents and receives their outputs joined. Because its mission contains "synthesize", Intelli wraps each parent's output in a labeled block, so the writer can tell facts from risks. <code>output_memory_map</code> copies the reply into <code>flow.memory</code>.</p>
<p><code>max_workers</code> caps how many tasks run at once, and Intelli also limits each provider to 10 concurrent tasks, which helps with rate limits. A <code>Flow</code> returns <code>output</code> and <code>type</code> for each task name. The <a href="https://www.intellinode.ai/docs/python/flows/async-flow">async flow docs</a> walk through a larger graph.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="pattern-3-routing-with-a-classifier">Pattern 3: routing with a classifier<a href="https://www.intellinode.ai/articles/agentic-workflow-python#pattern-3-routing-with-a-classifier" class="hash-link" aria-label="Direct link to Pattern 3: routing with a classifier" title="Direct link to Pattern 3: routing with a classifier">​</a></h2>
<p>A cheap Gemini call classifies inbound email, and a <code>DynamicConnector</code> sends it to the billing, bug or sales desk. Only the chosen branch runs, so you pay for one specialist instead of three.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flow </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TextTaskInput</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> DynamicConnector</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ConnectorMode</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">utils</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dynamic_utils </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> text_content_router</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> providers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> gpt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> claude</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gemini</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">email </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"I was charged twice for the March invoice."</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">triage </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Classify this email as billing, bug or sales. Reply with one word."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              gemini</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You triage inbound customer email."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">desks </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># memory_key: each desk reads the email itself, not the one word label</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"billing"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Draft a reply about the charge."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gpt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You work on the billing desk."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    memory_key</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"email"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"bug"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Write a bug report for engineering."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> claude</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You are a QA lead."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                memory_key</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"email"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"sales"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Draft a sales follow up."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gpt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You are an account executive."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  memory_key</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"email"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">route</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">output</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> output_type</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">output</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">startswith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Error"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># unknown key: stop here, flow.errors has the details</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> text_content_router</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">output</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> output_type</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"billing"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"billing"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"invoice"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"charge"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># first key is the fallback</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"bug"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"bug"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"crash"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"sales"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"sales"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pricing"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"demo"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">flow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tasks</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"triage"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> triage</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">desks</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    map_paths</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    dynamic_connectors</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"triage"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> DynamicConnector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        route</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"billing"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"billing"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"bug"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"bug"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"sales"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"sales"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        name</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"email_triage"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mode</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">ConnectorMode</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">CONTENT_BASED</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">memory</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">store</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"email"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> email</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">initial_input</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">email</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">sorted</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># ['billing', 'triage']: only the chosen desk ran</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>A few rules that matter in production:</p>
<ul>
<li>A destination receives its parent's output, here the one-word label. The desks read the full email from <code>flow.memory</code> through <code>memory_key</code>.</li>
<li>A connector supports at most 4 destinations. For more, route in two levels.</li>
<li><code>text_content_router</code> falls back to the first key when nothing matches, so list your safest desk first.</li>
<li>An unknown key stops the branch. The <code>Error</code> check uses that on purpose: without it, a failed classifier returns text containing "error" and lands on the bug desk. We hit exactly this in testing.</li>
<li>A shared step fed by all three desks never runs, because a <code>Flow</code> task waits for all of its parents. Give each branch its own follow-up step.</li>
</ul>
<p>Length, sentiment, error and type routers are in the <a href="https://www.intellinode.ai/docs/python/flows/dynamic-path">dynamic path docs</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="pattern-4-loops-for-self-review">Pattern 4: loops for self-review<a href="https://www.intellinode.ai/articles/agentic-workflow-python#pattern-4-loops-for-self-review" class="hash-link" aria-label="Direct link to Pattern 4: loops for self-review" title="Direct link to Pattern 4: loops for self-review">​</a></h2>
<p>Some outputs need a few passes, such as a subject line that must fit in 60 characters. <code>LoopTask</code> repeats its steps until a stop condition passes or <code>max_loops</code> is reached.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flow </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TextTaskInput</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> LoopTask</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> providers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> gpt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rewrite </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Rewrite this email subject line to be shorter and clearer."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               gpt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You write email subject lines. Reply with the subject line only."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">loop </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> LoopTask</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"shorten subject"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">rewrite</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> max_loops</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                stop_condition</span><span class="token operator" style="color:#393A34">=</span><span class="token keyword" style="color:#00009f">lambda</span><span class="token plain"> i</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> out</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> out_type</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mem</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">out</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                store_history_memory_key</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"subject_history"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">flow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tasks</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"subject"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> loop</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> map_paths</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">initial_input</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"Important information regarding upcoming changes to your monthly subscription billing cycle"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"subject"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"output"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> step </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">memory</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">retrieve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"subject_history"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">step</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"iteration"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> step</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"output"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>The stop check is plain Python, which is free and deterministic. You can add a reviewer model to <code>steps</code>, but <code>LoopTask</code> returns the last step's output, so you would get the review instead of the draft. <code>max_loops=4</code> doubles as a cost ceiling.</p>
<p><code>Flow</code> requires an acyclic graph and raises <code>ValueError</code> on a cycle, so repetition lives inside one node and the graph stays easy to review. The <a href="https://www.intellinode.ai/docs/python/flows/loop">loop docs</a> cover the stop condition and history.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="pattern-5-tool-calls-routed-to-mcp-servers">Pattern 5: tool calls routed to MCP servers<a href="https://www.intellinode.ai/articles/agentic-workflow-python#pattern-5-tool-calls-routed-to-mcp-servers" class="hash-link" aria-label="Direct link to Pattern 5: tool calls routed to MCP servers" title="Direct link to Pattern 5: tool calls routed to MCP servers">​</a></h2>
<p>Here the model decides whether it needs data, and the flow runs the tool. First, a small MCP server with one tool (install the extra with <code>pip install "intelli[mcp]"</code>):</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># order_server.py</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mcp </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> MCPServerBuilder</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">server </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> MCPServerBuilder</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"OrderTools"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:#393A34">@server</span><span class="token decorator annotation punctuation" style="color:#393A34">.</span><span class="token decorator annotation punctuation" style="color:#393A34">add_tool</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">lookup_order</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">order_id</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""Return the shipping status of an order"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    orders </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"A-1001"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"shipped on 28 Sep, arrives Friday"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"A-1002"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"waiting for stock"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:#e3116c">f"Order </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">order_id</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">orders</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">get</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation">order_id</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">,</span><span class="token string-interpolation interpolation"> </span><span class="token string-interpolation interpolation string" style="color:#e3116c">'not found'</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> __name__ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"__main__"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    server</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">transport</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"stdio"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> print_info</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># stdout carries the MCP protocol</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>If GPT calls <code>lookup_order</code>, a <code>ToolDynamicConnector</code> routes to an MCP task that runs the tool. If it answers in text, the flow sends that answer to a direct step.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> json</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sys</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flow </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Agent</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TextTaskInput</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ToolDynamicConnector</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> providers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> gpt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">TOOLS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"lookup_order"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"description"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Get the shipping status of an order by id, for example A-1001"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"parameters"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"object"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token string" style="color:#e3116c">"properties"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"order_id"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"string"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token string" style="color:#e3116c">"required"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"order_id"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">tool_call_to_mcp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tool_output</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    call </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tool_output</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"tool_calls"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    args </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">loads</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">call</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"arguments"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"update_model_params"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"tool"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> call</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                    </span><span class="token operator" style="color:#393A34">**</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string-interpolation string" style="color:#e3116c">f"arg_</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">k</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> v </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> k</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> v </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">items</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">router </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Answer the customer. Call a tool when you need order data."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              gpt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You are a support assistant for an online store."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> model</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"gpt-4.1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tools</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">TOOLS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">lookup </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Run the order lookup"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              Agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"mcp"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mcp"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Order tools"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"command"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sys</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">executable</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"args"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"order_server.py"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              pre_process</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">tool_call_to_mcp</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> model_params</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">direct </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Rewrite this answer for the customer in two sentences."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              gpt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You answer store policy questions."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">flow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tasks</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"router"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> router</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"lookup"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> lookup</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"direct"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> direct</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    map_paths</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    dynamic_connectors</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"router"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ToolDynamicConnector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        destinations</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"tool_called"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"lookup"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"no_tool"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"direct"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">initial_input</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"Where is my order A-1001?"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"lookup"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"direct"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"output"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Details that are easy to miss:</p>
<ul>
<li>Intelli does not map the tool call to MCP arguments for you. The <code>tool_call_to_mcp</code> pre-process turns it into <code>tool</code> and <code>arg_*</code> params.</li>
<li>Each destination receives the router's output: <code>lookup</code> gets the tool call and <code>direct</code> gets the model's text answer, not the customer's question.</li>
<li>Pass <code>model_params={}</code> on the MCP task. <code>Task</code> has a shared mutable default, and updated params would otherwise leak into other tasks.</li>
<li>The router must be an OpenAI or Anthropic agent, because Gemini and Mistral tool calls are not parsed into the shape the connector checks. Intelli passes the tools list through unchanged. This one uses the chat completions format, hence <code>gpt-4.1</code>. GPT-5 models go through the Responses API, which expects the flat tool format, and Anthropic expects <code>input_schema</code>.</li>
<li>Tool choice depends on the model. In our tests a 0.5B local model called <code>lookup_order</code> for a return policy question and sometimes skipped it for a real order id.</li>
</ul>
<p>See the <a href="https://www.intellinode.ai/docs/python/flows/dynamic-tool">dynamic tool docs</a> for the connector and the <a href="https://www.intellinode.ai/docs/python/mcp/server">MCP server docs</a> for streamable HTTP servers.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="pattern-6-your-own-code-and-approval-steps">Pattern 6: your own code and approval steps<a href="https://www.intellinode.ai/articles/agentic-workflow-python#pattern-6-your-own-code-and-approval-steps" class="hash-link" aria-label="Direct link to Pattern 6: your own code and approval steps" title="Direct link to Pattern 6: your own code and approval steps">​</a></h2>
<p>Not every step should be a model. A <code>CustomAgent</code> wraps your own Python, such as a CRM lookup, and sits in the graph like any other agent. The same shape works as a human approval gate before an action.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flow </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> CustomAgent</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TextTaskInput</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> DynamicConnector</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> providers </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> claude</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">CRMLookup</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">CustomAgent</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">execute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> agent_input</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> new_params</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        account </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> agent_input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">desc</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># call your CRM API or database here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:#e3116c">f"Account </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">account</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">: Business plan, 40 seats, renews 1 May, 2 open tickets"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">ApprovalGate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">CustomAgent</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">execute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> agent_input</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> new_params</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        draft </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> agent_input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">desc</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">split</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"\n\n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># drop the task header Intelli adds</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"\n--- draft for review ---\n"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> draft</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        approved </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">input</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Approve and send? [y/N] "</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">lower</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"y"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"APPROVED\n"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> draft </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> approved </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"REJECTED"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">SendEmail</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">CustomAgent</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">execute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> agent_input</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> new_params</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># call your email provider here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"sent"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">flow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tasks</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"crm"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"acme corp"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> CRMLookup</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"draft"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Write a short renewal check-in email using the CRM note."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                      claude</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You write emails for account managers."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"approve"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Human review"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ApprovalGate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"send"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Send the approved email"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> SendEmail</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    map_paths</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"crm"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"draft"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"draft"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"approve"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    dynamic_connectors</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"approve"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> DynamicConnector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">lambda</span><span class="token plain"> out</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> out_type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"send"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">out</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">startswith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"APPROVED"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"stop"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"send"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"send"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"human_approval"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">sorted</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># 'send' is only there when the reviewer typed y</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>A rejected draft returns the key "stop", which is not a destination, so the flow ends before <code>SendEmail</code>.</p>
<p>The approval logic is your application code. <code>input()</code> works for a CLI or an internal script. In a web app, end the flow at the gate, save the draft as pending, and start a second flow when a reviewer approves. Intelli does not checkpoint a paused flow and resume it, which is a real gap if reviews take hours.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="intelli-flows-vs-langgraph-crewai-and-plain-code">Intelli flows vs LangGraph, CrewAI and plain code<a href="https://www.intellinode.ai/articles/agentic-workflow-python#intelli-flows-vs-langgraph-crewai-and-plain-code" class="hash-link" aria-label="Direct link to Intelli flows vs LangGraph, CrewAI and plain code" title="Direct link to Intelli flows vs LangGraph, CrewAI and plain code">​</a></h2>
<p>If you are comparing LangGraph alternatives in Python, be precise about what each tool is built for. Sources, checked on September 30, 2026: LangGraph's <a href="https://docs.langchain.com/oss/python/langgraph/persistence" target="_blank" rel="noopener noreferrer">persistence</a> and <a href="https://docs.langchain.com/oss/python/langgraph/graph-api" target="_blank" rel="noopener noreferrer">graph API</a> docs and <a href="https://github.com/langchain-ai/langgraph" target="_blank" rel="noopener noreferrer">GitHub repo</a>, and CrewAI's <a href="https://docs.crewai.com/en/concepts/flows" target="_blank" rel="noopener noreferrer">Flows</a> and <a href="https://docs.crewai.com/en/concepts/llms" target="_blank" rel="noopener noreferrer">LLM</a> docs and <a href="https://github.com/crewAIInc/crewAI" target="_blank" rel="noopener noreferrer">GitHub repo</a>. Star counts change daily.</p>
<table><thead><tr><th></th><th>Intelli flows</th><th>LangGraph</th><th>CrewAI</th><th>Plain Python</th></tr></thead><tbody><tr><td>Core idea</td><td>Tasks and agents in a DAG, with routers and loop nodes</td><td>Stateful graph of nodes and conditional edges</td><td>Role-based crews, plus event-driven Flows</td><td>Your own functions and <code>asyncio</code></td></tr><tr><td>State and persistence</td><td>In-process <code>Memory</code> or SQLite <code>DBMemory</code>; no checkpoint and resume</td><td>Checkpointers (<code>InMemorySaver</code>, <code>SqliteSaver</code>, <code>PostgresSaver</code>) and durable execution</td><td>Pydantic or dict state; <code>@persist</code> saves to SQLite</td><td>Whatever you build</td></tr><tr><td>Loops and branching</td><td>Connectors for branching; cycles rejected, use <code>LoopTask</code></td><td>Cycles supported, recursion limit of 1000 super-steps by default</td><td><code>@router</code> for branching, <code>or_</code> and <code>and_</code> to join listeners</td><td><code>if</code> and <code>while</code></td></tr><tr><td>Human in the loop</td><td>A custom gate step; you store and restart</td><td><code>interrupt()</code> pauses, <code>Command(resume=...)</code> continues</td><td><code>@human_feedback</code> decorator</td><td>Your own</td></tr><tr><td>Providers per step</td><td>Built-in: OpenAI, Anthropic, Gemini, Mistral, NVIDIA, vLLM or Ollama, llama.cpp</td><td>Any model you call inside a node</td><td><code>llm</code> per agent, native or through LiteLLM</td><td>Each vendor SDK</td></tr><tr><td>Ecosystem</td><td>Small, fewer integrations</td><td>42.5k GitHub stars, LangSmith tracing, JS version</td><td>59.2k GitHub stars, MCP and A2A support</td><td>None needed</td></tr><tr><td>Concepts to learn</td><td>Agent, Task, Flow, connectors</td><td>State, nodes, edges, checkpointers, threads</td><td>Agents with roles, tasks, crews, flows</td><td>None, but you write retries and fan out</td></tr></tbody></table>
<p><strong>Choose Intelli flows</strong> when the steps are known, you want a different provider per step or local models in CI, and your team would rather learn four classes than a new state model. <strong>Choose LangGraph</strong> when runs are long, must survive crashes, or pause for a person and resume hours later. Its <a href="https://docs.langchain.com/oss/python/langgraph/interrupts" target="_blank" rel="noopener noreferrer">interrupts</a> use a checkpointer and a thread ID, exactly the machinery Intelli lacks. It works without LangChain, and its <a href="https://github.com/langchain-ai/langgraph" target="_blank" rel="noopener noreferrer">README</a> lists durable execution as a core feature.</p>
<p><strong>Choose CrewAI</strong> when the problem maps to roles and goals, or you like decorator-style <a href="https://docs.crewai.com/en/concepts/flows" target="_blank" rel="noopener noreferrer">Flows</a> with persistence built in. <strong>Write plain code</strong> for two or three calls with no branching. Anthropic's post says the same: start with direct API calls, since framework layers can hide the prompts you debug.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="from-prototype-to-production">From prototype to production<a href="https://www.intellinode.ai/articles/agentic-workflow-python#from-prototype-to-production" class="hash-link" aria-label="Direct link to From prototype to production" title="Direct link to From prototype to production">​</a></h2>
<p><strong>Check <code>flow.errors</code> before you use any output.</strong> Agent failures come back as <code>Error: ...</code> strings, not exceptions, and downstream steps still run on that text. In our test with a dead endpoint on the risks step, the reply step wrote the customer an apology about a connection problem. MCP tasks are the exception: a failed tool call returns text such as <code>Error from MCP tool ...</code>, which <code>flow.errors</code> does not record, so check that output too.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># continuing the support flow from pattern 2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> logging</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">max_workers</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> initial_input</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">ticket</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">errors</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">                    </span><span class="token comment" style="color:#999988;font-style:italic"># failed agents return "Error: ..." text, not exceptions</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> step</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> message </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">errors</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">items</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        logging</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"step %s failed: %s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> step</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> message</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">300</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">generate_graph_img</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"support_flow"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> save_path</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># needs "intelli[visual]"</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Send those tickets to a retry or a manual queue, not the customer.</p>
<p><strong>Log and draw the graph.</strong> <code>log=True</code> on a <code>Task</code> prints the start of its input and output, and on a <code>Flow</code> it logs routing decisions and soft errors. <code>generate_graph_img</code> writes a PNG with every node labeled by agent type and provider, handy when security asks which vendor sees which data. It needs <code>pip install "intelli[visual]"</code>.</p>
<p><strong>Draft flows from plain language.</strong> Vibe Agents turn a description into a flow spec that you can review, commit and rebuild without calling the planner again.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> os</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flow </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> VibeAgent</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    vibe </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> VibeAgent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">planner_provider</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"anthropic"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                     planner_api_key</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"ANTHROPIC_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                     planner_model</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"claude-sonnet-5"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># set it: the default is an OpenAI model</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    flow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> vibe</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">build</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"Read a support ticket, extract the facts and the churn risks in parallel, "</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"then write a reply from both."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        save_dir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"./ticket_flow"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># writes flow_spec.json and a graph image</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">initial_input</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"Order #1001 arrived damaged."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># later, in CI or production: rebuild from the reviewed spec, no planner call</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    same_flow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> vibe</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">build_from_spec</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vibe</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">load_spec</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"./ticket_flow/flow_spec.json"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Always set <code>planner_model</code> for a non-OpenAI planner, and use a strong one: a 0.5B local model returned invalid JSON in our tests. The <a href="https://www.intellinode.ai/docs/python/vibe-agents">Vibe Agents docs</a> cover editing a saved spec.</p>
<p><strong>Develop against local models.</strong> Swap the helpers in <code>providers.py</code> for <code>local()</code> and run the flows in CI against Ollama with no token spend. One exception: the <code>vllm</code> provider does not forward tools. For the tool routing pattern, use the OpenAI provider with a <code>ProxyHelper</code> pointed at Ollama:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># in the pattern 5 script, build the router Task with this agent</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">utils</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">proxy_helper </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> ProxyHelper</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ollama </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ProxyHelper</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ollama</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">set_openai_proxy_values</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"base"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"http://localhost:11434"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">router_agent </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"openai"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"You are a support assistant for an online store."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                     </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"key"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ollama"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"model"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"qwen2.5:0.5b"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tools"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> TOOLS</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                     options</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"proxy_helper"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ollama</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Small local models are good for testing wiring and error handling, not for judging output quality.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="faq">FAQ<a href="https://www.intellinode.ai/articles/agentic-workflow-python#faq" class="hash-link" aria-label="Direct link to FAQ" title="Direct link to FAQ">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="is-an-agentic-workflow-the-same-as-a-multi-agent-system">Is an agentic workflow the same as a multi-agent system?<a href="https://www.intellinode.ai/articles/agentic-workflow-python#is-an-agentic-workflow-the-same-as-a-multi-agent-system" class="hash-link" aria-label="Direct link to Is an agentic workflow the same as a multi-agent system?" title="Direct link to Is an agentic workflow the same as a multi-agent system?">​</a></h3>
<p>Close, but not the same. A multi-agent system in Python is any setup where several agents with different jobs cooperate. An agentic workflow fixes how they cooperate in code: the graph sets the order, and each agent decides only within its step. Intelli flows are a multi-agent framework in Python built around that idea.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="what-is-the-best-langgraph-alternative-in-python">What is the best LangGraph alternative in Python?<a href="https://www.intellinode.ai/articles/agentic-workflow-python#what-is-the-best-langgraph-alternative-in-python" class="hash-link" aria-label="Direct link to What is the best LangGraph alternative in Python?" title="Direct link to What is the best LangGraph alternative in Python?">​</a></h3>
<p>It depends on what you need. For known steps across several providers, a lighter graph runner such as Intelli flows, or plain <code>asyncio</code>, is usually enough. CrewAI fits role-based teams of agents. For durable runs that pause for a person and resume later, LangGraph itself is hard to beat.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="can-ai-agents-run-in-parallel-in-python">Can AI agents run in parallel in Python?<a href="https://www.intellinode.ai/articles/agentic-workflow-python#can-ai-agents-run-in-parallel-in-python" class="hash-link" aria-label="Direct link to Can AI agents run in parallel in Python?" title="Direct link to Can AI agents run in parallel in Python?">​</a></h3>
<p>Yes. In an Intelli <code>Flow</code>, tasks with no dependency between them run at the same time in a thread pool, capped by <code>max_workers</code>. A task with several parents waits for all of them and receives their outputs merged.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="can-an-agent-workflow-loop-back-to-an-earlier-step">Can an agent workflow loop back to an earlier step?<a href="https://www.intellinode.ai/articles/agentic-workflow-python#can-an-agent-workflow-loop-back-to-an-earlier-step" class="hash-link" aria-label="Direct link to Can an agent workflow loop back to an earlier step?" title="Direct link to Can an agent workflow loop back to an earlier step?">​</a></h3>
<p>Not as a graph edge in Intelli, because <code>Flow</code> rejects cycles with a <code>ValueError</code>. Use <code>LoopTask</code> to repeat steps inside one node with a stop condition. Its <code>max_loops</code> cap also bounds the cost of a run.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="what-happens-when-one-step-of-the-workflow-fails">What happens when one step of the workflow fails?<a href="https://www.intellinode.ai/articles/agentic-workflow-python#what-happens-when-one-step-of-the-workflow-fails" class="hash-link" aria-label="Direct link to What happens when one step of the workflow fails?" title="Direct link to What happens when one step of the workflow fails?">​</a></h3>
<p>The failed task's output becomes an <code>Error: ...</code> string, and its name appears in <code>flow.errors</code>. MCP tool failures return <code>Error from MCP tool ...</code> text instead and are not recorded there. The flow keeps going, so later tasks may run on that error text. Check <code>flow.errors</code> after every run and route failures to a retry or a person.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="start-with-one-pattern">Start with one pattern<a href="https://www.intellinode.ai/articles/agentic-workflow-python#start-with-one-pattern" class="hash-link" aria-label="Direct link to Start with one pattern" title="Direct link to Start with one pattern">​</a></h2>
<p>Pick the pattern closest to a process you already run, usually the chain or the router, and get it working on a local model before you add hosted providers.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip install intelli</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pip install "intelli[mcp]"      # MCP servers and tool routing</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pip install "intelli[visual]"   # flow graph images</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Then follow the <a href="https://www.intellinode.ai/docs/python/flows/get-started">flows get started guide</a> to build your first <code>Flow</code> with your own tasks.</p>]]></content>
        <author>
            <name>IntelliNode Team</name>
            <uri>https://github.com/intelligentnode</uri>
        </author>
        <category label="Python" term="Python"/>
        <category label="AI Agents" term="AI Agents"/>
        <category label="Agentic Workflows" term="Agentic Workflows"/>
        <category label="Orchestration" term="Orchestration"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[How to Build an AI Agent in Python, Step by Step]]></title>
        <id>https://www.intellinode.ai/articles/how-to-build-ai-agent-python</id>
        <link href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python"/>
        <updated>2026-09-30T09:00:00.000Z</updated>
        <summary type="html"><![CDATA[Learn how to build an AI agent in Python with a small tool calling loop, then run the same agent on OpenAI, Claude or a local model using Intelli.]]></summary>
        <content type="html"><![CDATA[<p>Here is how to build an AI agent in Python without a big framework: give a model a short list of functions, run a loop that executes the calls it asks for, send the results back, and stop after a fixed number of steps. That loop is the agent. Everything else is guardrails.</p>
<p>In this guide you will build an order status agent in under 100 lines of plain Python on top of the Intelli <code>Chatbot</code>. The same code runs on OpenAI, Claude or a local Ollama model by changing one constructor and the model name, and you will see exactly where a tiny local model falls short.</p>
<p><img decoding="async" loading="lazy" alt="Glowing core circled by orbit rings and small bodies, showing a Python AI agent loop that calls tools and returns" src="https://www.intellinode.ai/assets/images/how-to-build-ai-agent-python-ab60d24cf9cb0c4df3992af42e23036b.jpg" width="1200" height="630" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-an-ai-agent-is-made-of">What an AI agent is made of<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#what-an-ai-agent-is-made-of" class="hash-link" aria-label="Direct link to What an AI agent is made of" title="Direct link to What an AI agent is made of">​</a></h2>
<p>An agent has four parts: a model, a set of tools, a loop and a stop rule. The model decides which tool to call. Your code runs the tool. The loop repeats until the model answers in plain text or hits the step limit.</p>
<p>Anthropic's <a href="https://www.anthropic.com/engineering/building-effective-agents" target="_blank" rel="noopener noreferrer">Building effective agents</a> describes agents the same way: models using tools in a loop, with stopping conditions such as a maximum number of iterations. It also recommends starting with LLM APIs directly before adopting a framework, which is the path taken here.</p>
<p>The business case: an online retailer gets a steady stream of "where is my order" tickets, and each one takes a support rep two lookups, the order system and then the carrier. A chatbot without tools guesses. An agent with tools looks it up, and hands anything involving money to a human.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-to-build-an-ai-agent-in-python-in-four-steps">How to build an AI agent in Python in four steps<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#how-to-build-an-ai-agent-in-python-in-four-steps" class="hash-link" aria-label="Direct link to How to build an AI agent in Python in four steps" title="Direct link to How to build an AI agent in Python in four steps">​</a></h2>
<p>Every agent loop, in any framework, repeats the same four steps:</p>
<ol>
<li><strong>Ask.</strong> Send the conversation and the tool list to the model.</li>
<li><strong>Check.</strong> If the reply is text, you are done. If it is a tool call, keep going.</li>
<li><strong>Run.</strong> Execute each requested function with the arguments the model chose.</li>
<li><strong>Send back.</strong> Add the results to the conversation and go back to step 1.</li>
</ol>
<p>The OpenAI <a href="https://developers.openai.com/api/docs/guides/function-calling" target="_blank" rel="noopener noreferrer">function calling guide</a> documents the same cycle, with two details that shape your code: arguments arrive as a JSON string, and a model may call several functions in one turn.</p>
<p>The stop rule is the part most tutorials skip. Every pass is a full model call that resends the whole conversation. At 1,000 tickets a day and three calls per ticket you pay for 3,000 calls a day, and one confused conversation without a cap can burn twenty on its own. A <code>max_steps</code> cap keeps the bill bounded.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-1-install-intelli-and-define-your-tools">Step 1: Install Intelli and define your tools<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#step-1-install-intelli-and-define-your-tools" class="hash-link" aria-label="Direct link to Step 1: Install Intelli and define your tools" title="Direct link to Step 1: Install Intelli and define your tools">​</a></h2>
<p>You need Python 3.10 or newer.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip install intelli</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Keep your data access in normal Python functions and describe each one with a JSON schema. The model reads the descriptions to decide what to call, so write them for a new colleague, not for a linter.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># order_agent.py (part 1)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chatbot_input </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> ChatModelInput</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">utils</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">model_helper </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> is_reasoning_model</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Stand-ins for your order database and your carrier's API</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ORDERS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"A-1001"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"status"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"shipped"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"carrier"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"DHL"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tracking_number"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"DHL-778120"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"A-1002"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"status"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"processing"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"carrier"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tracking_number"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SHIPMENTS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"DHL-778120"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"eta"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"2026-10-03"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"last_scan"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Leipzig hub"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_order_status</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">order_id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    order </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ORDERS</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">order_id</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">upper</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> order </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:#e3116c">f"order </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">order_id</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c"> not found"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_shipment_eta</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tracking_number</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> SHIPMENTS</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tracking_number</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"unknown tracking number"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">TOOL_SPECS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"get_order_status"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"description"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Look up an order by id. Returns status, carrier and tracking number."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"parameters"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"object"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"properties"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"order_id"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"string"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"description"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Order id such as A-1001"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"required"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"order_id"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"get_shipment_eta"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"description"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Get the delivery estimate for a carrier tracking number."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"parameters"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"object"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"properties"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"tracking_number"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"string"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"required"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"tracking_number"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">TOOLS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"get_order_status"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> get_order_status</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"get_shipment_eta"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> get_shipment_eta</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">tools_for</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">provider</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""Wrap the same specs in the format each API expects."""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> provider </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"anthropic"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"description"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"description"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                 </span><span class="token string" style="color:#e3116c">"input_schema"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"parameters"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> TOOL_SPECS</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> is_reasoning_model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># GPT-5 family runs on the Responses API</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">**</span><span class="token plain">t</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> TOOL_SPECS</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> t</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> TOOL_SPECS</span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Intelli passes your tool list to the provider as given, and the APIs expect different shapes: OpenAI chat completions nests the schema under <code>function</code>, GPT-5 models on the Responses API use a flat format, and Anthropic wants <code>input_schema</code>. The <a href="https://www.intellinode.ai/docs/python/chatbot/tool-calling">tool calling docs</a> show each one.</p>
<p><code>tools_for</code> keeps one <code>TOOL_SPECS</code> list and converts at the edge, so adding a tool is one new entry. It uses <code>is_reasoning_model</code>, the same check Intelli uses to route a model to the Responses API.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-2-write-the-agent-loop">Step 2: Write the agent loop<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#step-2-write-the-agent-loop" class="hash-link" aria-label="Direct link to Step 2: Write the agent loop" title="Direct link to Step 2: Write the agent loop">​</a></h2>
<p>This is the whole agent. It stays the same no matter which provider you use.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># order_agent.py (part 2)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SYSTEM </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"You are an order support assistant. Use the tools to answer. "</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token string" style="color:#e3116c">"Never guess an order status or a delivery date."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">run_tool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> raw_args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fn </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> TOOLS</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> fn </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:#e3116c">f"unknown tool </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">name</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> fn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">**</span><span class="token plain">json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">loads</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">raw_args </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"{}"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">JSONDecodeError</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TypeError</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> err</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:#e3116c">f"bad arguments for </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">name</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">err</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">run_agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> question</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> max_steps</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    chat </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ChatModelInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">SYSTEM</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> model</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tools</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">tools_for</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">provider</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                          temperature</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> max_tokens</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    chat</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">add_user_message</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> step </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">max_steps</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> step </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> max_steps </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            chat</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">tools </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># last step: no tools, so the model must answer in text</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        reply </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">chat</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">isinstance</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">reply</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">dict</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tool_response"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> reply  </span><span class="token comment" style="color:#999988;font-style:italic"># plain text means the agent is done</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        results </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> call </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> reply</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"tool_calls"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># a model can ask for several tools at once</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> call</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> call</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"function"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"arguments"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            output </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> run_tool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"step </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">step </span><span class="token string-interpolation interpolation operator" style="color:#393A34">+</span><span class="token string-interpolation interpolation"> </span><span class="token string-interpolation interpolation number" style="color:#36acaa">1</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">name</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">(</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">args</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">) -&gt; </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">output</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            results</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">append</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"tool"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"arguments"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"result"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> output</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        chat</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">add_assistant_message</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Calling tools: "</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">", "</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">r</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"tool"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> r </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> results</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        chat</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">add_user_message</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Tool results: "</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dumps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">results</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                              </span><span class="token string" style="color:#e3116c">"\nCall another tool if you still need data. Otherwise answer the question."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Sorry, I could not finish this request."</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Why the loop looks like this:</p>
<ul>
<li><code>bot.chat(chat)</code> always returns a list. When the model wants a tool, the first item is a dict with <code>"type": "tool_response"</code> and a <code>tool_calls</code> list, each call carrying <code>function.name</code> and <code>function.arguments</code> as a JSON string. The shape is the same for OpenAI chat completions, the GPT-5 Responses API and Anthropic, so the loop has no provider branches.</li>
<li>The inner loop over <code>tool_calls</code> matters. Claude can ask for several lookups in one turn, such as two order ids in one question, and Intelli collects every <code>tool_use</code> block into that list. Read only the first call and you silently drop work.</li>
<li>Results go back as a user message, because <code>ChatModelInput</code> has no tool role message with a <code>tool_call_id</code>. You lose native tool result threading. In exchange the history is plain text on every provider, and removing tools on the last step cannot orphan a tool call.</li>
<li>On the final step, <code>chat.tools = None</code> forces a text answer, so the customer always gets a reply.</li>
<li><code>run_tool</code> returns errors as data. A wrong order id becomes a message the model can react to instead of an exception that kills the request.</li>
</ul>
<p><code>temperature=0</code> is safe across providers. Intelli leaves it out for GPT-5 models and for the Claude 5 family, which reject sampling parameters.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-3-run-the-agent">Step 3: Run the agent<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#step-3-run-the-agent" class="hash-link" aria-label="Direct link to Step 3: Run the agent" title="Direct link to Step 3: Run the agent">​</a></h2>
<p>Read keys from environment variables, never from source files.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># main.py</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> os</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">function</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chatbot </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Chatbot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ChatProvider</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> order_agent </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> run_agent</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"OPENAI_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ChatProvider</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">OPENAI</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> options</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"timeout"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">run_agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"gpt-5.5"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Where is my order A-1001 and when will it arrive?"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Each tool call prints a line, so you can watch the agent work. The intended path is two tool steps: <code>get_order_status</code> returns the tracking number <code>DHL-778120</code>, <code>get_shipment_eta</code> uses it, and the third model call answers in text. The default timeout for a Chatbot call is 180 seconds. Set a lower one, as above, for anything customer-facing.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="run-the-same-agent-on-openai-claude-or-a-local-model">Run the same agent on OpenAI, Claude or a local model<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#run-the-same-agent-on-openai-claude-or-a-local-model" class="hash-link" aria-label="Direct link to Run the same agent on OpenAI, Claude or a local model" title="Direct link to Run the same agent on OpenAI, Claude or a local model">​</a></h2>
<p>Provider choice keeps changing for business reasons: price, rate limits, data residency, or a new model that is better at your task. The loop does not know which provider it talks to, so switching is a constructor change.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># switch.py</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> os</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">function</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chatbot </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Chatbot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ChatProvider</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">utils</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">proxy_helper </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> ProxyHelper</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> order_agent </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> run_agent</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">question </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Where is my order A-1001 and when will it arrive?"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># OpenAI: GPT-5 models go through the Responses API</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">openai_bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"OPENAI_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ChatProvider</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">OPENAI</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">run_agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">openai_bot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"gpt-5.5"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Anthropic: same loop, tool specs converted by tools_for()</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">claude_bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"ANTHROPIC_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ChatProvider</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ANTHROPIC</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">run_agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">claude_bot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"claude-sonnet-5"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Local: Ollama's OpenAI-compatible server, no key and no bill</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ProxyHelper</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">set_openai_proxy_values</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"base"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"http://localhost:11434"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local_bot </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"ollama"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ChatProvider</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">OPENAI</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> options</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"proxy_helper"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> local</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">run_agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">local_bot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"qwen2.5:0.5b"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> question</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>A few things happen behind those three calls:</p>
<ul>
<li><code>gpt-5.5</code> goes to the Responses API with the flat tool format. On that path Intelli merges all messages into one input string, so role structure is lost in long chats. GPT-5 models also accept <code>reasoning_effort</code> and <code>verbosity</code> on <code>ChatModelInput</code>.</li>
<li>For Claude, <code>tools_for</code> switches to <code>input_schema</code>, and parallel calls arrive in the same <code>tool_calls</code> list.</li>
<li>The local bot is the OpenAI provider pointed at Ollama through a fresh <code>ProxyHelper</code>, with a dummy key. Ollama supports tools on its OpenAI-compatible endpoint, as its <a href="https://ollama.com/blog/tool-support" target="_blank" rel="noopener noreferrer">tool support announcement</a> explains. Keep the model id from starting with <code>gpt-5</code> (that routes to the Responses API), and skip Intelli's <code>vllm</code> provider here, because it does not forward tools.</li>
</ul>
<p>The <a href="https://www.intellinode.ai/docs/python/chatbot/model-switching">model switching page</a> covers the other providers for plain chat.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="what-a-05b-local-model-did-in-our-tests">What a 0.5B local model did in our tests<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#what-a-05b-local-model-did-in-our-tests" class="hash-link" aria-label="Direct link to What a 0.5B local model did in our tests" title="Direct link to What a 0.5B local model did in our tests">​</a></h3>
<p>We ran this exact loop against <code>qwen2.5:0.5b</code> on a laptop through Ollama. The results are a useful warning.</p>
<ul>
<li>The first lookup worked in all 35 runs: the model called <code>get_order_status</code> with <code>A-1001</code> and replied sensibly.</li>
<li>It never made the second hop. Across all 35 runs and three different follow-up messages, it never called <code>get_shipment_eta</code>, so it never had a real delivery date.</li>
<li>It sometimes invented one anyway: "by the next business day" with one follow-up message, and "within 3 days" in every run after we added the refund tool from the next section.</li>
<li>One extra sentence in the system prompt, "Keep answers under three sentences", made it stop calling tools in 3 of 3 runs and ask for the order id it had just been given.</li>
</ul>
<p>At <code>temperature=0</code> the answers were identical run after run, so retrying does not help. The loop was fine; the model was too small to plan.</p>
<p>Use a local model for development, CI and demos where you pay nothing per token, and test the model you will ship before you trust any multi-step behavior. Larger local models are worth trying, but measure them on your own questions.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="add-guardrails-before-you-ship">Add guardrails before you ship<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#add-guardrails-before-you-ship" class="hash-link" aria-label="Direct link to Add guardrails before you ship" title="Direct link to Add guardrails before you ship">​</a></h2>
<p>The loop already has a step cap and a text-only last step. The remaining checks belong in <code>run_tool</code>, the one place where model output turns into real actions. Add this to <code>order_agent.py</code> and delete the first <code>run_tool</code>:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># order_agent.py (guardrails, replaces the first run_tool)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> logging</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> time</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">log </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> logging</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">getLogger</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"order_agent"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ORDER_ID </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">compile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r"^[A-Z]-\d{4}$"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">NEEDS_APPROVAL </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"issue_refund"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">issue_refund</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">order_id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> amount</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"order_id"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> order_id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"refunded"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> amount</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># your payments API call goes here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">TOOLS</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"issue_refund"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> issue_refund</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">TOOL_SPECS</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">append</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"issue_refund"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"description"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Refund an order. Needs human approval."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"parameters"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"object"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token string" style="color:#e3116c">"properties"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"order_id"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"string"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"amount"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"type"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"number"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                   </span><span class="token string" style="color:#e3116c">"required"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"order_id"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"amount"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">run_tool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> raw_args</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> approve</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fn </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> TOOLS</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> fn </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:#e3116c">f"unknown tool </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">name</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        args </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">loads</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">raw_args </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"{}"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">JSONDecodeError</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"arguments were not valid JSON"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token builtin">isinstance</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">args</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">dict</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"arguments must be a JSON object"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"order_id"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> args </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> ORDER_ID</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword" style="color:#00009f">match</span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">args</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"order_id"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">strip</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">upper</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"order ids look like A-1001"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> name </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> NEEDS_APPROVAL </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">approve </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> approve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"refunds need a human. Tell the customer a teammate will follow up."</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    started </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">perf_counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> fn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">**</span><span class="token plain">args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">except</span><span class="token plain"> TypeError </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> err</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:#e3116c">f"bad arguments for </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">name</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">err</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    log</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">info</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"tool=%s args=%s ms=%.0f"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">perf_counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> started</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> result</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>What this buys you:</p>
<ul>
<li><strong>Argument validation.</strong> The model chose those arguments. Check them like user input before they reach a database.</li>
<li><strong>An approval gate for side effects.</strong> Read-only tools run freely. Anything that moves money, sends email or changes records goes in <code>NEEDS_APPROVAL</code>. Because <code>approve</code> defaults to <code>None</code> and <code>run_agent</code> never passes one, every refund is refused until you wire in a real approval step, such as a button in your support console.</li>
<li><strong>A log line per tool run.</strong> Tool name, arguments and latency are what you need when a customer says the bot gave them the wrong date. Call <code>logging.basicConfig(level=logging.INFO)</code> once at startup to see them.</li>
<li><strong>Bounded cost.</strong> <code>max_tokens=1000</code> bounds each reply and <code>max_steps</code> bounds the number of calls. Together they cap the worst-case cost of any single question, and <code>options={"timeout": 60}</code> caps how long each call can wait.</li>
</ul>
<p>One gap to plan for: the Chatbot returns message content only, not the provider's token usage. For cost per conversation, count model calls per question in your own logs and reconcile them with the provider's usage dashboard.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="when-one-agent-is-not-enough">When one agent is not enough<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#when-one-agent-is-not-enough" class="hash-link" aria-label="Direct link to When one agent is not enough" title="Direct link to When one agent is not enough">​</a></h2>
<p>A single loop handles one job well. Real processes chain jobs: look up the order, then write the customer email in your brand voice, maybe on a different model. Intelli flows let you wrap the loop you already wrote as one step.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># flow.py</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> os</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">flow </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Agent</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> CustomAgent</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> TextTaskInput</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> intelli</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">function</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">chatbot </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Chatbot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ChatProvider</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> order_agent </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> run_agent</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">OrderLookup</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">CustomAgent</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""Runs the tool calling loop as one step of a flow."""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> model</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token builtin">super</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">agent_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">bot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">model </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> bot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> model</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">execute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> agent_input</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> new_params</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> run_agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">bot</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> agent_input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">desc</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">lookup </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> OrderLookup</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Chatbot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"OPENAI_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ChatProvider</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">OPENAI</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"gpt-5.5"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">writer </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Agent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"text"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"anthropic"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"You write short, friendly customer emails."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"key"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"ANTHROPIC_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"model"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"claude-sonnet-5"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">flow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Flow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tasks</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"lookup"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Where is my order A-1001 and when will it arrive?"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lookup</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"email"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">TextTaskInput</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Write a three line email to the customer with this update."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> writer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    map_paths</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"lookup"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"email"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flow</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">start</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"email"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"output"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p><code>CustomAgent</code> turns any Python code into a flow step, so your tested loop runs unchanged inside it. The flow passes the lookup answer into the writer's prompt, and <code>result</code> holds one entry per task with its <code>output</code>. Here the lookup runs on GPT-5.5 and the email on Claude, a split you might pick when one model is cheaper for tool use and another writes better copy. The <a href="https://www.intellinode.ai/docs/python/flows/agent">Agent docs</a> list the built-in agent types.</p>
<p>Where you go next depends on your tools. If they already live behind an MCP server (the <a href="https://modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer">Model Context Protocol</a> is an open standard for connecting AI apps to tools and data), <code>ToolDynamicConnector</code> can send the flow to an MCP task when the model calls a tool, and a small <code>pre_process</code> step maps the call's name and arguments onto that task. See <a href="https://www.intellinode.ai/docs/python/flows/dynamic-tool">dynamic tool routing</a> and the <a href="https://www.intellinode.ai/docs/python/mcp/client">MCP client</a>. If you need branching, loops and parallel steps, read <a href="https://www.intellinode.ai/articles/agentic-workflow-python">Agentic Workflows in Python Without LangGraph</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="write-your-own-loop-or-adopt-a-framework">Write your own loop or adopt a framework<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#write-your-own-loop-or-adopt-a-framework" class="hash-link" aria-label="Direct link to Write your own loop or adopt a framework" title="Direct link to Write your own loop or adopt a framework">​</a></h2>
<p><strong>The raw provider SDK is enough</strong> if you are committed to one provider and have a couple of tools. You can write this loop against the OpenAI or Anthropic SDK in an afternoon. The hard part of shipping an agent is the tools and the test questions, not the loop.</p>
<p><strong>Intelli earns its place</strong> when you want to keep provider choice open: one reply shape across OpenAI, Anthropic and OpenAI-compatible servers, one constructor to switch, and a path from a single loop to flows and MCP without a rewrite. Your team writes plain Python functions, not a graph DSL.</p>
<p><strong>Know the limits.</strong> Tool calls are parsed for OpenAI (chat completions and Responses), Anthropic and OpenAI-compatible endpoints. Intelli forwards tools to Gemini and Mistral but does not turn their tool calls into <code>tool_response</code> today, so this loop will not work on them yet. There is no native tool result message, replies carry no token usage, and the community is far smaller: <code>openai-agents</code> alone had about 12 million PyPI downloads in the last 30 days, according to <a href="https://pypistats.org/packages/openai-agents" target="_blank" rel="noopener noreferrer">pypistats</a> on September 30, 2026.</p>
<p><strong>Pick a bigger framework when its built-ins match your needs.</strong> The <a href="https://openai.github.io/openai-agents-python/" target="_blank" rel="noopener noreferrer">OpenAI Agents SDK</a> ships handoffs between agents, guardrails, sessions and tracing, and supports other providers through adapters such as LiteLLM. The <a href="https://code.claude.com/docs/en/agent-sdk/overview" target="_blank" rel="noopener noreferrer">Claude Agent SDK</a> gives you the agent loop, built-in file, shell and web tools, permissions and hooks that power Claude Code, in Python and TypeScript. <a href="https://docs.langchain.com/oss/python/langgraph/overview" target="_blank" rel="noopener noreferrer">LangGraph</a> is a low-level orchestration runtime for long-running, stateful agents, with persistence and human-in-the-loop support. If you need a workflow that resumes after a crash, LangGraph is built for that and a hand-written loop is not.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="faq">FAQ<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#faq" class="hash-link" aria-label="Direct link to FAQ" title="Direct link to FAQ">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="do-i-need-langchain-or-another-framework-to-build-an-ai-agent-in-python">Do I need LangChain or another framework to build an AI agent in Python?<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#do-i-need-langchain-or-another-framework-to-build-an-ai-agent-in-python" class="hash-link" aria-label="Direct link to Do I need LangChain or another framework to build an AI agent in Python?" title="Direct link to Do I need LangChain or another framework to build an AI agent in Python?">​</a></h3>
<p>No. An agent is a loop around model calls, and the version here fits in one file. Frameworks pay off when you need their built-ins, such as tracing, handoffs or durable state. Adopt one when you can name the feature you are missing.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="what-is-the-best-model-for-a-python-ai-agent">What is the best model for a Python AI agent?<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#what-is-the-best-model-for-a-python-ai-agent" class="hash-link" aria-label="Direct link to What is the best model for a Python AI agent?" title="Direct link to What is the best model for a Python AI agent?">​</a></h3>
<p>Start with a strong hosted model so you know the loop and tools work, then try cheaper models on the same test questions. A model can chat well and still fail at tool use, as the 0.5B test above shows, so measure tool calls directly. Because switching is a constructor change, comparing models is cheap.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="can-i-create-an-ai-agent-locally-in-python">Can I create an AI agent locally in Python?<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#can-i-create-an-ai-agent-locally-in-python" class="hash-link" aria-label="Direct link to Can I create an AI agent locally in Python?" title="Direct link to Can I create an AI agent locally in Python?">​</a></h3>
<p>Yes. Point the OpenAI provider at Ollama with <code>ProxyHelper</code>, as shown above, and the agent runs with no API key and no per-token cost. In our tests a 0.5B model called one tool reliably but never chained two, so use a larger local model for multi-step tasks and test it on your own data.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="how-many-steps-should-an-ai-agent-loop-allow">How many steps should an AI agent loop allow?<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#how-many-steps-should-an-ai-agent-loop-allow" class="hash-link" aria-label="Direct link to How many steps should an AI agent loop allow?" title="Direct link to How many steps should an AI agent loop allow?">​</a></h3>
<p>Set the cap a little above the longest path you expect. The order agent needs two tool steps plus an answer, so <code>max_steps=5</code> leaves room for one retry. Log how often requests hit the cap, because a rising number usually means a tool description or error message is confusing the model.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="how-much-does-it-cost-to-run-an-ai-agent">How much does it cost to run an AI agent?<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#how-much-does-it-cost-to-run-an-ai-agent" class="hash-link" aria-label="Direct link to How much does it cost to run an AI agent?" title="Direct link to How much does it cost to run an AI agent?">​</a></h3>
<p>Each step is one model call that resends the growing conversation, so a two-tool answer costs about three calls. Multiply by your ticket volume and your provider's token prices for a monthly estimate. Step caps, short tool results and a local model for development keep it predictable.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="start-building">Start building<a href="https://www.intellinode.ai/articles/how-to-build-ai-agent-python#start-building" class="hash-link" aria-label="Direct link to Start building" title="Direct link to Start building">​</a></h2>
<p>Install the package and copy <code>order_agent.py</code> from this article:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip install intelli</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Then swap the fake <code>ORDERS</code> dict for your real order API, keep <code>max_steps</code> and the approval gate, and run it against a local model first. The <a href="https://www.intellinode.ai/docs/python/chatbot/tool-calling">tool calling guide</a> covers <code>tool_choice</code> and each provider's tool format, and the <a href="https://www.intellinode.ai/docs/python/get-started/installation">installation page</a> lists the optional extras, such as <code>pip install "intelli[mcp]"</code> for MCP support.</p>]]></content>
        <author>
            <name>IntelliNode Team</name>
            <uri>https://github.com/intelligentnode</uri>
        </author>
        <category label="Python" term="Python"/>
        <category label="AI Agents" term="AI Agents"/>
        <category label="Tool Calling" term="Tool Calling"/>
        <category label="Tutorial" term="Tutorial"/>
    </entry>
</feed>