create_stuff_documents_chain
은 LangChain 라이브러리에서 사용되는 함수 또는 메서드로, 여러 개의 문서
를 입력받아 이를 조합하여 특정 작업을 수행하는 체인을 생성하는 데 사용된다. 이 체인은 문서들을 처리하여 답변 생성, 요약, 검색 등 다양한 작업을 수행할 수 있다.
즉, context의 내용에 Document 객체를 직접 입력받는다. retriever 통해 vector_store를 검색한 결과가 List[Document]가 리턴될텐데 따로 우리가 문자열로 변환하지 않고 자동으로 변환하여 LLM으로 호출할 수 있도록 하는 체인이다.
create_stuff_documents_chain
의 목적
- 문서 처리 파이프라인 생성:
create_stuff_documents_chain
은 여러 문서가 주어졌을 때, 이들을 하나의 체인으로 연결하여 처리할 수 있게 한다. 예를 들어, 여러 문서를 받아 요약하거나, 특정 질문에 대한 답변을 생성하는 등의 작업을 체인으로 구성할 수 있다. - 다양한 작업 수행: 이 체인은 주로 문서 요약, 검색, 질의응답(Q&A)와 같은 작업에 사용된다. 여러 문서를 하나로 결합하여 정보를 종합하거나, 각 문서를 개별적으로 처리한 후 그 결과를 합치는 방식으로 동작할 수 있다.
사용 예시
다음은 create_stuff_documents_chain
을 사용하는 간단한 예시입니다:
prompt = ChatPromptTemplate.from_messages(
["Please summarize the following documents: {context}"]
)
# retriever를 통해 docs를 리턴받는다. (여기서는 retriever는 생략하고 결과를 docs로 리턴받았다고 생각)
docs = [
Document(page_content="신용카드 발급 방법은"),
Document(page_content="개인정보에 동의하면 됩니다."),
]
llm = ChatOpenAI(
model_name="gpt-4o-mini",
temperature=0.0,
)
chain = create_stuff_documents_chain(llm, prompt) # stuff_document 체인 생성
answer = chain.invoke({"context": docs}) # llm 호출 시 stuff_document가 List[Document]를 문자열로 변환해서 호출해준다
print("answer:", answer)
answer 내용은 아래와 같다.
신용카드 발급 방법은 간단합니다. 신청자는 개인정보에 동의해야 하며, 이를 통해 카드 발급 절차가 진행됩니다.
stream 사용법
stream을 사용하려면 chain.invoke
대신 chain.stream
함수를 호출하면 된다.
for chunk in chain.stream({"context": docs}):
print(chunk)
실행결과
신
용
카
드
발
급
방법
은
간
단
합니다
.
신청
자는
개인정보
에
동
의
해야
하
며
,
이를
통해
카드
발
급
절
차
가
진행
됩니다
.
create_stuff_documents_chain 소스 내용 분석
create_stuff_documents_chain 함수 내용을 한번 보자. (stuff.py)
def create_stuff_documents_chain(
llm: LanguageModelLike,
prompt: BasePromptTemplate,
*,
output_parser: Optional[BaseOutputParser] = None,
document_prompt: Optional[BasePromptTemplate] = None,
document_separator: str = DEFAULT_DOCUMENT_SEPARATOR,
document_variable_name: str = DOCUMENTS_KEY,
) -> Runnable[Dict[str, Any], Any]:
"""Document 리스트를 모델로 전송하게 하는 체인 생성
Args:
llm: 언어 모델
prompt: 프롬프트 템플릿. "context"라는 입력 변수를 포함해야 하며(또는 document_variable을 설정하여 이를 재정의할 수 있음), 이는 형식화된 문서를 전달하는 데 사용됩니다..
output_parser: 출력 파서. 기본값은 StrOutputParser이다.
document_prompt: 각 문서를 문자열로 형식화하는 데 사용되는 프롬프트. 입력 변수로는 "page_content" 또는 모든 문서에 포함된 메타데이터 키를 사용할 수 있다. "page_content"는 자동으로 Document.page_content를 가져오며, 다른 모든 입력 변수는 자동으로 Document.metadata 딕셔너리에서 가져온다. 기본값은 Document.page_content만 포함하는 프롬프트이다.
document_separator: 형식화된 문서 문자열 사이에 사용할 문자열 구분자.
document_variable_name: 프롬프트에서 형식화된 문서에 사용할 변수 이름. 기본값은 "context"이다.
Returns:
LCEL Runnable. 입력은 "context" 키가 포함된 딕셔너리로, 이 키는 List[Document]에 매핑되어야 하며, 프롬프트에서 사용되는 다른 입력 변수도 포함될 수 있다. Runnable의 반환 타입은 사용된 output_parser에 따라 달라진다.
""" # noqa: E501
_validate_prompt(prompt)
_document_prompt = document_prompt or DEFAULT_DOCUMENT_PROMPT
_output_parser = output_parser or StrOutputParser()
# 아래 내용이 docs(List[Document])를 가져와서 문자열로 바꿔주는 로직이다.
def format_docs(inputs: dict) -> str:
return document_separator.join(
format_document(doc, _document_prompt)
for doc in inputs[document_variable_name]
)
return (
RunnablePassthrough.assign(**{document_variable_name: format_docs}).with_config(
run_name="format_inputs"
)
| prompt
| llm
| _output_parser
).with_config(run_name="stuff_documents_chain")
위의 로직을 보면 docs를 문자열로 format_docs가 있다.
def format_docs(inputs: dict) -> str:
return document_separator.join(
format_document(doc, _document_prompt)
for doc in inputs[document_variable_name]
)
여기에 문서로 입력된 정보는 docs이다.
docs = [
Document(page_content="신용카드 발급 방법은"),
Document(page_content="개인정보에 동의하면 됩니다."),
]
실제 입력된 inputs
값을 디버깅해보면 아래와 가다.
document_variable_name
이 context
인 것을 찾아서 doc을 가져온 다음 \n\n
을 구분자로 page_content
의 내용을 합쳐서 리턴한다.
현재 버전(langchain 0.2.12)에서는 document_variable_name를 다른 이름으로 변경을 하면 안되는 것 같다. 무조건 context를 사용해야 하는 듯 함.
즉, 리턴되는 문자열은 신용카드 발급 방법은\n\n개인정보에 동의하면 됩니다.
이다.
이 문자열을 LLM으로 던져서 결과를 응답받는 구조이다.