[ContentOps Agent #4] 학습 목표, 요구사항, Harness 회고

2026. 8. 16. 00:03ㆍPROJECT/Toy Project

[ContentOps Agent #4] 학습 목표, 요구사항, Harness 회고

1. 시작하며

앞선 세 글에서는 ContentOps Agent를 시작하기 전에 무엇을 배우고, 어떤 기능을 만들고, 어떤 방식으로 AI와 개발할지를 정리했다.

처음 프로젝트를 시작할 때 가장 중요하게 생각했던 것은 단순히:

Spring AI

pgvector

RAG

LangGraph

Agent

를 한 번씩 사용해보는 것이 아니었다.

오히려 다음과 같은 질문에 직접 답해보고 싶었다.

Vector Search만으로 충분할까?

Chunking을 바꾸면 검색 품질이 실제로 달라질까?

Exact Keyword 검색에서는 어떤 문제가 생길까?

정답 문서를 찾았는데도 LLM이 틀릴 수 있을까?

Prompt를 바꾸면 실제 품질이 좋아질까?

Agent는 질문에 맞는 Tool을 제대로 선택할까?

여러 Tool이 필요한 질문도 처리할 수 있을까?

그리고 이번 프로젝트에서는 이전 두 프로젝트와 달리 Evaluation Dataset을 개발 Harness 안에 처음부터 넣었다.

구현
 ↓
Test
 ↓
Evaluation
 ↓
실패 Case
 ↓
원인 분석
 ↓
대안 비교
 ↓
Human Gate
 ↓
개선
 ↓
동일 Dataset 재평가

Phase 9까지 모두 끝난 지금은 이 계획이 실제 개발 과정에서도 유지됐는지 확인해볼 수 있게 됐다.

결론부터 말하면 학습 목표와 Harness는 대체로 지켰다.

다만 처음 예상했던 기술을 모두 구현한 것은 아니다.

Hybrid Search

RRF

Reranker

LangGraph

는 최종 시스템에 들어가지 않았다.

중요한 것은 이것들을 넣어봤는데 효과가 없었다는 뜻이 아니다.

현재 Dataset에서 도입 근거가 충분히 나오지 않았기 때문에 비교 실험 자체를 하지 않았다.

이번 회고에서는 이 차이까지 포함해서 정리해보려고 한다.


2. 최종 시스템은 어떻게 만들어졌나

최종 ContentOps Agent의 Question 처리 흐름은 다음과 같다.

Question API
      ↓
LLM Tool Selection
      ↓
 ┌───────────────┬───────────────┬───────────────┐
 ↓               ↓               ↓
Policy Search  Content Search  Content Detail
 ↓               ↓               ↓
 └───────────────┴───────────────┘
                 ↓
          Answer + Sources

실제로 제공한 Tool은 다음 세 가지다.

search_policy_documents

search_contents

get_content_detail

정책이나 운영 가이드를 묻는 질문은 Vector Search를 이용하고,

"15세 콘텐츠 공개 정책 알려줘."

같은 질문은:

search_policy_documents

를 사용한다.

반면:

"이번 달 공개 예정 액션 콘텐츠 알려줘."

같은 질문은 콘텐츠 DB를 조회한다.

search_contents

그리고 특정 콘텐츠의 상태를 물으면:

get_content_detail

을 사용한다.

다만 여기서 중요한 점이 하나 있다.

최종 Question API의 Agent 흐름과 Retrieval Evaluation은 완전히 같은 경로가 아니다.

Retrieval Evaluation은 별도로:

Evaluation Dataset
       ↓
RetrievalService
       ↓
RagService
       ↓
Hit Rate / Recall / MRR

구조를 사용한다.

따라서 뒤에서 이야기하는:

Hit Rate@K

Recall@K

MRR

은 최종 Agent Question API 전체의 품질을 의미하는 값이 아니다.

정책 문서 Retrieval 자체를 평가한 결과다.

이 구분은 끝까지 유지해야 한다.


3. 가장 먼저 확인한 것은 Vector Search였다

Baseline은 최대한 단순하게 시작했다.

Document
   ↓
Chunk
   ↓
Embedding
   ↓
PostgreSQL + pgvector


Question
   ↓
Embedding
   ↓
Vector Search
   ↓
Top K
   ↓
LLM

초기 Retrieval 결과는 다음과 같았다.

Hit Rate@K = 0.9

Recall@K = 0.9

MRR = 0.75

유형별로 보면:

SEMANTIC
Hit = 1.0

EXACT_KEYWORD
Hit = 1.0

SIMILAR_DOCUMENT
Hit = 0.667

였다.

여기서 예상과 다른 결과가 하나 나왔다.

처음에는:

OPS-101

M-03

CONTENT_BLOCKED

처럼 정확한 문자열이 포함된 질문에서 Vector Search가 가장 먼저 실패할 것이라고 생각했다.

하지만 실제 Dataset에서는 Exact Keyword Query의 Document Hit가:

1.0

이었다.

즉:

Vector Search
↓
Exact Keyword를 못 찾음
↓
Keyword Search 필요

라는 처음 가정이 실제로는 재현되지 않았다.

반대로 실제 Miss는 유사한 문서가 경쟁하는 Query에서 발생했다.

특히 retrieval-010에서는 정답 문서 대신 FAQ 성격의 유사 문서가 검색됐다.

결국 이번 Corpus에서 확인한 첫 번째 중요한 점은:

Vector Search의 약점이 내가 예상한 위치에서 반드시 나타나는 것은 아니다.

라는 것이었다.


4. Chunking을 바꾸면 결과가 달라질까?

다음으로 Chunking 전략을 비교했다.

Baseline에서는:

Fixed Chunk

500 / 50

구조를 사용했다.

이후:

Fixed 500 / 50

Section 기반

Fixed 200 / 20

을 같은 Dataset으로 비교했다.

처음에는 Chunk Size를 바꾸면 Retrieval 결과도 어느 정도 달라질 것이라고 예상했다.

하지만 실제 결과에서는:

Hit Rate

Recall

MRR

이 모두 동일했다.

이유를 확인해보니 Sample Document 자체가 비교적 짧고 Section 단위도 작았다.

결과적으로 Chunking 전략을 바꿔도 실제로 만들어지는 검색 단위가 크게 달라지지 않았다.

그래서 최종적으로는 Baseline Chunking을 유지했다.

여기서 중요한 점은:

Chunking을 실험했다.
↓
새로운 Chunking을 적용했다.

가 아니었다.

실제로는:

Chunking 후보 비교
       ↓
Metric 동일
       ↓
기존 구조 유지

였다.

이번 프로젝트에서 계속 유지하려고 했던:

변경 자체를 개선으로 판단하지 않는다.

는 원칙이 그대로 적용된 경우였다.


5. Hybrid Search, RRF, Reranker는 왜 안 넣었나

처음 학습 목표에는 다음 기술들이 있었다.

Keyword Search

Hybrid Search

RRF

Reranker

그런데 최종 코드에는 하나도 들어가지 않았다.

가장 큰 이유는 문서 단위 Retrieval에서 도입 근거가 나오지 않았기 때문이다.

Exact Keyword Query는 이미:

Hit = 1.0

이었고,

정답 문서가 검색되기는 하지만 계속 낮은 순위에 머무는 Document Ranking Failure도:

0건

이었다.

Hit된 Query에서 정답 문서의 최저 Rank 역시:

2

였다.

이 상황에서:

RAG 포트폴리오니까 Hybrid Search를 넣는다.

RRF도 넣는다.

Reranker도 넣는다.

고 하면 처음 세운 원칙과 오히려 맞지 않는다.

그래서 이 기술들은 후보에서 멈췄다.

다만 이것을:

Hybrid Search는 필요 없다.

RRF는 효과가 없다.

Reranker는 쓸모없다.

라고 말할 수는 없다.

비교하지 않았기 때문이다.

정확하게 말하면:

현재 Sample Corpus의 문서 단위 Retrieval에서는 이를 도입할 정도의 실패가 확인되지 않았다.

정도다.

이 부분은 학습 목표 측면에서는 조금 아쉽기도 했다.

처음에는 실제로 Hybrid Search나 Reranker까지 적용해서 Before / After를 직접 보고 싶었기 때문이다.

결국 이번 Dataset은 그런 기술을 학습하기에는 너무 쉬웠던 측면이 있다.

예를 들어 다음처럼 Dataset을 조금 더 어렵게 구성했다면 다른 결과가 나왔을 수도 있다.

Code가 제목이 아니라 본문 깊숙한 곳에만 존재

정답 문서가 Rank 4~6에 위치

서로 거의 같은 표현을 사용하는 여러 문서

하나의 문서가 매우 길어 여러 Chunk로 크게 나뉨

다음에 같은 주제를 다시 한다면 이 부분은 개선할 수 있을 것 같다.


6. 문서를 찾는 것과 답을 잘하는 것은 달랐다

이번 프로젝트에서 가장 명확하게 확인한 것 중 하나다.

Retrieval 결과가:

Hit Rate = 0.9

라고 해서 Answer 정확도 역시 0.9인 것은 아니었다.

실제로는 다음처럼 실패 위치를 나눌 필요가 있었다.

정답 문서를 찾지 못함
→ Retrieval Failure


정답 문서는 찾음

하지만 필요한 Section이 없음
→ Context / Section Failure


필요한 Context까지 있음

하지만 답변에서 누락
→ Generation Failure


Context에 없는 내용을 생성
→ Grounding Failure

실제 Query에서도 이 차이가 나타났다.

retrieval-010
→ 유사 문서가 정답 문서를 밀어냄

002 / 003 / 009
→ 정답 Document는 Hit
→ 하지만 필요한 Section이 Context에 없음

006
→ 필요한 Section은 존재
→ 하지만 Answer가 핵심 내용을 빠뜨림

이 결과를 보면서 RAG를:

Retrieval + LLM

하나의 Black Box로 보면 안 된다는 것을 직접 확인할 수 있었다.

Answer가 틀렸다고 해서 항상 Prompt를 고칠 문제도 아니었다.


7. No Answer는 생각보다 잘 작동했다

Evaluation Dataset에는 일부러 답이 존재하지 않는 질문도 넣었다.

예를 들어:

담당자가 누구인가?

매출이 얼마인가?

처럼 현재 문서에 존재하지 않는 정보다.

이 Query를 제거하거나 Expected Answer를 수정하지 않았다.

최종적으로 011, 012 Query에서는 LLM이 존재하지 않는 담당자나 매출 데이터를 만들어내지 않고 답변을 거절했다.

현재 제공된 문서에서는 확인할 수 없습니다.

이 결과는 이번 프로젝트에서 중요했다.

RAG 시스템에서는:

무조건 답변 생성

보다:

근거가 없으면 답하지 않음

이 더 올바른 결과일 수 있기 때문이다.

따라서 No Answer Query를 실패 Dataset으로 지우지 않고 끝까지 유지했다.


8. Source를 제공했지만 Citation이라고 하기는 어렵다

Question API의 Response에는 Source를 같이 내려준다.

Answer
+
Sources

구조 자체는 구현했다.

하지만 현재 Source는 LLM이 실제 답변을 만들 때 어떤 문장을 사용했는지 추적한 Citation은 아니다.

Retriever가 반환한 Top K Document Metadata를 내려주는 방식이다.

즉:

Retriever가 검색한 문서
=
Source 목록

이다.

따라서 LLM이 실제 답변에 사용하지 않은 문서도 Source에 포함될 수 있다.

처음 목표는 운영자가:

이 답변이 어느 문서를 근거로 했는가?

를 확인할 수 있게 만드는 것이었다.

현재 구현은 그 방향에는 가깝지만:

Answer Claim
↔
Source

를 1:1로 연결한 구조는 아니다.

그래서 Grounding과 Citation 측면에서는 아직 개선할 부분이 남아 있다.


9. Prompt를 강화하면 정말 좋아질까?

Prompt 역시 감으로 수정하지 않고 비교해봤다.

Prompt A
vs
Prompt B

형태로 동일 Dataset을 이용했다.

Prompt B에는 Grounding과 No Answer 관련 규칙을 더 강하게 넣었다.

처음에는:

규칙 강화
↓
Hallucination 감소
↓
답변 품질 증가

를 기대했다.

하지만 실제 결과는 그렇게 단순하지 않았다.

목표로 했던 Failure가 해결되지 않았고 Token과 Latency는 오히려 증가했다.

LLM Latency는:

2012ms

↓

3275ms

로 증가했다.

Completion Token도:

353

↓

1063

으로 늘었다.

즉 Prompt가 길어지고 규칙이 많아졌다고 해서 반드시 더 좋은 Prompt가 되는 것은 아니었다.

이번에도:

Prompt가 더 자세하다.
=
품질이 더 좋다.

라고 볼 수 없었다.

Prompt 역시 코드와 마찬가지로 Evaluation 대상이라는 것을 직접 확인했다.


10. Agent 단계에서는 또 다른 문제가 나타났다

RAG 평가 이후에는 콘텐츠 DB Tool을 추가했다.

최종 Agent에는:

search_policy_documents

search_contents

get_content_detail

세 Tool을 제공했다.

그리고 Evaluation Dataset에는 다음 유형의 질문을 넣었다.

Policy Only

Content Search

Content Detail

Multi Tool

결과는:

Tool Set Accuracy
0.625

Sequence Accuracy
0.375

였다.

세부 결과는:

MATCH
3

WRONG_ORDER
2

MISSING_TOOL
3

WRONG_TOOL
0

였다.

여기서 재미있는 점은:

WRONG_TOOL = 0

이라는 것이다.

Agent가 완전히 엉뚱한 Tool을 선택하는 문제는 없었다.

하지만 그렇다고 Tool Selection이 잘 됐다고 말할 수는 없다.

특히 여러 Tool이 필요한 질문에서 문제가 발생했다.


11. Multi Tool 요구사항은 끝까지 해결하지 못했다

예를 들어 요구사항에는 다음 질문이 있었다.

"콘텐츠 100번이 왜 공개되지 않는지
정책 기준으로 설명해줘."

기대하는 흐름은:

get_content_detail
        ↓
현재 콘텐츠 상태 확인

+

search_policy_documents
        ↓
관련 정책 확인

↓

두 결과 조합

이었다.

하지만 실제 tool-007, tool-008에서는 Agent가:

get_content_detail

만 호출했다.

즉 콘텐츠 상태는 확인했지만 정책 검색을 이어서 호출하지 않았다.

이건 요구사항을 글자 그대로 보면 미달이다.

여기서 Evaluation Dataset을:

Content Detail 하나면 정답

으로 바꾼다면 Tool Selection Accuracy는 올라갈 수 있다.

하지만 그렇게 하지 않았다.

처음 정의한 Expected Tool을 그대로 유지했다.

결과적으로:

MISSING_TOOL

Failure가 최종 결과에도 남았다.

이번 프로젝트에서 Evaluation Dataset을 Harness로 사용한다는 원칙을 가장 잘 보여준 부분 중 하나라고 생각한다.


12. 그렇다면 LangGraph를 넣어야 했을까?

Multi Tool 실패가 확인됐기 때문에 LangGraph는 충분히 후보가 될 수 있었다.

예를 들어:

Question
 ↓
Tool 호출
 ↓
결과 충분?
 ├─ YES → Answer
 │
 └─ NO
      ↓
   추가 Tool

처럼 명시적인 State 기반 Workflow를 만들 수 있다.

하지만 한 번의 Agent Evaluation 결과만 보고 바로 LangGraph를 추가하지는 않았다.

현재 문제는:

잘못된 Tool 선택

이라기보다:

첫 Tool 이후 필요한 후속 Tool을 호출하지 않음

이었다.

Human Gate에서는:

단순 Tool Calling 유지

vs

Workflow 명시

vs

LangGraph

를 후보로 두고 검토했다.

최종적으로 이번 프로젝트에서는 단순 Tool Calling을 유지하고 Multi Tool 실패를 한계로 남겼다.

따라서:

LangGraph가 필요 없었다.

라고 단정하기보다,

LangGraph를 도입할 가능성은 확인했지만, 현재 실험 하나만으로 구조를 추가하기에는 근거가 부족하다고 판단해 보류했다.

가 더 정확하다.


13. 요구사항은 얼마나 지켰을까?

전체적으로는 핵심 요구사항 대부분을 구현했다.

Question API

Document Loading

Chunking

Embedding

pgvector

Vector Search

RAG Answer

Source

Retrieval Evaluation

Generation Evaluation

Content Search

Content Detail

Tool Selection Evaluation

까지 구현했다.

반면 완전히 만족하지 못한 부분도 있다.

가장 명확한 것은 Multi Tool이다.

FR-20
여러 Tool 조합

→ 미달

그리고 Tool Selection도 완전하지 않다.

Tool Set Accuracy
0.625

이기 때문이다.

Source 역시 제공은 하지만 실제 답변 Claim과 Source를 1:1로 연결한 Citation은 아니다.

Agent Workflow State도 LangGraph 같은 명시적 State Model을 사용하지 않았다.

그래서 이번 프로젝트를:

요구사항을 모두 완벽히 만족했다.

라고 쓰는 것보다는,

최소 RAG와 콘텐츠 Tool, Evaluation 구조는 구현했지만 Multi Tool과 Grounding 일부는 해결되지 않은 상태로 끝났다.

라고 정리하는 것이 맞다.


14. Harness는 이번 프로젝트에서 가장 잘 작동한 부분이었다

#3에서는 이번 프로젝트의 개발 방식 자체를 하나의 실험처럼 가져가려고 했다.

구조는 다음과 같았다.

Docs
+
Project Rule
+
Test
+
Evaluation
+
Experiment
+
Git

이 방식은 실제로 꽤 잘 작동했다.


14.1 Test와 Evaluation을 분리했다

./gradlew test가 통과했다고 해서:

Retrieval이 좋다.

고 판단하지 않았다.

Test
→ 코드가 정상적으로 동작하는가?


Evaluation
→ AI 결과의 품질은 어떤가?

를 분리했다.

이 구분 덕분에:

코드는 정상

하지만

Hit Rate = 0.9

같은 상태를 자연스럽게 받아들일 수 있었다.


14.2 Evaluation Dataset을 구현에 맞춰 바꾸지 않았다

이번 프로젝트에서 가장 지키고 싶었던 원칙이었다.

끝까지 실패한:

retrieval-010

tool-007

tool-008

을 Dataset에서 지우지 않았다.

Expected Document나 Expected Tool을 실제 구현 결과에 맞게 변경하지도 않았다.

결과적으로 최종 Metric이 완벽하지는 않지만 어떤 문제가 남아 있는지를 그대로 설명할 수 있다.


14.3 기술 도입의 Gate로 Evaluation을 사용했다

이번 프로젝트의 기술 선택은 대략 다음과 같이 이루어졌다.

Exact Keyword Document Miss 없음
→ Keyword Search 미도입


Document Ranking Failure 0
→ Reranker 미도입


Chunking 전략 변경
→ Metric 동일
→ Baseline 유지


Multi Tool 실패
→ Workflow 후보 검토
→ LangGraph Human Gate
→ 보류

Phase에 도달했다는 이유로 기술을 자동으로 넣지 않았다.

이 점은 처음 정했던 개발 방식과 가장 잘 맞았다.


15. 이번에는 Skill이 아니라 Rule로 시작했다

Live Event Stream 회고에서:

SKILL.md라고 만들었지만
실제로는 항상 적용되는 Rule에 가깝다.

는 결론을 냈었다.

그래서 이번 프로젝트에서는 처음부터:

.cursor/
└── rules/
    └── content-ops-agent.mdc

구조를 사용했다.

Rule에는:

현재 Phase를 넘어가지 않는다.

Evaluation 전에 최적화하지 않는다.

기술을 미리 도입하지 않는다.

Evaluation Dataset을 임의로 변경하지 않는다.

중요한 구조 변경은 Human Gate를 거친다.

같은 제약을 넣었다.

이 부분은 실제로 잘 작동했다.

다만 #3에서는 반복 Workflow가 발견되면 Skill로 분리해볼 수도 있다고 했다.

실제로:

Evaluation

↓

실패 Query 분류

↓

Experiment Markdown 정리

작업은 Phase마다 반복됐다.

하지만 끝까지 별도의 Skill로 분리하지는 않았다.

Rule 하나로도 프로젝트 진행에는 문제가 없었다.

따라서:

Rule → 반복 Workflow 발견 → Skill 추출

이라는 방식 자체는 아직 실제로 검증해보지는 못했다.


16. README는 이번에는 처음부터 관리했다

이전 프로젝트에서 반복적으로 아쉬웠던 것이 README였다.

Playback Gate와 Live Event Stream에서는 실행 방법과 검증 방법을 프로젝트 후반에 많이 보강했다.

이번에는 Phase 1부터 다음 내용을 README에 넣었다.

프로젝트 실행 방법

PostgreSQL + pgvector 실행 방법

Document 적재 방법

Question API 호출 방법

Test 실행 방법

Evaluation 실행 방법

이번에는 README를 마지막 정리 문서가 아니라 Harness의 진입점으로 사용한다는 목표를 비교적 잘 지켰다.


17. Git은 문제 중심 History를 만들었다

Commit도 가능하면 문제 중심으로 남겼다.

대략적인 흐름은 다음과 같다.

feat: implement baseline vector rag

experiment: evaluate baseline retrieval

experiment: analyze chunking quality

experiment: evaluate exact keyword retrieval

experiment: analyze ranking quality

experiment: evaluate generation grounding

feat: add content data tools

experiment: evaluate tool selection

experiment: compare baseline and final

Live Event Stream에서 문제 재현과 해결 Commit이 충분히 분리되지 않았던 점을 의식했지만, 이번에는 조금 다른 상황이었다.

예를 들어 처음 예상한:

Exact Keyword Retrieval Miss

가 실제로 발생하지 않았다.

그러니:

experiment: reproduce exact keyword retrieval misses

↓

feat: add hybrid retrieval

같은 History도 자연스럽게 만들어지지 않았다.

이건 원칙을 어긴 문제라기보다 가정했던 문제가 실제로 발생하지 않은 결과에 가깝다.

다만 Prompt 변경의 경우에는 변경과 재평가가 비교적 가까운 흐름으로 남아 있어, 문제 재현 → 변경 → 재평가를 Git에서 완전히 분리했다고 보기는 어렵다.


18. 예상과 가장 달랐던 것

프로젝트를 시작하면서 예상했던 것과 실제 결과를 비교해보면 꽤 차이가 있었다.

1. Exact Keyword가 Vector Search의 첫 번째 약점일 줄 알았다

실제로는:

EXACT_KEYWORD Hit = 1.0

이었다.

가장 먼저 나타난 Miss는 유사 문서 경쟁이었다.


2. Chunk Size를 바꾸면 검색 결과가 달라질 줄 알았다

실제로는 Sample Section 자체가 작아서 세 Chunking 전략의 Metric이 같았다.

500 / 50

Section

200 / 20

↓

Metric 동일

3. Prompt를 강화하면 답변 품질이 좋아질 줄 알았다

목표 Failure는 해결되지 않았고:

LLM Latency

2012ms
→
3275ms

Completion Token은:

353
→
1063

으로 증가했다.

더 긴 Prompt가 더 좋은 Prompt는 아니었다.


4. Agent 단계에서는 LangGraph를 사용하게 될 줄 알았다

실제 문제는 Wrong Tool이 아니었다.

WRONG_TOOL = 0

대신:

MISSING_TOOL

WRONG_ORDER

가 문제였다.

그래서 LangGraph를 후보로 검토했지만 이번 프로젝트에서는 보류했다.


5. Final Retrieval Metric이 Baseline보다 좋아질 줄 알았다

실제로는:

Baseline

Hit Rate 0.9
Recall   0.9
MRR      0.75


Final

Hit Rate 0.9
Recall   0.9
MRR      0.75

로 동일했다.

최종 시스템에서 달라진 것은 Retrieval 성능 자체보다:

콘텐츠 Database Tool

Agent Tool Selection

이라는 처리 범위의 확장이었다.


19. 지금 남아 있는 한계

Phase 9까지 끝났지만 최종 시스템에는 여전히 한계가 있다.

SIMILAR_DOCUMENT Query 1건 Retrieval Miss

Document는 Hit하지만 정답 Section이 없는 Query 존재

정답 Context가 있어도 Answer에서 누락되는 Generation Failure 존재

Source가 실제 Claim 단위 Citation은 아님

Tool Selection Accuracy 0.625

Multi Tool Query에서 필요한 Tool 누락

단순 Tool Calling만 사용

Question API와 Retrieval Evaluation 경로가 다름

특히 다음 두 가지는 중요하게 남았다.

첫 번째는 Retrieval Metric과 최종 Agent 품질을 같은 숫자로 설명하면 안 된다는 것이다.

Hit Rate 0.9

은 Policy Retrieval 평가 결과이지 Agent 전체 성공률이 아니다.

두 번째는 Multi Tool이다.

현재 Agent는 하나의 Tool을 고르는 것은 비교적 잘하지만:

한 번 Tool 호출
   ↓
결과 확인
   ↓
추가 Tool 필요 여부 판단

과 같은 다단계 Workflow는 아직 안정적으로 처리하지 못한다.

다음에 이 프로젝트를 확장한다면 이 부분이 가장 자연스러운 다음 실험이 될 것 같다.


20. 다음 프로젝트에서도 가져갈 것

이번 프로젝트를 통해 다음 원칙은 계속 가져가도 될 것 같다.

Evaluation Dataset은 구현보다 먼저 고정한다

구현이 실패함
↓
Dataset 수정

이 아니라:

실패 기록
↓
원인 분석

으로 간다.


기술 이름을 성과로 만들지 않는다

Hybrid Search 적용

RRF 적용

LangGraph 적용

가 성과가 아니다.

실제 실패를 어떤 방식으로 해결했는지가 더 중요하다.


실패가 안 나오면 기술을 억지로 넣지 않는다

다만 해당 기술 자체를 학습하고 싶다면 실패가 나타날 수 있는 Dataset을 설계할 필요가 있다.

이번 프로젝트에서는 이 부분이 가장 큰 학습 포인트 중 하나였다.


Retrieval, Generation, Agent 품질을 분리한다

정답 문서를 찾는 것

↓

정답을 생성하는 것

↓

적절한 Tool을 선택하는 것

은 서로 다른 문제다.

하나의 Metric으로 합치지 않는다.


Prompt 변경도 비용이 있다

Prompt를 수정하면 품질뿐 아니라:

Latency

Token

응답 길이

도 같이 확인한다.


Evaluation 경로와 실제 서비스 경로가 다르면 명시한다

현재 Retrieval Evaluation과 최종 Agent Question API는 다른 경로다.

이 차이를 숨기지 않는다.

다음 프로젝트에서는 가능하다면 둘을 더 동일한 계약으로 묶는 구조도 고민해볼 수 있을 것 같다.


21. 정리

ContentOps Agent를 시작할 때 가장 중요하게 생각했던 것은:

RAG를 만들어봤다.

LangGraph를 사용했다.

Reranker를 붙였다.

를 포트폴리오에 적는 것이 아니었다.

오히려:

어떤 질문에서 검색이 실패했는가?

왜 실패했는가?

문서를 못 찾은 것인가?

문서는 찾았지만 Context가 부족한 것인가?

Context는 맞는데 LLM이 틀린 것인가?

Agent가 Tool을 잘못 골랐는가?

아니면 필요한 Tool을 하나 빠뜨린 것인가?

를 구분해서 설명할 수 있는 것이 목표였다.

최종 Retrieval 결과는:

Hit Rate@K
0.9

Recall@K
0.9

MRR
0.75

였다.

Agent Tool Selection은:

Set Accuracy
0.625

Sequence Accuracy
0.375

였다.

그리고 최종 시스템에는 여전히:

유사 문서 Retrieval Miss

Generation Failure

Multi Tool Failure

가 남아 있다.

반대로 처음 생각했던:

Hybrid Search

RRF

Reranker

LangGraph

는 최종 시스템에 들어가지 않았다.

하지만 이번 프로젝트에서는 그것도 결과라고 생각한다.

Baseline
   ↓
Evaluation
   ↓
실패 Case
   ↓
원인 분석
   ↓
대안 비교
   ↓
Human Gate
   ↓
개선 또는 유지
   ↓
동일 Dataset 재평가

이 Loop를 지키다 보니 예상했던 기술을 모두 사용할 이유가 생기지 않았다.

다만 동시에 한 가지 한계도 알게 됐다.

기술을 억지로 넣지 않는 것과, 그 기술을 실제로 실험해서 이해하는 것은 다른 문제다.

Hybrid Search나 Reranker를 실제로 비교해보고 싶다면 다음에는 그 기술이 필요한 실패가 발생할 정도로 Dataset을 더 어렵게 설계해야 한다.

이번 프로젝트에서 가장 의미 있었던 것은 최종 Metric이 크게 올라간 것이 아니다.

처음에는:

RAG가 틀렸다.

정도로만 볼 수 있었던 문제를 이제는:

Retrieval Failure인지

Ranking Failure인지

Context 구성 문제인지

Generation Failure인지

Grounding Failure인지

Tool Selection 문제인지

구분해서 볼 수 있게 됐다는 점이다.

그리고 AI Coding Assistant를 사용하는 방식도 이전 프로젝트보다 조금 더 명확해졌다.

Docs로 범위를 제한하고

↓

Rule로 AI 행동을 제한하고

↓

Test로 동작을 검증하고

↓

Evaluation으로 품질을 측정하고

↓

Experiment로 결과를 남기고

↓

Human Gate에서 기술을 선택한다.

결국 이번 프로젝트에서도 이전 프로젝트부터 이어진 원칙은 같다.

문제를 직접 확인하고, 측정하고, 원인을 구분한 뒤 필요한 선택만 하고 그 결과를 다시 검증한다.

ContentOps Agent에서는 이 원칙을 처음으로 AI 시스템의 품질 평가까지 확장해볼 수 있었다.

반응형