Agentic RAG 상세 구현
이전 'AgenticRAG 만들기' 튜토리얼에서는 Agentic RAG의 개념과 그 필요성에 대해 알아보았습니다. 이번 튜토리얼에서는 실제 Agentic RAG 워크플로우를 단계별로 구축하는 구체적인 방법을 안내합니다.
목표
genos 환경에서 다음과 같은 기능들을 포함하는 Agentic RAG 워크플로우를 구현합니다.
RAG 여부 검사: 사용자의 질의를 분석하여 RAG를 수행할지 여부를 판단합니다.
Relevance Checker: 검색된 문서, 생성된 답변을 분석하여 사용자의 질문과 최종적으로 관련이 있는지 평가합니다.
Query Rewritte: 사용자의 질의를 분석하여 검색 문서를 더 잘 찾을 수 있도록 Query를 재작성합니다.
Loop Counter: 기준에 맞지 않는 답변 생성 혹은 문서 검색이 반복될 경우, 무한루프를 빠져나올 수 있도록 합니다.
1. 워크플로우 생성 및 기본 구성
먼저 Flowise에서 새로운 워크플로우를 생성하고, Agentic RAG를 구현하기 위한 기본 틀을 설정합니다.
새 워크플로우 생성: Genos의 에이전트 -> 워크플로우 -> 워크플로우 생성에서 새 워크플로우를 생성합니다.
Agentflow 생성: 본 튜토리얼에서는 Agnetflow 2.2.3 버전으로 Agentic RAG를 구현합니다.

2. 핵심 노드 구현하기
<목표>에서 작성된 Agentic RAG의 노드를 각각 설정합니다. Grader는 LLM Node를 활용하여 구성할 수 있으며, 평가 노드의 경우 System Prompt를 통해 평가 기준을 명확히 정의하는 것이 중요합니다.
2.0. 기본 세팅
Agentflow를 구성하기 전 기본적인 세팅에 대해 안내합니다.
사진과 같이 노드를 구성합니다.이는 Sequential Agentflow의 기본 시작 노드 구성입니다.

State에서는 다음과 같이 변수를 추가합니다.
Retrieved Documents
Replace
Default Value
Loopcnt
Replace
0
RewrittenQuery
Replace
보강된 질의가 없습니다. 사용자의 질문으로 검색하세요.
Answer
Replace
Default Value
2.1. RAG Checker
사용자의 질의를 분석해 RAG 여부를 판단합니다.
노드 추가: LLM Node를 추가하고
Document Grader
로 이름을 변경합니다.프롬프트 설정: 아래 예시와 같이 프롬프트를 작성하여 사용자 질문을 분석하도록 지시합니다.
[System Prompt]
당신은 RAG 어시스턴트의 질문 분석가(Query Type Classifier)입니다.
당신의 임무는 사용자의 질문을 분석하여, 단순한 질의인지 아니면 외부 문서 기반의 검색 및 추론(RAG)이 필요한 복합적인 질의인지 구분하는 것입니다.
🔍 분류 기준:
1. CHAT
- 상식, 간단한 설명, 계산, 정의, 개인 의견 등 단순 응답이 가능한 질문
- 일반적인 인사
예:
- "에펠탑 높이는?"
- "JSON이 뭐야?"
- "3 + 5는 뭐야?"
- "ChatGPT는 어떻게 작동해?"
- "안녕?"
- "너에 대해 소개해줄래?"
2. RAG
- 금융, 경제에 관한 질문
- 특정 문서 기반 정보가 필요한 질문
- 내부 DB 또는 문서 검색이 필요한 질의
- 정책, 보고서, 기업 문서, 실시간 정보, 문서 기반 비교 등
- 구체적인 정보를 요구하는 질의에는 반드시 RAG를 출력하세요.
예:
- "최근 우리나라의 물가 보고서에 대해 요약해줘"
- "보고서를 바탕으로 삼성전자 이익 추이 알려줘"
- "한국은행이 발표한 기준금리 인상 이유는?"
- "정보보호최고책임자는 어떤 경우에 지정해야 하나요?"
- "전자금융감독규정에 대해 알려주세요."
🛠 출력 형식:
- "RAG" 또는 "CHAT" 중 하나로만 응답하십시오.
- 다른 말은 하지 마십시오.
[Human Prompt]
사용자 질문 : {question}
2.2. Condition Node 설정 1
RAG Checker의 Output을 Input으로 받아 RAG의 여부를 결정합니다.
사진과 같은 설정을 통해 분기를 설정할 수 있습니다.

Chat으로 인계될 경우, LLM Node에 간단한 프롬프트로 질문에 대응할 수 있도록 설정합니다.

본 튜토리얼에서는
[System Prompt] 당신은 통합 어시스턴트입니다. 사용자의 질문에 친절하고 자세하게 답변하세요. [Human Prompt] 사용자 질문 : {question}
으로 구성하였습니다.
2.3. Retriever Use Node 및 Loopcnt 세팅
사용자의 질의가 RAG로 분기되었을 때의 노드 구성을 설정합니다.
노드 추가: 사진과 같은 구성으로 노드를 설정합니다.
프롬프트 설정: RAG Manager의 Prompt를 다음과 같이 설정합니다.
[System Prompt] 당신은 도구를 무조건 사용하는 에이전트입니다. 사용자의 질문에 자세히 대답하기 위해 RetrieverTool을 사용하여 사용자의 질문과 관련있는 참고 문서들을 검색하세요. Retrieval에는 사용자의 질문을 그대로 넣되, 만약 보강된 질의가 있다면 해당 질문으로 검색하세요. 절대 Retrieval을 통해 가져온 참고 문서를 바탕으로 직접 답변하지 말고 다음 Agent에게 해당 내용을 넘기세요. [Human Prompt] 사용자 질문 : {question} 보강된 질문 : {RewrittenQuery}
RewrittenQuery는 첫 시행시 '보강된 질의가 없습니다. 사용자의 질문으로 검색하세요.' 가 입력되고, 두번째 시행부터 보강된 질의가 입력됩니다.
Loopcnt 갱신 및 검색 결과 저장: 일반적으로는 Tool Node의 State 업데이트는 사진과 같이 진행됩니다.
하지만 이번 Case의 경우, Tool Node를 사용할 때마다 Loopcnt를 갱신해야 하기에 Code를 활용하여 State를 업데이트합니다.
const RetrievedDocuments = $flow.output[0].sourceDocuments; let cnt = Number($flow.state.Loopcnt); cnt = cnt + 1; return { Loopcnt: cnt, RetrievedDocuments: RetrievedDocuments };
위 코드를 통해 Tool Node가 한번 호출될 때마다 RetrievedDocument에 검색된 문서가 Replace 형태로 저장되며, cnt는 +1된 값이 저장됩니다.
2.4. LoopCondition
Loopcnt를 검사하여 횟수가 초과되었을 경우, Loop를 탈출하도록 분기합니다.

왼쪽 LoopCondition에 해당하는 Condition Node의 구성입니다. 코드에서 분기합니다.
const state = $flow.state; // 1. Loopcnt가 3이면 CantAnswer if (Number(state.Loopcnt) === 3) { return "CantAnswer"; } // 그 외는 기본 처리 (선택 사항) return "PASS";
CantAnswer로 분기되었을 경우, LLM Node를 통해 사용자에게 내부 VDB에 존재하지 않는 정보이기에 답변할 수 없음을 알리도록 지시합니다.
[System Prompt] 당신은 안내자입니다. 사용자에게 '말씀주신 질문은 내부 VDB에 존재하지 않는 문서이기에 답변드릴 수 없습니다.' 라고 말하세요.
2.5. Generator
검색된 문서를 바탕으로 사용자의 질문에 답변하는 노드를 구성합니다.
Generator는 일반적인 RAG 답변 생성에 사용되는 방식으로 구현됩니다. 'Generator'라는 이름을 갖는 LLM Node를 LoopCondition 분기로부터 연결한 뒤, 다음과 같은 프롬프트를 작성합니다.

[System Prompt]
당신은 신뢰할 수 있는 **답변 생성 전문가(Answer Generator)**입니다.
사용자의 질문에 대해 반드시 제공된 참고 문서 내의 정보만을 근거로 하여 정직하고 정확하게 응답해야 합니다.
📌 답변 지침:
반드시 참고 문서 내에 명시된 내용만 기반으로 답변을 작성하세요.
문서에 존재하지 않거나 불확실한 정보는 절대로 추론하거나 지어내지 마세요.
문서에 명시적 정보가 부족한 경우, “답변할 수 없습니. NO로 분기하세요.” 혹은 “제공된 문서에는 해당 정보가 없습니다. NO로 분기하세요.”라고 응답하세요.
답변의 마지막에는 반드시 출처 URL 또는 문서명을 명시하세요.
(예: 참고: 출처1, 출처2 등)
예외나 유추는 금지합니다.
❌ “문맥상 이럴 것이다”
❌ “일반적으로는 …라고 본다”
✅ “문서에 따르면 …라고 명시되어 있다”
목표:
사실에 기반한 안전한 답변
거짓 없는 정직한 응답
철저한 출처 기반
[Human Prompt]
사용자 질문 : {question}
연관 문서 : {RetrievedDocuments}
LLM Node의 하단 Update State에서 'Answer' 변수에 $flow.output.content를 지정하여 'Answer' 키에 답변이 저장되도록 합니다.

2.6. Relevance Checker
검색된 문서와 생성된 답변이 사용자의 질문과 긴밀히 연관되어 있는지 확인합니다. 해당 노드를 통해 RAG의 품질을 자체적으로 평가하고 판단할 수 있습니다.

'Relevance Checker'라는 노드를 추가한 뒤, 다음과 같이 프롬프트를 작성합니다. 평가 기준은 작성자의 요구사항에 따라 다르게 작성할 수 있습니다.
당신은 **RelevanceChecker (관련성 평가자)**입니다. 당신의 임무는 사용자 질문에 대해 생성된 답변이 실제로 유익하고 적절한 정보를 제공하는지를 평가하는 것입니다. 검색 기반 응답 시스템의 정확도와 신뢰성을 보장하는 최종 필터 역할을 수행해야 합니다. 📏 평가 기준 (모두 만족해야 "PASS") 직접적 관련성: 답변이 질문의 핵심 요구를 정확히 해결하고 있는가? 정보의 구체성: 구체적이고 실질적인 정보를 제공하고 있는가? 문맥 일치: 질문의 의도 및 맥락과 일치하는 정보인가? 주제 일탈 여부: 주제에서 벗어나거나 다른 방향으로 흐르지 않았는가? 답변 가능성 : 사용자의 질문에 답변을 할 수 있는가? (답변할 수 없을 경우 NO) 📌 관련성 허용 기준 문서의 핵심 내용이 질문과 실질적인 관련성이 있으면 PASS 질문의 주요 요소 중 다수를 다루고 있으면 PASS 단, 문맥이 완전히 벗어나거나 질문에 대해 답변을 제대로 수행하지 못한 경우는 NO 🚫 판단 지침 위 기준 중 하나라도 불충족 시 반드시 "NO"로 판단 관련된 참고 문서가 존재하지 않거나, 핵심 질문을 해결하지 못하면 반드시 "NO" (PASS 금지) '답변할 수 없습니다'와 같은 응답은 무조건 NO 처리 "PASS" 또는 "NO" 중 하나의 단어만 대문자로 출력 절대로 이유, 설명, 부연 문장을 덧붙이지 말 것 [Human Prompt] 사용자 질문 : {question} 검색된 문서 : {RetrievedDocuments} 답변 : {answer}
2.7. Condition Node 설정 2
Relevance Checker의 Output을 바탕으로 질의를 재작성할지 최종 답변을 생성할지 분기합니다.
Relevance Checker의 Output이 'PASS'라면 FinalAnswerGenerator로, 'NO'라면 QueryRewritter로 분기되도록 설정합니다.

2.8. QueryRewritter
검색된 문서와 답변이 질문에 대답하기에 충분하지 않다면, Query를 새롭게 작성하여 문서를 다시 검색합니다.
'QueryRewritter'라는 이름을 갖는 LLM Node를 생성한 뒤, Relevance Check의 'Rewrite'와 연결합니다.
[System Prompt] 당신은 Query Rewriter입니다. 당신의 임무는 사용자의 질문을 분석하여, 검색 정확도와 문서 정합성을 높일 수 있도록 질문을 더 구체적이고 명확하게 보강하는 것입니다. 🎯 목적: - Weaviate 기반 RAG 시스템에서 더 정밀한 검색 결과를 얻기 위해, 질문을 재구성하거나 중요한 키워드를 추가합니다. - 애매하거나 축약된 표현을 구체화하여 검색 품질을 높입니다. 🛠 보강 원칙: 1. 질문의 **핵심 의도**를 유지하며, 누락된 개념이나 키워드를 자연스럽게 보완합니다. 2. 필요한 경우 **시점(날짜, 연도 등)**, **대상(기관명, 문서명 등)**, **맥락 정보(업무, 분야 등)** 를 추가합니다. 3. 단순 키워드 나열이 아닌 **의미 있는 검색 문장**으로 구성합니다. 4. 쓸모없는 수식어나 감탄사 등은 제거하여 **정제된 검색 질의(Query)**를 생성합니다. 💬 출력 형식: - 🔍 Rewritten Query: "<보강된 검색용 질의>" - 설명과 같은 다른 말은 하지 않고 보강된 검색용 질의만 출력하세요. 📌 예시: - 원래 질문: "기준금리 언제 바뀜?" - 🔍 Rewritten Query: "한국은행 기준금리 변경 시점 및 변경일자" - 원래 질문: "BOK 보고서에서 물가 내용 있어?" - 🔍 Rewritten Query: "한국은행 보고서에서 물가 관련 정책, 전망, 원인에 대한 언급" [Human Prompt] 사용자 질문 : {question}
LLM의 Output을 안정성 높게 제어하기 위해 Json Structured Output을 사용합니다.
이를 사용하지 않을 경우,
Expected Output : "삼성전자의 주가" Wrong Output : "Rewritten Query : 삼성전자의 주가" 와 같이 불필요한 요소가 추가될 수 있습니다.

Output은 State의 RewrittenQuery에 저장되도록 설정합니다. 이후, Loop Node를 생성하여 RAGManager로 돌아가도록 설정합니다.
2.9. FinalAnswerGenerator
평가 결과가 PASS일 경우, 최종적으로 답변을 생성하는 노드입니다.
LLM Node를 생성하여 다음과 같이 프롬프트를 작성합니다.
[System Prompt] 당신은 **최종 답변가(Final Answerer)**입니다. 사용자의 질문에 대한 검색 및 분석 결과를 기반으로, 종합적인 답변을 작성하는 것이 당신의 역할입니다. 다음 사항을 반드시 지켜주세요: 📌 작성 지침: 답변은 마크다운(Markdown) 형식으로 정리하세요. 중요한 개념은 굵게, 항목은 리스트 또는 표로 구조화하여 가독성을 높이세요. 사용자의 질문을 명확히 이해한 후, 직접적으로 답변하세요. 출처는 이미 어플리케이션에서 표기됩니다. 절대 출처를 적시하지 마세요. [Human Prompt] 사용자 질문 : {question} 검색된 문서 : {RetrievedDocuments} 답변 : {Answer}
2.10. Node별 History 제어하기
Node마다의 History 접근 권한을 제어하여 LLM의 성능을 최고로 이끌어낼 수 있도록 합니다.
본 튜토리얼에서는 아래와 같이 Memory 접근 권한을 설정하였습니다.
Node NameMemoryRAGChecker
All conversation Messages
Assistant
Empty
RAG Manager
Empty
Generator
Empty
RelevanceChecker
Empty
RAGAssistant
Empty
QueryRewritter
All conversation Messages
FinalAnswerGenerator
Empty
Memory 접근 권한 선정은 각 Node의 역할이 이전 기록을 반드시 참고해야 하는지를 기준으로 두어 설정하였습니다.
3. 워크플로우 테스트하기
여태까지의 구성을 바탕으로 워크플로우를 테스트합니다. 워크플로우 우측 상단의 채팅 아이콘을 클릭해 질의를 던져볼 수 있습니다.



VDB에 존재하는 내용에 대해 질의하면, 내부 평가를 거친 뒤 답변이 생성됩니다.




VDB에 존재하지 않는 내용에 대해 질의할 경우, 설정한 횟수만큼(해당 튜토리얼에서는 3번) Query를 재작성하며 평가를 반복한 뒤, 횟수를 초과할 경우 "VDB에 존재하지 않는 문서이기에 답변드릴 수 없습니다." 라는 답변을 내보내며 답변을 종료합니다.
4. 결론 및 추가 개선사항
본 튜토리얼을 통해 Genos에서 자체 평가를 거치고, 미리 정한 반복 횟수를 초과하면 Loop를 탈출하는 Agentic RAG 워크플로우를 구현했습니다. 해당 워크플로우는 일반적인 RAG에 비해 더 높은 신뢰도와 정확성을 제공합니다.
여기서 더 나아가 다음 사항들을 개선해볼 수 있습니다.
프롬프트 고도화: 각 평가 노드의 프롬프트를 더욱 정교하게 다듬어 평가 기준을 강화할 수 있습니다.
평가 노드 추가: 해당 튜토리얼에서는 RelevanceChecker 노드만을 활용하여 RAG의 품질을 평가하였지만, 자체적인 평가 노드들을 추가해 더 엄격한 기준을 적용할 수 있습니다.
Last updated
Was this helpful?