Tutorial: llmPrompt

Course Schedule

llmPrompt Chatbot
Image: Live Chat Symbol Icon, AI Tools Icon Set, ChatGPT

We are building a live LLM agent from scratch using a two-part architecture: a mobile agent frontend and a back-end harness.

In this first tutorial, the mobile agent acts as a simple chatbot interface where you enter prompts, and the back-end harness works as a straightforward proxy, routing traffic straight to-and-from an LLM runner. As the model streams completions back, your mobile app will render the output token by token, typewriter-style in real-time. The backend sends your prompt to various available free LLM runners. If you have a paid subscription to a cloud-based LLM provider such as OpenAI/ChatGPT, Anthropic/Claude, OpenRouter, etc. you can use it in addition. We will also show you how to host a local LLM on an Ubuntu host.

We will add new features to both the harness and the agent every single tutorial. By the end of the term, the back-end harness will evolve to handle episodic memory, tool execution, semantic memory, and retrieval-augmented generation (RAG) with knowledge graph. Meanwhile, your mobile agent will transform into a dedicated control center used to manage the harness and to provide essential human-in-the-loop guardrails.

Note: through out the term, always treat messages you send to the harness and the LLM runner as public utterances without any reasonable expectation of privacy. Further, prompts sent to LLM runners will be used by the providers for various reasons set out in their “Terms of Service” they will have you agreed to when you sign up.

Image showing
harnessd serving as HTTP/2 proxy for an LLM runner

About the Tutorials

All tutorials and projects in this course can be completed individually or in teams of up to two students. You are free to change partner for each assignment.

We do not assume any prior mobile, back-end, or LLM-assisted development experience on your part. We will start from the ground up by introducing you to the front-end integrated development environments (IDEs) and back-end development tools. You are welcome to use coding assistants (AI agents). However, you are responsible for understanding all covered material, and you must be able to defend all architectural and design choices during project evaluations.

Are the tutorials cumulative?

Yes, the tutorials and projects are all cumulative. We are building a single agent-and-harness system piece by piece over the term. By the end of the semester, you will have engineered a comprehensive, fully functional agentic AI system.

Because the tutorials require manual infrastructure configuration tailored to each student or team, including but not limited to individualized security certificates and API keys, we cannot provide ready-made reference solutions for download. If you encounter setup difficulties, please seek help from the teaching staff immediately.

For the ULCS (non-MDE) version of the course, each project is directly supported by two immediately preceding tutorials. For example, Tutorials 1 and 2 demonstrate how to accomplish the foundational tasks required for Project 1.

While the tutorials can largely be completed by cut-and-pasting code snippets from the specifications, you will be significantly more productive during project design and implementation if you actively understoodd the codebase. As a general rule of thumb: the less time you spend reading, the more time you will spend debugging.

Objectives

Front end:

Back end:

API and protocol handshake

Our harness communicates with LLM runner using OpenAI-compatible /v1/chat/completions API. Being the first API to popularize LLM, it has been become the industry de facto standard, supported not only by OpenAI, but also Claude, Gemini, Perplexity, Grok, Qwen, Kimi, GLM, MiniMax, Mistral, OpenRouter, Ollama, oMLX, and others (some with variations in the actual endpoint URL). It governs the format of the JSON data sent to/from the LLM runner. It also governs how the SSE protocol is used to stream the return completion.

For this tutorial, our back-end harness, harnessd (‘d’ for daemon as a long-running background Unix process that acts as a server is usually called), has two APIs:

Using the following syntax:

url-endpoint
-> request: data sent to harness
<- response: data sent to agent followed by HTTP Status

The protocol handshake consists of:

/
-> HTTP GET
<- "UM EECS Agentic harnessd" 200 OK
    
/llmprompt
-> HTTP POST { model, messages, stream }
<- { OpenAI's /v1/chat/completions SSE stream } 200 OK

Note: ‘/’ API is disabled on mada.eecs.umich.edu to deter random probing.

Data format

To post a prompt to the harness using the llmprompt API, the front-end agent sends a JSON object consisting of a model field, a messages field, and a stream field. The model field holds the name of the model, for example gemma3:270m. The messages field carries an array of JSON objects, whose content carries a prompt and role specifies the kind of prompt, whether it’s a user prompt for the LLM to complete, or a system prompt giving the LLM instructions. The stream field indicates whether the LLM runner should stream its response or to batch and send it in one message. For example:

{
    "model": "gemma3:270m",
    "messages": [
        {
            "role": "user",
            "content": "howdy?"
        }
    ],
    "stream": true
}

Some models require "max_tokens" to be listed as a top-level field in addition.

Upon receiving this JSON object, harnessd simply forwards it to an LLM runner. In this course, we only work with streaming responses, so stream is always set to true and the reply from the LLM runner is in the form of a SSE stream. The JSON objects carried in OpenAI’s /v1/chat/completions API is specified in their Create chat completion document. In this tutorial, harnessd works as a straightforward proxy, it simply passes traffic straight through. It does not accumulate and store traffic going either direction.

Specifications

There are TWO parts to this tutorial: a front-end mobile agent and a back-end harness. For the front end, you can build either for Android with Kotlin/Compose or for iOS in SwiftUI. For the back end, you can choose between these micro web frameworks: Echo (Go), Fastify (Typescript), Starlette (Python), or axum (Rust).

You only need to build one front-end agent, AND one of the alternative back-end harness. Until you have a working harness of your own, you can use the one running on mada.eecs.umich.edu to test your front end. To receive full credit, your agent MUST work with your own harness and with the one on mada.eecs.umich.edu. Your harness can either connect to a cloud-based or a self-hosted LLM. We have provided a list of LLM providers with free-tier access. You will need to apply for an API key at the provider of your choice (usually no credit card required). We have also provided instructions on how to host a local LLM provider. Local hosting is optional.

IMPORTANT: unless you plan to learn both the Android and iOS platforms, you should do all tutorials on the same platform as for your projects.

:warning:There is a graded TODO item in the back-end spec that you must complete as part of the tutorial.


Prepared by Sugih Jamin | Last updated: June 21st, 2026

Acknowledgments: I used Gemini 3.5 Flash to proofread, refine the phrasing, and improve the stylistic flow of this document. All structural concepts, technical details, and final editorial decisions remain entirely my responsibility.