Detailed content
🎯 The goal: a real buddy
You already have the pieces scattered throughout the course: a running model (3.1), a local API (3.5), memory with RAG (3.6), and a private network with Tailscale (2.4). This step-by-step guide brings it all together into a personal companion — with a name, a way of speaking, a screen to chat on, and a voice to answer you out loud. Nothing leaves your machine.
🆕 New here?
"System prompt" is a fixed instruction that the model reads before for any conversation: it defines who it is, its tone, and its rules. It’s the personality of the buddy. "TTS" = text-to-speech (text → speech) and "STT" = speech-to-text (speech → text): both also run locally, without sending your audio to anyone.
🔑 The four pieces of the buddy
A buddy = four things you’ve already seen, put together:
- •Personality: a system prompt in the Modelfile with a name, tone, and rules.
- •Face: an interface — Open WebUI (3.2) or a minimal HTML page.
- •Voice and ears: Piper (TTS) to speak and whisper.cpp (STT) to listen.
- •Memory + reach: RAG (3.6) to remember you and Tailscale (2.4) to use it from your phone.
✓ Local Buddy
- ✓Personality and rules 100% under your control
- ✓Chat, voice, and memory without leaving your machine
- ✓Unlimited use — talk all day, only pay for electricity
- ✓Accessible from your phone through your private network
✗ Cloud assistant
- ✗Personality limited to what the platform allows
- ✗Your audio and conversations pass through third parties
- ✗Voice and heavy use are billed per minute/token
- ✗Disappears if access, pricing, or regional availability is cut off
🪪 Personality via system prompt
Personality doesn't come from "training" anything — it comes from a fixed instruction. In Ollama, you save that instruction in a Modelfile (the recipe you saw in module 3.1) and creates a named model from it. Change the text in SYSTEM and you change who the buddy is.
⌨️ Copy-run: give "Hermes" a name and tone
Objective: create a local model called hermes with a defined personality. Save as Modelfile and run the commands.
How to check: ask "who are you?". It should introduce itself as Hermes, in the set tone—and follow the rules (short, no making things up). Don't like it? Edit the SYSTEM and run ollama create again.
💡 Practical tip
A good system prompt has four parts: who it is (name/role), how it speaks (tone), what it can/can’t do (rules), and the language. Avoid huge paragraphs—the model follows short, specific instructions better than a manual. Start simple and adjust as you see how it responds.
🖥️ One interface: a dedicated interface
"Giving it a face" just means pointing an interface at your model. Two paths: the ready — reuse Open WebUI (3.2), which already has chat with history — or lean: a 20-line HTML page that talks directly to the local API (3.5). Start with the simplest one to understand the flow; then move up to Open WebUI when you want history and attachments.
⌨️ Copy-run: a minimal "face" in HTML
Objective: a page that sends your question to the local endpoint and displays the answer. Save it as buddy.html.
How to check: open the file in your browser, type a question, and click Send — your local model’s response appears on screen. It’s your buddy with a personality of its own.
⚠️ Attention: CORS
If the browser blocks the call (CORS error when opening via file://), allow the origin in Ollama: run it with OLLAMA_ORIGINS=* (e.g., OLLAMA_ORIGINS=* ollama serve). For a real buddy, prefer the Open WebUI in http://localhost:3000 — it already takes care of that and gives you history, model switching, and document RAG.
✓ Open WebUI (ready to use)
- ✓Conversation history and multiple chats
- ✓Model switching and embedded-document RAG
- ✓Ready to use from your phone over the tailnet
📄 Minimal HTML (for teaching)
- •Shows the whole workflow in 20 lines
- •Easy to customize its "look" however you want
- •Without history or attachments — it’s just the skeleton
🔊 Giving it a voice: local TTS (Piper)
Voice is what turns a chat into a buddy. Instead of just reading on the screen, it speaks. For this, we use a TTS engine that runs on your machine—the Piper — and streams the model’s response directly to the speaker.
🆕 New here?
"TTS" = text-to-speech: converts text to speech. O Piper is a lightweight, fast local TTS (it even runs on CPU), with several voices — including in Brazilian Portuguese. Each voice is a .onnx; replace the file and the buddy changes its voice.
The voice pipeline is short — text goes in one end, sound comes out the other:
The response becomes text
The local model responds via API (3.5). You take only the response text.
Piper synthesizes speech
The text goes into Piper with a voice .onnx; it generates an audio file .wav.
The system plays the audio
A simple player (aplay on Linux, afplay on macOS) reproduces the .wav. The buddy “spoke.”
⌨️ Copy-run: the buddy says a sentence
Objective: install Piper and turn text into sound through the speaker. Paste it into the terminal.
How to check: you hears the sentence. It changed the .onnx? The voice changes. That same piper ... | aplay is the voice component of the loop in topic 6.
🎤 Listen: local STT (optional)
Giving it a voice is already halfway there. The other half is giving it ears: being able to talk to the buddy instead of typing. It’s optional—but with local STT, the loop is complete (you speak → it understands → responds → speaks back).
🆕 New here?
"STT" = speech-to-text: converts speaks in text (the opposite of TTS). The whisper.cpp is a lightweight local implementation of the Whisper model that runs on the CPU—it records from the microphone, transcribes, and returns text without sending your audio to the cloud.
⌨️ Copy-run: transcribe your voice locally
Objective: record 5 seconds from the microphone and see the text of what you said. All offline.
How to check: the text of what you said appears in the terminal. This text is exactly what you would pass as content in the API call — closing the voice loop.
✓ Worth listening when
- ✓You want to use it hands-free (cooking, driving)
- ✓Speaking is faster than typing for your use case
- ✓You want a 100% private voice assistant
✗ Stick to text if
- ✗Hardware is underpowered — STT + LLM + TTS together are demanding
- ✗A noisy environment interferes with transcription
- ✗You just want the text buddy for now (start simple)
🧠 Memory, voice, and phone: all together
We still need to fit in the last two pieces and start the engine. Memory: with RAG (3.6), the buddy remembers you—preferences, projects, past conversations—by retrieving those passages and injecting them into the context before responding. Reach: running behind Tailscale (2.4), you can talk to it from your phone, anywhere, as if you were at home. See how it all fits together:
Read it as a equation: face + brain + voice + memory (the cyan pieces you assembled in the course) add up to a full local buddy (in amber). Remove any part and it still works — it just feels less buddy-like.
💡 Memory + mobile, in practice
Memory (RAG, 3.6): store your notes (preferences, facts, history) in a vector store and, with each question, retrieve the relevant passages and add them to the prompt—this way Hermes “remembers” without retraining anything. Cell phone (Tailscale, 2.4): with OLLAMA_HOST=0.0.0.0 and the Tailscale app on your phone, switch localhost by IP 100.x from your machine and talk to the buddy from anywhere—without exposing anything to the public internet.
⌨️ Copy-run main: the talking buddy loop
Objective: the complete circuit in one file — question → local API → response → Piper speaks. Save it as buddy-voz.sh.
How to check: run bash buddy-voz.sh, ask something and listen your buddy's response. Type exit to shut down. Requires curl, jq, piper e aplay. Want audio too? Switch the read using the whisper.cpp transcription (topic 5).
✓ A well-built Buddy
- ✓Short, clear, tested system prompt
- ✓Everything local: voice, memory, and data on your machine
- ✓Access only through the tailnet, never the open internet
✗ Common errors
- ✗Expose port 11434 directly to the internet
- ✗Huge system prompt that the model ignores
- ✗Stacking STT+LLM+TTS on hardware that can’t handle it
Self-check (optional): what gives your local buddy the personality — name, tone, and rules?
📌 Module summary
Next module:
4.6 — Performance, scale & troubleshooting