跳到主要内容

图执行引擎:strategy / subgraph / node / edge 与主循环

30 秒导读: Koog 把一个 agent 的行为画成一张有向图——节点(node)是"做一件事",边(edge)是"做完这件事后往哪走"。整张图由一个统一引擎驱动:一个 while(true) 主循环,反复"跑当前节点 → 按边挑下一个节点",直到走到终点节点(finish)才停。本章只讲这套骨架和控制流,不讲怎么用 DSL 搭图(见 02),也不讲节点里具体调 LLM 做了什么(见 02/03)。

本章所有源码引用都在克隆的这个目录下,为简洁,下文路径省略这段公共前缀: agents/agents-core/src/commonMain/kotlin/ai/koog/agents/core/ 例如下文写 agent/entity/AIAgentSubgraph.kt:288,完整路径是 agents/agents-core/src/commonMain/kotlin/ai/koog/agents/core/agent/entity/AIAgentSubgraph.kt:288


1. 这是什么(零基础也能懂)

一句话定义: Koog 的执行引擎,就是把"agent 该怎么一步步干活"写成一张流程图,然后用一台通用的图解释器去跑它。

它要解决的问题。 一个 agent 干活通常不是一条直线,而是"想一下 → 要不要调工具 → 调完再想 → 够了就收尾"这种带分支、带循环的过程。如果每个 agent 都手写一套 if/else + while,代码会乱成一团、没法复用、没法插桩观测。Koog 的做法是:把"走哪一步"这件事从业务代码里抽出来,交给一个统一引擎,业务只需声明"有哪些节点、节点之间怎么连"。

一句话直觉/类比。 把它想成地铁网:

  • 节点(node) = 车站,每站干一件具体的事(比如"问一次 LLM""执行一个工具")。
  • 边(edge) = 轨道,决定"这站办完事之后开往哪一站";轨道上还能装闸机(条件),不满足条件就不走这条。
  • 引擎 = 那台一直在开的列车:到站(执行节点)→ 看该走哪条轨道(选边)→ 开过去,循环往复,到终点站(finish)才停

四个承重概念(本章从头到尾就围着这四个转):

概念白话代码里是谁
strategy(策略)整张图 = 一个 agent 的完整行为剧本AIAgentGraphStrategy(本质是"根子图")
subgraph(子图)图里可嵌套的一块,自带起点/终点,可局部换工具AIAgentSubgraphBase
node(节点)图里一个执行单元,做一件事,输出一个值AIAgentNodeBase
edge(边)有向连线,决定下一步去哪,可转换/过滤数据AIAgentEdge

本节不出现底层代码。记住一句话就行:Koog 把 agent 建模成有向图,再用一个统一循环去驱动它。


2. 顶层全景(它大概怎么转)

2.1 从"调用 agent"到"图开始跑"的调用链

先看外层入口,搞清楚"我调 agent.run(...) 之后,控制权是怎么一路传到那台图解释器的"。

用户代码
│ agent.run(input)

AIAgent<Input,Output> 顶层接口:只声明 run / createSession
│ (agent/AIAgent.kt:15 expect abstract class)

GraphAIAgent 图式 agent 的实现:持有 strategy + promptExecutor + toolRegistry
│ (agent/GraphAIAgent.kt:48)
│ run() 建一个 Session

AIAgentRunSessionImpl 一次运行的生命周期管理:准备 context、发 pipeline 事件
│ (agent/AIAgentRunSessionImpl.kt:88 调 strategy.execute)

AIAgentGraphStrategy.execute 策略 = 根子图;负责 checkpoint 恢复钩子,再委托给子图引擎
│ (agent/entity/AIAgentGraphStrategy.kt:74)

AIAgentSubgraphBase ★ 真正的图执行引擎 ★
.executeWithInnerContext while(true) 主循环在这里
(agent/entity/AIAgentSubgraph.kt:270)

怎么读这张图: 从上到下是"越来越靠近引擎内核"。前三层是脚手架(接口、运行会话、策略包装),第四层 AIAgentSubgraphBase 才是本章的主角——那个 while(true) 循环就住在它里面。

2.2 部件一句话职责

部件干什么在哪个文件
AIAgent顶层抽象接口,只规定 run / createSession;是 expect abstract class,多平台agent/AIAgent.kt:15
GraphAIAgent图式 agent 的具体实现,组装 strategy / executor / tools / features,准备执行 contextagent/GraphAIAgent.kt:48
AIAgentGraphStrategy策略,本质是"根子图";额外承担 checkpoint 恢复(restoreStateIfNeeded)agent/entity/AIAgentGraphStrategy.kt:26
AIAgentSubgraphBase图执行引擎:选工具 → 跑主循环 → 发子图事件agent/entity/AIAgentSubgraph.kt:48
AIAgentNodeBase节点基类:持有出边列表 edges、负责 resolveEdge 选边agent/entity/AIAgentNode.kt:22
AIAgentEdge有向边:forwardOutput 返回 Option,命中/不命中agent/entity/AIAgentEdge.kt:23
ExecutionPointNode让子图能"强制从某个节点、带某个输入继续"——支撑跳转与 checkpointagent/entity/ExecutionPointNode.kt:9

2.3 主线走一遍(高层,不进代码)

一次 agent.run(input) 的大致流向:

run(input)
→ 建 Session,准备 AIAgentGraphContext(装 LLM 上下文、状态、存储、pipeline)
→ strategy.execute(context, input)
→ 先看有没有 checkpoint 要恢复(有就把执行点搬到断点处)
→ 进入子图引擎 executeWithInnerContext:
currentNode = 起点; currentInput = input
loop:
iterations++;若超上限 → 抛异常
output = currentNode 执行(input)
若被请求"中断"(跳转/回滚) → return null(交给上层重跑)
edge = 在 currentNode 的出边里,挑第一条"命中"的
若没有命中的边:
是 finish 节点 → 收尾,退出循环
不是 finish → 抛"卡死"异常
否则:currentNode = edge.目标节点; currentInput = edge 转换后的值
→ 返回 finish 节点的输出

看懂这段"大盘",下面第 3 节就逐个把关键机制拆开讲。


3. 核心原理(逐个机制,由浅入深)

3.1 节点与边:图的两种"零件"

节点:一个执行单元 + 一串出边

节点基类是 AIAgentNodeBase<in TInput, TOutput>——泛型说明它吃一个 TInput、吐一个 TOutput。它身上最关键的两样东西:

  1. 出边列表 edges:这个节点能通向哪些下家。它只能追加、不能乱改(private set,只经 addEdge 扩展),而且有序——选边时按顺序匹配,第一条命中的胜出。

真实源码(agent/entity/AIAgentNode.kt:52-64,edgesaddEdge):

public var edges: List<AIAgentEdge<TOutput, *>> = emptyList()
private set

public open fun addEdge(edge: AIAgentEdge<TOutput, *>) {
edges = edges + edge
}

这段说明:边是一条条追加进来的,顺序就是声明顺序,后面选边靠的正是这个顺序。

  1. 执行方法 execute:节点干的活。注意它的返回类型是 TOutput?——允许返回 null,表示"这次没产出、待会儿要重来"(用于中断/恢复,见 3.4)。普通节点(SimpleAIAgentNodeImpl)总是返回非空值。

引擎调节点时走的是去类型化的包装 executeUnsafe(agent/entity/AIAgentNode.kt:133),内部把 Any? 强转成 TInput 再调 execute——因为引擎主循环里 currentInputAny?,编译期对不上具体类型,只能在这一层做 unchecked cast。

边:带条件的有向连线

AIAgentEdge<IncomingOutput, OutgoingInput> 连接源节点的输出类型目标节点的输入类型。它的灵魂是那个 forwardOutput 函数(agent/entity/AIAgentEdge.kt:23-26):

public expect class AIAgentEdge<IncomingOutput, OutgoingInput> internal constructor(
fromNode: AIAgentNodeBase<*, IncomingOutput>,
toNode: AIAgentNodeBase<OutgoingInput, *>,
forwardOutput: suspend (context, output: IncomingOutput) -> Option<OutgoingInput>,
)

关键设计:forwardOutput 返回 Option(Koog 内部的"有值/无值"容器,agent/utils/Option.kt:Some 装值、None 表示空)。这一个返回值同时编码了两件事:

forwardOutput 返回含义引擎的反应
Some(转换后的值)这条边命中走这条边,把 Some 里的值当作下一个节点的输入
None这条边不命中(条件不满足)跳过它,继续试下一条边

也就是说,"条件判断"和"数据转换"被合并进同一个函数:一条边既可以无脑透传(forwardTo 默认 Some(output),见 agent/entity/AIAgentNode.kt:145-153),也可以在 forwardOutput 里做过滤(不满足就 None)或映射(满足就 Some(变形后的值))。这就是 Koog 表达"分支"的方式:同一个节点挂多条边,每条边自己判断要不要接这单

AIAgentEdgeexpect class,JVM 侧的 actual(jvmCommonMain/.../AIAgentEdge.kt)里 forwardOutputUnsafeoutput as IncomingOutput 的去类型化转发,和节点的 executeUnsafe 是一对配套的"擦除类型"入口。

选边:resolveEdge 的"顺序匹配、命中即停"

节点跑完后,引擎要决定"往哪走"——这就是 resolveEdge(agent/entity/AIAgentNode.kt:87-100):

public suspend fun resolveEdge(context, nodeOutput: TOutput): ResolvedEdge? {
for (currentEdge in edges) {
val output = currentEdge.forwardOutputUnsafe(nodeOutput, context)
if (!output.isEmpty) { // 命中(Some)
return ResolvedEdge(currentEdge, output.value)
}
}
return null // 没有任何边命中
}

语义两句话:

  • edges 顺序逐条试,第一条返回 Some 的边就是答案,连同它转换后的输出打包成 ResolvedEdge 返回。
  • 一条都不命中就返回 null——这个 null 是主循环的重要信号(见 3.3:它要么意味着"到终点了",要么意味着"卡死了")。

选边流程图:

节点输出 nodeOutput

├─ 边①.forwardOutput → Some? ── 是 ──▶ 走边①,输入 = Some.value (停止,不再试后面)
│ └ 否(None)
├─ 边②.forwardOutput → Some? ── 是 ──▶ 走边②
│ └ 否
├─ 边③ … 依次

└─ 全部 None ──▶ resolveEdge 返回 null

3.2 子图与策略:图可以套图,策略是"最外层那张图"

子图 = 可嵌套、可换工具的一块图

AIAgentSubgraphBase(agent/entity/AIAgentSubgraph.kt:48)本身也是一个 AIAgentNodeBase——这是 Koog 的一个漂亮设计:子图对外看就是一个"大节点",有输入有输出,可以像普通节点一样嵌进更大的图里。它的输入/输出类型直接取自内部的起点/终点节点(inputType = start.inputTypeoutputType = finish.outputType,agent/entity/AIAgentSubgraph.kt:58-59)。

子图比普通节点多出来的能力,是可以为自己这一段局部覆盖运行环境:

子图可局部覆盖的东西参数作用
可用的工具集toolSelectionStrategy这段只暴露哪些工具(见 3.5)
LLM 模型llmModel这段临时换个模型
采样参数llmParams这段临时换 temperature 等
响应处理器responseProcessor这段的响应后处理
历史隔离freshHistory只继承父级 system 消息,子图内的对话结束即丢弃

进入子图执行时,execute(agent/entity/AIAgentSubgraph.kt:173)会先算出生效工具集、用这些覆盖值 copy 一个新的 LLM 上下文替换进 context,跑完再还原原上下文(freshHistory 时直接丢弃子图内产生的对话)。这层"进出子图的上下文换挡",让局部配置不会泄漏到子图之外

策略 = 根子图 + checkpoint 恢复钩子

AIAgentGraphStrategy(agent/entity/AIAgentGraphStrategy.kt:26)是 expect class,其基类 AIAgentGraphStrategyBase(:43)直接继承 AIAgentSubgraphBase。所以一句话:

策略就是"最外层那张子图"(根子图)——它没有更外层的父图,就是整个 agent 的完整行为。

策略比普通子图多的,是它是执行的总入口,因此额外挂了 checkpoint(检查点)恢复逻辑。看它的 execute(agent/entity/AIAgentGraphStrategy.kt:74-85):

override suspend fun execute(context, input: TInput): TOutput? {
restoreStateIfNeeded(context) // 跑之前:有断点就先恢复到断点

var result: TOutput? = super.execute(context, input) // 委托给子图引擎跑一遍

// 如果这次没跑出结果、但 context 里又攒了新的"恢复数据",说明发生了跳转/回滚,循环再跑
while (result == null && context.getGraphAgentContextData() != null) {
restoreStateIfNeeded(context)
result = super.execute(context, input)
}
return result
}

两个恢复钩子:

  • restoreStateIfNeeded(:87):看 context 里有没有 GraphAgentContextData(一份"断点快照");有就调 restoreDefault 恢复,然后清掉这份数据。
  • restoreDefault(:104-142):把断点里的东西一样样搬回来——设置执行点(跳到断点节点之后)、还原 LLM 会话(模型、消息历史、参数、工具子集)、还原 storage、还原 iterations 计数。

这就解释了策略层那个外层 while:子图引擎每次遇到"要跳转/回滚"就返回 null 并在 context 留下断点数据;策略层看到 null + 有断点数据,就恢复现场再跑一遍,直到真正产出结果。跳转的机制细节见 3.4。

断点里的节点定位靠 setExecutionPointAfterNode(:199):它按 nodePath(以路径分隔符连接的节点路径,首段是 agent id 被丢弃)在 metadata.nodesMap 里找到那个"已完成的节点",用它保存的 output 解码后,解析出它的下一条边,把执行点钉在"该节点之后"。若那是个 FinishNode(无出边、输入即输出),则直接重启该 finish 节点。

3.3 主循环:整台引擎的心脏

这是本章最核心的一段——executeWithInnerContext 里的 while(true)(agent/entity/AIAgentSubgraph.kt:270-341)。它就是那台"到站→选轨→开车"的列车。

先看初始化(:273-286):currentNode 默认是起点 start;但如果存在强制执行点(getExecutionPoint() 非空,比如刚从 checkpoint 恢复,或被请求跳转),就改从那个节点、带那个输入开始,并把执行点重置。

再看循环体,一圈干四件事:

真实源码骨架(agent/entity/AIAgentSubgraph.kt:288-327,有删减):

while (true) {
// ① 迭代计数 + 上限保护
context.stateManager.withStateLock { state ->
if (++state.iterations > context.config.maxAgentIterations) {
throw AIAgentMaxNumberOfIterationsReachedException(context.config.maxAgentIterations)
}
}

// ② 执行当前节点(去类型化调用)
val nodeOutput: Any? = currentNode.executeUnsafe(context, currentInput)

// ③ 中断信号:context 里出现"恢复数据"= 有人请求跳转/回滚 → 交还上层
if (context.getGraphAgentContextData() != null) {
return null
}

// ④ 选边
val resolvedEdge = currentNode.resolveEdgeUnsafe(context, nodeOutput)
if (resolvedEdge == null) {
if (currentNode == finish) { // 没边可走 + 正好在终点 → 正常收尾
currentInput = nodeOutput
break
} else { // 没边可走 + 不在终点 → 卡死
throw AIAgentStuckInTheNodeException(currentNode, nodeOutput)
}
}
currentNode = resolvedEdge.edge.toNode // 前进到下一个节点
currentInput = resolvedEdge.output // 带上边转换后的输入
}

四步逐条讲清楚:

① 迭代上限——防死循环的保险丝。 每转一圈,state.iterations 自增;一旦超过 context.config.maxAgentIterations,立刻抛 AIAgentMaxNumberOfIterationsReachedException。这个上限默认只有 3(agent/config/AIAgentConfig.kt:115,maxAgentIterations: Int = 3),意味着不显式调大的话,一个 agent 最多走 3 步就会因超限而停——这是新手最容易撞到的坑。计数放在 withStateLock 里改,iterations 是受"是否 active"守护的属性(agent/entity/AIAgentState.kt:22)。

② 执行节点。 executeUnsafe 去类型化地跑当前节点,拿到 nodeOutput: Any?。节点内部具体做什么(调 LLM、执行工具……)是后续章节的事,引擎在这一层只把它当黑盒

③ 中断检查——把"跳转/回滚"接出循环。 节点跑完后,引擎立刻检查 context.getGraphAgentContextData()。若非空,表示在节点执行期间有人往 context 塞了"恢复数据"(即请求了跳转到别处、或回滚到 checkpoint),这一轮不能再正常选边,于是 return null 退出——把控制权交还给策略层那个外层 while(见 3.2),由它恢复现场后重跑。

④ 选边与退出。 用 3.1 的 resolveEdge 挑下一条边:

resolveEdge 结果当前是否 finish 节点引擎动作依据
命中某条边——前进:currentNode/currentInput 换成边的目标与转换值AIAgentSubgraph.kt:325-326
null(无边命中)正常退出:把节点输出当作最终结果,break:315-318
null(无边命中)卡死:抛 AIAgentStuckInTheNodeException:319-322

"卡死"这个设计值得记: finish 节点天然没有出边(FinishNode.addEdge 直接抛异常,agent/entity/AIAgentNode.kt:299-301),所以"走到 finish"和"没边可走"是同一件事——引擎正是靠 currentNode == finish 来区分"这是正常终点"还是"这是个死胡同"。任何非终点节点却没有一条边命中它的输出,都被判为图设计漏洞,当场报错而不是默默挂起。

退出之后(:329-340):把 currentInput(此时装着 finish 的输出)强转成 TOutput 返回;转不动就抛 IllegalStateException,提示 finish 节点必须产出与声明输出类型相容的值。

主循环一圈的状态流转:

┌─────────────────────────────────────────────────────────┐
│ ① iterations++ ──超上限──▶ 抛 MaxIterations 异常 │
│ │未超 │
│ ▼ │
│ ② 跑 currentNode → nodeOutput │
│ │ │
│ ▼ │
│ ③ context 有恢复数据? ──是──▶ return null(交还策略层重跑) │
│ │否 │
│ ▼ │
│ ④ resolveEdge(nodeOutput) │
│ ├─ 命中 ─▶ currentNode/Input 换新 ─┐ │
│ ├─ null 且在 finish ─▶ break(收尾) │ │
│ └─ null 且非 finish ─▶ 抛 Stuck 异常│ │
└──────────────────────────────────────────┘
└──────────── 回到 ① ────────────┘

3.4 ExecutionPoint:中断与跳转的实现底座

要解决的小问题: 有时需要"别按图的默认流程走,而是强行从节点 X、带输入 Y 继续"——比如从 checkpoint 恢复,或运行时动态改道。

机制: 接口 ExecutionPointNode(agent/entity/ExecutionPointNode.kt:9-33)给子图三个动作:

方法作用
enforceExecutionPoint(node, input)钉住下一步:强制从 node、带 input 开始
getExecutionPoint()读当前被钉住的执行点(没有则 null)
resetExecutionPoint()清除,恢复默认流程

AIAgentSubgraphBase 用两个私有字段 forcedNode / forcedInput 实现它(agent/entity/AIAgentSubgraph.kt:86-112)。enforceExecutionPoint 在已设置时拒绝重复设置(抛 IllegalStateException),保证同一时刻只有一个待跳转目标。

它怎么接进主循环: 就是 3.3 开头那段——主循环启动前先 getExecutionPoint(),非空就从被钉住的节点开始并 resetExecutionPoint()(:276-286)。而"发起跳转"的一侧,则是 3.3 步骤③:节点执行期间往 context 塞恢复数据 → 主循环 return null → 策略层 restoreDefault 里调 setExecutionPointAfterNode → 内部 enforceExecutionPoint 把目标钉好 → 下一轮 super.execute 的主循环从钉住的点接着跑。

一句话串起来: ExecutionPoint 是"跳转"的数据表示,return null 是"跳转"的控制流出口,策略层的 while + restore 是"跳转"的再入口。三者合起来,让 Koog 能在不破坏 while(true) 结构的前提下,支持任意的中断—恢复—跳转。

3.5 ToolSelectionStrategy:进子图时决定"这段能用哪些工具"

要解决的小问题: 不同阶段该暴露给 LLM 的工具往往不同——收集信息阶段给一堆搜索工具,收尾阶段可能一个都不给。子图正好是"分段"的单位,于是工具选择被挂在子图上。

进子图时 selectTools(agent/entity/AIAgentSubgraph.kt:121-152)按策略算出生效工具集。四种策略(sealed interface ToolSelectionStrategy,:432-481):

策略行为依据
ALL用当前上下文里的全部工具:122
NONE一个不给(空列表):123
Tools(tools)显式指定的那批工具:124
AutoSelectForTask(desc)问 LLM:根据子任务描述,从现有工具里挑相关的(结构化请求返回工具名列表,再按名筛选):125-151

AutoSelectForTask 是唯一会真的发一次 LLM 请求来挑工具的:它临时把历史压缩成 TLDR、追加一段"从这些工具里选与子任务相关的"提示、请求结构化的 SelectedTools(工具名列表)、再据此过滤,最后还原原 prompt(prompt = initialPrompt)——挑工具的这轮对话不污染正式历史。具体的 LLM 交互细节属于 03 LLM 层,这里只需知道:工具集是"进子图那一刻"定的,并在子图结束时随上下文一起还原。


4. 巧妙之处(可借鉴的技术)

  • "子图即节点"的自相似结构,让嵌套与复用免费获得。AIAgentSubgraphBase 继承 AIAgentNodeBase(agent/entity/AIAgentSubgraph.kt:57),于是一张图可以整块塞进另一张图当一个节点;策略无非是"没有父图的根子图"(AIAgentGraphStrategyBase : AIAgentSubgraphBase,agent/entity/AIAgentGraphStrategy.kt:49)。一套主循环通吃所有层级,引擎代码只需写一份

  • Option 把"条件"和"转换"合成一个返回值(agent/entity/AIAgentEdge.kt:23)。边的 forwardOutput 返回 Some 即"命中并给出转换后的值",None 即"这条边不接"。选边逻辑因此塌缩成一个极简的"顺序找第一个 Some"循环(agent/entity/AIAgentNode.kt:91-99),无需额外的谓词接口。

  • "走到 finish"和"没边可走"统一成同一条件,再靠 currentNode == finish 一步区分正常收尾与卡死(agent/entity/AIAgentSubgraph.kt:315-322)。配合 FinishNode 禁止出边(agent/entity/AIAgentNode.kt:299-301),让"终点"这个语义由数据结构本身保证,而非靠约定。

  • 跳转不破坏循环结构:节点内塞 context 数据 → 主循环 return null → 策略层 while + restore 再入(agent/entity/AIAgentGraphStrategy.kt:79-84)。把"中断/回滚/跳转"这种本会撕裂控制流的需求,收拢成"退出再重入"的干净模式。

  • 迭代保险丝默认极小(3)(agent/config/AIAgentConfig.kt:115):宁可保守报错也不放任 agent 空转烧钱——但也因此成了必须知道的默认值。


5. 边界与局限(诚实)

  • 默认 maxAgentIterations = 3(agent/config/AIAgentConfig.kt:115)。任何需要多轮"想—调工具—再想"的 agent,不显式调大就会撞 AIAgentMaxNumberOfIterationsReachedException。计数是跨整张图共享的(存在 stateManagerstate.iterations),不是每个子图各算一份。

  • 非 finish 节点必须至少有一条边命中其输出,否则卡死报错(AIAgentStuckInTheNodeException,agent/entity/AIAgentSubgraph.kt:319-322)。边的条件没覆盖某种输出,就是运行期错误——引擎不会替你兜底。

  • 类型安全在引擎主循环里被擦除:executeUnsafe / resolveEdgeUnsafe / forwardOutputUnsafe 都是 unchecked cast(agent/entity/AIAgentNode.kt:133:106)。图构建期(DSL,见 02)用泛型保证边两端类型对齐;一旦绕过 DSL 手接类型不匹配的节点,错误会推迟到运行期 ClassCastException

  • AutoSelectForTask 的工具筛选依赖 LLM 返回合规的结构化列表;虽有 fixingParser 兜底(agent/entity/AIAgentSubgraph.kt:145),仍是一次额外 LLM 往返,有成本与失败面。

  • 本章只覆盖引擎骨架与控制流。图怎么用 DSL 声明、有哪些预制节点/策略 → 02;节点内部的 LLM 动作与会话 → 03;工具定义与执行 → 04;pipeline 事件(onNodeExecution* / onSubgraphExecution*)与观测/持久化 → 05


6. 代码地图(导航索引)

路径均相对 agents/agents-core/src/commonMain/kotlin/ai/koog/agents/core/。行号 as-of sourceCommit;上游漂移时优先用符号名 grep 定位。

主题文件路径符号名
顶层 agent 接口agent/AIAgent.ktAIAgent(expect abstract class)、runcreateSession
图式 agent 实现agent/GraphAIAgent.ktGraphAIAgentprepareContext
运行会话 / 调 strategyagent/AIAgentRunSessionImpl.ktAIAgentRunSessionImpl.runstrategy.execute 调用点
单次运行基类agent/AIAgentBase.ktAIAgentBase.runstrategypipeline
策略 = 根子图agent/entity/AIAgentGraphStrategy.ktAIAgentGraphStrategyAIAgentGraphStrategyBaseexecute
checkpoint 恢复钩子agent/entity/AIAgentGraphStrategy.ktrestoreStateIfNeededrestoreDefaultsetExecutionPointAfterNode
图执行引擎 / 主循环agent/entity/AIAgentSubgraph.ktAIAgentSubgraphBaseexecuteWithInnerContextexecute
工具选择agent/entity/AIAgentSubgraph.ktselectToolsToolSelectionStrategy(ALL/NONE/Tools/AutoSelectForTask)
节点基类 / 选边agent/entity/AIAgentNode.ktAIAgentNodeBaseedgesaddEdgeresolveEdgeexecuteUnsafeforwardTo
起点/终点节点agent/entity/AIAgentNode.ktStartNodeFinishNode(addEdge 抛异常)
有向边agent/entity/AIAgentEdge.ktAIAgentEdgeforwardOutputforwardOutputUnsafe
有值/无值容器agent/utils/Option.ktOptionSomeNone
中断/跳转底座agent/entity/ExecutionPointNode.ktExecutionPointNodeExecutionPointenforceExecutionPoint
迭代上限配置agent/config/AIAgentConfig.ktmaxAgentIterations(默认 3)
引擎抛出的异常agent/exception/AIAgentException.ktAIAgentMaxNumberOfIterationsReachedExceptionAIAgentStuckInTheNodeException
迭代计数状态agent/entity/AIAgentState.ktiterations