跳到内容

步骤 08 - 代理 AI - 模型上下文协议

在上一节函数调用概念的基础上,让我们来探索如何通过 模型上下文协议 模式来使这个想法更加分布式。

基本上,我们将允许 LLM 充当一个真正的代理,使用模型上下文协议调用一组预定义的工具,以进一步增强其知识和/或功能。

模型上下文协议

《模型上下文协议》是一项开放标准,它促进了数据存储库和 AI 驱动工具之间安全、双向链接的创建。其设计很简单;开发人员可以通过 MCP 服务器公开他们的数据,或者构建与这些服务器接口的 AI 应用程序(MCP 客户端)。

MCP

在本步骤中,我们将了解如何在我们的应用程序中实现 MCP 服务器和客户端。MCP 客户端将集成到我们现有的代码中,而 MCP 服务器将是一个独立的应用程序,MCP 客户端的代理将调用它来检索额外的上下文。

最终代码可在 step-08 文件夹中找到,用于客户端应用程序(我们一直在处理的那个)。MCP 服务器应用程序可以在 step-08-mcp-server 文件夹中找到。与之前一样,我们建议您按照分步指南进行操作,以了解其工作原理以及实现此模式的不同步骤。

创建一个新的 MCP 天气服务器项目

让我们从头开始创建一个 Quarkus MCP 服务器(或者,您也可以直接使用 step-08-mcp-server 项目)。我们将添加 Quarkus MCP 服务器依赖项,以及 REST Client 依赖项,以便我们可以调用远程天气服务来检索给定位置的当前天气状况。

在您的终端中,确保您位于工作坊的主目录中,然后执行以下命令

 quarkus create app dev.langchain4j.quarkus.workshop:quarkus-langchain4j-workshop-08-mcp-server:1.0-SNAPSHOT -x quarkus-mcp-server-sse -x quarkus-rest-client-jackson

现在您应该会看到一个新的 quarkus-langchain4j-workshop-08-mcp-server 文件夹。在其中,创建一个新的 src/main/java/dev/langchain4j/quarkus/workshop/WeatherClient.java 文件。这将是我们将用于调用远程天气 API 的 REST 客户端。 用以下代码填充它:

WeatherClient.java
package dev.langchain4j.quarkus.workshop;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@Path("/v1/forecast")
@RegisterRestClient(configKey="weatherclient")
public interface WeatherClient {

    @GET
    public String getForecast(
            @QueryParam("latitude") double latitude,
            @QueryParam("longitude") double longitude,
            @QueryParam("forecastdays") int forecastDays,
            @QueryParam("hourly") String hourly
    );
}
现在创建一个 MCP 服务器类,其中包含带 @Tool 注释的方法,就像我们在上一步为本地函数调用所做的那样。唯一的区别是,在这种情况下,我们定义的 MCP 工具将通过 MCP 协议和给定的传输类型在网络上可用。
Weather.java
package dev.langchain4j.quarkus.workshop;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import io.quarkiverse.mcp.server.Tool;
import io.quarkiverse.mcp.server.ToolArg;

public class Weather {

    @RestClient
    WeatherClient weatherClient;

    @Tool(description = "Get weather forecast for a location.")
    String getForecast(@ToolArg(description = "Latitude of the location") double latitude,
                       @ToolArg(description = "Longitude of the location") double longitude) {
        return weatherClient.getForecast(
                latitude,
                longitude,
                16,
                "temperature_2m,snowfall,rain,precipitation,precipitation_probability");
    }
}

太棒了!剩下的就是为我们的项目添加一些配置。 向 application.properties 添加以下内容:

application.properties
# run the MCP server on a different port than the client
quarkus.http.port=8081

# Configure MCP server
quarkus.mcp.server.server-info.name=Weather Service
quarkus.mcp.server.traffic-logging.enabled=true
quarkus.mcp.server.traffic-logging.text-limit=100

# Configure the Rest Client
quarkus.rest-client.logging.scope=request-response
quarkus.rest-client.follow-redirects=true
quarkus.rest-client.logging.body-limit=50
quarkus.rest-client."weatherclient".uri=https://api.open-meteo.com/

很简单,对吧?只需几行代码,我们就能够构建一个功能齐全的 MCP 服务器,而使用其他任何堆栈或语言都需要大量的工作!Quarkus 万岁!

在另一个终端窗口/标签页中,从 quarkus-langchain4j-workshop-08-mcp-server 文件夹启动服务器

 ./mvnw quarkus:dev"

现在,让我们配置我们的客户端应用程序以使用新构建的 MCP 服务器。

一个新的 MCP 客户端依赖

Quarkus LangChain4j 也以最少的工作量支持 MCP。要使用它,我们需要安装一个新的 MCP 客户端依赖项。 打开您主项目(即不包含 MCP 服务器的项目)中的 pom.xml 文件,并添加以下依赖项:

pom.xml
<dependency>
    <groupId>io.quarkiverse.langchain4j</groupId>
    <artifactId>quarkus-langchain4j-mcp</artifactId>
</dependency>

提示

您也可以打开另一个终端并运行

./mvnw quarkus:add-extension -Dextensions="quarkus-langchain4j-mcp"

LangChain4j MCP 依赖项将允许我们调用远程 MCP 服务器。请记住,MCP 服务器可以用 Java 编写,就像我们上面创建的那个一样,但实际上它们可以是任何公开 MCP 协议的技术。

配置 MCP 客户端

既然我们有了依赖项,我们就只需要配置它,使其使用 http 传输类型来调用我们的 MCP 服务器。您可以在 application.properties 文件中执行此操作

application.properties
quarkus.langchain4j.mcp.weather.transport-type=http
quarkus.langchain4j.mcp.weather.url=https://:8081/mcp/sse/

请注意,我们使用了“weather”这个名称。我们将在 AI 服务中引用它,以使用这个特定的 MCP 服务器。

我们将向我们的 AI 服务添加一个 @McpToolBox(“weather”) 注释来引用可用的 MCP 服务器。我们将在提示中添加一些说明,以便模型调用检索汽车租赁地点的当前天气,并为驾驶员可能需要的特殊装备提供建议。

在 CustomerSupportAgent.java 文件中,使用以下内容更新 SystemMessage

CustomerSupportAgent.java
package dev.langchain4j.quarkus.workshop;

import io.quarkiverse.langchain4j.mcp.runtime.McpToolBox;
import jakarta.enterprise.context.SessionScoped;

import dev.langchain4j.service.SystemMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.ToolBox;

@SessionScoped
@RegisterAiService
public interface CustomerSupportAgent {

    @SystemMessage("""
            You are a customer support agent of a car rental company 'Miles of Smiles'.
            You are friendly, polite and concise.
            If the question is unrelated to car rental, you should politely redirect the customer to the right department.

            You will get the location and start dates for a booking from the booking table in the database.
            Figure out the coordinates for that location,
            and based on the coordinates and the date, call a tool to get the weather for that specific location on the give date.
            You should provide information about specific equipment the car rental booking might need based on the weather, 
            such as snow chains or air conditioning.

            Today is {current_date}.
            """)
    @ToolBox(BookingRepository.class)
    @McpToolBox("weather")
    String chat(String userMessage);
}

测试函数调用

让我们测试函数调用。 确保应用程序正在运行(./mvnw quarkus:dev)。

打开您的浏览器并访问 https://:8080。在聊天机器人中,让我们尝试以下交互

Hello, I would like to retrieve information about my booking.

AI:当然,我可以帮忙。我需要更多信息才能继续:1. 您的名字和姓氏,以便查找预订。2. 与您的预订相关的预订 ID。请提供这些详细信息,以便我能进一步帮助您。

My name is Speedy McWheels and my booking id is 2.

AI:Speedy McWheels 的预订详情如下:- 预订 ID: 2 - 客户姓名: Speedy McWheels - 租赁日期: 2024 年 8 月 5 日至 2024 年 8 月 12 日 - 地点: 加利福尼亚州洛杉矶。您是否需要有关该地点天气的查询,或者关于此预订还有什么我可以帮助您的吗?

yes I would like to know if I need any additional equipment

AI:您租赁期间加利福尼亚州洛杉矶的天气预计会比较温暖,没有降雪或显着降水。气温可能在 14°C 至 30°C 之间波动。鉴于此预测,以下是您可能需要的东西:- 空调:由于气温可能高达 30°C,租车时配备空调将确保驾驶舒适。- 无需雪链:由于没有降雪预报,因此不需要雪链。如果您还需要其他帮助,请随时提问!

结论

在本步骤中,我们探讨了如何在应用程序中与 MCP 服务器和客户端协同工作,使我们能够创建多功能的代理,这些代理不仅可以进行推理,还可以与能够为我们的应用程序提供额外功能和数据的远程系统进行动态交互。

在此上下文中,MCP 服务器与我们之前探索的本地函数调用概念非常相似,只是它运行在远程应用程序中。这使我们能够与可重用的组件进行接口(并构建它们)。

正如您所看到的,MCP 服务器的实际实现也是完全可定制的。

但是,引入工具和函数调用也带来了新的风险,例如 LLM 行为不当(例如,过度调用函数或使用错误的参数)或易受提示注入攻击。在下一步中,我们将探讨一种使用防护栏来缓解提示注入的简单方法,以确保更安全、更可靠的交互。