ИТ развој

Beyond the Prompt: Designing APIs for Evolving AI Agents

Надвор од поттикот: Дизајнирање API-ја за AI агенти што се развиваат

AI agents rarely fail because a prompt was one sentence too short. They fail because the systems around them were designed for a human clicking through a predictable interface, while an agent needs to observe state, take bounded actions, recover from ambiguity, and explain what happened.

That distinction changes API design. An endpoint that is technically correct for a frontend can still be a poor tool for an evolving agent. Agent-facing APIs need to be explicit, inspectable, safe to retry, and stable enough that a model can use them without relying on accidental knowledge of implementation details.

Design for actions, not screens

Many APIs inherit the shape of a web application: endpoints mirror forms, pages, and UI flows. This is understandable, but it can create awkward tools for an agent. A route such as POST /checkout may bundle validation, payment, stock allocation, and notifications into one opaque operation. When it fails, the agent has little useful information with which to decide its next move.

Instead, model meaningful business actions and expose the state transitions around them. A fulfillment API might offer separate capabilities to inspect an order, reserve inventory, create a shipment, and cancel an unshipped shipment. Each action should have a clear precondition and an observable result.

{
  "order_id": "ord_4821",
  "status": "awaiting_inventory",
  "available_actions": [
    "reserve_inventory",
    "cancel"
  ],
  "blocking_reasons": [
    {
      "code": "INSUFFICIENT_STOCK",
      "sku": "SKU-RED-42",
      "requested": 3,
      "available": 1
    }
  ]
}

The available_actions field is especially valuable. It prevents an agent from guessing which operation is valid in the current state. It also lets the backend evolve workflow rules without requiring the client to reverse-engineer every transition.

Make state legible and errors actionable

An agent cannot look at a red banner and infer intent. It needs structured error responses that distinguish malformed input, failed business rules, authorization problems, and transient infrastructure failures.

A response saying “Unable to process request” is a dead end. A response with a stable error code, a human-readable summary, a retry classification, and field-level details gives both the agent and its operator a path forward.

{
  "error": {
    "code": "PAYMENT_METHOD_EXPIRED",
    "message": "The selected payment method has expired.",
    "retryable": false,
    "action": "request_updated_payment_method",
    "details": {
      "payment_method_id": "pm_91"
    }
  }
}

Do not make every error retryable. Retrying a temporary database timeout may be sensible; retrying an invalid currency code is not. A useful API makes that difference explicit, while clients still apply bounded retry policies with backoff and idempotency protection.

Idempotency is an agent safety feature

Agents operate in a world of uncertain completion. A network request can time out after the server has already created the shipment. A process can restart between receiving a response and recording it. A model may repeat an action after losing context.

For operations with side effects, accept an idempotency key and persist the result associated with that key. Repeating the same request should return the original outcome rather than create another charge, ticket, email, or deployment.

POST /shipments
Idempotency-Key: 6a7d4e89-2d77-4cd0-9d23-3d2bdbe15342
Content-Type: application/json

On the backend, scope the key carefully: typically by tenant, operation, and key. Store a request fingerprint alongside the completed response. If the same key arrives with materially different input, return a conflict instead of silently accepting an ambiguous duplicate.

In PHP, this often means treating the idempotency record as part of the same transactional boundary as the business operation. If a shipment row commits but the idempotency record does not, the retry problem remains. If external work is involved, use an outbox pattern rather than attempting to make an email provider or payment gateway part of a database transaction.

Separate fast commands from slow work

Long-running work is uncomfortable for browsers and unreliable for agents. Report generation, bulk imports, video processing, and multi-step provisioning should usually become asynchronous jobs.

A command endpoint can validate the request, create a durable job, and return quickly. The agent can then poll a resource or receive a callback through an integration layer designed for that purpose.

{
  "job_id": "job_18f0",
  "status": "queued",
  "status_url": "/jobs/job_18f0"
}

Job status should be more informative than queued, running, and failed. Include progress where it is meaningful, terminal timestamps, a concise failure code, and links to produced resources. Avoid exposing raw worker exceptions as the public contract; they change too easily and can leak implementation details.

Use schemas as product boundaries

Typed request and response contracts are not bureaucracy. They are the shared language between the agent, its orchestrator, backend services, and human maintainers. A schema should document required fields, formats, enums, nullable values, pagination behavior, and examples of failure responses.

Be conservative when changing a published contract. Adding an optional field is generally manageable. Renaming a status, changing the meaning of a boolean, or turning a string into an object can break an agent in ways that look like reasoning failures.

  • Prefer explicit versioning when a semantic break is unavoidable.
  • Use stable identifiers rather than display names as inputs to mutations.
  • Paginate collections and provide deterministic ordering.
  • Expose timestamps in a documented, consistent format.
  • Deprecate deliberately, with a migration path rather than a surprise removal.

Constrain authority close to the data

Prompt instructions are useful, but they are not an authorization system. The API must enforce tenant boundaries, ownership, role permissions, rate limits, and operation-specific policies. An agent should receive the minimum capability needed for its current job.

This matters even more when an agent can call broad search or administrative endpoints. A convenient “do anything” token turns every prompt-injection mistake into a security incident. Narrow scopes, resource-level checks, audit logs, and approval-required operations are more durable controls.

For high-impact actions, consider a two-step design: create a proposed change, then execute it with a separate approval or confirmation endpoint. This preserves automation while making irreversible decisions visible and reviewable.

Observability is part of the interface

When an agent takes an action, operators need to reconstruct the chain of events. Return request or operation identifiers, propagate correlation IDs through services, and log structured events around state changes. The goal is not to log every prompt. It is to answer practical questions: what was attempted, under whose authority, against which resource, and what changed?

Metrics should follow the same principle. Track error classes, queue latency, idempotency replays, authorization denials, and failed state transitions. These signals reveal whether the problem is model behavior, an unclear contract, a capacity issue, or a broken dependency.

Build APIs that make the safe path easy

The strongest agent API is not the one with the most endpoints. It is the one that makes correct behavior obvious and unsafe behavior difficult: explicit state, clear actions, durable retries, narrow permissions, stable contracts, and useful failure information.

Prompts will change. Models will improve. Orchestration frameworks will come and go. A well-designed backend remains the steady part of the system, turning uncertain language-driven intent into controlled, observable business operations.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.