2026-08-19 (수) 개발일지
오늘의 회고: 검색 에이전트가 도구를 안 부르는 두 가지 원인
오늘은 proposal_automate 검색 에이전트가 도구 호출을 생략하고 아는 척 답변하는 문제를 파고들었다. 처음엔 프롬프트가 너무 길어서 중요한 규칙을 놓친다고 생각했는데, 파보니 히스토리 구조 자체가 모델을 도구 호출에서 멀어지게 만드는 또 다른 원인이 숨어 있었다. 결국 프롬프트를 4,464자에서 984자로 줄이고 블록 순서를 재배치했으며, 히스토리 구성 방식도 바꿨다.
프롬프트가 4,464자 — 모델이 도구를 안 부르는 이유를 길이에서 찾았다
검색 에이전트가 “로그 수집 이중화 장표 찾아줘” 같은 질문에 도구를 호출하지 않고 그냥 “몇 장 있습니다” 식으로 답변하는 사례가 잦았다. 처음엔 단순 프롬프트 길이 문제라 생각했다. agent.py의 INSTRUCTIONS가 3,885자, CHAT_RULES가 579자로 총 4,464자였다. Exa의 설계 원칙을 참고해 “200~800토큰, 가능하면 500 이하”라는 기준을 확인하고, 길이를 먼저 줄이기로 했다.
하지만 길이만 984자로 줄인 버전을 테스트해보니 도구 호출률이 여전히 70%대에 머물렀다. 원인이 길이만은 아니었다. 프롬프트 블록 순서를 들여다보니 정체성 → 답변 형식 → 대화 상태 유지 → 베이스 덱 추천 → 직접 만든 장표 → 도구 사용 규칙 순으로 배치되어 있었다. 가장 중요한 도구 사용 규칙이 맨 끝이 아니라 중간에 묻혀 있었고, recency bias를 고려하면 가장 중요한 규칙은 맨 끝에 두어야 한다.
그래서 블록 순서를 정체성 → 도구 고르기 → 제약 → 출력 형식으로 재배치했다. 도구 고르기 블록을 맨 앞으로 당기고, “신호가 없으면 무조건 search_slides를 부른다”는 규칙을 프롬프트 맨 마지막 줄에 배치했다. 또한 기존 프롬프트에 있던 불필요한 예시와 중복 설명을 제거해 984자로 압축했다.
검증: 새 프롬프트로 15턴 대화를 3회 반복한 결과, 도구 호출률 100%, 라우팅 정확도 100%, 금지어(em-dash, ~한다 등) 39/39 전부 통과, 중간 답변 길이 535자로 안정적이었다. 이전 대비 도구 호출률이 약 30%p 올랐다.
남은 과제: 프롬프트 길이가 짧아지면서 모델이 답변을 너무 간결하게 하는 경향이 생겼다. 사용자가 “더 자세히 알려줘”라고 후속 질문을 하면 slide_detail을 부르는 식으로 대응하도록 추가 튜닝이 필요할 수 있다.
assistant 답변을 히스토리에 넣으면 도구 호출이 죽는다 — 모델이 답변 형식을 따라하는 함정
프롬프트를 고친 뒤에도 긴 대화 세션에서 도구 호출이 다시 줄어드는 현상이 발견됐다. 30턴 대화의 후반부로 갈수록 모델이 도구 없이 직접 SlideID를 나열하는 비율이 높아졌다. 처음엔 컨텍스트 윈도우가 부족해서 프롬프트가 잘린 건가 싶어 CHAT_HIST_CHARS를 6,000자로 제한하고 히스토리 구성 방식을 점검했다.
그런데 A/B 테스트를 해보니 이전 assistant 답변을 히스토리에 포함시킨 쪽이 도구 호출률 0/36인 반면, assistant 답변을 제거하고 user 메시지만 유지한 쪽은 18/36으로 유의미하게 높았다. 더 놀라운 건, assistant 답변에 포함된 SlideID 목록이 모델에게 “이미 답이 나와있으니 도구를 부르지 않아도 된다”는 신호로 작용한다는 점이었다. 모델이 이전 assistant 답변의 형식을 그대로 따라하면서 도구 호출을 생략하고 직접 SlideID를 생성하기 시작한 것이다.
결정적 발견: assistant 답변에 SlideID 리스트가 있으면, 모델이 “도구 호출 없이도 답변을 생성할 수 있다”고 학습해버린다.
해결 방향은 두 가지였다. (A) assistant 답변을 아예 히스토리에서 제거한다 → 메모리 부족으로 컨텍스트 손실이 발생한다. (B) assistant 답변 중 SlideID 목록 부분만 필터링한다 → 구현이 복잡하고 실수로 중요한 정보까지 잘릴 위험이 있다. 결국 (C) assistant 답변을 히스토리에 포함하되, 도구 호출 결과(카드)만 남기고 모델이 생성한 텍스트는 제거하는 방식으로 결정했다. 즉, search_slides의 결과로 받은 카드 데이터만 히스토리에 쌓고, 모델이 그걸 바탕으로 생성한 “~~ 장표가 있습니다” 같은 문장은 저장하지 않았다.
검증: 수정 후 30턴 대화에서 도구 호출률이 36/36으로 회복됐다. assistant 답변을 히스토리에 넣지 않으면서도 컨텍스트 손실은 없었고, 모델이 이전 검색 결과를 참고해 후속 질문에 정확히 대응하는 것도 확인했다. 다만 히스토리에서 assistant 텍스트가 사라지면서 대화 흐름이 끊기는 느낌이 들 수 있어, 사용자 메시지와 도구 호출 결과만으로도 충분히 맥락이 유지되는지 추가 모니터링이 필요하다.
남은 과제: 현재 CHAT_HIST_CHARS = 6000으로 제한했지만, 30턴 이상 장기 대화에서는 여전히 앞부분이 잘릴 수 있다. 추후 sliding window 방식으로 전환하거나, 중요도 기반 요약을 도입할지 검토 중이다.
오늘의 한줄
모델에게 “도구를 써라”고 말하는 것보다, 도구를 쓰지 않아도 될 이유를 제거하는 것이 더 효과적이다.