Daily Tech Briefing
AI 科技速览
每天 5 分钟内学习 AI。获取最新的人工智能新闻,理解其重要性,并学习如何将其应用于您的工作。
Dev.to AI · 2026/8/2 08:00:11
Make agent-callable writes idempotent, or lose data
AI 中文解读
核心亮点:AI智能体自动执行任务时,重复操作可能导致数据错乱甚至丢失,这篇文章揭示了保障AI“手脚干净”的关键技术。
通俗解读:想象你让AI帮你报销一笔费用,它发出指令后网络卡顿,AI没收到回复,就自动再发一次。如果系统没有“重复操作识别”功能,这张报销单就可能被创建两次,月底对账就出麻烦了。过去人点按钮出错是小概率,但AI会自己重试、超时恢复、甚至同时发起多个操作,重复写入从“偶然”变成了家常便饭。现在的AI工具调用就像打电话,可能打两遍都通,但服务器必须记住“这笔单我已经处理过了”,不能因为重复呼叫就重复记账。这篇文章讲的正是如何给AI的操作加上“防重复”保险,让每次执行都有唯一编号,即使网络再不稳定,结果也始终正确。
实际影响:以后用AI处理财务、税务、客户账单等重要事务时,不用担心AI“手滑”搞出双倍扣款或假发票。对企业来说,这意味着AI自动化才能真正安全地接管核心业务,而不是只能做做演示。对我们普通用户,意味着AI替你办事时,更靠谱、更可信。
<p>An agent-native product isn't a chatbot bolted onto a CRUD app — it's an MCP server that lets an agent <em>do</em> things. Read invoices, create expenses, mark a client overdue. The demo is easy. The part that decides whether the thing survives contact with real traffic is the layer nobody screenshots: what happens when a write is sent twice.</p>
<p>This is the unglamorous half of agent-native engineering. Every write operation an agent can invoke needs idempotency, safe retries, and typed recoverable errors — not as a nice-to-have, but as the difference between a product and a liability. Here's why, and how to build it.</p>
<h2>
Agents turn "at-least-once" from a footnote into a daily event
</h2>
<p>Distributed systems people have known forever that networks give you at-least-once delivery, not exactly-once. A request goes out, the response gets lost on the way back, the caller doesn't know if the write landed, so it retries. With a human clicking a button, this is rare enough to hand-wave.</p>
<p>Agents remove the hand-wave. Three things about how agents call tools push duplicate writes from "edge case" to "Tuesday":</p>
<ol>
<li>
<strong>They retry automatically.</strong> A well-behaved MCP client backs off and retries on <code>429</code> and <code>5xx</code>. The Frihet MCP server (<a href="https://github.com/Frihet-io/frihet-mcp" rel="noopener noreferrer">github.com/Frihet-io/frihet-mcp</a>) enforces 100 requests per minute per <code>fri_</code> key, and its own client retries with exponential backoff when it hits that ceiling. A retry after a <code>429</code> is <em>supposed</em> to happen — which means every write behind that endpoint has to survive being delivered more than once.</li>
<li>
<strong>They time out and resume.</strong> An agent loop hits a wall-clock limit mid-tool-call, the session gets resumed, and the planner — having never seen a result — reasonably decides to "create the invoice" again.</li>
<li>
<strong>They fan out.</strong> Give a model a goal ("bill everyone for last month") and it will happily issue parallel tool calls. Two of them racing on the same logical write is now a concurrency problem, not a theoretical one.</li>
</ol>
<p>MCP itself does not save you here. A tool call is just RPC over JSON — the protocol says nothing about whether invoking <code>create_invoice</code> twice creates one invoice or two. That contract is entirely yours to define on the server. If you don't define it, you've defined it as "two."</p>
<p>For a read tool, a duplicate is free. For a write tool in a fiscal domain — an ERP that prepares VeriFactu records, IVA and IRPF filings — a duplicate is a phantom invoice in someone's tax quarter. The blast radius is exactly why the boring layer matters.</p>
<h2>
Idempotency keys: the client names the request, once
</h2>
<p>The fix is to let the caller assign a stable identity to a <em>logical</em> operation, independent of how many times it's physically delivered. That's an idempotency key: a client-generated token (usually a UUID) that stays the same across retries of the same intended write, and changes for a genuinely new one.</p>
<p>The rule for the client is simple: <strong>generate the key once, at the point of intent, and reuse it for every retry of that same intent.</strong> If the agent decides to bill a client, it mints one key and carries it through all four delivery attempts. When it decides to bill a <em>different</em> client, it mints a new one.</p>
<p>The server's job is to make the key mean something: the <em>first</em> time it sees a key, do the work and remember the result; every subsequent time, return the remembered result without touching the database again.</p>
<p>The subtlety that trips people up is atomicity. "Check if the key exists, then insert it" is a race — two concurrent retries both read "not found" and both do the work. The claim has to be a single atomic operation, which in Postgres is an <code>INSERT ... ON CONFL
分享
阅读原文 ↗