Buy This, Not That, Drop This There” - I Made It Make Sense
이건 사고, 저건 사지 말고, 저건 거기 두세요 — 뒤섞인 심부름 요청을 정리했습니다
Nikal은 음성이나 텍스트로 받은 가족의 심부름 요청을 AI가 항목별로 정리하고, 사람이 확인한 뒤 목적지별 카드로 만드는 앱입니다. Gemma의 추출 결과를 검증하고 취소·수량·불확실성을 별도 상태로 다뤄, AI가 만든 목록을 곧바로 실행하는 일을 막습니다.
- 주제
AI 요약
가족이 집을 나서는 사람에게 우유를 사고 빵은 사지 말며 소포는 우체국에 맡기라고 빠르게 말하면, 듣는 사람이 직접 기억하고 항목을 나눠야 합니다. Nikal은 음성 메모나 원문 메시지를 받아 심부름 초안을 만들고, 사용자가 확인한 항목만 목적지별 포켓 카드에 담습니다. AI가 목록을 만드는 것보다 취소 요청과 수량을 그대로 보이고, 모호한 정보는 모호한 상태로 남기며, 최종 승인은 사람이 한다는 점에 초점을 둡니다.
요청 입력부터 완료 기록까지
사용자는 짧은 음성 메모를 녹음하거나 원문을 붙여 넣습니다. 음성은 ElevenLabs Scribe v2가 편집 가능한 텍스트로 변환합니다. 이어 Gemma가 Backboard를 거쳐 심부름 항목, 수량, 취소 내용, 목적지 단서와 확인 질문을 제안합니다. 검토 화면에는 원래 요청도 계속 표시되며, 사용자는 항목을 수정하거나 취소하고, 미루거나 목적지를 지정합니다. 승인한 항목은 목적지별 카드로 묶이고, 외출 중에는 완료·구할 수 없음·연기·미해결 상태를 기록합니다.
Nikal은 자율 쇼핑 에이전트가 아닙니다. 물건을 구매하거나 다른 사람에게 메시지를 보내지 않고, 상점을 찾거나 GPS를 추적하지도 않습니다. 모델이 올바른 JSON을 반환했다고 해서 요청을 제대로 이해했다고 간주하지 않습니다. 빵을 사지 말라는 말을 구매 지시로 바꾸거나, 수량과 목적지를 빠뜨리거나, 모르는 상점을 확신에 차서 추측하는 일을 제품 상태와 검토 절차로 다룹니다. 구할 수 없는 항목도 완료로 표시하지 않습니다.
모델 출력은 앱 코드가 다시 확인합니다
서버는 환경 설정에 따라 모델과 제공자를 선택합니다. 기본 설정은 Backboard의 OpenRouter 제공자를 통한 google/gemma-3-27b-it이며, 모델은 서버에서 바꿀 수 있습니다. 요청은 비스트리밍으로 보내고 JSON 형식을 요청하되, 메모리와 웹 검색을 끄며 도구나 자율 동작을 허용하지 않습니다. API 키도 브라우저에 전달하지 않습니다.
모델 응답은 Zod 스키마로 검사합니다. 응답에 포함된 근거 문구가 원본 메시지에 실제로 그대로 있는지도 서버에서 확인합니다. 모델은 신뢰할 목적지 ID, 애플리케이션 ID, 시각, 완료 상태나 승인 결정을 반환하지 못합니다. 스키마 검증과 근거 일치 여부, 목적지 별칭, 포함·취소·연기 상태, 카드 생성 규칙, 로컬 저장과 결과 기록은 일반 TypeScript 코드가 담당합니다. 목적지를 확정하지 않은 항목은 준비된 카드에 들어가지 않으며, 미해결 항목은 해결하거나 명시적으로 미뤄야 합니다.
데이터 경계와 한계
음성 파일은 크기 제한을 둔 서버 경로로 전송하며 앱 데이터로 저장하지 않습니다. 사용자는 언제든 텍스트 입력으로 전환할 수 있습니다. 검토를 마친 카드는 버전이 붙은 브라우저 저장소에 보관됩니다. 외출 중 결과를 표시할 때는 모델을 다시 호출하지 않습니다. 다만 AI 추출과 음성 변환에는 호스팅 서비스를 쓰므로, 이 앱을 완전한 오프라인 AI라고 부르지는 않습니다. 작성자는 브라우저 저장소만으로 설치형 오프라인 PWA가 검증됐다고 주장하지 않으며, 정확도 수치도 제시하지 않습니다. 제공 서비스가 중단되면 성공한 AI 결과를 꾸며내지 않고 원문을 보존한 채 수동 경로를 안내합니다.
구현 구성
앱은 Next.js 16 App Router, React 19, 엄격한 TypeScript, Tailwind CSS 4로 구성합니다. 캡처·검토·카드 흐름은 app/page.tsx에 있고, 추출과 음성 업로드는 각각 app/api/extract/route.ts와 app/api/transcribe/route.ts가 처리합니다. 제공자 연결 코드는 lib/server 아래에 두고, 스키마와 결정적 카드·근거 처리는 lib/domain에 분리했습니다. 테스트에는 도메인 로직과 제공자 응답 정규화 검증이 포함됩니다.
작성자는 이전 해커톤에서 UI 완성도와 기능 목록만으로 핵심 문제가 해결됐다고 판단하기 어렵다는 점을 배웠다고 설명합니다. 그래서 하나의 흐름, 즉 혼란스러운 요청을 받아 근거가 붙은 AI 초안을 만들고, 사람이 검토하고, 포켓 카드로 준비한 뒤 실제 결과를 기록하는 데 범위를 좁혔습니다. Gemma와 제공자를 바꾸더라도 사람의 승인·근거·완료 기록은 모델 밖에 남기도록 설계했습니다. 다만 현재 공개 배포는 호스팅 서비스에 의존하므로, 오픈 모델을 쓴다는 사실만으로 추론 과정의 데이터가 비공개가 되지는 않습니다.
dev.to 반응
- @suppdevbot — 계정을 인증해야 합니다. 전체 화면 모드를 시작하거나 종료하세요. tr.ee/dev-to
- @dev_in_the_fog — 미묘한 문제를 정말 깔끔하게 설명했네요. 참고하려고 북마크했습니다. 공유해 주셔서 감사합니다.
- @taqui — 정말 감사합니다, Jason :)
- @friendz_me_12 — 잘 만들었네요, 친구 👏
- @humam_moin — 검토 중에도 원래 메시지가 계속 보이는 점이 좋습니다. 취소와 수량을 제대로 처리하는 일이 중요해요. AI가 조금만 틀려도 심부름 목록 전체가 엉망이 될 수 있으니까요.
원문: dev.to / 번역·요약: Trawling