跳转至

序列化

简介

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 在运行时传递类型信息的方式。

data class MyClass(val value: String)

// 内联具体化类型参数 — 在 Kotlin 中首选
val tokenReified = typeToken<MyClass>()

// 从 KClass 获取(当无法使用具体化类型参数时)
val tokenKClass = typeToken(MyClass::class)

// 泛型类型 — 在运行时保留类型参数
val tokenGeneric = typeToken<List<String>>()

record MyClass(
    String value
) {}

// 简单类
TypeToken tokenClass = TypeToken.of(MyClass.class);

// 泛型类型 — 使用 TypeCapture 来保留类型参数
TypeToken tokenGeneric = TypeToken.of(new TypeCapture<List<String>>() {});

JSONElement — 库无关的 JSON

JSONElementJSON 数据的中立中间表示。 它的存在使得序列化器、工具和代理内部实现不依赖于特定库的 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 类型之间进行转换。当你已经有一个 JsonElementJsonNode 等对象,并希望将其传递给 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
// 默认实例 — 使用 Json.Default
val defaultSerializer = KotlinxSerializer()

// 自定义 Json 配置
val customSerializer = KotlinxSerializer(
    json = Json {
        ignoreUnknownKeys = true
        prettyPrint = true
    }
)

你也可以在 Koog 的 JSONElement 与 kotlinx-serialization 的 JsonElement 之间进行转换。

val koogJson: JSONElement = JSONObject(
    mapOf(
        "key" to JSONPrimitive("value")
    )
)

// 转换为 kotlinx-serialization 动态 JSON 实例
val kotlinxJson: JsonElement = koogJson.toKotlinxJsonElement()

// 转换为 Koog 动态 JSON 实例
val koogJsonConverted: JSONElement = kotlinxJson.toKoogJSONElement()

JacksonSerializer(仅限 JVM

  • 模块ai.koog:serialization-jackson(独立依赖项)
  • 支持库:jackson-databind

将依赖项添加到你的 build.gradle.kts

dependencies {
    implementation("ai.koog:serialization-jackson:<version>")
}

然后创建序列化器:

// 默认实例 — 使用预先注册了 JSONElementModule 的新建 ObjectMapper
val defaultSerializer = JacksonSerializer()

// 自定义 ObjectMapper 配置
val customSerializer = JacksonSerializer(
    objectMapper = ObjectMapper().apply {
        configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    }
)

// 默认实例 — 使用预先注册了 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 之间进行转换。

val koogJson: JSONElement = JSONObject(
    mapOf(
        "key" to JSONPrimitive("value")
    )
)

// 转换为 Jackson 动态 JSON 实例
val jacksonJson: JsonNode = koogJson.toJacksonJsonNode()

// 转换为 Koog 动态 JSON 实例
val koogJsonConverted: JSONElement = jacksonJson.toKoogJSONElement()

```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

val agentConfig = AIAgentConfig(
    prompt = prompt("assistant") {
        system("You are a helpful assistant.")
    },
    model = OpenAIModels.Chat.GPT4o,
    maxAgentIterations = 10,
    serializer = JacksonSerializer()
)

在构造 AIAgentConfig 时传入 serializer 参数。 如果省略,则使用 JacksonSerializer

var agentConfig = AIAgentConfig.builder()
    .model(OpenAIModels.Chat.GPT4o)
    .prompt(
        Prompt.builder("assistant")
            .system("You are a helpful assistant")
            .build()
    )
    .maxAgentIterations(10)
    .serializer(new JacksonSerializer())
    .build();

工具如何与序列化器交互

代理运行时会自动在每个 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 序列化。

有关检查点创建和恢复的详细信息,请参阅 代理持久化