Version: 0.9.62.dev.260502

后端:
1. 主动调度补齐 `unfinished_feedback` 定位闭环——用户补充信息先在滚动窗口内定位到可校验的日程块,定位失败则继续 ask_user,不再硬猜 target_id 或直接跑 graph。
2. 聊天占管重跑链路加并发保护——`waiting_user_reply -> rerunning` 改为 DB CAS 抢占,重复补充只返回可见等待提示,避免并发生成多份 preview。
3. rerun 结果回写继续收口——新 preview_id 同步回 trigger 审计指针,session 只在拿到新 preview 时更新当前预览,ready_preview 后清空追问状态并释放回普通聊天。
4. 主动调度事件校验放宽 unfinished_feedback 的空 target 场景,允许先触发、后定位,再进入 graph + preview 主链路。
This commit is contained in:
Losita
2026-05-02 12:41:50 +08:00
parent a3eaa9b2c2
commit ba23ebd201
12 changed files with 891 additions and 103 deletions

View File

@@ -0,0 +1,69 @@
package feedbacklocate
import (
"encoding/json"
"strings"
)
const locateSystemPrompt = `
你是 SmartFlow 主动调度里专门负责 unfinished_feedback 的定位器。
你的任务只有一个:根据用户补充的话,把它定位到当前滚动窗口中的某一个 schedule_event定位不了就继续 ask_user。
硬规则:
1. 只允许输出 JSON不要输出 markdown不要输出解释性正文。
2. 只允许返回 action / target_type / target_id / reason / ask_user_question 这几个字段。
3. target_type 只能是 schedule_event。
4. target_id 必须来自候选列表里的 target_id不要编造不要猜一个新的。
5. 当你不能稳定定位时action 必须是 ask_user并给出一句短问题。
6. 当用户补充信息已经足够时action 必须是 select_candidate。
7. 请优先结合当前时间、用户原始补充话术、pending question 和候选日程的时间顺序来判断。
`
func buildPromptInput(req Request, generatedAt string, windowStart string, windowEnd string, candidates []eventCandidate) promptInput {
input := promptInput{
GeneratedAt: generatedAt,
UserMessage: strings.TrimSpace(req.UserMessage),
Window: promptWindowInput{
StartAt: windowStart,
EndAt: windowEnd,
},
}
if trimmed := strings.TrimSpace(req.PendingQuestion); trimmed != "" {
input.PendingQuestion = trimmed
}
if len(req.MissingInfo) > 0 {
input.MissingInfo = cloneAndTrimStrings(req.MissingInfo)
}
input.Candidates = append([]eventCandidate(nil), candidates...)
return input
}
func buildUserPrompt(input promptInput) (string, error) {
raw, err := json.MarshalIndent(input, "", " ")
if err != nil {
return "", err
}
var builder strings.Builder
builder.WriteString("请根据输入定位当前滚动窗口中的 schedule_event。")
builder.WriteString("只输出 JSON不要补充任何其它内容。\n")
builder.WriteString("输入:\n")
builder.WriteString(string(raw))
return builder.String(), nil
}
// BuildAskUserQuestion 负责把 missing_info 转成继续追问用户的短问题。
func BuildAskUserQuestion(missingInfo []string) string {
normalized := cloneAndTrimStrings(missingInfo)
if len(normalized) == 0 {
return "请补充能唯一定位到未完成日程块的信息。"
}
for _, item := range normalized {
if item == "feedback_target" {
return "请告诉我你指的是哪一个未完成的日程块,比如具体时间或名称。"
}
}
return "请补充 " + strings.Join(normalized, "、") + " 对应的信息。"
}