가을 산책 코스를 보내주는 AI를 만들었습니다. 그리고 아파졌습니다.
요약
사용자 개인의 취향과 경험을 반영하여 'Autumn Walks'라는 맞춤형 텔레그램 봇을 개발했습니다. 이 봇은 매일 아침 파리의 날씨 정보, 가을 색이 변하는 나무 목록(종류, 위치), 총 이동 거리 및 예상 도보 시간 등을 포함한 산책 코스를 제공합니다. 오픈 데이터와 개인화된 로직을 결합하여 사용자에게 실질적인 경험을 전달하는 것이 핵심입니다.
핵심 포인트
- 개인 맞춤형 봇으로 일상에 가치를 더함
- 파리 오픈 데이터를 활용해 나무 정보를 수집
- 날씨, 거리, 시간 등 종합 정보 제공
- 사용자 경험(UX) 중심의 개발 사례
_이 글은 Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass에 제출하는 내용입니다.
매일 아침, 제 노트북의 작은 봇이 색이 변하는 파리의 나무 5그루를 골라 날씨를 확인하고, 저에게 텔레그램으로 산책 코스를 보내줍니다. 이것을 읽는 데는 약 20초가 걸립니다. 하지만 실제 산책은 약 한 시간이 걸립니다. 이 비율 자체가 핵심 아이디어입니다.
제가 만든 것 (What I Built)
가을은 제가 가장 좋아하는 계절입니다. 저는 색상에 매료되고, 그 특유의 따뜻한 분위기에 빠집니다. 귀에 음악을 꽂고 파리 거리를 걸으며 그 느낌 자체를 즐기는 것을 좋아합니다. 또한 가을은 수프와 편안한 음식의 계절이며, 창문에 김이 서린 카페에서 사랑하는 사람과 함께 뜨거운 초콜릿을 마시러 나가는 계절이기도 합니다.
그래서 이번 주에는 제가 이 가을을 화면 뒤에 숨겨 보내지 않도록 무언가를 만들었습니다.
Autumn Walks는 매일 아침 8시 30분에 저에게 산책 코스를 보내주는 텔레그램 봇입니다:
- 날씨 조언 한 줄 (우산 필요 여부, 오후 가장 건조한 시간대)
- 10월에 빨간색, 주황색 또는 금색으로 변하는 나무 3~5그루와 그 나무가 있는 거리 및 가을 색상 정보
- 총 이동 거리, 실제 예상 도보 시간, 그리고 루프 코스용 Google Maps 링크
이 봇은 이틀 연속 같은 거리를 보내지 않습니다. 프랑스어와 영어로 작동합니다. 오직 한 사람, 즉 저를 위한 것입니다.
나무들은 파리의 오픈 데이터에서 가져왔습니다. 시는 관리하는 모든 나무(220,174그루)에 대한 정보를 공개하고 있습니다. 저는 그중 5개 중앙 구역(arrondissements)의 가을 색이 강한 종(은행나무, 액체나무, 페르시아 철목, 단풍나무, 참나무)인 2,065그루를 선별했습니다. 그리고 실제로 걸어갈 수 있는 나무들만 포함시켰습니다: 거리, 공원, 묘지입니다. 학교 운동장은 제외했습니다. (초기 버전에서는 제가 육묘장 안의 나무로 가는 코스를 받기도 했습니다.)
데모 (Demo)
목요일 아침에, 아무도 노트북을 만지지 않았는데 프랑스어로 제 휴대폰으로 도착한 내용입니다:
영어로:
- Oak, 73 rue de Provence, copper brown (참나무)
- Persian ironwood, rue Milton, red to orange (페르시아 철목)
- Liquidambar, 3 rue de la Jussienne, red to purple (액체단풍나무)
- Liquidambar, Jardin Nelson Mandela, red to purple (액체단풍나무)
- Maple, 8 rue de la Banque, red or orange (단풍나무)
총 거리: 3.9 km, 도보 시간: 65분.
제가 계획하지 않은 부분
목요일에 저는 아팠습니다. 제대로 아팠죠. 이 산책 코스 중 단 하나도 가지 못했습니다.
이것은 저를 많이 좌절시켰는데, 누군가 저에게 도전을 주면 끝까지 해내는 것을 좋아하기 때문입니다. 하지만 제 건강이 최우선입니다.
금요일에 봇은 침묵했습니다. 로그에는 아무런 기록도 없었습니다. 제 Mac은 하룻밤 동안 꺼져 있었고, macOS의 launchd는 수면 중에 놓친 작업을 따라잡지만, 기기가 전원이 꺼진 상태에서 놓친 작업은 그렇지 못합니다. 저를 밖에 나가게 만들려고 만든 봇은 저와 함께 안에 갇혀버린 것입니다.
그래서 이 게시물은 제가 구축하고 테스트했지만, 아직 사용하지 못한, 스스로 작동하는 도구에 관한 내용입니다. 첫 번째 산책 코스는 제가 다시 일어날 때 기다리고 있습니다.
코드
GitHub logo Mialy333 / autumn-walks
로컬 LLM 에이전트(Strands + Ollama의 Gemma 4 E2B)를 사용하여 색색의 파리 나무 사이 가을 산책 계획하기
autumn-walks
디자인 원칙: LLM이 글을 쓰고, 코드가 지리를 담당한다.
작동 방식
flowchart LR
Q["질문 또는 매일 8:30 작업"] --> A["Strands agent<br/>Ollama를 통한 Gemma 4 E2B"]
A --> W["get_weather<br/>Open-Meteo"]
A --> F["find_autumn_trees<br/>SQLite: 파리 나무들"]
A --> B["build_walk<br/>Python: 정류장, 거리, 지도 링크"]
H("산책 기록<br/>SQLite") -- "최근 거리 건너뛰기" --> B
W --> A
F --> A
B --> A
A --> M["전송 전 확인된 메시지"]
M --> T["Telegram"]
M -- "산책 기록"
H
Loading
A Strands Agents 에이전트는 Gemma 4 E2B를 Ollama에서 실행하며 세 가지 도구를 사용합니다:
get_weather: Open-Meteo(https://open-meteo.com)의 오늘 오후 기온, 강우 확률 및 풍속 (API 키 불필요), 그리고 가져가야 할지 여부 등...
제작 과정
스택: 두 개의 HTTP 호출을 제외하고 모두 오픈 소스이며 로컬에서 작동합니다:
- 모델: Gemma 4 E2B, Ollama를 사용하여 로컬 실행
- 에이전트 프레임워크: Strands Agents (Python)
- 데이터: 파리 오픈 데이터 "Les arbres" (ODbL), SQLite로 한 번 필터링
- 날씨: Open-Meteo (무료, API 키 불필요)
- 전달: 매일 아침
launchd작업으로 트리거되는 Telegram 봇
1단계: 작은 모델이 도구를 호출할 수 있는지 확인하기
다른 것을 작성하기 전에 스파이크 테스트를 진행했습니다. Gemma 4 E2B, 가짜 날씨 도구 하나, 질문 하나, 세 번의 실행을 했습니다. 이 모델은 3번 중 3번 모두 올바르게 도구를 호출했으며, 데 걸렸습니다. 이것이 이번 주 주요 위험 요소였고, 저녁 시간을 들여 해결했습니다.
2단계: 작동하게 만든 디자인 원칙
처음에는 Gemma가 모든 것을 하도록 했습니다: 나무 검색, 정류장 선택, 거리 계산. 하지만 이는 지시적인 방식으로 실패했습니다:
제목: LLM의 한계를 극복한 지리 기반 여행 경로 생성 시스템 구축
- 시스템 프롬프트가 없었을 때는 3회 시도 중 단 한 번도 도구를 호출하지 못했고, 경로를 계획할 수 없다고만 말했습니다.
- 직접 정류장을 선택하게 했을 때는 멀리 떨어진 희귀한 나무들을 골라 40분 동안 걸을 수 있는 4.8km에서 5.5km 길이의 루프를 만들었으며, 최대 4번까지 재시도했습니다.
- 심지어 장소를 지어냈습니다. 로딩된 어떤 구(arrondissement)에도 없는 뤽상부르 공원(Jardin du Luxembourg)을 포함하는 산책로가 있었습니다.
- 숫자 계산에 오류를 범했습니다: 1.2km 루프를 "약 3km"라고 부르고 거리를 잘못 표시했습니다.
그래서 저는 작업을 분리했습니다.
LLM이 글을 쓰고, 코드가 지리학을 담당한다.
이제 Python은 나무들을 검색하고, 거리 제한(가장 많은 종 우선, 그다음 가장 짧은 루프)에 맞는 3~5개의 최적 정류장을 선택하며, 모든 거리와 도보 시간을 계산합니다 (실제 거리는 직선거리 × 1.3 적용). 그리고 지도 링크를 만듭니다. 나무 이름과 가을 색깔 정보는 고정된 사전에서 가져옵니다. Gemma가 이 모든 것을 읽고 프랑스어 또는 영어로 짧고 친근한 메시지를 작성합니다. 이 과정에서 절대로 나무, 색상, 혹은 숫자를 지어내지 않습니다.
메시지가 전송되기 전에 코드가 이를 확인합니다: 산책로가 실제로 구축되었는지, 정확한 링크가 존재하는지, 시작 지점이 올바른지, 바람이 언급되었는지, 지난 두 번의 산책로와 같은 거리가 반복되지 않았는지, 그리고 150단어 미만인지 등을 검사합니다. 만약 확인에 실패하면 에이전트에게 이유를 알려주고 최대 두 번까지 다시 시도하게 합니다. 모든 재시도 이유는 기록됩니다.
현재 결과: 모든 테스트 실행에서 11개 항목 중 11개가 통과되었으며, 노트북 기준으로 산책로당 15초에서 21초가 소요됩니다.
단계 3: 메모리 (memory)
첫 번째 일일 버전은 매일 같은 산책로를 보냈습니다: 시작 지점도 같고, 나무들도 같습니다. 작은 SQLite 히스토리 기능을 추가하여 이를 수정했습니다. 이제 에이전트는 지난 두 번의 산책로에서 사용된 거리를 건너뜁니다. (나무만 건너뛰는 것만으로는 충분하지 않았습니다. 옆에 있는 나무가 있어도 같은 경로를 생성했기 때문입니다.) 이 기록은 제 컴퓨터에 보관되며 GitHub에는 절대 올라가지 않습니다.
Open Innovation이 중요한 이유
- 운영 비용이 들지 않습니다. 추론(Inference)은 로컬에서 이루어집니다. 네트워크 호출은 날씨 정보와 Telegram 메시지를 보내는 경우에만 발생합니다.
- 저의 위치와 습관은 저에게 머뭅니다. 호스팅되는 모델이라면 매일 제 출발 지점과 걷기 기록을 수신할 것입니다. 여기서는 그 데이터가 노트북을 벗어나지 않습니다. 정확히 말하자면: Telegram 메시지 자체는 Telegram 서버를 거치고, 날씨 호출은 제가 집이 아닌 공공 명소로 의도적으로 선택한 오페라(Opéra)의 좌표를 전송합니다.
- 데이터 역시 공개적입니다. 나무 데이터는 ODbL 하의 파리 오픈 데이터입니다. 나무 데이터를 발표하는 어떤 도시든 파일을 하나 교체하여 동일한 봇을 얻을 수 있습니다.
- 모든 것을 보고 변경할 수 있었습니다. 에이전트가 오작동했을 때, 저는 블랙박스 API가 무엇을 하는지 추측할 필요가 없었습니다. 지리 정보를 모델에서 분리하여 테스트할 수 있는 코드로 옮겼습니다. 모델을 교체하는 것은 설정 파일(config)의 한 줄만 수정하면 됩니다.
문제가 발생한 부분 (What Broke)
- 아파진 날 봇이 침묵했습니다 (위 참조): 꺼진 Mac은 놓친
launchd작업을 실행하지 못합니다. - 제 에이전트 세션 업로드가 차단되었습니다. 코딩 세션을 DEV에 DevRelay를 이용해 저장할 때, 전사(transcript)에 코드가 포함되어 있으면 Cloudflare 403 오류가 발생했습니다. 저는 코드 없는 가벼운 버전들을 게시했고, 코드는 리포지토리(repo)에 있습니다.
- 제 Gmail 주소가 첫 번째 커밋에 들어갔습니다. 커밋 작성자 정보와
pyproject.toml파일 모두에 포함되어 있었습니다. 첫 푸시 전에 발견하여 스쿼시(squash) 처리와 GitHub의 noreply 주소로 수정했습니다. - Telegram에서 지도 링크가 절반으로 잘렸습니다. 웨이포인트(waypoint) 사이의 구분자(
|) 때문이었습니다. 이를%7C로 인코딩하여 해결했습니다. - **Open-Meteo가 빌드하는 동안 503
공개적으로 메시지를 보낼 수 있는 봇은 없습니다. 한 사람을 위해 하나의 노트북에서 실행됩니다. 하지만 이 저장소(repo)는 적응하도록 구축되었습니다. 시작 지점, 언어, 전송 시간을 설정할 수 있습니다. 만약 거주 도시가 나무 데이터를 오픈 데이터로 공개한다면 댓글로 알려주세요. 앞으로 이것이 어디까지 발전할 수 있을지 알고 싶습니다.
나의 에이전트 세션
이 전체 빌드는 터미널에서 코딩 에이전트를 사용하여 진행되었습니다. 봇이 메모리와 Telegram 음성을 얻게 된 세션을 여기에 공유합니다:
가을 산책: 워크 메모리 및 Telegram 아침 메시지
당신
4km 제한을 유지하세요. 단계 4a: 에이전트에게 메모리를 제공하세요. 모든 산책(날짜, 시작 지점, 나무 종류, 루프 길이)을 저장하고 최근 2번의 산책에서 나무 종류는 제외한 다음, 연속으로 3번의 산책을 계획하고 이들이 다르다는 것을 보여주세요. 단계 4b: Telegram. 저는 봇을 만들고 토큰을 환경 파일에 넣었습니다. 그 파일을 절대 출력하지 말고, git이 무시하도록 설정했는지 확인하세요. 제 채팅 ID를 찾아서 거기에 저장하세요. 산책을 계획하고 제 채팅으로 보내는 명령어를 추가하고, 스스로 켜지 않도록 매일 아침 8시 30분에 예약하고, 지금 바로 실제 메시지를 하나 보내세요.
에이전트
▸ Bash 출력 파일 내용을 출력하지 않고 확인하기
입력
$ 출력 파일 내용을 출력하지 않고 확인하기
출력
커밋되지는 않았지만, 아직 git에 의해 무시되지 않았습니다. 무시 목록에 추가했습니다. 변수 이름만 표시되었습니다.
▸ Bash 최근 2번의 산책에서 나무 종류를 제외하고 연속으로 3번의 산책 계획하기
입력
$ 최근 2번의 산책에서 나무 종류를 제외하고 연속으로 3번의 산책 계획하기
출력
모든 확인이 통과했지만, 경로는 매번 동일합니다. 같은 네 거리만 있고, 이웃한 나무가 다릅니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
