DEV Community

Roronoa
Roronoa

Posted on

Your AI Feature's First User Should Be a Script, Not a Human

Your AI Feature's First User Should Be a Script, Not a Human

Last week I watched a demo of an AI travel assistant running on an Android phone. The model answered three questions in a row, the room nodded, and then the presenter rotated the device to show a map. The activity restarted, the conversation vanished, and the demo ended on the home screen. The model had worked perfectly while the feature had failed completely, and nobody in the room could tell you which was true.

The current agent trend is full of advice about remembering decisions instead of just data. Reasoning ledgers, memory layers, and state machines are excellent ideas for a server that never sleeps. On a phone, the ledger itself gets backgrounded, killed, or stripped of permissions before it writes its first entry. I think the first user of every mobile AI feature should be a script, not a human, because a script will not forgive the lifecycle.

My position is simple: if your AI feature cannot pass a scripted lifecycle probe, you do not have a demo problem, you have a product problem. Humans forgive lost conversations, invisible retry buttons, and silent failures. Scripts forgive nothing, which is exactly why you should let them go first.

Why Demos Lie to You

A demo is a story with a single happy path. The network is up, the permission dialog was accepted months ago, and the activity stays in the foreground for exactly ninety seconds. Your users live in the other world where airplane mode, battery saver, and a swipe-away gesture arrive without warning.

Every mobile AI failure I have debugged happened at a lifecycle boundary. The request was in flight when the app went to the background, the model returned a response that the killed activity could not deliver, or a permission revoke left the SDK in a state that only a restart could fix. None of those failures show up in a chat demo, and all of them show up in a probe.

The Probe Workflow

To run this kind of probing you need two cheap things: model access for thousands of small calls and a server that collects the results. I used MonkeyCode for both, because its current offer happens to match the workflow. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The operator's published terms, as of this writing, include free model access with a 10 million token allocation and a free server option, so treat the numbers as a snapshot and verify them before you build anything.

The point is not the vendor. The point is that the workflow should cost almost nothing, run every night, and produce a table you can argue about. The code below is generic, so you can point it at any model endpoint and any collector you already own.

Step 1: Write a Probe That Cannot Lie

The probe sends one fixed prompt and checks that the response contains the expected token. No chat, no creativity, no ambiguity.

# probe.py — run this on the device or emulator under each lifecycle condition
import time
import requests

PROBE_PROMPT = 'Reply with the word READY and nothing else.'
COLLECTOR = 'https://your-free-server.example/probe'  # your MonkeyCode server URL

def run_probe(label: str, model_url: str) -> dict:
    start = time.perf_counter()
    try:
        response = requests.post(model_url, json={'prompt': PROBE_PROMPT}, timeout=15)
        latency_ms = round((time.perf_counter() - start) * 1000, 1)
        text = response.json().get('text', '')
        result = {
            'label': label,
            'status': 'pass' if response.ok and 'READY' in text else 'fail',
            'latency_ms': latency_ms,
            'tokens': response.json().get('tokens', 0),
            'error': None,
        }
    except Exception as exc:
        result = {
            'label': label,
            'status': 'fail',
            'latency_ms': None,
            'tokens': 0,
            'error': str(exc),
        }
    requests.post(COLLECTOR, json=result)
    return result
Enter fullscreen mode Exit fullscreen mode

The fixed prompt matters more than it looks. A free-form conversation can pass a hundred times while a single-word contract catches regressions on the first run.

Step 2: Collect Results on the Free Server

The collector is a tiny endpoint that stores each probe result in a table. You can read the table in the morning and argue about the numbers instead of arguing about feelings.

# server.py — deploy this to your MonkeyCode free server
from fastapi import FastAPI, Request
import sqlite3

app = FastAPI()

@app.post('/probe')
async def record_probe(request: Request):
    data = await request.json()
    con = sqlite3.connect('probes.db')
    con.execute(
        "INSERT INTO probes (label, status, latency_ms, tokens, error, ts) "
        "VALUES (?, ?, ?, ?, ?, datetime('now'))",
        (data['label'], data['status'], data.get('latency_ms'),
         data.get('tokens'), data.get('error')),
    )
    con.commit()
    return {'ok': True}
Enter fullscreen mode Exit fullscreen mode

SQLite is enough for a single device matrix, and you can move to Postgres when the table outgrows you.

Step 3: Run the Lifecycle Matrix

This is the part that separates a probe from a benchmark. A benchmark measures speed on a clean device, while a probe measures survival across the transitions your users actually trigger.

Condition How to trigger it Pass criterion
Permission revoke Revoke network access while the app is foregrounded The next request fails gracefully and recovers after restore
Background kill Swipe the app away while a request is in flight No orphaned token spend and state restores on relaunch
Airplane mode Toggle it during streaming The request times out under 15 seconds without a crash
Battery saver Enable it at 20 percent charge Latency is recorded and the response is not silently dropped
Network switch Move from Wi-Fi to cellular mid-request The request completes or returns a retryable error

Run the matrix on at least one real device per OS, because the emulator will not reproduce radio behavior. Schedule the probe nightly with a cron job or a WorkManager task, and let the collector accumulate a history.

What the Results Actually Mean

A pass means the feature survived one specific transition on one specific device with one specific OS version. It is not a license to stop testing, but it is a tripwire that catches regressions before your users do. A fail means you have a reproducible bug with a device name, a timestamp, and a stack trace, which is more than most bug reports ever give you.

The token math works in your favor here. A probe call costs a fraction of a chat session, so a 10 million token allocation covers thousands of nightly runs across a small device matrix. That is the correct use of free model access: spend it on the boring, repetitive, honest calls that tell you when the feature broke.

Who Should Not Use This Workflow

Teams without access to a real device should not pretend an emulator is enough, because radios, thermal throttling, and permission behavior differ. Teams shipping server-side AI can skip the lifecycle matrix entirely, since the phone is not the failure domain. Teams that cannot act on failures should also skip it, because a probe that nobody reads is just another dashboard with a green light.

The Bottom Line

Your demo will always work, and your users will not always forgive. Put a script in front of your mobile AI feature before a human ever sees it, and spend your free tokens on the thousands of small probes that tell you the truth. If you want to run this tonight, MonkeyCode's free server and model access are enough to start; verify the current terms, then let the script go first. The chat window can wait.

Top comments (0)