Version: 0.8.5.dev.260330

后端:
1.把node/plan的具体逻辑做完了,没仔细看,进入下一步之前需要仔细review

前端:
无改动

全仓库:
无改动
This commit is contained in:
Losita
2026-03-30 22:08:30 +08:00
parent 6d22acb270
commit e1a06be768
10 changed files with 1494 additions and 184 deletions

View File

@@ -8,25 +8,18 @@ import (
)
const (
// ExecuteNextPlanSignal 表示“当前 plan 步骤已经完成,可以进入下一个步骤”。
//
// TODO(newagent/node): 后续 executeNode 识别到该信号后,调用 state.AdvanceStep() 或决定进入交付阶段。
// ExecuteNextPlanSignal 表示“当前 plan step 已完成,可以推进到下一个步骤”。
ExecuteNextPlanSignal = "[NEXT_PLAN]"
// ExecuteDoneSignal 表示“整个任务已经完成,可以结束执行链路”。
//
// TODO(newagent/node): 后续 executeNode 识别到该信号后,调用 state.Done() 并进入 deliver。
// ExecuteDoneSignal 表示“整个任务已经完成,可以进入最终交付”。
ExecuteDoneSignal = "[DONE]"
// ExecuteAskUserSignal 表示“执行阶段缺关键信息,需要向用户追问”。
//
// TODO(newagent/node): 后续若你决定支持 ask_user这里可作为统一控制信号继续扩展。
// ExecuteAskUserSignal 表示“执行当前步骤缺少关键信息,需要向用户追问”。
ExecuteAskUserSignal = "[ASK_USER]"
)
const executeSystemPrompt = `
你是 SmartFlow NewAgent 的执行器。
你的职责是在“当前 plan 步骤”的约束下,进行思考、执行、观察,再决定下一步动作。
请遵守以下规则:
@@ -52,13 +45,6 @@ func BuildExecuteSystemPrompt() string {
}
// BuildExecuteMessages 组装执行阶段的 messages。
//
// 职责边界:
// 1. 负责收敛执行阶段需要的 system / pinned / history / runtime prompt
// 2. 负责把“完整 plan + 当前步骤 + 控制信号”显式告知模型;
// 3. 不负责解析模型输出,也不负责真正调用工具。
//
// TODO(newagent/node): 后续 executeNode 应直接复用这个方法,而不是在节点内手拼执行提示词。
func BuildExecuteMessages(state *newagentmodel.CommonState, ctx *newagentmodel.ConversationContext) []*schema.Message {
return buildStageMessages(
BuildExecuteSystemPrompt(),
@@ -94,8 +80,13 @@ func BuildExecuteUserPrompt(state *newagentmodel.CommonState) string {
sb.WriteString(ExecuteAskUserSignal)
sb.WriteString("。\n")
sb.WriteString("\n当前步骤正文\n")
sb.WriteString(currentStep)
sb.WriteString(strings.TrimSpace(currentStep.Content))
sb.WriteString("\n")
if strings.TrimSpace(currentStep.DoneWhen) != "" {
sb.WriteString("\n当前步骤完成判定\n")
sb.WriteString(strings.TrimSpace(currentStep.DoneWhen))
sb.WriteString("\n")
}
} else {
sb.WriteString("当前 plan 已存在,但当前步骤索引无效;请不要擅自执行其他步骤。\n")
}

View File

@@ -8,24 +8,17 @@ import (
"github.com/cloudwego/eino/schema"
)
const (
// PlanDoneSignal 表示“规划阶段结束,可以进入 confirm 或下一阶段”。
//
// TODO(newagent/node): 后续由 planNode 读取模型输出时识别这个信号,并据此调用 state.FinishPlan(...)。
PlanDoneSignal = "[PLAN_DONE]"
)
const planSystemPrompt = `
你是 SmartFlow NewAgent 的规划器。
你的职责不是直接执行任务,而是先把用户意图拆成一组清晰、稳定、可逐步执行的自然语言计划。
你的职责不是直接执行任务,而是先把用户意图拆成一组清晰、稳定、可逐步执行的自然语言计划,并严格按后端约定的 JSON 协议输出。
请遵守以下规则:
1. 只负责规划,不要假装已经调用了工具,也不要伪造执行结果。
2. 每一轮只推进一步规划;如果信息不足,可以明确指出缺口
2. 每一轮只推进一步规划;如果信息不足,应明确转成 ask_user而不是继续硬猜
3. 若当前计划仍不完整,就继续围绕当前任务补全计划,不要跳去执行细节。
4. 若你认为计划已经完整可执行,请在输出中显式带上 ` + "`" + `[` + `PLAN_DONE` + `]` + "`" + ` 信号
5. 计划必须使用自然语言,便于后端将完整 plan 重新注入到后续上下文顶部。
4. 若你认为计划已经完整可执行,请返回 action=plan_done并附带完整 plan_steps
5. plan_steps 必须使用自然语言,便于后端将完整 plan 重新注入到后续上下文顶部。
6. 只输出 JSON不要输出 markdown不要输出额外解释不要在 JSON 外再补文字。
你会看到:
- 当前阶段与轮次信息
@@ -47,10 +40,8 @@ func BuildPlanSystemPrompt() string {
//
// 职责边界:
// 1. 负责把 state + context 收敛成规划阶段模型输入;
// 2. 负责把置顶上下文”和“工具摘要”放到 history 前面,降低模型跑偏概率;
// 3. 不负责解析模型输出,不负责判断是否真的完成规划。
//
// TODO(newagent/node): 后续 planNode 直接复用这个入口,不要在节点里散落拼 message 的逻辑。
// 2. 负责把置顶上下文工具摘要放在 history 前面,降低模型跑偏概率;
// 3. 不负责解析模型输出,不负责判断规划质量
func BuildPlanMessages(state *newagentmodel.CommonState, ctx *newagentmodel.ConversationContext, userInput string) []*schema.Message {
return buildStageMessages(
BuildPlanSystemPrompt(),
@@ -60,21 +51,15 @@ func BuildPlanMessages(state *newagentmodel.CommonState, ctx *newagentmodel.Conv
}
// BuildPlanUserPrompt 构造规划阶段的用户提示词。
//
// 设计目标:
// 1. 把当前阶段、轮次、既有 plan、当前步骤等控制信息显式写给模型
// 2. 保持自然语言风格,方便你后续继续改成自己想要的控制协议;
// 3. 用户原始输入单独放在末尾,避免被系统拼装信息淹没。
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")
sb.WriteString("如果计划已经完整,请显式输出 ")
sb.WriteString(PlanDoneSignal)
sb.WriteString("。\n")
sb.WriteString("本轮目标:围绕当前任务继续规划,直到形成一份稳定、可执行的自然语言 plan,或在信息不足时明确追问用户。\n\n")
sb.WriteString(BuildPlanDecisionContractText())
sb.WriteString("\n")
trimmedInput := strings.TrimSpace(userInput)
if trimmedInput != "" {
@@ -86,10 +71,61 @@ func BuildPlanUserPrompt(state *newagentmodel.CommonState, userInput string) str
return strings.TrimSpace(sb.String())
}
// BuildPlanDecisionContractText 返回规划阶段的输出协议说明。
func BuildPlanDecisionContractText() string {
return strings.TrimSpace(fmt.Sprintf(`
输出协议(严格 JSON
- speak给用户看的话若 action=%s这里通常就是要追问用户的问题
- action只能是 %s / %s / %s
- reason给后端和日志看的简短说明
- plan_steps仅当 action=%s 时允许返回;返回时必须是完整计划,不是增量
- plan_steps[].content步骤正文必填
- plan_steps[].done_when可选建议写“什么情况下算这一步做完”
合法示例:
{
"speak": "我先把计划再收束一下。",
"action": "%s",
"reason": "当前信息已足够继续规划"
}
{
"speak": "你更希望我优先安排今天,还是按整周来规划?",
"action": "%s",
"reason": "当前时间范围仍不明确"
}
{
"speak": "计划已经整理好了,我先给你确认一下。",
"action": "%s",
"reason": "当前计划已具备执行条件",
"plan_steps": [
{
"content": "先确认本周可用时间范围",
"done_when": "拿到明确的可用时间段列表"
},
{
"content": "基于可用时间生成执行安排",
"done_when": "得到一份用户可确认的安排方案"
}
]
}
`,
newagentmodel.PlanActionAskUser,
newagentmodel.PlanActionContinue,
newagentmodel.PlanActionAskUser,
newagentmodel.PlanActionDone,
newagentmodel.PlanActionDone,
newagentmodel.PlanActionContinue,
newagentmodel.PlanActionAskUser,
newagentmodel.PlanActionDone,
))
}
// buildStageMessages 组装某个阶段通用的 messages。
//
// 步骤说明:
// 1. 先合并 context 自带 system prompt 与阶段 prompt保证通用约束和阶段约束都生效;
// 1. 先合并 context 自带 system prompt 与阶段 prompt保证通用约束和阶段约束都生效
// 2. 再把置顶上下文块和工具摘要补成 system message尽量顶在 history 前面;
// 3. 最后追加历史消息与本轮 user prompt保持“新约束在前、历史在后”的稳定顺序。
func buildStageMessages(stageSystemPrompt string, ctx *newagentmodel.ConversationContext, runtimeUserPrompt string) []*schema.Message {
@@ -123,10 +159,10 @@ func buildStageMessages(stageSystemPrompt string, ctx *newagentmodel.Conversatio
return messages
}
// renderStateSummary 当前流程状态渲染成简洁文本。
// renderStateSummary 当前流程状态渲染成简洁文本。
func renderStateSummary(state *newagentmodel.CommonState) string {
if state == nil {
return "当前状态state 缺失,请先进行兜底处理。"
return "当前状态state 缺失,请先兜底处理。"
}
var sb strings.Builder
@@ -142,22 +178,30 @@ func renderStateSummary(state *newagentmodel.CommonState) string {
sb.WriteString("当前完整 plan\n")
for i, step := range state.PlanSteps {
sb.WriteString(fmt.Sprintf("%d. %s\n", i+1, strings.TrimSpace(step)))
sb.WriteString(fmt.Sprintf("%d. %s\n", i+1, strings.TrimSpace(step.Content)))
if strings.TrimSpace(step.DoneWhen) != "" {
sb.WriteString(fmt.Sprintf(" 完成判定:%s\n", strings.TrimSpace(step.DoneWhen)))
}
}
if step, ok := state.CurrentPlanStep(); ok {
sb.WriteString(fmt.Sprintf("当前步骤进度:%d/%d\n", current, total))
sb.WriteString("当前步骤内容:\n")
sb.WriteString(step)
sb.WriteString(strings.TrimSpace(step.Content))
sb.WriteString("\n")
if strings.TrimSpace(step.DoneWhen) != "" {
sb.WriteString("当前步骤完成判定:\n")
sb.WriteString(strings.TrimSpace(step.DoneWhen))
sb.WriteString("\n")
}
} else {
sb.WriteString("当前步骤进度:暂无有效当前步骤。\n")
sb.WriteString("当前步骤进度:暂无有效当前步骤。\n")
}
return sb.String()
}
// renderPinnedBlocks ConversationContext 中的置顶块渲染成一段独立的 system 内容
// renderPinnedBlocks ConversationContext 中的置顶块渲染成独立的 system 文本
func renderPinnedBlocks(ctx *newagentmodel.ConversationContext) string {
if ctx == nil {
return ""
@@ -186,7 +230,7 @@ func renderPinnedBlocks(ctx *newagentmodel.ConversationContext) string {
return strings.TrimSpace(sb.String())
}
// renderToolSchemas 工具摘要渲染成独立文本块。
// renderToolSchemas 工具摘要渲染成独立文本块。
func renderToolSchemas(ctx *newagentmodel.ConversationContext) string {
if ctx == nil {
return ""