上下文
上下文是一次模型请求实际收到的全部输入。它不是 Session 的同义词,也不是模型 内部自动保留的记忆。
语言模型每次调用都是独立请求。为了让模型继续当前任务,Foya 必须在每个 Step 开始前重新选择并发送必要信息。
上下文包含什么
标题链接一次请求的上下文主要由以下部分组成:
| 内容 | 来源 | 作用 |
|---|---|---|
| 系统指令 | Foya 内核 | 定义 Agent 的基本行为和安全边界 |
| 项目说明 | AGENTS.md 等文件 |
提供仓库约定和工作方式 |
| Skills Catalog | 已启用 Skill 元数据 | 告知模型可以按需加载哪些流程 |
| Rules | 全局和项目规则 | 约束 Agent 应该怎样工作 |
| Memory | 全局和项目记忆 | 提供跨会话事实与偏好 |
| 环境信息 | 当前 Session | 提供工作目录、平台、时间和审批模式 |
| 历史投影 | Event Store | 提供当前会话已经发生的内容 |
| Tool Schema | Tool Registry | 描述本次允许调用的工具 |
| Provider State | 可选 Provider 能力 | 携带 Provider 原生的连续上下文 |
二进制图片只在请求物化阶段从 Artifact Store 读取,不会直接存入事件日志。
上下文不是什么
标题链接上下文与以下概念不同:
| 概念 | 区别 |
|---|---|
| Session History | 保存会话完整消息;上下文只选择本次请求需要的视图 |
| Memory | 跨 Session 保存的长期事实;上下文是一次请求的临时输入 |
| Rules | 用户定义的行为要求;Rules 只是上下文的一种来源 |
| Prompt | 通常指指令文本;上下文还包含消息、工具定义、图片和原生状态 |
| 模型训练数据 | 模型已有知识;Foya 无法通过上下文永久修改模型 |
关闭窗口、切换界面或等待一段时间不会让模型自动记住更多内容。只有被 Foya 重新放入下一次请求的信息,模型才能直接使用。
两种历史
标题链接Foya 同时维护完整历史和模型历史。
Canonical History
标题链接Canonical History 来自不可变事件日志,保存完成的:
- User 消息;
- Assistant 消息;
- Tool Call;
- Tool Result;
- 从其他 Session 导入的消息。
它是历史事实来源。上下文管理不会为了节省 Token 删除这些消息。
Model Projection
标题链接Model Projection 是为当前模型请求生成的历史视图。它可能是:
Checkpoint+ 当前用户原始请求+ 最近的 Assistant 与 Tool 消息也可能在没有有效 Checkpoint 时直接使用全部有效消息。
Projection 可以重新生成。Checkpoint 无效、Route 不匹配或投影损坏时,Foya 会 回退到 Canonical History。
flowchart LR
EVENTS["Canonical History"] --> PROJECTION["Model Projection"]
CHECKPOINT["Checkpoint"] --> PROJECTION
RULES["Rules / Memory / Skills"] --> COMPILER["Context Compiler"]
PROJECTION --> COMPILER
TOOLS["Tool Schema"] --> COMPILER
COMPILER --> REQUEST["Provider Request"]
系统提示词的组装顺序
标题链接Foya 按稳定顺序组装系统提示词:
- 内核静态前缀;
- 用户全局目录和 Project 中的说明文件;
- Skill Catalog;
- 当前直接生效的 Rules;
- 可由模型按需加载的 Rule Index;
- 审批模式说明;
- 当前有效的 Memory;
- 工作目录、平台、Shell 和时间。
稳定前缀放在最前面有利于 Provider 使用 Prompt Cache。用户可控内容会标记来源和 权威边界,不能覆盖系统安全、审批或沙箱策略。
项目说明支持 AGENTS.md、CLAUDE.md 和 GEMINI.md。Foya 会清理控制字符、
按内容去重并限制单文件和总长度。
每个 Step 都重新编译
标题链接Context Compiler 在每次模型调用前运行,而不是只在 Turn 开始时运行。这样可以 立即包含:
- 刚完成的 Tool Result;
- 当前 Step 激活的 Deferred Tool;
- 根据活动内容匹配的新 Rule;
- 新生成的 Compaction Checkpoint;
- Provider 上一轮返回的真实 Usage。
编译器返回一个不可变请求视图,包括消息、工具、可选 Provider State、当前事件 边界和预算判断。
内容分层
标题链接为了识别压力来源,编译器把请求分成五层:
| 层次 | 典型内容 | 默认处理 |
|---|---|---|
| Pinned | 系统指令和持久上下文 | 优先保留 |
| History | 当前用户请求之前的历史 | 可以压缩 |
| Working Set | 最新 User 消息及其后的步骤 | 优先保持原文 |
| Tool Schema | 当前可调用工具的定义 | Deferred Tool 按需加载 |
| Native State | Provider 原生上下文状态 | 仅在匹配 Route 使用 |
分层不会直接删除内容,它用于诊断当前压力主要来自历史、当前工作集还是固定开销。
Token 预算
标题链接上下文窗口必须同时容纳输入和模型输出。Foya 因此不会把整个窗口都分配给历史。
已知模型窗口时:
- 首次请求默认保留窗口的四分之一作为输出空间;
- 初始保留最多为 16K Token;
- 获得真实 Usage 后,按上一轮输出的两倍动态保留;
- 动态保留限制在 1K 到 8K Token;
- 如果模型配置了更小的最大输入 Token,则使用更严格的上限。
不知道模型窗口时,Foya 使用保守预算:
历史高水位:32K Token输出保留:约 16K Token这些值是本地调度参数,不代表 Provider 一定具有相同限制。
如何估算下一次请求
标题链接不同模型对中文、代码和 JSON 的 Token 切分不同,本地字符估算不可能完全准确。
Foya 优先使用 Provider 上一次返回的真实输入 Token,然后计算请求 Payload 相比 上一轮增加或减少的大小,把差值应用到真实基线上。
只有缺少真实 Usage 时,才对完整 Payload 使用保守字符权重估算。估算范围包括:
- 所有模型可见消息;
- Tool Schema;
- Provider 原生状态;
- 附件的估算开销。
本地估算只负责决定是否提前整理上下文。即使估算仍高于预算,Foya 也会让 Provider 作最终容量判断,而不会仅凭估算拒绝请求。
Tool Schema
标题链接工具定义本身也占用上下文。
Tool Registry 将工具分为:
direct:每次请求直接可见;deferred:通过tool_search激活后,在下一 Step 可见;hidden:不暴露给模型。
大型工具集合无需在每次请求中发送全部 Schema。Deferred Tool 的激活状态只影响 当前 Turn,不永久修改注册表。
当历史投影包含大型 Tool Result 引用时,Foya 会自动加入
history_read_tool_result,使模型能够检查、分页读取或搜索原始结果。
图片与结构化输入
标题链接历史中只保存 Attachment 引用。向 Provider 发请求时,Foya 才从当前 Session 的 Artifact Store 读取图片字节,并检查:
- 当前模型是否声明支持图片输入;
- Artifact 是否仍然存在;
- 单次请求的图片总量是否超过限制。
不支持或不可用的图片会转换成明确的文本说明,不会伪装成成功传入的视觉内容。
用户选择的浏览器元素以结构化数据保存在消息中,并在 Provider 视图中标记为不可信 页面内容。
Route 隔离
标题链接上下文预算基线和 Provider 原生状态都绑定具体 Route。Route 由 Connection、协议、 端点和 Model 等稳定信息派生。
切换模型或连接后:
- 不复用旧 Route 的真实 Token 基线;
- 不重放旧 Route 的 Provider-native State;
- Portable Text Checkpoint 仍可在通过来源校验后使用。
这可以避免不同模型的容量和协议状态相互污染。
与其他机制的关系
标题链接当前边界
标题链接- Token 数量是近似值,最终限制由 Provider 决定。
- 内置 OpenAI 连接使用 Chat Completions,没有启用原生上下文继续状态。
- 当前 Working Set 以最新 User 消息为主要边界,不进行任意语义删除。
- 项目说明、Rules、Memory 和 Skill Catalog 都有独立长度上限。
- Foya 不把模型推理文本作为普通后续上下文反复发送,取消回合的显式保留除外。