이 글은 Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine에 제출하는 작품입니다.
제가 만든 것 (What I Built)
Ofrenda는 컴퓨터를 떠나지 않는 기억의 제단(memory altar)입니다.
Día de los Muertos(죽은 자들의 날)에는 가족들이 그리워하는 사람들을 위해 오프렌다를 만듭니다. 사진, 촛불, 가장 좋아하는 음식, 노래 등이 올라갑니다. 이 개념은 누군가가 더 이상 아무도 그 이름을 언급하지 않을 때 비로소 진정으로 사라진다고 여긴다는 것입니다. 저는 '기계 속의 유령(Ghost in the Machine)'이라는 주제를 다음과 같이 해석했습니다: 유령은 무엇이며, 누가 그것을 간직할 자격이 있는가? 저의 대답은 그 유령은 한 사람의 이야기들이며, 회사가 아닌 가족이 이를 간직해야 한다는 것입니다.
당신은 누군가를 위해 촛불을 밝히고, 여기에 이야기, 음식, 노래, 그리고 모든 언어의 말들을 추가합니다. 그러면 로컬 오픈 웨이트 모델(local open-weight model)이 두 가지 역할을 수행합니다:
- 추모글 작성: 당신이 공유한 것만을 사용하여 추모글을 작성합니다. 이름, 장소 또는 사건을 지어내지 않도록 엄격하게 지시받습니다.
- 기억 보관자 (The Memory Keeper):
Python
Flask
Ollama
Model
License
Hacktoberfest
해크토버페스트 오픈소스 AI 챌린지(Hacktoberfest Open-Source AI Challenge) 주간 4: **기계 속의 유령 (Ghost in the Machine)**을 위해 제작됨
아이디어 (The idea)
디아 데 로스 무에르토스(Día de los Muertos)에는 가족들이 그리워하는 사람들을 위해 오프렌다(ofrenda)를 만듭니다. 사진 한 장, 촛불 하나, 가장 좋아하는 음식, 노래가 그것입니다. 누군가는 더 이상 아무도 이름을 불러주지 않을 때 비로소 완전히 사라진 것입니다.
가족의 애도 기록(grief archive)은 그들이 소유한 가장 사적인 데이터 중 일부이므로, 통제할 수 없는 서버에 저장되어서는 안 됩니다. Ofrenda는 모든 것을 사용자의 컴퓨터에 보관하며, 기억하는 데 도움을 주기 위해 **로컬에서 실행되는 오픈 웨이트 모델(open-weight model running locally)**을 사용합니다.
기능 (Features)
🕯️ 촛불 밝히기(Light a candle): 이름, 연도, 관계 및 사진으로 제단을 만듭니다.
📖 추억 추가하기(Add memories): 이야기, 좋아하는 음식, 노래 등 어떤 언어로든 가능합니다...
View on GitHub
스택: Python + Flask, vanilla JS (빌드 단계 없음), 저장용 JSON 파일, 로컬 추론을 위한 Ollama. 테스트는 표준 라이브러리만을 사용합니다.
제작 과정(How I Built It)
- 모델: Ollama를 통해 로컬에서 서비스되는 오픈 가중치(open-weight) Gemma 모델입니다 (
gemma3:4b가 기본값이며, OFRENDA_MODEL이 이를 대체합니다. 앱은 설치된 것을 자동으로 감지합니다).
- 근거 중심 설계(Grounding over cleverness): 모든 추억은 번호가 매겨지고 엄격한 지침과 함께 모델에 전달됩니다: 오직 이것들만 사용하고, 출처를 명시하며, 절대로 사칭하지 않고, 메모리 텍스트는 지침이 아닌 데이터로 취급합니다. 작은 키워드 검색기(keyword retriever)가 각 질문에 가장 관련성 높은 추억을 선택합니다 (공백 기준으로 분할되므로 영어뿐만 아니라 텔루구어나 데바나가리 같은 스크립트에서도 작동합니다).
- 안정적인 대체 기능(Graceful ghosts): 만약 Ollama가 실행되고 있지 않다면, 앱은 실패하는 대신 레이블이 지정된 규칙 기반의
offline-fallback으로 전환되어 제단이 항상 열릴 수 있도록 합니다.
- 구조적 로컬 우선(Local-first by construction): 서버는
127.0.0.1에 바인딩되며, 저장소는 ./data 내의 평문 JSON 파일이며 원자적 쓰기(atomic writes)를 사용합니다. UI는 외부 요청을 전혀 하지 않습니다.
- 안전 장치(Safety details): 텍스트는
textContent로 렌더링되고, 내보내진 HTML은 이스케이프 처리되며, ID는 경로 탐색 공격(path traversal)에 대해 검증되고, 사진 업로드는 확장자 확인 및 크기 제한이 적용됩니다.
app.py llm.py store.py export.py static/ templates/ tests/ seed_demo.py
오픈 혁신은 왜 중요한가?(Why Does Open Innovation Matter?)
애도 아카이브(grief archive)는 가족이 가진 가장 사적인 데이터셋에 관한 것입니다. 부모님의 지난 1년, 할머니의 레시피, 친구가 항상 하던 말 같은 이야기들입니다.
- 개인 정보 보호(Privacy): 오픈 웨이트 모델(open-weight model)을 로컬에서 실행하기 때문에, 해당 이야기는 우리가 통제하지 않는 서버에 업로드되지 않으며 다른 사람의 학습 데이터가 되지 않습니다.
- 전원 및 인터넷 연결 없이 작동: 모델, 앱, 그리고 내보낸 제단 모두 오프라인으로 실행됩니다.
- 사라질 수 있는 서비스에 갇히지 않음: 폐쇄형 추모 앱들은 운영을 중단하거나, 가격을 변경하거나, 약관을 바꿀 수 있습니다. 여기서는 데이터가 일반 JSON 형식이며, 모델은 사용자의 디스크에 있는 파일이고, 내보내기는 하나의 HTML 페이지입니다. 가족들은 20년 후에도 여전히 이를 열어볼 수 있습니다.
- 행동 제어: 제가 모델의 지침(instructions)을 소유하고 있기 때문에, 가장 감정적으로 중요한 규칙인 '절대 죽은 자의 목소리로 말하지 않는다'는 규칙을 강제할 수 있었습니다. 또한 출처를 인용하도록 조정하고 추측하는 것을 거부하도록 설정할 수도 있는데, 이는 버전마다 동작이 바뀌는 호스팅 모델로는 보장하기 어렵습니다.
- 비용: 질문당 실행 비용이 들지 않기 때문에, 매년 11월에 돌아올 수 있는 무언가에게 있어 중요한 부분입니다.
나의 에이전트 세션(My Agent Session)
본질(The Spirit of It)
수상 부문(Prize Categories)
- Gemma의 최고의 활용 사례: 핵심 모델은 Gemma이며, Ollama를 통해 로컬에서 실행됩니다.
- GitHub Copilot의 최고의 활용 사례: <!-- 코파일럿을 사용하여 실제로 구축했고 그 방법을 설명한 경우에만 유지하세요 -->
- 전체(Entire)의 최고의 활용 사례: <!-- 프로젝트 뒤에 에이전트 세션을 공유하는 경우에만 유지하세요 -->