主题
12 · 类型(Types)
核心类型(Core Types)
LlmOptions
LlmOptions 类用于指定使用哪个 LLM 及其超参数。 它定义在 embabel-common 项目中,提供流式 API 来进行 LLM 配置:
java
// 用模型与温度创建 LlmOptions
var options = LlmOptions
.withModel(OpenAiModels.GPT_4O_MINI)
.withTemperature(0.8);
// 不同任务使用不同超参数
var analyticalOptions = LlmOptions
.withModel(OpenAiModels.GPT_4O_MINI)
.withTemperature(0.2)
.withTopP(0.9);重要方法:
withModel(String):指定模型名称;withRole(String):指定模型角色。该角色必须是配置中通过embabel.models.llms定义过的;withTemperature(Double):设置创造性/随机性(0.0 ~ 1.0);withTopP(Double):设置 nucleus 采样参数;withTopK(Integer):设置 top-K 采样参数;withPersona(String):添加一条 system message 人设。
LlmOptions 是可反序列化的,因此你可以在 application.yml 等应用配置文件中设置 LlmOptions 类型的属性。 这是把模型乃至超参数外部化的一种强力做法。
PromptRunner
用户应用中的所有 LLM 调用都应该通过 PromptRunner 接口进行。 一个 PromptRunner 一旦创建,就可以用同一套 LLM、超参数、工具组与 PromptContributor 运行多个提示词。
获取 PromptRunner
通过流式 API 从 OperationContext 获取 PromptRunner:
java
@Action
public Story createStory(UserInput input, OperationContext context) {
// 用默认 LLM 获取 PromptRunner
var runner = context.ai().withDefaultLlm();
// 用指定的 LLM 选项获取 PromptRunner
var customRunner = context.ai().withLlm(
LlmOptions.withModel(OpenAiModels.GPT_4O_MINI)
.withTemperature(0.8)
);
return customRunner.createObject("Write a story about: " + input.getContent(), Story.class);
}PromptRunner 方法
核心:创建对象
createObject(String, Class):从提示词创建类型化对象,失败则抛异常。异常会触发重试;若反复重试失败,则触发重规划。createObjectIfPossible(String, Class):尝试创建对象,失败时返回null。这可能引起重规划。generateText(String):生成简单文本响应。
通常你会想用某个 createObject 方法,以确保响应被正确类型化。
工具与上下文管理
withToolGroup(String):为 LLM 访问添加工具组;withToolObject(Object):添加带@Tool方法的领域对象;withPromptContributor(PromptContributor):添加上下文贡献器;withImage(AgentImage):为支持视觉的 LLM 在提示词中加入一张图片;withImages(AgentImage...):在提示词中加入多张图片。
LLM 配置
withLlm(LlmOptions):使用特定的 LLM 配置;withGenerateExamples(Boolean):控制示例生成。
返回特定类型
creating(Class):进入Creating流式 API,用于返回特定类型。
例如:
java
var story = context.ai()
.withDefaultLlm()
.withToolGroup(CoreToolGroups.WEB)
.creating(Story.class)
.fromPrompt("Create a story about: " + input.getContent());这么做的主要原因是为 few-shot 提示添加强类型示例。例如:
java
var story = context.ai()
.withDefaultLlm()
.withToolGroup(CoreToolGroups.WEB)
.creating(Story.class)
.withExample("A children's story", new Story("Once upon a time...")) // ①
.fromPrompt("Create a story about: " + input.getContent());① 示例会以 JSON 格式被包含进提示词,用以引导 LLM。
处理图片
java
var image = AgentImage.fromFile(imageFile);
var answer = context.ai()
.withLlm(AnthropicModels.CLAUDE_35_HAIKU) // ①
.withImage(image) // ②
.generateText("What is in this image?");① 需要支持视觉的模型:使用 Claude 3.x、GPT-4 Vision 或其它多模态 LLM。 ② 添加图片:图片会随文本提示词一起发给 LLM。可以多次调用以添加多张图片。
处理文档
java
var documentPath = java.nio.file.Paths.get("reports/quarterly-report.pdf");
var message = userMessage()
.document(documentPath)
.build();
var answer = context.ai()
.withDefaultLlm() // ①
.withMessage(message)
.generateText("Summarize this report");① 需要支持文档的模型:默认或配置的 LLM 必须能消费文档输入。文档支持取决于所选模型与提供方。
gemini-autoconfigure 提供方使用的是 Google 的 OpenAI 兼容端点,它支持文本与图片输入,但不支持文档文件部分。 要处理 Gemini 的文档输入,请使用原生的 Google GenAI 提供方。
multimodal(text, file) 与 multimodal(text, path) 是图片的快捷方式。 对文档文件或路径,请使用 multimodal(text).document(fileOrPath).build() 或 AgentDocument.fromFile(file)。
高级特性
rendering(String):使用 Jinja 模板生成提示词(返回Rendering接口);withTool(Subagent.ofClass(MyAgent.class).consuming(MyInput.class)):启用向其它 Agent 的移交(handoff);evaluateCondition(String, String):评估布尔条件。
校验(Validation)
Embabel 支持在领域对象上使用 JSR-380(Bean Validation)注解。 当通过 PromptRunner.createObject 或 createObjectIfPossible 创建对象时,反序列化后会自动执行校验。 如果校验失败,Embabel 会透明地重试该 LLM 调用以获取合法对象,并把校验错误描述给 LLM,帮助它修正响应。
如果第二次校验仍失败,会抛出 InvalidLlmReturnTypeException。 若不捕获,它将触发重规划。你也可以选择在发起 LLM 调用的动作方法内部捕获它,并在自己的代码里做相应处理。
注解用法简单示例:
java
public class User {
@NotNull(message = "Name cannot be null")
private String name;
@AssertTrue(message = "Working must be true")
private boolean working;
@Size(min = 10, max = 200, message
= "About Me must be between 10 and 200 characters")
private String aboutMe;
@Min(value = 18, message = "Age should not be less than 18")
@Max(value = 150, message = "Age should not be greater than 150")
private int age;
@Email(message = "Email should be valid")
private String email;
// 标准的 setter 与 getter
}你也可以使用自定义注解,其 validator 会由 Spring 注入。例如:
java
@Target({ElementType.FIELD, ElementType.PARAMETER}) // ①
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PalindromeValidator.class)
public @interface MustBePalindrome {
String message() default "Must be a palindrome";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class Palindromic {
@MustBePalindrome // ②
private String eats;
public Palindromic(String eats) {
this.eats = eats;
}
public String getEats() {
return eats;
}
}
@Component // ③
public class PalindromeValidator implements ConstraintValidator<MustBePalindrome, String> {
private final Ai ai; // ④
public PalindromeValidator(Ai ai) {
this.ai = ai;
}
@Override
public boolean isValid(String field, ConstraintValidatorContext context) {
if (field == null) {
return false;
}
return field.equals(new StringBuilder(field).reverse().toString());
}
}注记:
- ① 定义自定义注解;
- ② 把注解应用到字段上;
- ③ 把 validator 实现为 Spring 组件 —— 注意
@Component注解; - ④ Spring 会把依赖注入进 validator,例如本例中的
Ai实例。
于是我们就得到了带完整 Spring 依赖注入支持的、标准的 JSR-380 校验。
AgentImage
表示一张用于支持视觉的 LLM 的图片。
工厂方法:
AgentImage.fromFile(File):从文件加载(会依据常见扩展名自动识别 MIME 类型);AgentImage.fromPath(Path):从路径加载(自动识别 MIME 类型);AgentImage.create(String, byte[]):用显式 MIME 类型与字节数组创建;AgentImage.fromBytes(String, byte[]):用文件名与字节创建(自动识别 MIME 类型)。
如果对某个受支持的图片格式自动识别失败,请改用 AgentImage.create() 并显式指定 MIME 类型。
AgentDocument
表示一份用于支持文档的 LLM 的文档。
工厂方法:
AgentDocument.fromFile(File):从文件加载(会依据常见扩展名自动识别 MIME 类型);AgentDocument.fromPath(Path):从路径加载(自动识别 MIME 类型);AgentDocument.create(String, byte[], String?):用显式 MIME 类型、字节数组以及可选文件名创建;AgentDocument.fromBytes(String, byte[]):用文件名与字节创建(自动识别 MIME 类型)。
Embabel 能按 MIME 类型识别 PDF、CSV、Microsoft Office 文档(.doc、.docx、.xlsx)以及 OpenDocument 文件(.odt、.ods、.odp)。 提供方的支持情况随模型与适配器而异,请确认所选 LLM 能消费你要发送的文档格式。
如果对某个受支持的文档格式自动识别失败,请改用 AgentDocument.create() 并显式指定 MIME 类型。
示例:
java
var documentPath = java.nio.file.Paths.get("reports/quarterly-report.pdf");
var message = userMessage()
.document(documentPath)
.build();
var answer = context.ai()
.withDefaultLlm()
.withMessage(message)
.generateText("Summarize this report");