One indie team building a social matchmaking mini-program tried to skip the conversation box entirely. Their flow was clever: users post a need, the system matches people, AI handles the back-and-forth automatically. No direct chat required. It collapsed almost immediately — users had no idea where to start or what the product even did. They put the chat box back on the home screen, and things started working again.
The lesson they drew: for LLM-based products, any attempt to push the conversation box out of the core interaction flow is going to fail. Not because users are lazy, but because the box does something structural. It lets users arrive with a half-formed idea — "I feel stuck on my product" — and have the AI help them figure out what they actually need before anything else happens. A post form can't do that. A button grid can't do that.
So the conversation box is settled. What isn't settled is everything behind it.
The part that actually breaks in production
The UI widget is the easy half. A chat input, a message list, some streaming text — you can scaffold that in an afternoon. What breaks at scale, or even at modest usage, is the server side: how do you persist conversation state across requests, route tool calls reliably, and keep sessions from bleeding into each other?
Most early prototypes handle this with in-memory state. That works until the process restarts, or until you have two users at once, or until you want to replay a session for debugging. Then you need a real session store.
SQLite is the right answer for a solo developer or small team at this stage. It's a single file, zero-config, fast enough for hundreds of concurrent sessions, and it gives you a proper query interface for inspecting what's actually happening. The pattern is straightforward: each session gets a row, each message gets appended as a JSON blob or a normalized record, and the backend reconstructs context from the store on every request rather than trusting in-memory state.
The SQLite editor built into VicroCode means you can inspect and edit that session table directly without leaving the platform — useful when you're debugging a stuck conversation or want to manually replay a tool call sequence.
What the Python backend actually needs to do
The backend has three jobs: receive the user message, load session context from SQLite, call the model with the right context window, handle any tool calls the model returns, write results back to the session store, and stream the response. That's it, but each step has friction.
Tool call handling is where most early implementations get messy. The model returns a tool call, you execute it, you append the result, you call the model again. If that loop isn't managed cleanly — with proper error handling and result serialization back into the session — you end up with corrupted context that produces confusing responses downstream.
For AI agent development, this loop is the core primitive. An agent is just a chat backend that executes tools and persists the results. The Three Kingdoms SLG project in the market evidence is a clean example of this pattern taken to an extreme: the game server exposes a WebSocket API and JSON state, any model can connect and play, and the server-side validation is the same for human and AI clients. The agent is just a client that reads state and calls actions — the durability lives in the game server's own ledger, not in the model.
The same principle applies to a chat-first app. The model is stateless. Your session store is what makes the conversation coherent across turns.
Building this on VicroCode
The practical path here is a hosted Python backend with a SQLite session store, fronted by an HTML chat interface. You can run Python online with VicroCode's hosted execution environment, which handles the backend without you managing a server. The chat UI is a hosted HTML web app — a simple fetch-based interface that streams responses from your Python endpoint.
The session store schema doesn't need to be clever:
CREATE TABLE sessions (
session_id TEXT PRIMARY KEY,
created_at INTEGER,
updated_at INTEGER
);
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT,
role TEXT,
content TEXT,
tool_calls TEXT, -- JSON, nullable
created_at INTEGER
);On each request, the backend queries messages by session_id ordered by created_at, reconstructs the message list, and passes it to the model. Tool call results get their own rows with role `tool`. Nothing fancy — the simplicity is the point.
The model access comes through VicroCode's Model Center APIs, so you're not managing API keys or rate limiting yourself at the infrastructure level.
Where this approach hits its limits
SQLite works well until write concurrency becomes a real constraint. A single-file database with WAL mode handles more than most solo projects will ever need, but if you're building something with high-frequency parallel sessions — say, an agent that's being called by many users simultaneously with heavy tool use — you'll eventually want a proper database. That's outside VicroCode's current scope, so plan for that boundary if your usage pattern demands it.
Similarly, if your tool calls need to reach external services — third-party APIs, webhooks, anything outside the platform — you'll need to design those integrations carefully within what the hosted Python environment can reach. The architecture is sound; the question is which tools you can actually wire up.
The actual decision
The conversation box debate is over. What's worth your attention now is whether your session layer is durable enough to support real agent behavior — multi-turn reasoning, tool use, context that survives restarts. A Python backend with a SQLite session store, hosted and queryable in one place, is a reasonable starting point for most chat-first AI apps at the indie or small-team scale. The UI is the easy part. Build the ledger first.