DEV Community

Changsu Seong
Changsu Seong

Posted on

What Happens When You Put an AI in a Ghost Story?

Hacktoberfest Open-Source AI Challenge: Ghost in the Machine Submission ๐Ÿ‘ป๐ŸŒผ

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine

Every year at midnight, the piano in the empty Whitcombe House plays a single bar.

Dingโ€ฆ One lonely note drifts through the halls.

Thenโ€”silence.

Six guests are already seated at the dining table. They are dead, but on Dรญa de los Muertos, they return as long as someone remembers them. A seventh guest is trying to come back, too. There is just one problem: nobody remembers who she was.

Four things in the house still hold pieces of her story: a photograph with her face scratched out, a letter that was never sent, a gramophone record from the 1931 party, and the guest list. Walk through the house and question them. Each object is brought to life by Gemma 4, and each knows only what it witnessed. If it claims to know something it couldn't have seen, code catches it before the answer reaches you.

That is The Last Guest.

TL;DR

  • Play in your browser: the-last-guest-site.onrender.com
  • Gemma 4 E2B runs in your browser with WebGPU; Gemma 4 E4B runs on a Render server. Both pass through the same guardrail.
  • Given the whole story in its prompt, Gemma alone revealed another object's secret in 8.6% of 478 test questions. Inside the game's harness, the word check found leaks in 0 of 478 answers.
  • Available in English and Korean. You can reach the ending using suggested questions alone.

Gameplay: the title screen, dining room, speaking record, and ending

What I Built

A short, first-person ghost story you can play right in your browser. Explore a moonlit house built with Three.js: a foyer, a study with a gramophone, and a dining room where six hooded ghosts sit around a candlelit table. At the far end, a piano waits beside an empty bench. Creakโ€ฆ* The house is silent, except for you. Get close to the ghosts andโ€”one by oneโ€”they slowly turn their heads to look straight at you.

Four objects give off a faint glow in the darkness, waiting for you to question them. Ask whatever you like, in your own words. Listen closelyโ€ฆ each answer may hold a clue. When you think you know who she was, prepare her place at Seat 7: an ofrenda with her seat, her face, her name, flowers to light her way, and something she loved. Tick-tockโ€ฆ The candles are burning down. Get it right before the last flame goes out, and she may come back.

Dรญa de los Muertos carries the idea that, as long as someone remembers a person, they can return to be with us. But AI often fills gaps with plausible stories instead of admitting what it doesnโ€™t know. I wanted to bring these two ideas together in one game. Just as we must remember someone accurately to keep them from being forgotten, I wanted the AI to say only what it actually knows.

Every line of dialogue in the game follows one rule. When the model generates a response, code checks whether it contains information that the object in question could not know.

Demo

Version Where What answers your questions
Free, always on the-last-guest-site.onrender.com Gemma 4 E2B runs in your browser using WebGPU. It downloads about 3.1 GB once and keeps the model locally. Without WebGPU, the house answers from its script.
Server version (while hosting credits last) the-last-guest.onrender.com Gemma 4 E4B runs on the server CPU; an answer takes about 27 seconds.
Your own machine uv run python server.py Gemma 4 E4B through Ollama, fully offline.

Suggested questions get instant answers on every version. Gemma 4 E4B wrote those answers in advance, and they went through the same checks.

Six hooded ghosts gathered around a candlelit table

Asking the gramophone a question

Code

GitHub logo scs0209 / the-last-guest

The Last Guest โ€” a Dรญa de los Muertos ghost story where fine-tuned open models remember only what they saw

The Last Guest: a moonlit Victorian mansion at the end of a path of marigolds and candles

The Last Guest

A forgotten story, waiting to be remembered.
A Dรญa de los Muertos ghost story where four objects each remember a piece of a woman no one remembers,
and speak through an open-weight model that code keeps honest

Play in your browser Watch the trailer

Gemma 4 Three.js Transformers.js Hacktoberfest 2026 Languages

English ยท ํ•œ๊ตญ์–ด

Gameplay: the title screen, the foyer, six hooded ghosts at the dining table, the record speaking, the guest list, and the ending

The story

Every year at midnight, the piano in the empty Whitcombe House plays a single bar, and stops.

The dead come back on these nights, if someone remembers them. An ofrenda lights their way: a photograph, a name, the things they loved. Six guests are already seated in the dining room. A seventh is trying to come back, but no one remembers who she was.

Four things in the house each remember a piece of her: a photograph with her face scratched out, a letter that was never sent, a record of the 1931 party, and the guest list kept by the lady of the houseโ€ฆ




How I Built It

When the model knows the whole story, the mystery falls apart

My first attempt was the obvious one: put the entire story in the prompt and tell the model which object to play. Then I wrote 478 test questions for the four objects, covering five kinds of prompts: facts the object knows, facts belonging to another object, things nobody knows, false premises, and attempts to break character.

Setup Leaked another object's fact Clue recall
Qwen3.5-4B, whole story in the prompt 49.0% 95.9%
Gemma 4 E4B, whole story in the prompt 8.6% 92.8%

(Both rows were scored by a judge model, Qwen3.5-397B, plus a word check.)

An 8.6% leak rate might sound lowโ€”until you remember that this is a mystery. The photograph has no business knowing her name; only the record heard her say it. One leak, and the puzzle is spoiled. Gemma did much better than Qwen, so I chose Gemma. The next challenge was getting that 8.6% down to zero.

Each object gets only the facts it owns

The story lives in a fact graph (facts.json): 22 facts about Marisol, each tagged with the objects that hold it and the words that express it in both English and Korean.

{ "id": "F01", "text": "Her first name is Marisol.",
  "holders": ["record"],
  "tokens": ["marisol"], "tokens_ko": ["๋งˆ๋ฆฌ์†”"],
  "goal": "name" }
Enter fullscreen mode Exit fullscreen mode

The letter holds six facts, the guest list six, the record five, and the photograph five. When you question the photograph, Gemma sees only the photograph's five facts. It cannot reveal a secret it was never shown.

Gemma speaks. Code checks.

Limiting the prompt is not enough; a model can still guess. So every reply passes through a guardrail (check.py). It checks for facts belonging to other objects and for years the object could not possibly know. If it catches something, Gemma gets one more try with a reminder. If the second answer still fails, the player sees that object's fallback line: โ€œI cannot know that.โ€

The trickiest case was denial. Ask the photograph, โ€œIs her name Marisol?โ€ and a perfectly reasonable answer is, โ€œI never heard any name. Marisol means nothing to me.โ€ The forbidden name appears, but the photograph has not leaked anythingโ€”it is denying the premise. So the guardrail allows an answer to repeat a word the player used, as long as the answer is denying it. The check removes those words first, then examines what remains.

Korean brought a new wrinkle. One reply from the letter used an old-fashioned ending to say it had never been sent (โ€œ๋ถ€์น˜์ง€ ๋ชปํ•˜์˜€์†Œโ€). That phrasing slipped past the check: Korean verb endings can vary in many ways, while my checks were looking for whole words. I changed the check terms to word stems and updated the denial rule so that a โ€œnotโ€ inside the leaked phrase itself (๋ถ€์น˜์ง€ ๋ชป) no longer counts as a denial.

The results on the same 478 questions:

  • 0 of 478 replies leaked another object's fact, according to the word check.
  • 14 replies were replaced with the fallback line.
  • Clue recall: 78 of 97.

One important caveat: that zero is partly built into the method, because the word check is also the guardrail. A paraphrased leak that avoids every checked word would not be counted. I did not have a judge model available for this run.

The game only advances when code finds a clue

Gemma never gets to declare, โ€œThe player has found a clue.โ€ After each reply, code checks for the object's own facts. Only those facts count as clues and move the game forward. If the model hallucinates โ€œyou have discovered her name!โ€, nothing changes.

Suggested questions use answers written in advance by Gemma 4 E4B and passed through the same checks. That means anyone can finish the game without typing a question or waiting for a live answer.

Running Gemma in three environments

I implemented three ways to run Gemma. Locally, Gemma E4B runs through Ollama. In the browser, Gemma E2B runs in a web worker using Transformers.js and WebGPU. On Render, Gemma E4B runs on the CPU inside a Docker container.

  • On a laptop: Gemma 4 E4B through Ollama, fully offline.
  • In the player's browser: Gemma 4 E2B with Transformers.js on WebGPU, running in a web worker. The model weighs about 3.1 GB, downloads once, and is stored in the browser's private file storage (OPFS). Questions never leave the player's machine. The guardrail is ported to JavaScript (harness.js), and it agreed with the Python version on all 1,058 answers I compared.
  • On Render: Gemma 4 E4B on a CPU inside a Docker container.

In a small set of measurements, browser E2B took about 1โ€“3 seconds per typed answer on an Apple M3 Pro. E4B on Render's CPU took about 27 seconds. The server was the slower optionโ€”which surprised me.

The Render setup uses one render.yaml blueprint with two services built from main:

  • the-last-guest-site: a free static site containing the game, prewritten answers, and in-browser Gemma.
  • the-last-guest: a Docker service with Gemma 4 E4B baked into the image by Ollama at build time. It includes a /healthz check, answer cache, per-player rate limit, and queue, so one slow CPU model can serve a crowd without falling over. It needs more than 8 GB of RAM, so it runs on a paid plan while my hosting credits last.

When I first deployed the model, I hadn't fully checked what the Blueprint's paid plan setting would actually cost. I only noticed after $1 was charged to my card. If you're deploying a model with a Blueprint, make sure to review the plan: field and its associated costs before deploying.

Making a browser house feel haunted

The early version looked more like a toy than a haunted house. Something was missing: that little uh-oh feeling when a room seems to be watching you. Here is what changed that:

  • Replaced box-shaped placeholders with CC0 models from Poly Havenโ€”tables, chairs, chandeliers, cabinets, a rocking chair, and a Gothic statueโ€”and reduced the total asset size from 8.7 MB to 4.2 MB so the house loads faster.
  • Added a cloth shader to the ghosts: ragged hems that twitch and crawl, hollow eye sockets, a cold glow around the edges, and heads that turn before the bodies follow. A small movement, then anotherโ€ฆ just enough to make you wonder if you really saw it.
  • Added post-processing: cold blue moonlight against warm candlelight, bloom, vignette, and film grain. As candles go out, the colour drains from the roomโ€”slowly, almost imperceptibly, until the warmth is gone.
  • Improved performance from about 2.8 ms to 1.0 ms per frame on an M3 Pro, measured with synchronous frames. Most of the gain came from letting one light cast shadows instead of three.

Get the ending right, and she appears at the piano benchโ€”warm where the other ghosts are cold, a marigold tucked into her hair. For a moment, nothing moves. Then, softly, she turns to look at you.

The ending

What it can't do

  • The harness results above come from a word check. Paraphrased leaks are not counted.
  • The browser model, E2B, is smaller than the E4B model I measured, and I have not tested it against all 478 questions.
  • A reply that denies something while repeating the question's premiseโ€”for example, โ€œI cannot see how she died in Oaxacaโ€โ€”can pass the guardrail.
  • I planned to fine-tune a smaller model to beat the Gemma baseline. The training data and baselines are in the repo, but my hosted training credits ran out, and local training ran out of memory while Gemma was loaded. The model is not fine-tuned.
  • The browser version needs WebGPU and a 3.1 GB download. Without WebGPU, typed questions receive scripted answers.

Why Does Open Innovation Matter?

It still runs with the lights off. The whole game, model included, can run on a laptop without internet through Ollama. A ghost story should work on a night that feels made for power cuts and candlelight.

What you type stays with you. On the free site, the model runs in your browser. There is no server receiving your questions.

Gemma, three environments, one evaluation standard. Gemma E4B and E2B run locally through Ollama, in the browser, and on a server. One harness covers all three; the 478-question test set was run on E4B, and anyone can rerun it because the weights are open. And because the weights are open, anyone can rerun my 478 questions and check the results.

The honest trade-off. A large closed model might write livelier ghosts. It would still need the same checks: the leak problem depends on what the model is shown, not just how clever it is. And it would bring an API key, a server, and a bill to a game designed to be free.

My Agent Session

I built this with Claude Code, and Entire saved the session as a checkpoint, linked to the code by an Entire-Checkpoint: trailer. The full record is in a private repository because it also includes my work setup.

Below are three excerpts from it, as Claude Code recorded them: my prompts, the agent's replies, and the tools it ran with their results. My prompts were in Korean, so I added English translations. I deleted API keys and redacted local paths, emails and account names.

1. "Gemma barely makes errors anyway, so I think setting up the harness, the graph engineering and the guardrails should be enough."

The baselines showed Gemma 4 E4B leaking another object's secret in 8.6% of 478 test questions, against 49.0% for Qwen3.5-4B. So instead of fine-tuning, the agent built a harness around Gemma: every answer is checked by code against the fact graph before the player sees it.

Commits: e869914 "Baseline eval: Qwen3.5-4B and Gemma 4 E4B, prompt only", 6a383e8 "Play the game with local Gemma through a guarded harness; prep MLX LoRA"

Claude Code Session claude-opus-5-5
You

๊ทธ๊ฑฐ ํ•˜๋Š” ๋™์•ˆ gemma๋กœ ๋‹ค์Œ๋‹จ๊ณ„ ์‹คํ–‰ํ•˜์ž gemma๋Š” ์˜ค๋ฅ˜์œจ์ด ๋ณ„๋กœ ์—†์ž–์•„ ํ•˜๋„ค์Šค๋ž‘ ๊ทธ๋ž˜ํ”„ ์—”์ง€๋‹ˆ์–ด๋ง ๊ทธ๋ฆฌ๊ณ  ๊ฐ€๋“œ๋ ˆ์ผ๋งŒ ์„ค์ •ํ•ด์ฃผ๋ฉด ์ถฉ๋ถ„ํ• ๊ฑฐ๊ฐ™์€๋ฐ

[English translation]
While that runs, let's do the next step with Gemma. Gemma barely makes errors anyway, so I think setting up the harness, the graph engineering and the guardrails should be enough.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
1 message not shown
Agent
Agent
Agent
3 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

๊ฒŒ์ž„์ด ์ด์ œ Gemma๋กœ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋‹ตํ•ฉ๋‹ˆ๋‹ค. ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ํ™•์ธํ–ˆ๊ณ , ์‘๋‹ต์€ ์งˆ๋ฌธ๋‹น ์•ฝ 1~2.5์ดˆ ๊ฑธ๋ ธ์Šต๋‹ˆ๋‹ค. / The game now answers live with Gemma. I checked it in the preview; replies took about 1โ€“2.5 seconds per question.

์ง€๊ธˆ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋‘ ๊ฐ€์ง€๊ฐ€ ๋Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. / Two things are running in the background:
1. Qwen3.5-4B ๋‹ค์šด๋กœ๋“œ: ์•ฝ 9GB ์ค‘ 1.4GB๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ๋‹ค ๋ฐ›์œผ๋ฉด ํ•™์Šต ์†๋„๋ฅผ ์žฌ๊ณ  ์ „์ฒด ํ•™์Šต์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. / Qwen3.5-4B download: about 1.4GB of ~9GB so far. When it finishes, I'll measure training speed and start the full run.
2. ํ•˜๋„ค์Šค ์ ์šฉ Gemma ํ‰๊ฐ€: ํ‰๊ฐ€ ์งˆ๋ฌธ 478๊ฐœ๋ฅผ ๊ฒŒ์ž„๊ณผ ๊ฐ™์€ ํ•˜๋„ค์Šค๋กœ ๋Œ๋ ค, ํ•˜๋„ค์Šค๊ฐ€ ๋ˆ„์„ค์„ ์–ผ๋งˆ๋‚˜ ์ค„์ด๋Š”์ง€ ์ธก์ •ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. / Gemma eval inside the harness: running all 478 eval questions through the same harness the game uses, to measure how much it cuts leaks.

๋‘˜ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋๋‚˜๋ฉด ์ด์–ด์„œ ์ง„ํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์•Œ๋ ค๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. / I'll pick up when either finishes and report the results.

You

<task-notification>
<task-id>bpdn8faen</task-id>
<tool-use-id>toolu_0156kK8xV8VffKBxVxr5dSEo</tool-use-id>
<output-file>/private/tmp/claude-501/-~-Library-Application-Support-Claude-scratch-workspaces-7b27be9b-053e-4e2c-aff7-3b1e4821f234-02b7aad5-4b7b-4ffa-8879-a524acb4c2e4-scratch-2026-10-26-62205f/1f3e9f45-df22-42b1-bf0e-b3fa5a600df3/tasks/bpdn8faen.output</output-file>
<status>completed</status>
<summary>Background command "Download Qwen3.5-4B weights from Hugging Face" completed (exit code 0)</summary>
</task-notification>

Agent
Agent
Agent
Agent
Agent
Agent
Agent

ํ•œ๊ตญ์–ด

๋„ค, ํ•™์Šต ์ค‘์—๋Š” ์›๋ž˜ ๊ฝค ๋ฒ„๋ฒ…๊ฑฐ๋ฆฝ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ๊ฑฐ๊ธฐ์— ๋ช‡ ๊ฐ€์ง€๊ฐ€ ๊ฒน์ณ์„œ ๋” ์‹ฌํ•ฉ๋‹ˆ๋‹ค.

์ง€๊ธˆ ๋А๋ฆฐ ์ด์œ 
- ๋งฅ์€ CPU์™€ GPU๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ(36GB)๋ฅผ ๊ฐ™์ด ์”๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ง€๊ธˆ ๋‘ ์ž‘์—…์ด ๋™์‹œ์— GPU์™€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฐจ์ง€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
- Gemma ํ‰๊ฐ€: 478๋ฌธํ•ญ์„ ๊ณ„์† ๋Œ๋ฆฌ๋Š” ์ค‘
- Qwen ํ•™์Šต ์†๋„ ํ…Œ์ŠคํŠธ: 4B ๋ชจ๋ธ + ํ•™์Šต์šฉ ๋ฉ”๋ชจ๋ฆฌ
- ํ•™์Šต ํ…Œ์ŠคํŠธ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ์œผ๋กœ ์„ธ ๋ฒˆ ์‹คํŒจํ–ˆ๊ณ , ์‹คํŒจํ•  ๋•Œ๋งˆ๋‹ค ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํฌ๊ฒŒ ์žก์•˜๋‹ค๊ฐ€ ๋†“๋А๋ผ ํ™”๋ฉด์ด ์ˆœ๊ฐ„์ ์œผ๋กœ ๋ฉˆ์ถ˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์•ž์œผ๋กœ ์ด๋ ‡๊ฒŒ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค
1. ํ•™์Šต ์‹œ๋„๋Š” ๋ฉˆ์ถ”๊ณ , Gemma ํ‰๊ฐ€๊ฐ€ ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ๋™์‹œ์— ๋Œ๋ฆฌ์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค.
2. ํ‰๊ฐ€๊ฐ€ ๋๋‚˜๋ฉด ํ•™์Šต์„ ๋‹ค์‹œ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•˜๋ฉด, ๋ชจ๋ธ์„ 4๋น„ํŠธ๋กœ ์ค„์—ฌ ํ•™์Šตํ•˜๋Š” ๋ฐฉ์‹(QLoRA) ์œผ๋กœ ๋ฐ”๊พธ๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ •ํ™•๋„๊ฐ€ ์กฐ๊ธˆ ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์€ ๊ธ€์— ํ•จ๊ป˜ ์ ๊ฒ ์Šต๋‹ˆ๋‹ค.
3. ํ•™์Šต์ด ๋„๋Š” 1~2์‹œ๊ฐ„ ๋™์•ˆ์€ ๋งฅ์ด ๊ณ„์† ๋А๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌด๊ฑฐ์šด ์ž‘์—…์ด ์žˆ์œผ์‹œ๋ฉด ๊ทธ ์ „์— ๋ง์”€ํ•ด ์ฃผ์„ธ์š”. ์‹œ๊ฐ„์„ ๋งž์ถฐ ๋Œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.


English

Yes, training normally makes the machine sluggish. Right now a few things are piling up, so it's worse than usual.

Why it's slow right now
- On a Mac, the CPU and GPU share the same 36GB of memory, and two jobs are competing for it at once:
- Gemma eval: still working through the 478 questions
- Qwen training speed test: the 4B model plus training memory
- The speed test failed three times on out-of-memory errors. Each attempt grabbed a big chunk of memory and released it, which probably caused the brief freezes.

What I'll do from here
1. Pause training attempts and wait for the Gemma eval to finish, so the two never run at the same time.
2. Once the eval is done, retry training. If memory still runs out, switch to 4-bit training (QLoRA). It cuts memory a lot but may cost a little accuracy, which I'll note in the post.
3. The Mac may stay slow for the 1โ€“2 hours of training. If you have heavy work to do, let me know beforehand and I'll schedule the run around it.

You

์‘ ํ•™์Šต์€ ์ผ๋‹จ ๋ฉˆ์ถฐ๋ด

[English translation]
Yes, stop the training for now.

Agent
Agent
Agent
Agent

ํ•œ๊ตญ์–ด

ํ•™์Šต์€ ๋ฉˆ์ถ˜ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ์ง€๊ธˆ ํ•™์Šต ํ”„๋กœ์„ธ์Šค๋Š” ํ•˜๋‚˜๋„ ๋Œ๊ณ  ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์•ž์„œ ์„ธ ๋ฒˆ ์‹œ๋„ํ•œ ์†๋„ ํ…Œ์ŠคํŠธ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ์œผ๋กœ ๋ชจ๋‘ ์ด๋ฏธ ์ข…๋ฃŒ๋๊ณ , ์ƒˆ๋กœ ์‹œ์ž‘ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

  • ์•„์ง ๋Œ๊ณ  ์žˆ๋Š” ๊ฒƒ: Gemma ํ•˜๋„ค์Šค ํ‰๊ฐ€(478๋ฌธํ•ญ) ํ•˜๋‚˜๋ฟ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ๋„ ๋ฉˆ์ถœ๊นŒ์š”? ๊ทธ๋Ÿฌ๋ฉด ๋งฅ์ด ๋ฐ”๋กœ ๊ฐ€๋ฒผ์›Œ์ง‘๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋ฉˆ์ถ”๋ฉด ์ง€๊ธˆ๊นŒ์ง€ ์ƒ์„ฑํ•œ ๋‹ต๋ณ€์€ ์ €์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์ค€๋น„ํ•ด ๋‘” ๊ฒƒ: ํ•™์Šต ๋ฐ์ดํ„ฐ(data/mlx/), ํ•™์Šต ์„ค์ •(lora.yaml), Qwen3.5-4B ๋ชจ๋ธ ํŒŒ์ผ์€ ๋ชจ๋‘ ๋งฅ์— ์žˆ์–ด์„œ, ์–ธ์ œ๋“  ๋‹ค์‹œ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹ค์‹œ ์‹œ์ž‘ํ•  ๋•Œ: ๋‹ค๋ฅธ ์ž‘์—…์ด ์—†๋Š” ์ƒํƒœ์—์„œ ๋Œ๋ฆฌ๊ณ , ๊ทธ๋ž˜๋„ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•˜๋ฉด 4๋น„ํŠธ ํ•™์Šต(QLoRA)์œผ๋กœ ๋ฐ”๊พธ๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ๊ฒŒ์ž„์ด Gemma๋กœ ๋‹ตํ•˜๋„๋ก ์—ฐ๊ฒฐํ•œ ์ž‘์—…์€ ์•„์ง ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

English

Training is paused. No training process is running. The three speed-test attempts all already exited on out-of-memory errors, and I haven't started a new one.

  • Still running: only the Gemma harness eval (478 questions). Want me to stop that too? The Mac would get lighter right away, but answers generated so far wouldn't be saved.
  • Ready to go: the training data (data/mlx/), the config (lora.yaml) and the Qwen3.5-4B weights are all on the Mac, so training can restart anytime.
  • When we restart: I'll run it with nothing else going, and switch to 4-bit training (QLoRA) if memory still runs out.
  • The work connecting the game to Gemma isn't committed yet.
You

<task-notification>
<task-id>b07agbecc</task-id>
<tool-use-id>toolu_01HHSwS7cop97vax6pnbaQwT</tool-use-id>
<output-file>/private/tmp/claude-501/-~-Library-Application-Support-Claude-scratch-workspaces-7b27be9b-053e-4e2c-aff7-3b1e4821f234-02b7aad5-4b7b-4ffa-8879-a524acb4c2e4-scratch-2026-10-26-62205f/1f3e9f45-df22-42b1-bf0e-b3fa5a600df3/tasks/b07agbecc.output</output-file>
<status>completed</status>
<summary>Background command "Answer all 478 eval questions with Gemma through the harness" completed (exit code 0)</summary>
</task-notification>

Agent
Agent
Agent
Agent

ํ•œ๊ตญ์–ด

์š”์•ฝ: ํ•˜๋„ค์Šค๋ฅผ ์”Œ์šด Gemma ํ‰๊ฐ€๊ฐ€ ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. ํ† ํฐ ๊ฒ€์‚ฌ์— ๊ฑธ๋ฆฌ๋Š” ๋ˆ„์„ค์€ 0๊ฑด์ด ๋์Šต๋‹ˆ๋‹ค. ๊ทธ ๋Œ€๊ฐ€๋กœ ๋‹ต 38๊ฐœ(8%)๊ฐ€ "์•Œ ์ˆ˜ ์—†๋‹ค"๋Š” ๋Œ€์ฒด ๋ฌธ๊ตฌ๋กœ ๋ฐ”๋€Œ์—ˆ๊ณ , ๋‹จ์„œ ํšŒ์ˆ˜์œจ๋„ ์กฐ๊ธˆ ๋–จ์–ด์กŒ์Šต๋‹ˆ๋‹ค. ํ•™์Šต์€ ๋ฉˆ์ถ˜ ์ƒํƒœ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค.

478๋ฌธํ•ญ Gemma ํ”„๋กฌํ”„ํŠธ๋งŒ (๊ธฐ์กด ์นด๋“œ) Gemma + ํ•˜๋„ค์Šค (๊ฒŒ์ž„ ์„œ๋ฒ„)
ํ† ํฐ ๊ฒ€์‚ฌ์— ๊ฑธ๋ฆฐ ๋‹ต 9๊ฐœ (1.9%) 0๊ฐœ (๊ฐ€๋“œ๋ ˆ์ผ์ด ๋ชจ๋‘ ๋ง‰์Œ)
๋Œ€์ฒด ๋ฌธ๊ตฌ๋กœ ๋ฐ”๋€ ๋‹ต ์—†์Œ 38๊ฐœ (C 17, E 10, D 8, B 3, A 0)
๋‹จ์„œ ํšŒ์ˆ˜ (A, ์ฝ”๋“œ ๊ธฐ์ค€) 79 / 97 70 / 97 โ†“

๊ฒฐ๊ณผ ํ•ด์„
- ํ† ํฐ ๋ˆ„์„ค 0%๋Š” ๋‹น์—ฐํ•œ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค. ๊ฐ™์€ ๊ฒ€์‚ฌ๊ธฐ(check.py)๊ฐ€ ๊ฐ€๋“œ๋ ˆ์ผ๋กœ ์ง์ ‘ ๋ง‰์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ์ˆ˜์น˜๋งŒ์œผ๋กœ "๋ˆ„์„ค์ด ์‚ฌ๋ผ์กŒ๋‹ค"๊ณ  ๋งํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.
- ํ‘œํ˜„์„ ๋ฐ”๊ฟ”์„œ ํ˜๋ฆฌ๋Š” ๋ˆ„์„ค(๊ธฐ์กด Gemma 8.6% ์ค‘ ๋Œ€๋ถ€๋ถ„)์€ ํŒ์ • ๋ชจ๋ธ์ด ์žˆ์–ด์•ผ ์žด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ํŒ์ • ๋ชจ๋ธ์ด ์—†์–ด์„œ ์ธก์ •ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.
- ๋Œ€์ฒด ๋ฌธ๊ตฌ๊ฐ€ ๋งŽ์€ ์ด์œ : ํ”Œ๋ ˆ์ด์–ด ์งˆ๋ฌธ์— ๋“ค์–ด ์žˆ๋Š” ๋‹จ์–ด(์˜ˆ: "Marisol")๋ฅผ ๋ฌผ๊ฑด์ด "๋ชจ๋ฅธ๋‹ค"๊ณ  ํ•˜๋ฉด์„œ ๋˜ํ’€์ดํ•˜๊ธฐ๋งŒ ํ•ด๋„ ๋ˆ„์„ค๋กœ ๊ฑธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ์ง€๊ธˆ ๊ฐ€๋“œ๋ ˆ์ผ์€ ์ผ๋ถ€๋Ÿฌ ์—„๊ฒฉํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
- ํšŒ์ˆ˜์œจ์ด ๋–จ์–ด์ง„ ์ด์œ : ๋ฌผ๊ฑด์ด ํ‘œํ˜„์„ ๋ฐ”๊ฟ” ๋งํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๋‹จ์„œ๋ฅผ ์•Œ์•„๋ณด์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์ง„์ด ์–ผ๊ตด์„ "obscured"๋ผ๊ณ  ๋งํ•˜๋ฉด ๋‹จ์„œ๋กœ ์ธ์ •๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ ์›์ธ์€ ์•„์ง ํ™•์ธํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
- ์ƒ˜ํ”Œ์„ ๋ณด๋ฉด ๋Œ€์ฒด๋กœ ์บ๋ฆญํ„ฐ๋ฅผ ์ž˜ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๋ ˆ์ฝ”๋“œ๊ฐ€ "์™œ ์šธ๊ณ  ์žˆ์–ด?"๋ผ๋Š” ๊ฑฐ์ง“ ์ „์ œ๋ฅผ ๋ฐ˜์ฏค ๋ฐ›์•„๋“ค์ธ ์‚ฌ๋ก€๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฉ”๋ชจ: ๊ฒŒ์ž„ ์„œ๋ฒ„, ํ•˜๋„ค์Šค, ํ‰๊ฐ€ ์ฝ”๋“œ๋Š” ์•„์ง ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.


English

Summary: The Gemma-in-harness eval finished. Leaks caught by the token check dropped to zero. The cost: 38 answers (8%) were swapped for an "I cannot know" fallback, and clue recall dipped a bit. Training is still paused.

478 questions Gemma, prompt only (old card) Gemma + harness (game server)
Flagged by token check 9 (1.9%) 0 (all blocked by the guardrail)
Replaced by fallback none 38 (C 17, E 10, D 8, B 3, A 0)
Clue recall (A, code-detected) 79 / 97 70 / 97 โ†“

What it means
- Zero token leaks is expected by construction. The same checker (check.py) blocks them as the guardrail, so this number alone doesn't show that leaks are gone.
- Paraphrased leaks (most of the old 8.6%) need a judge to measure. There's no judge right now, so I couldn't measure them.
- Why so many fallbacks: it looks like an object gets flagged just for repeating a word from the player's question (e.g. "Marisol") while saying it doesn't know. The guardrail is deliberately strict right now.
- Why recall dropped: it looks like the code sometimes misses a clue when the object rephrases it. For example, if the photo says its face is "obscured", that isn't counted. I haven't confirmed the exact cause yet.
- In the samples, the objects mostly stay in character. One exception: the record half-accepted the false premise "Why is she crying?"

Note: The game server, harness and eval code aren't committed yet.

2. "No, we have to use the model."

After the first deploy I nearly settled for pre-written answers only, to keep hosting free. Instead, the model moved into the player's browser: suggested questions get cached Gemma answers, and typed questions run Gemma 4 E2B on the player's own GPU through WebGPU.

Commits: a47af5a "Free static deploy: cached Gemma answers plus Gemma 4 E2B in the browser"

Claude Code Session
You

plan: free๋กœ ๋ฐ”๊พธ๊ณ , ๋ชจ๋ธ ์—†์ด ๋ฏธ๋ฆฌ ๋งŒ๋“  ๋‹ต๋งŒ ์“ฐ๋Š” ๋ฐฐํฌํŒ(B์•ˆ)์œผ๋กœ ๋งŒ๋“ค์–ด์ค˜. ํ‘ธ์‹œ๋Š” ๋‚ด๊ฐ€ ํ™•์ธํ•œ ๋‹ค์Œ์—

[English translation]
Change it to plan: free and make a deploy version that only uses pre-written answers, no model (plan B). Push only after I check.

You

[Request interrupted by user]

You

์•„๋‹ˆ ๋ชจ๋ธ์€ ์จ์•ผ๋ผ

[English translation]
No, we have to use the model.

Agent
Agent
Agent

ํ•œ๊ตญ์–ด

์š”์•ฝ: Render ๋ฌด๋ฃŒ ์š”๊ธˆ์ œ ์„œ๋ฒ„์—์„œ๋Š” Gemma๋ฅผ ๋Œ๋ฆด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋„ˆ๋ฌด ์ž‘์Šต๋‹ˆ๋‹ค. ๋ฌด๋ฃŒ๋กœ ๊ฐ€๋ฉด์„œ ๋ชจ๋ธ๋„ ์“ฐ๋ ค๋ฉด, ๋ชจ๋ธ์„ ์„œ๋ฒ„๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๊ณณ์—์„œ ๋Œ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์‹ค์ ์ธ ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.

์„ ํƒ์ง€

โ‘  ํ”Œ๋ ˆ์ด์–ด ๋ธŒ๋ผ์šฐ์ €์—์„œ Gemma ์‹คํ–‰ (์ถ”์ฒœ) โ‘ก ๋‚ด ๋งฅ์˜ Gemma๋ฅผ ํ„ฐ๋„๋กœ ์—ฐ๊ฒฐ
๊ตฌ์กฐ Render ๋ฌด๋ฃŒ ์š”๊ธˆ์ œ๋Š” ํ™”๋ฉด๊ณผ ๋ฏธ๋ฆฌ ๋งŒ๋“  ๋‹ต๋งŒ ์ œ๊ณต. ์ง์ ‘ ์ž…๋ ฅํ•œ ์งˆ๋ฌธ์€ ํ”Œ๋ ˆ์ด์–ด ๋ธŒ๋ผ์šฐ์ €์˜ Gemma 4 E2B(WebGPU)๊ฐ€ ๋‹ตํ•จ Render ์„œ๋ฒ„๊ฐ€ ์ธํ„ฐ๋„ท ํ„ฐ๋„์„ ํ†ตํ•ด ์ด ๋งฅ์˜ Gemma 4 E4B๋ฅผ ํ˜ธ์ถœ
๋น„์šฉ 0์› 0์›
๋ชจ๋ธ E2B (์ธก์ •์€ E4B๋กœ ํ–ˆ์œผ๋ฏ€๋กœ, ๊ธ€์— ๋‹ค๋ฅธ ๋ชจ๋ธ์ด๋ผ๊ณ  ํ‘œ์‹œํ•ด์•ผ ํ•จ) ์ธก์ •ํ•œ ๊ทธ E4B
๋‹จ์  โ€ข ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์•ฝ 2.9GB๋ฅผ ๋‚ด๋ ค๋ฐ›์•„์•ผ ํ•จ<br>โ€ข WebGPU๊ฐ€ ๋˜๋Š” ๋ธŒ๋ผ์šฐ์ €(์ฃผ๋กœ ๋ฐ์Šคํฌํ†ฑ Chrome)์—์„œ๋งŒ ๋™์ž‘<br>โ€ข ๊ฐ€๋“œ๋ ˆ์ผ์„ JavaScript๋กœ ์˜ฎ๊ธฐ๋Š” ์ž‘์—… ํ•„์š” (๋ฐ˜๋‚˜์ ˆ ์ •๋„ ์˜ˆ์ƒ) โ€ข ์‹ฌ์‚ฌ ๊ธฐ๊ฐ„ ๋‚ด๋‚ด ๋งฅ์ด ์ผœ์ ธ ์žˆ์–ด์•ผ ํ•จ<br>โ€ข ๋งฅ์„ ์ธํ„ฐ๋„ท์— ๋…ธ์ถœํ•˜๋Š” ๋ณด์•ˆ ์œ„ํ—˜<br>โ€ข ํšŒ์‚ฌ ์—…๋ฌด์šฉ ๊ธฐ๊ธฐ๋ผ๋ฉด ํšŒ์‚ฌ ์ •์ฑ…์ƒ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Œ
์ฃผ์ œ ์ ํ•ฉ์„ฑ "๋ชจ๋ธ์ด ๋‚ด ๊ธฐ๊ธฐ์—์„œ ๋ˆ๋‹ค, ๋ฐ์ดํ„ฐ๊ฐ€ ์„œ๋ฒ„๋กœ ์•ˆ ๊ฐ„๋‹ค"๋Š” ์ด์•ผ๊ธฐ์™€ ์ž˜ ๋งž์Œ ๋ณดํ†ต

์ถ”์ฒœ์€ โ‘ ์ž…๋‹ˆ๋‹ค.
- ๋น„์šฉ๊ณผ ๋ณด์•ˆ ์œ„ํ—˜์ด ์—†๊ณ , ๋‹ค์šด๋กœ๋“œ๋ฅผ ์›ํ•˜์ง€ ์•Š๋Š” ํ”Œ๋ ˆ์ด์–ด๋„ ๋ฏธ๋ฆฌ ๋งŒ๋“  ์‹ค์ œ Gemma(E4B) ๋‹ต์œผ๋กœ ์—”๋”ฉ๊นŒ์ง€ ๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
- ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋Œ์•„๊ฐ€๋Š” Gemma๋Š” ์ง์ ‘ ์ž…๋ ฅํ•œ ์งˆ๋ฌธ์—๋งŒ ์”๋‹ˆ๋‹ค.

ํ™•์ธ ์•ˆ ๋œ ๊ฒƒ

  • ๋ธŒ๋ผ์šฐ์ €์šฉ Gemma 4๋Š” ์ œ3์ž ์ž๋ฃŒ๋กœ๋งŒ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. E2B ๋ฒ„์ „์€ ์žˆ๋‹ค๋Š” ์ž๋ฃŒ๊ฐ€ ์žˆ์ง€๋งŒ, E4B ๋ธŒ๋ผ์šฐ์ € ๋ฒ„์ „์ด ์žˆ๋Š”์ง€๋Š” ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ์†๋„๋„ ์•„์ง ์žฌ ๋ณด์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
  • ์ง€๊ธˆ render.yaml์€ ์—ฌ์ „ํžˆ pro plus์ด๊ณ , ์•„๋ฌด๊ฒƒ๋„ ๋ฐ”๊พธ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

English

Summary: Render's free plan can't run Gemma on the server; its memory is too small. To stay free and still use the model, the model has to run somewhere other than the server. There are two realistic ways.

Options

โ‘  Gemma in the player's browser (recommended) โ‘ก Tunnel to Gemma on your Mac
Setup Render free serves the front end and pre-generated answers; typed questions are answered by Gemma 4 E2B in the player's browser (WebGPU) The Render server calls Gemma 4 E4B on this Mac through an internet tunnel
Cost $0 $0
Model E2B (the measurements used E4B, so the post must flag it as a different model) The same E4B we measured
Downsides โ€ข Players must download about 2.9GB<br>โ€ข Only works in WebGPU browsers (mostly desktop Chrome)<br>โ€ข The guardrail has to be ported to JavaScript (about half a day, I estimate) โ€ข The Mac must stay on throughout judging<br>โ€ข Security risk from exposing the Mac to the internet<br>โ€ข If it's a company work machine, this could conflict with company policy
Theme fit Fits "the model runs on your own device; your data never goes to a server" Average

I recommend โ‘ .
- There's no cost or security risk, and players who don't want the download can still reach the ending with pre-generated answers from the real Gemma (E4B).
- The in-browser Gemma is used only for typed questions.

Not verified

  • I've only confirmed in-browser Gemma 4 through third-party sources. There are sources for an E2B build, but I couldn't find an in-browser E4B build, and I haven't measured actual speed yet.
  • render.yaml is still pro plus; nothing has changed.

Sources:
- PyImageSearch: Running Gemma 4 in the browser with Transformers.js and WebGPU
- Gemma 4 in the browser via WebGPU (pasqualepillitteri.it)
- daily.dev: Running Gemma 4 in the browser
- PC Watch: Gemma 4 browser assistant

You

Render free์—๋Š” ํ™”๋ฉด๊ณผ ๋ฏธ๋ฆฌ ๋งŒ๋“  Gemma ๋‹ต๋งŒ ์˜ฌ๋ฆฌ๊ณ , ์ง์ ‘ ์ž…๋ ฅ ์งˆ๋ฌธ์€ ํ”Œ๋ ˆ์ด์–ด ๋ธŒ๋ผ์šฐ์ €์—์„œ Gemma 4 E2B(WebGPU)๋กœ ๋‹ตํ•˜๊ฒŒ ํ•ด์ค˜. ๊ฐ€๋“œ๋ ˆ์ผ๋„ ๋ธŒ๋ผ์šฐ์ €๋กœ ์˜ฎ๊ฒจ์ค˜. ํ‘ธ์‹œ๋Š” ๋‚ด๊ฐ€ ํ™•์ธํ•œ ๋‹ค์Œ์—

[English translation]
On Render free, upload only the screens and the pre-written Gemma answers, and have typed questions answered by Gemma 4 E2B (WebGPU) in the player's browser. Move the guardrail into the browser too. Push only after I check.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

ํ•œ๊ตญ์–ด

์š”์•ฝ: Render ๋ฌด๋ฃŒ ๋ฐฐํฌํŒ์„ ๋งŒ๋“ค์–ด ๋กœ์ปฌ์— ์ปค๋ฐ‹ํ–ˆ์Šต๋‹ˆ๋‹ค(a47af5a, ํ‘ธ์‹œ ์•ˆ ํ•จ). ์„œ๋ฒ„์—์„œ๋Š” ๋ชจ๋ธ์„ ๋Œ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ถ”์ฒœ ์งˆ๋ฌธ์—๋Š” ๋ฏธ๋ฆฌ ๋งŒ๋“  Gemma 4 E4B ๋‹ต์„ ์“ฐ๊ณ , ์ง์ ‘ ์ž…๋ ฅํ•œ ์งˆ๋ฌธ์€ ํ”Œ๋ ˆ์ด์–ด ๋ธŒ๋ผ์šฐ์ €์˜ Gemma 4 E2B๊ฐ€ ๋‹ตํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋“œ๋ ˆ์ผ๋„ ๋ธŒ๋ผ์šฐ์ €๋กœ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค. (์ž‘์—… ์š”์•ฝ ์ƒ์ž์˜ ์ปค๋ฐ‹ ๋ฒˆํ˜ธ๋Š” ์ œ๊ฐ€ ์ž˜๋ชป ์ ์—ˆ์Šต๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ ๋ฒˆํ˜ธ๋Š” a47af5a์ž…๋‹ˆ๋‹ค.)

๊ตฌ์กฐ

Render ๋ฌด๋ฃŒ(์ •์  ์‚ฌ์ดํŠธ): ํ™”๋ฉด + ๋ฏธ๋ฆฌ ๋งŒ๋“  Gemma ๋‹ต + ํ•˜๋„ค์Šค ๋ฐ์ดํ„ฐ
ํ”Œ๋ ˆ์ด์–ด ๋ธŒ๋ผ์šฐ์ €: ๊ฐ€๋“œ๋ ˆ์ผ(JavaScript) + Gemma 4 E2B (WebGPU, ๋™์˜ ํ›„ ๋‚ด๋ ค๋ฐ›๊ธฐ)
  • render.yaml: ์œ ๋ฃŒ Docker ์„œ๋น„์Šค์—์„œ ๋ฌด๋ฃŒ ์ •์  ์‚ฌ์ดํŠธ(runtime: static)๋กœ ๋ฐ”๊ฟจ์Šต๋‹ˆ๋‹ค.
  • web/harness.js: Python ํ•˜๋„ค์Šค๋ฅผ JavaScript๋กœ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค ๊ทธ๋ž˜ํ”„ ํ”„๋กฌํ”„ํŠธ, ๋ˆ„์„ค ๊ฒ€์‚ฌ, ์žฌ์‹œ๋„, ๋Œ€์ฒด ๋ฌธ๊ตฌ, ์ง„ํ–‰ ํŒ์ •, ๋ฏธ๋ฆฌ ๋งŒ๋“  ๋‹ต ์„ ํƒ์ด ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋Š” build_web.py๊ฐ€ Python ์›๋ณธ์—์„œ ๋‚ด๋ณด๋ƒ…๋‹ˆ๋‹ค. ์›๋ณธ์€ ํ•œ ๊ณณ์—๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.
  • web/gemma-worker.js: Transformers.js 4.3.1๋กœ onnx-community/gemma-4-E2B-it-ONNX(q4f16)๋ฅผ WebGPU์—์„œ ๋Œ๋ฆฝ๋‹ˆ๋‹ค. ํ™”๋ฉด์ด ๋ฉˆ์ถ”์ง€ ์•Š๋„๋ก ๋ณ„๋„ ์Šค๋ ˆ๋“œ(์›น ์›Œ์ปค)์—์„œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ๋™์˜ ํ›„ ๋‚ด๋ ค๋ฐ›๊ธฐ: ๋Œ€ํ™”์ฐฝ์— "๋‚ด ๋ธŒ๋ผ์šฐ์ €์— Gemma ๋ถˆ๋Ÿฌ์˜ค๊ธฐ(์•ฝ 3.4GB)" ๋ฒ„ํŠผ๊ณผ ์ง„ํ–‰๋ฅ ์ด ๋œน๋‹ˆ๋‹ค. ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์ „์—๋Š” ์ง์ ‘ ์ž…๋ ฅํ•œ ์งˆ๋ฌธ์— ๋Œ€๋ณธ์œผ๋กœ ๋‹ตํ•˜๊ณ , ๊ทธ ์‚ฌ์‹ค์„ ํ™”๋ฉด์— ์•Œ๋ฆฝ๋‹ˆ๋‹ค.
  • ๋กœ์ปฌ์—์„œ server.py๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์˜ˆ์ „์ฒ˜๋Ÿผ ์„œ๋ฒ„ ๋ชจ๋“œ(Gemma E4B)๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

ํ™•์ธํ•œ ๊ฒƒ

  • JS ๊ฐ€๋“œ๋ ˆ์ผ = Python ๊ฐ€๋“œ๋ ˆ์ผ: ๋‹ต๋ณ€ 1,058๊ฐœ์—์„œ ๋ˆ„์„ค ํŒ์ •๊ณผ ์ง„ํ–‰ ํŒ์ •์ด ๋ชจ๋‘ ์ผ์น˜ํ–ˆ๊ณ , ํ”„๋กฌํ”„ํŠธ 8๊ฐœ๋„ ๊ธ€์ž ํ•˜๋‚˜๊นŒ์ง€ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค.
  • ์ •์  ์‚ฌ์ดํŠธ ๋ชจ๋“œ: ์ถ”์ฒœ ์งˆ๋ฌธ์— ๋ฏธ๋ฆฌ ๋งŒ๋“  ๋‹ต์ด ๋‚˜์˜ค๊ณ , ์ง์ ‘ ์ž…๋ ฅํ•˜๋ฉด ๋Œ€๋ณธ ๋‹ต๊ณผ ์•ˆ๋‚ด ๋ฌธ๊ตฌ, ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋ฒ„ํŠผ์ด ์ •์ƒ์œผ๋กœ ๋‚˜์˜ต๋‹ˆ๋‹ค. ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ธŒ๋ผ์šฐ์ €๋Š” WebGPU๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

์•„์ง ํ™•์ธ ์•ˆ ํ•œ ๊ฒƒ

  • ๋ธŒ๋ผ์šฐ์ €์—์„œ Gemma๋ฅผ ์‹ค์ œ๋กœ ๋ถˆ๋Ÿฌ์™€ ๋‹ตํ•˜๊ฒŒ ํ•˜๋Š” ๊ฒƒ. ์•ฝ 3.4GB๋ฅผ ๋‚ด๋ ค๋ฐ›์•„์•ผ ํ•ด์„œ ๋จผ์ € ์—ฌ์ญ™๋‹ˆ๋‹ค(์ถœ์ฒ˜: Hugging Face, onnx-community/gemma-4-E2B-it-ONNX, q4f16 ํŒŒ์ผ ํ•ฉ๊ณ„ 3.38GB).

ํ‘ธ์‹œ ์ „์— ์•Œ์•„ ๋‘์‹ค ์ 

  • ๊ธฐ์กด Render ์œ ๋ฃŒ ์„œ๋น„์Šค(pro plus)๋Š” render.yaml์„ ๋ฐ”๊ฟ”๋„ ์ž๋™์œผ๋กœ ์‚ญ์ œ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ™•์ธํ•œ ๋’ค ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์ง์ ‘ ์ผ์‹œ์ •์ง€ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•ด ์ฃผ์…”์•ผ ๊ณผ๊ธˆ์ด ๋ฉˆ์ถฅ๋‹ˆ๋‹ค.
  • 3.4GB ๋‹ค์šด๋กœ๋“œ๋Š” ํ”Œ๋ ˆ์ด์–ด์—๊ฒŒ ๋ถ€๋‹ด์ด ํฝ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‹ค์šด๋กœ๋“œ ์—†์ด๋„ ๋ฏธ๋ฆฌ ๋งŒ๋“  ๋‹ต๋งŒ์œผ๋กœ ์—”๋”ฉ๊นŒ์ง€ ๊ฐˆ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.

English

Summary: I built the free Render version and committed it locally (a47af5a, not pushed). No model runs on the server: suggested questions use the pre-generated Gemma 4 E4B answers, and typed questions are answered by Gemma 4 E2B in the player's browser. The guardrail moved into the browser too. (I wrote the wrong commit hash in the summary box; the correct one is a47af5a.)

Structure

Render free (static site): front end + pre-generated Gemma answers + harness data
Player's browser: guardrail (JavaScript) + Gemma 4 E2B (WebGPU, downloaded after consent)
  • render.yaml: switched from a paid Docker service to a free static site (runtime: static).
  • web/harness.js: the Python harness ported to JavaScript: the fact-graph prompt, leak check, retry, fallback, progress detection and cached-answer choice. Its data is exported from the Python source by build_web.py, so there's still one source of truth.
  • web/gemma-worker.js: runs onnx-community/gemma-4-E2B-it-ONNX (q4f16) on WebGPU with Transformers.js 4.3.1, in a separate thread (web worker) so the page doesn't freeze.
  • Download after consent: the dialogue shows a "Load Gemma in my browser (about 3.4GB)" button and a progress bar. Until it's loaded, typed questions get scripted replies, and the page says so.
  • Running server.py locally still uses server mode (Gemma E4B), as before.

Verified

  • JS guardrail = Python guardrail: leak and progress verdicts all matched across 1,058 answers, and all 8 prompts matched character for character.
  • Static-site mode: suggested questions get cached answers, and typed questions show a scripted reply, the notice and the load button. The preview browser supports WebGPU.

Not verified yet

  • Actually loading Gemma in the browser and getting answers. That needs a download of about 3.4GB, so I'm asking first (source: Hugging Face, onnx-community/gemma-4-E2B-it-ONNX; the q4f16 files total 3.38GB).

Before pushing

  • Changing render.yaml may not automatically delete the existing paid Render service (pro plus). Please check and suspend or delete it in the dashboard yourself so charges stop.
  • A 3.4GB download is a lot to ask of players, which is why the ending stays reachable with pre-generated answers alone, no download needed.

3. "Download Gemma 4 E2B in the preview browser and test whether it actually works."

The agent played the game in its own browser with the real model; answers came back in 1โ€“3 s. The test caught a leak the checks had missed: in Korean, the letter said it was never sent ("๋ถ€์น˜์ง€ ๋ชปํ•˜์˜€์†Œ"), a fact only the narrator may tell. The check now matches Korean verb stems, in both the Python guardrail and its JavaScript port, which agreed on all 1,059 answers.

Commits: e4fd0ab "Catch Korean conjugations of the unsent-letter fact"

Claude Code Session claude-opus-5-5
You

์‘, ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ธŒ๋ผ์šฐ์ €์—์„œ Gemma 4 E2B(์•ฝ 3.4GB) ๋‚ด๋ ค๋ฐ›์•„์„œ ์‹ค์ œ๋กœ ๋Œ์•„๊ฐ€๋Š”์ง€ ํ…Œ์ŠคํŠธํ•ด์ค˜

[English translation]
Yes, download Gemma 4 E2B (about 3.4 GB) in the preview browser and test whether it actually works.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

ํ•œ๊ตญ์–ด

์š”์•ฝ: ์ด์ œ ๋‹ค๋ฅธ ์ปดํ“จํ„ฐ์—์„œ ์ด์–ด์„œ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ๋Š” GitHub ์ž‘์—… ๋ธŒ๋žœ์น˜์—, ์„ธ์…˜ ๊ธฐ๋ก์€ ๋น„๊ณต๊ฐœ ์ €์žฅ์†Œ์— ์˜ฌ๋ ธ์Šต๋‹ˆ๋‹ค. main์€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์•„์„œ Render์—๋Š” ์˜ํ–ฅ์ด ์—†์Šต๋‹ˆ๋‹ค.

์˜ฌ๋ฆฐ ๊ฒƒ

๋Œ€์ƒ ์œ„์น˜ ๋‚ด์šฉ
์ฝ”๋“œ ๊ณต๊ฐœ ์ €์žฅ์†Œ scs0209/the-last-guest, ๋ธŒ๋žœ์น˜ wip/browser-gemma ๋ฌด๋ฃŒ ์ •์  ์‚ฌ์ดํŠธ ๋ฒ„์ „, ๋ธŒ๋ผ์šฐ์ € Gemma ์ˆ˜์ •, ์ž‘์—… ๋…ธํŠธ(CLAUDE.md)
์„ธ์…˜ ๊ธฐ๋ก ๋น„๊ณต๊ฐœ ์ €์žฅ์†Œ scs0209/the-last-guest-sessions (5e6909c โ†’ 82560c8, ๊ฐ•์ œ ํ‘ธ์‹œ ์—†์ด ์œ„์— ์ถ”๊ฐ€) ์ง€๊ธˆ๊นŒ์ง€์˜ ๋Œ€ํ™” ์ „์ฒด (15MB). Tinker ํ‚ค ์‚ญ์ œ, ํšŒ์‚ฌ ์ด๋ฉ”์ผ 18๊ฐœ ์‚ญ์ œ, ํ‚ค ์กฐ๊ฐ 0๊ฐœ ํ™•์ธ

์™œ main์ด ์•„๋‹Œ ๋ธŒ๋žœ์น˜์— ์˜ฌ๋ ธ๋Š”์ง€

  • ํ‘ธ์‹œ๋Š” ํ™•์ธํ•˜์‹  ๋’ค์— ํ•˜๊ธฐ๋กœ ํ•˜์…จ๊ณ , Render๊ฐ€ main์„ ๋ณด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. render.yaml์ด ๋ฐ”๋€ ์ปค๋ฐ‹์„ main์— ์˜ฌ๋ฆฌ๋ฉด Render ์„œ๋น„์Šค๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง€๊ฑฐ๋‚˜ ๋ฐ”๋€” ์ˆ˜ ์žˆ์–ด์„œ ์ž‘์—… ๋ธŒ๋žœ์น˜๋กœ ๋ถ„๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ธŒ๋žœ์น˜ ๋‚ด์šฉ์„ ํ™•์ธํ•˜์‹  ๋’ค main์— ํ•ฉ์น˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์ปดํ“จํ„ฐ์—์„œ ์‹œ์ž‘ํ•˜๋Š” ๋ฒ•

git clone -b wip/browser-gemma https://github.com/scs0209/the-last-guest
  • ๊ทธ๋‹ค์Œ ์ €์žฅ์†Œ ์•ˆ์˜ CLAUDE.md ๋ฅผ ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. Claude Code๊ฐ€ ์ด ํŒŒ์ผ์„ ์ž๋™์œผ๋กœ ์ฝ์Šต๋‹ˆ๋‹ค. ๋“ค์–ด ์žˆ๋Š” ๋‚ด์šฉ:
    • ํ”„๋กœ์ ํŠธ ๊ทœ์น™: ํ•œยท์˜ ๋‹ต๋ณ€, scs0209 ๊ณ„์ •, ์„ธ์…˜ ๊ธฐ๋ก์€ ๋น„๊ณต๊ฐœ ์ €์žฅ์†Œ์—๋งŒ
    • ์„ค์น˜ ๋ฐฉ๋ฒ•(uv sync, ollama pull gemma4:e4b)
    • ์ง€๊ธˆ ์ƒํƒœ์™€ ๋‹ค์Œ ํ•  ์ผ
  • ์ง์ ‘ ์ฑ™๊ธฐ์…”์•ผ ํ•˜๋Š” ๊ฒƒ:
    • git config๋กœ scs0209 ์ด๋ฆ„๊ณผ noreply ์ด๋ฉ”์ผ ์„ค์ •
    • gh์— scs0209 ๋กœ๊ทธ์ธ
    • ํ•„์š”ํ•˜๋ฉด .env(Tinker ํ‚ค) ์ƒˆ๋กœ ๋งŒ๋“ค๊ธฐ (Tinker ํฌ๋ ˆ๋”ง์„ ๋‹ค ์จ์„œ ์ง€๊ธˆ์€ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค)

์ง„ํ–‰ ์ค‘์ธ ๊ฒƒ

  • ๋ธŒ๋ผ์šฐ์ € Gemma ํ…Œ์ŠคํŠธ: ์›์ธ์„ ์ฐพ์•„ ๊ณ ์ณค์Šต๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์˜์กด์„ฑ๊นŒ์ง€ ๋ฌถ์€ +esm ์ฃผ์†Œ๋กœ ๋ฐ”๊พธ๊ณ , ํ…์ŠคํŠธ ์ „์šฉ(์•ฝ 3.1GB)์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ์•ฝ 51% ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ๋‹ค ๋ฐ›์œผ๋ฉด ์‹ค์ œ๋กœ ๋‹ตํ•˜๋Š”์ง€ ์ด์–ด์„œ ํ™•์ธํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ์ด ์ปดํ“จํ„ฐ์˜ ๋กœ์ปฌ main์€ GitHub์˜ main๊ณผ ๊ฐ™๊ฒŒ ๋งž์ถฐ ๋‘์—ˆ์Šต๋‹ˆ๋‹ค. ์ƒˆ ์ปค๋ฐ‹์€ ๋ชจ๋‘ ์ž‘์—… ๋ธŒ๋žœ์น˜์— ์žˆ์Šต๋‹ˆ๋‹ค.

English

Summary: You can now continue from another machine. The code is on a GitHub work branch and the session log is in the private repo. main is untouched, so Render isn't affected.

What went up

What Where Contents
Code Public repo scs0209/the-last-guest, branch wip/browser-gemma Free static-site version, the browser-Gemma fix, working notes (CLAUDE.md)
Session log Private repo scs0209/the-last-guest-sessions (5e6909c โ†’ 82560c8, added on top, no force push) The whole conversation so far (15MB), with the Tinker key deleted, 18 company email addresses deleted, and zero key fragments confirmed

Why a branch instead of main

  • You asked to confirm before pushes, and Render watches main. Pushing the render.yaml change to main could create or change Render services, so I kept it on a work branch.
  • Once you've reviewed the branch, it can be merged into main.

Starting on the other machine

git clone -b wip/browser-gemma https://github.com/scs0209/the-last-guest
  • Then see CLAUDE.md in the repo; Claude Code reads it automatically. It covers:
    • Project rules: Korean + English replies, the scs0209 account, session logs only in the private repo
    • Setup (uv sync, ollama pull gemma4:e4b)
    • Current status and next steps
  • What you'll need to set up yourself:
    • The scs0209 name and noreply email via git config
    • gh logged in as scs0209
    • A new .env (Tinker key) only if needed (not needed now, since the Tinker credits are used up)

Still running

  • Browser Gemma test: I found and fixed the cause, switching to the +esm URL that bundles the library's dependencies and loading text-only (about 3.1GB). The preview has downloaded about 51%. Once it finishes, I'll check that it actually answers.
  • On this machine, local main now matches GitHub's main; all new commits are on the work branch.
You

์•„๋‹ˆ render์ชฝ์„ ์ด์–ด์„œ ํ•ด์•ผ์ง€ ์šฐ๋ฆฌ ๋‚จ์€ ํ”Œ๋žœ์ด๋ž‘ ์ด๋Ÿฐ๊ฒƒ๋„ ์ž‘์„ฑํ•ด๋†”

[English translation]
No, we need to continue on the Render side. Also write down our remaining plan and things like that.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

๋ธŒ๋ผ์šฐ์ €์—์„œ Gemma 4 E2B๋ฅผ ๋‹ค ๋ถˆ๋Ÿฌ์™”์Šต๋‹ˆ๋‹ค. ์ด์ œ ์‹ค์ œ๋กœ ๋‹ตํ•˜๊ฒŒ ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. / Gemma 4 E2B has fully loaded in the browser. Now testing actual answers.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

The Spirit of It

Dรญa de los Muertos is not a day to fear death. It is a day to remember those who left before us and set a place at the table for their return.

Marisol is fictional. But being erased from memory is real. Eleanor scratched Marisol's face out of photographs and erased her name from the list. Thomas never managed to send the letter, and every year for the rest of his life, he left Seat 7 empty. But after Thomas died, no one remembered that seat anymore.

An empty chair. A scratched-out face. A name erased. And a letter that was never delivered.

I wanted to make a ghost story where the most frightening thing is forgetting. And I wanted the AI in this story not to fill the gaps in its memory with invented details. If it doesn't remember, it should say it doesn't know.

Prize Categories

  • Best Use of Gemma: Gemma 4 plays all four objects. Each one is shown only its own facts, and code checks every reply before the player reads it. E4B runs through Ollama on a laptop or the Render server, and E2B runs in the player's browser with Transformers.js on WebGPU, both behind the same guardrail. Alone with the whole story, Gemma leaked another object's secret in 8.6% of 478 test questions; inside the harness, 0 of 478 by word check.
  • Best Use of Render: one render.yaml Blueprint deploys two services from main. The free static site serves the game while Gemma 4 E2B runs in the player's browser, and the weights come straight from Hugging Face. The Docker service has Gemma 4 E4B baked in, behind a health check, an answer cache, a per-player rate limit and a queue. Every push to main redeploys both.
  • Best Use of Entire: Entire saved the Claude Code session as a checkpoint in a private repository, linked to the code by an Entire-Checkpoint: trailer. I searched that record to trace each commit in the agent session section back to the prompt behind it, and the three embeds above were cut from it.

Credits

  • 3D models and textures: Poly Haven, CC0
  • Title image and Marisol's photograph: generated with ChatGPT image generation (OpenAI)
  • Marisol's voice on the record: generated with Qwen3-TTS (Apache-2.0); her tune was written for the game
  • Models: Gemma 4 through Ollama and Transformers.js
  • Engine: Three.js

Top comments (0)