Milvus와 Camel을 사용한 검색 증강 생성(RAG)
이 가이드에서는 CAMEL과 Milvus를 사용하여 검색 증강 생성(RAG) 시스템을 구축하는 방법을 설명합니다.
RAG 시스템은 검색 시스템과 생성 모델을 결합하여 주어진 프롬프트에 따라 새 텍스트를 생성합니다. 이 시스템은 먼저 Milvus를 사용하여 말뭉치에서 관련 문서를 검색한 다음 생성 모델을 사용하여 검색된 문서를 기반으로 새 텍스트를 생성합니다.
CAMEL은 다중 에이전트 프레임워크입니다. Milvus는 세계에서 가장 진보된 오픈 소스 벡터 데이터베이스로, 임베딩 유사도 검색 및 AI 애플리케이션을 강화하기 위해 구축되었습니다.
이 노트북에서는 사용자 지정 방식과 자동 방식 모두에서 CAMEL 검색 모듈의 사용법을 보여드립니다. 또한 AutoRetriever
과 ChatAgent
을 결합하는 방법과 Function Calling
을 사용하여 AutoRetriever
과 RolePlaying
을 결합하는 방법도 보여드립니다.
크게 4가지로 구성됩니다:
- 사용자 정의 RAG
- 자동 RAG
- 자동 RAG를 사용한 싱글 에이전트
- Auto RAG를 사용한 롤플레잉
데이터 로드
먼저 https://arxiv.org/pdf/2303.17760.pdf 에서 CAMEL 용지를 로드해 보겠습니다. 이것이 로컬 예제 데이터가 될 것입니다.
$ pip install -U "camel-ai[all]" pymilvus
Google Colab을 사용하는 경우 방금 설치한 종속성을 활성화하려면 런타임을 다시 시작해야 할 수 있습니다(화면 상단의 "런타임" 메뉴를 클릭하고 드롭다운 메뉴에서 "세션 다시 시작"을 선택).
import os
import requests
os.makedirs("local_data", exist_ok=True)
url = "https://arxiv.org/pdf/2303.17760.pdf"
response = requests.get(url)
with open("local_data/camel paper.pdf", "wb") as file:
file.write(response.content)
1. 사용자 지정 RAG
이 섹션에서는 사용자 정의 RAG 파이프라인을 설정하고 VectorRetriever
을 예로 들어 보겠습니다. 임베딩 모델은 OpenAIEmbedding
, 저장소는 MilvusStorage
으로 설정하겠습니다.
OpenAI 임베딩을 설정하려면 아래에서 OPENAI_API_KEY
을 설정해야 합니다.
os.environ["OPENAI_API_KEY"] = "Your Key"
임베딩 인스턴스를 가져와 설정합니다:
from camel.embeddings import OpenAIEmbedding
embedding_instance = OpenAIEmbedding()
벡터 스토리지 인스턴스를 가져와서 설정합니다:
from camel.storages import MilvusStorage
storage_instance = MilvusStorage(
vector_dim=embedding_instance.get_output_dim(),
url_and_api_key=(
"./milvus_demo.db", # Your Milvus connection URI
"", # Your Milvus token
),
collection_name="camel_paper",
)
url_and_api_key
:
- 로컬 파일(예:
./milvus.db
)을 Milvus 연결 URI로 사용하는 것이 가장 편리한 방법인데, Milvus Lite를 자동으로 활용하여 모든 데이터를 이 파일에 저장하기 때문입니다. - 데이터 규모가 큰 경우, 도커나 쿠버네티스에 더 성능이 좋은 Milvus 서버를 설정할 수 있습니다. 이 설정에서는 서버 URL(예:
http://localhost:19530
)을 사용하세요. - 밀버스의 완전 관리형 클라우드 서비스인 질리즈 클라우드를 사용하려면, 질리즈 클라우드의 퍼블릭 엔드포인트와 API 키에 해당하는 연결 uri와 토큰을 조정하세요.
리트리버 인스턴스를 가져와 설정합니다:
기본적으로 similarity_threshold
은 0.75로 설정되어 있습니다. 변경할 수 있습니다.
from camel.retrievers import VectorRetriever
vector_retriever = VectorRetriever(
embedding_model=embedding_instance, storage=storage_instance
)
통합 Unstructured Module
을 사용하여 콘텐츠를 작은 청크로 분할하고, 콘텐츠는 chunk_by_title
함수를 사용하여 자동으로 분할되며, 각 청크의 최대 문자는 500 자로 OpenAIEmbedding
에 적합한 길이입니다. 청크의 모든 텍스트가 벡터 스토리지 인스턴스에 임베드되어 저장되므로 다소 시간이 걸리므로 잠시만 기다려주세요.
vector_retriever.process(content_input_path="local_data/camel paper.pdf")
[nltk_data] Downloading package punkt to /root/nltk_data...
[nltk_data] Unzipping tokenizers/punkt.zip.
[nltk_data] Downloading package averaged_perceptron_tagger to
[nltk_data] /root/nltk_data...
[nltk_data] Unzipping taggers/averaged_perceptron_tagger.zip.
이제 쿼리를 입력하여 벡터 스토리지에서 정보를 검색할 수 있습니다. 기본적으로 코사인 유사도 점수가 가장 높은 상위 1개 청크의 텍스트 콘텐츠를 반환하며, 검색된 콘텐츠가 쿼리와 관련이 있는지 확인하려면 유사도 점수가 0.75보다 높아야 합니다. top_k
값을 변경할 수도 있습니다.
반환된 문자열 목록에는
- 유사도 점수
- 콘텐츠 경로
- 메타데이터
- 텍스트
retrieved_info = vector_retriever.query(query="What is CAMEL?", top_k=1)
print(retrieved_info)
[{'similarity score': '0.8321675658226013', 'content path': 'local_data/camel paper.pdf', 'metadata': {'last_modified': '2024-04-19T14:40:00', 'filetype': 'application/pdf', 'page_number': 45}, 'text': 'CAMEL Data and Code License The intended purpose and licensing of CAMEL is solely for research use. The source code is licensed under Apache 2.0. The datasets are licensed under CC BY NC 4.0, which permits only non-commercial usage. It is advised that any models trained using the dataset should not be utilized for anything other than research purposes.\n\n45'}]
관련 없는 쿼리를 해보겠습니다:
retrieved_info_irrelevant = vector_retriever.query(
query="Compared with dumpling and rice, which should I take for dinner?", top_k=1
)
print(retrieved_info_irrelevant)
[{'text': 'No suitable information retrieved from local_data/camel paper.pdf with similarity_threshold = 0.75.'}]
2. 자동 RAG
이 섹션에서는 기본 설정으로 AutoRetriever
을 실행해 보겠습니다. 기본 임베딩 모델로는 OpenAIEmbedding
, 기본 벡터 저장소로는 Milvus
을 사용합니다.
여러분이 해야 할 일은
- 콘텐츠 입력 경로를 설정합니다(로컬 경로 또는 원격 URL일 수 있음).
- Milvus의 원격 URL 및 API 키를 설정합니다.
- 쿼리 제공
자동 RAG 파이프라인은 지정된 콘텐츠 입력 경로에 대한 컬렉션을 생성하고, 컬렉션 이름은 콘텐츠 입력 경로 이름에 따라 자동으로 설정되며, 컬렉션이 존재하는 경우 직접 검색을 수행합니다.
from camel.retrievers import AutoRetriever
from camel.types import StorageType
auto_retriever = AutoRetriever(
url_and_api_key=(
"./milvus_demo.db", # Your Milvus connection URI
"", # Your Milvus token
),
storage_type=StorageType.MILVUS,
embedding_model=embedding_instance,
)
retrieved_info = auto_retriever.run_vector_retriever(
query="What is CAMEL-AI",
content_input_paths=[
"local_data/camel paper.pdf", # example local path
"https://www.camel-ai.org/", # example remote url
],
top_k=1,
return_detailed_info=True,
)
print(retrieved_info)
Original Query:
{What is CAMEL-AI}
Retrieved Context:
{'similarity score': '0.8252888321876526', 'content path': 'local_data/camel paper.pdf', 'metadata': {'last_modified': '2024-04-19T14:40:00', 'filetype': 'application/pdf', 'page_number': 7}, 'text': ' Section 3.2, to simulate assistant-user cooperation. For our analysis, we set our attention on AI Society setting. We also gathered conversational data, named CAMEL AI Society and CAMEL Code datasets and problem-solution pairs data named CAMEL Math and CAMEL Science and analyzed and evaluated their quality. Moreover, we will discuss potential extensions of our framework and highlight both the risks and opportunities that future AI society might present.'}
{'similarity score': '0.8378663659095764', 'content path': 'https://www.camel-ai.org/', 'metadata': {'filetype': 'text/html', 'languages': ['eng'], 'page_number': 1, 'url': 'https://www.camel-ai.org/', 'link_urls': ['#h.3f4tphhd9pn8', 'https://join.slack.com/t/camel-ai/shared_invite/zt-2g7xc41gy-_7rcrNNAArIP6sLQqldkqQ', 'https://discord.gg/CNcNpquyDc'], 'link_texts': [None, None, None], 'emphasized_text_contents': ['Mission', 'CAMEL-AI.org', 'is an open-source community dedicated to the study of autonomous and communicative agents. We believe that studying these agents on a large scale offers valuable insights into their behaviors, capabilities, and potential risks. To facilitate research in this field, we provide, implement, and support various types of agents, tasks, prompts, models, datasets, and simulated environments.', 'Join us via', 'Slack', 'Discord', 'or'], 'emphasized_text_tags': ['span', 'span', 'span', 'span', 'span', 'span', 'span']}, 'text': 'Mission\n\nCAMEL-AI.org is an open-source community dedicated to the study of autonomous and communicative agents. We believe that studying these agents on a large scale offers valuable insights into their behaviors, capabilities, and potential risks. To facilitate research in this field, we provide, implement, and support various types of agents, tasks, prompts, models, datasets, and simulated environments.\n\nJoin us via\n\nSlack\n\nDiscord\n\nor'}
3. 자동 RAG가 있는 단일 에이전트
이 섹션에서는 AutoRetriever
와 하나의 ChatAgent
를 결합하는 방법을 보여드리겠습니다.
에이전트 함수를 설정해 보겠습니다. 이 함수에서는 이 에이전트에 쿼리를 제공하여 응답을 얻을 수 있습니다.
from camel.agents import ChatAgent
from camel.messages import BaseMessage
from camel.types import RoleType
from camel.retrievers import AutoRetriever
from camel.types import StorageType
def single_agent(query: str) -> str:
# Set agent role
assistant_sys_msg = BaseMessage(
role_name="Assistant",
role_type=RoleType.ASSISTANT,
meta_dict=None,
content="""You are a helpful assistant to answer question,
I will give you the Original Query and Retrieved Context,
answer the Original Query based on the Retrieved Context,
if you can't answer the question just say I don't know.""",
)
# Add auto retriever
auto_retriever = AutoRetriever(
url_and_api_key=(
"./milvus_demo.db", # Your Milvus connection URI
"", # Your Milvus token
),
storage_type=StorageType.MILVUS,
embedding_model=embedding_instance,
)
retrieved_info = auto_retriever.run_vector_retriever(
query=query,
content_input_paths=[
"local_data/camel paper.pdf", # example local path
"https://www.camel-ai.org/", # example remote url
],
# vector_storage_local_path="storage_default_run",
top_k=1,
return_detailed_info=True,
)
# Pass the retrieved infomation to agent
user_msg = BaseMessage.make_user_message(role_name="User", content=retrieved_info)
agent = ChatAgent(assistant_sys_msg)
# Get response
assistant_response = agent.step(user_msg)
return assistant_response.msg.content
print(single_agent("What is CAMEL-AI"))
CAMEL-AI is an open-source community dedicated to the study of autonomous and communicative agents. It provides, implements, and supports various types of agents, tasks, prompts, models, datasets, and simulated environments to facilitate research in this field.
4. 자동 RAG로 롤플레잉하기
이 섹션에서는 Function Calling
을 적용하여 RETRIEVAL_FUNCS
과 RolePlaying
을 결합하는 방법을 보여드리겠습니다.
from typing import List
from colorama import Fore
from camel.agents.chat_agent import FunctionCallingRecord
from camel.configs import ChatGPTConfig
from camel.functions import (
MATH_FUNCS,
RETRIEVAL_FUNCS,
)
from camel.societies import RolePlaying
from camel.types import ModelType
from camel.utils import print_text_animated
def role_playing_with_rag(
task_prompt, model_type=ModelType.GPT_4O, chat_turn_limit=10
) -> None:
task_prompt = task_prompt
user_model_config = ChatGPTConfig(temperature=0.0)
function_list = [
*MATH_FUNCS,
*RETRIEVAL_FUNCS,
]
assistant_model_config = ChatGPTConfig(
tools=function_list,
temperature=0.0,
)
role_play_session = RolePlaying(
assistant_role_name="Searcher",
user_role_name="Professor",
assistant_agent_kwargs=dict(
model_type=model_type,
model_config=assistant_model_config,
tools=function_list,
),
user_agent_kwargs=dict(
model_type=model_type,
model_config=user_model_config,
),
task_prompt=task_prompt,
with_task_specify=False,
)
print(
Fore.GREEN
+ f"AI Assistant sys message:\n{role_play_session.assistant_sys_msg}\n"
)
print(Fore.BLUE + f"AI User sys message:\n{role_play_session.user_sys_msg}\n")
print(Fore.YELLOW + f"Original task prompt:\n{task_prompt}\n")
print(
Fore.CYAN
+ f"Specified task prompt:\n{role_play_session.specified_task_prompt}\n"
)
print(Fore.RED + f"Final task prompt:\n{role_play_session.task_prompt}\n")
n = 0
input_msg = role_play_session.init_chat()
while n < chat_turn_limit:
n += 1
assistant_response, user_response = role_play_session.step(input_msg)
if assistant_response.terminated:
print(
Fore.GREEN
+ (
"AI Assistant terminated. Reason: "
f"{assistant_response.info['termination_reasons']}."
)
)
break
if user_response.terminated:
print(
Fore.GREEN
+ (
"AI User terminated. "
f"Reason: {user_response.info['termination_reasons']}."
)
)
break
# Print output from the user
print_text_animated(Fore.BLUE + f"AI User:\n\n{user_response.msg.content}\n")
# Print output from the assistant, including any function
# execution information
print_text_animated(Fore.GREEN + "AI Assistant:")
tool_calls: List[FunctionCallingRecord] = assistant_response.info["tool_calls"]
for func_record in tool_calls:
print_text_animated(f"{func_record}")
print_text_animated(f"{assistant_response.msg.content}\n")
if "CAMEL_TASK_DONE" in user_response.msg.content:
break
input_msg = assistant_response.msg
정의된 리트리버 함수로 롤플레잉을 실행합니다:
role_playing_with_rag(
task_prompt="""What is the main termination reasons for AI Society
dataset, how many number of messages did camel decided to
limit, what's the value plus 100? You should refer to the
content in path camel/local_data/camel paper.pdf"""
)
AI Assistant sys message:
BaseMessage(role_name='Searcher', role_type=<RoleType.ASSISTANT: 'assistant'>, meta_dict={'task': "What is the main termination reasons for AI Society dataset, how many number of messages did camel decided to limit, what's the value plus 100?", 'assistant_role': 'Searcher', 'user_role': 'Professor'}, content="===== RULES OF ASSISTANT =====\nNever forget you are a Searcher and I am a Professor. Never flip roles! Never instruct me!\nWe share a common interest in collaborating to successfully complete a task.\nYou must help me to complete the task.\nHere is the task: What is the main termination reasons for AI Society dataset, how many number of messages did camel decided to limit, what's the value plus 100?. Never forget our task!\nI must instruct you based on your expertise and my needs to complete the task.\n\nI must give you one instruction at a time.\nYou must write a specific solution that appropriately solves the requested instruction and explain your solutions.\nYou must decline my instruction honestly if you cannot perform the instruction due to physical, moral, legal reasons or your capability and explain the reasons.\nUnless I say the task is completed, you should always start with:\n\nSolution: <YOUR_SOLUTION>\n\n<YOUR_SOLUTION> should be very specific, include detailed explanations and provide preferable detailed implementations and examples and lists for task-solving.\nAlways end <YOUR_SOLUTION> with: Next request.")
AI User sys message:
BaseMessage(role_name='Professor', role_type=<RoleType.USER: 'user'>, meta_dict={'task': "What is the main termination reasons for AI Society dataset, how many number of messages did camel decided to limit, what's the value plus 100?", 'assistant_role': 'Searcher', 'user_role': 'Professor'}, content='===== RULES OF USER =====\nNever forget you are a Professor and I am a Searcher. Never flip roles! You will always instruct me.\nWe share a common interest in collaborating to successfully complete a task.\nI must help you to complete the task.\nHere is the task: What is the main termination reasons for AI Society dataset, how many number of messages did camel decided to limit, what\'s the value plus 100?. Never forget our task!\nYou must instruct me based on my expertise and your needs to solve the task ONLY in the following two ways:\n\n1. Instruct with a necessary input:\nInstruction: <YOUR_INSTRUCTION>\nInput: <YOUR_INPUT>\n\n2. Instruct without any input:\nInstruction: <YOUR_INSTRUCTION>\nInput: None\n\nThe "Instruction" describes a task or question. The paired "Input" provides further context or information for the requested "Instruction".\n\nYou must give me one instruction at a time.\nI must write a response that appropriately solves the requested instruction.\nI must decline your instruction honestly if I cannot perform the instruction due to physical, moral, legal reasons or my capability and explain the reasons.\nYou should instruct me not ask me questions.\nNow you must start to instruct me using the two ways described above.\nDo not add anything else other than your instruction and the optional corresponding input!\nKeep giving me instructions and necessary inputs until you think the task is completed.\nWhen the task is completed, you must only reply with a single word <CAMEL_TASK_DONE>.\nNever say <CAMEL_TASK_DONE> unless my responses have solved your task.')
Original task prompt:
What is the main termination reasons for AI Society dataset, how many number of messages did camel decided to limit, what's the value plus 100?
Specified task prompt:
None
Final task prompt:
What is the main termination reasons for AI Society dataset, how many number of messages did camel decided to limit, what's the value plus 100?
AI User:
Instruction: Provide a summary of the main termination reasons in the AI Society dataset.
Input: None
AI Assistant:
Function Execution: local_retriever
Args: {'query': 'main termination reasons for AI Society dataset'}
Result: Original Query:
{main termination reasons for AI Society dataset}
Retrieved Context:
Next we examine the conversation termination reasons for both AI Society and Code datasets. As can be seen in Figure 8, the main termination reasons for AI Society dataset is Assistant Instruct whereas for Code it is Token Limit. The latter is expected as the since responses that contain code tend to be long. It is also interesting to note that in both datasets, the termination due to Maximum Number of Messages is low indicating that the limit of 40 maximum messages is reasonable. Our decision t
Solution: The main termination reason for the AI Society dataset is "Assistant Instruct." This indicates that conversations in the AI Society dataset typically end when the assistant is instructed to terminate the conversation.
Next request.
AI User:
Instruction: Identify the number of messages that the camel decided to limit in the context provided.
Input: None
AI Assistant:
Solution: The context provided from the local retriever indicates that the camel decided to limit the number of messages to a maximum of 40 messages. This is mentioned in the retrieved context where it states that the termination due to Maximum Number of Messages is low, indicating that the limit of 40 maximum messages is reasonable.
Next request.
AI User:
Instruction: Calculate the value of the message limit plus 100.
Input: None
AI Assistant:
Function Execution: add
Args: {'a': 40, 'b': 100}
Result: 140
Solution: The value of the message limit plus 100 is 140.
Next request.
AI User:
CAMEL_TASK_DONE
AI Assistant:
Solution: Understood, the task is completed.
Next request.