跳到主要内容

LLM 层:Prompt 模型、Executor、多 Client 与会话

30 秒导读: 上一章 讲的节点(比如 nodeLLMRequest)最终都要"跟大模型说句话"。这一层就是那句话怎么被表示、怎么被送出去、怎么在多个模型/厂商之间切换。核心是三层分工:Prompt/Message(不可变的对话数据)→ PromptExecutor(决定用哪个模型、怎么路由)→ LLMClient(把统一数据编码成各家 HTTP API)。Agent 侧再包一层 会话(session),让节点安全地读写这段历史。


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

一句话定义: 这一层把"和 LLM 对话"拆成"数据(说了什么)、"调度(找哪个模型)、"翻译(怎么发给 OpenAI/Anthropic)"三件互不耦合的事。

解决什么问题: 你写 agent 时不想被绑死在某一家。今天用 GPT-5、明天想换 Claude、或想让同一段对话历史在两家之间来回切——只要对话数据本身跟厂商无关,切换就只是"换个翻译官"。Koog 的做法正是:对话历史用一套中立的数据结构存,每家厂商各有一个 client 负责编解码

核心组件一览:

组件白话职责所在模块
Prompt / Message一段不可变的对话历史(系统/用户/助手消息 + 参数)prompt-model
PromptBuilder用 DSL 拼出 Prompt 的构造器prompt-model
LLMParams温度、maxTokens、结构化 schema、tool choice 等调用参数prompt-model
PromptExecutor"把 prompt 送出去"的统一入口;可组合出多模型/路由/轮询prompt-executor-model
LLMClient单个厂商的真实 HTTP 客户端(编码请求、解码响应)prompt-executor-clients/*
AIAgentLLMContext + sessionagent 侧的会话入口,节点通过它读写历史、发起请求agents-core

一句话直觉:Prompt 想成一封跟快递公司无关的信;PromptExecutor前台(决定这封信交给哪家快递);LLMClient具体那家快递(把信塞进它家规定的信封格式)。换快递不用重写信。


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

一次"节点请求 LLM"从上到下穿过这四层。怎么读这张图:从上到下是一次请求的下沉路径,箭头是调用方向;虚线是"同一份 Prompt 被重新编码"的关键点。

节点(02 章) agent.nodeLLMRequest 之类
│ llm.writeSession { requestLLM() }

┌─────────────────────────────┐
│ AIAgentLLMContext │ 持有 prompt / model / tools / executor
│ · writeSession(写锁) │ 发请求 + 把回复 append 回 prompt
│ · readSession(读锁) │
└─────────────┬───────────────┘
│ executor.execute(prompt, model, tools)

┌─────────────────────────────┐
│ PromptExecutor(可组合) │ MultiLLM / Routing / RoundRobin
│ 1. resolveModel → 选出有效模型 │
│ 2. clientFor(provider) → 选 client │
└─────────────┬───────────────┘
│ client.execute(prompt, model, tools)

┌─────────────────────────────┐
│ LLMClient(单厂商) │ OpenAI / Anthropic / Google / ...
│ · 把中立 Prompt 编码成本家 API 请求 ┄┄┄> 换 model 就换 client,重新编码同一份历史
│ · HTTP 调用,解码回 Message.Assistant │
└─────────────┬───────────────┘
│ Message.Assistant(回复)

回到 writeSession,appendPrompt { message(response) }

主线走一遍(高层):

  1. 节点在 writeSession 里调 requestLLM()
  2. 会话把当前 prompt + tools 交给 PromptExecutor.execute(...)
  3. Executor 先 resolveModel 决定真正用哪个模型(可能触发 fallback),再按 provider 挑出一个 LLMClient
  4. Client 把这份中立的 Prompt 编码成本家 HTTP 请求,发出,解码回 Message.Assistant
  5. 会话把这条助手消息 append 回 prompt,历史增长一轮。

关键卖点就在第 3-4 步:prompt 从头到尾没变过,换模型/换厂商只是换了个 client 重新编码同一份历史——这就是"LLM 可随时切换、历史无缝适配"。


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

3.1 Prompt 与 Message:不可变的对话历史

要解决的小问题: 对话历史要能被反复拷贝、裁剪、换参数,还不能被并发改乱。

思路: 用 Kotlin 的 data class + 密封类型(sealed),把历史做成不可变值——任何"修改"都返回新对象,老对象不动。

Prompt 就是三元组:一串消息 + id + 参数。

// 示意,非源码:Prompt 的形状
data class Prompt(
val messages: List<Message>, // 不可变的对话历史
val id: String,
val params: LLMParams = LLMParams()
)

真实定义在 prompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/Prompt.kt:25(Prompt)。所有"改"都是拷贝:withMessages(Prompt.kt:129)、withParams(Prompt.kt:138)、withUpdatedParams(Prompt.kt:223)都返回新的 Prompt。它还顺手算两个只读量:latestTokenUsage(取最后一条助手消息的 token 数,Prompt.kt:102)和 totalTimeSpent(首尾消息时间差,Prompt.kt:117)。

Message 是一棵密封树,按角色分三种,每种由若干 MessagePart 组成:

消息类型Role允许的 part典型来源
Message.SystemSystem只允许 Text系统提示/人设
Message.UserUserRequestPart(Text、Attachment、Tool.Result)用户输入、工具结果回填
Message.AssistantAssistantResponsePart(Text、Reasoning、Tool.Call)模型回复

真实定义在 prompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/message/Message.kt:21(Message sealed interface)。MessagePart 用方向来分类(Message.kt:246):RequestPart 是发给模型的、ResponsePart 是模型返回的、ContentPart(Text/Attachment)两个方向都行。工具相关的两种 part 尤其重要:MessagePart.Tool.Call(模型要调工具,Message.kt:348)和 MessagePart.Tool.Result(工具结果,Message.kt:387)——它们让"一次工具往返"也能被存进同一条历史里(工具系统细节见 04 章)。

每条消息还带 metaInfo:请求侧是 RequestMetaInfo(只有时间戳,Message.kt:441),响应侧是 ResponseMetaInfo(带 totalTokensCount/inputTokensCount/outputTokensCount/modelId,Message.kt:489)——token 统计和跨模型溯源都靠它。

3.2 PromptBuilder DSL:把历史拼出来

要解决的小问题: 手写一串 Message.System(...)Message.User(...) 太啰嗦。

思路: 给一个 prompt("id") { ... } 的类型安全 DSL,块里按顺序 system/user/assistant 即可。

// 示意,非源码:最小用法
val p = prompt("example") {
system("You are a helpful assistant.")
user("法国的首都是哪?")
}

入口函数 prompt(id, params, clock, build)prompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/dsl/PromptDSL.kt:35,内部委托给 Prompt.build。构造器本体 PromptBuilderprompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/dsl/PromptBuilder.kt:36:system(:68)、user(:117/:131)、assistant(:192)、toolCall(:270)、toolResult(:170)、reasoning(:245)逐个往内部可变列表加消息,最后 build()(PromptBuilder.kt:359)冻结成不可变 Prompt

要在旧历史上追加?prompt(existing) { ... }(PromptDSL.kt:74),底层是 PromptBuilder.from(prompt)(PromptBuilder.kt:44)先把老消息拷进来再续写——这正是会话里 appendPrompt 的基础(见 3.6)。

3.3 LLMParams:一次调用的旋钮

要解决的小问题: 温度、最大 token、要不要强制调工具、要不要结构化输出——这些"怎么生成"的参数得跟"说了什么"分开存。

LLMParams 就是这组旋钮,定义在 prompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/params/LLMParams.kt:30

字段作用
temperature随机性;构造时校验必须在 0.0..2.0(LLMParams.kt:41)
maxTokens输出上限
numberOfChoices要几个候选回复
speculation推测式生成(如 OpenAI PredictedOutput)
schema结构化输出的 JSON schema(见 3.7)
toolChoice工具调用行为:Auto / None / Required / Named(name)
user追踪用的用户标识
additionalProperties逃生口:塞任意自定义参数

两个设计点值得记:

  • default(other) 合并(LLMParams.kt:68):当前实例里为 null 的字段用 other 的值补上——用于"局部覆盖 + 全局默认"。
  • ToolChoice 是密封类(LLMParams.kt:270):Auto/None/Required/Named 四态,会话里的 setToolChoiceRequired() 等便捷方法就是改这个(见 3.6)。

3.4 PromptExecutor:统一入口 + 可组合的调度

要解决的小问题: "把 prompt 送出去"这件事,要能在"单模型直连""多厂商共存""同厂商多 key 轮询"之间自由组合,还不改调用方代码。

思路: 定义一个统一抽象接口,所有变体都实现它;调用方永远只依赖接口。

接口 PromptExecutorAPIprompt/prompt-executor/prompt-executor-model/src/commonMain/kotlin/ai/koog/prompt/executor/model/PromptExecutorAPI.kt:18,核心方法:

方法干什么
execute(prompt, model, tools)发一次请求,拿一条 Message.Assistant(:29)
executeStreaming(...)流式,返回 Flow<StreamFrame>(:44)
executeMultipleChoices(...)多候选(默认降级为单条,:60)
moderate(prompt, model)内容审核,返回 ModerationResult(:80)
resolveModel(model, op)把"想要的模型"解析成"实际用的模型"(:94)
models()列出所有可用模型(:165)

PromptExecutor 本身是个 expect abstract class(多平台声明,prompt-executor-model/.../PromptExecutor.kt:14),实现它的三个组合器就是这一层的精华:

PromptExecutor (抽象)
├─ MultiLLMPromptExecutor provider → 唯一 client 的 map;找不到走 fallback
├─ RoutingLLMPromptExecutor 委托给 LLMClientRouter 选 client(支持负载分发)
└─ (RoundRobinRouter 是路由策略,不是 executor)

resolveModel 是"可切换"的关键钩子。 它接受一个 LLModel、返回一个 ResolvedModel,实现可以中途把请求的模型换成另一个(比如 fallback)。默认实现原样返回(PromptExecutorAPI.kt:94)。

MultiLLMPromptExecutor:一 provider 一 client

构造时给一张 Map<LLMProvider, LLMClient>,外加可选 fallback(prompt-executor-model/.../llms/MultiLLMPromptExecutor.kt:41)。它的 resolveModel 逻辑很直白:

// 示意,非源码:MultiLLM 的模型解析
resolveModel = when {
model.provider in llmClients -> model // 有对口 client,直接用
fallback != null -> fallback.model // 没有,降级到 fallback 模型
else -> throw ModelResolutionException(...) // 都没有,报错
}

真实实现在 MultiLLMPromptExecutor.kt:148(resolveModel);真正发请求时 clientFor(effectiveModel)model.provider 取出 client 并委托(MultiLLMPromptExecutor.kt:191 + :292)。这就是"换模型即换 client"落地的那一行:只要新模型的 provider 在 map 里,同一份 prompt 直接交给新 client 重新编码。

RoutingLLMPromptExecutor + RoundRobinRouter:同 provider 多 client 轮询

当一个 provider 想挂多个 client(多个 API key、多个区域端点做负载分发)时,用 RoutingLLMPromptExecutor(prompt-executor-model/.../llms/RoutingLLMPromptExecutor.kt:40)。它把"选哪个 client"这件事外包给 LLMClientRouter 接口(LLMClientRouter.kt:13,只有 clientsclientFor(model) 两个成员)。

默认路由策略是 RoundRobinRouter(prompt-executor-model/.../llms/RoundRobinRouter.kt:19):同一 provider 下多个 client 时,用一个原子计数器取模轮流发。

// 真实逻辑,RoundRobinRouter.kt:68 ClientPool.Multiple.next()
override fun next(): LLMClient =
clients[counter.fetchAndIncrement().mod(clients.size)] // 原子自增取模,线程安全

单 client 时会走 ClientPool.Single 省掉计数(RoundRobinRouter.kt:61)。

3.5 LLMClient:把中立 Prompt 翻译成各家 API

要解决的小问题: OpenAI、Anthropic、Google 的 HTTP 请求体格式各不相同。统一的 Prompt 必须被翻译成每家能懂的样子。

思路: 每个 provider 一个模块、一个 LLMClient 实现,职责单一:编码请求 + HTTP + 解码响应

接口 LLMClientAPIprompt-executor-clients/src/commonMain/.../clients/LLMClientAPI.kt:19,和 executor 的方法几乎一一对应(execute/executeStreaming/executeMultipleChoices/moderate/models),外加 llmProvider()(:104)让上层知道"我是哪家"。LLMClient 本体也是 expect abstract class,同时实现 LLMClientAPI 和 embedding 接口(LLMClient.kt:11)。

provider 各自成模块,互不依赖:

模块目录(prompt-executor-clients/ 下)厂商
prompt-executor-openai-clientOpenAI
prompt-executor-anthropic-clientAnthropic
prompt-executor-google-clientGoogle Gemini
prompt-executor-bedrock-clientAWS Bedrock
prompt-executor-ollama-clientOllama(本地)
prompt-executor-deepseek-clientDeepSeek
prompt-executor-openrouter-clientOpenRouter
prompt-executor-dashscope-client阿里 DashScope

以 OpenAI 为样本看 client 职责。 OpenAILLMClient(prompt-executor-openai-client/.../openai/OpenAILLMClient.kt:107)持有 OpenAIClientSettings(baseUrl、v1/chat/completions 等路径,:88),继承自 AbstractOpenAILLMClient。真正的"编码 → 发 → 解码"三步在抽象基类里:

// 真实逻辑,AbstractOpenAILLMClient.kt:182 execute()
override suspend fun execute(prompt, model, tools): Message.Assistant {
val response = getResponse(prompt, model, tools) // 内部 serializeProviderChatRequest 编码 + HTTP
return processProviderChatResponse(response).first() // 解码回中立 Message.Assistant
}

编码那步 serializeProviderChatRequest(在 streaming 分支可见全貌,AbstractOpenAILLMClient.kt:196)把中立结构一一映射到 OpenAI 字段:messagestools.map { it.toOpenAIChatTool() }prompt.params.toolChoice?.toOpenAIToolChoice()params换句话说:同一份 Prompt,换成 Anthropic client 就走另一套 toXxx 映射——历史不用动,这就是"无缝适配"的机械原理。

模型目录 OpenAIModels(prompt-executor-openai-client/.../openai/OpenAIModels.kt:54)是一堆预置的 LLModel 常量(Chat.GPT5Moderation.Omni 等)。LLModel 本身很小(prompt-llm/.../llm/LLModel.kt:16):provider + id + capabilities + 上下文长度。supports(capability) 用来在发请求前校验能力(LLModel.kt:29),比如流式前 model.requireCapability(LLMCapability.Completion)(AbstractOpenAILLMClient.kt:193)。

3.6 会话入口:节点如何读写这段历史

要解决的小问题: 上面全是"无状态的一次调用"。但 agent 运行时,prompt/model/tools会随对话演进的可变状态,还可能被并发访问。谁来守着它?

思路: AIAgentLLMContext 持有这份可变状态,并只暴露两种带锁的访问方式——readSession(读锁,可并发)writeSession(写锁,独占)。节点(02 章那些)一律通过它读写。

Context 的公共逻辑在 agents/agents-core/src/commonMain/.../context/AIAgentLLMContextCommon.kt:22。它用一把 RWLock(非可重入)守着可变的 prompt(:107),并提供:

  • writeSession { ... }(AIAgentLLMContextCommon.kt:178):拿写锁,建一个 AIAgentLLMWriteSession,块结束时把会话里改过的 prompt/tools/model 写回 context(:196-198)。
  • readSession { ... }(AIAgentLLMContextCommon.kt:216):拿读锁,建只读会话,改动不回写。

writeSession 是 02 章节点的主力入口。 它的方法都在 agents/agents-core/src/commonMain/.../session/AIAgentLLMWriteSessionCommon.kt:

方法干什么位置
appendPrompt { ... }用 PromptBuilder 往当前 prompt 追加消息:115
requestLLM()发请求,并把回复 append 回 prompt:181
requestLLMWithoutTools()禁用工具后再请求:144
requestLLMOnlyCallingTools()强制只调工具(ToolChoice.Required):154
requestLLMForceOneTool(tool)强制调某个具名工具:163
requestLLMStreaming()流式请求,返回 Flow<StreamFrame>:191
requestLLMStructured(...)结构化输出请求(见 3.7):207
requestLLMMultipleChoices()拿多候选回复:258
requestModeration(...)内容审核:199
changeModel(newModel)中途换模型(下一次请求即生效):129
changeLLMParams(...) / setToolChoice*()改参数/工具选择:136 / :399
replaceHistoryWithTLDR(...)压缩历史(具体策略见 05 章):465

注意一个分工:写会话本身不直接调 executor。它把真正的请求委托给内部的 readSession,再把结果 append 回去。看 requestLLM:

// 真实逻辑,AIAgentLLMWriteSessionCommon.kt:181
public suspend fun requestLLM(): Message.Assistant =
readSession.requestLLM().also { response ->
appendPrompt { message(response) } // 把回复写进历史
}

readSession.requestLLM()(AIAgentLLMReadSessionCommon.kt:101)才真正碰 executor:先 preparePrompt(用 config.missingToolsConversionStrategy.convertPrompt 适配工具消息,:69),再 executor.executeProcessed(...)(:85)。requestLLMWithoutTools/requestLLMForceOneTool 等只是在发之前改一下 LLMParams.toolChoice 再走同一条 execute(AIAgentLLMReadSessionCommon.kt:112:143)。

把"可切换"串起来: 节点在 writeSessionchangeModel(Claude) 只是把 model 字段改了;下一次 requestLLM() 就带着原封不动的历史 + 新 model 去找 executor,executor 的 resolveModel/clientFor 自动换到对口 client 重新编码。整条链路没有一处需要"转换历史格式"。

3.7 结构化输出、流式、审核(一句话定位)

这三样都挂在同一套 executor/session 上,细节各有模块,这里只给坐标:

  • 结构化输出(structured): requestLLMStructured(serializer)(AIAgentLLMWriteSessionCommon.kt:219)让模型按某个 Kotlin 类型的 JSON schema 回答;底层走 executor.executeStructured,schema 生成与校验在 prompt-structure 模块。schema 也可直接塞进 LLMParams.Schema(LLMParams.kt:198,分 JSON.Basic/JSON.Standard)。
  • 流式(streaming): 返回 Flow<StreamFrame>StreamFrame 是密封类型(prompt-model/.../streaming/StreamFrame.kt:13),分文本(TextDelta/TextComplete)、推理(ReasoningDelta/ReasoningComplete)、工具调用(ToolCallDelta/ToolCallComplete)和收尾 End(带 finishReason 与 token 元信息)。
  • 审核(moderation): requestModeration(model)executor.moderateModerationResult。类别体系(ModerationCategory:Harassment/Hate/...)在 prompt-model/.../dsl/ModerationAPI.kt:10

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

  • 不可变值 + 拷贝语义治并发。 Prompt/Message 全是 data class,任何"改"都产生新对象(Prompt.kt:129 起)。会话只需在写回那一刻用写锁保护字段替换,而不必锁住整个对话结构——数据本身天然线程安全。

  • 两级 expect abstract class 把"接口"和"平台实现"解耦。 PromptExecutor(PromptExecutor.kt:14)、LLMClient(LLMClient.kt:11)、AIAgentLLMContext(context/AIAgentLLMContext.kt:57)都用这招:公共行为写在 ...Common 基类,平台差异留给 actual。文档/调用方只认接口。

  • resolveModel 作为"可插拔的模型替换点"。 把"想要哪个模型"和"实际用哪个"分成两步(PromptExecutorAPI.kt:94 + MultiLLMPromptExecutor.kt:148),fallback、灰度、按操作类型选模型全都塞进这一个钩子,不污染 execute 主流程。

  • 写会话委托读会话。 请求逻辑只写一遍在 read session,write session 只加"append 回历史"这层薄壳(AIAgentLLMWriteSessionCommon.kt:181)——读写行为天然一致,不会漂移。

  • round-robin 用原子取模,零锁轮询。 counter.fetchAndIncrement().mod(size)(RoundRobinRouter.kt:68)在多 key 负载分发下线程安全又便宜。


5. 边界与局限(诚实说明)

  • RWLock 不可重入。writeSession/readSession/withPrompt 内部再调其中任意一个(同一 context)会死锁——源码注释反复警告(AIAgentLLMContextCommon.kt:117-119:169-171)。

  • 能力不匹配直接抛。 模型不支持某能力时 client 直接 requireCapability 抛异常(如流式,AbstractOpenAILLMClient.kt:193);executeMultipleChoices/executeStreaming 在部分 client 上是 Not implemented(LLMClientAPI.kt:61:77)。

  • 直接读 prompt 不加锁。 在会话外直接读 context.prompt/model/tools 拿到的是"当前恰好装着的快照",不保证与并发写一致;要一致视图得在 readSession 里读(AIAgentLLMContextCommon.kt:99-105)。

  • DetachedPromptExecutorAPI 是逃生口而非常规路径。 直接从 context 取 promptExecutor 绕开 agent 逻辑(不走 ToolsConversionStrategy、不改 agent 状态),需显式 opt-in(AIAgentLLMContext.kt:29)。

  • 本章不覆盖: 工具的 schema 生成与注册(见 04 章)、历史压缩策略的具体实现(replaceHistoryWithTLDR 背后的 HistoryCompressionStrategy,见 05 章)。


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

主题文件路径关键符号
Prompt 数据结构prompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/Prompt.ktPromptwithMessageswithUpdatedParamslatestTokenUsage
Message / MessagePartprompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/message/Message.ktMessageMessage.System/User/AssistantMessagePart.Tool.Call/ResultResponseMetaInfo
PromptBuilder DSLprompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/dsl/PromptBuilder.ktPromptBuildersystemuserassistantbuildfrom
prompt() 入口prompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/dsl/PromptDSL.ktpromptemptyPrompt
调用参数prompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/params/LLMParams.ktLLMParamsdefaultSchemaToolChoice
Executor 抽象接口prompt/prompt-executor/prompt-executor-model/src/commonMain/kotlin/ai/koog/prompt/executor/model/PromptExecutorAPI.ktPromptExecutorAPIexecuteresolveModel
多 LLM executorprompt/prompt-executor/prompt-executor-model/src/commonMain/kotlin/ai/koog/prompt/executor/llms/MultiLLMPromptExecutor.ktMultiLLMPromptExecutorresolveModelclientForFallbackPromptExecutorSettings
路由 executorprompt/prompt-executor/prompt-executor-model/src/commonMain/kotlin/ai/koog/prompt/executor/llms/RoutingLLMPromptExecutor.ktRoutingLLMPromptExecutor
轮询路由prompt/prompt-executor/prompt-executor-model/src/commonMain/kotlin/ai/koog/prompt/executor/llms/RoundRobinRouter.ktRoundRobinRouterClientPool
路由接口prompt/prompt-executor/prompt-executor-model/src/commonMain/kotlin/ai/koog/prompt/executor/llms/LLMClientRouter.ktLLMClientRouterclientFor
Client 接口prompt/prompt-executor/prompt-executor-clients/src/commonMain/kotlin/ai/koog/prompt/executor/clients/LLMClientAPI.ktLLMClientAPIllmProvider
OpenAI client(样本)prompt/prompt-executor/prompt-executor-clients/prompt-executor-openai-client/src/commonMain/kotlin/ai/koog/prompt/executor/clients/openai/OpenAILLMClient.ktOpenAILLMClientOpenAIClientSettings
OpenAI 编解码prompt/prompt-executor/prompt-executor-clients/prompt-executor-openai-client-base/src/commonMain/kotlin/ai/koog/prompt/executor/clients/openai/base/AbstractOpenAILLMClient.ktexecuteserializeProviderChatRequestprocessProviderChatResponse
OpenAI 模型目录prompt/prompt-executor/prompt-executor-clients/prompt-executor-openai-client/src/commonMain/kotlin/ai/koog/prompt/executor/clients/openai/OpenAIModels.ktOpenAIModels
LLModelprompt/prompt-llm/src/commonMain/kotlin/ai/koog/prompt/llm/LLModel.ktLLModelsupports
会话 contextagents/agents-core/src/commonMain/kotlin/ai/koog/agents/core/agent/context/AIAgentLLMContextCommon.ktAIAgentLLMContextCommonwriteSessionreadSessionwithPrompt
写会话agents/agents-core/src/commonMain/kotlin/ai/koog/agents/core/agent/session/AIAgentLLMWriteSessionCommon.ktappendPromptrequestLLMchangeModelreplaceHistoryWithTLDR
读会话agents/agents-core/src/commonMain/kotlin/ai/koog/agents/core/agent/session/AIAgentLLMReadSessionCommon.ktrequestLLMpreparePromptrequestLLMStructured
流式帧prompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/streaming/StreamFrame.ktStreamFrameTextDeltaToolCallCompleteEnd
审核prompt/prompt-model/src/commonMain/kotlin/ai/koog/prompt/dsl/ModerationAPI.ktModerationCategoryModerationResult

同组其它章: index · 01 图执行引擎 · 02 构建层 · 04 工具系统 · 05 Feature 与运行时扩展