本集对应课程章节:CHAPTER 12(从文件变更到混合排序 / Dream 的真实机制 / AgentDefinition 与 Persona 如何合并)
内容来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》),本页为二次演绎的解读与音频稿件版本。
本集看三块偏系统的设计:记忆检索、Dream 记忆整理机制、子 Agent 配置合并。
共同特征:都是「看起来很简单、实际有一堆降级路径」的设计。生产系统真正复杂的地方不在主流程,而在于出问题时怎么办。
| 维度 | 读完本集你能做到 | 对应证据 |
|---|---|---|
| 检索流水线 | 按真实顺序复述 sync-on-search → FTS → embedding → KNN → 合并加权 → MMR | hybrid_search |
| 降级语义 | 说明 embedding 失败与 MMR 关闭时各自发生什么 | Err(e) => { tracing::warn!(...); None } / MmrConfig::default() |
| Dream 门控 | 说出三道门与默认触发方式 | check_dream_gates / MemoryDreamConfig |
| Dream 可靠性 | 解释 DreamLock、幂等要求与写入失败回滚 | dream_lock.rs / rollback(prior) |
| 配置结构 | 区分 AgentDefinition / SubagentRole / SubagentPersona / EffectiveRuntimeConfig | subagent-resolution/src/types.rs |
| 合并优先级 | 按字段判断四级级联与 definition fallback | resolve_effective_overrides |
| 失败模式 | 区分 Persona 的「失败关闭」与 role prompt 的「软降级」 | persona_error / role_prompt_warning |
查询前先同步脏文件,再结合 FTS5 BM25 与可选的 sqlite-vec KNN。合并分数经过时间衰减、来源权重与访问增益,最后可选择启用 MMR 多样性重排。
| 步骤 | 机制 | 说明 |
|---|---|---|
| 01 · SYNC | 查询前同步 | MemoryFileWatcher 累积变化的 Markdown 路径;backend.search() 开始时重新索引新增/修改文件,并删除已移除文件的旧 chunk |
| 02 · FTS | BM25 候选 | 先执行普通 FTS,再补充 global 与 workspace 来源查询,降低 session 数量过多造成的挤出 |
| 03 · VECTOR | 可选 KNN | 仅在 sqlite-vec 与 provider 可用时嵌入 query |
| 04 · SCORE | 归一化与合并 | BM25 分数与向量 L2 距离分别归一化;双路命中按权重合并,同时保证结果不低于该 chunk 的 FTS 分数 |
| 05 · WEIGHT | 时间与来源 | session 按半衰期指数衰减;global 与 workspace 视为 evergreen(不衰减);再乘 source weight 与适度 access boost |
| 06 · DIVERSITY | 可选 MMR | 开启后按相关性与 snippet 的 Jaccard 差异做贪心重排,最后截断到 max_results |
候选数量细节:candidate_limit = config.max_results * 3——先宽召回再精排。
| 开关 | 真实行为 |
|---|---|
| Embedding 失败 | 向量路径停止,FTS 结果仍进入 hybrid_search_merge;记录 warning 并传 None 继续 → fallback = FTS-only,不是整次搜索失败 |
| MMR 默认状态 | MmrConfig::default() 设置 enabled: false 与 lambda: 0.7;0.7 只在显式开启 MMR 后生效,默认不会重排 |
关键心智模型:向量检索是渐进增强的加分项,不是必需品。页面或调用方无需把 embedding 故障当成整次搜索失败。
Dream 把近期 session 日志与现有 MEMORY.md 合并成长期记忆,由会话结束、可选周期检查或手动命令进入,受门控与最佳努力锁共同约束。
| 门 | 条件 |
|---|---|
| 配置门 | MemoryDreamConfig.enabled 默认 true;子 Agent 会话直接跳过 Dream |
| 时间门 | min_hours 默认 4;锁文件 mtime 记录上次成功 consolidation 的时间 |
| 会话门 | min_sessions 默认 3;统计上次 consolidation 后修改的 session Markdown,排除当前会话 |
默认check_interval_secs = None,代表不启用周期检查。源码明确支持 session end 和/dream;只有配置检查间隔后,session actor 才会按周期检查门控。
不能概括为「所有空闲时必然自动运行」。
容量规划含义:默认行为下 Dream 的调用次数 ≈ 会话结束次数,而非某种定时器频率。
| 机制 | 说明 |
|---|---|
DreamLock 是最佳努力协调 | .dream-lock 保存 PID,并用 mtime 兼作上次成功时间;活进程持有且未过期时返回 Ok(None);死进程或超时锁可被回收 |
| 必须幂等 | 源码注释明确说明它并非严格互斥。写后复读降低竞争概率,仍可能有两个进程都认为自己获胜 → Dream 必须容忍重复 consolidation |
| 成功边界决定清理边界 | 模型返回空 / NO_REPLY / 无 Markdown 标题 → 不写入也不删 session |
| 写入失败回滚 | 写 MEMORY.md 失败时调用 rollback(prior) 恢复旧锁状态 |
| 延迟清理 | 写入成功后才清理已读取的 session;5 分钟内仍活跃的文件会跳过 |
| 索引更新 | 搜索索引只移除实际删掉的路径,再为新 MEMORY.md 重建索引与 embedding |
可靠性四要素:门控减少无效调用 → 最佳努力锁降低并发 → 幂等承担少量重复风险 → 回滚与延迟清理保证失败后仍能再次尝试。
工程选择评注:与其上一把分布式锁增加复杂度与故障面,不如承认竞争存在、把幂等做扎实。
子 Agent 先解析可执行骨架,再把 spawn 参数、role 默认值和 Persona 默认值折叠为运行时配置。两套结构在不同阶段生效,最终共同决定子会话。
| 结构 | 职责 | 关键内容 |
|---|---|---|
AgentDefinition | 可版本化的 Agent 合同 | 从 .grok/agents/*.md 解析:prompt_mode、tool_config、capability_mode、permission_mode、tools、isolation、model、hooks、MCP 继承等。项目定义的发现优先级高于 user 与 bundled |
SubagentRole | 按类型命中的运行时预设 | 由 subagent_type 查找;给出 capability、model、reasoning effort、prompt file、默认 isolation;role prompt 在 spawn 时读取 |
SubagentPersona | 按名称选择的行为层 | inline instructions、instructions file、inputs、outputs、model、reasoning effort、default isolation。inline 文本在文件内容之前合并,再作为 <persona> 块进入 prompt |
EffectiveRuntimeConfig | 已解析结果 | model、reasoning_effort、capability_mode、persona、persona_instructions、role_prompt、role_prompt_warning、role_name、persona_error、isolation |
事实校正:EffectiveRuntimeConfig中没有temperature、max_tokens或tools字段。
| 级别 | 来源 | 覆盖字段 |
|---|---|---|
| 01 | spawn override | 调用 task 时显式给出的 model、reasoning、capability、persona、isolation |
| 02 | role default | model、reasoning、capability、isolation |
| 03 | persona default | model、reasoning、isolation。不提供 capability_mode |
| 04 | parent / none | 未命中的字段保留 None,交由下游继承父级;isolation 最终落到 None 模式 |
Definition fallback(之后仍有):
reasoning_effort 仍为空 → 读取 AgentDefinition.effort;None 且 definition isolation 为 Worktree → 升级为 Worktree;AgentDefinition.model 与父模型继承。| 模式 | 行为 |
|---|---|
| 失败关闭:Persona | 找不到、内容为空或读取文件失败 → 写入 persona_error;文件 I/O 失败提前返回默认化结果;spawn 侧看到 Persona 错误后中止创建 |
| 软降级:role prompt | prompt_file 读取失败只产生 role_prompt_warning;其余 model、reasoning、capability、isolation 仍继续解析 |
一个是中止,一个只是警告——区分二者是理解这套配置系统的关键。
max_results * 3?enabled 与 lambda 默认值,以及 0.7 何时生效?DreamLock 为何是「最佳努力」而非严格互斥?EffectiveRuntimeConfig 中不存在的三个字段?capability_mode 的覆盖?persona_error 与 role_prompt_warning 的后果差异?grok-build-main,核对日期 2026-07-17;代码块保留真实函数与分支,唯一折叠处已用注释说明。check_interval_secs = None。temperature、max_tokens、tools 不在 EffectiveRuntimeConfig 中;Persona 不提供 capability_mode。给流水线加可选增强(向量检索、重排、缓存)时,必须在设计文档里写清:它挂掉之后系统还剩什么。Grok Build 的答案是 FTS-only——基础能力永不消失。
候选取最终结果的数倍(此处为三倍),先保证召回,再逐层精排与加权。一上来就精排会在第一步就丢掉正确答案。
多路分数合并时,保证结果不低于最强单路的原始分数。否则「增强」可能让原本正确的结果排名下降——这类回归很难被发现。
后台整理类任务应叠加多道门(配置门 / 时间门 / 数量门),并把触发条件写得精确。模糊的「空闲时自动运行」会导致成本不可预测。
在单机多进程场景下,用最佳努力锁 + 幂等设计,通常比引入分布式互斥更划算。前提是重复执行必须安全——这正是幂等要解决的问题。
清理动作只在写入成功之后执行;写入失败要回滚锁状态以便重试;对「仍然活跃」的文件保留时间宽限。这三点是后台任务不丢数据的底线。
AgentDefinition 提供 Agent 骨架,role 与 Persona 提供 spawn 阶段的运行时输入。优先级是逐字段级联,准确分析要先确认该字段真实存在于哪一种结构。*来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)*
*本页为二次演绎解读稿,配套音频为配音版。*