← 블로그

· MCP, RAG, 개발

사내 지식 MCP 서버 구현기 1편: 에이전트가 E-GENE를 이해하기까지

입사 전부터 AI를 업무와 학습에 적극적으로 써 왔다. 입사 후에도 자연스럽게 E-GENE를 배우고 일하는 데 AI를 쓰기 시작했는데, 얼마 지나지 않아 한 가지 문제가 눈에 띄었다.

E-GENE에서만 쓰는 개념을 물으면 AI가 전혀 다른 의미로 이해하는 경우가 많았다. 예를 들어 "Form 만드는 법"을 물으면 한참을 찾다가 E-GENE의 Form이 아니라 HTML이나 React의 Form으로 받아들여 프론트엔드 답변을 내놓았다. field, task, 내장 함수처럼 E-GENE 안에서 별도의 의미를 갖는 용어들도 마찬가지였다.

더 큰 문제는 그다음이었다. 처음의 오해를 바로잡지 않으면 AI는 잘못된 전제 위에서 대화를 이어 갔다. 대화가 길어질수록 맥락은 점점 어긋났고, 결국 세션 전체가 엉뚱한 방향으로 흘러가기도 했다.

원인은 단순했다. AI의 학습 데이터에 E-GENE에 대한 정보가 없다. 그래서 이런 생각을 했다.

사내 지식을 에이전트가 활용할 수 있도록 연결할 수는 없을까?

처음에는 E-GENE 화면에 직접 붙는 챗봇으로 만들었다. 이후 구조를 단순화하면서, 답변을 직접 생성하는 대신 검색 결과와 근거만 돌려주는 MCP 서버로 바꿨다. 지금은 팀에서도 함께 쓰고 있다.

만드는 과정에서 예상보다 많은 것을 배웠다. 네 편으로 나눠 정리한다.

  1. 만들게 된 계기와 전체 구조, 실제 효용 (이 글)
  2. 검색 품질을 높이기 위해 적용한 방법
  3. 성능 평가 방법과 결과
  4. 운영 비용과 자동화

1. 먼저 데이터셋을 만들었다

상용 에이전트가 E-GENE를 이해하려면 먼저 답변의 근거가 될 지식이 필요했다. 사내 기술 커뮤니티의 Q&A, 공식 매뉴얼, 표준 DB 스키마 등을 모아 하나의 데이터셋으로 만들고, 임베딩해 벡터 데이터베이스에 저장했다.

질문이 들어오면 벡터 검색(Semantic Search)과 키워드 검색(BM25)을 함께 쓰는 하이브리드 검색으로 관련 문서를 찾는다. 그다음 검색 결과를 정제해 LLM에 컨텍스트로 넘긴다. 데이터를 어떤 기준으로 정제했는지, 검색 품질을 높이려고 무엇을 했는지는 2편에서 다룬다.

E-GENE에는 관리자 설정에서 확인할 수 있는 AI 설정 항목이 있다. 교육 기간에 우연히 이 메뉴를 발견했는데, 당시 개발용 패키지에서는 실제 AI 서비스와 연결돼 있지 않았다.

"이미 연결할 수 있는 구조가 있다면 직접 붙여 보면 되겠다."

그래서 단순한 챗봇 서버를 만들어 연결했다. 검색 결과를 Gemini의 컨텍스트로 넘겨 답변을 생성하게 했고, 개인 Gemini API 키만 등록하면 E-GENE 화면에서 바로 AI에게 질문할 수 있게 했다. 검색기와 프롬프트 구성, LLM 호출은 LangChain으로 연결했다. 기본적인 RAG 구조라 프로토타입은 비교적 빨리 완성됐다.

2. 비용과 성능의 한계

실제로 써 보니 두 가지 문제가 걸렸다.

첫째는 비용이다. 서버가 답변까지 직접 생성하려면 요청마다 LLM을 호출해야 한다. 성능이 좋은 모델은 비싸서 결국 상대적으로 저렴한 모델을 쓰게 되고, 그만큼 답변 품질은 낮아진다. 무엇보다 사용자가 늘수록 호출 비용도 함께 는다. 팀 단위로 배포할 생각을 하니 개인 사이드 프로젝트로 감당하기에는 부담이 컸다.

둘째는 답변 품질의 한계다. 서버가 생성한 답변을 평가해 보니 정답 일치 점수가 0.5 부근에서 좀처럼 오르지 않았다. 검색 로직을 고치고, 재시도 과정을 넣고, 프롬프트를 계속 조정해도 넘기 어려운 구간이 있었다. 모델별 평가 결과는 3편에서 다룬다.

특히 문제였던 것은 저렴한 모델이 주어진 근거를 무시하고 내용을 지어내는 경우였다. 실제로 존재하지 않는 컬럼명으로 SQL을 만든 적도 있다. 처음에 에이전트가 틀린 이유는 E-GENE를 몰라서였지만, 이제는 정확한 근거를 줘도 모델 성능 때문에 잘못된 답을 만드는 문제가 생긴 것이다.

LangChain을 운영하면서 느낀 부담도 있었다. 검색 품질을 높이고 데이터를 정제하는 과정에서 라이브러리의 추상화가 두껍다는 점이 불편했다. 답변이 이상할 때 검색 결과가 잘못된 것인지, 프롬프트 구성의 문제인지, 모델이 근거를 무시한 것인지 빠르게 구분하기 어려웠다. Ragas로 벤치마크를 구성하던 중에는 라이브러리 버전이 바뀌면서 의존성이 깨지는 일도 겪었다.

3. 답변은 각자의 에이전트에 맡긴다

그러다 한 가지 생각이 들었다. 나를 포함해 팀원들은 이미 Claude Code나 Codex 같은 상용 코딩 에이전트로 개발하고 있었다. 성능 좋은 모델을 이미 각자 쓰고 있는데, 굳이 내 서버가 저렴한 모델을 호출해 답변을 다시 만들 필요는 없었다.

확인하려고 서버에 붙였던 저가형 모델과 상용 에이전트에 같은 근거를 주고 답변을 비교했다. 블라인드 평가에서도 상용 에이전트의 성능이 분명히 높았다. 실제로 써 봐도 환각을 억제하는 능력과 주어진 근거로 문제를 푸는 능력에서 차이가 크게 느껴졌다.

그래서 역할을 나눴다. 답변 생성은 이미 쓰고 있는 상용 에이전트에 맡기고, 내 서버는 E-GENE에 관한 정확한 근거만 돌려주도록 구조를 바꿨다. LangChain으로 구성했던 챗봇 로직을 걷어 내고 MCP 서버로 다시 구현했다. 서버에서 답변 생성용 LLM을 부를 필요가 없어졌고, 서버의 LLM 호출 비용이 0원이 되면서 앞의 비용 문제도 함께 풀렸다.

구조를 나란히 놓으면 차이가 더 분명하다.

기존 구조
사용자 → 자체 챗봇 → 검색 → 저가 LLM → 답변

현재 구조
사용자 → 상용 에이전트 → MCP 검색 → 근거 반환 → 상용 에이전트가 답변

4. 개발할 때 무엇이 편해졌나

이제는 개발 중인 에이전트 화면에서 E-GENE 관련 내용을 그대로 물을 수 있다. 에이전트는 여러 문서에 흩어진 정보를 검색하고, 필요한 근거를 정리한 뒤 지금 작업 중인 코드와 대화 맥락에 맞춰 답한다.

개인화된 에이전트 설정과 기존 대화 맥락을 그대로 쓸 수 있다는 점도 체감 차이가 컸다. 모든 개념을 처음부터 다시 설명받는 대신, 이미 아는 내용 위에 필요한 정보만 이어서 이해할 수 있었다. 답변마다 커뮤니티 글이나 매뉴얼의 출처 링크가 함께 와서, 필요하면 원문을 바로 확인할 수도 있다.

검색 성능은 최적화를 거쳐 평가셋 38문항 기준 p50 약 0.57초, p95 약 0.64초다. 다만 이 수치는 검색 API 자체의 응답 시간이고, 에이전트가 답변을 생성하는 시간이나 네트워크 왕복은 포함하지 않는다. 질문에서 답변까지의 체감 시간은 쓰는 에이전트 쪽에 더 크게 좌우된다.

5. 어떤 도구를 제공하나

사용자가 도구를 직접 고를 필요는 없다. 질문을 입력하면 에이전트가 상황에 맞는 도구를 판단해 호출한다. 자주 쓰는 도구를 추리면 다음과 같다.

도구하는 일
lookup_schema테이블과 컬럼, 데이터 타입, 키 정보를 조회한다. 복잡한 조건으로 DB 도구에서 직접 테이블을 찾는 대신, 자연어로 물어 필요한 스키마 정보를 바로 확인한다.
search_knowledge커뮤니티 글, 매뉴얼, DB 스키마를 통합 검색한다. 질문 하나를 최대 세 가지 표현으로 확장해 검색하고, 결과와 출처 링크를 함께 돌려준다.
check_before_change코드를 고치기 전에 자주 하는 실수나 주의할 점을 확인한다. 에이전트가 반복해서 틀리는 사례를 따로 정리해 두었다. 4편에서 자세히 다룬다.
get_document / find_similar_documents검색된 문서의 원문을 보거나, 지금 문제와 비슷한 사례를 찾는다.

개인적으로 가장 자주 쓰는 도구는 lookup_schema다. 컬럼 길이 제한이나 키 제약 때문에 나는 오류는 테이블 정의를 정확히 확인하는 것만으로 원인의 절반 이상을 찾을 수 있다. 이제는 에이전트가 필요한 스키마를 직접 조회하니, 개발 흐름을 끊지 않고 문제를 확인할 수 있다.

6. 마무리

Claude Code에서는 명령어 한 줄로 MCP 서버를 등록할 수 있고, 다른 MCP 지원 에이전트에서도 비슷하게 연결할 수 있다. 다만 최근 Claude Code의 보안 정책이 강화되면서, 환경에 따라 등록할 때 추가 설정이 필요할 수 있다.

다음 편에서는 검색의 근거가 되는 데이터를 어떻게 구성했는지, 검색 품질을 높이려고 어떤 시도를 했는지 정리한다.