까보KKABO.DEV
KOEN

에이전트 구축

AI 검색이 놓친 업무 기록, 색인 본문부터 확인하기

담당자가 남긴 원인 메모가 검색 본문에는 없었다. 본문을 합친 뒤에도, 앞 200자만 전달하면 그 문장이 빠질 수 있다. 같은 기록의 근거를 따라가며 고칠 단계를 찾고, 작은 실험으로 차이를 확인한다.

2026-09-20읽는데 11분Read in English#agent-search#rag#search

업무 화면에서 기록을 열면 ‘반납함 안에서 책이 눌려 표지가 파손됨’이라는 원인 메모가 보인다. 그런데 에이전트에게 “파손 도서와 관련된 문의를 찾아줘”라고 요청했을 때는 이 기록을 놓친다고 해보자. 사람은 메모를 읽고 관련 기록이라고 판단한다. 에이전트가 쓰는 검색 도구에도 같은 문장이 전달됐는지부터 확인할 필요가 있다.

ADK Java로 만든 업무용 봇의 검색 도구를 다루면서 확인한 문제가 이와 비슷했다. 문의 기록은 처음 남길 때 한 번에 완성되지 않았다. 문의한 사람은 제목과 내용만 적고, 조사한 원인과 처리한 내용은 담당자가 나중에 다른 필드에 적었다. 그런데 검색 설정은 그 메모까지 포함하지 않고 있었다. 이 글의 도서관과 필드 값은 실제 업무를 설명하기 위해 바꾼 가상 예제다.

확인할 것은 두 가지다. 검색할 때 이 문장을 쓰는가. 찾은 뒤에는 모델에게 이 문장을 주는가. 앞의 문제를 고쳐도 뒤의 문제는 남을 수 있다. 본문을 합친 실제 변경과 앞부분 발췌의 한계를 살펴보고, 검색 근거 실험에서 설정을 바꿔 보며 같은 문장이 어디서 빠지는지 확인하자.

문의 내용과 처리 결과는 다른 필드에 남는다

처음 남긴 문의에는 “책의 상태를 확인해 달라”는 요청만 있었다. 담당자가 기록을 처리하며 원인과 조치를 추가했다.

필드 저장된 값
제목 반납 자료 상태 확인 요청
문의 내용 반납한 책의 상태를 확인해 주세요
원인 메모 반납함 안에서 책이 눌려 표지가 파손됨
조치 메모 수거 주기를 조정하고 완충재를 점검함

제목과 문의 내용만 읽는 검색 도구는 문의를 남길 때의 설명만 받는다. 나중에 밝혀진 ‘표지 파손’이나 실제로 한 ‘완충재 점검’을 검색에 활용하려면 그 메모도 검색 대상에 포함돼야 한다. 원본 데이터베이스에 필드가 있다는 사실만으로 이 조건이 충족되지는 않는다.

여기서 찾는 것은 특정 단어 하나만이 아니다. ‘표지 파손’이 없더라도 의미 검색이 이 기록을 관련 있다고 판단할 수는 있다. 다만 담당자가 확인한 원인 문장을 검색에 쓰지 않는다면, 그 문장에 담긴 정보를 활용할 기회가 빠진다. 문서가 검색될 가능성과, 특정 근거를 검색에 사용할 수 있는지는 구분해야 한다.

검색 서비스와 모델이 받는 데이터는 다르다

이 사례는 정형 기록을 당시 Vertex AI Search라는 관리형 검색 서비스에 적재하는 구조였다. 현재 공식 문서의 명칭은 Agent Search다. 이 글에서는 이 이름을 쓴다. 검색 도구가 질문을 받을 때마다 업무 화면의 모든 필드를 읽는 구조가 아니라, 미리 적재하고 검색에 사용하도록 정한 데이터를 조회했다.

적재할 때는 원본에서 검색할 텍스트를 선택한다. 질문을 받으면 검색 서비스가 관련 문서를 고르고, 애플리케이션은 그 결과에서 필요한 필드를 골라 모델에게 보낸다. 원인 메모는 이 과정 중 두 곳에서 빠질 수 있다. 처음부터 검색할 텍스트에 포함하지 않았거나, 찾은 문서를 응답으로 정리하면서 제외했을 때다.

원본 기록사용자 질문검색 대상검색 도구검색 서비스 응답모델 입력필드 선택·적재관련 문서검색 요청필드 선택·발췌
  • 1행: 원본 기록, 사용자 질문
  • 2행: 검색 대상, 검색 도구
  • 3행: 검색 서비스 응답
  • 4행: 모델 입력
  • 원본 기록 → 검색 대상: 필드 선택·적재
  • 사용자 질문 → 검색 도구
  • 검색 대상 → 검색 서비스 응답: 관련 문서
  • 검색 도구 → 검색 서비스 응답: 검색 요청
  • 검색 서비스 응답 → 모델 입력: 필드 선택·발췌
그림 1. 미리 적재한 데이터로 검색한 뒤, 애플리케이션이 선택한 내용을 모델에게 전달한다

Agent Search에서는 다음 설정을 따로 확인한다. 이름이 비슷해도 바꾸는 동작은 다르다.

설정 하는 일
searchable 해당 텍스트를 검색에 사용한다. 원인 메모로 관련 기록을 찾고 싶다면 확인할 설정이다
retrievable 해당 필드의 원문 값을 검색 응답으로 반환할 수 있게 한다. 검색에 썼더라도 반환은 별도다
title·description 키 속성 제목·설명에 해당하는 필드를 지정한다. 키 속성으로 매핑한 필드는 기본으로 검색에 쓰인다

키 속성이 없는 텍스트 필드도 searchable을 켤 수 있다. indexable은 필터·부스팅·패싯 등에 쓰는 별도 설정이므로, 그 이름만 보고 본문 검색 여부를 판단하면 안 된다. 키 속성으로 매핑한 필드는 기본 indexable·searchable이고 더 높은 가중치를 받지만, 반환할 원문 필드와 애플리케이션의 전달 코드는 다시 확인해야 한다. 필드 설정 (새 탭에서 열림) · 스키마 제공 (새 탭에서 열림)

예를 들어 원인 메모가 검색에는 쓰이지만 반환되지 않는다면, 검색 서비스는 그 메모를 참고해 기록을 고를 수 있다. 그러나 제목만 받은 모델은 ‘반납 자료 상태 확인 요청’이라는 말에서 파손 원인을 확인할 수 없다. 기록을 찾는 데 성공한 것과 답변의 근거를 확보한 것이 다른 이유다.

문의 내용에 원인과 조치 메모를 더했다

변경 전에는 제목을 title, 문의 내용을 description으로 매핑했다. 원인 메모와 조치 메모에는 키 속성 매핑도, Searchable 설정도 없었다.

두 메모를 각각 description에 연결하려 했을 때는 콘솔이 복수 매핑을 거부하는 오류를 직접 확인했다. 그래서 검색용 뷰에서 세 필드를 하나로 합치고 그 필드를 설명 본문으로 매핑했다. 아래는 실제 업무 값을 담지 않은 구성 예시다.

text
변경 전
description ← "반납한 책의 상태를 확인해 주세요"

변경 후
description ← SEARCH_TEXT
문의: 반납한 책의 상태를 확인해 주세요
원인: 반납함 안에서 책이 눌려 표지가 파손됨
조치: 수거 주기를 조정하고 완충재를 점검함

제목은 그대로 title에 연결했다. 본문을 합칠 때는 HTML 태그 같은 잔재도 정리했다. 예시에서 달라진 것은 검색 서비스에 주는 설명이다. 이전에는 문의한 사람의 요청만 있었지만, 변경 후에는 담당자가 확인한 원인과 조치도 함께 있다. 위의 ‘문의·원인·조치’ 표시는 각 문장의 출처를 읽기 쉽게 붙인 예시이며 실제 저장 형식을 그대로 옮긴 것은 아니다.

지금 다시 보면 원인·조치 메모를 별도 필드로 유지하고 Searchable을 켜는 방법도 있다. 하나로 합치면 검색용 본문 한 곳을 구성하고 반환할 수 있다. 필드를 나누면 원인과 조치를 각각 검색·반환하도록 설정할 수 있다. 다만 어느 방법이 더 잘 찾는지는 같은 질문과 기록으로 비교해야 한다. 이 사례에서는 두 방법을 비교 측정하지 않았다. 키 속성 가중치를 고려해 당시 통합을 선택했다는 뜻도 아니다.

복수 매핑 거부는 당시 콘솔에서 본 동작이다. 2026년 9월 15일 확인한 필드 설정·스키마 제공·스키마 갱신 문서에서는 개수 제한을 찾지 못했다. 따라서 다른 시스템에서도 반드시 본문을 합쳐야 한다고 일반화할 수는 없다. 필요한 필드와 접근 권한을 정한 뒤 구성을 고르고, 실제 적재 문서와 스키마에 반영됐는지 확인해야 한다. 원본 뷰만 바꾼 상태와 검색 서비스가 새 본문을 사용하는 상태를 같은 것으로 보면 안 된다.

검색된 뒤에도 근거 문장이 남아야 한다

검색 범위를 넓혀도 모델이 그 내용을 읽는지는 따로 봐야 한다. 당시 도구에는 질의를 여러 표현으로 넓히고 결과 건수를 늘리는 보완책이 있었다. 늘어난 결과를 모델에 전달할 때는 일부 요약용 필드만 남겼다. 그런데 그 응답에는 담당자가 남긴 원인·조치 메모가 없었다.

그래서 통합 본문의 앞 200자를 발췌 필드로 추가 반환했다. 검색 결과를 짧게 정리하면서도 본문의 일부를 모델에게 주는 방식이다. 통합 본문과 이 발췌는 현재도 사용하는 설계다.

앞의 짧은 문의라면 200자 안에 원인과 조치까지 들어간다. 이제 문의 내용에 반납 경위와 확인 요청이 길게 적혀 있다고 해보자. 합치는 순서는 같아도 원인 메모가 뒤로 밀린다. 발췌 길이는 그대로인데, 필요한 문장의 위치가 달라진다.

같은 기록의 상태 검색에 사용할 본문 모델에게 주는 앞 200자
문의가 짧다 문의 + 원인 + 조치 원인 문장까지 포함할 수 있다
문의가 200자를 넘는다 문의 + 원인 + 조치 앞의 문의만 남고 뒤의 원인은 빠진다

두 번째 경우에도 검색 본문에는 원인 문장이 있다. 이 기록이 검색 결과에 잡혔다면, 검색 결과 수를 더 늘리는 것으로 이미 반환된 기록의 발췌를 고칠 수는 없다. 애플리케이션이 최종 응답에 무엇을 넣는지를 봐야 한다. 이 표는 설명용 반례이며 현재 시스템의 동일한 장애를 재현한 결과는 아니다.

누락을 확인했다면 답에 필요한 원인·조치 필드를 골라 반환하거나, 질문과 관련된 부분을 발췌하거나, 상세 조회로 필요한 내용을 더 읽는 방법을 검토할 수 있다. 발췌 길이를 늘리는 것도 한 방법이지만 다음 기록은 더 길 수 있다. 모든 본문을 무조건 보내기보다 질문에 필요한 근거와 허용된 반환 범위를 함께 정해야 한다. 이 대안들을 현재 시스템에 적용하거나 성능을 비교한 것은 아니다.

직접 해보기: 같은 근거 문장을 끝까지 따라가기

아래 실험은 앞의 가상 기록 한 건을 사용한다. 대상 기록이 검색 결과에 있다는 조건을 고정하고, 본문 구성·필드 반환·발췌를 바꿨을 때 원인 문장이 어디까지 전달되는지만 계산한다. 실제 검색 엔진의 매칭이나 순위, 모델의 답변을 흉내 내는 실험은 아니다.

먼저 관문 1 메모를 검색 본문에 포함에서 ‘넣는다’를 골라 보자. 원본에 있던 문장이 검색 본문과 모델 입력에 나타난다. 그 상태에서 문의 내용을 길게를 켜면, 검색 본문에는 남아 있는데 앞 200자 발췌에서는 사라진다. 관문 3에서 발췌 길이를 늘리거나 ‘파손’이 있는 줄을 고르면 무엇이 달라지는지도 확인할 수 있다. 줄 선택은 차이를 보여주기 위한 문자열 규칙이다. 실제 제품의 의미 발췌 기능을 재현한 것은 아니다.

직접 해보기

원인 문장은 모델까지 갈 수 있을까

담당자가 적은 원인 문장(왼쪽의 주황 원)이 기록에서 모델 입력까지 관문 세 개를 지납니다. 먼저 관문 1넣는다로 바꿔 보세요. 원인 문장이 어느 관문에서 빠지는지 바로 보입니다.

대상 기록은 검색 결과에 있다고 가정합니다. 원인 문장의 전달 여부만 계산하며, 검색 성공·순위·답변 품질은 판정하지 않습니다.

장면 재현

기록

담당자가 적은 것원인 있음
제목: 반납 자료 상태 확인 요청
문의: 반납한 책의 상태를 확인해 주세요
원인: 반납함 안에서 책이 눌려 표지가 파손됨
조치: 수거 주기를 조정하고 완충재를 점검함
문의 18자 · 원인과 조치는 그대로입니다.

관문 1 메모를 검색 본문에 포함여기서 빠짐

검색 서비스는 우리가 준 본문만 읽습니다. description에 매핑해 검색에 쓰는 본문입니다.

검색 서비스

색인한 본문원인 없음
문의: 반납한 책의 상태를 확인해 주세요
(원인·조치 메모 없음)

관문 2 본문 원문을 응답에 포함

retrievable에 해당합니다. ID·제목은 항상 반환한다고 가정합니다.

애플리케이션

응답에서 받은 것원인 없음

id + title + SEARCH_TEXT (22자)

문의: 반납한 책의 상태를 확인해 주세요
설명용 응답 펼치기
{
  "id": "LIB-001",
  "title": "반납 자료 상태 확인 요청",
  "SEARCH_TEXT": "문의: 반납한 책의 상태를 확인해 주세요"
}

관문 3 모델에게 줄 발췌

반환된 본문의 첫 글자부터 정한 글자 수까지 보냅니다.

원인 문장은 본문에 없음 · 본문 22자

문의 내용원인 문장모델에게 보내는 구간

모델 입력

모델이 실제로 받는 문장원인 없음
문의: 반납한 책의 상태를 확인해 주세요

발췌 끝 · 22자 전달 · 고정한 ID·제목을 더해 모델에게 보냅니다.

관문 1에서 빠졌습니다 — 검색할 본문을 만들 때 원인 메모가 빠졌습니다.

발췌 길이를 늘려도 이 본문에는 원인 문장이 없습니다. 원본에는 있으므로, 적재할 필드와 Searchable 설정부터 확인할 상황입니다.

다른 상황에도 적용해 보기

검색 서비스 응답에는 원인 문장 전체가 있습니다. 그런데 모델에게 보낸 응답에는 제목만 있습니다. 먼저 확인할 곳은 어디일까요?

관문 2에서 ‘안 돌려준다’를 고르면 발췌 방법을 바꿔도 원인 문장을 가져올 수 없다. 이때는 발췌보다 앞의 반환 필드를 봐야 한다. 실험에서 원인 문장이 끝까지 남았더라도 확인한 것은 그 문장의 전달 여부다. 모델이 그 근거를 적절히 사용해 답하는지는 실제 답변에서 별도로 확인한다.

실제 데이터에서는 빠진 단계를 먼저 고른다

내 시스템에서 같은 증상을 조사한다면, 관련 있다고 판단한 기록 ID 하나와 판단의 근거 문장 하나를 고른다. 모델의 최종 답변만 읽지 말고 검색 서비스의 원래 응답과 모델에게 보낸 도구 응답을 나란히 확인한다. 두 응답을 구분해야 필드가 서비스에서 빠졌는지, 애플리케이션이 제외했는지 알 수 있다.

확인한 상태 다음에 확인할 곳
기대한 기록이 검색 결과에 없다 적재된 본문·Searchable 설정·검색 요청·필터·순위를 확인한다. 근거가 본문에 없으면 해당 필드가 적재 대상에 포함됐는지 먼저 확인한다
서비스 응답에는 있지만 합친 목록에서 빠진다 질의별 결과 목록과 병합 뒤 목록을 비교한다. 중복 제거·재정렬·건수 제한을 확인한다
기록은 반환됐지만 서비스 응답에 원인 메모가 없다 원문 필드의 Retrievable 설정과 요청·응답 구성을 확인한다
서비스 응답에는 원인이 있지만 모델 입력에는 없다 애플리케이션의 필드 선택과 발췌 구간을 확인한다
모델 입력에도 원인이 있는데 답변이 틀린다 어떤 근거를 사용해 답했는지 본다. 문장 전달만으로 답변 품질까지 검증된 것은 아니다

이 사례에서는 여러 질의의 결과를 합치는 방식도 바꿨다. 문서별 반환 점수의 최댓값으로 정렬하던 방식에서, 각 목록의 순위를 활용하는 RRF로 바꾼 것이다. 이는 여러 목록에서 문서의 순서를 정하는 변경이다. 이미 선택한 문서의 원인 메모가 응답에서 잘리는 문제는 별도로 고쳐야 한다. 두 변경을 한 번에 평가하면 무엇 때문에 결과가 달라졌는지 구분하기 어렵다.

고칠 단계를 골랐다면 같은 질문·필터·대상 기록으로 전후를 비교한다. 적재나 스키마를 바꾼 경우에는 검색 서비스에 반영된 뒤 확인한다. 원본 수정 시각과 검색 결과를 관측한 시각도 남겨야 아직 반영되지 않은 상태를 수정 실패로 오해하지 않는다. 스키마 변경과 재색인의 관계는 스키마 갱신 문서 (새 탭에서 열림)를 확인할 수 있다.

처음 놓친 질문만 확인하면 다른 질문에서 생긴 변화를 놓칠 수 있다. 원래 잘 찾던 질문도 함께 비교하면서, 기록 ID와 순위, 전달된 근거 문장, 실제 답변을 따로 남긴다. ‘기록을 찾았다’와 ‘그 근거로 답했다’를 하나의 성공 표시로 합치지 않는 것이 중요하다.

이 글에서 다룬 것은 2026년 8월의 구현 변경이며, 변경 뒤 검색 품질의 개선 폭은 측정하지 않았다. 여기서 가져갈 수 있는 것은 진단 방법이다. 놓친 기록을 하나 열고, 관련 있다고 판단한 바로 그 문장을 검색 본문과 두 응답에서 찾아보면 다음에 확인할 설정과 코드를 좁힐 수 있다.

참고

필드 설정·스키마 문서는 2026년 9월 15일 재확인했다. 제품 사양 설명과 당시 구현·관찰, 가상 실험을 구분했다.