AI 服务
到目前为止,我们已经讨论了像 ChatModel、ChatMessage 和 ChatMemory 等底层组件。
在这个层面工作非常灵活,赋予你完全的自由,但也意味着需要编写大量的样板代码。
由于基于 LLM 的应用程序通常需要多个组件协同工作 (例如,提示模板、聊天记忆、LLM、输出解析器、RAG 组件:嵌入模型和存储),并且通常涉及多轮交互,因此对它们的编排变得愈加繁琐。
我们希望你能专注于业务逻辑,而不是底层实现的细节。
为此,LangChain4j 目前提供了两个高级概念来帮助你:AI 服务 和 链(Chains)。
链 (Chains) (传统)
链的概念源于 Python 的 LangChain(在 LCEL 引入之前)。
其核心思想是为每个常见用例,如聊天机器人或 RAG,提供一个链。
链将多个底层组件结合起来,并协调它们之间的交互。
它们的主要问题是,如果你需要自定义某些东西,它们会显得过于死板。
目前,LangChain4j 仅实现了两种链:ConversationalChain
和 ConversationalRetrievalChain
,并且我们暂时没有计划增加更多。
AI 服务
我们提出了另一种专为 Java 设计的解决方案,称为 AI 服务。
其思想是将与 LLM 和其他组件交互的复杂性隐藏在一个简单的 API 后面。
这种方法与 Spring Data JPA 或 Retrofit 非常相似:你声明性地定义一个具有所需 API 的接口,然后 LangChain4j 提供一个实现此接口的对象(代理)。
你可以将 AI 服务看作应用程序中服务层的一个组件。
它提供 AI 服务。这就是它的名字。
AI 服务处理最常见的操作:
- 为 LLM 格式化输入
- 解析 LLM 的输出
它们还支持更高级的功能:
- 聊天记忆
- 工具
- RAG
AI 服务可用于构建有状态的聊天机器人,以促进来回交互,也可用于自动化流程,其中每次对 LLM 的调用都是独立的。
让我们看看最简单的 AI 服务。之后,我们将探索更复杂的例子。
最简单的 AI 服务
首先,我们定义一个只有一个方法 chat
的接口,它接受一个 String
作为输入并返回一个 String
。
interface Assistant {
String chat(String userMessage);
}
然后,我们创建低级组件。这些组件将在我们的 AI 服务底层使用。
在本例中,我们只需要 ChatModel
:
ChatModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName(GPT_4_O_MINI)
.build();
最后,我们可以使用 AiServices
类来创建 AI 服务的一个实例:
Assistant assistant = AiServices.create(Assistant.class, model);
注
在 Quarkus 和 Spring Boot 应用程序中,自动配置会处理创建 Assistant
bean。
这意味着你不需要调用 AiServices.create(...)
,只需在需要的地方注入/自动装配 Assistant
即可。
现在我们可以使用 Assistant
了:
String answer = assistant.chat("Hello");
System.out.println(answer); // Hello, how can I help you?
它是如何工作的?
你将接口的 Class
以及低级组件提供给 AiServices
,然后 AiServices
会创建一个实现此接口的代理对象。
目前,它使用反射,但我们也在考虑其他替代方案。
这个代理对象处理所有输入和输出的转换。
在本例中,输入是一个 String
,但我们使用的是 ChatModel
,它接受 ChatMessage
作为输入。
因此,AiService
会自动将其转换为 UserMessage
并调用 ChatModel
。
由于 chat
方法的输出类型是 String
,在 ChatModel
返回 AiMessage
后,它将在从 chat
方法返回之前被转换成 String
。
Quarkus 应用程序中的 AI 服务
LangChain4j Quarkus 扩展
极大地简化了在 Quarkus 应用程序中使用 AI 服务。
更多信息请访问 这里。
Spring Boot 应用程序中的 AI 服务
LangChain4j Spring Boot starter
极大地简化了在 Spring Boot 应用程序中使用 AI 服务。
@SystemMessage
现在,让我们看一个更复杂的例子。
我们将强制 LLM 使用俚语回答 😉
这通常通过在 SystemMessage
中提供指令来实现。
interface Friend {
@SystemMessage("You are a good friend of mine. Answer using slang.")
String chat(String userMessage);
}
Friend friend = AiServices.create(Friend.class, model);
String answer = friend.chat("Hello"); // Hey! What's up?
在这个例子中,我们添加了 @SystemMessage
注解,其中包含我们想要使用的系统提示模板。
这将在底层被转换为一个 SystemMessage
,并与 UserMessage
一起发送给 LLM。
@SystemMessage
还可以从资源中加载提示模板:@SystemMessage(fromResource = "my-prompt-template.txt")
系统消息提供者
系统消息也可以使用系统消息提供者动态定义:
Friend friend = AiServices.builder(Friend.class)
.chatModel(model)
.systemMessageProvider(chatMemoryId -> "You are a good friend of mine. Answer using slang.")
.build();
正如你所看到的,你可以根据聊天记忆 ID(用户或对话)提供不同的系统消息。
@UserMessage
现在,假设我们使用的模型不支持系统消息,或者我们只是想为此目的使用 UserMessage
。
interface Friend {
@UserMessage("You are a good friend of mine. Answer using slang. {{it}}")
String chat(String userMessage);
}
Friend friend = AiServices.create(Friend.class, model);
String answer = friend.chat("Hello"); // Hey! What's shakin'?
我们用 @UserMessage
注解替换了 @SystemMessage
,并指定了一个包含变量 it
的提示模板,it
指代唯一的 方法参数。
还可以使用 @V
注解 String userMessage
并为提示模板变量分配一个自定义名称:
interface Friend {
@UserMessage("You are a good friend of mine. Answer using slang. {{message}}")
String chat(@V("message") String userMessage);
}
注
请注意,当与 Quarkus 或 Spring Boot 一起使用 LangChain4j 时,使用 @V
并不是必需的。
只有在 Java 编译时未启用 -parameters
选项时,才需要此注解。
@UserMessage
也可以从资源中加载提示模板:@UserMessage(fromResource = "my-prompt-template.txt")
有效 AI 服务方法示例
以下是一些有效 AI 服务方法的示例。
`UserMessage`
String chat(String userMessage);
String chat(@UserMessage String userMessage);
String chat(@UserMessage String userMessage, @V("country") String country); // userMessage 包含 "{{country}}" 模板变量
@UserMessage("What is the capital of Germany?")
String chat();
@UserMessage("What is the capital of {{it}}?")
String chat(String country);
@UserMessage("What is the capital of {{country}}?")
String chat(@V("country") String country);
@UserMessage("What is the {{something}} of {{country}}?")
String chat(@V("something") String something, @V("country") String country);
@UserMessage("What is the capital of {{country}}?")
String chat(String country); // 仅在 Quarkus 和 Spring Boot 应用程序中有效
`SystemMessage` 和 `UserMessage`
@SystemMessage("Given a name of a country, answer with a name of it's capital")
String chat(String userMessage);
@SystemMessage("Given a name of a country, answer with a name of it's capital")
String chat(@UserMessage String userMessage);
@SystemMessage("Given a name of a country, {{answerInstructions}}")
String chat(@V("answerInstructions") String answerInstructions, @UserMessage String userMessage);
@SystemMessage("Given a name of a country, answer with a name of it's capital")
String chat(@UserMessage String userMessage, @V("country") String country); // userMessage 包含 "{{country}}" 模板变量
@SystemMessage("Given a name of a country, {{answerInstructions}}")
String chat(@V("answerInstructions") String answerInstructions, @UserMessage String userMessage, @V("country") String country); // userMessage 包含 "{{country}}" 模板变量
@SystemMessage("Given a name of a country, answer with a name of it's capital")
@UserMessage("Germany")
String chat();
@SystemMessage("Given a name of a country, {{answerInstructions}}")
@UserMessage("Germany")
String chat(@V("answerInstructions") String answerInstructions);
@SystemMessage("Given a name of a country, answer with a name of it's capital")
@UserMessage("{{it}}")
String chat(String country);
@SystemMessage("Given a name of a country, answer with a name of it's capital")
@UserMessage("{{country}}")
String chat(@V("country") String country);
@SystemMessage("Given a name of a country, {{answerInstructions}}")
@UserMessage("{{country}}")
String chat(@V("answerInstructions") String answerInstructions, @V("country") String country);
多模态
AI 服务目前不支持多模态,
请使用低级 API 来实现。
返回类型
AI 服务方法可以返回以下类型之一:
String
- 在这种情况下,返回 LLM 生成的输出,不进行任何处理/解析- 结构化输出 支持的任何类型 - 在这种情况下,
AI 服务将在返回前将 LLM 生成的输出解析为所需类型
任何类型都可以额外包装在 Result<T>
中,以获取有关 AI 服务调用的额外元数据:
TokenUsage
- AI 服务调用期间使用的令牌总数。如果 AI 服务对 LLM 进行了多次调用(例如,因为执行了工具),它将对所有调用的令牌使用量求和。- Sources - RAG 检索期间检索到的
Content
- AI 服务调用期间执行的所有工具(包括请求和结果)
- 最终聊天响应的
FinishReason
- 所有中间
ChatResponse
- 最终
ChatResponse
一个例子:
interface Assistant {
@UserMessage("Generate an outline for the article on the following topic: {{it}}")
Result<List<String>> generateOutlineFor(String topic);
}
Result<List<String>> result = assistant.generateOutlineFor("Java");
List<String> outline = result.content();
TokenUsage tokenUsage = result.tokenUsage();
List<Content> sources = result.sources();
List<ToolExecution> toolExecutions = result.toolExecutions();
FinishReason finishReason = result.finishReason();
结构化输出
如果你想从 LLM 接收结构化输出(例如,一个复杂的 Java 对象,
而不是 String
中的非结构化文本),你可以将 AI 服务方法的返回类型从 String
更改为其他类型。
注
有关结构化输出的更多信息,请参阅 这里。
几个例子:
boolean
作为返回类型
interface SentimentAnalyzer {
@UserMessage("Does {{it}} has a positive sentiment?")
boolean isPositive(String text);
}
SentimentAnalyzer sentimentAnalyzer = AiServices.create(SentimentAnalyzer.class, model);
boolean positive = sentimentAnalyzer.isPositive("It's wonderful!");
// true
Enum
作为返回类型
enum Priority {
CRITICAL, HIGH, LOW
}
interface PriorityAnalyzer {
@UserMessage("Analyze the priority of the following issue: {{it}}")
Priority analyzePriority(String issueDescription);
}
PriorityAnalyzer priorityAnalyzer = AiServices.create(PriorityAnalyzer.class, model);
Priority priority = priorityAnalyzer.analyzePriority("The main payment gateway is down, and customers cannot process transactions.");
// CRITICAL
POJO 作为返回类型
class Person {
@Description("first name of a person") // 你可以添加一个可选的描述来帮助 LLM 更好地理解
String firstName;
String lastName;
LocalDate birthDate;
Address address;
}
@Description("an address") // 你可以添加一个可选的描述来帮助 LLM 更好地理解
class Address {
String street;
Integer streetNumber;
String city;
}
interface PersonExtractor {
@UserMessage("Extract information about a person from {{it}}")
Person extractPersonFrom(String text);
}
PersonExtractor personExtractor = AiServices.create(PersonExtractor.class, model);
String text = """
In 1968, amidst the fading echoes of Independence Day,
a child named John arrived under the calm evening sky.
This newborn, bearing the surname Doe, marked the start of a new journey.
He was welcomed into the world at 345 Whispering Pines Avenue
a quaint street nestled in the heart of Springfield
an abode that echoed with the gentle hum of suburban dreams and aspirations.
""";
Person person = personExtractor.extractPersonFrom(text);
System.println(person); // Person { firstName = "John", lastName = "Doe", birthDate = 1968-07-04, address = Address { ... } }
JSON 模式
当提取自定义 POJO(实际上是 JSON,然后解析成 POJO)时,
建议在模型配置中启用 "JSON 模式"。
这样,LLM 将被迫以有效的 JSON 格式响应。
注
请注意,JSON 模式和工具/函数调用是相似的功能,
但有不同的 API 并用于不同的目的。
当你始终需要 LLM 以结构化格式(有效 JSON)响应时,JSON 模式非常有用。
此外,通常不需要状态/记忆,因此与 LLM 的每次交互都是独立的。
例如,你可能想从文本中提取信息,比如文本中提到的人的列表,
或者将自由形式的产品评论转换为带有字段的结构化形式,如String productName
、Sentiment sentiment
、List<String> claimedProblems
等。
另一方面,当 LLM 应该能够执行某些操作(例如,查找数据库、搜索网页、取消用户预订等)时,工具/函数非常有用。
在这种情况下,LLM 会提供一份工具列表及其预期的 JSON 模式,然后 LLM 自主决定
是否调用其中任何一个来满足用户请求。
早些时候,函数调用经常用于结构化数据提取,
但现在我们有了 JSON 模式功能,它更适合此目的。
以下是如何启用 JSON 模式:
对于 OpenAI:
对于 Azure OpenAI:
AzureOpenAiChatModel.builder()
...
.responseFormat(new ChatCompletionsJsonResponseFormat())
.build();
- 对于 Vertex AI Gemini:
VertexAiGeminiChatModel.builder()
...
.responseMimeType("application/json")
.build();
或者通过从 Java 类指定显式模式:
VertexAiGeminiChatModel.builder()
...
.responseSchema(SchemaHelper.fromClass(Person.class))
.build();
从 JSON 模式:
VertexAiGeminiChatModel.builder()
...
.responseSchema(Schema.builder()...build())
.build();
- 对于 Google AI Gemini:
GoogleAiGeminiChatModel.builder()
...
.responseFormat(ResponseFormat.JSON)
.build();
或者通过从 Java 类指定显式模式:
GoogleAiGeminiChatModel.builder()
...
.responseFormat(ResponseFormat.builder()
.type(JSON)
.jsonSchema(JsonSchemas.jsonSchemaFrom(Person.class).get())
.build())
.build();
从 JSON 模式:
GoogleAiGeminiChatModel.builder()
...
.responseFormat(ResponseFormat.builder()
.type(JSON)
.jsonSchema(JsonSchema.builder()...build())
.build())
.build();
- 对于 Mistral AI:
MistralAiChatModel.builder()
...
.responseFormat(MistralAiResponseFormatType.JSON_OBJECT)
.build();
- 对于 Ollama:
OllamaChatModel.builder()
...
.responseFormat(JSON)
.build();
- 对于其他模型提供商:如果底层模型提供商不支持 JSON 模式,
提示工程是你的最佳选择。另外,尝试降低temperature
以获得更高的确定性。
流式传输
AI 服务可以在使用 TokenStream
返回类型时逐个令牌地流式传输响应:
interface Assistant {
TokenStream chat(String message);
}
StreamingChatModel model = OpenAiStreamingChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName(GPT_4_O_MINI)
.build();
Assistant assistant = AiServices.create(Assistant.class, model);
TokenStream tokenStream = assistant.chat("Tell me a joke");
tokenStream
.onPartialResponse((String partialResponse) -> System.out.println(partialResponse))
.onPartialThinking((PartialThinking partialThinking) -> System.out.println(partialThinking))
.onRetrieved((List<Content> contents) -> System.out.println(contents))
.onIntermediateResponse((ChatResponse intermediateResponse) -> System.out.println(intermediateResponse))
// 这将在工具执行之前调用。BeforeToolExecution 包含 ToolExecutionRequest(例如工具名称、工具参数等)
.beforeToolExecution((Consumer<BeforeToolExecution> beforeToolExecution) -> System.out.println(beforeToolExecution))
// 这将在工具执行之后调用。ToolExecution 包含 ToolExecutionRequest 和工具执行结果。
.onToolExecuted((ToolExecution toolExecution) -> System.out.println(toolExecution))
.onCompleteResponse((ChatResponse response) -> System.out.println(response))
.onError((Throwable error) -> error.printStackTrace())
.start();
Flux
你也可以使用 Flux<String>
代替 TokenStream
。
为此,请导入 langchain4j-reactor
模块:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
<version>1.3.0-beta9</version>
</dependency>
interface Assistant {
Flux<String> chat(String message);
}
聊天记忆
AI 服务可以使用聊天记忆来“记住”以前的交互:
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
在这种情况下,相同的 ChatMemory
实例将用于 AI 服务的所有调用。
但是,如果你有多个用户,这种方法将不起作用,
因为每个用户都需要自己的 ChatMemory
实例来维护他们各自的对话。
解决此问题的方法是使用 ChatMemoryProvider
:
interface Assistant {
String chat(@MemoryId int memoryId, @UserMessage String message);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10))
.build();
String answerToKlaus = assistant.chat(1, "Hello, my name is Klaus");
String answerToFrancine = assistant.chat(2, "Hello, my name is Francine");
在这种情况下,ChatMemoryProvider
将提供两个不同的 ChatMemory
实例,每个记忆 ID 一个。
以这种方式使用 ChatMemory
时,为了避免内存泄漏,清除不再需要的对话的内存也很重要。要使 AI 服务内部使用的聊天记忆可访问,只需定义它的接口扩展 ChatMemoryAccess
接口即可。
interface Assistant extends ChatMemoryAccess {
String chat(@MemoryId int memoryId, @UserMessage String message);
}
这使得既可以访问单个对话的 ChatMemory
实例,又可以在对话终止时清除它。
String answerToKlaus = assistant.chat(1, "Hello, my name is Klaus");
String answerToFrancine = assistant.chat(2, "Hello, my name is Francine");
List<ChatMessage> messagesWithKlaus = assistant.getChatMemory(1).messages();
boolean chatMemoryWithFrancineEvicted = assistant.evictChatMemory(2);
注
请注意,如果 AI 服务方法没有用 @MemoryId
注解的参数,ChatMemoryProvider
中的 memoryId
值将默认为字符串 "default"
。
注
请注意,不应针对同一个 @MemoryId
并发调用 AI 服务,
因为它可能导致 ChatMemory
损坏。
目前,AI 服务没有实现任何机制来防止针对同一个 @MemoryId
的并发调用。
工具(函数调用)
AI 服务可以使用工具进行配置,LLM 可以使用这些工具:
class Tools {
@Tool
int add(int a, int b) {
return a + b;
}
@Tool
int multiply(int a, int b) {
return a * b;
}
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.tools(new Tools())
.build();
String answer = assistant.chat("What is 1+2 and 3*4?");
在这种情况下,LLM 将在提供最终答案之前请求执行 add(1, 2)
和 multiply(3, 4)
方法。
LangChain4j 将自动执行这些方法。
有关工具的更多详细信息,请参阅 这里。
RAG
AI 服务可以使用 ContentRetriever
进行配置,以启用简单的 RAG:
EmbeddingStore embeddingStore = ...
EmbeddingModel embeddingModel = ...
ContentRetriever contentRetriever = new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel);
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.contentRetriever(contentRetriever)
.build();
配置 RetrievalAugmentor
提供了更大的灵活性,
可以实现高级 RAG 功能,例如查询转换、重新排序等:
RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
.queryTransformer(...)
.queryRouter(...)
.contentAggregator(...)
.contentInjector(...)
.executor(...)
.build();
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.retrievalAugmentor(retrievalAugmentor)
.build();
有关 RAG 的更多详细信息,请参阅 这里。
更多 RAG 示例请参阅 这里。
自动审核
AI 服务可以自动执行内容审核。当检测到不当内容时,会抛出 ModerationException
,其中包含原始的 Moderation
对象。
此对象包含有关被标记内容的信息,例如被标记的具体文本。
可以在构建 AI 服务时配置自动审核:
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.moderationModel(moderationModel) // 配置审核模型
.build();
编程式 ChatRequest 重写
在某些情况下,在将 ChatRequest
发送给 LLM 之前对其进行修改可能很有用。例如,可能需要向用户消息附加一些额外上下文,或者根据某些外部条件修改系统消息。
可以通过使用 UnaryOperator<ChatRequest>
配置 AI 服务来实现,该函数实现将应用于 ChatRequest
的转换:
Assistant assistant = AiServices.builder(Assistant.class)
.chatModel(model)
.chatRequestTransformer(transformingFunction) // 配置要应用于 ChatRequest 的转换函数
.build();
如果还需要访问 ChatMemory
来实现所需的 ChatRequest
转换,也可以使用 BiFunction<ChatRequest, Object, ChatRequest>
配置 chatRequestTransformer
方法,其中传递给此函数的第二个参数是记忆 ID。
链接多个 AI 服务
你的 LLM 驱动应用程序的逻辑越复杂,
将其分解为更小的部分就越重要,这在软件开发中是常见做法。
例如,将大量指令塞进系统提示以应对所有可能的情况容易出错且效率低下。如果指令太多,LLM 可能会忽略一些。
此外,指令的呈现顺序也很重要,这使得该过程更具挑战性。
这个原则也适用于工具、RAG 和模型参数,如 temperature
、maxTokens
等。
你的聊天机器人可能不需要随时了解你拥有的每个工具。
例如,当用户只是向聊天机器人打招呼或说再见时,
让 LLM 访问数十甚至数百个工具是昂贵且有时甚至是危险的(每次包含在 LLM 调用中的工具都会消耗大量令牌),
并且可能导致意外结果(LLM 可能会产生幻觉或被操纵以调用带有意外输入的工具)。
关于 RAG:同样,有时需要向 LLM 提供一些上下文,
但并非总是如此,因为它会产生额外成本(更多上下文 = 更多令牌)
并增加响应时间(更多上下文 = 更高延迟)。
关于模型参数:在某些情况下,你可能需要 LLM 具有高度确定性,
因此你会设置较低的 temperature
。在其他情况下,你可能会选择较高的 temperature
,等等。
重点是,更小、更具体的组件更容易、更便宜地开发、测试、维护和理解。
另一个需要考虑的方面涉及两个极端:
- 你是希望你的应用程序具有高度确定性,
其中应用程序控制流程而 LLM 只是其中一个组件? - 还是你希望 LLM 具有完全自主权并驱动你的应用程序?
或者两者兼而有之,这取决于具体情况?
当你将应用程序分解为更小、更易于管理的部分时,所有这些选项都是可能的。
AI 服务可以作为常规(确定性)软件组件使用并与其组合:
- 你可以一个接一个地调用 AI 服务(即链接)。
- 你可以使用确定性和 LLM 驱动的
if
/else
语句(AI 服务可以返回boolean
)。 - 你可以使用确定性和 LLM 驱动的
switch
语句(AI 服务可以返回enum
)。 - 你可以使用确定性和 LLM 驱动的
for
/while
循环(AI 服务可以返回int
和其他数字类型)。 - 你可以在单元测试中模拟 AI 服务(因为它是一个接口)。
- 你可以单独对每个 AI 服务进行集成测试。
- 你可以单独评估并为每个 AI 服务找到最佳参数。
- 等等
让我们考虑一个简单的例子。
我想为我的公司构建一个聊天机器人。
如果用户向聊天机器人打招呼,
我希望它用预定义的问候语进行响应,而不是依赖 LLM 来生成问候语。
如果用户提问,我希望 LLM 使用公司的内部知识库(即 RAG)生成响应。
下面是如何将此任务分解为 2 个单独的 AI 服务:
interface GreetingExpert {
@UserMessage("Is the following text a greeting? Text: {{it}}")
boolean isGreeting(String text);
}
interface ChatBot {
@SystemMessage("You are a polite chatbot of a company called Miles of Smiles.")
String reply(String userMessage);
}
class MilesOfSmiles {
private final GreetingExpert greetingExpert;
private final ChatBot chatBot;
...
public String handle(String userMessage) {
if (greetingExpert.isGreeting(userMessage)) {
return "Greetings from Miles of Smiles! How can I make your day better?";
} else {
return chatBot.reply(userMessage);
}
}
}
GreetingExpert greetingExpert = AiServices.create(GreetingExpert.class, llama2);
ChatBot chatBot = AiServices.builder(ChatBot.class)
.chatModel(gpt4)
.contentRetriever(milesOfSmilesContentRetriever)
.build();
MilesOfSmiles milesOfSmiles = new MilesOfSmiles(greetingExpert, chatBot);
String greeting = milesOfSmiles.handle("Hello");
System.out.println(greeting); // Greetings from Miles of Smiles! How can I make your day better?
String answer = milesOfSmiles.handle("Which services do you provide?");
System.out.println(answer); // At Miles of Smiles, we provide a wide range of services ...
请注意,我们如何使用更便宜的 Llama2 来完成识别文本是否为问候语的简单任务,
以及使用更昂贵的 GPT-4 和内容检索器(RAG)来完成更复杂的任务。
这是一个非常简单且有些天真的例子,但希望它能演示这个想法。
现在,我可以模拟 GreetingExpert
和 ChatBot
,并单独测试 MilesOfSmiles
此外,我可以单独对 GreetingExpert
和 ChatBot
进行集成测试。
我可以分别评估它们,并为每个子任务找到最优参数,
或者,从长远来看,甚至可以为每个特定的子任务微调一个小的专用模型。