<?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: Ben Mccarthy</title>
    <description>The latest articles on DEV Community by Ben Mccarthy (@ben_mccarthy_aae742e0d499).</description>
    <link>https://dev.to/ben_mccarthy_aae742e0d499</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4095844%2Ffa58a380-a6bb-43d0-adbc-8c9861e0e868.png</url>
      <title>DEV Community: Ben Mccarthy</title>
      <link>https://dev.to/ben_mccarthy_aae742e0d499</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ben_mccarthy_aae742e0d499"/>
    <language>en</language>
    <item>
      <title>Why Giving AI Agents More Context Can Make Them Worse</title>
      <dc:creator>Ben Mccarthy</dc:creator>
      <pubDate>Mon, 14 Sep 2026 18:16:38 +0000</pubDate>
      <link>https://dev.to/ben_mccarthy_aae742e0d499/why-giving-ai-agents-more-context-can-make-them-worse-1fii</link>
      <guid>https://dev.to/ben_mccarthy_aae742e0d499/why-giving-ai-agents-more-context-can-make-them-worse-1fii</guid>
      <description>&lt;p&gt;Most AI agent demos start with the model.&lt;/p&gt;

&lt;p&gt;We ended up spending far more time thinking about what gets put &lt;strong&gt;around&lt;/strong&gt; the model.&lt;/p&gt;

&lt;p&gt;I run 23 holiday lets and have been building AI agents into the day-to-day operation. Guest messaging sounds like one of the easier problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Guest sends a message.&lt;/li&gt;
&lt;li&gt;Give the message to an LLM.&lt;/li&gt;
&lt;li&gt;Send back the answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That works brilliantly right up until the guest asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can I park a second car?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now the answer depends on the property, the booking, the parking arrangement, whether they're currently checked in, whether anything has changed since the listing was written and potentially something a member of the team said twenty minutes ago.&lt;/p&gt;

&lt;p&gt;The model is suddenly the easy bit.&lt;/p&gt;

&lt;p&gt;While building &lt;a href="https://zugrow.com/" rel="noopener noreferrer"&gt;Zugrow&lt;/a&gt;, one of the lessons that kept coming back was this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An agent can have a very good model and still make a bad decision because you gave it the wrong state.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So we stopped treating context as a giant blob of text and started treating it like application data.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Don't give the model everything you know
&lt;/h2&gt;

&lt;p&gt;My instinct initially was simple.&lt;/p&gt;

&lt;p&gt;More context = better answer.&lt;/p&gt;

&lt;p&gt;So if a guest messaged about a booking, why not give the agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the property description&lt;/li&gt;
&lt;li&gt;all amenities&lt;/li&gt;
&lt;li&gt;house rules&lt;/li&gt;
&lt;li&gt;the entire conversation&lt;/li&gt;
&lt;li&gt;booking details&lt;/li&gt;
&lt;li&gt;previous guest questions&lt;/li&gt;
&lt;li&gt;internal notes&lt;/li&gt;
&lt;li&gt;host instructions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It feels sensible.&lt;/p&gt;

&lt;p&gt;It also produces a mess.&lt;/p&gt;

&lt;p&gt;Important information gets buried amongst things that have nothing to do with the current question.&lt;/p&gt;

&lt;p&gt;Instead, the agent should get the &lt;strong&gt;smallest useful view of reality&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;GuestContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;checkInTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;checkOutTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;parking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ParkingPolicy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nl"&gt;booking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;arrivalDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;departureDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;guestCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BookingStatus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nl"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;recentMessages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Message&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If somebody asks about parking, the agent doesn't need the Wi-Fi password, the boiler instructions and six months of pricing history.&lt;/p&gt;

&lt;p&gt;Give it what it needs to answer the question in front of it.&lt;/p&gt;

&lt;p&gt;That sounds obvious.&lt;/p&gt;

&lt;p&gt;In an agent system, it is surprisingly easy to forget.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Separate facts from instructions
&lt;/h2&gt;

&lt;p&gt;This made a bigger difference than I expected.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Parking is available behind the building.
Guests should normally use Bay 14.
Sometimes another space may be available.
Do not guarantee a second space.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two completely different things happening here.&lt;/p&gt;

&lt;p&gt;The first three sentences describe the world.&lt;/p&gt;

&lt;p&gt;The last sentence describes what the agent is allowed to do.&lt;/p&gt;

&lt;p&gt;Mixing those together makes the prompt harder to reason about.&lt;/p&gt;

&lt;p&gt;We now think of them separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;facts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;parkingType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allocated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;primaryBay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;14&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;additionalSpacePossible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mayGuaranteeAdditionalSpace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The distinction matters because facts can change.&lt;/p&gt;

&lt;p&gt;Policy usually changes much less often.&lt;/p&gt;

&lt;p&gt;It also means the application can enforce some rules without relying on the model remembering them.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Database state beats listing text
&lt;/h2&gt;

&lt;p&gt;Listings are written for humans.&lt;/p&gt;

&lt;p&gt;Agents need structured state.&lt;/p&gt;

&lt;p&gt;Suppose the listing says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Parking is available for guests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Perfectly reasonable marketing copy.&lt;/p&gt;

&lt;p&gt;But the agent needs to know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;parkingAvailable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;guaranteedSpaces&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="nx"&gt;extraSpacesRequireApproval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those two things communicate roughly the same information to a human.&lt;/p&gt;

&lt;p&gt;They are very different inputs for software.&lt;/p&gt;

&lt;p&gt;The more agents we added, the more I found myself converting vague property information into explicit state.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Early check-in may sometimes be available.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;standardCheckIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;15:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;earlyCheckInAllowed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;earliestPossibleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;13:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;requiresTeamApproval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent can now reason from something much closer to reality.&lt;/p&gt;

&lt;p&gt;And more importantly, our application can stop it making promises it shouldn't make.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Freshness matters as much as accuracy
&lt;/h2&gt;

&lt;p&gt;There is another problem.&lt;/p&gt;

&lt;p&gt;A fact can be correct and still be wrong.&lt;/p&gt;

&lt;p&gt;Yesterday:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;wifi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;working&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Today the router has died.&lt;/p&gt;

&lt;p&gt;The database technically contains a fact.&lt;/p&gt;

&lt;p&gt;It is just stale.&lt;/p&gt;

&lt;p&gt;So useful agent context needs some idea of freshness:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ContextValue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;channel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That opens up much better behaviour.&lt;/p&gt;

&lt;p&gt;The application can say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hoursSince&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wifi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;requireVerification&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or the agent can respond cautiously rather than stating something as certain.&lt;/p&gt;

&lt;p&gt;This became an important mental model for me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent context is not knowledge. It is a snapshot.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Snapshots age.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Recheck state before doing anything
&lt;/h2&gt;

&lt;p&gt;This matters even more once agents can act.&lt;/p&gt;

&lt;p&gt;Imagine this sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10:00:00 Guest asks for early check-in
10:00:02 Agent reads availability
10:00:08 Cleaner changes schedule
10:00:11 Agent confirms early check-in
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model made the right decision using the information it had.&lt;/p&gt;

&lt;p&gt;The system still made the wrong decision.&lt;/p&gt;

&lt;p&gt;That is a normal software concurrency problem wearing an AI hat.&lt;/p&gt;

&lt;p&gt;The fix is boring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;suggestion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;latestState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bookings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCurrent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bookingId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;stillValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;suggestion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;latestState&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;requireHumanReview&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;suggestion&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the model to decide what it &lt;strong&gt;would like&lt;/strong&gt; to do.&lt;/p&gt;

&lt;p&gt;The application checks whether it is &lt;strong&gt;still allowed&lt;/strong&gt; to do it.&lt;/p&gt;

&lt;p&gt;That second check matters far more than making the prompt another 500 words longer.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Give the agent events, not endless history
&lt;/h2&gt;

&lt;p&gt;Conversation history causes the same problem.&lt;/p&gt;

&lt;p&gt;It is tempting to keep throwing every previous message into the context window.&lt;/p&gt;

&lt;p&gt;But imagine a guest has sent 70 messages during a two-week stay.&lt;/p&gt;

&lt;p&gt;Most of that conversation is irrelevant when they ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What time is checkout tomorrow?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of treating history as one enormous transcript, you can reduce it into state and recent events.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;booking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentBooking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;relevantPropertyFacts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;recentEvents&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;guest_message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What time is checkout tomorrow?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;late_checkout_request&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not_requested&lt;/span&gt;&lt;span class="dl"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model gets much less information.&lt;/p&gt;

&lt;p&gt;But the information it does get matters more.&lt;/p&gt;

&lt;p&gt;That is usually the trade I want.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Log what the agent actually saw
&lt;/h2&gt;

&lt;p&gt;This is the part I would build earlier if I started again.&lt;/p&gt;

&lt;p&gt;When an agent gives a strange answer, knowing the output is not enough.&lt;/p&gt;

&lt;p&gt;You need to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What did it believe was true at the time?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So every decision should have a trace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AgentTrace&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;contextVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then when somebody asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why did the agent tell this guest they had two parking spaces?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you don't have to guess.&lt;/p&gt;

&lt;p&gt;You can inspect the exact state supplied to the model.&lt;/p&gt;

&lt;p&gt;A surprising number of apparent "AI mistakes" turn out to be ordinary software mistakes upstream.&lt;/p&gt;

&lt;p&gt;Wrong property.&lt;/p&gt;

&lt;p&gt;Old data.&lt;/p&gt;

&lt;p&gt;Missing field.&lt;/p&gt;

&lt;p&gt;Incorrect booking state.&lt;/p&gt;

&lt;p&gt;The model simply gave a perfectly reasonable answer to the reality we accidentally handed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern we ended up with
&lt;/h2&gt;

&lt;p&gt;Our agent flow increasingly looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Guest message
      ↓
Intent / task
      ↓
Context builder
      ↓
Relevant current state
      ↓
AI decision
      ↓
Deterministic validation
      ↓
State recheck
      ↓
Human approval or action
      ↓
Audit log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM sits in the middle.&lt;/p&gt;

&lt;p&gt;It isn't the application.&lt;/p&gt;

&lt;p&gt;That distinction seems obvious written down, but a lot of agent prototypes blur it.&lt;/p&gt;

&lt;p&gt;They build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data → enormous prompt → model → action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then try to improve reliability by making the enormous prompt even larger.&lt;/p&gt;

&lt;p&gt;Eventually you are asking a probabilistic model to compensate for missing application architecture.&lt;/p&gt;

&lt;p&gt;That doesn't scale particularly well.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;If I were building an agent system from scratch now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give the model the minimum context required for the current task.&lt;/li&gt;
&lt;li&gt;Store important facts as structured data rather than prose.&lt;/li&gt;
&lt;li&gt;Keep facts and agent permissions separate.&lt;/li&gt;
&lt;li&gt;Track when important context was last updated.&lt;/li&gt;
&lt;li&gt;Recheck state immediately before an agent takes an action.&lt;/li&gt;
&lt;li&gt;Prefer recent events and current state over enormous conversation histories.&lt;/li&gt;
&lt;li&gt;Record exactly what context the model saw when it made a decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The strange thing about building AI agents is that the longer I work on them, the less time I spend thinking about the model.&lt;/p&gt;

&lt;p&gt;The model is important.&lt;/p&gt;

&lt;p&gt;But most of the reliability comes from fairly ordinary software engineering around it.&lt;/p&gt;

&lt;p&gt;And honestly, I think that is good news.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I built &lt;a href="https://zugrow.com/" rel="noopener noreferrer"&gt;Zugrow&lt;/a&gt;, an AI-first property management platform, and use the same systems across the holiday lets I operate. I'm particularly interested in how other people are handling context construction, stale state and pre-action validation in agent systems.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
    <item>
      <title>We let AI agents run 23 vacation rental properties. Here is what we never let them do.</title>
      <dc:creator>Ben Mccarthy</dc:creator>
      <pubDate>Wed, 26 Aug 2026 20:17:16 +0000</pubDate>
      <link>https://dev.to/ben_mccarthy_aae742e0d499/we-let-ai-agents-run-23-vacation-rental-properties-here-is-what-we-never-let-them-do-16b2</link>
      <guid>https://dev.to/ben_mccarthy_aae742e0d499/we-let-ai-agents-run-23-vacation-rental-properties-here-is-what-we-never-let-them-do-16b2</guid>
      <description>&lt;p&gt;I run short lets. Twenty three of them. About eighteen months ago I started replacing the parts of that job that were eating my evenings, mostly answering the same question about parking for the fortieth time, with AI agents.&lt;/p&gt;

&lt;p&gt;They work. They also taught me that almost everything I first believed about shipping autonomous agents was wrong.&lt;/p&gt;

&lt;p&gt;This is not a post about prompt engineering. It is about the four rules we ended up with once real money was moving through the thing, and why three of them are about what the agent is &lt;em&gt;not&lt;/em&gt; allowed to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 1: The guardrail goes in code, not in the prompt
&lt;/h2&gt;

&lt;p&gt;This is the one I got wrong first, and it is the one I see everywhere.&lt;/p&gt;

&lt;p&gt;Our pricing agent reprices every property every night. It has a floor: never go below what the property costs to turn around. My first version put that in the system prompt. Something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Never suggest a nightly price below the floor of £X.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It held. Most of the time. Then one night it suggested £38 on a property with a £52 floor, because the surrounding context made a cheap night look reasonable and the instruction was, to the model, one consideration among many.&lt;/p&gt;

&lt;p&gt;A prompt is a request. Code is a guarantee. The fix is dull and total:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The model proposes. Code disposes.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proposed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pricingAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suggest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;floor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;settingsFloor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;        &lt;span class="c1"&gt;// what the host configured&lt;/span&gt;
  &lt;span class="nf"&gt;hardFloor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gapNights&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// what the maths says it cannot go below&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;proposed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;Math.max&lt;/code&gt; is the entire safety property. It does not matter what the model returns. It does not matter if someone jailbreaks the prompt, or if we swap models, or if the context window fills with something strange. The floor holds because the floor is arithmetic, not persuasion.&lt;/p&gt;

&lt;p&gt;The general form: &lt;strong&gt;any constraint you would be embarrassed to have violated must be enforced after the model returns, in code that the model cannot influence.&lt;/strong&gt; If your only defence is an instruction in a prompt, you do not have a constraint. You have a preference.&lt;/p&gt;

&lt;p&gt;Ask yourself, for every rule in your system prompt: what happens if the model ignores this exactly once? If the answer is "we lose money" or "we upset a customer" or "we break the law", it does not belong in the prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 2: Ship every agent switched off, and make the user turn it up
&lt;/h2&gt;

&lt;p&gt;Every agent we run has three positions. Off, Suggest, Auto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AgentMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;off&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;suggest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Suggest, the agent does the whole job and then stops. It writes the reply and waits for you to press send. It works out the new price and shows it to you. All the work, none of the authority.&lt;/p&gt;

&lt;p&gt;Everything ships in Suggest. Not as a beta phase we later remove, but permanently, as the default. The user promotes an agent to Auto themselves, per agent, when that particular agent has earned it in their eyes.&lt;/p&gt;

&lt;p&gt;This felt like cowardice when I built it. It turned out to be the single thing that made the product usable, for two reasons.&lt;/p&gt;

&lt;p&gt;The obvious one is trust. Nobody hands over their inbox on day one. Suggest lets someone watch an agent be right forty times before it gets to act alone, and that is a much better argument than anything on a landing page.&lt;/p&gt;

&lt;p&gt;The less obvious one is that &lt;strong&gt;Suggest mode is the best evaluation harness you will ever build.&lt;/strong&gt; Every time a user edits a draft before sending it, that is a labelled failure, free, in production, with the correction attached. You do not have to construct an eval set that guesses at what real inputs look like. Real inputs are showing up, and users are marking your homework because it is in their interest to do so.&lt;/p&gt;

&lt;p&gt;We found our worst prompt bug that way. The messaging agent was signing off in a way that read as slightly cold to guests. No test would have caught it. Forty users editing the same sentence out of forty drafts caught it in a week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 3: When it fails, work out which direction is safe
&lt;/h2&gt;

&lt;p&gt;"Fail safe" is meaningless until you decide what safe means for that specific agent, and the answer differs per agent.&lt;/p&gt;

&lt;p&gt;We have an agent that watches booking requests approaching expiry. If a request is about to time out with no decision, the platform counts that against your response rate, which affects your ranking. So this agent acts on the clock.&lt;/p&gt;

&lt;p&gt;It fails closed. If it is fifteen minutes from expiry and nothing has happened, it declines. Declining is the recoverable outcome: a guest can rebook, and your response rate survives. Silently letting it expire is not recoverable.&lt;/p&gt;

&lt;p&gt;But note the second half, which took a near miss to learn: &lt;strong&gt;it only ever acts on silence.&lt;/strong&gt; If you or another agent has already answered that request, it does nothing at all. The dangerous version of this agent is the one that decides it knows better and overrides a human decision made four minutes ago.&lt;/p&gt;

&lt;p&gt;So the rule is two-sided. Pick the safe direction for the specific failure, and define precisely the state in which the agent is allowed to act at all. "No human has touched this" is usually the right precondition, and it is easy to forget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule 4: Some things never get automated, at any autonomy level
&lt;/h2&gt;

&lt;p&gt;Even on Auto, three categories of action are unavailable to our agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Anything that spends or refuses money.&lt;/strong&gt; A price floor never gets crossed. A booking never gets declined by an agent acting on its own judgment, except in the narrow expiry case above where the alternative is worse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anything irreversible.&lt;/strong&gt; No cancellations, no charges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anything that would misrepresent the human.&lt;/strong&gt; Agents do not make promises on the host's behalf that the host has not agreed to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a technical limit. We could ship it. It is a product decision, and I think it is the right one, because the failure mode is asymmetric. An agent that is too cautious costs you a few minutes. An agent that declines the wrong booking or undercuts your floor costs you a night's revenue and a guest, and you find out afterwards.&lt;/p&gt;

&lt;p&gt;When you are deciding where your own line sits, the question is not "can the model do this reliably?" It is "if this goes wrong at 3am while nobody is watching, is the damage recoverable?" If it is not, keep a human in it, however good your evals look.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this costs
&lt;/h2&gt;

&lt;p&gt;I want to be honest about the trade, because posts like this usually are not.&lt;/p&gt;

&lt;p&gt;Constraining agents this heavily makes the product less impressive in a demo. "It drafts a reply and you approve it" is a worse sentence than "it runs your whole inbox". We have lost people at that sentence.&lt;/p&gt;

&lt;p&gt;It also means we ship slower. Every new agent needs its guardrails written in code, which is more work than adding a paragraph to a prompt.&lt;/p&gt;

&lt;p&gt;What we get for that is an agent estate that has not yet done something I had to apologise for. Eighteen months, twenty three properties, thousands of guest messages. For software touching other people's businesses and other people's holidays, I will take that trade every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If a rule matters, enforce it in code after the model returns. A prompt is a request, not a constraint.&lt;/li&gt;
&lt;li&gt;Ship in Suggest mode. It buys trust, and it is a free production eval harness.&lt;/li&gt;
&lt;li&gt;Decide which direction is safe for each agent, and define the exact state it may act in. Usually: only on silence.&lt;/li&gt;
&lt;li&gt;Never automate the irreversible, no matter how good your evals are.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is clever. That is sort of the point. The interesting work in agents right now is not making them more capable, it is working out what they are allowed to touch.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build &lt;a href="https://zugrow.com" rel="noopener noreferrer"&gt;Zugrow&lt;/a&gt;, which is where these agents live, and I host twenty three short lets, which is where they get tested. Happy to answer anything in the comments about how a specific guardrail is implemented.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
