授權呼叫: 介紹 Transformers 智慧體 2.0

HuggingFace發表於2024-05-27

簡要概述

我們推出了 Transformers 智慧體 2.0!

⇒ 🎁 在現有智慧體型別的基礎上,我們新增了兩種能夠 根據歷史觀察解決複雜任務的智慧體

⇒ 💡 我們致力於讓程式碼 清晰、模組化,並確保最終提示和工具等通用屬性透明化

⇒ 🤝 我們加入了 分享功能,以促進社群智慧體的發展。

⇒ 💪 全新的智慧體框架效能卓越,使得 Llama-3-70B-Instruct 智慧體在 GAIA 排行榜上超越了基於 GPT-4 的智慧體!

🚀 快去體驗,力爭在 GAIA 排行榜上名列前茅!

什麼是智慧體?

大型語言模型 (LLMs) 能夠處理廣泛的任務,但它們通常在邏輯、計算和搜尋等特定任務上表現不佳。當在這些它們表現不好的領域被提示時,它們經常無法生成正確的答案。

克服這種弱點的一種方法就是建立一個 智慧體,它只是一個由 LLM 驅動的程式。智慧體透過 工具 獲得能力,幫助它執行動作。當智慧體需要特定技能來解決特定問題時,它會依賴於工具箱中的適當工具。

因此,在解決問題時,如果智慧體需要特定技能,它可以直接依賴於工具箱中的適當工具。

實驗上,智慧體框架通常表現非常好,在多個基準測試上達到了 SOTA。例如,看看 HumanEval 的最上面的提交: 它們就是智慧體系統。

Transformers 智慧體方法

構建智慧體的過程很複雜,需要高水平的清晰度和模組化設計。一年前,我們釋出了 Transformers 智慧體,現在我們正加倍努力實現我們的核心設計目標。

我們的框架力求實現:

  • 簡化以提升清晰度: 我們將抽象減少到最低限度。簡單的錯誤日誌和可訪問的屬性讓你輕鬆檢查系統發生的情況,從而獲得更多的清晰度。
  • 模組化設計: 我們更願意提供構建模組,而不是一個完整、複雜的特性集。這樣你可以自由選擇最適合你的專案的構建模組。
    • 例如,由於任何智慧體系統只是由 LLM 引擎驅動的載體,我們決定在概念上分離這兩者,使你可以用任何底層 LLM 建立任何型別的智慧體。

此外,我們還提供 分享功能,讓你能在前人的基礎上繼續構建!

主要元素

  • Tool (工具): 這是一個類,允許你使用工具或實現一個新的工具。它主要由一個可呼叫的前向method 組成,執行工具動作,以及一些必要的屬性: name (名稱) 、descriptions (描述) 、inputs (輸入) 和output_type (輸出型別)。這些屬性用於動態生成工具的使用手冊,並將其插入到 LLM 的提示中。
  • Toolbox (工具箱): 這是一組工具,作為資源提供給智慧體,用於解決特定任務。出於效能考慮,工具箱中的工具已經例項化並準備好使用。這是因為某些工具需要時間來初始化,所以通常更好的是重用現有的工具箱,只更換一個工具,而不是在每次智慧體初始化時從頭開始構建一組工具。
  • CodeAgent (程式碼智慧體): 一個非常簡單的智慧體,其動作作為單個 Python 程式碼塊生成。它將無法對先前的觀察進行迭代。
  • ReactAgent (反應智慧體): ReAct 智慧體遵循一個迴圈: 思考 ⇒ 行動 ⇒ 觀察直到解決任務。我們提出了兩種 ReActAgent 類:
    • ReactCodeAgent (反應程式碼智慧體) 將其動作作為 Python 程式碼塊生成。
    • ReactJsonAgent (反應 JSON 智慧體) 將其動作作為 JSON 程式碼塊生成。

檢視 文件 瞭解如何使用每個元件!

智慧體在底層是如何工作的?

本質上,智慧體的作用是“允許 LLM 使用工具”。智慧體有一個關鍵的 agent.run() 方法,該方法:

  • 在一個 特定提示 中向你的 LLM 提供關於工具使用的資訊。這樣,LLM 可以選擇執行工具來解決任務。
  • 解析 來自 LLM 輸出的工具呼叫 (可以透過程式碼、JSON 格式或任何其他格式)。
  • 執行 呼叫。
  • 如果智慧體被設計為對先前的輸出進行迭代,它會 保留 先前的工具呼叫和觀察的記憶。這個記憶可以根據你希望它持續的時間長短而變得更加或更少細緻。

graph of agent workflows

關於智慧體的更多一般背景資訊,你可以閱讀 Lilian Weng 的 這篇優秀部落格,或者閱讀我們之前的部落格,關於使用 LangChain 構建 智慧體

要深入瞭解我們的包,請檢視 智慧體文件

示例用例

為了獲得此功能的早期訪問許可權,請首先從其 main 分支安裝 transformers :

pip install "git+https://github.com/huggingface/transformers.git#egg=transformers[agents]"

智慧體 2.0 將在 v4.41.0 版本中釋出,預計將於五月中旬上線。

自我修正的檢索增強生成

快速定義: 檢索增強生成 (RAG) 是“使用 LLM 回答使用者查詢,但基於從知識庫檢索到的資訊來回答”。與使用普通或微調的 LLM 相比,它有許多優點: 舉幾個例子,它允許基於真實事實來回答問題,減少虛構,它允許向 LLM 提供特定領域的知識,並且可以細粒度地控制對知識庫資訊的訪問。

假設我們想要執行 RAG,並且某些引數必須動態生成。例如,根據使用者查詢,我們可能想要將搜尋限制在知識庫的特定子集,或者我們可能想要調整檢索到的文件數量。難題是: 如何根據使用者查詢動態調整這些引數?
嗯,我們可以透過讓我們的智慧體訪問這些引數來實現!

讓我們設定這個系統。

安裝以下依賴項:

pip install langchain sentence-transformers faiss-cpu

我們首先載入一個想要在其上執行 RAG 的知識庫: 這個資料集是許多 huggingface 包的文件頁面彙編,以 markdown 格式儲存。

import datasets
knowledge_base = datasets.load_dataset("m-ric/huggingface_doc", split="train")

現在我們透過處理資料集並將其儲存到向量資料庫中來準備知識庫,以便檢索器使用。我們將使用 LangChain,因為它具有用於向量資料庫的優秀工具:

from langchain.docstore.document import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings

source_docs = [
    Document(
        page_content=doc["text"], metadata={"source": doc["source"].split("/")[1]}
    ) for doc in knowledge_base
]

docs_processed = RecursiveCharacterTextSplitter(chunk_size=500).split_documents(source_docs)[:1000]

embedding_model = HuggingFaceEmbeddings("thenlper/gte-small")
vectordb = FAISS.from_documents(
    documents=docs_processed,
    embedding=embedding_model
)

現在我們已經準備好了資料庫,讓我們構建一個基於它回答使用者查詢的 RAG 系統!

我們希望我們的系統根據查詢僅從最相關的資訊來源中選擇。

我們的文件頁面來自以下來源:

>>> all_sources = list(set([doc.metadata["source"] for doc in docs_processed]))
>>> print(all_sources)

['blog', 'optimum', 'datasets-server', 'datasets', 'transformers', 'course',
'gradio', 'diffusers', 'evaluate', 'deep-rl-class', 'peft',
'hf-endpoints-documentation', 'pytorch-image-models', 'hub-docs']

我們如何根據使用者查詢選擇相關的來源?

👉 讓我們構建一個 RAG 系統作為智慧體,它將自由選擇其資訊來源!

我們建立一個檢索工具,智慧體可以使用它選擇的引數來呼叫:

import json
from transformers.agents import Tool
from langchain_core.vectorstores import VectorStore

class RetrieverTool(Tool):
    name = "retriever"
    description = "Retrieves some documents from the knowledge base that have the closest embeddings to the input query."
    inputs = {
        "query": {
            "type": "text",
            "description": "The query to perform. This should be semantically close to your target documents. Use the affirmative form rather than a question.",
        },
        "source": {
            "type": "text",
            "description": ""
        },
    }
    output_type = "text"
    
    def __init__(self, vectordb: VectorStore, all_sources: str, **kwargs):
        super().__init__(**kwargs)
        self.vectordb = vectordb
        self.inputs["source"]["description"] = (
	        f"The source of the documents to search, as a str representation of a list. Possible values in the list are: {all_sources}. If this argument is not provided, all sources will be searched."
	      )

    def forward(self, query: str, source: str = None) -> str:
        assert isinstance(query, str), "Your search query must be a string"

        if source:
            if isinstance(source, str) and "[" not in str(source): # if the source is not representing a list
                source = [source]
            source = json.loads(str(source).replace("'", '"'))

        docs = self.vectordb.similarity_search(query, filter=({"source": source} if source else None), k=3)

        if len(docs) == 0:
            return "No documents found with this filtering. Try removing the source filter."
        return "Retrieved documents:\n\n" + "\n===Document===\n".join(
            [doc.page_content for doc in docs]
        )

現在建立一個利用這個工具的智慧體就很簡單了!

智慧體在初始化時需要以下引數:

  • tools : 智慧體將能夠呼叫的工具列表。
  • llm_engine : 驅動智慧體的 LLM。

我們的 llm_engine 必須是一個可呼叫的物件,它接受一個 messages 列表作為輸入並返回文字。它還需要接受一個 stop_sequences 引數,指示何時停止生成。為了方便起見,我們直接使用包中提供的 HfEngine 類來獲取一個呼叫我們的 Inference API 的 LLM 引擎。

from transformers.agents import HfEngine, ReactJsonAgent

llm_engine = HfEngine("meta-llama/Meta-Llama-3-70B-Instruct")

agent = ReactJsonAgent(
    tools=[RetrieverTool(vectordb, all_sources)],
    llm_engine=llm_engine
)

agent_output = agent.run("Please show me a LORA finetuning script")

print("Final output:")
print(agent_output)

由於我們將智慧體初始化為 ReactJsonAgent ,它已經自動獲得了一個預設的系統提示,告訴 LLM 引擎逐步處理並生成工具呼叫作為 JSON 程式碼塊 (根據需要,你可以替換此提示模板)。

然後,當它的 .run() 方法被啟動時,智慧體會負責呼叫 LLM 引擎,解析工具呼叫的 JSON 程式碼塊並執行這些工具呼叫,所有這些都在一個迴圈中進行,只有在提供最終答案時才會結束。

我們得到以下輸出:

Calling tool: retriever with arguments: {'query': 'LORA finetuning script', 'source': "['transformers', 'datasets-server', 'datasets']"}
Calling tool: retriever with arguments: {'query': 'LORA finetuning script'}
Calling tool: retriever with arguments: {'query': 'LORA finetuning script example', 'source': "['transformers', 'datasets-server', 'datasets']"}
Calling tool: retriever with arguments: {'query': 'LORA finetuning script example'}
Calling tool: final_answer with arguments: {'answer': 'Here is an example of a LORA finetuning script: https://github.com/huggingface/diffusers/blob/dd9a5caf61f04d11c0fa9f3947b69ab0010c9a0f/examples/text_to_image/train_text_to_image_lora.py#L371'}

Final output:
Here is an example of a LORA finetuning script: https://github.com/huggingface/diffusers/blob/dd9a5caf61f04d11c0fa9f3947b69ab0010c9a0f/examples/text_to_image/train_text_to_image_lora.py#L371

我們可以看到自我修正的實際效果: 智慧體最初嘗試限制來源,但由於缺乏相應的文件,它最終沒有限制任何來源。

我們可以透過檢查第二步的日誌中的 llm 輸出來驗證這一點: print(agent.logs[2]['llm_output'])

Thought: I'll try to retrieve some documents related to LORA finetuning scripts from the entire knowledge base, without any source filtering.

Action:
{
  "action": "retriever",
  "action_input": {"query": "LORA finetuning script"}
}

使用簡單的多智慧體設定 🤝 進行高效的網頁瀏覽

在這個例子中,我們想要構建一個智慧體並在 GAIA 基準測試上對其進行測試 (Mialon et al. 2023)。GAIA 是一個非常困難的基準測試,大多數問題需要使用不同的工具進行多個步驟的推理。一個特別困難的要求是擁有一個強大的網路瀏覽器,能夠導航到具有特定約束條件的頁面: 使用網站的內部導航發現頁面,按時間選擇特定的文章 …

網頁瀏覽需要深入到子頁面並滾動大量不必要的文字標記,這對於解決更高階別的任務是不必要的。我們將網頁瀏覽的子任務分配給一個專業的網頁瀏覽智慧體。我們為其提供了一些瀏覽網頁的工具和一個特定的提示 (檢視倉庫以找到特定的實現)。

定義這些工具超出了本文的範圍: 但是你可以在 倉庫 中找到特定的實現。

from transformers.agents import ReactJsonAgent, HfEngine

WEB_TOOLS = [
    SearchInformationTool(),
    NavigationalSearchTool(),
    VisitTool(),
    DownloadTool(),
    PageUpTool(),
    PageDownTool(),
    FinderTool(),
    FindNextTool(),
]

websurfer_llm_engine = HfEngine(
    model="CohereForAI/c4ai-command-r-plus"
) # 我們選擇 Command-R+ 因為它具有很高的上下文長度

websurfer_agent = ReactJsonAgent(
    tools=WEB_TOOLS,
    llm_engine=websurfer_llm_engine,
)

為了允許更高層次的任務解決智慧體呼叫這個智慧體,我們可以簡單地將其封裝在另一個工具中:

class SearchTool(Tool):
    name = "ask_search_agent"
    description = "A search agent that will browse the internet to answer a question. Use it to gather informations, not for problem-solving."

    inputs = {
        "question": {
            "description": "Your question, as a natural language sentence. You are talking to an agent, so provide them with as much context as possible.",
            "type": "text",
        }
    }
    output_type = "text"

    def forward(self, question: str) -> str:
        return websurfer_agent.run(question)

然後我們使用這個搜尋工具初始化任務解決智慧體:

from transformers.agents import ReactCodeAgent

llm_engine = HfEngine(model="meta-llama/Meta-Llama-3-70B-Instruct")
react_agent_hf = ReactCodeAgent(
    tools=[SearchTool()],
    llm_engine=llm_engine,
)

讓我們做這個任務:

使用 Marisa Alviar-Agnew 和 Henry Agnew 根據 CK-12 許可在 LibreText 的《初級化學》材料中提供的密度資料,編譯日期為 2023 年 8 月 21 日。
我有一加侖的蜂蜜和一加侖的蛋黃醬,溫度為 25 攝氏度。我每次從一加侖蜂蜜中取出一杯蜂蜜。我要取出多少次一杯蜂蜜,才能使蜂蜜的重量低於蛋黃醬?假設容器本身的重量相同。

Thought: I will use the 'ask_search_agent' tool to find the density of honey and mayonnaise at 25C.
==== Agent is executing the code below:
density_honey = ask_search_agent(question="What is the density of honey at 25C?")
print("Density of honey:", density_honey)
density_mayo = ask_search_agent(question="What is the density of mayonnaise at 25C?")
print("Density of mayo:", density_mayo)
===
Observation:
Density of honey: The density of honey is around 1.38-1.45kg/L at 20C. Although I couldn't find information specific to 25C, minor temperature differences are unlikely to affect the density that much, so it's likely to remain within this range.
Density of mayo: The density of mayonnaise at 25°C is 0.910 g/cm³.

===== New step =====
Thought: I will convert the density of mayonnaise from g/cm³ to kg/L and then calculate the initial weights of the honey and mayonnaise in a gallon. After that, I will calculate the weight of honey after removing one cup at a time until it weighs less than the mayonnaise.
==== Agent is executing the code below:
density_honey = 1.42 # taking the average of the range
density_mayo = 0.910 # converting g/cm³ to kg/L
density_mayo = density_mayo * 1000 / 1000 # conversion

gallon_to_liters = 3.785 # conversion factor
initial_honey_weight = density_honey * gallon_to_liters
initial_mayo_weight = density_mayo * gallon_to_liters

cup_to_liters = 0.236 # conversion factor
removed_honey_weight = cup_to_liters * density_honey
===
Observation:

===== New step =====
Thought: Now that I have the initial weights of honey and mayonnaise, I'll try to calculate the number of cups to remove from the honey to make it weigh less than the mayonnaise using a simple arithmetic operation.
==== Agent is executing the code below:
cups_removed = int((initial_honey_weight - initial_mayo_weight) / removed_honey_weight) + 1
print("Cups removed:", cups_removed)
final_answer(cups_removed)
===
>>> Final answer: 6

✅ 答案是 正確的

測試我們的智慧體

讓我們使用智慧體框架進行一些基準測試,看看不同模型的表現!

以下實驗的所有程式碼都可以在 這裡 找到。

基準測試大型語言模型引擎

agents_reasoning_benchmark 是一個小型但強大的推理測試,用於評估智慧體效能。這個基準測試已經在 我們之前的部落格 中使用並進行了更詳細的解釋。

這個想法是,你為智慧體使用的工具選擇可以極大地改變某些任務的效能。因此,這個基準測試限制了使用的工具集為一個計算器和一個非常基礎的搜尋工具。我們從幾個資料集中挑選了問題,這些問題只能使用這兩個工具來解決:

  • 來自 HotpotQA 的 30 個問題 (Yang et al., 2018),用於測試搜尋工具的使用。
  • 來自 GSM8K 的 40 個問題 (Cobbe et al., 2021),用於測試計算器工具的使用。
  • 來自 GAIA 的 20 個問題 (Mialon et al., 2023),用於測試使用這兩個工具解決困難問題的能力。

在這裡,我們嘗試了三種不同的引擎: Mixtral-8x7BLlama-3-70B-Instruct,和 GPT-4 Turbo

benchmark of agent performances

結果在上方顯示 - 為了提高精度,我們顯示了兩輪完整執行的平均值。我們還測試了 Command-R+Mixtral-8x22B,但由於清晰度原因,這裡沒有顯示。

Llama-3-70B-Instruct 在開源模型中領先: 它與 GPT-4 相當,尤其在與 ReactCodeAgent 的結合中表現出色,得益於 Llama 3 強大的編碼效能!

💡 比較基於 JSON 和基於程式碼的 React 智慧體很有趣: 對於像 Mixtral-8x7B 這樣較弱的 LLM 引擎,基於程式碼的智慧體不如基於 JSON 的智慧體表現好,因為 LLM 引擎經常無法生成好的程式碼。但隨著更強大的模型作為引擎,基於程式碼的版本表現尤為出色: 在我們的經驗中,基於程式碼的版本甚至在使用 Llama-3-70B-Instruct 時超越了基於 JSON 的版本。因此,我們使用基於程式碼的版本進行下一個挑戰: 在完整的 GAIA 基準測試上進行測試。

使用多模態智慧體刷 GAIA 排行榜

GAIA (Mialon et al., 2023) 是一個非常困難的基準測試: 在上面的 agent_reasoning_benchmark 中可以看到,即使我們挑選了可以使用兩種基本工具解決的任務,模型也幾乎沒有達到 50% 的表現。

現在我們想要在完整的測試集上獲得分數,不再挑選問題。因此,我們需要覆蓋所有模態,這導致我們使用這些特定的工具:

  • SearchTool : 如上所述的網頁瀏覽器。
  • TextInspectorTool : 將文件作為文字檔案開啟並返回其內容。
  • SpeechToTextTool : 將音訊檔案轉錄為文字。我們使用基於 distil-whisper 的預設工具。
  • VisualQATool : 分析影像的視覺內容。對於這些,我們使用全新的 Idefics2-8b-chatty

我們首先初始化這些工具 (更多細節,請檢查 倉庫 中的程式碼)。

然後我們初始化我們的智慧體:

from transformers.agents import ReactCodeAgent, HfEngine

TASK_SOLVING_TOOLBOX = [
    SearchTool(),
    VisualQATool(),
    SpeechToTextTool(),
    TextInspectorTool(),
]

react_agent_hf = ReactCodeAgent(
    tools=TASK_SOLVING_TOOLBOX,
    llm_engine=HfEngine(model="meta-llama/Meta-Llama-3-70B-Instruct"),
    memory_verbose=True,
)

在完成 165 個問題所需的一段時間後,我們提交了我們的結果到 GAIA 排行榜,然後…… 🥁🥁🥁

GAIA leaderboard

⇒ 我們的智慧體排名第四: 它超過了許多基於 GPT-4 的智慧體,現在已成為開源類別中的領先競爭者!

總結

在接下來的幾個月裡,我們將繼續改進這個包。我們已經在我們開發路線圖中確定了幾個令人興奮的路徑:

  • 更多的智慧體共享選項: 目前你可以從 Hub 推送或載入工具,我們將實現推送/載入智慧體。
  • 更好的工具,特別是用於影像處理。
  • 長期記憶管理。
  • 多智慧體協作。

👉 去嘗試一下 Transformers 智慧體! 我們期待著收到你的反饋和你的想法。

讓我們一起用更多的開源模型刷排行榜登頂! 🚀


英文原文: https://hf.co/blog/agents

原文作者: Aymeric Roucher, Lysandre, Pedro Cuenca

譯者: innovation64

相關文章