> ## Documentation Index
> Fetch the complete documentation index at: https://simplellmfunc.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 编译管道

> invocation 配置、对话记录和运行时补丁如何变成最终的 LLM 请求

# 编译管道

编译管道将 invocation 配置、基础对话记录和待处理的运行时补丁转换为发送给 LLM 提供商的最终消息。这是一个两阶段的过程，对话记录补丁应用与请求渲染之间有着清晰的边界。

## 两个阶段

```
compile_invocation_turn(spec, transcript, pending_mutations, selfref_snapshot)
│
├─► 阶段 1：reduce_turn_context(transcript, mutations, selfref_snapshot)
│       • 将所有待处理的运行时补丁应用到对话记录
│       • 如检测到标记，刷新 selfref 快照
│       • 克隆结果（无共享引用）
│       → 返回：ReducedTurnContext
│
└─► 阶段 2：convert_to_llm_request(reduced, prompt_contract)
        • 解析系统提示词（selfref > 显式设置 > 对话记录 > docstring）
        • 在对话记录中放置/替换系统消息
        • 渲染最终消息（注入工具规格、must_principles）
        → 返回：CompiledTurnContext
```

## 阶段 1：归约轮次上下文

`reduce_turn_context` 接收当前基础对话记录和所有待处理的运行时补丁，输出一份干净的、已应用补丁的对话记录。

```python theme={null}
def reduce_turn_context(
    transcript: NormalizedMessageList,
    pending_mutations: List[ContextMutation],
    selfref_snapshot: Optional[DataFromSelfRef] = None,
) -> ReducedTurnContext:
```

处理过程：

1. **应用运行时补丁** — `apply_mutations(transcript, pending_mutations)` 按顺序处理每个 mutation：
   * `AssistantMessageMutation` → 追加助手消息
   * `ToolResultMutation` → 追加工具结果
   * `ContextReplaceMutation` → 替换整个列表
   * `ContextSummaryMutation` → 替换为摘要，存储经验
   * `ExperienceRemember/Forget` → 累积后在下一个非经验 mutation 之前批量提交
   * 等等

2. **刷新 selfref 快照** — 如果 mutation 应用后的对话记录包含 selfref 标记（经验、摘要），则从对话内容重新解析 `DataFromSelfRef`。这确保快照反映的是刚刚应用的压缩或经验 mutation 的结果。

3. **克隆** — 结果是深拷贝，不存在共享状态的修改。

输出：

```python theme={null}
@dataclass
class ReducedTurnContext:
    transcript: NormalizedMessageList      # Mutation-applied, cloned
    selfref_snapshot: Optional[DataFromSelfRef]  # Refreshed if needed
```

## 阶段 2：转换为 LLM 请求

`convert_to_llm_request` 接收归约后的对话记录和 invocation 的 prompt contract，生成发送给提供商的最终消息。

```python theme={null}
def convert_to_llm_request(
    reduced: ReducedTurnContext,
    prompt_contract: PromptContract,
) -> CompiledTurnContext:
```

处理过程：

1. **解析系统提示词** — 优先级顺序：
   * 如果 `selfref_snapshot` 存在 → 渲染基础提示词 + 经验块
   * 否则如果 `prompt_contract.system_prompt` 已设置 → 直接使用
   * 否则如果对话记录中有系统消息 → 提取其内容
   * 否则如果 `prompt_contract.base_instruction` 存在 → 使用 docstring 兜底

2. **放置系统消息** — 替换对话记录中已有的系统消息，或在开头插入新的系统消息。如果没有解析到系统提示词，则移除已有的系统消息。

3. **渲染 LLM 消息** — `render_llm_input_messages()` 完成消息的最终处理：
   * 如果挂载了工具，在开头注入 `<tool_best_practices>` 块
   * 如果需要，追加 `<must_principles>` 块（指示模型使用原生工具调用）
   * 返回可直接发送给提供商的最终消息列表

输出：

```python theme={null}
@dataclass
class CompiledTurnContext:
    transcript: NormalizedMessageList       # The transcript after system prompt resolution
    system_prompt: Optional[str]           # The resolved system prompt text
    llm_messages: NormalizedMessageList    # Final messages for the provider
    selfref_snapshot: Optional[DataFromSelfRef]  # Carried forward
```

## 在 ReAct 循环中的位置

```python theme={null}
# Simplified ReAct loop structure
while has_more_work:
    # 1. Collect mutations from hooks
    hook_mutations = collect_hook_mutations(state)
    
    # 2. Compile context (Stage 1 only — apply mutations)
    compiled_context = compile_context(state, hook_mutations + pending)
    
    # 3. Compile for LLM (Stage 1 + Stage 2 — full pipeline)
    turn = compile_invocation_turn(spec, compiled_context.messages, [], selfref)
    
    # 4. Send to LLM
    llm_result = execute_single_llm_phase(turn.llm_messages, ...)
    
    # 5. Execute tools if needed
    tool_result = schedule_tool_batch(llm_result.tool_calls, ...)
    
    # 6. Collect all new mutations for next iteration
    pending = llm_result.mutations + tool_result.mutations
```

每次迭代都经过完整的管道。Runtime 副作用不会“直接追加到 live list”，而是产生补丁并在边界应用。这保证了即使经过 50 次工具调用，对话记录状态依然一致且可审计。

## 唯一入口点

所有编译流程都经过同一个函数：

```python theme={null}
def compile_invocation_turn(
    spec: InvocationSpec,
    transcript: NormalizedMessageList,
    pending_mutations: Optional[List[ContextMutation]] = None,
    selfref_snapshot: Optional[DataFromSelfRef] = None,
) -> CompiledTurnContext:
```

`@llm_function` 和 `@llm_chat` 使用相同的入口点。不同的装饰器模式不存在独立的编译路径。

## 为什么分两个阶段？

这种拆分带来以下优势：

* **单独使用阶段 1** 用于内部状态管理（例如 ReAct 循环中的 `compile_context` 更新状态而不渲染给 LLM）
* **阶段 2** 仅在实际调用 LLM 时添加提供商相关的渲染（工具注入、系统提示词放置）
* **测试** — 可以独立测试 mutation 应用和提示词渲染
* **SelfRef 刷新** — 发生在两个阶段之间，确保快照在提示词解析之前是最新的

## 实际意义

对大多数用户来说，这些都是不可见的。你写函数、传 history、挂载工具、消费事件即可。

但当你需要调试内部行为：

* **调试对话记录问题** → 检查产生了哪些运行时补丁、以什么顺序
* **理解系统提示词行为** → 了解阶段 2 中的优先级顺序
* **构建框架扩展** → 使用编译边界，而不是直接修改 live messages

重要区别：mutation 是内部对话记录补丁。它不是 docstring、模板参数、工具 schema 或初始 history 的来源。

<Card title="下一节：SelfRef 自引用" icon="brain" href="/zh/context/selfref">
  持久上下文（经验、压缩、分叉）如何通过 SelfReference 系统工作。
</Card>
