8. 개선: 쿼리셋 문자열 표현 스타일 다듬기¶
8.1. 개선 포인트¶
랭체인의 Document
인스턴스 리스트를 문자열 표현으로 출력하면
>>> from langchain_core.documents import Document
>>> doc_list = [
... Document(metadata={}, page_content='d1'),
... Document(metadata={}, page_content='d2'),
... ]
아래와 같이 문자열 표현 안에 metadata
속성값과 page_content
속성값이 모두 표현됩니다.
>>> str(doc_list)
"[Document(metadata={}, page_content='d1'), Document(metadata={}, page_content='d2')]"
랭체인에서는 프롬프트에 문서를 반영할 때, 이렇게 문자열 표현을 사용하는 경우가 많습니다.
그런데, PaikdabangMenuDocument
쿼리셋을 문자열 표현으로 출력하면
>>> qs = PaikdabangMenuDocument.objects.all()
>>> str(qs[:2])
다음과 같이 metadata
속성값과 page_content
속성값이 모두 표현되지 않습니다.
출력
‘<PaikdabangMenuDocumentQuerySet [<PaikdabangMenuDocument: PaikdabangMenuDocument object (1)>, <PaikdabangMenuDocument: PaikdabangMenuDocument object (2)>]>’
그럼 랭체인과 엮어서 사용할 경우, 아래와 같이 매번 리스트/사전 포맷으로 변환해야만 합니다.
변환하니까 문자열 표현에서 metadata
속성값과 page_content
속성값이 모두 잘 표현됩니다.
>>> qs = await PaikdabangMenuDocument.objects.search("빽다방 고카페인 음료 종류는?")
>>> doc_list = [{"metadata": doc.metadata, "page_content": doc.page_content} for doc in qs]
>>> str(doc_list)
"[{'metadata': {'source': '빽다방.txt'}, 'page_content': '5. 빽사이즈 원조커피(ICED)\\n - 빽다방 즐겨보세요 :) [주의. 564mg 고카페인으로 카페인에 민감한 어린이, 임산부는 섭취에 주의바랍니다]\\n - 가격방.txt'}, 'page_content': '5. 빽사이즈 원조커피(ICED)\\n - 빽다방의 BEST메뉴를 더 크게 즐겨보세요 :) [주어린이, 임산부는 섭취에 주의바랍니다]\\n - 가격: 4000원'}, {'metadata': {'source': '빽다방.txt'}, 'page_피 제로슈거(ICED)\\n - 빽다방의 BEST메뉴를 더 크게, 제로슈거로 즐겨보세요 :) [주의. 686mg 고카페인으로 카 - 가격: 4000원'}, {'metadata': {'source': '빽다방.txt'}, 'page_content': '6. 빽사이즈 원조커피 제로슈거(빽다방의 BEST메뉴를 더 크게, 제로슈거로 즐겨보세요 :) [주의. 686mg 고카페인으로 카페인에 민감한 어린이, 임산부는 섭취에 주의바랍니다]\\n - 가격: 4000원'}]"
그런데 매번 리스트/사전 포맷으로 변환하는 것은 번거롭습니다.
PaikdabangMenuDocumentQuerySet
클래스와 PaikdabangMenuDocument
클래스의 문자열 표현을 개선해보겠습니다.
8.2. QuerySet 클래스의 문자열 표현 개선¶
QuerySet
클래스에서는 문자열 표현에서 "<클래스명 데이터일부>"
포맷으로 생성토록 __repr__
메서드에 구현되어있습니다.
팁
파이썬 클래스 인스턴스에서는 repr(obj)
함수를 호출하면 __repr__
메서드가 호출되며,
str(obj)
함수를 호출하면 __str__
메서드가 있으면 호출되며, __str__
메서드가 없으면 __repr__
메서드가 호출됩니다.
__repr__
메서드를 재정의하여, 리스트 포맷으로 문자열을 생성토록 합니다.
class PaikdabangMenuDocumentQuerySet(models.QuerySet):
# ...
def __repr__(self):
return repr(list(self)) # QuerySet을 리스트처럼 출력
그럼 쿼리셋의 문자열 표현이 리스트 포맷으로 생성됩니다.
출력
‘[<PaikdabangMenuDocument: PaikdabangMenuDocument object (1)>, <PaikdabangMenuDocument: PaikdabangMenuDocument object (2)>]’
8.3. 모델 클래스의 문자열 표현 개선¶
모델 인스턴스의 문자열 표현은 "<PaikdabangMenuDocument: PaikdabangMenuDocument object (1)>"
입니다.
모델 클래스에는 __repr__
메서드와 __str__
메서드가 아래와 같이 재정의되어있기 때문입니다.
class Model(...):
# ...
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self)
def __str__(self):
return "%s object (%s)" % (self.__class__.__name__, self.pk)
PaikdabangMenuDocument
모델 클래스의 두 메서드를 아래와 같이 재정의해주세요.
__repr__
메서드에서는 metadata
속성값과 page_content
속성값을 모두 표현하도록 하고,
__str__
메서드에서는 __repr__
메서드를 호출하도록 합니다.
class PaikdabangMenuDocument(LifeCycleModelMixin, models.Model):
# ...
def __repr__(self):
return f"Document(metadata={self.metadata}, page_content={self.page_content!r})"
def __str__(self):
return self.__repr__()
이제 문서의 쿼리셋이나 문서 인스턴스를 문자열 표현에서 metadata
속성값과 page_content
속성값이 모두 잘 표현됨을 확인하실 수 있습니다.
>>> doc_list = await PaikdabangMenuDocument.objects.search("빽다방 고카페인 음료 종류는?")
>>> prompt = f"넌 AI Assistant. 모르는 건 모른다고 대답.\n\n[[빽다방 메뉴 정보]]\n{doc_list}"
>>> prompt
생성된 문자열
“넌 AI Assistant. 모르는 건 모른다고 대답.nn[[빽다방 메뉴 정보]]n[Document(metadata={‘source’: ‘빽다방.txt’}, page_content=’5. 빽사이 BEST메뉴를 더 크게 즐겨보세요 :) [주의. 564mg 고카페인으로 카페인에 민감한 어린이, 임산부는 섭취에 주의바랍니다]\n - 가격: 4000원’), Document=’5. 빽사이즈 원조커피(ICED)\n - 빽다방의 BEST메뉴를 더 크게 즐겨보세요 :) [주의. 564mg 고카페인으로 카페인에 민감한 어린이, 임산부는 섭취에 주의바랍니다]source’: ‘빽다방.txt’}, page_content=’6. 빽사이즈 원조커피 제로슈거(ICED)\n - 빽다방의 BEST메뉴를 더 크게, 제로슈거로 즐겨보세요 :) [주의. 686mg 고카페인으 가격: 4000원’), Document(metadata={‘source’: ‘빽다방.txt’}, page_content=’6. 빽사이즈 원조커피 제로슈거(ICED)\n - 빽다방의 BEST메뉴를 더 크게 고카페인으로 카페인에 민감한 어린이, 임산부는 섭취에 주의바랍니다]\n - 가격: 4000원’)]”
이제 랭체인 프롬프트에서도 간편하게 쿼리셋과 문서 인스턴스를 사용하실 수 있습니다. 😉