规划器 Agent
规划器 Agent 是一类可以通过迭代规划周期来规划和执行多步任务的 AI Agent。 它们会持续构建或更新规划、执行步骤,并根据当前状态检查完成标准。
规划器 Agent 适用于复杂的任务, 这些任务需要将高层目标分解为较小的、可操作的步骤, 并根据每一步的结果调整规划。
规划器 Agent 通过迭代规划周期运行:
- 规划器根据当前状态创建或更新规划。
- 规划器执行规划中的单个步骤,并更新状态。
- 规划器根据当前状态确定规划是否已完成。
- 如果规划已完成,循环结束。
- 如果规划未完成,循环从第一步重新开始。
graph LR
A[创建或更新规划] --> B["执行步骤并更新状态"]
B --> C["检查是否完成"]
C -->|已完成| D[[完成]]
C -->|"未完成"| A前提条件
在开始之前,请确保你具备以下条件:
- 一个可以运行的 Kotlin/JVM 项目。
- 已安装 Java 17+。
- 来自你用于实现 AI Agent 的 LLM 提供者的有效 API 密钥。有关所有可用提供者的列表, 请参阅 LLM 提供者。
TIP
使用环境变量或安全的配置管理系统来存储你的 API 密钥。 避免在源代码中直接硬编码 API 密钥。
添加依赖项
要使用规划器 Agent,请在你的构建配置中包含以下依赖项:
dependencies {
implementation("ai.koog:koog-agents:VERSION")
}有关所有可用的安装方法,请参阅安装 Koog。
基于 LLM 的简单规划器
基于 LLM 的简单规划器使用 LLM 来生成和评估规划。 它们在基于字符串的状态上运行,并通过 LLM 请求执行步骤。 基于字符串的状态意味着 Agent 状态被记录为单个字符串, 其中 Agent 接受一个初始状态字符串并返回最终状态字符串作为结果。
Koog 提供了两种简单的规划器:
- SimpleLLMPlanner 仅在最开始生成一次规划,然后遵循该规划直至完成。 要包含重新规划功能,请扩展
SimpleLLMPlanner并重写assessPlan方法, 以指示 Agent 何时应重新规划。 - SimpleLLMWithCriticPlanner 实现了使用 LLM 的
assessPlan方法。 该方法通过 LLM 请求检查规划的有效性,并评估 Agent 是否应该重新规划。
以下示例展示了如何使用 SimpleLLMPlanner 创建一个简单的规划器 Agent:
// Create the planner
val planner = SimpleLLMPlanner()
// Wrap it in a planner strategy
val strategy = AIAgentPlannerStrategy(
name = "simple-planner",
planner = planner
)
// Configure the agent
val agentConfig = AIAgentConfig(
prompt = prompt("planner") {
system("You are a helpful planning assistant.")
},
model = OpenAIModels.Chat.GPT4o,
maxAgentIterations = 50
)
// Create the planner agent
val agent = PlannerAIAgent(
promptExecutor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),
strategy = strategy,
agentConfig = agentConfig
)
suspend fun main() {
// Run the agent with a task
val result = agent.run("Create a plan to organize a team meeting")
println(result)
}GOAP (目标导向型行动规划)
GOAP 是一种算法规划方法,它使用 A* 搜索 来寻找最优操作序列。 GOAP Agent 并不使用 LLM 来生成规划, 而是根据预定义的终端目标和操作自动发现操作序列。 在 Koog 中,GOAP 通过 DSL 实现,让你可以声明式地定义终端目标和操作。
GOAP 规划器涉及三个主要概念:
- 状态 (State):代表世界的当前状态。
- 操作 (Actions):定义可以执行的内容,包括前置条件、效果(信念)、开销和执行逻辑。
- 终端目标 (Goals):定义目标条件、启发式开销和价值函数。
GOAP 规划器使用 A* 搜索来寻找既能满足终端目标条件又能使总开销最小的操作序列。
要创建 GOAP Agent,你需要:
- 将状态定义为一个数据类,其属性代表与你的终端目标相关的各个方面。
- 使用 goap() 函数创建一个 GOAPPlanner 实例。
- 使用 AIAgentPlannerStrategy 包装该规划器,并将其传递给 PlannerAIAgent 构造函数。
NOTE
规划器选择单个操作及其序列。 每个操作都包含一个必须为真才能执行该操作的前置条件, 以及一个定义预测结果的信念。 有关信念的更多信息,请参阅状态信念与实际执行的比较。
在以下示例中,GOAP 处理创建文章的高层规划(大纲 → 草稿 → 审查 → 发布), 而 LLM 则在每个操作中执行实际的内容生成。
// Define a state for content creation
data class ContentState(
val topic: String,
val hasOutline: Boolean = false,
val outline: String = "",
val hasDraft: Boolean = false,
val draft: String = "",
val hasReview: Boolean = false,
val isPublished: Boolean = false
) : GoapAgentState<String, String>(topic) {
// output of the agent:
override fun provideOutput(): String = draft
}
// Create and run the agent
val agentConfig = AIAgentConfig(
prompt = prompt("writer") {
system("You are a professional content writer.")
},
model = OpenAIModels.Chat.GPT4o,
maxAgentIterations = 20
)
// Create GOAP planner strategy with LLM-powered actions
val plannerStrategy = AIAgentPlannerStrategy.goap("content-planner", ::ContentState) {
// Define actions with preconditions and beliefs
action(
name = "Create outline",
precondition = { state -> !state.hasOutline },
belief = { state -> state.copy(hasOutline = true, outline = "Outline") },
cost = { 1.0 }
) { ctx, state ->
// Use LLM to create the outline
val response = ctx.llm.writeSession {
appendPrompt {
user("Create a detailed outline for an article about: ${state.topic}")
}
requestLLM()
}
state.copy(hasOutline = true, outline = response.content)
}
action(
name = "Write draft",
precondition = { state -> state.hasOutline && !state.hasDraft },
belief = { state -> state.copy(hasDraft = true, draft = "Draft") },
cost = { 2.0 }
) { ctx, state ->
// Use LLM to write the draft
val response = ctx.llm.writeSession {
appendPrompt {
user("Write an article based on this outline:
${state.outline}")
}
requestLLM()
}
state.copy(hasDraft = true, draft = response.content)
}
action(
name = "Review content",
precondition = { state -> state.hasDraft && !state.hasReview },
belief = { state -> state.copy(hasReview = true) },
cost = { 1.0 }
) { ctx, state ->
// Use LLM to review the draft
val response = ctx.llm.writeSession {
appendPrompt {
user("Review this article and suggest improvements:
${state.draft}")
}
requestLLM()
}
println("Review feedback: ${response.content}")
state.copy(hasReview = true)
}
action(
name = "Publish",
precondition = { state -> state.hasReview && !state.isPublished },
belief = { state -> state.copy(isPublished = true) },
cost = { 1.0 }
) { ctx, state ->
println("Publishing article...")
state.copy(isPublished = true)
}
// Define the goal with a completion condition
goal(
name = "Published article",
description = "Complete and publish the article",
condition = { state -> state.isPublished }
)
}
val agent = AIAgent(
promptExecutor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),
strategy = plannerStrategy,
agentConfig = agentConfig
)
suspend fun main() {
val result = agent.run("The Future of AI in Software Development")
println("Final draft: $result")
}高级 GOAP 功能
自定义开销函数
由于 A* 搜索将开销作为寻找最优操作序列的一个因素, 你可以为操作和终端目标定义自定义开销函数来引导规划器:
action(
name = "Expensive operation",
precondition = { true },
belief = { state -> state.copy(operationDone = true) },
cost = { state ->
// Dynamic cost based on state
if (state.hasOptimization) 1.0 else 10.0
}
) { ctx, state ->
// Execute action
state.copy(operationDone = true)
}状态信念与实际执行的比较
GOAP 区分了信念(乐观预测)和实际执行这两个概念:
- 信念 (Belief):规划器认为会发生的事情,用于规划。
- 执行 (Execution):实际发生的情况,用于真实的状态更新。
这使得规划器能够根据预期结果制定规划,同时妥善处理实际结果:
action(
name = "Attempt complex task",
precondition = { state -> !state.taskComplete },
belief = { state ->
// Optimistic belief: task will succeed
state.copy(taskComplete = true)
},
cost = { 5.0 }
) { ctx, state ->
// Actual execution might fail or have different results
val success = performComplexTask()
state.copy(
taskComplete = success,
attempts = state.attempts + 1
)
}