# 上下文组装：把资料放到模型真正看得见的位置

**用途：**业务已经取到了角色设定、世界书、历史摘要或工具结果，需要决定它们放在模型输入的哪里、以哪个消息角色出现。

**公共入口：**SDK 的 `placeContextBlocks`、`getPlacementCapabilities`。这是纯消息组装能力，输入消息与资料块，返回新消息数组及放置记录；不调用 LLM，不检索数据库，不保存任何长期资料。

## 一次具体调用

原输入里有角色设定和用户问题。你选择把“进入禁区需要手令”放在角色设定后面：

```ts
import { placeContextBlocks } from '@flowgpt/roleplay-harness';

const result = placeContextBlocks({
  messages: [
    { role: 'system', content: '你扮演城门守卫。' },
    { role: 'user', content: '我递出铜钥匙。' },
  ],
  layout: { character: { start: 0, end: 1 } },
  blocks: [{
    id: 'gate-rule', position: 'after_character', order: 0,
    role: 'system', content: '进入禁区需要手令；铜钥匙不能替代手令。',
  }],
  createMessage: message => message,
});
// result.messages：角色设定 → 手令规则 → 本轮用户输入
// 将这个新数组交给模型，规则才会参与本轮生成。
```

这个例子只展示组装；资料是否可信、是否被授权、是否适合本轮，应由调用前的业务选择完成。组装后也应计数并执行模型预算检查。

[位置、资料块与布局定义](https://github.com/FlowGPT/roleplay-harness/blob/fa41d220d48327e58d994f936517b23f2f15f658/packages/sdk/src/context-placement.ts#L1) · [组装函数](https://github.com/FlowGPT/roleplay-harness/blob/fa41d220d48327e58d994f936517b23f2f15f658/packages/sdk/src/context-placement.ts#L50) · [完整组装示例](https://github.com/FlowGPT/roleplay-harness/blob/fa41d220d48327e58d994f936517b23f2f15f658/examples/sdk/09-context-placement.ts#L1)

## 位置可以改，但需要对应锚点

| 位置 | 放在哪里 | 宿主必须提供什么 |
| --- | --- | --- |
| `before_character` / `after_character` | 角色描述前/后 | `layout.character` 的半开区间 |
| `before_examples` / `after_examples` | 示例对话前/后 | `layout.examples` |
| `at_depth` | 真实聊天历史的指定深度 | `layout.conversation: {messageIndices, emptyIndex}`；前者是原消息数组中真实历史的有序索引，后者是无历史时的插入点。不要把样例、作者备注或 lore 算成聊天历史 |
| `author_note_top` / `author_note_bottom` | 作者备注顶部/底部 | `layout.authorNote` 是原消息数组索引；继承该消息的 role |
| `outlet` | 模板中显式的 `{{outlet::name}}` 插槽 | `layout.outlets: {name: 消息索引}`，block 提供 `outlet_name:name`；每个对应标记恰好一次。内容嵌进该消息，继承该消息的 role |

`depth:0` 在最后一条真实对话之后，`depth:1` 在最后一条真实对话之前；深度超过历史条数时放在最早一条真实对话前。`order` 控制同类放置的顺序，同值再按 ID 排序；相邻区域共享插入点时先遵守区域顺序，不能用一个更小的 order 任意跨越角色/示例区域。没有对应锚点、重复 ID、非法角色或嵌套 outlet 时会报错，不悄悄丢掉已经选择的条目。

`getPlacementCapabilities(layout)` 只按你声明的布局列出可用位置与 outlet 名称；完整的边界、索引和模板标记校验在 `placeContextBlocks` 执行。调用方可以用 `renderBlock` 格式化内容，或用 `renderMessages` 将独立资料块转换成多条消息；作者备注和 outlet 是嵌入宿主消息，不按独立消息的格式转换。

公共组装器支持八种位置，不等于每条业务接线开放了全部位置。世界书服务的选择结果、Harness 当前布局和可用位置应配套，详见 [世界书](atoms-worldbook.md)。

## 分析工具的上下文是另一个入口

六类分析工具可以传 `compileContext`，它返回整条 user message 文本；系统岗位说明仍由 `prompt` 提供。先检索资料再编译，不能在同步编译器里期待自动查库。普通 V2 Actor、后台 Orchestrator、专家和 Director 各自组装输入；给 Actor 注入过的世界书不会自动传播到其他模型。

[自定义编译器的职责](https://github.com/FlowGPT/roleplay-harness/blob/fa41d220d48327e58d994f936517b23f2f15f658/packages/tools/src/contracts.ts#L87) · [系统提示和 user 材料的组合](https://github.com/FlowGPT/roleplay-harness/blob/fa41d220d48327e58d994f936517b23f2f15f658/packages/tools/src/modules/memory/advisory.ts#L244)

## 窗口在哪里改，超了怎么办

模型完整窗口由实际模型配置与适配层负责，资料字符截断、世界书 token 预算、Actor 输出预留属于不同限制。`placeContextBlocks` 不替你扩容或自动压缩；组装完成后由宿主计数。普通应用可以使用 [Compact](atoms-compact.md) 和相应 `contextGovernance`；自己构建 Agent 时也要明确执行预算策略。

验收时直接检查最终 `messages` 与 `placements`：规则在哪条消息、role 是什么、是否出现一次、给哪个模型；再测缺少锚点和预算超限。不能只检查“世界书接口返回了条目”。
