🚀 免費嘗試 Zilliz Cloud,完全托管的 Milvus,體驗速度提升 10 倍!立即嘗試

milvus-logo
LFAI
主頁
  • 整合

使用 Mistral AI、Milvus 和 Llama-agents 的多代理系統

本手冊的目標

在本筆記簿中,我們將探討不同的想法:

  • 1️⃣ Store Data into Milvus:學習將資料儲存到Milvus中,Milvus是為高速相似性搜索和人工智能應用而設計的高效向量資料庫。

  • 2️⃣使用llama-index與Mistral模型進行資料查詢:探索如何結合Mistral模型使用llama-index查詢儲存於Milvus的資料。

  • 3️⃣建立自動化的資料搜尋與讀取代理:建立能根據使用者查詢自動搜尋與讀取資料的代理。這些自動化代理程式可提供快速、精確的回覆,減少手動搜尋的工作量,進而提升使用者體驗。

  • 4️⃣開發基於使用者查詢的元資料篩選代理程式:實施可自動根據使用者查詢產生元資料篩選程式的代理程式,精煉搜尋結果並使其符合上下文,避免混亂並提高所擷取資訊的準確性,即使是複雜的查詢也不例外。

  • 🔍 摘要 在本筆記簿結束時,您將全面了解如何使用 Milvus、llama-index 搭配 llama-agents 以及 Mistral 模型來建立一個強大且有效率的資料檢索系統。

Milvus

Milvus 是一個開放原始碼的向量資料庫,以向量嵌入和相似性搜尋為 AI 應用程式提供動力。

在本筆記簿中,我們使用 Milvus Lite,它是 Milvus 的輕量級版本。

使用 Milvus Lite,您可以在幾分鐘內開始使用向量相似性搜尋建立 AI 應用程式!Milvus Lite 適合在下列環境中執行:

  • Jupyter Notebook / Google Colab
  • 筆記型電腦
  • 邊緣裝置

image.png image.png

llama-agents

llama-agents 可讓代理以微服務的方式執行。這樣就能上下擴充服務。

llama-index

LlamaIndex 是 LLM 應用程式的資料框架。它提供的工具包括

  • 資料連接器可從原始來源和格式擷取現有資料。
  • 資料索引將您的資料結構化,使其成為 LLM 易於使用且效能優異的中間表示形式。
  • 引擎提供自然語言存取您的資料。
  • 代理是由 LLM 驅動的知識工作者,透過工具來增強,從簡單的輔助功能到 API 整合等等。

image.png image.png

Mistral AI

Mistral AI 是一個建立 LLM 與 Embeddings 模型的研究實驗室,他們最近發表了新版本的模型,Mistral Nemo 與 Mistral Large,這兩個模型在 RAG 與函式呼叫方面表現得特別好。正因為如此,我們將在本筆記本中使用它們。

安裝相依性

$ pip install llama-agents pymilvus openai python-dotenv
$ pip install llama-index-vector-stores-milvus llama-index-readers-file llama-index-llms-ollama llama-index-llms-mistralai llama-index-embeddings-mistralai
# NOTE: This is ONLY necessary in jupyter notebook.
# Details: Jupyter runs an event-loop behind the scenes.
#          This results in nested event-loops when we start an event-loop to make async queries.
#          This is normally not allowed, we use nest_asyncio to allow it for convenience.
import nest_asyncio

nest_asyncio.apply()

取得 Mistral 的 API 金鑰

您可以從Mistral Cloud Console 取得 Mistral API 金鑰。

"""
load_dotenv reads key-value pairs from a .env file and can set them as environment variables.
This is useful to avoid leaking your API key for example :D
"""

from dotenv import load_dotenv
import os

load_dotenv()
True

下載資料

$ mkdir -p 'data/10k/'
$ wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/10k/uber_2021.pdf' -O 'data/10k/uber_2021.pdf'
$ wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/10k/lyft_2021.pdf' -O 'data/10k/lyft_2021.pdf'

準備嵌入模型

我們定義本筆記本使用的 Embedding Model。我們使用mistral-embed ,這是一個由 Mistral 開發的 Embedding Model,它已經針對 Retrievals 訓練過,這使得它對於我們的 Agentic RAG 系統來說是一個非常好的模型。詳情請參閱 Mistral 文件的Embedding頁面。

from llama_index.core import Settings
from llama_index.embeddings.mistralai import MistralAIEmbedding

# Define the default Embedding model used in this Notebook.
# We are using Mistral Models, so we are also using Mistral Embeddings

Settings.embed_model = MistralAIEmbedding(model_name="mistral-embed")

定義 LLM 模型

Llama Index 使用 LLM 來回應提示和查詢,並負責撰寫自然語言回應。 我們定義 Mistral Nemo 為預設。Nemo 提供最多 128k tokens 的大型上下文視窗。它的推理能力、世界知識和編碼準確度在同級產品中都是最先進的。

from llama_index.llms.ollama import Ollama

Settings.llm = Ollama("mistral-nemo")

安裝 Milvus 並載入資料

Milvus是一個廣受歡迎的開放原始碼向量資料庫,以高效能、可擴充的向量相似性搜尋為 AI 應用程式提供動力。

  • 將 uri 設定為本機檔案,例如./milvus.db ,是最方便的方法,因為它會自動利用Milvus Lite將所有資料儲存在此檔案中。
  • 如果您有大規模的資料,例如超過一百萬個向量,您可以在Docker 或 Kubernetes 上架設效能更高的 Milvus 伺服器。在此設定中,請使用伺服器的 uri,例如http://localhost:19530 ,作為您的 uri。
  • 如果您想使用Zilliz Cloud,Milvus 的完整管理雲端服務,請調整 uri 和 token,對應 Zilliz Cloud 的Public Endpoint 和 API key
from llama_index.vector_stores.milvus import MilvusVectorStore
from llama_index.core import (
    SimpleDirectoryReader,
    VectorStoreIndex,
    StorageContext,
    load_index_from_storage,
)
from llama_index.core.tools import QueryEngineTool, ToolMetadata

input_files = ["./data/10k/lyft_2021.pdf", "./data/10k/uber_2021.pdf"]

# Create a single Milvus vector store
vector_store = MilvusVectorStore(
    uri="./milvus_demo.db", dim=1024, overwrite=False, collection_name="companies_docs"
)

# Create a storage context with the Milvus vector store
storage_context = StorageContext.from_defaults(vector_store=vector_store)

# Load data
docs = SimpleDirectoryReader(input_files=input_files).load_data()

# Build index
index = VectorStoreIndex.from_documents(docs, storage_context=storage_context)

# Define the query engine
company_engine = index.as_query_engine(similarity_top_k=3)

定義工具

建立有效代理程式的關鍵步驟之一,就是定義它可以用來執行任務的工具。這些工具基本上是代理程式可以用來擷取資訊或執行動作的函式或服務。

下面,我們將定義兩個工具,讓我們的代理可以用來查詢 2021 年 Lyft 和 Uber 的財務資訊。這些工具將會整合到我們的代理程式中,讓代理程式能夠以精確且相關的資訊回應自然語言查詢。

如果您看一下我們在頂端的圖表,這就是「Agent 服務」。

# Define the different tools that can be used by our Agent.
query_engine_tools = [
    QueryEngineTool(
        query_engine=company_engine,
        metadata=ToolMetadata(
            name="lyft_10k",
            description=(
                "Provides information about Lyft financials for year 2021. "
                "Use a detailed plain text question as input to the tool."
                "Do not attempt to interpret or summarize the data."
            ),
        ),
    ),
    QueryEngineTool(
        query_engine=company_engine,
        metadata=ToolMetadata(
            name="uber_10k",
            description=(
                "Provides information about Uber financials for year 2021. "
                "Use a detailed plain text question as input to the tool."
                "Do not attempt to interpret or summarize the data."
            ),
        ),
    ),
]
from llama_index.llms.ollama import Ollama
from llama_index.llms.mistralai import MistralAI

# Set up the agent
llm = Ollama(model="mistral-nemo")

response = llm.predict_and_call(
    query_engine_tools,
    user_msg="Could you please provide a comparison between Lyft and Uber's total revenues in 2021?",
    allow_parallel_tool_calls=True,
)

# Example usage without metadata filtering
print("Response without metadata filtering:")
print(response)
Response without metadata filtering:
The revenue for Lyft in 2021 was $3.84 billion.

Uber's total revenue for the year ended December 31, 2021 was $17,455 million.

元資料篩選

Milvus支援Metadata 過濾,這是一種技術,可讓您根據與資料相關的特定屬性或標籤,精細並縮小搜尋結果的範圍。當您擁有大量資料,且只需要擷取符合特定條件的相關資料子集時,此功能尤其有用。

元資料篩選的使用案例

  • 搜尋結果的精確度:透過套用元資料篩選器,您可以確保搜尋結果與使用者的查詢高度相關。例如,如果您有一系列財務文件,您可以根據公司名稱、年份或任何其他相關的元資料來篩選這些文件。

  • 效率:元資料篩選有助於減少需要處理的資料量,使搜尋作業更有效率。這在處理大型資料集時尤其有利。

  • 客製化:不同的使用者或應用程式可能有不同的需求。元資料篩選可讓您自訂搜尋結果,以滿足特定需求,例如擷取特定年份或公司的文件。

使用範例

在下面的程式碼區塊中,元資料篩選被用來建立一個篩選的查詢引擎,根據特定的元資料鍵值對來擷取文件:file_namelyft_2021.pdf

下面定義的QueryEngineTool 比上面定義的更通用,在上面的定義中,我們每個公司(Uber 和 Lyft)都有一個工具,在這個定義中,它更通用。我們只知道我們有關於公司的財務文件,但僅止於此。 透過加入 Metadata Filtering,我們可以過濾只從特定文件取得的資料。

from llama_index.core.vector_stores import ExactMatchFilter, MetadataFilters

# Example usage with metadata filtering
filters = MetadataFilters(
    filters=[ExactMatchFilter(key="file_name", value="lyft_2021.pdf")]
)

print(f"filters: {filters}")
filtered_query_engine = index.as_query_engine(filters=filters)

# Define query engine tools with the filtered query engine
query_engine_tools = [
    QueryEngineTool(
        query_engine=filtered_query_engine,
        metadata=ToolMetadata(
            name="company_docs",
            description=(
                "Provides information about various companies' financials for year 2021. "
                "Use a detailed plain text question as input to the tool."
                "Use this tool to retrieve specific data points about a company. "
                "Do not attempt to interpret or summarize the data."
            ),
        ),
    ),
]
filters: filters=[MetadataFilter(key='file_name', value='lyft_2021.pdf', operator=<FilterOperator.EQ: '=='>)] condition=<FilterCondition.AND: 'and'>

函式呼叫

Mistral Nemo 和 Large 支援原生函式呼叫。透過 LLM 上的predict_and_call 函式,可與 LlamaIndex 工具無縫整合。這允許使用者附加任何工具,並讓 LLM 決定要呼叫哪些工具 (如果有)。

您可以在 llama-index 網站上瞭解更多關於Agents的資訊。

# Set up the LLM we will use for Function Calling

llm = Ollama(model="mistral-nemo")

與代理互動

現在我們可以實作 Metadata 過濾:

  1. 在第一張圖中,Agent 應該無法找到任何與使用者查詢相關的資訊,因為這是關於 Uber 的資訊,而我們只會篩選關於 Lyft 的文件。
  2. 在第二個例子中,Agent 應該可以找到關於 Lyft 的資訊,因為我們只會搜尋關於 Lyft 的文件。
response = llm.predict_and_call(
    query_engine_tools,
    user_msg="How many employees does Uber have?",
    allow_parallel_tool_calls=True,
)
print(response)
I'm unable to provide information about Uber's employee count as it's outside the given Lyft context.
response = llm.predict_and_call(
    query_engine_tools,
    user_msg="What are the risk factors for Lyft?",
    allow_parallel_tool_calls=True,
)

print(response)
Investing in Lyft carries significant risks. These include general economic factors like impacts from pandemics or crises, operational factors such as competition, pricing changes, and driver/ride growth unpredictability, insurance coverage issues, autonomous vehicle technology uncertainties, reputational concerns, potential security breaches, reliance on third-party services, and challenges in expanding platform offerings. Lyft's business operations are subject to numerous other risks not explicitly mentioned here, which could also harm its financial condition and prospects.

沒有元資料篩選的混淆範例

> Question: What are the risk factors for Uber?

> Response without metadata filtering:
Based on the provided context, which pertains to Lyft's Risk Factors section in their Annual Report, some of the potential risk factors applicable to a company like Uber might include:

- General economic factors such as the impact of global pandemics or other crises on ride-sharing demand.
- Operational factors like competition in ride-hailing services, unpredictability in results of operations, and uncertainty about market growth for ridesharing and related services.
- Risks related to attracting and retaining qualified drivers and riders.

在這個範例中,系統錯誤地提供了關於 Lyft 而非 Uber 的資訊,導致了誤導性的回應。系統一開始就說它沒有這些資訊,但接著又繼續說下去。

使用代理程式擷取元資料篩選器

為了解決這個問題,我們可以使用代理程式從使用者的問題中自動擷取元資料篩選器,並在回答問題的過程中套用這些篩選器。這可確保系統擷取正確的相關資訊。

程式碼範例

以下是一個程式碼範例,示範如何使用代理從使用者的問題中擷取元資料篩選器來建立篩選式查詢引擎:

說明

  • Prompt Template:PromptTemplate 類用於定義從使用者問題中抽取元資料篩選器的範本。該模板指示語言模型考慮公司名稱、年份和其他相關屬性。

  • LLM: Mistral Nemo 用來根據使用者的問題產生元資料篩選器。模型會根據問題和範本來擷取相關的篩選條件。

  • 元資料篩選器:LLM 的回應會被解析以建立MetadataFilters 物件。如果沒有提及特定的篩選條件,則會傳回一個空的MetadataFilters 物件。

  • 過濾查詢引擎index.as_query_engine(filters=metadata_filters) 方法會建立一個查詢引擎,將擷取的元資料過濾器套用至索引。這可確保只擷取符合篩選條件的文件。

from llama_index.core.prompts.base import PromptTemplate


# Function to create a filtered query engine
def create_query_engine(question):
    # Extract metadata filters from question using a language model
    prompt_template = PromptTemplate(
        "Given the following question, extract relevant metadata filters.\n"
        "Consider company names, years, and any other relevant attributes.\n"
        "Don't write any other text, just the MetadataFilters object"
        "Format it by creating a MetadataFilters like shown in the following\n"
        "MetadataFilters(filters=[ExactMatchFilter(key='file_name', value='lyft_2021.pdf')])\n"
        "If no specific filters are mentioned, returns an empty MetadataFilters()\n"
        "Question: {question}\n"
        "Metadata Filters:\n"
    )

    prompt = prompt_template.format(question=question)
    llm = Ollama(model="mistral-nemo")
    response = llm.complete(prompt)

    metadata_filters_str = response.text.strip()
    if metadata_filters_str:
        metadata_filters = eval(metadata_filters_str)
        print(f"eval: {metadata_filters}")
        return index.as_query_engine(filters=metadata_filters)
    return index.as_query_engine()
response = create_query_engine(
    "What is Uber revenue? This should be in the file_name: uber_2021.pdf"
)
eval: filters=[MetadataFilter(key='file_name', value='uber_2021.pdf', operator=<FilterOperator.EQ: '=='>)] condition=<FilterCondition.AND: 'and'>
## Example usage with metadata filtering
question = "What is Uber revenue? This should be in the file_name: uber_2021.pdf"
filtered_query_engine = create_query_engine(question)

# Define query engine tools with the filtered query engine
query_engine_tools = [
    QueryEngineTool(
        query_engine=filtered_query_engine,
        metadata=ToolMetadata(
            name="company_docs_filtering",
            description=(
                "Provides information about various companies' financials for year 2021. "
                "Use a detailed plain text question as input to the tool."
            ),
        ),
    ),
]
# Set up the agent with the updated query engine tools
response = llm.predict_and_call(
    query_engine_tools,
    user_msg=question,
    allow_parallel_tool_calls=True,
)

print("Response with metadata filtering:")
print(response)
eval: filters=[MetadataFilter(key='file_name', value='uber_2021.pdf', operator=<FilterOperator.EQ: '=='>)] condition=<FilterCondition.AND: 'and'>
Response with metadata filtering:
Uber's total revenue for the year ended December 31, 2021, is $17.455 billion.

使用 Mistral Large 協調不同的服務

Mistral Large 是 Mistral 的旗艦型號,具有非常好的推理、知識和編碼能力。它是需要大型推理能力或高度專業化的複雜任務的理想選擇。它擁有進階的函式呼叫能力,這正是我們需要來協調不同代理的地方。

為什麼我們需要更聰明的 Model?

下面要回答的問題特別具有挑戰性,因為它需要協調多種服務和代理來提供一致且精確的回應。這涉及到協調各種工具和代理來擷取和處理來自不同來源的資訊,例如來自不同公司的財務資料。

這有什麼難的?

  • 複雜性:這個問題涉及到多個代理和服務,每個代理和服務都有自己的功能和資料來源。協調這些代理,使其無縫合作是一項複雜的任務。
  • 資料整合:這個問題需要整合來自不同來源的資料,由於資料格式、結構和元資料的差異,這可能是一項挑戰。

  • 情境瞭解:問題可能需要理解不同資訊之間的上下文和關係,這是一項對認知要求很高的任務。

為什麼 Mistral Large 在這種情況下會有幫助?

由於 Mistral Large 具備先進的推理和函式呼叫功能,因此非常適合這項任務。以下是它的幫助方式:

  • 進階推理:Mistral Large 可以處理複雜的推理任務,使其成為協調多個代理和服務的理想選擇。它可以理解不同資訊之間的關係,並做出明智的決策。

  • 函式呼叫功能:Mistral Large 具備先進的函式呼叫功能,對於協調不同代理的動作至關重要。這可讓各種服務進行無縫整合與協調。

  • 專業知識:Mistral Large 專為高度專業化的任務所設計,因此非常適合處理需要深厚領域知識的複雜查詢。

基於所有這些原因,我決定在這裡使用 Mistral Large 而非 Mistral Nemo 會比較適合。

from llama_agents import (
    AgentService,
    ToolService,
    LocalLauncher,
    MetaServiceTool,
    ControlPlaneServer,
    SimpleMessageQueue,
    AgentOrchestrator,
)

from llama_index.core.agent import FunctionCallingAgentWorker
from llama_index.llms.mistralai import MistralAI

# create our multi-agent framework components
message_queue = SimpleMessageQueue()
control_plane = ControlPlaneServer(
    message_queue=message_queue,
    orchestrator=AgentOrchestrator(llm=MistralAI("mistral-large-latest")),
)

# define Tool Service
tool_service = ToolService(
    message_queue=message_queue,
    tools=query_engine_tools,
    running=True,
    step_interval=0.5,
)

# define meta-tools here
meta_tools = [
    await MetaServiceTool.from_tool_service(
        t.metadata.name,
        message_queue=message_queue,
        tool_service=tool_service,
    )
    for t in query_engine_tools
]

# define Agent and agent service
worker1 = FunctionCallingAgentWorker.from_tools(
    meta_tools, llm=MistralAI("mistral-large-latest")
)

agent1 = worker1.as_agent()
agent_server_1 = AgentService(
    agent=agent1,
    message_queue=message_queue,
    description="Used to answer questions over differnet companies for their Financial results",
    service_name="Companies_analyst_agent",
)
import logging

# change logging level to enable or disable more verbose logging
logging.getLogger("llama_agents").setLevel(logging.INFO)
## Define Launcher
launcher = LocalLauncher(
    [agent_server_1, tool_service],
    control_plane,
    message_queue,
)
query_str = "What are the risk factors for Uber?"
result = launcher.launch_single(query_str)
INFO:llama_agents.message_queues.simple - Consumer AgentService-27cde4ed-5163-4005-90fc-13c158eda7e3: Companies_analyst_agent has been registered.
INFO:llama_agents.message_queues.simple - Consumer ToolService-b73c500a-5fbe-4f57-95c7-db74e173bd1b: default_tool_service has been registered.
INFO:llama_agents.message_queues.simple - Consumer 62465ab8-32ff-436e-95fa-74e828745150: human has been registered.
INFO:llama_agents.message_queues.simple - Consumer ControlPlaneServer-f4c27d43-5474-43ca-93ca-a9aeed4534d7: control_plane has been registered.
INFO:llama_agents.services.agent - Companies_analyst_agent launch_local
INFO:llama_agents.message_queues.base - Publishing message to 'control_plane' with action 'ActionTypes.NEW_TASK'
INFO:llama_agents.message_queues.simple - Launching message queue locally
INFO:llama_agents.services.agent - Processing initiated.
INFO:llama_agents.services.tool - Processing initiated.
INFO:llama_agents.message_queues.base - Publishing message to 'Companies_analyst_agent' with action 'ActionTypes.NEW_TASK'
INFO:llama_agents.message_queues.simple - Successfully published message 'control_plane' to consumer.
INFO:llama_agents.services.agent - Created new task: 0720da2f-1751-4766-a814-ba720bc8a467
INFO:llama_agents.message_queues.simple - Successfully published message 'Companies_analyst_agent' to consumer.
INFO:llama_agents.message_queues.simple - Consumer MetaServiceTool-5671c175-7b03-4bc8-b60d-bd7101d0fc41: MetaServiceTool-5671c175-7b03-4bc8-b60d-bd7101d0fc41 has been registered.
INFO:llama_agents.message_queues.base - Publishing message to 'default_tool_service' with action 'ActionTypes.NEW_TOOL_CALL'
INFO:llama_agents.message_queues.simple - Successfully published message 'default_tool_service' to consumer.
INFO:llama_agents.services.tool - Processing tool call id f4c270a4-bc47-4bbf-92fe-e2cc80757943 with company_docs
INFO:llama_agents.message_queues.base - Publishing message to 'control_plane' with action 'ActionTypes.COMPLETED_TASK'
INFO:llama_agents.message_queues.base - Publishing message to 'MetaServiceTool-5671c175-7b03-4bc8-b60d-bd7101d0fc41' with action 'ActionTypes.COMPLETED_TOOL_CALL'
INFO:llama_agents.message_queues.base - Publishing message to 'Companies_analyst_agent' with action 'ActionTypes.NEW_TASK'
INFO:llama_agents.message_queues.simple - Successfully published message 'control_plane' to consumer.
INFO:llama_agents.message_queues.simple - Successfully published message 'MetaServiceTool-5671c175-7b03-4bc8-b60d-bd7101d0fc41' to consumer.
INFO:llama_agents.services.agent - Created new task: 0720da2f-1751-4766-a814-ba720bc8a467
INFO:llama_agents.message_queues.simple - Successfully published message 'Companies_analyst_agent' to consumer.
INFO:llama_agents.message_queues.base - Publishing message to 'default_tool_service' with action 'ActionTypes.NEW_TOOL_CALL'
INFO:llama_agents.message_queues.simple - Successfully published message 'default_tool_service' to consumer.
INFO:llama_agents.services.tool - Processing tool call id f888f9a8-e716-4505-bfe2-577452e9b6e6 with company_docs
INFO:llama_agents.message_queues.base - Publishing message to 'MetaServiceTool-5671c175-7b03-4bc8-b60d-bd7101d0fc41' with action 'ActionTypes.COMPLETED_TOOL_CALL'
INFO:llama_agents.message_queues.simple - Successfully published message 'MetaServiceTool-5671c175-7b03-4bc8-b60d-bd7101d0fc41' to consumer.
INFO:llama_agents.message_queues.base - Publishing message to 'control_plane' with action 'ActionTypes.COMPLETED_TASK'
INFO:llama_agents.message_queues.base - Publishing message to 'human' with action 'ActionTypes.COMPLETED_TASK'
INFO:llama_agents.message_queues.simple - Successfully published message 'control_plane' to consumer.
INFO:llama_agents.message_queues.simple - Successfully published message 'human' to consumer.
print(result)
[{"name": "finalize", "arguments": {"input": "Uber faces several risk factors, including general economic impacts such as pandemics or downturns, operational challenges like competition, market growth uncertainty, attracting and retaining drivers and riders, insurance adequacy, autonomous vehicle technology development, maintaining its reputation and brand, and managing growth. Additionally, reliance on third-party providers for various services can introduce further risks to its operations."}}]

總結

在這個筆記本中,你已經看到如何使用 llama-agents 來呼叫適當的工具來執行不同的動作。透過結合使用 Mistral Large 與 Mistral Nemo,我們展示了如何利用不同 LLM 的優勢,有效地協調智慧型、資源效率型系統。我們看到,Agent 可以挑選包含使用者所要求資料的集合。