#
从任意的 LLM 推理提供商中创建一个 Transformers 智能体

作者: Aymeric Roucher

> 本教程建立在智能体知识的基础上：要了解更多关于智能体的信息，你可以从这里介绍开始。

Transformers Agents 是一个用于构建智能体的库，它使用 LLM 在 `llm_engine` 参数中提供动力。这个参数的设计是为了给用户最大的自由度去选择任意 LLM。

让我们看看如何从一些主要提供商的 API 中构建这个 `llm_engine`。

##
HuggingFace 无服务器 API 和专用端点

Transformers Agents 提供了一个内置的 `HfEngine` 类，允许你通过无服务器 API 或你自己的专用端点使用 Hub 上的任何模型。这是使用 HF 智能体的首选方式。

    >>> from transformers.agents import HfEngine, ReactCodeAgent

    >>> repo_id = "meta-llama/Meta-Llama-3-8B-Instruct"
    >>> endpoint_url = "your_endpoint_url"

    >>> llm_engine = HfEngine(model=repo_id)  # you could use model=endpoint_url here

    >>> agent = ReactCodeAgent(tools=[], llm_engine=llm_engine)

    >>> agent.run("What's the 10th Fibonacci number?")

    ['unicodedata', 're', 'math', 'collections', 'queue', 'itertools', 'random', 'time', 'stat', 'statistics']

智能体的 `llm_engine` 初始化参数可以是一个简单的可调用对象，如下所示：

    def llm_engine(messages, stop_sequences=[]) -> str:
        return response(messages)

这个可调用对象是 llm 引擎的核心。它应该满足以下要求：

- 以 聊天模板 格式的消息列表作为输入，并输出一个 `str`。
- 接受一个 `stop_sequences` 参数，智能体系统将传递给它应该停止生成的序列。

让我们更仔细地看看我们使用的 `HfEngine` 的代码：

    from typing import List, Dict
    from transformers.agents.llm_engine import MessageRole, get_clean_message_list
    from huggingface_hub import InferenceClient

    llama_role_conversions = {
        MessageRole.TOOL_RESPONSE: MessageRole.USER,
    }

    class HfEngine:
        def __init__(self, model: str = "meta-llama/Meta-Llama-3-8B-Instruct"):
            self.model = model
            self.client = InferenceClient(model=self.model, timeout=120)

        def __call__(self, messages: List[Dict[str, str]], stop_sequences=[]) -> str:
            # Get clean message list
            messages = get_clean_message_list(messages, role_conversions=llama_role_conversions)

            # Get LLM output
            response = self.client.chat_completion(messages, stop=stop_sequences, max_tokens=1500)
            response = response.choices[0].message.content

            # Remove stop sequences from LLM output
            for stop_seq in stop_sequences:
                if response[-len(stop_seq) :] == stop_seq:
                    response = response[: -len(stop_seq)]
            return response

在这里，引擎不是一个函数，而是一个带有 `__call__` 方法的类，这使得存储诸如客户端之类的属性成为可能。

我们还使用了 `get_clean_message_list()` 实用工具来将连续的消息连接到同一个角色。 这个方法接受一个 `role_conversions` 参数，用于将 Transformers 智能体支持的角色的范围转换为你的 LLM 所接受的那些角色。

这个配方可以适用于任何 LLM！让我们看看其他例子。

##
为任何 LLM 适配配方

使用上述配方，你可以使用任何 LLM 推理源作为你的 `llm_engine`。 只需记住两个主要约束：

- `llm_engine` 是一个可调用对象，它以 聊天模板 格式的消息列表作为输入，并输出一个 `str`。
- 它接受一个 `stop_sequences` 参数。

###
OpenAI

    import os
    from openai import OpenAI

    openai_role_conversions = {
        MessageRole.TOOL_RESPONSE: MessageRole.USER,
    }

    class OpenAIEngine:
        def __init__(self, model_name="gpt-4o"):
            self.model_name = model_name
            self.client = OpenAI(
                api_key=os.getenv("OPENAI_API_KEY"),
            )

        def __call__(self, messages, stop_sequences=[]):
            messages = get_clean_message_list(messages, role_conversions=openai_role_conversions)

            response = self.client.chat.completions.create(
                model=self.model_name,
                messages=messages,
                stop=stop_sequences,
                temperature=0.5,
            )
            return response.choices[0].message.content

###
Anthropic

    from anthropic import Anthropic, AnthropicBedrock

    # Cf this page for using Anthropic from Bedrock: https://docs.anthropic.com/en/api/claude-on-amazon-bedrock
    class AnthropicEngine:
        def __init__(self, model_name="claude-3-5-sonnet-20240620", use_bedrock=False):
            self.model_name = model_name
            if use_bedrock:
                self.model_name = "anthropic.claude-3-5-sonnet-20240620-v1:0"
                self.client = AnthropicBedrock(
                    aws_access_key=os.getenv("AWS_BEDROCK_ID"),
                    aws_secret_key=os.getenv("AWS_BEDROCK_KEY"),
                    aws_region="us-east-1",
                )
            else:
                self.client = Anthropic(
                    api_key=os.getenv("ANTHROPIC_API_KEY"),
                )

        def __call__(self, messages, stop_sequences=[]):
            messages = get_clean_message_list(messages, role_conversions=openai_role_conversions)
            index_system_message, system_prompt = None, None
            for index, message in enumerate(messages):
                if message["role"] == MessageRole.SYSTEM:
                    index_system_message = index
                    system_prompt = message["content"]
            if system_prompt is None:
                raise Exception("No system prompt found!")

            filtered_messages = [message for i, message in enumerate(messages) if i != index_system_message]
            if len(filtered_messages) == 0:
                print("Error, no user message:", messages)
                assert False

            response = self.client.messages.create(
                model=self.model_name,
                system=system_prompt,
                messages=filtered_messages,
                stop_sequences=stop_sequences,
                temperature=0.5,
                max_tokens=2000,
            )
            full_response_text = ""
            for content_block in response.content:
                if content_block.type == "text":
                    full_response_text += content_block.text
            return full_response_text

###
下一步

现在去为你自己选择的那个语言模型推理服务，用 `transformers.agents` 做一个 `llm_engine` 吧！

做好之后，你可以用这个新的 `llm_engine` 来玩玩这些应用场景：

- 智能体 RAG：通过查询重构和自查询来增强你的 RAG
- 用于文本到 SQL 的智能体，带自动错误校正

< > Update on GitHub