Skip to content
All Stories
2026-11-12•11 min read

AI Agents · Coding Agents · Multimodal AI · Pixeltable Cloud · CLI · Computed Columns

Can a Coding Agent Maintain a Multimodal AI Backend? Recorded Runs, From Build to Export

A coding agent can write a backend. Keeping it running is harder: changing a column, refilling failed model calls, deploying it, reading its logs and traces, exporting its data. Here is one claims desk app taken through all of it with pxt commands, every output from recorded runs.

Pierre Brunelle

Pierre Brunelle

Pixeltable Team

View as Markdown

Coding agents are good at writing a backend. The trouble starts the day after: a new column on a table that already holds data, a model provider that times out halfway through a batch, a deploy, a request that came back wrong. In the usual stack each of those touches a different system, and the agent can edit code in all of them but can see the result of almost none.

This post takes one multimodal app through that whole life, using pxt commands an agent can run and read, and at the end one short script. The app is a claims desk: a customer sends a photo and a voice note, and the app transcribes the note, checks the photo, routes the claim and writes a reply. Every output below is from recorded runs of the app, on my laptop and then on Pixeltable Cloud. The requests to the agent are staged, and the outage is simulated.

Why the day after the build is harder#

A multimodal backend is usually an API service, a queue and workers, a preprocessing job, an embedding job, a vector database, a metadata database, a status table and a retry script. Each piece is fine on its own. The trouble is in between. When the agent changes a prompt, which rows are stale? When OpenAI fails for twenty minutes, which cells need a retry, and who wrote that bookkeeping?

Pixeltable keeps that state in the tables. A column is either stored data or a computation over other columns, so the system knows what depends on what, what failed and what each version held. That is what lets the commands below answer the agent's questions in plain text.

The app: one file#

I asked the agent for a claims desk: "transcribe the voice note, check the photo, route the claim, write a reply." It wrote one app.py. The class below is an excerpt of that file, not a runnable program: the helper functions, QUESTIONS and MODEL are defined above it in the file.

python

The annotations are stored columns: the product name, the photo, the voice note. The assignments are computed columns. seen is a CLIP query that names the product in the photo, photo_ok and route are plain Python functions, and the rest are model calls. triage is one call that answers four questions about the photo and the transcript. The same file declares the HTTP routes:

python

The file adds two query routes the same way: /similar returns the stored claims whose transcripts are closest to a text, and /review lists the claims that did not route to approve, highest severity first.

Build and serve: two commands#

The recorded schema update is the one that added the products table and its CLIP index. An earlier version of the file had already created claims, so it shows as updated:

bash

A POST with a blender photo and a voice note ("The jar on my blender cracked down the side the second time I used it, and now it leaks all over the counter.") came back with the stored row in 9.6 s, routed to approve. The route answers once every computed column of the row is stored, so the agent's first read of the result is also the database's.

Change a table that already holds data#

Then the second request: "Add a column with what is wrong in each claim, and make the reply one sentence." The agent added two lines to the class and, before applying anything, asked for the plan:

bash

"0 destructive" is the line an agent should check before every apply. pxt schema update then added the column and computed it for the five stored claims: four demo claims a seed script filed, and the blender claim from the POST. The transcripts and replies were not run again, because nothing they read had changed.

The prompt change is the subtle one. The reply prompt lives in a function body, so the catalog does not change: pxt schema update reports "catalog is up to date" but reloads app.py for the commands that follow, and then the agent asks for the rerun explicitly:

bash

Three columns, because reply_text and voice_reply read reply. Nothing upstream of it reran.

I had stopped the local service before this change, so the recording has no request against the changed table. A service still running on the old file needs pxt service update app.py claims -f after each of the two edits: its insert route answers 409 once the table has columns it does not know, and its process keeps the old prompt for new rows. pxt recompute rewrites stored rows only.

Recover from a provider outage#

Next, a claim arrives while OpenAI is unreachable. I simulated that by pointing the import script's OpenAI base URL at a closed port. The script inserts with on_error='ignore', so the row is kept with its errors:

bash

One recompute of the first failed column filled every cell that depended on it. One thing we learned recording this: the HTTP insert route aborts on a model error and stores nothing, so a client that must not lose a request retries it, or the app takes it through a Python insert like the one above.

Every change is a version#

bash

The column, the reply rerun, the outage's claim and its fix are four versions (I dropped the timestamp and deletes columns here). pxt revert claims/claims --steps N -f undoes the last N of them. It counts back from the newest version and takes no version number, and it is the undo an agent needs when the change it just made was the wrong one.

Deploy the same app.py to Pixeltable Cloud#

The file the agent changed on my laptop went to Pixeltable Cloud as it was. The project folder added a requirements.txt, and pxt db diff --add-config-entry wrote the database's entry in pixeltable.toml. Then the same commands, pointed at a pxt:// database:

bash

pxt db update builds the database's image from the project and uploads the project. The first build took 950 s; two later runs with the image already built took 143 s and 251 s. The OpenAI key went in as a secret scoped to that one database (pxt secret set). A service reads its secrets when it starts, so the agent sets the secret before pxt service update, as this run did, or runs pxt service restart after.

The POST used an API key granted access to this one service and nothing else (pxt key create hero-ship-post2 --grant access:pxt://pixeltable:claims/services/claims), deleted right after with pxt key delete hero-ship-post2:

bash

The reply is one sentence, because the prompt change went up with the file.

Read what happened: logs and spans#

Last, the agent reads the request from the server's side:

bash

The version the insert made, then the insert, then the request. A service that fails to start puts its traceback in the same log.

A hosted service also keeps sampled traces, and pxt service spans prints them with each span indented under its parent. These are four of that request's spans:

bash

So 7.1 s at the gateway, 6.9 s of it in the insert that runs the computed columns. The trace stops there: in this build the insert is one span, with no span per model call, so it tells the agent which layer was slow and not which column. Spans are read only from hosted services.

Take the data somewhere else#

The last step has no pxt command. It is one library call in a script the agent writes and runs, here copying five text columns of the hosted table into a SQLite file:

python
bash

db_connect_str is a SQLAlchemy connection string; SQLite is the only target I ran. The script prints nothing, so the agent reads the result from the target database. I exported text columns only, not the photo or the voice note.

What the agent reads at each step#

StepCommandWhat the output tells the agent
Buildpxt schema updateWhich tables were created or updated
Serve, and again after a changepxt service updateWhich routes were added, and where they answer
Plan a changepxt schema diffWhat will change, and whether anything is destructive
Rerunpxt recomputeWhich columns reran, on how many rows
Recoverpxt errors, pxt recompute --errors-onlyEvery failed cell with its error, then what was filled
Undopxt history, pxt revertEach version and what it changed, then the last ones undone
Deploypxt db updateWhether the hosted database's generation is done
Observepxt service logs, pxt service spansRequests, inserts and startup errors, then where a request's time went
Exportexport_sql() in a scriptNothing: the agent queries the target database

Where this is the wrong tool#

If you don't want a schema, Pixeltable is the wrong tool: everything above follows from the columns being declared. It also won't judge your prompts for you. The agent can rerun a reply in one command, but deciding that the new reply is better is still evaluation, and evaluation is still on you. And a hosted database is a real build: the first image took about sixteen minutes, which you notice when an agent is waiting on it.

For the same app from a different angle, ClaimBot covers the claim row itself, and What Is Pixeltable? walks the local-to-Cloud path in more detail. Configure Pixeltable Cloud covers pixeltable.toml, secrets and deploy order.

Try it with your agent#

bash

Then ask your agent for an app. If you have no account, pxt new gives your agent a free hosted trial database. Claim it within 48 hours or it is deleted; once claimed, the organization is on the Community plan. The commands and outputs above were recorded on Pixeltable 0.7.24.dev7, a development build from before the 0.7.24 release.

Frequently asked questions

Can a coding agent maintain a multimodal AI backend?

Yes, when every operation is a command the agent can run and whose output it can read. In Pixeltable the backend is one app.py, and building, changing, recovering from failed model calls, deploying and reading logs and traces are pxt commands: pxt schema diff, pxt schema update, pxt recompute, pxt errors, pxt history, pxt db update, pxt service update, pxt service logs and pxt service spans.

How does an agent add a column to an AI pipeline that already has data?

It adds the computed column to app.py, runs pxt schema diff to see the plan (the recorded run printed "0 destructive"), then pxt schema update, which computes the new column for every stored row without rerunning the columns that did not change. A service already running answers 409 on its insert route until pxt service update restarts it.

What happens to rows when a model provider is down?

An insert from Python with on_error="ignore" keeps the row and stores the error in each failed cell. pxt errors lists them, and pxt recompute with --errors-only on the first failed column fills them and everything that depends on it. An HTTP insert route aborts on a model error and stores nothing.

Does the app.py change to run on Pixeltable Cloud?

No. The same file deploys with pxt db update, then pxt schema update and pxt service update against a pxt:// database URI. The recorded run added only a requirements.txt and a database entry in pixeltable.toml.

Declarative. Multimodal. Incremental.

One Python file for tables, indexes, and HTTP. Start locally, or sign up for Cloud.

Get started

Docs quickstart · 10-minute tutorial · GitHub · Discord