AI Engineer 履歴書へ
Desktop · Planning Tool· 2026

NIJIKAI

A Tokyo Settai Planner for the Person Who Books the Evening

二次会 (nijikai) is the second party of the night — the bar or the karaoke box after dinner, where the actual conversation happens. The app is named for it because that leg is the one every restaurant-finder ignores, and the one a client evening turns on.

Origin Story開発の背景

Restaurant sites answer 'where is good?'. A secretary booking a client dinner is answering a different question entirely: which room will hold six people, has a door on it, can feed a guest who does not eat pork, takes a corporate card, and is bookable eleven days from now. Nijikai answers that one, and treats a superb restaurant that fails a hard requirement as dropped rather than flagged.

How It's Builtつくり方

It shares its architecture with Sakurako, and for the same reason. A brief becomes roughly fourteen deterministic Exa queries planned in Rust, whose results go to a single toolless model instance that reads them once and writes the shortlist; a second instance turns the picked rooms into a runsheet. Searching is written the way the job is actually done — the word that matters is 個室 (private room), not 'best restaurant'; a bonenkai is searched in Japanese (忘年会) because the course-and-nomihoudai banquet plans never surface in English; and a dietary constraint gets its own query, because a general restaurant search will never turn up a halal kitchen.

Engineering Notesエンジニアリングノート

One toolless pass instead of a fan-out

The shortlisting instance gets one flat block of results and no tools at all. The earlier design fanned out to a dozen headless instances each running their own search loop, and each paid roughly 21k tokens of harness overhead before doing any work, re-sending every fetched page on every later turn. Collapsing it took a run from about $2.15 to $0.20.

Enforcing 'never invent a URL'

Every candidate URL is already in the prompt, so strip_unknown_urls deletes any booking or info link the model did not actually get from the results. The instruction is not trusted; the output is filtered against the input.

Three-state, because absence is not denial

privateRoom, englishSupport and corporateCardOk distinguish 'read that there isn't one' from 'nobody knows yet'. Collapsing the two turns unconfirmed into confirmed-absent, which is how a client party ends up seated in the middle of a dining room.

Sizing prompts for scraped text

Web text tokenises at roughly 2 characters per token, not 4. Sizing a prompt by chars/4 underestimates by 2× and trips the spend cap — one of several numbers here that only reveal themselves in production.

The interesting constraint in settai software is that the failure mode is social, not technical. A wrong price is an inconvenience; a guest seated with their back to the door, or a host settling the bill in front of them, is the actual product failing. That is why the unknowns are left visibly unknown.

What It Does主な機能

01

A Brief, Not a Search Box

Occasion, party size, guest seniority, languages in the room, dietary constraints, how much they will drink, and a 1–5 register dial. The register does more work than the budget: two steps too loose reads as a slight, two steps too tight reads as trying too hard.

02

Rooms With Their Risks on the Front

Each result carries per-head price, private-room status, seat count, booking lead time, card acceptance and last order — plus a risks list saying how it could embarrass the host. Cash only. No private room on a Friday. Fifteen minutes from the station. A venue with three honest risks listed is more useful than one described as perfect.

03

Nulls Stay Null

If a price or a phone number was not on a page, the field is empty and the gap is named. A secretary can settle an unknown with one phone call; they cannot un-ring a number the model invented.

04

The Runsheet

Pick a room per leg and it comes back timed and costed, carrying the things that are on no restaurant page: the kamiza seating plan for this party and this room, how the bill gets settled out of the guest's sight, the last trains per line — because those end the night, not the venue's closing time — whether a temiyage is expected, and what to do when the nijikai turns out to be full. Exportable as a PDF to hand to whoever is hosting.

05

Three-State Facts

privateRoom, englishSupport and corporateCardOk are three-state on purpose. false means the model read that there is not one; null means nobody knows yet. Collapsing them turns 'unconfirmed' into 'confirmed absent', which is how a party ends up seated in the middle of a dining room.

06

No Server, No Telemetry

There is no Nijikai back end. The brief goes to the model as a prompt when you run a search, that instance queries Exa and fetches pages, map tiles come from a CDN, and everything else — brief, shortlist and runsheets — stays in a single JSON file on your machine that About will show you the path to.

Where It Stands現在地

Nijikai runs end-to-end today: brief in, shortlist streamed, runsheet out as a PDF. It needs the Claude Code CLI on PATH and an Exa key set in Settings, which is why it is a local build rather than a download — a run costs around ten cents, so Exa's monthly free credit covers roughly a hundred of them.

// UNDER THE HOOD
技術構成

Tauri 2 with a Rust core. The shortlisting pass is given no tools at all: it receives one flat block of results and reads it once. That is deliberate — the earlier design fanned out to a dozen headless instances that each ran their own search loop, and each paid roughly 21k tokens of harness overhead before doing any work, re-sending every fetched page on every subsequent turn. Collapsing it to one toolless instance took a run from about $2.15 to $0.20. Model output is distrusted by construction: every candidate URL is already in the prompt, so strip_unknown_urls deletes any bookingUrl or infoUrl the model did not actually get from the results — 'never invent a URL' is enforced rather than requested. Several constraints cost real time to find and are load-bearing: Tauri runs synchronous commands on the main thread, so anything doing network or process I/O goes through an off-thread helper or the window freezes; scraped web text tokenises at roughly 2 chars/token rather than 4, so sizing a prompt by chars/4 underestimates by 2× and trips the spend cap; Exa bills per request for the first 10 results, so asking for fewer costs exactly the same and query count is the only price dial; the front end mints the job id and registers it before invoking, because minting it in Rust meant every event emitted before the IPC reply landed was addressed to an id the window did not know yet and was silently dropped; children are spawned via setsid into their own process group and killed on window-close and app-exit, with a Rust test asserting that a grandchild dies; and PDF export uses createPDFWithConfiguration rather than NSPrintOperation, because calling runOperation from inside a main-thread callback deadlocks when WebKit renders out of process.