728x90
반응형

Text2Cypher 에이전트 인터페이스 구축을 위한 청사진

배경

Retrieval-Augmented Generation(RAG)는 여전히 많은 관심을 받고 있고, 그럴 만한 이유가 있죠. RAG는 Large Language Model과 대상 정보 검색을 결합해서, 관련 데이터에 더 빠르게 접근하고 정확한 답변을 생성하는 강력한 프레임워크거든요. RAG 애플리케이션은 주로 구조화되지 않은 데이터에 초점을 맞추지만, 저는 구조화된 데이터를 함께 통합하는 걸 선호해요. 중요하지만 간과되는 접근 방식이죠. 이걸 위해 제가 가장 좋아하는 방법 중 하나는 와 같은 Graph Database를 활용하는 거예요.

Graph Database에서 데이터를 검색하는 일반적인 접근 방식은 Text2Cypher인데요. 여기서 Natural Language 쿼리는 자동으로 Cypher 구문으로 변환되어 Database를 쿼리하게 돼요. 이 기술은 사용자 쿼리를 해석하고, 의도를 추론해서 유효한 Cypher 쿼리로 변환하는 Language Model(또는 규칙 기반 시스템)을 사용하죠. 덕분에 RAG 애플리케이션이 Knowledge Graph에서 관련 정보를 검색하고 정확한 답변을 생성할 수 있게 되는 거예요.

LLM을 사용하여 Cypher 생성— 이미지 출처:

Text2Cypher는 사용자가 기본적인 Graph 스키마나 Cypher 구문을 몰라도 Natural Language로 질문을 할 수 있게 해주기 때문에 정말 유연하죠. 하지만 언어 해석에는 뉘앙스가 있고 정확한 스키마 정보가 필요하기 때문에, 보시는 것처럼 정확도가 부족할 수도 있어요. Neo4j Text2Cypher(2024) 데이터 세트를 사용한 벤치마킹 결과인데요.

벤치마크의 가장 중요한 결과는 다음 그림에서 확인하실 수 있어요.

Text2Cypher 벤치마크 결과 — 이미지 출처:

전반적으로 벤치마크는 세 가지 모델 그룹을 비교하고 있어요.

  • Text2Cypher 작업을 위해 Fine-tuning된 모델
  • 개방형 기본 모델
  • 폐쇄형 기본 모델

벤치마크에서는 Google BLEU(상단 차트)와 ExactMatch(하단 차트)라는 두 가지 지표를 사용해서, 올바른 Cypher 쿼리를 생성하는 성능을 평가해요.

Google BLEU 지표는 생성된 쿼리와 참조 쿼리 간의 유사성 정도(n-gram 기준)를 측정하는데요. 점수가 높을수록 일반적으로 참조 쿼리와 더 유사하다는 의미이지만, 쿼리가 Database 컨텍스트에서 올바르게 실행된다는 것을 보장하지는 않아요.

반면에 ExactMatch는 실행 기반 지표예요. 올바른 쿼리 텍스트와 정확히 일치하는 생성된 쿼리의 비율을 나타내고, 실행 시 동일한 결과가 생성됨을 의미하죠. ExactMatch는 정확성을 더 엄격하게 측정하고 실제 환경에서 쿼리의 유용성과 더 직접적으로 연결된다고 볼 수 있어요.

Fine-tuning을 통해 꽤 괜찮은 결과가 나오긴 했지만, 전반적인 정확도 수준을 보면 Text2Cypher는 아직 발전하고 있는 기술이라는 걸 알 수 있어요. 일부 모델은 여전히 모든 경우에 완벽하게 정확한 쿼리를 생성하는 데 어려움을 겪고 있고, 추가적인 개선이 필요하다는 걸 보여주죠.

LlamaIndex 워크플로

이번 포스팅에서는 LlamaIndex 워크플로를 사용해서 Text2Cypher에 대한 좀 더 적극적인 전략을 구현해 볼 거예요. 일반적으로 대부분의 벤치마크가 단일 쿼리 생성에 의존하는 방식 대신, 재시도나 대체 쿼리 생성을 허용하는 다단계 접근 방식을 시도해 보려고 해요. 이러한 추가 단계와 대안을 통해서 전반적인 정확성을 높이고, 잘못된 Cypher 생성 사례를 줄이는 것을 목표로 하고 있답니다.

코드는 다음에서 사용할 수 있어요:. 그리고도 준비되어 있답니다. 애플리케이션에 기여하고 디버깅을 도와주신님께 감사드립니다! 🙂

에이전트가 모두 포함된 호스팅된 웹 애플리케이션은 여기에서 사용할 수 있어요:

LlamaIndex Workflow는 이벤트 기반 시스템을 통해 다양한 작업을 연결해서 다단계 AI 프로세스를 구성하는 실용적인 방법이에요. 복잡한 작업을 관리하기 쉬운 작은 조각으로 나누어 서로 통신하게 해주죠. Workflow의 각 단계는 특정 이벤트를 처리하고 새로운 이벤트를 생성해서 문서 처리, 질문 답변, 콘텐츠 생성 같은 작업을 수행하는 일련의 작업을 만들어내요. 시스템이 단계 간 조정을 자동으로 처리해주니까 복잡한 AI 애플리케이션을 더 쉽게 구축하고 유지 관리할 수 있답니다.

Flows

Naive Text2Cypher Flow

Naive Text2Cypher 아키텍처는 자연어 질문을 Neo4j Graph Database에 대한 Cypher `Query`로 변환하는 간단한 접근 방식이에요. 3단계 Workflow로 작동하는데요.

1. `Vector Embedding`으로 저장된 유사 예제를 가지고 Few-Shot 학습을 이용해서 입력 질문으로부터 Cypher `Query`를 생성해요.

2. 시스템은 Graph Database에 대해 생성된 Cypher `Query`를 실행하죠.

3. Database 결과를 Large Language Model(LLM)을 통해 처리해서 원래 질문에 답하는 자연어 응답을 생성합니다.

이 아키텍처는 Cypher `Query` 생성 및 응답 형식화를 위해 LLM과 `Vector Embedding`을 활용해서 간단하면서도 효과적인 파이프라인을 유지해요.

시각화된 단순한 Text2Cypher Workflow는 다음과 같아요.

Naive Text2Cypher Flow

대부분의 Neo4j `Schema` 생성 방법이 어려움을 겪고 있다는 점은 주목할 만해요.다중 레이블이 지정된 `Node`. 이 문제는 복잡성을 더할 뿐만 아니라 `Prompt`를 압도할 수 있는 레이블의 조합 폭발로 인해 발생하죠. 이를 완화하기 위해 `Schema` 생성 프로세스에서 Actor 및 Director 레이블을 제외할 거예요.

schema = graph_store.get_schema_str(exclude_types=["Actor", "Director"])

파이프라인은 generate_cypherstep으로 시작해요.

@step
async def generate_cypher(self, ctx: Context, ev: StartEvent) -> ExecuteCypherEvent:
    question = ev.input
    # Cypher query generation using an LLM
    cypher_query = await generate_cypher_step(
        self.llm, question, self.few_shot_retriever
    )
    # Streaming event information to the web UI.
    ctx.write_event_to_stream(
        SseEvent(
            label="Cypher generation",
            message=f"Generated Cypher: {cypher_query}",
        )
    )

    # Return for the next step
    return ExecuteCypherEvent(question=question, cypher=cypher_query)

generate_cypher 단계에서는 자연어 질문을 가져와서 Large Language Model(LLM)을 사용하고 `Vector Embedding`에서 유사한 예를 검색해서 이를 Cypher `Query`로 변환해요. 또한 이 단계에서는 생성된 Cypher `Query`를 실시간으로 사용자 인터페이스로 다시 스트리밍해서 `Query` 생성 프로세스에 대한 즉각적인 피드백을 제공하죠. 전체 코드 및 `Prompt`를 확인해 보세요.

재시도 Flow를 사용하는 Naive Text2Cypher

재시도 기능이 포함된 향상된 Text2Cypher 버전은 자체 수정 메커니즘을 추가해서 원래 아키텍처를 기반으로 구축되었어요. 생성된 Cypher `Query`가 실행되지 않으면 시스템은 CorrectCypherEvent 단계에서 오류 정보를 Large Language Model(LLM)에 다시 공급해서 `Query` 수정을 시도해요. 이는 사람이 오류 피드백을 받은 후 접근 방식을 수정하는 방법과 유사하게 시스템의 탄력성을 높이고 초기 실수를 처리할 수 있게 만들어 준답니다.

재시도 Workflow를 사용하여 시각화된 Naive Text2Cypher는 다음과 같아요.

재시도 흐름이 포함된 순진한 Text2Cypher

ExecuteCypherEvent를 한번 살펴볼까요?

@step
async def execute_query(
    self, ctx: Context, ev: ExecuteCypherEvent
) -> SummarizeEvent | CorrectCypherEvent:
    # Get global var
    retries = await ctx.get("retries")
    try:
        database_output = str(graph_store.structured_query(ev.cypher))
    except Exception as e:
        database_output = str(e)
        # Retry
        if retries < self.max_retries:
            await ctx.set("retries", retries + 1)
            return CorrectCypherEvent(
                question=ev.question, cypher=ev.cypher, error=database_output
            )

    return SummarizeEvent(
        question=ev.question, cypher=ev.cypher, context=database_output
    )

실행 함수는 먼저 쿼리 실행을 시도하고, 성공하면 요약을 위해 결과를 전달해요. 하지만 뭔가 잘못되면 바로 포기하지 않아요. 대신 남은 재시도 횟수가 있는지 확인하고, 있다면 무엇이 잘못되었는지에 대한 정보와 함께 수정을 위해 쿼리를 다시 보내죠. 이는 피드백을 받은 후 접근 방식을 수정하는 방법과 마찬가지로 실수로부터 배울 수 있는 보다 관용적인 시스템을 만드는 거예요. 전체 코드 및 프롬프트를 확인해 보세요.

재시도 및 평가 흐름을 갖춘 순진한 Text2Cypher

재시도 흐름을 갖춘 순진한 Text2Cypher를 기반으로 하는 이 버전은 쿼리 결과가 사용자의 질문에 대답하기에 충분한지 확인하는 평가 단계를 추가했어요. 결과가 부적절할 경우 시스템은 개선 방법에 대한 정보와 함께 수정을 위해 쿼리를 다시 보내죠. 결과가 수용 가능하다면 흐름은 최종 요약 단계로 진행돼요. 이러한 추가 검증 계층은 파이프라인의 탄력성을 강화해서 사용자가 가능한 가장 정확하고 완전한 답변을 받을 수 있도록 보장하는 거죠.

재시도 및 평가 흐름을 갖춘 Naive Text2Cypher

추가 평가 단계는 다음과 같이 구현돼요.

@step
async def evaluate_context(
    self, ctx: Context, ev: EvaluateEvent
) -> SummarizeEvent | CorrectCypherEvent:
    # Get global var
    retries = await ctx.get("retries")
    evaluation = await evaluate_database_output_step(
        self.llm, ev.question, ev.cypher, ev.context
    )
    if retries < self.max_retries and not evaluation == "Ok":
        await ctx.set("retries", retries + 1)
        return CorrectCypherEvent(
            question=ev.question, cypher=ev.cypher, error=evaluation
        )
    return SummarizeEvent(
        question=ev.question, cypher=ev.cypher, context=ev.context
    )

evaluate_check 함수는 쿼리 결과가 사용자의 질문을 적절하게 해결하는지 여부를 결정하는 간단한 검사예요. 평가 결과 결과가 충분하지 않고 남은 재시도 횟수가 있는 것으로 나타나면 쿼리를 구체화할 수 있도록 CorrectCypherEvent를 반환해요. 그렇지 않으면 결과가 최종 요약에 적합함을 나타내는 SummarizeEvent를 진행하죠.

나중에 잘못된 Cypher 문을 수정하여 흐름이 성공적으로 자체 복구된 인스턴스를 캡처하는 것이 훌륭하다는 것을 깨달았어요. 이러한 예는 향후 Cypher 생성을 위한 동적 few-shot 프롬프트로 활용될 수 있죠. 이 접근 방식을 통해 에이전트는 자가 치유뿐 아니라 시간이 지남에 따라 지속적으로 자가 학습하고 개선할 수 있어요. 이러한 few-shot 예제를 저장하는 예제 코드는 최고의 자가 치유 정확도를 제공하므로 이 흐름에 대해서만 구현돼요.

@step ```html async def summarize_answer(self, ctx: Context, ev: SummarizeEvent) -> StopEvent: retries = await ctx.get("retries") # If retry was successful: if retries > 0 and check_ok(ev.evaluation): # print(f"Learned new example: {ev.question}, {ev.cypher}") # Store success retries to be used as fewshots! store_fewshot_example(ev.question, ev.cypher, self.llm.model) ```

반복 계획자 흐름

마지막 흐름은 가장 복잡하고, 또 제가 처음으로 야심차게 디자인한 흐름이에요. 여러분이 제 탐구를 통해 배울 수 있도록 코드에 잘 보관해 두었답니다.

반복 계획 흐름은 반복 계획 시스템을 구현해서 좀 더 정교한 접근 방식을 사용해요. Cypher 쿼리를 바로 생성하는 대신 하위 쿼리 계획을 만들고, 실행 전에 각 하위 쿼리 Cypher 구문을 검증하죠. 초기 결과가 부족하면 계획을 수정할 수 있는 정보 확인 메커니즘도 포함되어 있어요. 이 시스템은 최대 3번 정보 수집을 반복할 수 있고, 매번 이전 결과를 바탕으로 접근 방식을 개선해요. 이렇게 복잡한 쿼리를 관리하기 쉬운 단계로 나누고 각 단계에서 검증을 거치면 복잡한 쿼리를 처리할 수 있는 훨씬 꼼꼼한 질문 답변 시스템이 만들어지는 거죠.

시각화된 반복 계획자 워크플로는 아래와 같아요.

반복 계획 흐름

쿼리 플래너 프롬프트를 한번 살펴볼까요? 저는 시작할 때 꽤 야심찼어요. LLM이 다음과 같은 응답을 생성할 거라고 예상했거든요.

class SubqueriesOutput(BaseModel):
    """Defines the output format for transforming a question into parallel-optimized retrieval steps."""

    plan: List[List[str]] = Field(
        description=(
            """A list of query groups where:
        - Each group (inner list) contains queries that can be executed in parallel
        - Groups are ordered by dependency (earlier groups must be executed before later ones)
        - Each query must be a specific information retrieval request
        - Split into multiple steps only if intermediate results return ≤25 values
        - No reasoning or comparison tasks, only data fetching queries"""
        )
    )

출력은 복잡한 질문을 순차적 및 병렬 쿼리 단계로 변환하기 위한 구조화된 계획을 나타내요. 각 단계는 병렬로 실행될 수 있는 쿼리 그룹으로 구성되고, 이후 단계는 이전 단계의 결과에 따라 달라지죠. 쿼리는 추론 작업은 피하고 정보 검색만을 위한 것이고, 결과 크기를 관리하기 위해 필요한 경우 더 작은 단계로 나뉘어요. 예를 들어, 다음 계획은 두 배우의 영화를 병렬로 나열하는 것으로 시작해서 첫 번째 단계의 결과에서 가장 높은 수익을 올리는 영화를 식별하는 단계로 이어져요.

plan = [
# 2 steps in parallel
    [
        "List all movies made by Tom Hanks in the 2000s.",
        "List all movies made by Tom Cruise in the 2000s.",
    ],
# Second step
    ["Find the highest profiting movie among winner of step 1"],
]

이 아이디어는 정말 멋지죠? 복잡한 질문을 더 작고 실행 가능한 단계로 나누고 병렬 처리를 사용해서 검색을 최적화하는 건 정말 현명한 방법이에요. 정말 속도를 높일 수 있는 전략처럼 들리네요. 하지만 실제로 LLM이 이걸 안정적으로 수행할 거라고 기대하는 건 좀 야심찬 일이에요. 병렬성은 이론적으로는 효율적이지만, 복잡성이 엄청나게 높아지거든요. 종속성, 중간 결과, 그리고 병렬 단계 간의 논리적 일관성을 유지하는 건 고급 모델도 쉽게 실패할 수 있어요. 순차 실행이 덜 매력적일 수는 있지만, 현재로서는 더 안정적이고 모델의 인지 오버헤드를 크게 줄여준답니다.

게다가 LLM은 특히 단계 간의 종속성을 추론할 때 목록 목록과 같은 구조화된 도구 출력을 따르는 데 어려움을 겪는 경우가 많아요. 여기서는 도구 출력 없이 프롬프트만으로 이런 작업에 대한 모델 성능을 얼마나 향상시킬 수 있을지 궁금하네요.

반복적인 계획 흐름에 대한 code를 확인해 보세요!

벤치마킹

LlamaIndex 워크플로 아키텍처에서 text2ypher 에이전트를 평가하기 위한 벤치마크 데이터 세트를 만드는 건 정말 흥미로운 발전 같아요.

우리는 반복 계획과 같은 워크플로의 잠재력을 제대로 파악하지 못하는 ExactMatch와 같은 기존의 일회성 Cypher 실행 지표에 대한 대안을 찾고 있었어요. 이런 워크플로우에서는 쿼리를 구체화하고 관련 정보를 검색하기 위해 여러 단계를 사용하기 때문에 단일 단계 실행 지표는 적절하지 않거든요.

그래서 이 제품을 사용하기로 결정했어요. 답변_관련성은 Ragas가 우리가 측정하려는 대상과 더 잘 맞는다고 느껴졌거든요. 여기서는 LLM을 사용해서 답변을 생성한 다음, 이를 판단 기준으로 사용해서 실제와 비교하는 방식이에요. 우리는 약 50개의 샘플로 구성된 사용자 정의 데이터세트를 준비했는데, 데이터베이스 결과가 지나치게 크거나 상세해서 LLM 심사위원이 관련성을 효과적으로 평가하기 어렵게 만드는 상황을 방지하도록 설계되었어요. 결과를 짧게 유지하면 단일 단계 및 다단계 작업 흐름을 공정하고 집중적으로 비교할 수 있죠.

결과는 이렇습니다!

벤치마크 결과

Claude 3.5 Sonnet, Deepseek-V3, 그리고 GPT-4o가 답변 관련성 측면에서 상위 3개 모델로 나타났고, 각각 0.80 이상의 점수를 받았어요. NaiveText2CypherRetryCheckFlow는 전반적으로 가장 높은 관련성을 생성하는 경향이 있었고, IterativePlanningFlow는 지속적으로 낮은 순위(0.163까지 떨어짐)를 보였죠.

OpenAI o1 모델은 상당히 정확하지만, 여러 번의 시간 초과(90초로 설정) 때문에 최상위에 있지는 못했을 거예요. Deepseek-V3는 높은 점수와 상대적으로 낮은 대기 시간을 고려하면 특히 유망해 보여요. 전반적으로 이러한 결과는 실제 배포 시나리오에서 원시 정확성뿐만 아니라 안정성과 속도의 중요성도 강조하고 있어요.

흐름 간의 상승을 쉽게 확인할 수 있는 다른 테이블도 한번 살펴볼까요?

벤치마크 결과

Sonnet 3.5는 NaiveText2CypherFlow의 0.596 점에서 NaiveText2CypherRetryFlow의 0.616점으로 꾸준히 상승한 다음, NaiveText2CypherRetryCheckFlow에서 0.843으로 크게 도약했어요. GPT-4o는 전반적으로 유사한 패턴을 보여 NaiveText2CypherFlow의 0.622에서 NaiveText2CypherRetryFlow의 0.603으로 약간 감소했지만, NaiveText2CypherRetryCheckFlow에서는 0.837로 크게 상승했죠. 이러한 개선 사항은 재시도 메커니즘과 최종 확인 단계를 추가하면 답변 관련성이 크게 향상된다는 것을 보여줘요.

다음은 벤치마크 코드입니다.

벤치마크 결과는 5% 이상 달라질 수 있어요. 즉, 여러 번 실행하면 약간 다른 결과와 최고 성과를 관찰할 수 있다는 의미죠.

학습 및 생산

2개월간 진행된 프로젝트를 통해 정말 많은 것을 배울 수 있었어요. 한 가지 하이라이트는 테스트 벤치마크에서 84%의 관련성을 달성한 건데, 이건 정말 중요한 성과거든요. 하지만 이게 프로덕션 환경에서 84%의 정확도를 달성한다는 의미일까요? 아마도 아닐 거예요.

프로덕션 환경에는 고유한 과제가 있어요. 실제 데이터는 벤치마크 데이터 세트보다 잡음이 많고, 다양하며, 덜 구조적인 경우가 많거든요. 아직 자세히 이야기하지는 않았지만, 실제 애플리케이션과 사용자를 통해 우리가 실제로 보게 될 것은 프로덕션 준비 단계의 필요성이에요. 이는 통제된 벤치마크에서 높은 정확도를 달성하는 데 초점을 맞추는 것뿐만 아니라, 시스템이 안정적이고 적응 가능하며 실제 조건에서 일관된 결과를 제공하는지 확인하는 것을 의미하죠.

이러한 설정에서는 관련 없는 질문이 Text2Cypher 파이프라인을 통과하지 못하도록 몇 가지 가드레일을 구현해야 해요.

관련 없는 질문

여기 가드레일 구현 예가 있어요. 단순히 관련 없는 질문의 경로를 바꾸는 것 외에도, 초기 가드레일 단계에서는 사용자가 질문할 수 있는 질문을 안내하고, 사용 가능한 도구를 보여주고, 이를 효과적으로 사용하는 방법을 보여줌으로써 사용자를 교육할 수도 있죠.

아래에서는 사용자 입력 값을 데이터베이스에 매핑하는 프로세스를 추가하는 것의 중요성을 강조하고 있어요. 이는 사용자가 제공한 정보가 데이터베이스 스키마와 일치하는지 확인해서 정확한 쿼리 실행을 가능하게 하고, 불일치하거나 모호한 데이터로 인해 발생하는 오류를 최소화하는 데 중요하거든요.

값을 데이터베이스에 매핑

이건 사용자가 'SF' 영화를 요청하는 예시인데요. 문제는 장르가 데이터베이스에 '공상과학'으로 저장되어 있어서 쿼리가 결과를 반환하지 않기 때문에 발생해요.

종종 간과되는 것은 Null 값의 존재예요. 실제 데이터에서는 Null 값이 일반적이므로 이를 고려해야 하고, 특히 정렬과 같은 작업을 수행할 때 더욱 그렇습니다. 이를 올바르게 처리하지 못하면 예상치 못한 결과나 오류가 발생할 수 있어요.

Null 값 처리

이 예시에서는 평점이 Null 값인 임의의 영화를 얻게 되죠. 이 문제를 해결하려면 쿼리에 m.imdbRating IS NOT NULL인 추가 절이 있어야 해요.

누락된 정보가 단순한 데이터 문제가 아니라 스키마 제한 때문일 수도 있어요. 예를 들어 오스카상 수상 영화를 요청했는데 스키마에 수상 정보가 없다면, 쿼리가 원하는 결과를 반환할 수 없겠죠.

누락된 데이터

LLM은 사용자를 만족시키도록 훈련되었기 때문에 스키마에 맞는 결과는 주겠지만 유효하지 않을 수도 있어요. 저는 아직 그런 경우를 가장 잘 처리하는 방법을 잘 모르겠네요.

마지막으로 쿼리 계획에 대해 이야기해볼게요. 질문에 답하기 위해 이런 쿼리 계획을 사용했어요.

2000년대에 톰 행크스와 톰 크루즈 중 누가 더 많은 영화를 만들었나요? 그리고 우승자의 최고 수익 영화는 무엇인가요?

계획은 이렇습니다.

plan = [
# 2 steps in parallel
    [
        "List all movies made by Tom Hanks in the 2000s.",
        "List all movies made by Tom Cruise in the 2000s.",
    ],
# Second step
    ["Find the highest profiting movie among winner of step 1"],
]

인상적이지만, 사실 Cypher는 매우 유연해서 GPT-4o가 이걸 단일 쿼리로 처리할 수 있어요.

이 경우 병렬성은 과잉이라고 생각해요. 정말 복잡한 질문 유형을 처리해야 한다면 쿼리 플래너를 포함할 수 있지만, 많은 multi-hop 질문은 단일 Cypher 문으로도 효율적으로 처리할 수 있다는 점을 기억하세요.

이 예시는 또 다른 문제를 보여주는데요. LLM에 제한된 정보만 제공되었기 때문에 최종 답변이 모호해졌어요. 특히, 우주 전쟁의 승자는 톰 크루즈죠. 이 경우 추론은 데이터베이스 내에서 이미 발생했기 때문에 LLM이 해당 로직을 처리할 필요가 없어요. LLM은 기본적으로 이런 식으로 작동하는 경향이 있어서, 정확하고 명확한 응답을 보장하려면 LLM에 전체 컨텍스트를 제공하는 것이 중요하다는 점을 강조하고 싶어요.

마지막으로, 많은 결과를 반환하는 질문을 어떻게 처리해야 할지도 생각해봐야 해요.

많은 결과 반환

저희 구현에서는 결과에 대해 100개의 레코드라는 엄격한 제한을 두고 있어요. 이렇게 하면 데이터 볼륨을 관리하는 데 도움이 되지만, 경우에 따라 너무 과도할 수도 있고 추론 과정에서 LLM을 오도할 수도 있죠.

그리고 이 블로그에 소개된 모든 에이전트가 대화형인 것도 아니에요. 대화형으로 만들려면 처음에 질문을 다시 작성하는 단계가 필요할 거예요. 이는 가드레일 단계의 일부일 수도 있겠죠. 그리고 프롬프트에서 전체를 전달할 수 없는 대규모 그래프 스키마가 있다면, 관련된 그래프 스키마를 동적으로 가져오는 시스템을 마련해야 할 거예요.

프로덕션 환경에 적용할 때는 주의해야 할 점이 정말 많아요!

요약

에이전트는 매우 유용할 수 있지만, 처음에는 간단하게 시작하고 지나치게 복잡한 구현부터 시작하지 않는 것이 가장 좋아요. 다양한 아키텍처를 효과적으로 평가하고 비교할 수 있는 견고한 벤치마크를 설정하는 데 집중하세요. 도구 출력과 관련해서는 사용을 최소화하거나 가능한 가장 간단한 도구를 사용하는 게 좋다고 생각해요. 많은 에이전트가 도구 출력을 효과적으로 처리하는 데 어려움을 겪고, 종종 수동 파싱이 필요하거든요.

여러분이 구현한 것들도 보고 싶네요! 프로젝트를 Neo4j 데이터베이스에 연결하고 실험을 시작해 보세요.


  • GraphRAG
  • LlamaIndex
  • 텍스트2사이퍼

에이치시스템즈LogTree는 Neo4j 기반 GraphRAG 플랫폼으로, 데이터를 자동으로 지식그래프화하고 자연어 질의로 즉시 답을 제공합니다.

👉 에이치시스템즈 홈페이지

728x90
반응형

+ Recent posts