Files
smartmate/backend/newAgent/阶段3_上下文瘦身设计.md
LoveLosita 4195e65cba Version: 0.9.8.dev.260408
后端:
1.execute 上下文瘦身第一版落地(固定 4 消息骨架 + ReAct 窗口压缩 + JSON 输出约束)
  - 新建 prompt/execute_context.go:
    execute 阶段改为 message[0..3] 固定结构;
    加入历史摘要、当轮 ReAct 绑定展示、同工具 observation 压缩(保留最新)与工具简表返回示例提示
  - 更新 prompt/execute.go:
    重写 plan/ReAct 执行提示词;
    补齐“可做/不可做”约束;
    统一严格 JSON 指令;
    补充 tool_call.arguments/abort/speak 非空等格式护栏
  - 更新 model/execute_contract.go:
    新增 ExecuteDecision/ToolCallIntent 自定义 Unmarshal;
    兼容空字符串占位与 tool_call.parameters→arguments 回退解析
  - 更新 node/correction.go:
    为 correction 注入 history kind 标记,避免被当作真实用户输入污染摘要
  - 更新 node/execute.go:
    补齐 continue/ask_user/confirm 的 speak 兜底;
    移除工具结果写入前 3000 字截断

2.工具层微调语义重构(任务视角概览 + 首个空位查询 + 移动权限收紧)
  - 更新 tools/read_tools.go:
    get_overview 改为任务视角全量输出(课程仅占位统计);
    新增 find_first_free(首个命中位 + 当日负载明细);
    find_free 保留兼容别名;
    list_tasks 增加 status/category 校验与空结果纠偏文案
  - 更新 tools/registry.go:
    注册 find_first_free;
    find_free 改兼容别名;
    同步 get_overview/list_tasks/move/batch_move 描述语义
  - 更新 tools/write_tools.go:
    move/batch_move 仅允许 suggested,existing/pending 明确拒绝并返回可读错误
  - 更新 tools/SCHEDULE_TOOLS.md:
    同步 get_overview/find_first_free/list_tasks/move/batch_move 的最新入参与返回示例
  - 更新 prompt/plan.go:
    读工具示例由 find_free 调整为 find_first_free

3.交接文档与阶段说明同步
  - 更新 newAgent/HANDOFF_粗排修复与Prompt重构.md:
    更新为 2026-04-08;
    补充“最新增量交接”章节(当前主矛盾、P0/P1、验证清单)
  - 更新 newAgent/阶段3_上下文瘦身设计.md:
    同步 existing/suggested 的 move/batch_move 约束口径
  - 更新 newAgent/Log.txt:
    追加本轮 execute 调试日志快照

前端:无
仓库:无
2026-04-08 21:35:05 +08:00

12 KiB
Raw Blame History

阶段 3上下文瘦身设计

本文档更新时间2026-04-08

0. 文档目的

这份文档只服务第 3 阶段“上下文瘦身”。

职责边界:

  1. 记录当前已经和用户对齐的“瘦身后 execute 上下文骨架”。
  2. 记录第 3 阶段的落地顺序、非目标和完成标准。
  3. 作为后续上下文被裁剪后的继续施工依据。

明确不负责:

  1. 不展开第 4 阶段 prompt 三层拆分的最终结构。
  2. 不在这里继续讨论粗排和 abort 协议本身。

1. 当前已经确认的收敛结论

以下结论已经对齐,后续不要再回摆:

  1. 第 3 阶段先解决“上下文过胖、重复、噪音多”,不是先做 prompt 三层重构。
  2. 工具参数定义和 JSON 调用示例,应该放在工具块里,不应该散落在别的 message。
  3. 上下文结构应尽量通用化,后端只填“已有事实”,不要引入大量需要主观概括的分类字段。
  4. msg3msg4 必须是一一对应的一组:
    • msg3 是最近一次工具调用记录;
    • msg4 是与 msg3 对应的工具结果;
    • 如果当前没有最近工具调用,则 msg3 / msg4 应一起缺省。
  5. msg4 已经承载“最近一次工具结果”,msg5 不应再重复这部分内容。
  6. msg5 只保留“有唯一来源的运行态事实”,不能放没有明确 owner 的解释性字段。
  7. 当前步骤 可以保留,唯一来源应是 CommonState.CurrentPlanStep()
  8. 当前目标 当前不保留,因为没有稳定 owner容易变成后端替模型总结下一步动作。
  9. 最近观察 当前不保留,因为没有稳定结构化来源;如果需要相关信息,应直接通过 msg4 表达最近一次工具结果。
  10. 已确认语义 这类过宽、难填、边界不清的字段不要进入第 3 阶段方案。

2. 瘦身后的目标上下文骨架

这里记录的是第 3 阶段完成后,execute 阶段理想的 messages 骨架。

注意:

  1. 这是“上下文瘦身后的骨架”,不是第 4 阶段 prompt 三层重构的最终形态。
  2. 重点是减量、去重、压缩,而不是新增更多层次和字段。

2.1 目标 message 列表

message[0] role=system
执行规则:
- 只围绕当前步骤行动
- 只输出严格 JSON
- 不要伪造工具结果
- 读操作使用 action=continue + tool_call
- 写操作使用 action=confirm + tool_call
- 缺少关键信息时用 action=ask_user
- 当前流程应终止时用 action=abort
- next_plan / done 时 goal_check 必填

message[1] role=system
可用工具:
- 工具名
- 工具说明
- 参数定义
- 最小 JSON 调用示例

message[2] role=assistant
历史摘要:
- 用户目标
- 更早但仍有效的事实
- 最近失败摘要
- 已折叠说明(重复查询、过程话术、旧修正链已省略)

message[3] role=assistant
最近一次工具调用记录(与 message[4] 成对)

message[4] role=tool
最近一次工具结果

message[5] role=system
当前执行状态:
- 当前轮次
- 当前步骤
- 当前步骤完成判定

message[6] role=user
请继续当前任务的执行阶段,严格输出 JSON。

2.2 各 message 的职责边界

message[0]:执行规则

只保留 execute 的稳定规则,不在这里重复工具参数,也不在这里放运行态数据。

message[1]:工具块

必须包含:

  1. 工具名
  2. 工具说明
  3. 参数定义
  4. 最小 JSON 调用示例

原因:

  1. 这是 LLM 真实调用工具时最直接依赖的材料。
  2. 如果只给工具名不给参数,模型很容易继续出现缺参调用。
  3. 第 3 阶段里,工具块比“更长的执行 prompt”更重要。

message[2]:历史摘要

只保留“更早但仍有效”的摘要,不保留全量流水账。

允许保留的内容:

  1. 用户原始目标
  2. 当前会话中仍然有效的约束
  3. 最近失败摘要
  4. 历史折叠说明

不应保留的内容:

  1. assistant 的过程话术,例如“我先看一下”“我接下来准备……”
  2. 同工具同参数的多份原始重复结果
  3. 整段 correction 往返原文

message[3] + message[4]:最近一组工具观察

这是 execute 在当前轮之前最关键、最新鲜的一组观察。

约束:

  1. message[3]message[4] 必须成对出现。
  2. message[3] 是调用记录,message[4] 是与之对应的工具结果。
  3. 若当前没有最近工具调用,则这两条一起省略。
  4. 不能凭空生成“最近结果摘要”。

message[5]:当前执行状态

这里只允许放“有唯一来源的运行态事实”。

当前允许的字段:

  1. 当前轮次
  2. 当前步骤
  3. 当前步骤完成判定

当前不允许的字段:

  1. 当前目标
  2. 最近观察
  3. 已确认语义
  4. 任何需要后端主观解释才能生成的字段

message[6]:本轮触发指令

作用很单一:

  1. 明确“现在继续 execute”
  2. 强化“严格输出 JSON”

不要在这里重复整份计划、工具说明或历史摘要。


3. 一个更接近真实落地的示例

下面这个例子只用于校准方向,不要求文案逐字一致。

message[0] role=system
你是 SmartFlow NewAgent 的执行器。

执行规则:
1. 只围绕当前步骤行动,不要跳到别的步骤。
2. 只输出严格 JSON不要输出 markdown不要输出 JSON 之外的解释。
3. 不要伪造工具结果。
4. 读操作用 action=continue + tool_call。
5. 写操作用 action=confirm + tool_call。
6. 缺少关键上下文且无法补齐时,输出 action=ask_user。
7. 当前流程应正式终止时,输出 action=abort。
8. 输出 action=next_plan 或 action=done 时goal_check 必填。

message[1] role=system
可用工具:

1. get_overview
说明:查看当前窗口内整体分布。
参数:
{}
调用示例:
{
  "name": "get_overview",
  "arguments": {}
}

2. find_free
说明:查找满足指定连续时长的空位。
参数:
{
  "duration": "int, 必填",
  "day": "int, 可选"
}
调用示例:
{
  "name": "find_free",
  "arguments": {
    "duration": 2,
    "day": 5
  }
}

3. list_tasks
说明:列出任务,可按类别和状态过滤。
参数:
{
  "category": "string, 可选",
  "status": "string, 可选, 可取 all/existing/suggested/pending"
}
调用示例:
{
  "name": "list_tasks",
  "arguments": {
    "status": "suggested"
  }
}

4. move
说明:移动一个已预排任务(仅 suggested。
参数:
{
  "task_id": "int, 必填",
  "new_day": "int, 必填",
  "new_slot_start": "int, 必填"
}
调用示例:
{
  "name": "move",
  "arguments": {
    "task_id": 128,
    "new_day": 5,
    "new_slot_start": 1
  }
}

5. swap
说明:交换两个已落位任务。
参数:
{
  "task_a": "int, 必填",
  "task_b": "int, 必填"
}
调用示例:
{
  "name": "swap",
  "arguments": {
    "task_a": 128,
    "task_b": 136
  }
}

6. batch_move
说明:批量原子移动多个任务。
参数:
{
  "moves": [
    {
      "task_id": "int, 必填",
      "new_day": "int, 必填",
      "new_slot_start": "int, 必填"
    }
  ]
}
调用示例:
{
  "name": "batch_move",
  "arguments": {
    "moves": [
      {
        "task_id": 128,
        "new_day": 5,
        "new_slot_start": 1
      },
      {
        "task_id": 129,
        "new_day": 5,
        "new_slot_start": 3
      }
    ]
  }
}

7. unplace
说明:取消一个已落位任务。
参数:
{
  "task_id": "int, 必填"
}
调用示例:
{
  "name": "unplace",
  "arguments": {
    "task_id": 128
  }
}

补充约束:
- suggested 可以 move / swap / unplace
- existing 不能 move / batch_move仅作已安排事实层
- pending 不能 move / swap / unplace
- 如果当前任务已经是 suggested不要再把它当 pending 去 place

message[2] role=assistant
历史摘要:
- 用户目标:把任务类 [101,102] 调整到本周,尽量分布更均匀,周五不要太满。
- 当前已知事实:
  1. 当前阶段是 execute
  2. task_class_ids=[101,102]
  3. 当前状态统计existing=9, suggested=18, pending=0
- 最近一次失败摘要find_free 缺少 duration 参数
- 更早的重复查询、过程话术、旧修正链已折叠

message[3] role=assistant
tool_call:
{
  "name": "get_overview",
  "arguments": {}
}

message[4] role=tool
规划窗口概览:
- existing=9
- suggested=18
- pending=0
- 周三第5-8节 suggested 偏密
- 周五第1-2节有空位
- 周五第3-4节有空位

message[5] role=system
当前执行状态:
- 当前轮次2/8
- 当前步骤:先识别最值得调整的 suggested 任务和候选空位
- 当前步骤完成判定:能明确指出哪些任务值得调整,以及候选目标时段

message[6] role=user
请继续当前任务的执行阶段,严格输出 JSON。

4. 第 3 阶段的具体落地计划

4.1 第一件事:先抓真实输入样本

目标:

  1. 先拿到 BuildExecuteMessages() 真正送给模型的样本。
  2. 不先拍脑袋改结构,先确认“胖点”究竟在 history、pinned还是 runtime prompt。

当前可复用入口:

  1. backend/newAgent/prompt/execute.go
  2. backend/newAgent/prompt/base.go
  3. backend/newAgent/node/execute.go 中已有 execute 上下文调试日志

产出:

  1. 至少 2 到 3 份真实样本
  2. 标出每个 message 的长度、重复点和噪音来源

4.2 第二件事:补 execute 专用的历史压缩层

目标:

  1. 不再把 ConversationContext.History 原样全量喂给 execute。
  2. 形成“更早历史摘要 + 最近一组工具观察”的结构。

必须做的事:

  1. 同工具同参数的重复查询,不保留多份原始结果。
  2. 更早结果改成摘要,只保留最近一条原始结果。
  3. assistant 过程话术不再进入后续模型历史。
  4. correction / 工具失败链改成“最近失败摘要”,不要保留整段往返原文。
  5. 保留合法的 assistant tool_call + tool result 成对消息,不能破坏 OpenAI 兼容格式。

4.3 第三件事:压缩 pinned / runtime 的重复信息

目标:

  1. 避免 state summary + pinned + runtime user prompt 三处重复抄同一份信息。
  2. 保留最新、必要、唯一来源的信息。

原则:

  1. 当前计划/当前步骤只保留最新版本,不做历史累积。
  2. msg5 只保留“当前轮次 + 当前步骤 + 当前步骤完成判定”。
  3. 工具结果只出现在 msg4,不在 msg5 再复述。
  4. 粗排语义只保留一处,不要在多条 message 重复提醒。

4.4 第四件事:最后再接 token budget

目标:

  1. 在完成摘要化和去重后,再做按预算裁剪。
  2. 避免一上来直接砍历史,把真正有价值的信息也一起砍掉。

可复用思路:

  1. 参考旧链路 agent.go 的历史预算计算和裁剪流程。
  2. 参考 backend/pkg/token_budget.go 中的预算估算与窗口裁剪函数。

要求:

  1. 不一定照搬旧链路。
  2. 但应复用“先估算、再裁剪、最后收敛会话窗口”的思路。

5. 第 3 阶段明确不做什么

为了避免和第 4 阶段混淆,这一轮明确不做:

  1. 不把 execute prompt 直接拆成三层正式文件结构。
  2. 不在通用执行 prompt 里重写完整排程领域模块。
  3. 不额外新增没有稳定 owner 的字段,例如:
    • 当前目标
    • 最近观察
    • 已确认语义
  4. 不继续围绕粗排补边角语义。
  5. 不继续围绕 abort 协议扩展描述文案。

6. 第 3 阶段完成标准

至少要满足:

  1. execute 首轮 messages 明显变短。
  2. 同工具同参数的重复查询不会继续堆多份原始结果。
  3. assistant 过程话术不再进入后续执行历史。
  4. 最近一次失败模式仍能被模型感知。
  5. 最近一次工具调用与结果仍以合法配对形式保留。
  6. msg5 不再重复 msg4 的内容。
  7. 不破坏第 1-2 阶段已经打通的粗排 / abort 语义。

7. 供下一轮继续时快速判断的检查清单

如果下一轮接手时要快速判断是否做对,可以先问这几个问题:

  1. execute 现在是否还是“全量 history + 全量 pinned + 全量 runtime prompt”直接拼接
  2. 工具参数和 JSON 示例是否已经进入单独工具块?
  3. msg3 / msg4 是否仍保持一一对应?
  4. msg5 是否只保留运行态事实,而没有重复工具结果?
  5. assistant 的过程话术是否还在继续污染后续历史?
  6. correction 失败链是否还在整段保留?

如果以上问题仍然大多回答“是”,说明第 3 阶段还没有真正完成。