dev.to

FieldIssue: take a walk, report a problem, come back with evidence

FieldIssue: 산책하며 문제를 신고하고, 증거를 들고 다시 돌아오세요

FieldIssue는 현장 사진과 메모로 공공장소 문제를 신고하고, 재방문 사진을 비교해 변화 기록을 쌓는 오픈소스 웹 앱입니다. Gemma와 미세 조정한 Qwen3-8B를 분리해 활용하고, 모델 평가와 실패 사례도 공개합니다.

AI 요약

FieldIssue는 파손된 벤치나 넘친 쓰레기통 같은 현장 문제를 사진으로 신고한 뒤, 다시 방문해 찍은 사진과 비교하는 웹 앱입니다. 한 번의 신고를 단일 민원으로 끝내지 않고, 어떤 상태가 새로 생겼고 사라졌으며 그대로인지 날짜별 증거 기록으로 남깁니다. 작성자는 10월 9일 인도 러크나우(Lucknow)에서 잘린 나무와 쓰레기를 촬영해 신고했습니다. Gemma는 부러진 가지, 쌓인 마른 가지와 낙엽, 플라스틱 쓰레기 등 세 가지 상태를 정리했습니다. 약 50m 떨어진 두 번째 지점에서는 쓰러진 가지가 흙길을 막고 있다고 기록했습니다.

신고와 재방문 절차

사용자는 사진과 짧은 메모를 등록합니다. 이후 같은 장소를 같은 각도에서 다시 촬영하면 FieldIssue가 두 사진을 비교해 추가·해소·유지된 상태를 정리합니다. 두 가지 규칙으로 신고 이력을 관리합니다. Gemma는 상태를 제안하지만 신고를 해결 처리하는 주체는 사람입니다. 사람이 직접 해결한 경우에도 이력을 남깁니다. 같은 파일을 다시 올리면 바이트 단위로 확인해 “반복 사진: 새 증거 없음”이라고 표시합니다. 이 검사는 단순 재업로드를 막지만, 의도적인 조작까지 방지하지는 않는다고 설명합니다.

앱에는 주변 미해결 신고를 경로에 넣는 산책 기능, 선택적으로 계정을 연결해 저장하는 비공개 산책, 신고 배정과 회원 2명의 승인을 거쳐 해결하는 비공개 커뮤니티 기능도 있습니다. 오프라인 상태에서 촬영한 사진은 휴대전화에 대기시킨 뒤 사용자가 검토하고 업로드합니다. 재시도에는 멱등성 키를 사용해 연결이 끊겨도 같은 신고가 중복 생성되지 않도록 했습니다.

모델 구성과 평가

프런트엔드는 React, Vite, Tailwind, shadcn/ui, Leaflet으로 구성했습니다. TypeScript 기반 Hono API가 Mastra 워크플로를 실행하며, Render 컨테이너 하나에서 Node API와 비공개 FastAPI 프로세스를 운영합니다. 데이터베이스는 Render PostgreSQL과 PostGIS이고, 의미 검색에는 별도의 Tiger Data pgvector 인덱스를 씁니다. Gemma는 사진 분석, Tinker로 미세 조정한 Qwen3-8B는 신고자 메모 해석, Backboard는 두 공개 모델의 결과 비교를 맡습니다. 어떤 모델도 신고를 단독으로 종결하지 않습니다. 모델 출력은 저장 전에 스키마 검증을 거칩니다.

작성자는 비격식 힌디어와 힌글리시(Hinglish)를 포함한 합성 현장 메모 36개로 Qwen3-8B를 미세 조정했습니다. 학습에 쓰지 않은 메모 18개를 평가했을 때 기본 모델과 미세 조정 모델 모두 스키마 유효성은 18/18, 카테고리 정확도는 17/18이었습니다. 심각도 정확도는 기본 모델 13/18에서 미세 조정 모델 17/18로 높아졌습니다. 앞선 10월 7일 실험에서도 7개 중 2개 개선됐다고 보고합니다. 다만 평가 자료가 18개의 합성 메모뿐이므로 벤치마크로 볼 수 없다고 밝히고, 정답과 오답을 공개 Model Lab에서 확인하도록 했습니다. 예를 들어 “Dustbin bhara hai aur kachra bahar gira hai.”라는 메모에 기본 모델은 HIGH, 미세 조정 모델은 데이터셋 주석과 같은 MEDIUM 심각도를 반환했습니다. 반면 다른 테스트에서는 성능이 나빠진 사례도 공개했습니다.

메모 해석 기능은 사용자가 동의한 뒤 소유한 신고에만 적용합니다. 사진이나 좌표는 보내지 않으며 기존 분류를 덮어쓰거나 신고를 해결 처리하지 않습니다. TabPFN으로 재방문 우선순위를 매기는 실험도 했지만, 합성 데이터 24개에서 16/24를 기록해 다수 클래스만 예측하는 기준선 17/24보다 낮았습니다. 그래서 실험 결과는 Lab에 남기고 실제 추천에는 넣지 않았습니다.

검증과 운영상 한계

브라우저에서 저장한 산책이 실패했을 때 API 테스트는 통과했습니다. 위치 선택기가 좌표만 보내야 하는 API에 화면 표시용 메타데이터까지 보낸 문제였습니다. 작성자는 이 사례를 들어 깨끗한 요청 본문만 시험하지 말고 전체 폼을 확인해야 한다고 설명합니다. ElevenLabs API 키가 만료됐을 때는 캐시된 음성 안내가 계속 재생돼 문제가 가려졌고, 새 음성 생성 요청에서만 401 오류가 발생했습니다. 이후 캐시 응답과 신규 호출을 따로 점검하도록 했습니다. SerpApi가 주변 장소 정보를 찾지 못한 경우에도 Gemma 신고는 저장되며, Sentry는 이를 민감한 메모나 사진을 노출하지 않는 PLACE_CONTEXT_UNAVAILABLE 오류로 기록합니다.

최신 빌드는 자동 테스트 242개를 통과했습니다. CI는 API 테스트 168개와 Python 테스트 74개를 실행하고 PostGIS와 pgvector, 개발 및 컴파일된 API의 HTTP 동작, 메모리 512MiB 제한의 Render 컨테이너를 점검합니다. 운영 비밀값이 없으면 시작을 막고, FastAPI가 루프백 주소에서만 수신하는지와 Python 프로세스가 죽었을 때 컨테이너도 종료하는지 확인합니다. 계정 분리, 저장 산책, 회원 권한 철회, 음성 재생은 호스팅 환경에서도 점검했습니다.

모든 모델 평가는 합성 데이터에 기반하며, 실제 재방문으로 사람이 라벨링한 변화 이력은 다음 과제로 남았습니다. 무료 호스팅과 제공사 크레딧에 의존해 데이터베이스와 일부 API 자격 증명에는 만료 시점이 있고, 신규 추론에도 하루 60 provider units 제한이 있습니다. 프로젝트는 MIT 라이선스로 공개됐으며, 프롬프트와 출력 스키마, 평가 결과를 확인할 수 있도록 했습니다.

원문: dev.to / 번역·요약: Trawling