<?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: George Zaletski</title>
    <description>The latest articles on DEV Community by George Zaletski (@george_zaletski).</description>
    <link>https://dev.to/george_zaletski</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%2F4038371%2Faf921b9a-3cd9-4166-88ac-21496a42ddf9.png</url>
      <title>DEV Community: George Zaletski</title>
      <link>https://dev.to/george_zaletski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/george_zaletski"/>
    <language>en</language>
    <item>
      <title>The hard part of building AI agents isn't the model. It's letting them act</title>
      <dc:creator>George Zaletski</dc:creator>
      <pubDate>Mon, 20 Jul 2026 14:45:06 +0000</pubDate>
      <link>https://dev.to/george_zaletski/the-hard-part-of-building-ai-agents-isnt-the-model-its-letting-them-act-5gin</link>
      <guid>https://dev.to/george_zaletski/the-hard-part-of-building-ai-agents-isnt-the-model-its-letting-them-act-5gin</guid>
      <description>&lt;p&gt;Getting an LLM to write a paragraph is easy — you can do it in an afternoon. Getting one to actually &lt;em&gt;do&lt;/em&gt; things, like create a record, book a meeting, or send an email on someone's behalf, is where it gets hard. And the surprising part is how little of that difficulty is about the model.&lt;/p&gt;

&lt;p&gt;I build Referent, practice-management software for law firms. The idea is that agents do the busywork and a person signs off on anything that leaves the office. Law is a brutal place to test this. A wrong action can be expensive, the people using it aren't developers, and "close enough" doesn't fly.&lt;/p&gt;

&lt;p&gt;A few things bit us on the way from an AI that answers questions to one that takes actions. None of them are specific to law, so I figured they were worth writing down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context is the bottleneck, not the model
&lt;/h2&gt;

&lt;p&gt;You'd think a smarter model gets you a better agent. Mostly it didn't. What held us back was getting a correct, current picture of the situation in front of the agent in the first place.&lt;/p&gt;

&lt;p&gt;Take one client. Their information is spread across an inbox, a calendar, some documents, and a couple of lines someone typed into a notes field months ago. No single system has the full story, and the systems disagree with each other — the email says Tuesday, the calendar says Wednesday. Somebody has to pull all of that together and decide which version is true before the agent can safely act on any of it. That somebody is your code.&lt;/p&gt;

&lt;p&gt;Our users said the same thing when we asked them. Their biggest complaint wasn't that the AI wasn't clever enough. It was two ordinary problems: their information is &lt;a href="https://ailawyer.pro/research/small-firm-legal-operations-2026" rel="noopener noreferrer"&gt;scattered across disconnected tools&lt;/a&gt;, and the AI they'd tried forgets everything from one session to the next. So we spend far more time fetching and reconciling data than we do on prompts. The prompt is almost the easy part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nothing writes without a human
&lt;/h2&gt;

&lt;p&gt;Our one hard rule is that the agent prepares and a person approves. In code, that means the agent can't touch the database at all.&lt;/p&gt;

&lt;p&gt;It reads whatever it needs, but it can't create a matter or send an email on its own. It produces a proposal instead — a typed record that says what it wants to do, why, and what it based that on — and drops it in a queue. Someone looks at it and confirms, edits, or bins it. Only then does anything happen for real.&lt;/p&gt;

&lt;p&gt;The two best things about this we didn't really plan for. A wrong guess from the model costs one click to reject instead of turning into an incident. And you get an audit trail for free — every change points back to a proposal, its sources, and whoever approved it. In a regulated field, that trail is the thing that lets you ship at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assume the agent is flaky
&lt;/h2&gt;

&lt;p&gt;Model calls are nondeterministic, and everything retries eventually. Those two facts together will quietly wreck your data if you let them.&lt;/p&gt;

&lt;p&gt;Let the same "create matter" step run twice and you've got duplicate matters. Make sending an email a direct side effect, and a retry sends it twice — to a client. So writes get idempotency keys, commits are transactional, and nothing the user can see happens outside that approval step from before. The agent is allowed to be unreliable. It just isn't allowed to have side effects.&lt;/p&gt;

&lt;p&gt;What clicked for us was to stop treating the agent as a function we call, and start treating it like a flaky client hitting our API over a bad connection. You already know how to handle that one. Validate everything, make writes idempotent, and assume any given message shows up zero times, once, or five times.&lt;/p&gt;

&lt;h2&gt;
  
  
  The review screen is the whole product
&lt;/h2&gt;

&lt;p&gt;Human-in-the-loop only works if checking the agent's work is faster than doing it yourself. Miss that and it breaks in one of two ways. If reviewing is a slog, people start approving things without reading them, and your safety story is fiction. If it's a real slog, they drop the tool and go back to doing it by hand, and you've automated nothing.&lt;/p&gt;

&lt;p&gt;So a proposal has to carry its evidence with it. When the agent wants to open a matter, the reviewer sees the emails, the document, and the reasoning right there — enough to say yes or no in a couple of seconds without going digging. We spend a lot of design time on that one screen. It matters more than the chat box everyone puts in their demos.&lt;/p&gt;

&lt;p&gt;A bonus we didn't see coming: every approve, edit, and reject is a person labeling the agent's output. Give it a few weeks and you've got a real evaluation set, which beats the synthetic benchmarks we started with.&lt;/p&gt;

&lt;h2&gt;
  
  
  So
&lt;/h2&gt;

&lt;p&gt;If you're building agents that actually do things, the interesting work isn't picking a better model. It's the unglamorous stuff around it: getting the state right, putting a human in front of every write, making actions safe to retry, and building a review screen fast enough that people stay in the loop because they want to. That's most of the job.&lt;/p&gt;

&lt;p&gt;We're doing this for law at &lt;a href="https://referent.law" rel="noopener noreferrer"&gt;Referent&lt;/a&gt; right now. I'd like to know how other people are drawing the propose–review–commit line, so if you've built something in this shape, tell me in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
