Skip to content
게으른 엔지니어의 기술 블로그
Go back

웹 검색을 언제 같이 쓸지 설계하다가, 매번 질문 하나에 무너졌다

obsidian-gemini-butler에는 /chat이 보관함(vault)에 이미 충분한 답이 있으면 웹 검색을 아예 안 하고, 없을 때만 웹으로 폴백하는 기능이 있다. 여기서 자연스럽게 다음 질문이 나왔다 — 보관함에 답이 있긴 한데, 그 노트가 쓰인 이후에 새로 나온 정보가 있다면 어떻게 하지? 지금 구조론 이런 경우 영원히 웹 검색을 안 한다. 이 문제를 GitHub 이슈로만 등록해두고 설계는 안 한 채로 남겨뒀었는데, 이번에 “코드는 건드리지 말고 얘기만 하자”는 조건으로 붙잡고 앉아서 풀어봤다. 결과부터 말하면, 내가 낸 제안은 세 번 다 무너졌고 제일 단순한 답은 상대방한테서 나왔다.

첫 번째 안: 자동으로 감지해서 켜자 — 이건 꺼내기도 전에 접었다

“최신”이나 “요즘” 같은 단어가 질문에 있으면 자동으로 웹 검색을 같이 켜는 방법을 잠깐 생각했다. 그런데 이 프로젝트 자체의 최근 이력을 떠올려보니 답이 이미 나와 있었다 — 두 개의 다른 이슈(병합 후보 탐지, 병합 원자성 감사)가 똑같이 “모델이 충분한 근거 없이 판단하게 하면 신뢰도가 애매해진다”는 결론으로 끝났었다. 문구 감지도 결국 같은 종류의 판단이라, 시작하기도 전에 접고 명시적 트리거(사용자가 직접 켜는 방식)로 방향을 잡았다.

웹에서 찾은 정보가 노트보다 정말 최신인지 확인하려면 두 날짜를 비교하면 된다고 생각했다. 노트의 수정일이야 이미 있으니, 검색 결과에도 발행일이 딸려올 거라고 짐작했다. 이 짐작을 그대로 믿고 넘어갈 수도 있었는데, 마침 “어디서 검색하는 거냐”는 질문이 나온 김에 실제로 OpenAI Responses API의 web_search 툴을 직접 호출해봤다.

결과는 짐작과 달랐다. 응답에 붙는 인용 정보(annotation)에는 제목과 URL만 있고 발행일 필드가 아예 없었다. 어쩌다 URL 안에 날짜처럼 보이는 숫자가 우연히 박혀 있는 경우가 있었을 뿐, 구조화된 필드는 없었다. 날짜 비교라는 전제 자체가 성립하지 않는다는 걸 코드를 짜기 전에, 대화 중에 확인한 셈이다.

대안으로 “검색어 자체에 노트 날짜 이후라는 조건을 박아 넣자”는 방향을 제시했다 — 이건 방금 그 호출에서 “2026년 8월”이라고 검색어에 직접 넣었더니 실제로 8월 발행 기사가 나온 걸 보고 떠올린 것이었다. 사후 비교가 안 되면 애초에 검색 단계에서 범위를 좁히자는 우회로였다.

중간에 나온 지적: 최신이 항상 좋은 게 아니다

여기서 “오랫동안 맞는 얘기도 있는데, 웹에서 최신이라고 무조건 좋은 건 아니다”라는 지적이 나왔다. 맞는 말이었다. 그리고 이게 아까 정한 “명시적 트리거” 결정을 다시 한번 정당화해줬다 — 안정적인 주제(정의, 이미 끝난 사건 등)엔 사용자가 애초에 이 기능을 켤 이유가 없으니, “이 질문이 시간에 민감한지”를 시스템이 따로 판단할 필요가 없어진다. 새 장치를 추가한 게 아니라, 이미 내린 결정의 근거가 하나 더 생긴 순간이었다.

세 번째 안: 노트 답/웹 답/종합, 세 개를 만들자 — 호출 수부터 걸렸다 (url_citation)

노트 기준 답과 웹 기준 답을 각각 독립적으로 만든 다음 합치자는 안을 냈다. 판단을 시스템이 안 하고 사용자가 두 답을 직접 비교하게 하자는 취지였다. 그런데 “결국 호출이 3번 필요한 거 맞냐”는 확인 질문에, 그렇다고 인정할 수밖에 없었다 — 지금 1번이던 호출이 3번으로 는다.

이어서 “노트를 줬는데 모델이 실제로 안 쓰면 결국 웹 답변을 두 번 만드는 꼴 아니냐”는 지적이 들어왔다. 정확했다. 노트가 실제로 관련 없으면 노트-기준 호출은 빈 답만 내놓고, 종합 단계는 사실상 웹 답변을 다른 말로 반복하는 것밖에 안 된다.

이걸 메워보려고 “웹 검색 툴의 인용 표시(url_citation)를 구분 신호로 쓰자”는 안을 냈다 — 인용이 붙은 부분은 웹, 안 붙은 부분은 노트라고 보면 호출을 한 번으로 줄일 수 있다는 논리였다. 그런데 “그건 LLM이 학습해서 알고 있는 내용도 있잖아”라는 한마디에 이것도 무너졌다. 인용이 없다고 해서 그게 노트에서 온 건지, 모델이 원래 알던 배경지식인지는 구분이 안 된다. 이 둘은 신뢰도가 완전히 다른데, 내 제안은 그 둘을 같은 것으로 취급하고 있었다.

결국 제일 단순한 답이 이겼다 (사용자 트리거 웹 검색 폴백)

마지막에 나온 제안은 이거였다 — 지금처럼 그냥 질문하고 답을 받는다. 그 답을 보고 사용자가 부족하다고 느끼면, 그때 “웹에서도 찾아줘”를 누른다. 그러면 앞서 걸렸던 문제들이 한 번에 다 풀린다. 호출 낭비는 생기지 않는다 — 웹 검색은 사용자가 필요하다고 판단한 뒤에만 일어나니까. 합치는 단계를 시스템이 자동으로 만들 필요도 없다 — 두 답을 채팅창에 순서대로 보여주기만 하면, 비교는 사용자가 눈으로 한다. “최신이 항상 좋은 건 아니다”라는 문제도 저절로 풀린다 — 판단은 처음부터 끝까지 사용자 몫이다.

남는 생각

이 대화에서 내가 낸 제안은 전부 그럴듯해 보였다. 자동 감지, 날짜 비교, 세 갈래 종합, 인용 표시 활용 — 하나씩 들으면 다 말이 됐다. 그런데 매번 “그거 실제로 그런지 확인했어?”, “그러면 결국 몇 번 부르는 거야?”, “그건 원래 알던 거랑 구분이 안 되잖아” 같은 짧은 질문 하나에 전제가 무너졌다. 복잡한 설계를 다시 더 복잡하게 고치는 대신, 매번 한 겹 걷어내고 더 단순한 답으로 돌아갔다. 결과적으로 제일 마지막에 남은 안이 제일 단순했다는 게, 이 대화 전체에서 가장 뚜렷한 패턴이었다.

이건 아직 이슈에 방향만 정리해둔 상태고, 코드는 한 줄도 안 썼다. 구현은 다음 이야기다.


Share this post:

Previous Post
검증된 방식을 그대로 옮기려다가 — 조건이 바뀐 걸 세 번 놓쳤다