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 调试日志快照

前端:无
仓库:无
This commit is contained in:
LoveLosita
2026-04-08 21:35:05 +08:00
parent d3f65609f0
commit 4195e65cba
13 changed files with 4692 additions and 332 deletions

View File

@@ -1,6 +1,7 @@
package model
import (
"encoding/json"
"fmt"
"strings"
)
@@ -49,6 +50,47 @@ type ExecuteDecision struct {
Abort *AbortIntent `json:"abort,omitempty"`
}
// UnmarshalJSON 兼容执行决策里几种模型高频跑偏但语义可恢复的写法。
//
// 职责边界:
// 1. 负责把“空字符串占位字段”归一化成未填写,避免 json 反序列化阶段直接失败;
// 2. 负责把 tool_call / abort 交给各自的兼容解析逻辑,尽量保留可恢复的信息;
// 3. 不负责业务合法性校验action 与字段互斥关系仍交给 Validate 判定。
func (d *ExecuteDecision) UnmarshalJSON(data []byte) error {
type rawExecuteDecision struct {
Speak string `json:"speak,omitempty"`
Action ExecuteAction `json:"action"`
Reason string `json:"reason,omitempty"`
GoalCheck string `json:"goal_check,omitempty"`
ToolCall json.RawMessage `json:"tool_call,omitempty"`
Abort json.RawMessage `json:"abort,omitempty"`
}
var raw rawExecuteDecision
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
d.Speak = raw.Speak
d.Action = raw.Action
d.Reason = raw.Reason
d.GoalCheck = raw.GoalCheck
toolCall, err := decodeOptionalJSONObject[ToolCallIntent](raw.ToolCall)
if err != nil {
return fmt.Errorf("tool_call 解析失败: %w", err)
}
d.ToolCall = toolCall
abortIntent, err := decodeOptionalJSONObject[AbortIntent](raw.Abort)
if err != nil {
return fmt.Errorf("abort 解析失败: %w", err)
}
d.Abort = abortIntent
return nil
}
// Normalize 统一清洗 execute 决策中的字符串字段。
func (d *ExecuteDecision) Normalize() {
if d == nil {
@@ -173,6 +215,32 @@ type ToolCallIntent struct {
Arguments map[string]any `json:"arguments,omitempty"`
}
// UnmarshalJSON 兼容 tool_call 里“arguments / parameters”两种高频字段名。
//
// 职责边界:
// 1. 优先使用标准字段 arguments保持当前正式协议不变
// 2. 仅当 arguments 缺失时,回退复用 parameters兼容模型历史习惯
// 3. 不负责校验参数是否满足具体工具 schema后续仍由工具层负责。
func (t *ToolCallIntent) UnmarshalJSON(data []byte) error {
type rawToolCallIntent struct {
Name string `json:"name"`
Arguments map[string]any `json:"arguments,omitempty"`
Parameters map[string]any `json:"parameters,omitempty"`
}
var raw rawToolCallIntent
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
t.Name = raw.Name
t.Arguments = raw.Arguments
if len(t.Arguments) == 0 && len(raw.Parameters) > 0 {
t.Arguments = raw.Parameters
}
return nil
}
// Normalize 清洗工具调用意图中的稳定字段。
func (t *ToolCallIntent) Normalize() {
if t == nil {
@@ -193,6 +261,36 @@ func (t *ToolCallIntent) Validate() error {
return nil
}
// decodeOptionalJSONObject 统一兼容“可选对象字段被模型写成空字符串”的情况。
//
// 步骤说明:
// 1. 字段缺失、null、空字符串都视为“未填写”返回 nil
// 2. 只有在确实出现对象内容时,才继续反序列化为目标结构;
// 3. 若模型传入了非空字符串等不可恢复内容,显式报错,避免把脏数据静默吞掉。
func decodeOptionalJSONObject[T any](raw json.RawMessage) (*T, error) {
trimmed := strings.TrimSpace(string(raw))
if trimmed == "" || trimmed == "null" {
return nil, nil
}
if strings.HasPrefix(trimmed, "\"") {
var text string
if err := json.Unmarshal(raw, &text); err != nil {
return nil, err
}
if strings.TrimSpace(text) == "" {
return nil, nil
}
return nil, fmt.Errorf("期望对象,实际收到非空字符串")
}
var out T
if err := json.Unmarshal(raw, &out); err != nil {
return nil, err
}
return &out, nil
}
// ExecuteEvidenceSource 表示“当前步骤完成证明”来自哪里。
type ExecuteEvidenceSource string