위에서 설명한 프로세스는 사용자가 한 가지 질문만 할 수 있을 때 잘 작동합니다. 그러나 이 애플리케이션은 후속 질문도 허용하므로 몇 가지 추가적인 문제가 발생합니다. 예를 들어 이전 질문과 답변을 모두 저장하여 새 질문을 LLM에 보낼 때 추가 컨텍스트로 포함할 수 있도록 해야 합니다.
이 애플리케이션의 채팅 기록은 Langchain과 Elasticsearch 통합의 일부인 또 다른 클래스인 ElasticsearchChatMessageHistory 클래스를 통해 관리됩니다. 관련 질문과 답변의 각 그룹은 사용된 세션 ID에 대한 참조와 함께 Elasticsearch 인덱스에 기록됩니다.
이전 섹션에서 LLM의 응답이 청크로 클라이언트에 스트리밍되지만 전체 응답이 포함된 answer 변수가 생성되는 것을 보셨을 것입니다. 이는 각 상호작용 후에 질문과 함께 응답이 기록에 추가될 수 있도록 하기 위한 것입니다:
클라이언트가 요청 URL의 쿼리 문자열에 session_id 인수를 보내면 해당 질문은 동일한 세션의 이전 질문의 맥락에서 이루어진 것으로 간주됩니다.
이 애플리케이션에서 후속 질문에 대해 취하는 접근 방식은 LLM을 사용하여 전체 대화를 요약하는 압축 질문을 만들어 검색 단계에 사용하는 것입니다. 이는 잠재적으로 방대한 질문 및 답변 기록에 대해 벡터 검색을 실행하지 않기 위한 것입니다. 이 작업을 수행하는 로직은 다음과 같습니다:
이는 주요 질문이 처리되는 방식과 많은 유사점이 있지만 이 경우에는 LLM의 스트리밍 인터페이스를 사용할 필요가 없으므로 대신 invoke() 메서드가 사용됩니다.
질문을 압축하기 위해 다른 프롬프트가 사용되며, 이는 **api/templates/condense_question_prompt.txt` 파일에 저장됩니다:
이 프롬프트는 세션의 모든 질문과 답변, 그리고 마지막에 새로운 후속 질문을 렌더링합니다. LLM은 모든 정보를 요약한 간소화된 질문을 제공하도록 지시받습니다.
LLM이 생성 단계에서 가능한 한 많은 맥락을 파악할 수 있도록 대화의 전체 기록이 검색된 문서 및 후속 질문과 함께 기본 프롬프트에 추가됩니다. 다음은 예제 애플리케이션에서 사용된 프롬프트의 최종 버전입니다:
압축된 질문의 사용 방식은 필요에 따라 조정할 수 있다는 점에 유의하세요. 일부 애플리케이션의 경우 생성 단계에서도 압축된 질문을 전송하는 것이 더 효과적이며 토큰 수 또한 줄일 수 있습니다. 또는 아예 압축된 질문을 사용하지 않고 항상 전체 채팅 내역을 보내는 것이 더 나은 결과를 얻을 수도 있습니다. 이제 이 애플리케이션의 작동 방식을 잘 이해하셨기를 바라며, 다양한 프롬프트를 실험하여 사용 사례에 가장 적합한 것을 찾을 수 있기를 바랍니다.