문서에서 커스텀 멀티 홉(multi-hop) 쿼리 만들기
문서에서 커스텀 멀티 홉(multi-hop) 쿼리 만들기
이 튜토리얼에서는 문서에서 커스텀 멀티 홉 쿼리를 만드는 방법을 배워요. 이건 표준 쿼리 유형으로는 만들 수 없는 쿼리를 생성하게 해 주는 아주 강력한 기능이에요. 여러분의 사용 사례에 더 특화된 쿼리를 만드는 데도 도움이 돼요.
출처: 문서
본문
샘플 문서 로드
GitLab handbook의 샘플 문서를 사용하고 있어요. 아래 명령으로 다운로드할 수 있어요.
! git clone https://huggingface.co/datasets/vibrantlabsai/Sample_Docs_Markdown
from langchain_community.document_loaders import DirectoryLoader, TextLoader
path = "Sample_Docs_Markdown/"
loader = DirectoryLoader(path, glob="**/*.md")
docs = loader.load()
KG 생성
문서로 기본 지식 그래프(knowledge graph)를 만들어요.
from ragas.testset.graph import KnowledgeGraph
from ragas.testset.graph import Node, NodeType
kg = KnowledgeGraph()
for doc in docs:
kg.nodes.append(
Node(
type=NodeType.DOCUMENT,
properties={
"page_content": doc.page_content,
"document_metadata": doc.metadata,
},
)
)
LLM과 Embedding 모델 설정
원하는 모델을 써도 되는데, 여기서는 open-ai 모델을 사용할게요.
from openai import OpenAI
from ragas.llms import llm_factory
from ragas.embeddings import OpenAIEmbeddings
openai_client = OpenAI()
llm = llm_factory("gpt-4o-mini", client=openai_client)
embedding = OpenAIEmbeddings(client=openai_client)
추출기와 관계 빌더 설정
멀티 홉 쿼리를 만들려면 어떤 문서 집합이 그 쿼리에 쓰일 수 있는지 파악해야 해요. Ragas는 문서/노드 사이의 관계를 사용해 멀티 홉 쿼리 생성에 적합한 노드를 판별해요. 예를 들어, 노드 A와 노드 B가 어떤 관계(엔티티나 keyphrase 중복 같은)로 연결되어 있다면 그 둘 사이에 멀티 홉 쿼리를 만들 수 있어요.
여기서는 추출기 2개와 관계 빌더 1개를 사용해요.
- Headline extractor: 문서에서 헤드라인을 추출해요
- Keyphrase extractor: 문서에서 핵심어(keyphrase)를 추출해요
- Headline splitter: 헤드라인을 기준으로 문서를 노드로 나눠요
- OverlapScore Builder: keyphrase 중복을 기준으로 노드 사이 관계를 만들어요
from ragas.testset.transforms import Parallel, apply_transforms
from ragas.testset.transforms import (
HeadlinesExtractor,
HeadlineSplitter,
KeyphrasesExtractor,
OverlapScoreBuilder,
)
headline_extractor = HeadlinesExtractor(llm=llm)
headline_splitter = HeadlineSplitter(min_tokens=300, max_tokens=1000)
keyphrase_extractor = KeyphrasesExtractor(
llm=llm, property_name="keyphrases", max_num=10
)
relation_builder = OverlapScoreBuilder(
property_name="keyphrases",
new_property_name="overlap_score",
threshold=0.01,
distance_threshold=0.9,
)
transforms = [
headline_extractor,
headline_splitter,
keyphrase_extractor,
relation_builder,
]
apply_transforms(kg, transforms=transforms)
Applying KeyphrasesExtractor: 6%|██████▏ | 2/36 [00:01<00:17, 1.94it/s]Property 'keyphrases' already exists in node 'a2f389'. Skipping!
Applying KeyphrasesExtractor: 17%|██████████████████▋ | 6/36 [00:01<00:04, 6.37it/s]Property 'keyphrases' already exists in node '3068c0'. Skipping!
Applying KeyphrasesExtractor: 53%|██████████████████████████████████████████████████████████▌ | 19/36 [00:02<00:01, 8.88it/s]Property 'keyphrases' already exists in node '854bf7'. Skipping!
Applying KeyphrasesExtractor: 78%|██████████████████████████████████████████████████████████████████████████████████████▎ | 28/36 [00:03<00:00, 9.73it/s]Property 'keyphrases' already exists in node '2eeb07'. Skipping!
Property 'keyphrases' already exists in node 'd68f83'. Skipping!
Applying KeyphrasesExtractor: 83%|████████████████████████████████████████████████████████████████████████████████████████████▌ | 30/36 [00:03<00:00, 9.35it/s]Property 'keyphrases' already exists in node '8fdbea'. Skipping!
Applying KeyphrasesExtractor: 89%|██████████████████████████████████████████████████████████████████████████████████████████████████▋ | 32/36 [00:04<00:00, 7.76it/s]Property 'keyphrases' already exists in node 'ef6ae0'. Skipping!
Persona 구성
자동 persona 생성기를 사용해 이 단계를 자동화할 수도 있어요.
from ragas.testset.persona import Persona
person1 = Persona(
name="gitlab employee",
role_description="A junior gitlab employee curious on workings on gitlab",
)
persona2 = Persona(
name="Hiring manager at gitlab",
role_description="A hiring manager at gitlab trying to underestand hiring policies in gitlab",
)
persona_list = [person1, persona2]
멀티 홉 쿼리 만들기
MultiHopQuerySynthesizer 를 상속받아 쿼리 생성을 위한 시나리오를 만드는 함수를 수정해요.
단계:
- 노드 사이의 관계를 기준으로 (nodeA, relationship, nodeB)의 적합 집합을 찾아요
- 각 적합 집합에 대해
- keyphrase를 하나 이상의 persona와 매칭해요
- (Nodes, Persona, Query Style, Query Length)의 모든 가능한 조합을 만들어요
- 조합에서 필요한 수만큼 쿼리를 샘플링해요
from dataclasses import dataclass
import typing as t
from ragas.testset.synthesizers.multi_hop.base import (
MultiHopQuerySynthesizer,
MultiHopScenario,
)
from ragas.testset.synthesizers.prompts import (
ThemesPersonasInput,
ThemesPersonasMatchingPrompt,
)
@dataclass
class MyMultiHopQuery(MultiHopQuerySynthesizer):
theme_persona_matching_prompt = ThemesPersonasMatchingPrompt()
async def _generate_scenarios(
self,
n: int,
knowledge_graph,
persona_list,
callbacks,
) -> t.List[MultiHopScenario]:
# query and get (node_a, rel, node_b) to create multi-hop queries
results = kg.find_two_nodes_single_rel(
relationship_condition=lambda rel: (
True if rel.type == "keyphrases_overlap" else False
)
)
num_sample_per_triplet = max(1, n // len(results))
scenarios = []
for triplet in results:
if len(scenarios) < n:
node_a, node_b = triplet[0], triplet[-1]
overlapped_keywords = triplet[1].properties["overlapped_items"]
if overlapped_keywords:
# match the keyword with a persona for query creation
themes = list(dict(overlapped_keywords).keys())
prompt_input = ThemesPersonasInput(
themes=themes, personas=persona_list
)
persona_concepts = (
await self.theme_persona_matching_prompt.generate(
data=prompt_input, llm=self.llm, callbacks=callbacks
)
)
overlapped_keywords = [list(item) for item in overlapped_keywords]
# prepare and sample possible combinations
base_scenarios = self.prepare_combinations(
[node_a, node_b],
overlapped_keywords,
personas=persona_list,
persona_item_mapping=persona_concepts.mapping,
property_name="keyphrases",
)
# get number of required samples from this triplet
base_scenarios = self.sample_diverse_combinations(
base_scenarios, num_sample_per_triplet
)
scenarios.extend(base_scenarios)
return scenarios
query = MyMultiHopQuery(llm=llm)
scenarios = await query.generate_scenarios(
n=10, knowledge_graph=kg, persona_list=persona_list
)
scenarios[4]
MultiHopScenario(
nodes=2
combinations=['Diversity Inclusion & Belonging', 'Diversity, Inclusion & Belonging Goals']
style=Web search like queries
length=short
persona=name='Hiring manager at gitlab' role_description='A hiring manager at gitlab trying to underestand hiring policies in gitlab')
멀티 홉 쿼리 실행
result = await query.generate_sample(scenario=scenarios[-1])
result.user_input
Output
'How does GitLab ensure that its DIB roundtables are effective in promoting diversity and inclusion?'
야! 멀티 홉 쿼리를 만들었어요. 이제 문서 사이의 관계를 만들고 탐색하면서 그런 쿼리들을 얼마든지 만들 수 있어요.