package newagentprompt import ( "fmt" "strconv" "strings" newagentmodel "github.com/LoveLosita/smartflow/backend/newAgent/model" "github.com/cloudwego/eino/schema" ) const planSystemPrompt = ` 你是 SmartMate 的规划器。 你的职责不是直接执行任务,而是先把用户意图拆成一组清晰、稳定、可逐步执行的自然语言计划,并严格按后端约定的 JSON 协议输出。 请遵守以下规则: 1. 只负责规划,不要假装已经调用了工具,也不要伪造执行结果。 2. 每一轮只推进一步规划;如果信息不足,应明确转成 ask_user,而不是继续硬猜。 3. 若当前计划仍不完整,就继续围绕当前任务补全计划,不要跳去执行细节。 4. 若你认为计划已经完整可执行,请返回 action=plan_done,并附带完整 plan_steps。 5. plan_steps 必须使用自然语言,便于后端将完整 plan 重新注入到后续上下文顶部。 6. 只输出 JSON,不要输出 markdown,不要输出额外解释,不要在 JSON 外再补文字。 7. 每次输出前先评估任务复杂度:simple(简单明确,无复杂依赖)、moderate(多步操作,需要一定推理)、complex(需要深度推理、多方案比较或复杂依赖关系)。 8. 根据复杂度判断 need_thinking:你是否需要深度思考才能生成高质量计划?当不确定时倾向于 false。 9. 粗排识别规则:若满足以下两个条件,在 action=plan_done 时附加 needs_rough_build=true 和 task_class_ids: 条件1:用户输入中存在"任务类 ID"字段(见上下文"任务类 ID"部分); 条件2:用户意图明确是"批量安排/帮我排课/把任务类排进日程"等批量调度需求。 满足时:后端会在用户确认计划后自动运行粗排算法(硬性约束已由算法保证,无需 LLM 校验)。 你的 plan_steps 应聚焦于"用读写工具优化方案",建议两步: 第1步:用 get_overview / query_target_tasks / query_available_slots 等读工具审视粗排结果,找出可优化的点(时段分布不均、空位未利用等); 第2步:用 move / batch_move 等写工具微调后,将最终方案展示给用户确认。 禁止安排任何"校验/验证约束"步骤——硬性约束由算法兜底,LLM 不需要操心。 你会看到: - 当前阶段与轮次信息 - 已有完整 plan(如果之前已经规划过) - 当前步骤(如果已存在) - 置顶上下文块 - 可用工具摘要 - 历史对话 请基于这些输入继续规划,而不是重复忽略既有 plan。 ` // BuildPlanSystemPrompt 返回规划阶段系统提示词。 func BuildPlanSystemPrompt() string { return strings.TrimSpace(planSystemPrompt) } // BuildPlanMessages 组装规划阶段的 messages。 // // 职责边界: // 1. 负责把 state + context 收敛成规划阶段模型输入; // 2. 负责把置顶上下文和工具摘要放在 history 前面,降低模型跑偏概率; // 3. 不负责解析模型输出,也不负责判断规划质量。 func BuildPlanMessages(state *newagentmodel.CommonState, ctx *newagentmodel.ConversationContext, userInput string) []*schema.Message { return buildStageMessages( BuildPlanSystemPrompt(), ctx, BuildPlanUserPrompt(state, userInput), ) } // BuildPlanUserPrompt 构造规划阶段的用户提示词。 func BuildPlanUserPrompt(state *newagentmodel.CommonState, userInput string) string { var sb strings.Builder sb.WriteString("请继续当前任务的规划阶段。\n") sb.WriteString(renderStateSummary(state)) sb.WriteString("\n") sb.WriteString("本轮目标:围绕当前任务继续规划,直到形成一份稳定、可执行的自然语言 plan,或在信息不足时明确追问用户。\n\n") sb.WriteString(BuildPlanDecisionContractText()) sb.WriteString("\n") if state != nil && len(state.TaskClassIDs) > 0 { parts := make([]string, len(state.TaskClassIDs)) for i, id := range state.TaskClassIDs { parts[i] = strconv.Itoa(id) } sb.WriteString(fmt.Sprintf("\n本次排课请求涉及的任务类 ID(前端传入):[%s]\n", strings.Join(parts, ", "))) sb.WriteString("规划时请结合上述任务类 ID 判断是否需要粗排(needs_rough_build),并在 plan_steps 中体现排课意图。\n") } trimmedInput := strings.TrimSpace(userInput) if trimmedInput != "" { sb.WriteString("\n用户本轮输入:\n") sb.WriteString(trimmedInput) sb.WriteString("\n") } return strings.TrimSpace(sb.String()) } // BuildPlanDecisionContractText 返回规划阶段的输出协议说明。 func BuildPlanDecisionContractText() string { return strings.TrimSpace(fmt.Sprintf(` 输出协议(严格 JSON): - speak:给用户看的话;若 action=%s,这里通常就是要追问用户的问题 - action:只能是 %s / %s / %s - reason:给后端和日志看的简短说明 - complexity:任务复杂度,只能是 simple / moderate / complex - need_thinking:是否需要深度思考才能生成高质量计划,只能是 true / false - plan_steps:仅当 action=%s 时允许返回;返回时必须是完整计划,不是增量 - plan_steps[].content:步骤正文,必填 - plan_steps[].done_when:可选,建议写"什么情况下算这一步做完" - needs_rough_build:仅当满足粗排识别规则时为 true,否则省略;为 true 时后端自动运行粗排算法 - task_class_ids:needs_rough_build=true 时必填,从上下文"任务类 ID"字段读取 合法示例: { "speak": "我先把计划再收束一下。", "action": "%s", "reason": "当前信息已足够继续规划", "complexity": "moderate", "need_thinking": false } { "speak": "你更希望我优先安排今天,还是按整周来规划?", "action": "%s", "reason": "当前时间范围仍不明确", "complexity": "simple", "need_thinking": false } { "speak": "计划已经整理好了,我先给你确认一下。", "action": "%s", "reason": "当前计划已具备执行条件", "complexity": "simple", "need_thinking": false, "plan_steps": [ { "content": "先确认本周可用时间范围", "done_when": "拿到明确的可用时间段列表" }, { "content": "基于可用时间生成执行安排", "done_when": "得到一份用户可确认的安排方案" } ] } `, newagentmodel.PlanActionAskUser, newagentmodel.PlanActionContinue, newagentmodel.PlanActionAskUser, newagentmodel.PlanActionDone, newagentmodel.PlanActionDone, newagentmodel.PlanActionContinue, newagentmodel.PlanActionAskUser, newagentmodel.PlanActionDone, )) }