<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Natarajan Murugesan</title>
    <description>The latest articles on DEV Community by Natarajan Murugesan (@natarajan_murugesan_b00c4).</description>
    <link>https://dev.to/natarajan_murugesan_b00c4</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3816382%2Ff3bdc1b8-af7e-4dae-97f3-fdfa09bdca77.jpg</url>
      <title>DEV Community: Natarajan Murugesan</title>
      <link>https://dev.to/natarajan_murugesan_b00c4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/natarajan_murugesan_b00c4"/>
    <language>en</language>
    <item>
      <title>Build a Practical AI Agent with Gemma 4, Real Tools, and a Local LLM</title>
      <dc:creator>Natarajan Murugesan</dc:creator>
      <pubDate>Sat, 11 Apr 2026 19:35:38 +0000</pubDate>
      <link>https://dev.to/natarajan_murugesan_b00c4/build-a-practical-ai-agent-with-gemma-4-real-tools-and-a-local-llm-31k4</link>
      <guid>https://dev.to/natarajan_murugesan_b00c4/build-a-practical-ai-agent-with-gemma-4-real-tools-and-a-local-llm-31k4</guid>
      <description>&lt;h2&gt;
  
  
  Build a Practical AI Agent with Real Tools and a Local LLM
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;How to combine Tavily, OpenWeatherMap, and LangGraph into a clean local-first workflow&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most local LLM demos are either too simple to be useful or too complex to reuse.&lt;/p&gt;

&lt;p&gt;I wanted something in the middle: a practical AI agent that can search the web, check live weather, and still keep the reasoning layer local. So I built a workflow using LangGraph, Tavily, OpenWeatherMap, and a local LLM running through Ollama.&lt;/p&gt;

&lt;p&gt;The result is a clean local-first architecture where tools provide real-time facts and the model turns them into useful answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Local LLMs are great for privacy, experimentation, and cost control. But on their own, they do not know live weather or fresh web results.&lt;/p&gt;

&lt;p&gt;That is where external tools become valuable.&lt;/p&gt;

&lt;p&gt;In this setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tavily provides current web search results&lt;/li&gt;
&lt;li&gt;OpenWeatherMap provides live weather data&lt;/li&gt;
&lt;li&gt;the local LLM handles reasoning and phrasing&lt;/li&gt;
&lt;li&gt;LangGraph orchestrates the workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This combination creates something much more useful than a plain local chatbot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture at a Glance
&lt;/h2&gt;

&lt;p&gt;Before looking at the code, here is the core workflow.&lt;/p&gt;

&lt;p&gt;Rather than asking the model to do everything in one opaque step, I split the system into clear responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;router node&lt;/strong&gt; decides what kind of request it is&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;Tavily node&lt;/strong&gt; fetches fresh web results&lt;/li&gt;
&lt;li&gt;an &lt;strong&gt;OpenWeatherMap node&lt;/strong&gt; fetches live weather data&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;local LLM node&lt;/strong&gt; combines those results into the final answer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes the system easier to debug, easier to extend, and much easier to reason about.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrn4fsyhl3lt2npvwlfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrn4fsyhl3lt2npvwlfb.png" alt=" " width="800" height="711"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key design principle is simple: &lt;strong&gt;tools provide facts, the local LLM provides reasoning&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this architecture works
&lt;/h2&gt;

&lt;p&gt;This pattern works because it separates responsibility cleanly.&lt;/p&gt;

&lt;p&gt;The model is no longer expected to hallucinate current weather or pretend it knows the latest web information. Instead, tools provide live facts and the LLM focuses on synthesis, reasoning, and response generation.&lt;/p&gt;

&lt;p&gt;That separation makes the system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;more reliable&lt;/li&gt;
&lt;li&gt;easier to debug&lt;/li&gt;
&lt;li&gt;easier to evolve over time&lt;/li&gt;
&lt;li&gt;more practical for real applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example use cases
&lt;/h2&gt;

&lt;p&gt;This kind of agent can answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“What is LangGraph and why is it useful?”&lt;/li&gt;
&lt;li&gt;“What is the weather in Amsterdam today?”&lt;/li&gt;
&lt;li&gt;“Summarize today’s AI news and tell me whether I need a jacket this evening.”&lt;/li&gt;
&lt;li&gt;“Give me a quick weather summary and travel advice for Rotterdam.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is where the stack becomes more interesting than a typical chatbot.&lt;/p&gt;

&lt;h2&gt;
  
  
  State schema
&lt;/h2&gt;

&lt;p&gt;A simple state schema can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing_extensions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TypedDict&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;search_results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;weather_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the workflow explicit and makes it easier to inspect what each node is contributing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Router node
&lt;/h2&gt;

&lt;p&gt;The router decides whether the request needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;web search&lt;/li&gt;
&lt;li&gt;weather lookup&lt;/li&gt;
&lt;li&gt;both&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a simple keyword-based version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;router_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;has_weather&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;forecast&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jacket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;has_search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;what is&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;news&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;explain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;why&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;has_weather&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;has_search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;both&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;has_weather&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is intentionally simple. A more advanced version could use an LLM-based classifier or structured intent routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tavily search node
&lt;/h2&gt;

&lt;p&gt;Tavily is a clean option for search in AI workflows because it returns structured results that are easy to pass into the next node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tavily&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TavilyClient&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;tavily&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TavilyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TAVILY_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tavily_search_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tavily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_results&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;snippet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Title: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;URL: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Snippet: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snippet&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This node is responsible only for search. It does not try to answer the question yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenWeatherMap node
&lt;/h2&gt;

&lt;p&gt;The OpenWeatherMap node handles live weather data. In this version, the city is resolved to coordinates first, and then current weather is fetched.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;OPENWEATHER_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENWEATHER_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_coordinates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://api.openweathermap.org/geo/1.0/direct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;appid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OPENWEATHER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No coordinates found for city: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;openweather_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Amsterdam,NL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_coordinates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.openweathermap.org/data/2.5/weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;appid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OPENWEATHER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;units&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metric&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;
Location: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Temperature: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; °C
Feels like: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;feels_like&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; °C
Condition: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Humidity: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;humidity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%
Wind speed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wind&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;speed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; m/s
&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This node only retrieves and formats facts. It does not do interpretation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local LLM answer node
&lt;/h2&gt;

&lt;p&gt;Once the tool results are available, the local LLM can turn them into a useful final answer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_ollama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOllama&lt;/span&gt;

&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOllama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemma4:e4b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# replace with your local gemma4 tag if available
&lt;/span&gt;    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;answer_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;
You are a helpful AI assistant.

Use the available tool results below to answer the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s request clearly.

User request:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Web search results:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Weather data:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the point where the local model adds value. It is not fetching data. It is turning tool outputs into a coherent response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the LangGraph workflow
&lt;/h2&gt;

&lt;p&gt;Here is how the graph can be wired together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.graph&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;START&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;router&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;router_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tavily_search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tavily_search_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openweather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;openweather_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answer_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;START&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;router&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route_after_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_conditional_edges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;router&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;route_after_router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tavily_search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openweather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;both&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tavily_search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tavily_search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openweather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openweather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version keeps the orchestration explicit. You can easily expand it later with retries, memory, more tools, or better routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the agent
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize today&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s AI news and tell me if I need a jacket in Amsterdam&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Amsterdam,NL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search_results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example output
&lt;/h2&gt;

&lt;p&gt;For a weather-related prompt, the agent produced this response:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The weather in Amsterdam today is expected to be a &lt;strong&gt;clear sky&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Temperature:&lt;/strong&gt; 9.22 °C
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feels like:&lt;/strong&gt; 4.85 °C
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wind speed:&lt;/strong&gt; 11.62 m/s
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Humidity:&lt;/strong&gt; 71%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Should you carry a jacket?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes. The wind makes it feel significantly colder than the actual temperature, so a jacket is definitely recommended.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is exactly the kind of output I was aiming for: not just raw tool data, but a useful, human-readable answer grounded in live information.&lt;/p&gt;

&lt;p&gt;At that point, the agent can combine fresh web information, live weather data, and local reasoning in one workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I like this pattern
&lt;/h2&gt;

&lt;p&gt;For me, this is where local LLMs become much more interesting.&lt;/p&gt;

&lt;p&gt;Not as isolated chatbots.&lt;br&gt;
Not as black-box “agents.”&lt;br&gt;
But as reasoning layers connected to real tools.&lt;/p&gt;

&lt;p&gt;This pattern gives me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local-first reasoning&lt;/li&gt;
&lt;li&gt;explicit orchestration&lt;/li&gt;
&lt;li&gt;practical utility&lt;/li&gt;
&lt;li&gt;easier debugging&lt;/li&gt;
&lt;li&gt;a strong path for extension&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes it useful both as a learning project and as a foundation for more advanced AI applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;This project reminded me that useful AI agents do not need to be huge or mysterious.&lt;/p&gt;

&lt;p&gt;A small graph, a couple of well-chosen tools, and a local LLM are enough to build something genuinely practical.&lt;/p&gt;

&lt;p&gt;LangGraph gives the structure. Tavily and OpenWeatherMap provide live facts. The local model turns those facts into useful answers.&lt;/p&gt;

&lt;p&gt;That feels like a strong foundation for local-first AI systems.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langgraph</category>
      <category>ollama</category>
      <category>python</category>
    </item>
    <item>
      <title>Stop met tokens verspillen in OpenClaw</title>
      <dc:creator>Natarajan Murugesan</dc:creator>
      <pubDate>Sun, 15 Mar 2026 10:58:02 +0000</pubDate>
      <link>https://dev.to/natarajan_murugesan_b00c4/stop-met-tokens-verspillen-in-openclaw-39oe</link>
      <guid>https://dev.to/natarajan_murugesan_b00c4/stop-met-tokens-verspillen-in-openclaw-39oe</guid>
      <description>&lt;h2&gt;
  
  
  OpenClaw Agents: verlaag LLM-kosten zonder kwaliteit op te offeren
&lt;/h2&gt;

&lt;p&gt;De meeste teams proberen LLM-kosten te verlagen door prompts korter te maken, outputs in te korten of goedkopere modellen te gebruiken.&lt;/p&gt;

&lt;p&gt;Dat helpt een beetje.&lt;/p&gt;

&lt;p&gt;Maar in echte OpenClaw-omgevingen komt de grootste verspilling meestal ergens anders vandaan: &lt;strong&gt;inefficiënt runtime-gedrag&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Kapotte fallback-ketens, provider/auth-mismatches, verouderde sessiecontext en inconsistente agentconfiguraties kunnen ongemerkt zorgen voor meer retries, hoger tokengebruik, meer latency en ruis in logs.&lt;/p&gt;

&lt;p&gt;De praktische les is simpel:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Optimaliseer eerst runtime-gedrag. Optimaliseer prompts daarna.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Waar de extra kosten meestal vandaan komen
&lt;/h2&gt;

&lt;p&gt;In veel OpenClaw-deployments wordt vermijdbare uitgave veroorzaakt door:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gemengde model/provider-routing&lt;/li&gt;
&lt;li&gt;fallbacks die wel geconfigureerd zijn, maar in de praktijk niet werken&lt;/li&gt;
&lt;li&gt;langlevende sessies die oude geschiedenis meenemen&lt;/li&gt;
&lt;li&gt;verschillende instellingen tussen vergelijkbare agents&lt;/li&gt;
&lt;li&gt;terugkerende auth-, failover- of timeoutproblemen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deze problemen creëren verborgen overhead nog vóórdat het model überhaupt een antwoord genereert.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wat je als eerste moet oplossen
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Gebruik één geldige geauthenticeerde route
&lt;/h3&gt;

&lt;p&gt;Je primaire model moet overeenkomen met de credentials die de agent daadwerkelijk heeft.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stel een geldige &lt;code&gt;model.primary&lt;/code&gt; in&lt;/li&gt;
&lt;li&gt;Verwijder fallbacks die afhankelijk zijn van ontbrekende credentials&lt;/li&gt;
&lt;li&gt;Houd routing deterministisch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alleen dit al kan mislukte pogingen en rumoerige uitvoerpaden verminderen.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Houd fallback-ontwerp minimaal
&lt;/h3&gt;

&lt;p&gt;Fallback is bedoeld voor veerkracht, niet voor normale routing.&lt;/p&gt;

&lt;p&gt;Een goede vuistregel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;houd de fallback-lijst op &lt;code&gt;0–2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;neem alleen geteste, bruikbare entries op&lt;/li&gt;
&lt;li&gt;vermijd cross-provider fallback tenzij die volledig ondersteund wordt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lange fallback-ketens verhogen de kosten vaak meer dan dat ze risico verlagen.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Beperk de groei van context
&lt;/h3&gt;

&lt;p&gt;Verouderde geschiedenis verhoogt stilletjes het aantal inputtokens.&lt;/p&gt;

&lt;p&gt;Een praktisch patroon is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;contextPruning.mode = cache-ttl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;contextPruning.ttl = 5m&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compaction.mode = safeguard&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dit helpt prompt-bloat te voorkomen in chatintensieve omgevingen.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Reset inactieve sessies
&lt;/h3&gt;

&lt;p&gt;Als sessies te lang actief blijven, blijven ze oude context meeslepen.&lt;/p&gt;

&lt;p&gt;Een nuttige instelling is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;session.reset.idleMinutes = 15&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maak ook verouderde sessies leeg na grote configuratiewijzigingen, zodat oude metadata nieuwe runs niet beïnvloedt.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Lijn multi-agentbeleid op elkaar af
&lt;/h3&gt;

&lt;p&gt;Als meerdere agents vergelijkbaar werk doen, houd ze dan op hetzelfde routing- en sessiebeleid, tenzij er een echte reden is om daarvan af te wijken.&lt;/p&gt;

&lt;p&gt;Dat maakt gedrag voorspelbaarder over Slack, Telegram of andere kanalen heen.&lt;/p&gt;




&lt;h2&gt;
  
  
  Oud versus nieuw
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimensie&lt;/th&gt;
&lt;th&gt;Oud gedrag&lt;/th&gt;
&lt;th&gt;Geoptimaliseerd gedrag&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Primaire routing&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Gemengde routes&lt;/td&gt;
&lt;td&gt;Eén geauthenticeerde route&lt;/td&gt;
&lt;td&gt;Duidelijk uitvoerpad&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Fallback-afhandeling&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Ongeldige fallback-pogingen&lt;/td&gt;
&lt;td&gt;Kapotte fallback verwijderd&lt;/td&gt;
&lt;td&gt;Minder retry-verspilling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Foutpatroon&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Terugkerende auth/failover-ruis&lt;/td&gt;
&lt;td&gt;Schonere logs&lt;/td&gt;
&lt;td&gt;Makkelijkere triage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Contexttrend&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Blijft groeien&lt;/td&gt;
&lt;td&gt;TTL pruning + compaction&lt;/td&gt;
&lt;td&gt;Minder prompt-bloat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Idle-gedrag&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Verouderde sessies blijven bestaan&lt;/td&gt;
&lt;td&gt;Idle reset na 15 min&lt;/td&gt;
&lt;td&gt;Lager basis-tokengebruik&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Agentconsistentie&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Drift tussen agents&lt;/td&gt;
&lt;td&gt;Gedeeld beleid&lt;/td&gt;
&lt;td&gt;Voorspelbaardere operaties&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Hoe je de wijziging valideert
&lt;/h2&gt;

&lt;p&gt;Na het bijwerken van de configuratie:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;voer &lt;code&gt;openclaw status&lt;/code&gt; uit&lt;/li&gt;
&lt;li&gt;bevestig de actieve modelroute&lt;/li&gt;
&lt;li&gt;bekijk enkele minuten de logs&lt;/li&gt;
&lt;li&gt;controleer op auth-fouten, failovers en timeouts&lt;/li&gt;
&lt;li&gt;verifieer dat nieuwe sessies schoon starten&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Een configuratie kan er correct uitzien en toch nog tokens verspillen tijdens runtime, dus validatie is belangrijk.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wat je moet meten
&lt;/h2&gt;

&lt;p&gt;Een eenvoudige KPI-set is al voldoende:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gemiddeld aantal inputtokens per beurt&lt;/li&gt;
&lt;li&gt;aantal failover-fouten per dag&lt;/li&gt;
&lt;li&gt;aantal auth-mismatches per dag&lt;/li&gt;
&lt;li&gt;aantal timeouts per dag&lt;/li&gt;
&lt;li&gt;cache read ratio&lt;/li&gt;
&lt;li&gt;kosten per 100 gesprekken&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nuttige formules:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token-efficiëntie&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nuttige antwoorden / Totaal aantal inputtokens
(Mislukte pogingen / Totaal aantal pogingen) * 100
((Huidige week - Vorige week) / Vorige week) * 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Aanbevolen reviewritme:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dagelijks:&lt;/strong&gt; fouten en timeouts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wekelijks:&lt;/strong&gt; token- en kostentrends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maandelijks:&lt;/strong&gt; routingbeleid en modelreview&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Belangrijkste conclusie
&lt;/h2&gt;

&lt;p&gt;Kostenverlaging in OpenClaw is meestal niet eerst een promptprobleem.&lt;/p&gt;

&lt;p&gt;Het is een probleem van uitvoeringsdiscipline.&lt;/p&gt;

&lt;p&gt;De grootste besparingen komen meestal van:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;model/provider/auth-afstemming&lt;/li&gt;
&lt;li&gt;kort en geldig fallback-ontwerp&lt;/li&gt;
&lt;li&gt;context pruning en compaction&lt;/li&gt;
&lt;li&gt;sessie-resetbeleid&lt;/li&gt;
&lt;li&gt;consistentie tussen agents&lt;/li&gt;
&lt;li&gt;doorlopende meting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Als je die dingen eerst oplost, krijg je meestal tegelijk lagere kosten, meer stabiliteit en eenvoudiger beheer.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
      <category>openclaw</category>
    </item>
    <item>
      <title>Stop Wasting Tokens in OpenClaw</title>
      <dc:creator>Natarajan Murugesan</dc:creator>
      <pubDate>Sat, 14 Mar 2026 23:11:57 +0000</pubDate>
      <link>https://dev.to/natarajan_murugesan_b00c4/stop-wasting-tokens-in-openclaw-5757</link>
      <guid>https://dev.to/natarajan_murugesan_b00c4/stop-wasting-tokens-in-openclaw-5757</guid>
      <description>&lt;h2&gt;
  
  
  OpenClaw Agents: Reduce LLM Cost Without Sacrificing Quality
&lt;/h2&gt;

&lt;p&gt;Most teams try to reduce LLM cost by shortening prompts, cutting output length, or switching to cheaper models.&lt;/p&gt;

&lt;p&gt;That can help a little.&lt;/p&gt;

&lt;p&gt;But in real OpenClaw setups, the biggest waste usually comes from somewhere else: &lt;strong&gt;runtime inefficiency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Broken fallback chains, provider/auth mismatch, stale session context, and inconsistent agent configuration can quietly increase retries, token usage, latency, and log noise.&lt;/p&gt;

&lt;p&gt;The practical lesson is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Optimize runtime behavior first. Optimize prompts second.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Where the extra cost usually comes from
&lt;/h2&gt;

&lt;p&gt;In many OpenClaw deployments, avoidable spend is caused by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mixed model/provider routing&lt;/li&gt;
&lt;li&gt;fallbacks that are configured but cannot actually run&lt;/li&gt;
&lt;li&gt;long-lived sessions carrying stale history&lt;/li&gt;
&lt;li&gt;different settings across similar agents&lt;/li&gt;
&lt;li&gt;repeated auth, failover, or timeout issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These problems create hidden overhead before the model even generates a response.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to fix first
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Use one valid authenticated lane
&lt;/h3&gt;

&lt;p&gt;Your primary model should match the credentials the agent actually has.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set a valid &lt;code&gt;model.primary&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Remove fallbacks that depend on missing credentials&lt;/li&gt;
&lt;li&gt;Keep routing deterministic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This alone can reduce failed attempts and noisy execution paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Keep fallback design minimal
&lt;/h3&gt;

&lt;p&gt;Fallback should be for resilience, not normal routing.&lt;/p&gt;

&lt;p&gt;A good rule:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep fallback list to &lt;code&gt;0–2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;only include tested, usable entries&lt;/li&gt;
&lt;li&gt;avoid cross-provider fallback unless fully supported&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Long fallback chains often increase cost more than they reduce risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Control context growth
&lt;/h3&gt;

&lt;p&gt;Stale history silently increases input tokens.&lt;/p&gt;

&lt;p&gt;A practical pattern is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;contextPruning.mode = cache-ttl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;contextPruning.ttl = 5m&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compaction.mode = safeguard&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This helps prevent prompt bloat in chat-heavy environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Reset idle sessions
&lt;/h3&gt;

&lt;p&gt;If sessions stay alive too long, they keep dragging old context forward.&lt;/p&gt;

&lt;p&gt;A useful setting is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;session.reset.idleMinutes = 15&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also clear stale sessions after major config changes so old metadata does not affect new runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Align multi-agent policy
&lt;/h3&gt;

&lt;p&gt;If multiple agents do similar work, keep them on the same routing and session policy unless there is a real reason to differ.&lt;/p&gt;

&lt;p&gt;That makes behavior more predictable across Slack, Telegram, or other channels.&lt;/p&gt;




&lt;h2&gt;
  
  
  Old vs New
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Old behavior&lt;/th&gt;
&lt;th&gt;Optimized behavior&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;em&gt;Primary routing&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mixed lanes&lt;/td&gt;
&lt;td&gt;Single authenticated lane&lt;/td&gt;
&lt;td&gt;Clear execution path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;em&gt;Fallback handling&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Invalid fallback attempts&lt;/td&gt;
&lt;td&gt;Broken fallback removed&lt;/td&gt;
&lt;td&gt;Less retry waste&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;em&gt;Error pattern&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recurring auth/failover noise&lt;/td&gt;
&lt;td&gt;Cleaner logs&lt;/td&gt;
&lt;td&gt;Easier triage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;em&gt;Context trend&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keeps growing&lt;/td&gt;
&lt;td&gt;TTL pruning + compaction&lt;/td&gt;
&lt;td&gt;Lower prompt bloat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;em&gt;Idle behavior&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stale sessions persist&lt;/td&gt;
&lt;td&gt;Idle reset at 15 min&lt;/td&gt;
&lt;td&gt;Lower baseline token use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;em&gt;Agent consistency&lt;/em&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Drift between agents&lt;/td&gt;
&lt;td&gt;Shared policy&lt;/td&gt;
&lt;td&gt;Predictable operations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  How to validate the change
&lt;/h2&gt;

&lt;p&gt;After updating config:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run &lt;code&gt;openclaw status&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;confirm the active model lane&lt;/li&gt;
&lt;li&gt;watch logs for a few minutes&lt;/li&gt;
&lt;li&gt;check for auth errors, failovers, and timeouts&lt;/li&gt;
&lt;li&gt;verify new sessions start clean&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A config can look correct and still waste tokens in runtime, so validation matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to measure
&lt;/h2&gt;

&lt;p&gt;A simple KPI set is enough:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;avg input tokens per turn&lt;/li&gt;
&lt;li&gt;failover errors per day&lt;/li&gt;
&lt;li&gt;auth mismatch errors per day&lt;/li&gt;
&lt;li&gt;timeouts per day&lt;/li&gt;
&lt;li&gt;cache read ratio&lt;/li&gt;
&lt;li&gt;cost per 100 conversations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful formulas:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token efficiency&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Useful responses / Total input tokens
(Failed attempts / Total attempts) * 100
((Current week - Previous week) / Previous week) * 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suggested review cadence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Daily:&lt;/strong&gt; errors and timeouts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weekly:&lt;/strong&gt; token and cost trend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monthly:&lt;/strong&gt; routing policy and model review&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;OpenClaw cost reduction is usually not a prompt problem first.&lt;/p&gt;

&lt;p&gt;It is an execution-discipline problem.&lt;/p&gt;

&lt;p&gt;The biggest savings usually come from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model/provider/auth alignment&lt;/li&gt;
&lt;li&gt;Short and valid fallback design&lt;/li&gt;
&lt;li&gt;Context pruning and compaction&lt;/li&gt;
&lt;li&gt;Session reset policy&lt;/li&gt;
&lt;li&gt;Multi-agent consistency&lt;/li&gt;
&lt;li&gt;Ongoing measurement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you fix those first, you usually get lower cost, better stability, and easier operations at the same time.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openclaw</category>
      <category>agents</category>
      <category>llm</category>
    </item>
    <item>
      <title>Building Multi-Agent Slack Routing with OpenClaw</title>
      <dc:creator>Natarajan Murugesan</dc:creator>
      <pubDate>Tue, 10 Mar 2026 08:46:34 +0000</pubDate>
      <link>https://dev.to/natarajan_murugesan_b00c4/building-multi-agent-slack-routing-with-openclaw-4n5o</link>
      <guid>https://dev.to/natarajan_murugesan_b00c4/building-multi-agent-slack-routing-with-openclaw-4n5o</guid>
      <description>&lt;p&gt;As AI systems evolve, a single assistant is often not enough. Many workflows benefit from multiple specialized agents — each with its own personality, capabilities, and routing logic.&lt;/p&gt;

&lt;p&gt;Recently I experimented with OpenClaw to build a multi-agent Slack integration, where different Slack accounts route conversations to different agents.&lt;/p&gt;

&lt;p&gt;This mini post summarizes the key learnings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent Setup in OpenClaw
&lt;/h2&gt;

&lt;p&gt;The first step was creating two agents:&lt;/p&gt;

&lt;p&gt;main – default assistant&lt;/p&gt;

&lt;p&gt;nila – secondary agent with a different personality&lt;/p&gt;

&lt;p&gt;Basic commands used during setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openclaw agents list
openclaw agents add nila
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once created, we configured bindings to map Slack accounts to specific agents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openclaw agents bind --agent main --bind slack:default
openclaw agents bind --agent nila --bind slack:nila
openclaw agents bindings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key behavior&lt;/p&gt;

&lt;p&gt;Bindings in OpenClaw are exclusive per target.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;slack:default → main agent&lt;/li&gt;
&lt;li&gt;slack:nila → nila agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A binding cannot be owned by multiple agents simultaneously.&lt;/p&gt;

&lt;p&gt;This design prevents routing conflicts and keeps conversation ownership clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Agent Routing Concept
&lt;/h2&gt;

&lt;p&gt;With the bindings configured, OpenClaw can route messages based on the Slack account interacting with the system.&lt;/p&gt;

&lt;p&gt;Example routing flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Slack (default account)
        ↓
   main agent

Slack (nila account)
        ↓
   nila agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach enables &lt;strong&gt;different agent personalities or capabilities within the same workspace&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Slack App Configuration
&lt;/h2&gt;

&lt;p&gt;During setup we encountered a common Slack DM issue:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sending messages to this app has been turned off.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In most cases, this is caused by Slack app configuration or installation state.&lt;/p&gt;

&lt;p&gt;To enable bot DM interactions correctly, the following settings are required.&lt;br&gt;
Required Slack App Settings&lt;/p&gt;

&lt;p&gt;✔ App Home messages enabled&lt;br&gt;
✔ Socket Mode enabled with a valid xapp- token&lt;br&gt;
✔ Required bot scopes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;chat:write&lt;/li&gt;
&lt;li&gt;im:read&lt;/li&gt;
&lt;li&gt;im:history&lt;/li&gt;
&lt;li&gt;im:write&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After modifying scopes or permissions, always:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reinstall the Slack app&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Otherwise the new permissions will not take effect.&lt;/p&gt;
&lt;h2&gt;
  
  
  Debugging and Diagnostics
&lt;/h2&gt;

&lt;p&gt;OpenClaw provides useful commands for troubleshooting routing and connectivity issues.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw channels list
openclaw channels status &lt;span class="nt"&gt;--json&lt;/span&gt;
openclaw status
openclaw logs &lt;span class="nt"&gt;--follow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands help verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;channel bindings&lt;/li&gt;
&lt;li&gt;Slack connection status&lt;/li&gt;
&lt;li&gt;agent routing&lt;/li&gt;
&lt;li&gt;runtime logs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Important Behavior: Session Visibility
&lt;/h2&gt;

&lt;p&gt;One subtle behavior we noticed involves cross-session communication.&lt;/p&gt;

&lt;p&gt;If the policy&lt;br&gt;
&lt;code&gt;tools.sessions.visibility&lt;/code&gt;&lt;br&gt;
restricts visibility, then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;agents may &lt;strong&gt;not be able to send messages across sessions&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful for enforcing &lt;strong&gt;isolation between agents&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;OpenClaw’s routing model makes it surprisingly easy to build multi-agent Slack assistants.&lt;/p&gt;

&lt;p&gt;With the right configuration, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run multiple agent personalities&lt;/li&gt;
&lt;li&gt;route conversations dynamically&lt;/li&gt;
&lt;li&gt;isolate sessions for security&lt;/li&gt;
&lt;li&gt;debug behavior through simple CLI tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture opens interesting possibilities for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI team assistants&lt;/li&gt;
&lt;li&gt;domain-specific agents&lt;/li&gt;
&lt;li&gt;developer copilots&lt;/li&gt;
&lt;li&gt;automated workflows&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
