Umjetna inteligencija (UI)

AI Agents: Engineering Software That Actually Gets Things Done

AI agenti: razvoj softvera koji zaista obavlja zadatke

AI agents are easy to mistake for chatbots with better branding. That mistake leads to disappointing implementations: a polished interface that can explain a task but cannot reliably complete one. An agent becomes useful when it can turn an objective into a controlled sequence of actions—gathering context, making bounded decisions, using tools, checking outcomes, and asking for help when confidence is low.

The engineering challenge is not simply choosing a capable model. It is designing a system that can act in the real world without becoming unpredictable, expensive, or unsafe. The most successful agents are usually less magical than the demos suggest. They are narrow where they need to be, explicit about their permissions, and surrounded by conventional software that provides memory, validation, observability, and guardrails.

Start with work, not personality

A productive agent should be defined by a concrete unit of work. “Help our support team” is too broad. “Draft a response to a billing question using the account record and current policy, then route it for approval” is a system design problem that can be evaluated.

Good early candidates share a few characteristics: the task repeats often, inputs are reasonably available, a human already knows what a good outcome looks like, and mistakes can be contained. Examples include triaging incoming issues, extracting structured details from documents, preparing incident summaries, checking pull requests against a team checklist, or assembling a first draft of a customer update.

These uses do not remove judgment. They move routine preparation work into a faster loop, leaving people to handle exceptions, approvals, and decisions with meaningful consequences.

Think in loops: observe, decide, act, verify

An agent is best understood as a loop, not a single prompt. It observes the relevant state, decides what to do next, invokes a tool or produces an output, and verifies whether the action achieved the intended result. If verification fails, it should retry only when a retry is sensible, choose a different path when possible, or escalate.

For example, an agent asked to update a project tracker should not merely call an update tool after reading a message. A sturdier flow is:

  1. Identify the referenced project and work item.
  2. Retrieve the current item state and confirm that it is eligible for the requested change.
  3. Extract the proposed fields into a structured representation.
  4. Validate required fields, allowed values, and the requesting user’s authority.
  5. Apply the update with an idempotency key or another duplicate-prevention mechanism.
  6. Read the item back and confirm that the saved state matches the request.
  7. Record an audit event and report the result or the reason for escalation.

Most of that flow is familiar application engineering. The model is valuable in the ambiguous parts: interpreting language, selecting among permitted actions, and summarizing messy information. Deterministic code should own the rules that must always hold.

Tool design determines reliability

Agents do not become reliable merely because a model can call functions. The tools themselves need careful contracts. A vague tool such as update_record(data) asks the model to guess too much. A focused tool such as set_ticket_priority(ticket_id, priority, reason) reduces ambiguity and makes validation straightforward.

Keep tools small, typed, and explicit about side effects. Describe inputs, constraints, expected outputs, and failure conditions. Return useful errors rather than generic failures. “Ticket is already closed” gives an agent a chance to select an appropriate next step; “request failed” does not.

Separate reading from writing

Read operations are normally easier to expose broadly. Write operations deserve tighter controls. An agent may be allowed to search documentation and retrieve account details, while sending an email, changing access, issuing a refund, or deploying code requires confirmation or a narrowly scoped policy.

This separation also improves debugging. When an agent produces a bad recommendation, teams can inspect the context and reasoning path without having to unwind an unnecessary external action.

Use structured outputs at system boundaries

Natural language is excellent for communicating with people and poor for serving as an unvalidated interface between components. Whenever an agent hands work to software, prefer a schema. The model can still explain its decision in prose, but the system should consume validated fields.

{
  "action": "escalate",
  "ticket_id": "T-1842",
  "reason": "The request changes billing ownership.",
  "requires_human_approval": true
}

The application should validate that action is allowed, that the ticket exists, and that the proposed transition is valid before it does anything. If parsing or validation fails, send the task back for repair with a specific error, or route it to a person. Never treat well-formed JSON as proof that the requested action is safe or correct.

Make failure a first-class outcome

Agent systems fail in more ways than ordinary integrations. A model can misunderstand an instruction, a tool can time out, retrieved information can be incomplete, or a downstream system can accept a request but fail before the response reaches the agent. Designing only for the happy path creates automation that appears competent until the first busy afternoon.

Define what the system should do when it cannot proceed. Useful patterns include bounded retries for transient errors, deadlines for long-running workflows, checkpoints before irreversible actions, and clear escalation queues. Retries should be deliberate: retrying a read after a temporary timeout may be reasonable; retrying a payment or message send without an idempotency strategy may create duplicates.

Human handoff should include the task, relevant evidence, attempted actions, current state, and the exact question that needs a decision. “Please review” creates more work. “The customer requested an ownership change, but the requester is not listed as an administrator; approve or reject the change” creates a decision-ready handoff.

Measure outcomes, not impressive conversations

An agent should be evaluated against the work it is meant to improve. Track completion quality, escalation rate, correction rate, time to resolution, tool failures, and the cost of completing a successful task. Review samples of both successful and failed runs. A low escalation rate is not automatically good if the agent is acting beyond its competence; an appropriate escalation can be evidence of sound controls.

Build a representative test set before expanding access. Include ordinary cases, ambiguous requests, missing data, conflicting instructions, malformed tool responses, and requests that must be refused. Run the same cases after prompt, model, tool, or policy changes. This turns agent iteration into an engineering discipline rather than a cycle of anecdotal demos.

Deployment is a governance decision

Production readiness is not a label a model earns. It is a decision about risk, permissions, monitoring, and rollback. Begin with low-impact actions, use least-privilege credentials, log tool calls and policy decisions, and make it easy to disable a workflow. Sensitive data should be deliberately minimized in prompts and retrieval results, not included by default for convenience.

Clear ownership matters too. Someone must be responsible for the workflow’s rules, quality targets, incident response, and change approval. An agent that spans teams without an owner tends to accumulate exceptions until nobody trusts it.

The useful future is deliberately engineered

The enduring value of AI agents will not come from systems that sound autonomous. It will come from systems that reliably remove friction from real work while preserving accountability. The model may supply flexibility, but reliability comes from good interfaces, constrained permissions, validated state changes, and honest handling of uncertainty.

That is encouraging news for software teams. Building capable agents does not require abandoning established engineering practice. It requires applying it with more care at the boundary where language becomes action. Start with one useful workflow, make every action observable, and earn autonomy step by step. That is how an agent stops being a demonstration and starts getting things done.

Portret autora bloga

Mihajlo

Ja sam Mihajlo — programer vođen znatiželjom, disciplinom i stalnom željom da stvorim nešto smisleno. Dijelim uvide, tutorijale i besplatne usluge kako bih pomogao drugima da pojednostave svoj rad i rastu u svijetu softvera i umjetne inteligencije koji se neprestano razvija.