<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: adricasi</title>
    <description>The latest articles on DEV Community by adricasi (@adricasi).</description>
    <link>https://dev.to/adricasi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F526023%2Fd29d726c-c960-47bd-a267-e31c5060474e.jpg</url>
      <title>DEV Community: adricasi</title>
      <link>https://dev.to/adricasi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adricasi"/>
    <language>en</language>
    <item>
      <title>HanashiGemma (話Gemma): a patient Japanese conversation partner that runs on my friend's laptop</title>
      <dc:creator>adricasi</dc:creator>
      <pubDate>Sun, 04 Oct 2026 20:36:33 +0000</pubDate>
      <link>https://dev.to/adricasi/hanashigemma-hua-gemma-a-patient-japanese-conversation-partner-that-runs-on-my-friends-laptop-26i7</link>
      <guid>https://dev.to/adricasi/hanashigemma-hua-gemma-a-patient-japanese-conversation-partner-that-runs-on-my-friends-laptop-26i7</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;My friend Marc is learning Japanese for a trip to Japan next year. He has been studying with real dedication, and like many learners, they find that the jump from textbook to a live conversation is the hardest part: real replies come quickly, there's little time to think about which particle fits, and it's hard to find a relaxed place to practise where small mistakes don't matter.&lt;/p&gt;

&lt;p&gt;I wanted to give them exactly that: a calm, patient partner they can practise with whenever they like, at their own pace.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;HanashiGemma&lt;/strong&gt; (&lt;em&gt;hanashi&lt;/em&gt; 話 = "talk"): a patient roleplay partner for real situations. You pick a scene: &lt;strong&gt;buying a train ticket at Shinjuku&lt;/strong&gt;, &lt;strong&gt;ordering ramen at an izakaya&lt;/strong&gt;, or &lt;strong&gt;asking for directions in Kyoto&lt;/strong&gt;. Gemma plays the station clerk, the server or a friendly local, in beginner Japanese (JLPT N5–N4, polite form).&lt;/p&gt;

&lt;p&gt;Every line Gemma says comes with the help a beginner needs, at their own pace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Furigana&lt;/strong&gt; over the kanji, so every word can be read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Romaji and English&lt;/strong&gt; one click away (hidden by default, so you try to read first).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma's Breakdown&lt;/strong&gt;: the particles and phrases in that line, explained in English.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suggested replies&lt;/strong&gt;: if you freeze, click one, edit it, send it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gentle Fix&lt;/strong&gt;: if your sentence is off, a friendly card shows what you wrote, a more natural version and &lt;em&gt;why&lt;/em&gt;, and the scene just continues. No red marks, never the word "wrong".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=Xcb_41Hrddw" rel="noopener noreferrer"&gt;HanashiGemma DEMO&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the video, everything runs &lt;strong&gt;locally on my laptop with Ollama, with no internet&lt;/strong&gt;. I order ramen at an izakaya: first in English (Gemma shows &lt;em&gt;おすすめは何ですか？&lt;/em&gt;, "What do you recommend?"), then in romaji (&lt;em&gt;ビールもお願いします。&lt;/em&gt;, "A beer too, please."), then in Japanese (&lt;em&gt;おかいけいをおねがいします。&lt;/em&gt;, "The bill, please."), and finally with a real mistake: &lt;em&gt;カードがはらえますか？&lt;/em&gt; → Gentle Fix &lt;em&gt;カードで払えますか？&lt;/em&gt; ("Can I pay by card?";&lt;br&gt;
で means "by/with").&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/adricasi/hanashigemma" rel="noopener noreferrer"&gt;GITHUB&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/adricasi/hanashigemma/blob/main/INDEX.md" rel="noopener noreferrer"&gt;Code Index&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The open pieces it runs on&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4: the open-weight model at the core.&lt;/strong&gt; The demo runs &lt;code&gt;gemma4:12b&lt;/code&gt; locally; on a smaller laptop, &lt;code&gt;gemma4:e4b&lt;/code&gt; works too. &lt;code&gt;gemma4:12b&lt;/code&gt; follows longer conversations better, and switching between them is a single setting.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/adricasi/hanashigemma/blob/main/src/prompt.ts" rel="noopener noreferrer"&gt;src/prompt.ts&lt;/a&gt; builds the system prompt. It sets the scenario role, keeps Japanese at N5–N4 with desu/masu, and defines the Gentle Fix rules (including English or romaji input and no writing-system corrections). It also asks for the JSON reply shape.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/adricasi/hanashigemma/blob/main/src/scenarios.ts" rel="noopener noreferrer"&gt;src/scenarios.ts&lt;/a&gt; defines the 3 fixed scenarios that the prompt plugs in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/adricasi/hanashigemma/blob/main/src/reply.ts" rel="noopener noreferrer"&gt;src/reply.ts&lt;/a&gt; holds the zod schema and the cross-checks. Segments must join back to jp, kanji need readings, and breakdown phrases must appear in jp.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/adricasi/hanashigemma/blob/main/src/server/turn.ts" rel="noopener noreferrer"&gt;src/server/turn.ts&lt;/a&gt; runs the call, validates the reply and retries once, then shows a friendly error (REQ-003).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/adricasi/hanashigemma/tree/main/src/model" rel="noopener noreferrer"&gt;src/model/&lt;/a&gt; has the adapters (ollama.ts, gemini.ts, factory.ts), chosen by GEMMA_BACKEND.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ollama: local inference.&lt;/strong&gt; It runs Gemma on the laptop itself, with no internet connection and no account, and supports structured output, which the whole app relies on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open-source frameworks around it:&lt;/strong&gt; TypeScript, Node.js with Hono for a small local server, and zod to validate everything Gemma returns.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How the app is built around Gemma&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Browser  ▶  small local server builds the scene prompt and checks every reply ▶  Gemma 4 on Ollama (same laptop)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One structured answer per turn.&lt;/strong&gt; Every turn, Gemma returns a single JSON object: its reply split into words with their hiragana readings (for the furigana), the romaji, an English translation, a short grammar breakdown, suggested replies and, when helpful, a Gentle Fix. Ollama receives the JSON schema, so Gemma's output always has exactly that shape, and the interface is simply a view of that object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust, but verify.&lt;/strong&gt; Small models make small mistakes, so the server checks every reply before it is shown: the words must spell the sentence exactly, grammar notes must refer to words that are really there, and readings must be in hiragana. If something is off, Gemma gets one retry with the reason, so a broken turn is never shown.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; with small open models, details matter. Making the "reading" field required in the schema took one tricky turn from failing 5 times out of 6 to working almost every time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/adricasi/hanashigemma/tree/main/docs/adr" rel="noopener noreferrer"&gt;Architecture Decision Record&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;For this project, an open-weight model is the whole point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It runs on my friend's laptop with no internet.&lt;/strong&gt; Practising on a train or a plane works the same as at home.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Their mistakes stay private.&lt;/strong&gt; With Gemma running locally, nothing they type leaves their computer, and the app stores nothing at all. A closed API would send every hesitant sentence to someone else's server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to practise.&lt;/strong&gt; No API key, no quota, no bill. My friend can practise for hours without anyone counting tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I can swap the model.&lt;/strong&gt; Gemma 4 comes in several sizes. Picking e4b for any laptop or 12b for better conversations is one setting, and the same code runs against hosted Gemma too. Nothing ties the app to one vendor. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's what made it something I could actually hand to a friend and say "use it as much as you want".&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;I built it &lt;a href="https://github.com/adricasi/hanashigemma/tree/main/docs/spec" rel="noopener noreferrer"&gt;spec-first&lt;/a&gt; with an AI coding agent (Claude Code) as the main developer and Gemini as an independent code reviewer. The cross-review caught several real bugs before release.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;Best Use of Gemma&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/adricasi"&gt;@adricasi&lt;/a&gt; - Adrià Casals.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
    </item>
  </channel>
</rss>
