{"id":173,"date":"2026-06-27T23:35:21","date_gmt":"2026-06-27T20:35:21","guid":{"rendered":"https:\/\/agentura.ro\/blog\/?p=173"},"modified":"2026-06-27T23:42:36","modified_gmt":"2026-06-27T20:42:36","slug":"sensors-teach-your-ai-agent-to-watch-notify-and-act","status":"publish","type":"post","link":"https:\/\/agentura.ro\/blog\/sensors-teach-your-ai-agent-to-watch-notify-and-act\/","title":{"rendered":"Sensors: teach your AI agent to watch, notify, and act"},"content":{"rendered":"<p class=\"agx-lede\">Most AI agents wait to be asked. <strong>Sensors<\/strong> flip that \u2014 a tiny script that watches one condition on a loop, inside your agent&#8217;s own container, and pings your agent the moment something actually changes.<\/p>\n<p>A lot of useful work in a business is that shape: <em>watch this, and tell me when it changes.<\/em> New orders. A price. A server. A car cooling down. One of our users recently told their agent to watch their Tesla&#8217;s cabin temperature and ping them when it cooled \u2014 then, over a single chat, turned it into a full <strong>thermostat<\/strong> that switches the AC on and off by itself. We&#8217;ll get there. First, let&#8217;s build one.<\/p>\n<h2>What a sensor actually is<\/h2>\n<p>Three parts. <strong>A script<\/strong> you (or your agent) write at <code>workspace\/sensors\/&lt;name&gt;.py<\/code> that checks one thing and prints a single line of JSON. <strong>A loop<\/strong> the platform runs for you \u2014 every <code>interval_seconds<\/code> (minimum 10) \u2014 executing that script in the agent&#8217;s sandbox, with <em>no model call per tick<\/em>, so it&#8217;s effectively free to run. And <strong>a delivery<\/strong> that fires only when the condition trips.<\/p>\n<p>The whole contract your script prints is just <code>{\"condition_met\": true, \"message\": \"\u2026\", \"data\": {\u2026}}<\/code>. That&#8217;s it.<\/p>\n<p><strong>You don&#8217;t need to read any of the code below to use sensors.<\/strong> The steps that follow show what&#8217;s happening under the hood \u2014 handy if you&#8217;re technical or want to hand-tune one. The everyday way to make a sensor is simply to <em>ask<\/em>: tell your agent something like <em>&#8220;watch X and tell me when Y&#8221;<\/em> and it writes the script, picks the trigger, and wires up the delivery for you. The Tesla thermostat further down was built exactly that way \u2014 entirely by chat, with no human touching code.<\/p>\n<h2>Step 1 \u2014 Open the Sensors tab<\/h2>\n<p>Open any agent and go to the <strong>\ud83d\udef0\ufe0f Sensors<\/strong> tab. You&#8217;ll see the contract, a <strong>+ Add sensor<\/strong> button, and any existing sensors with their last status.<\/p>\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/agentura.ro\/blog\/wp-content\/uploads\/2026\/06\/s01.webp\" alt=\"The Sensors tab. Each sensor shows its script, interval, trigger mode and last status.\" \/><figcaption class=\"wp-element-caption\">The Sensors tab. Each sensor shows its script, interval, trigger mode and last status.<\/figcaption><\/figure>\n<h2>Step 2 \u2014 Write the script<\/h2>\n<p>In the <strong>Files<\/strong> tab, create a file under <code>sensors\/<\/code> \u2014 say <code>demo_check.py<\/code> \u2014 that prints the JSON contract. Here&#8217;s a self-contained example you can paste as-is:<\/p>\n<pre><code>import json, os\n\n# Sensors hand the previous run&#039;s &quot;data&quot; back as $SENSOR_PREV_DATA, so a sensor\n# can compare against last time. This demo just counts runs.\nprev  = json.loads(os.environ.get(&quot;SENSOR_PREV_DATA&quot;, &quot;{}&quot;))\ncount = prev.get(&quot;count&quot;, 0) + 1\n\nprint(json.dumps({\n    &quot;condition_met&quot;: count &gt;= 1,            # &lt;-- your real check goes here\n    &quot;message&quot;: f&quot;Demo check #{count}: all good&quot;,\n    &quot;data&quot;: {&quot;count&quot;: count},\n}))<\/code><\/pre>\n<p>The platform hands your script a couple of things for free as environment variables: <code>$SENSOR_PREV_DATA<\/code> (the previous run&#8217;s <code>data<\/code>, so you can diff against last time) and credentials to call your agent&#8217;s connectors.<\/p>\n<h2>Step 3 \u2014 Add the sensor<\/h2>\n<p>Back on the Sensors tab, click <strong>+ Add sensor<\/strong> and fill in the form: a name, the script filename, how often to run, and the trigger.<\/p>\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/agentura.ro\/blog\/wp-content\/uploads\/2026\/06\/s02.webp\" alt=\"The new-sensor form: point it at your script and choose how it fires.\" \/><figcaption class=\"wp-element-caption\">The new-sensor form: point it at your script and choose how it fires.<\/figcaption><\/figure>\n<ul>\n<li><strong>Trigger<\/strong> \u2014 <code>edge<\/code> fires once when the condition flips false\u2192true (best for &#8220;tell me when X happens&#8221;); <code>level<\/code> fires on every true tick; <code>change<\/code> fires whenever the <code>data<\/code> changes.<\/li>\n<li><strong>Cooldown<\/strong> \u2014 the minimum gap between pings, so a condition that stays true doesn&#8217;t spam you.<\/li>\n<li><strong>Deliveries<\/strong> \u2014 an <code>alert<\/code>, an <code>email<\/code>, a <code>chat_message<\/code> straight into the chat, a written <code>workspace_file<\/code>, or an <code>agent_turn<\/code> that wakes the agent to <em>act<\/em>.<\/li>\n<\/ul>\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/agentura.ro\/blog\/wp-content\/uploads\/2026\/06\/s03.webp\" alt=\"Name it and point it at your script \u2014 here a &quot;btc_drop&quot; sensor using btc_watch.py.\" \/><figcaption class=\"wp-element-caption\">Name it and point it at your script \u2014 here a &quot;btc_drop&quot; sensor using btc_watch.py.<\/figcaption><\/figure>\n<h2>Step 4 \u2014 Test before you trust<\/h2>\n<p>Hit <strong>Test (dry-run)<\/strong>. It runs your script once, now, and shows exactly what it returned \u2014 <code>condition_met<\/code>, whether it <em>would<\/em> fire, the message and the data \u2014 without changing anything or sending a delivery. The fastest way to iterate until it&#8217;s right.<\/p>\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/agentura.ro\/blog\/wp-content\/uploads\/2026\/06\/s04.webp\" alt=\"The dry-run shows condition_met, would_fire and the message + data \u2014 with no side effects.\" \/><figcaption class=\"wp-element-caption\">The dry-run shows condition_met, would_fire and the message + data \u2014 with no side effects.<\/figcaption><\/figure>\n<h2>Step 5 \u2014 Watch it run<\/h2>\n<p>Once saved, the sensor runs on its loop. Click <strong>History<\/strong> to see every recent run: its status, whether it <em>fired<\/em>, how long the script took, and the message it printed. This is where &#8220;cheap to watch, only act on an edge&#8221; becomes visible.<\/p>\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/agentura.ro\/blog\/wp-content\/uploads\/2026\/06\/s05.webp\" alt=\"Real run history from a sensor that watches a Tesla \u2014 and acts: it holds the cabin in a 25\u201330\u00b0C band.\" \/><figcaption class=\"wp-element-caption\">Real run history from a sensor that watches a Tesla \u2014 and acts: it holds the cabin in a 25\u201330\u00b0C band.<\/figcaption><\/figure>\n<h2>The Tesla that built its own thermostat<\/h2>\n<p>Which brings us back to the car. The user asked their agent to set the Model X to 25\u00b0 and tell them when it reached it. The agent wrote a sensor that polls the car through the <code>tessie<\/code> connector every 60 seconds. The cabin was baking at 51\u00b0C; eleven minutes later, on its own:<\/p>\n<blockquote>\n<p>\ud83d\udef0\ufe0f <strong>Model X cabin reached 24.1\u00b0C (target 25.0\u00b0C)<\/strong><\/p>\n<\/blockquote>\n<p>Then they kept going \u2014 fire when it goes <em>above<\/em> 30\u00b0, turn the AC off when it hits 25\u00b0, and finally <em>&#8220;bounce between 30 and 25 indefinitely.&#8221;<\/em> The agent rewrote the sensor each time until it was a genuine thermostat: cabin \u2265 30\u00b0C \u2192 start the AC; \u2264 25\u00b0C \u2192 stop it; in between \u2192 nothing. The history above is that exact sensor. And notice: the control loop runs as plain code \u2014 the model was only involved while <em>authoring<\/em> it by chat. That&#8217;s the whole idea: natural language to write the automation, cheap deterministic code to run it. Your script reaches real data through your agent&#8217;s connectors \u2014 any REST API, your store, your CRM, or a car:<\/p>\n<pre><code>import json, os, urllib.request\n\ndef connector_get(slug, path):\n    req = urllib.request.Request(\n        f&quot;{os.environ[&#039;INTERNAL_BASE_URL&#039;]}\/api\/internal\/connectors\/{slug}{path}&quot;,\n        headers={\n            &quot;X-Internal-Secret&quot;: os.environ[&quot;ORCHESTRATOR_SECRET&quot;],  # injected per agent\n            &quot;X-Company-Id&quot;:      os.environ[&quot;COMPANY_ID&quot;],\n            &quot;X-Agent-Slug&quot;:      os.environ[&quot;AGENT_SLUG&quot;],\n        })\n    return json.load(urllib.request.urlopen(req, timeout=15))\n\n# Read a Tesla&#039;s cabin temperature through the Tessie connector\nstate  = connector_get(&quot;tessie&quot;, &quot;\/&lt;VIN&gt;\/state?use_cache=false&quot;)\ninside = state[&quot;climate_state&quot;][&quot;inside_temp&quot;]\n\nprint(json.dumps({\n    &quot;condition_met&quot;: inside &lt;= 25.0,\n    &quot;message&quot;: f&quot;Cabin {inside}\u00b0C (target 25\u00b0C)&quot;,\n    &quot;data&quot;: {&quot;inside_temp&quot;: inside},\n}))<\/code><\/pre>\n<h2>How Agentura sensors are different<\/h2>\n<p>Most automation and &#8220;AI agent&#8221; platforms hand you a fixed menu: a time trigger, a webhook trigger, and a no-code condition builder where you pick a field and an operator. That&#8217;s neat \u2014 until the thing you want to watch doesn&#8217;t fit the menu. The nearest relatives in spirit are Apache Airflow&#8217;s <em>sensors<\/em> (poll until a condition is met) and the newer idea of &#8220;ambient agents&#8221; (agents woken by events instead of prompts) \u2014 but those are still scaffolding a developer wires up by hand.<\/p>\n<p>Here, <strong>your agent writes the sensor itself, as real, bespoke code<\/strong> \u2014 generated on the spot for exactly what you asked. There&#8217;s no fixed list of supported conditions to choose from: describe it in plain language and the agent can chain several API calls, compute a rolling average, join two data sources, diff against the last run, decide, and act. Because it&#8217;s genuine code running against your connectors, <strong>the ceiling is whatever code and your integrations can do \u2014 in practice, the sky&#8217;s the limit<\/strong>, not whatever some settings form happened to anticipate. And it stays cheap: the loop is plain code with no model call per tick, so you can watch something every few seconds without spending tokens \u2014 the AI only steps in to build or change a sensor, or when a trip is set to wake it to act.<\/p>\n<h2>A few rules of thumb<\/h2>\n<ul>\n<li><strong>Default to <code>edge<\/code> + a cooldown<\/strong> \u2014 the difference between &#8220;tell me when it hits 25\u00b0&#8221; and a ping every 60 seconds while it sits there.<\/li>\n<li><strong>Checks are free; actions aren&#8217;t.<\/strong> Polling and conditions are plain code. Only <code>agent_turn<\/code> spends a model call \u2014 gate it behind the trigger.<\/li>\n<li><strong>Minimum interval is 10 seconds<\/strong>, and <strong>Test (dry-run)<\/strong> until the JSON is right before you let it loose.<\/li>\n<\/ul>\n<p>Sensors give your agents something most assistants lack: a sense of their surroundings, and the initiative to act on it. Point one at whatever you keep checking by hand and let it watch, so you don&#8217;t have to.<\/p>\n<p><strong><a href=\"https:\/\/agentura.ro\/\">Build your team \u2192<\/a><\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A step-by-step guide to Agentura Sensors \u2014 tiny scripts that watch a condition on a loop and ping your agent the moment it matters. Featuring a Tesla thermostat built entirely by chat.<\/p>\n","protected":false},"author":1,"featured_media":174,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22],"tags":[9,89,88,87,90],"class_list":["post-173","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-capabilities","tag-automation","tag-monitoring","tag-proactive-agents","tag-sensors","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/posts\/173","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/comments?post=173"}],"version-history":[{"count":2,"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/posts\/173\/revisions"}],"predecessor-version":[{"id":181,"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/posts\/173\/revisions\/181"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/media\/174"}],"wp:attachment":[{"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/media?parent=173"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/categories?post=173"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/agentura.ro\/blog\/wp-json\/wp\/v2\/tags?post=173"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}