AI Engineer Europe 2026

Why (Senior) Engineers Struggle to Build AI Agents

Read the talk

Why (Senior) Engineers Struggle to Build AI Agents

Building reliable agents means preserving intent, letting plans change, recovering from failures, and evaluating outcomes instead of prescribing every step.

From a talk by Philipp Schmid

Before you start: Familiarity with API calls, application state, and software tests is helpful; no particular agent framework is required.

From prescribed steps to delegated goals

What changes when the software you build chooses how to carry out a task? Philipp Schmid, who introduces himself as working at DeepMind on agents with Gemini and the Gemini API, identifies five shifts from his experience with engineers inside and outside Google. The starting point is a familiar development sequence: write a specification or PRD, implement it, test it, deploy it, and let users use it.

Agent development makes observation part of the implementation loop. Define instructions, run the agent, inspect its behavior, adjust the prompt or tools, and run it again. You are improving a system’s ability to reach an outcome, including behavior you did not explicitly write.

Schmid compares the change to moving from traffic controller to dispatcher. A traffic controller regulates lights, speeds, and available roads. A dispatcher specifies a destination: get from Germany to London. The traveler might take a train, fly, or drive through the tunnel. The goal stays fixed while the route can vary. A coding agent can likewise take surprising intermediate actions and still deliver the requested result. That distinction—between specifying the outcome and prescribing every step—underlies the five examples that follow.

Slide titled “Traffic Controller vs. Dispatcher” with three explanatory bullets and blue and green illustrations of traffic control and dispatch.Open full source frame
Traffic controller versus dispatcher: two mindsets for building software and agents.
0:150:28
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

0:15 · section reference included

Text as state

A research agent proposes a plan. An interface with only accept and deny buttons treats the next interaction as a Boolean decision. But the user may want to approve the plan and modify its scope in the same response: approve the market research, focus on the US, and exclude California. The language carries both authorization to proceed and a constraint on the work.

If the interface cannot preserve both meanings, the user may have to reject the plan, answer a follow-up question, and wait for a replacement. A useful state representation instead retains the approval together with the additional instruction. For example, the response Approve the plan; focus on the US market and exclude California. should leave the research pending, with the plan approved and its scope constrained. Approval is not evidence that research has already happened.

Approve the plan and preserve the constraint

Constructed example: The exact response wording, record labels, and explicit pending execution state are teaching details constructed from the research-plan example.

User response — unchanged
Approve the plan; focus on the US market and exclude California.

Operation: Record approval and the additional scope instruction without marking the research as completed.

Task

Before: Proposed plan
Market research
After: Approved plan, research still pending · Unchanged
Market research

Plan approval

Before: Proposed plan
Awaiting user response
After: Approved plan, research still pending · Changed
Approved

Additional scope instruction

Before: Proposed plan
Not present
After: Approved plan, research still pending · Added
Focus on the US market and exclude California.

Research execution

Before: Proposed plan
Pending
After: Approved plan, research still pending · Unchanged
Pending
One response changes approval and scope while leaving execution pending.

Personalization has the same problem. A user may generally prefer Celsius but want Fahrenheit for cooking. A single use_fahrenheit flag loses the conditional preference; a location flag such as is_europe cannot supply it either.

InputWhat a single flag loses
Approve, but exclude CaliforniaApproval and a scope change together
Celsius generally; Fahrenheit for cookingA default with a contextual exception

Structured storage can hold these preferences. The design mistake is reducing their meaning to a structure too narrow to express them. Preserve the context the model needs to interpret the next request.

2:052:16
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

2:05 · section reference included

Let intent change during the conversation

Context is not limited to text: images, video, and audio can also shape what the agent should do. The next shift concerns who chooses the response. In a traditional customer-support pipeline, a cancellation request goes through an intent classifier, receives a churn label, and enters a predefined retention or cancellation workflow.

Now suppose the agent explores the request and offers an alternative. The user accepts and changes their mind about canceling. The initial classification no longer describes the conversation’s goal. An agent needs to respond to the updated meaning, rather than continue along the cancellation path because that was the first label assigned. Enumerating every offer, response, and change of intent as a stateful workflow becomes difficult. Delegating some control lets the next action follow the conversation as it develops.

Slide with trap, collision, and fix bullets beside a customer support conversation showing a cancellation request, an offer, and acceptance, labeled churn and retention.Open full source frame
A cancellation request becomes retention in the “Hand Over Control” example.
3:424:05
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

3:42 · section reference included

Return errors to the agent

Handing over control means leaving a purely deterministic workflow. It also changes how failures should enter that workflow: an error is another input the agent can reason about. Schmid uses Go as an analogy. Go functions commonly return an ordinary result alongside an error value, and the caller checks and handles the error explicitly. A failed tool call can similarly produce information for the next decision instead of terminating the entire task.

Retrying a cheap HTTP request or product search may be acceptable. Restarting a long agent run has different consequences. Schmid asks us to imagine an agent that has already worked for five or fifteen minutes: starting over repeats the preceding computation and may discard useful context. Feed the failure back to the model so it can consider a workaround or an additional check while retaining the work already done.

A small Go adapter can make both outcomes available to the surrounding agent loop:

go

package agent

type SearchInput struct {
    Query string
}

type SearchObservation struct {
    Input   SearchInput
    Results []string
    Error   string
}

func ObserveSearch(
    input SearchInput,
    search func(string) ([]string, error),
) SearchObservation {
    results, err := search(input.Query)
    observation := SearchObservation{
        Input:   input,
        Results: results,
    }
    if err != nil {
        observation.Error = err.Error()
    }
    return observation
}

The caller adds this observation to the existing context. The failed query remains visible, any returned results remain available, and the error becomes material for choosing the next action. Recovery still requires a decision; returning an error as data does not itself repair the failure. The purpose is to continue from the current state instead of automatically restarting.

4:535:08
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

4:53 · section reference included

Trace the route, grade the outcome

Traditional unit, integration, and smoke tests often check a deterministic relationship: input A passed through code B produces output C. An agent can take different steps and produce different results from the same input. Evaluating it therefore requires asking how often it succeeds, not just whether one run passed an assertion.

Schmid’s hypothetical customer agent succeeds on the same prompt only one out of ten times—too flaky to release. This is an illustration of the reliability question, not a measured benchmark or a proposed universal release threshold.

Success also depends on the task. A research report and a customer-feedback response may need different qualitative criteria, assessed by an LLM judge or a human expert. Trace what the agent does so its behavior can be inspected, but grade whether its output satisfies the request. One user’s report might require four additional research steps and more tokens than another’s. The longer route can still be the successful one; matching a single expected sequence would miss that distinction.

6:136:25
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

6:13 · section reference included

Make the tool contract carry the meaning

Schmid’s fifth shift starts with a mismatch: agents evolve, while existing APIs retain assumptions built for their human developers. In a product microservice, delete_item(id) may feel self-explanatory to someone who has worked on the service for years. The agent has none of that accumulated context.

What does id identify? What happens when deletion fails? A developer familiar with the implementation may already know. Initially, the agent sees function schemas, docstrings, and tool definitions—not the code behind them. Those exposed surfaces must carry the information needed to choose and use the operation.

  • Semantic names: Identify the operation and the kind of object it acts on.
  • Parameter descriptions: Explain what an identifier refers to, rather than merely labeling it id.
  • Failure behavior: Describe what the caller receives when the operation cannot complete.

The interface should be understandable without the experience of the team that built it. Delegating tool selection does not remove the application’s responsibility to validate an operation before executing it.

“Agents Evolve, APIs Don’t” slide contrasts a yellow Human-Grade API panel with a green Agent-Ready API panel, including code examples and semantic naming, verbose docstrings, and just-in-time adaptation labels.Open full source frame
Human-grade and agent-ready APIs compared side by side.
7:478:10
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

7:47 · section reference included

Choose what reliability must mean before release

Trusting the model and verifying its work are complementary responsibilities. Avoid forcing every request through one fixed sequence merely because that sequence is familiar. Preserve the meaning in the context and provide a way to recover when something goes wrong. Longer runs make recovery especially consequential: imperfect models can encounter unexpected situations well after useful work has accumulated.

The release decision then becomes an explicit reliability judgment. An assertion about one successful run is insufficient. Determine how frequently the agent must succeed for the user-facing task, and evaluate against that requirement. Schmid gives no single percentage that makes every agent ready; choosing the acceptable level is part of engineering the product.

9:049:19
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

9:04 · section reference included

Build to delete

The final implication concerns the lifetime of the software itself. Invoking The Bitter Lesson, Schmid argues that better models and agents will lead engineers to rebuild the same capabilities repeatedly. Build to delete is his engineering interpretation: expect some of today’s software to become disposable as the underlying capabilities improve.

Summary slide with five numbered cards: Stop Fighting the Model, Preserve Meaning, Design for Recovery, Evaluate Don’t Assert, and Build to Delete.Open full source frame
“Trust, but Verify” closes with five principles, including “Build to Delete.”

Schmid closes by pointing readers to his companion article for a deeper treatment and code examples. The enduring responsibility is to make the current agent useful and reliable while remaining willing to replace the implementation that gets it there.

9:5310:08
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

9:53 · section reference included

Resources

From the talk

  • Schmid’s companion essay develops the five engineering shifts with examples of contextual state, recovery, evaluation, and descriptive tool interfaces.

  • The Bitter LessonArticle9:53

    Rich Sutton’s 2019 essay on why general methods that scale with computation prevail, reproduced in a university-hosted PDF.

  • Errors are valuesArticle

    Rob Pike explains how Go programs can manipulate error values and organize error handling without repetitive checks.

Updates since the talk

Transcript download
Read the transcript and explore slides

Loading transcript and slides…