ASTER CODE / 内核原理
上下文选择与压缩保留
来源替换、条目去重、排序、必需项、预算与压缩摘要的权责。
模型输入是一份受预算约束的投影
事实日志保存执行依据。 上下文装配器选择本轮送给模型的材料。 材料可能来自文件、技能目录、检索和工作区信息。 装配器裁剪材料时,原事实日志仍然保留。
- 01来源先提交候选
文件、技能目录和检索提供条目。
- 02同一来源只保留最新贡献
后一次贡献替换前一次贡献,再处理条目去重。
assembler.rsL153 ↗ - 03预算约束最终输入
装配器依据排序、必需项和来源配额选择内容。
assembler.rsL153 ↗
例如,修复跨月错误时,候选材料包含日期函数和测试。 模型窗口有限,宿主需要选择当前有关的条目。 “没有送进本轮输入”不代表“材料不存在”。
装配器按什么顺序选择材料
| 阶段 | 实际行为 | 设计原因 |
|---|---|---|
| 来源替换 | 每个来源只保留最后一次贡献 | 避免同源旧结果反复叠加 |
| 条目去重 | 相同身份的条目合并 | 避免重复内容消耗预算 |
| 排序 | 按实现中的排序规则排列 | 让条目选择保持确定 |
| 必需项 | 先按必需项分支保留 | 必须材料优先于普通配额 |
| 总量限制 | 检查条目数和估计令牌数 | 限制普通候选的输入成本 |
| 来源配额 | 检查各来源的条目和令牌 | 避免单一来源占满上下文 |
默认 ContextBudget 是 12 项和 4000 个估计令牌。
这只是默认装配预算,不是模型上下文窗口。
宿主选择其他策略时,具体额度随策略变化。
入口:默认预算 L13 ↗。
必需项是一个明确的例外
装配器先处理 is_required() 条目。
必需项计入总量,但跳过普通配额检查。
因此,必需项可能使总量超过默认上限。
不要把 max_tokens 当成所有输入的绝对硬上限。
普通项超出令牌预算时,装配器跳过该项。
条目数达到上限时,装配器结束选择。
返回值用 truncated 标记材料发生过裁剪。
源码漫游
上下文装配源码漫游
观察同一来源的旧内容怎样被替换,以及预算怎样限制模型输入。
pub fn assemble(&self, results: &[ContextResult]) -> ContextAssembly {let latest_per_source = retain_latest_result_per_source(results);let mut deduped: HashMap<String, ContextItem> = HashMap::new();let mut source_count = 0usize;for result in latest_per_source {for item in &result.items {source_count = source_count.saturating_add(1);let key = dedupe_key(item);match deduped.get(&key) {Some(existing)if existing.is_required().cmp(&item.is_required()).then_with(|| ranking_score(existing).total_cmp(&ranking_score(item))).then_with(|| existing.relevance.total_cmp(&item.relevance)).is_ge() => {}_ => {deduped.insert(key, item.clone());}}}}let mut items = deduped.into_values().collect::<Vec<_>>();items.sort_by(compare_ranked_items);let mut selected = Vec::new();
let mut items = deduped.into_values().collect::<Vec<_>>();items.sort_by(compare_ranked_items);let mut selected = Vec::new();let mut total_tokens = 0usize;let mut truncated = source_count > items.len();let mut source_item_counts: HashMap<String, usize> = HashMap::new();let mut source_token_counts: HashMap<String, usize> = HashMap::new();for item in items {if item.is_required() {total_tokens = total_tokens.saturating_add(estimated_tokens(&item));selected.push(item);continue;}if selected.len() >= self.budget.max_items {truncated = true;break;}let item_tokens = estimated_tokens(&item);if total_tokens.saturating_add(item_tokens) > self.budget.max_tokens {truncated = true;continue;}let source_key = source_policy_key(&item);if let Some(max_items) = self.source_policy.max_items_per_source {let count = source_item_counts.get(&source_key).copied().unwrap_or(0);if count >= max_items {truncated = true;
## Current State<what is done> / <what remains>---Pinned original goal (copy verbatim under ## Goal; leave empty only if unknown):{goal}Conversation:{conversation}
源码与本文版本一致,保留原始行号。
同一来源再次贡献时会发生什么
假设文件来源先返回旧版日期函数。 随后,该来源返回修改后的日期函数。 装配器保留第二份来源结果。 装配器再对保留下来的条目去重和排序。 旧内容不会仅因追加次数增加而占据更多输入。
这项规则按来源生效。 两个不同来源返回相近内容时,还要经过条目去重。 来源名与条目身份都应由宿主稳定提供。
压缩摘要为什么不能替代事实
压缩模板要求保留原始目标、改动、命令和当前状态。 模板还要求保留最新纠正与下一步动作。 摘要帮助模型接续工作,但摘要仍可能遗漏事实。 需要执行不可逆动作时,运行应核对不确定的陈述。
模板要求摘要少于 600 个词。
这是模型指导,不保证模型保留全部历史细节。
原始事实与目标定义为后续核对提供依据。
源码:压缩长度要求 L6 ↗。
模板明确区分用户要求与模型推断。
一次答复的格式要求也有原始作用范围。
摘要不能把历史推断升级为永久授权。
源码:摘要来源约束 L27 ↗。
长期目标与压缩如何协作
目标定义保存在原事实流中。 模型摘要保留目标文本以便继续推理。 目标准入仍读取当前定义与版本。 摘要写着“全部完成”也不能代替步骤接受记录。
因此,排查长任务遗漏时要核对两条路径:
- 模型输入是否包含当前目标和当前步骤。
- Core 目标定义是否仍指向正确版本。
怎样排查材料没有进入输入
- 核对来源返回的
ContextResult。 - 检查同源后一次贡献。
- 核对条目的去重身份。
- 查看必需标记与排序信息。
- 对照总量预算和来源配额。
- 检查
truncated与最终条目。
返回材料为空时,先核对来源。 候选存在却未选中时,再检查装配策略。 这两类故障需要不同的修复。
相关机制见执行循环。