序列化
简介
Koog 使用一个轻量级、与库无关的序列化层,将工具参数和结果与 JSON 相互转换。 该层位于代理运行时与底层序列化库之间,因此您可以更换库而无需更改任何工具或代理代码。
除了工具之外,序列化层还被代理功能(如持久化)用于序列化和反序列化节点输入和输出。
默认情况下,Koog 使用 KotlinxSerializer(基于 kotlinx-serialization)。
在 JVM 上,您也可以切换到 JacksonSerializer(基于 jackson-databind)。
JSONSerializer 接口
JSONSerializer 是位于 serialization-core 中的核心抽象。
该接口有四个主要方法(编码/解码到字符串和 JSONElement),
以及两个用于在 JSONElement 和字符串之间转换的便捷方法:
encodeToString/decodeFromString— 将类型化值序列化到/从 JSON 字符串。encodeToJSONElement/decodeFromJSONElement— 将类型化值序列化到/从JSONElement树。encodeJSONElementToString/decodeJSONElementFromString— 在JSONElement与其字符串形式之间转换。
以下示例展示了所有关键操作:
@Serializable
data class User(val name: String, val age: Int)
val serializer: JSONSerializer = KotlinxSerializer()
// 将数据类编码为 JSON 字符串
val json: String = serializer.encodeToString(User("Alice", 30), typeToken<User>())
// 将 JSON 字符串解码回数据类
val user: User = serializer.decodeFromString(json, typeToken<User>())
// 编码为 JSONElement 树
val element: JSONElement = serializer.encodeToJSONElement(user, typeToken<User>())
// 从 JSONElement 树解码
val userFromElement: User = serializer.decodeFromJSONElement(element, typeToken<User>())
// 在 JSONElement 和原始 JSON 字符串之间转换
val jsonString = """{"key": "value"}"""
val jsonElement: JSONElement = serializer.decodeJSONElementFromString(jsonString)
val backToString: String = serializer.encodeJSONElementToString(jsonElement)
```java // Jackson 可序列化类 record User( @JsonProperty("name") String name, @JsonProperty("age") int age ) {}
var serializer = new JacksonSerializer();
// 将数据类编码为 JSON 字符串 String json = serializer.encodeToString(new User("Alice", 30), TypeToken.of(User.class));
// 将 JSON 字符串解码回数据类 User user = serializer.decodeFromString(json, TypeToken.of(User.class));
// 编码为 JSONElement 树 JSONElement element = serializer.encodeToJSONElement(user, TypeToken.of(User.class));
// 从 JSONElement 树解码 User userFromElement = serializer.decodeFromJSONElement(element, TypeToken.of(User.class));
// 在 JSONElement 和原始 JSON 字符串之间转换
String jsonString = "{\"key\": \"value\"}";
JSONElement jsonElement = serializer.decodeJSONElementFromString(jsonString);
String backToString = serializer.encodeJSONElementToString(jsonElement);
类型令牌
TypeToken 是 Koog 在运行时传递类型信息的方式。
JSONElement — 库无关的 JSON 树
JSONElement 是 JSON 数据的中立中间表示。
它的存在使得序列化器、工具和代理内部实现不依赖于特定库的 JSON 类型。
层次结构
JSONElement
├── JSONObject – key-value pairs (entries: Map<String, JSONElement>)
├── JSONArray – ordered list (elements: List<JSONElement>)
└── JSONPrimitive
├── JSONLiteral – string, number, or boolean
└── JSONNull – JSON null singleton
与库类型之间的转换
每个序列化集成都提供了扩展函数,允许你在 JSONElement 和库自身的动态 JSON 类型之间进行转换。当你已经有一个 JsonElement、JsonNode 等对象,并希望将其传递给 Koog(或反之)时,这非常有用,而无需经过完整的编码/解码循环。下面为每个支持的库提供了示例。
构建和读取元素
val obj = JSONObject(
mapOf(
"name" to JSONPrimitive("Alice"),
"age" to JSONPrimitive(30),
"active" to JSONPrimitive(true),
)
)
val arr = JSONArray(listOf(JSONPrimitive(1), JSONPrimitive(2), JSONPrimitive(3)))
// 从对象中读取值
val nameContent: String = (obj.entries["name"] as JSONPrimitive).content // "Alice"
val age: Int? = (obj.entries["age"] as JSONPrimitive).intOrNull // 30
JSONObject obj = new JSONObject(
Map.of(
"name", JSONPrimitive.of("Alice"),
"age", JSONPrimitive.of(30),
"active", JSONPrimitive.of(true)
)
);
JSONArray arr = new JSONArray(List.of(JSONPrimitive.of(1), JSONPrimitive.of(2), JSONPrimitive.of(3)));
// 从对象中读取值
String nameContent = ((JSONPrimitive) obj.getEntries().get("name")).getContent(); // "Alice"
Integer age = ((JSONPrimitive) obj.getEntries().get("age")).getIntOrNull(); // 30
支持的序列化器
KotlinxSerializer(默认)- 模块:ai.koog:serialization-core(通过 ai.koog:agents-core 间接包含)
- 支持库:kotlinx-serialization
你也可以在 Koog 的 JSONElement 与 kotlinx-serialization 的 JsonElement 之间进行转换。
JacksonSerializer(仅限 JVM)
- 模块:
ai.koog:serialization-jackson(独立依赖项) - 支持库:jackson-databind
将依赖项添加到你的 build.gradle.kts:
然后创建序列化器:
// 默认实例 — 使用预先注册了 JSONElementModule 的新建 ObjectMapper
var defaultSerializer = new JacksonSerializer();
// 自定义 ObjectMapper 配置
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
var customSerializer = new JacksonSerializer(objectMapper);
Note
JacksonSerializer 会自动在它使用的 ObjectMapper 上注册 JSONElementModule,
以便正确序列化/反序列化 JSONElement 类型。
你也可以在 Koog 的 JSONElement 与 Jackson 的 JsonNode 之间进行转换。
```java JSONElement koogJson = new JSONObject( Map.of( "key", JSONPrimitive.of("value") ) );
``` // 转换为 Jackson 动态 JSON 实例 JsonNode jacksonJson = JacksonJSONElementMappers.toJacksonJsonNode(koogJson);
// 转换为 Koog 动态 JSON 实例
JSONElement koogJsonConverted = JacksonJSONElementMappers.toKoogJSONElement(jacksonJson);
```
<!--- KNIT exampleSerializationJava05.java -->
在 AIAgentConfig 中配置序列化器
在构造 AIAgentConfig 时传入 serializer 参数。
如果省略,则使用 KotlinxSerializer。
工具如何与序列化器交互
代理运行时会自动在每个 Tool 实例上调用以下方法。
在正常使用中,您无需自行调用它们。
decodeArgs(rawArgs, serializer)(JSON → TArgs) — 将来自 LLM 的原始 JSON 参数反序列化为工具的强类型参数类。encodeArgs(args, serializer)(TArgs → JSON) — 将强类型参数序列化回 JSON(供某些代理功能使用)。decodeResult(rawResult, serializer)(JSON → TResult) — 反序列化存储的 JSON 结果。encodeResult(result, serializer)(TResult → JSON) — 将工具的结果序列化为 JSON。encodeResultToString(result, serializer)(TResult → String) — 将工具的结果序列化为发送给 LLM 的字符串。 默认情况下,委托给encodeResult。可以重写以自定义发送给 LLM 的结果格式。
这些方法在 Tool 上是 open 的,因此如果您需要为特定工具自定义序列化行为,可以重写它们。
功能如何使用序列化器
序列化层不仅限于工具——某些代理功能也依赖它。
例如,持久化 使用在 AIAgentConfig 中配置的 JSONSerializer 来序列化和反序列化节点输入和输出,以创建检查点和恢复代理状态。这意味着流经持久化节点的任何类型都必须能够被配置的 JSONSerializer 序列化。
有关检查点创建和恢复的详细信息,请参阅 代理持久化。