본 게시물은 Hacktoberfest Open-Source AI Challenge, Week 2: Side Hustle 제출물입니다.
An animated screen recording of the app running. A capture screen with a photo and an asking price; four pipeline stages completing in sequence; then a verdict card reading PASS with a MAX OFFER of $20.22 and the note that the asking price sits 197% over the ceiling; and finally the same card re-priced by hand while the model stays untouched.
무엇인가요: 리셀러를 위한 오프라인 우선 소싱 코파일럿(copilot)입니다. 물품을 사진으로 찍거나 보이는 것을 타이핑하면, 로컬 오픈 웨이트 모델이 이를 읽고, 일반 TypeScript가 eBay 수수료, 배송비, 부자재 및 원하는 마진까지 고려하여 지불해야 할 최대 금액을 결정합니다. 인터넷 연결 불필요, API 키 필요 없음, 스캔당 비용 없음.
시간이 부족한가요? 판정 카드(verdict card)는 한 화면의 제품이며, 엔지니어링은 그 아래에 있습니다. 모든 주장은 열어볼 수 있는 파일로 연결됩니다 — 랜딩 페이지 · 소스 코드 · docs/KEYS.md에는 모든 키가 지정된 통합의 실시간 검증 상태가 하나의 표에 있습니다.
제가 만든 것
이것은 다음과 같은 순간을 위한 것입니다: 중고품 가게 지하실에 서서 자신이 무엇인지 모르는 재킷을 들고 있는 여성, 휴대폰 신호는 없고, 8달러가 괜찮은 생각인지 결정할 시간이 약 10초 남았을 때.
그녀는 모두가 하는 일을 합니다. 마켓플레이스 앱을 열고 추측을 타이핑하고 기다리며, 같을 수도 있고 아닐 수도 있는 수십 개의 목록들을 응시하다가 직감에 의존해 구매합니다. 또는 포기합니다. 그녀는 무엇이 맞았는지 절대 알지 못할 것입니다.
Sourcerer는 대신 하나의 질문에 답합니다: 지금 내가 지불해야 할 최대 금액은 얼마인가?
VERDICT ● BUY
MAX OFFER $35.32
EST. RESALE $133.40 ($126.73 – $140.07)
...
이 블록의 모든 숫자는 모델이 아닌 packages/shared에 의해 계산되었습니다: $133.40 판매액에 대한 eBay의 13.6%와 $0.30 결제 수수료, $8 배송비, $1 재료비를 합산하여 순액 $105.96을 얻고, 이를 그녀가 만들고자 하는 3배 배수로 나눈 값이며, 여기서 $35.32가 나옵니다.
태그에 다른 가격을 입력하면 네트워크 연결 없이도 전체 내용이 즉시 재가격화됩니다. 왜냐하면 산술 계산은 모델 호출이 아니기 때문입니다. 그 숫자가 제품이고, 나머지 모든 것은 그것 주변의 지지 구조물일 뿐입니다.
실제로 작동하는 곳. 어떤 휴대폰도 3.3 GB 규모의 비전 모델을 담고 있지 않으므로, Gemma는 그녀가 이미 소유한 노트북이나 가정용 기기에서 구동됩니다. 휴대폰은 카메라와 화면이며, 이 두 장치는 자체 네트워크를 통해 통신합니다. 이는 가장 중요한 경우를 다룹니다: 지하실에 신호가 없고 노트북이 가방 안에 있을 때입니다. 어느 기기도 접근할 수 없을 때도 앱은 서비스 워커 캐시에서 부팅되며, 그녀의 판매 기록을 기반으로 가격을 책정하고, estimate 태그를 붙여서 절대 실시간 비교 자료로 통과하지 못하게 합니다. 브라우저 전용 대체 기능은 자신이 무엇인지에 대해 정직합니다—모델이 아닌 휴리스틱(heuristics)이며, 화면에 그렇게 표시할 뿐 조용히 추측하지 않습니다.
누가 비용을 지불하는가. 한 달에 20~40개의 품목을 처리하는 주말 플리퍼입니다. 그녀는 창고를 최적화하는 것이 아니라, 서랍 속에 일 년 동안 방치되는 물건을 사지 않도록 하고, 확신이 없다는 이유로 $140짜리 거래 기회를 지나치는 것을 막으려고 노력합니다. 이 앱은 또한 그녀의 리스팅도 작성해 주는데, 이것이 작업의 또 다른 절반입니다—그녀가 그날 아침에 산 물건 더미에 대한 제목과 설명을 밤 11시에 입력하는 고된 노동입니다.
데모
네 개의 스크린샷은 사용하게 될 순서대로 흐름을 보여줍니다. 전체 과정을 편집 없이 보고 싶다면, webm 파일은 이 노트북에서 단일 실행으로 이루어졌습니다: 화면 캡처 → 네 가지 파이프라인 단계 전체 → 판정(verdict) → 수동으로 변경된 태그 가격과 모델을 건드리지 않고 재가격화된 판정 → 실행 로그 열기 → 인사이트(Insights).
A phone screenshot of Sourcerer's verdict card for a Levi's 501 Big E Selvedge Denim Jacket. The header chips read Online, Local · gemma3:4b and heuristic pricing, and the item chips read Jeans and good. A green check and the word BUY sit above two panels: MAX OFFER $35.32, and RESALE $133.40 with a range from $126.73 to $140.07 — above
판정 카드(verdict card)입니다. 가장 지배적인 신호는 화면에서 가장 큰 글꼴의 최대 제시 가격, 해당 상품이 나온 재판매 범위, 그리고 숫자가 어디서 왔는지 알려주는 칩으로 구성됩니다. 희망 가격은 수정 가능하며, 타이핑하는 동안 전체 카드가 기기 내(on-device)에서 재가격화됩니다. 재판매 가격은 상태에 맞춰 조정된 비교 대상(comps median)입니다—이 비교 대상들은 '좋음(good)' 상태이므로 ×0.92가 적용되어 아래 패널의 원본 중앙값보다 약간 낮게 표시됩니다.
실행 로그(run log)는 주목할 만한 부분입니다. 이것은 오버레이가 아니라 스캔 응답 필드이며, 각 단계별로 어떤 제공업체(provider)가 해당 단계를 실행했는지와 함께 '이 스캔에서 기기를 벗어난 것은 없습니다(Nothing in this scan left your device).'라고 표시됩니다.
A phone screenshot of an expanded panel headed
어디서 무엇이 실행되었는지 보여줍니다. 모든 스캔은 어떤 제공업체가 각 단계를 실행했는지, 얼마나 오래 걸렸는지, 그리고 기기를 벗어난 것이 있었는지 기록하므로, 개인 정보 보호 주장이 단순히 희망 사항이 아니라 검증 가능한(checkable) 필드가 됩니다: 이것은 응답의 필드입니다. 워크플로우에는 네 개의 단계가 있고 여기에는 세 개의 행이 있는데, 이는 판정 단계가 산술적이기 때문에 제공업체를 호출하지 않아 보고할 것이 없기 때문입니다. 헤더의 칩은 메인 브레인의 주 모델—gemma3:4b, 비전 기본값(vision default)—을 이름으로 지정하는 반면, 각 행은 실제로 해당 단계를 실행한 모델을 이름으로 지정합니다. 텍스트 전용 경로(text-only paths)는 의도적으로 1B를 사용하기 때문에 Identify가 gemma3:1b라고 표시됩니다.
An expanded Comparables panel headed
비교 항목들은 각각 출처가 태그되어 있습니다. 여기에 있는 모든 행은 사용자가 실제로 실현한 가격이며, 낯선 사람에게서 스크래핑된 희망 가격이 아닙니다.
A dark-theme phone screenshot taken with the browser's network disabled: the header chips read
네트워크가 완전히 끊긴 상태에서 브라우저로 촬영했습니다. 칩이 그렇게 말하고, 두뇌는 연결할 수 없으며, 앱은 여전히 부팅되고 서비스 워커 캐시를 통해 캡처 화면을 제공합니다. 왜냐하면 자선 가게의 사각지대는 이 장치가 사용되도록 의도된 곳이기 때문입니다.
모든 21개의 스크린샷과 캡션, 대체 텍스트는 docs/media/에 있으며, docs/media/CAPTIONS.md에서 목록화되어 있습니다.
모든 스크린샷과 비디오는 scripts/capture-evidence.ts에 의해 생성되었습니다. 이 스크립트는 실제 브라우저에서 실제 두뇌를 대상으로 앱을 구동하고, 커밋된 픽스처(fixture)에서 샘플 데이터를 주입하여 두 번의 실행이 동일한 프레임을 생성하도록 하며, 스크린샷과 비디오를 한 번에 작성합니다. 이들은 목업(mockup)도 아니고 수정된 것도 아닙니다. 표지 이미지만이 예외인데, 이것은 그 캡처 중 하나로 구성되었습니다.
공개 랜딩 페이지는 **https://sourcerer-ochre.vercel.app**에서 라이브입니다. 한 페이지에 모든 주장이 담겨 있으며, 헤드라인 숫자는 캡처 시점의 eval JSON에서 읽어오기 때문에 수동으로 입력된 통계처럼 변동할 수 없습니다.
해당 스크립트는 인정할 만한 버그도 발견했습니다. 바로 캡처 양식에 입력했던 희망 가격이 판정 카드(verdict card)에 의해 조용히 폐기되고 있었다는 것입니다. 이 카드는 가격의 사적 복사본을 유지하고 있었고, 그 초기값은 0으로 설정되어 있었습니다. 첫 번째 캡처된 모든 스크린샷은 마치 물건이 공짜인 것처럼 가격이 책정되었습니다. 이는 모든 타입 검사(typecheck), 모든 빌드, 그리고 전체 테스트 스위트(test suite)를 통과했으며, 화면을 렌더링하고 결과를 파일에 기록하는 순간 눈에 띄었습니다. 스크린샷은 거짓말할 수 없는 테스트입니다.
하드웨어 사양은 본 게시물의 타이밍 주장 전체에 걸쳐 다음과 같습니다: 8코어 CPU, RAM 15.4 GB, VRAM 3.6 GB를 가진 NVIDIA GTX 1650 — 모델의 일부를 담기에는 충분하지만 전부 다 담기에는 부족하여 대부분의 추론(inference)은 CPU로 실행됩니다. 클라우드 GPU도, 유료 추론도 어느 순간에도 사용되지 않았습니다.
코드
https://github.com/ItsPriyamSri/sourcerer
MIT 라이선스. 클론 후 실행: pnpm install && bash scripts/setup.sh를 수행한 다음, 두 개의 터미널을 사용합니다 —
pnpm --filter @sourcerer/brain dev와 pnpm --filter @sourcerer/web dev. 핵심 루프(core loop)가 작동하는 것을 보기 위해 API 키는 필요하지 않습니다.
만약 두 파일만 읽으신다면: README에는 각 부분이 무엇을 위한 것인지, 그리고 어떤 기술이 어떤 작업을 하는지 설명되어 있으며, docs/ARCHITECTURE.md에서는 파이프라인과 제공자(provider)가 누락되었을 때 모든 계층이 어떻게 작동하는지를 다루고 있습니다.
구축 과정
발견: 모델은 읽고, 산술 계산이 결정한다
저는 작은 모델에게 전체 작업을 맡기도록 요청하는 것부터 시작했습니다. 즉, 물건을 보고 그것을 사야 할지 말아야 하는지를 알려주게 한 것입니다. 이 모델은 두 번째 부분에서 알아차리기 어렵고 무시하기에 비용이 많이 드는 방식으로 성능이 좋지 않았습니다. 4B 모델에게 판정(verdict)을 요청하면, 실제로 필요한 것은 해결책(solve)이기 때문에 자신만만하고 그럴듯하지만 산술적으로 틀린 답변을 받게 됩니다:
max_offer = (resale − resale·fee − shipping − supplies) / target_multiple
언어 모델은 그 용도가 아닙니다. 그것은 빛이 좋지 않은 곳에서 라벨의 흐릿한 사진을 보고 “이것은 Pendleton 보드 셔츠이고, 줄어들었군”이라고 말하는 데 적합한 도구입니다.
따라서 시스템은 영구적으로 분리됩니다:
| 누가 하는가 | 왜 그런가 |
|---|
| 사진에서 아이템을 읽기 | Gemma 3 4B, 로컬로 | 흐릿하고, 나쁜 빛에 관용적이며, 흥미로운 방식으로 가끔 틀린다. 완벽하게 적합하다. |
| ... | | |
The 결과는 브라우저가 오프라인에서 보여주는 판정과 서버가 온라인에서 계산하는 판정이 _같은 코드_라는 것입니다. decideVerdict는 순수(pure)하며 I/O를 사용하지 않기 때문에, 재판매업자가 데드 존에 가격을 입력해도 서버의 판정과 센트 단위까지 일치하는 답변을 얻습니다. 이것은 사소한 장점이 아닙니다. 이 도구가 돈과 관련하여 신뢰받을 수 있는 이유입니다.
두 가지 변경 사항이 실제로 한 것들
솔직한 엔지니어링 이야기는 제가 프롬프트 작업이 핵심 동력이 될 것이라고 예상했지만, 실제로는 그렇지 않았다는 것입니다.