Version: 0.9.6.dev.260407
后端: 1.execute 正式终止协议补齐(abort / exhausted / completed 统一建模) - 更新model/common_state.go:新增 FlowTerminalStatus / FlowTerminalOutcome;补齐 Abort/Exhaust/ClearTerminalOutcome/IsCompleted 等统一终止语义 - 更新model/execute_contract.go:新增 ExecuteActionAbort 与 AbortIntent;补齐 action 校验互斥规则 - 更新prompt/execute.go:Plan/ReAct 两套 execute contract 升级到 V2,补充 abort 协议与 JSON 示例 2.graph 路由与 deliver 收口统一围绕 terminal outcome - 更新graph/common_graph.go:RoughBuild 改 branch;粗排异常可直接 Deliver;Execute 路由不再按“最后一轮”提前误收口 - 更新node/execute.go:轮次耗尽改写为 Exhaust;接入 handleExecuteActionAbort;abort 不在 execute 直接对用户收口 - 更新node/deliver.go:deliver summary 优先按 abort/exhausted 收口;不再无脑 Done;最终状态文案改为“本轮流程已结束” - 更新node/agent_nodes.go:仅 completed 路径写 schedule preview,aborted/exhausted 跳过 3.提示与状态摘要同步终止语义 - 更新prompt/base.go:state summary 增加 terminal outcome 展示 前端:无 仓库:无
This commit is contained in:
@@ -102,8 +102,17 @@ func RunAgentGraph(ctx context.Context, input newagentmodel.AgentGraphRunInput)
|
|||||||
)); err != nil {
|
)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// RoughBuild -> Execute:粗排完成后直接进入执行阶段微调。
|
// RoughBuild -> Execute / Deliver:
|
||||||
if err := g.AddEdge(NodeRoughBuild, NodeExecute); err != nil {
|
// 1. 正常粗排完成后进入 execute 微调;
|
||||||
|
// 2. 若粗排阶段已写入正式终止结果(如粗排异常 abort),则直接进入 deliver 收口。
|
||||||
|
if err := g.AddBranch(NodeRoughBuild, compose.NewGraphBranch(
|
||||||
|
branchAfterRoughBuild,
|
||||||
|
map[string]bool{
|
||||||
|
NodeExecute: true,
|
||||||
|
NodeDeliver: true,
|
||||||
|
NodeInterrupt: true,
|
||||||
|
},
|
||||||
|
)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Execute -> Execute(继续 ReAct) / Confirm(写操作待确认) / Deliver(完成) / Interrupt(需要追问用户)
|
// Execute -> Execute(继续 ReAct) / Confirm(写操作待确认) / Deliver(完成) / Interrupt(需要追问用户)
|
||||||
@@ -224,11 +233,31 @@ func branchAfterConfirm(_ context.Context, st *newagentmodel.AgentGraphState) (s
|
|||||||
// confirm 节点产出确认请求后,当前连接必须进入 interrupt 收口。
|
// confirm 节点产出确认请求后,当前连接必须进入 interrupt 收口。
|
||||||
// 真正的用户确认结果应由外部回调写回状态,再重新进入 graph。
|
// 真正的用户确认结果应由外部回调写回状态,再重新进入 graph。
|
||||||
return NodeInterrupt, nil
|
return NodeInterrupt, nil
|
||||||
|
case newagentmodel.PhaseDone:
|
||||||
|
return NodeDeliver, nil
|
||||||
default:
|
default:
|
||||||
return NodePlan, nil
|
return NodePlan, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func branchAfterRoughBuild(_ context.Context, st *newagentmodel.AgentGraphState) (string, error) {
|
||||||
|
if st == nil {
|
||||||
|
return NodeExecute, nil
|
||||||
|
}
|
||||||
|
if nextNode, interrupted := branchIfInterrupted(st); interrupted {
|
||||||
|
return nextNode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
flowState := st.EnsureFlowState()
|
||||||
|
if flowState == nil {
|
||||||
|
return NodeExecute, nil
|
||||||
|
}
|
||||||
|
if flowState.Phase == newagentmodel.PhaseDone {
|
||||||
|
return NodeDeliver, nil
|
||||||
|
}
|
||||||
|
return NodeExecute, nil
|
||||||
|
}
|
||||||
|
|
||||||
func branchAfterExecute(_ context.Context, st *newagentmodel.AgentGraphState) (string, error) {
|
func branchAfterExecute(_ context.Context, st *newagentmodel.AgentGraphState) (string, error) {
|
||||||
if st == nil {
|
if st == nil {
|
||||||
return NodeExecute, nil
|
return NodeExecute, nil
|
||||||
@@ -244,7 +273,13 @@ func branchAfterExecute(_ context.Context, st *newagentmodel.AgentGraphState) (s
|
|||||||
if flowState.Phase == newagentmodel.PhaseWaitingConfirm {
|
if flowState.Phase == newagentmodel.PhaseWaitingConfirm {
|
||||||
return NodeConfirm, nil
|
return NodeConfirm, nil
|
||||||
}
|
}
|
||||||
if flowState.Phase == newagentmodel.PhaseDone || flowState.Exhausted() {
|
// 1. 这里只围绕“是否已经写入正式终止结果”做路由,避免把“刚好用完最后一轮预算”
|
||||||
|
// 误判成已经 exhausted 收口;
|
||||||
|
// 2. 真正的 exhausted 语义应由下一次 Execute 入口在 NextRound() 失败时统一写入,
|
||||||
|
// 这样 rough_build / execute / deliver 才都围绕同一份 terminal outcome 工作;
|
||||||
|
// 3. 若此处直接按 RoundUsed>=MaxRounds 跳 Deliver,会绕过 Execute 内的 Exhaust 写入,
|
||||||
|
// 导致 deliver 收口和后续预览落盘语义不一致。
|
||||||
|
if flowState.Phase == newagentmodel.PhaseDone {
|
||||||
return NodeDeliver, nil
|
return NodeDeliver, nil
|
||||||
}
|
}
|
||||||
return NodeExecute, nil
|
return NodeExecute, nil
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
newagenttools "github.com/LoveLosita/smartflow/backend/newAgent/tools"
|
newagenttools "github.com/LoveLosita/smartflow/backend/newAgent/tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,6 +16,47 @@ const (
|
|||||||
PhaseDone Phase = "done"
|
PhaseDone Phase = "done"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FlowTerminalStatus 表示本轮流程最终是如何结束的。
|
||||||
|
//
|
||||||
|
// 说明:
|
||||||
|
// 1. completed 表示任务按预期完成,允许走正常交付与预览落盘;
|
||||||
|
// 2. aborted 表示业务语义上的主动终止,例如粗排异常、执行期明确中止;
|
||||||
|
// 3. exhausted 表示安全边界触发的被动停止,例如执行轮次耗尽。
|
||||||
|
type FlowTerminalStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
FlowTerminalStatusCompleted FlowTerminalStatus = "completed"
|
||||||
|
FlowTerminalStatusAborted FlowTerminalStatus = "aborted"
|
||||||
|
FlowTerminalStatusExhausted FlowTerminalStatus = "exhausted"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlowTerminalOutcome 保存“流程为什么结束”的最终结果快照。
|
||||||
|
//
|
||||||
|
// 职责边界:
|
||||||
|
// 1. Stage 说明终止发生在哪个阶段,便于 graph/deliver/debug 统一收口;
|
||||||
|
// 2. Code 作为稳定机器码,便于后续前端或埋点按类型识别;
|
||||||
|
// 3. UserMessage 是最终给用户看的收口文案;
|
||||||
|
// 4. InternalReason 只用于日志与排查,不直接暴露给用户。
|
||||||
|
type FlowTerminalOutcome struct {
|
||||||
|
Status FlowTerminalStatus `json:"status"`
|
||||||
|
Stage string `json:"stage,omitempty"`
|
||||||
|
Code string `json:"code,omitempty"`
|
||||||
|
UserMessage string `json:"user_message,omitempty"`
|
||||||
|
InternalReason string `json:"internal_reason,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize 统一清洗终止结果里的字符串字段。
|
||||||
|
func (o *FlowTerminalOutcome) Normalize() {
|
||||||
|
if o == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
o.Status = FlowTerminalStatus(strings.TrimSpace(string(o.Status)))
|
||||||
|
o.Stage = strings.TrimSpace(o.Stage)
|
||||||
|
o.Code = strings.TrimSpace(o.Code)
|
||||||
|
o.UserMessage = strings.TrimSpace(o.UserMessage)
|
||||||
|
o.InternalReason = strings.TrimSpace(o.InternalReason)
|
||||||
|
}
|
||||||
|
|
||||||
const DefaultMaxRounds = 30
|
const DefaultMaxRounds = 30
|
||||||
|
|
||||||
// CommonState 承载可持久化的主流程状态。
|
// CommonState 承载可持久化的主流程状态。
|
||||||
@@ -54,6 +97,10 @@ type CommonState struct {
|
|||||||
// NeedsRoughBuild 由 Plan 节点在 plan_done 时写入,标记 Confirm 后是否需要走粗排节点。
|
// NeedsRoughBuild 由 Plan 节点在 plan_done 时写入,标记 Confirm 后是否需要走粗排节点。
|
||||||
// 粗排节点执行完毕后会将此字段重置为 false。
|
// 粗排节点执行完毕后会将此字段重置为 false。
|
||||||
NeedsRoughBuild bool `json:"needs_rough_build,omitempty"`
|
NeedsRoughBuild bool `json:"needs_rough_build,omitempty"`
|
||||||
|
|
||||||
|
// TerminalOutcome 保存“本轮流程最终如何结束”的统一收口结果。
|
||||||
|
// 第二轮开始,rough_build / execute / deliver 都应围绕这份快照判断收口语义。
|
||||||
|
TerminalOutcome *FlowTerminalOutcome `json:"terminal_outcome,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommonState(traceID string, userID int, conversationID string) *CommonState {
|
func NewCommonState(traceID string, userID int, conversationID string) *CommonState {
|
||||||
@@ -87,11 +134,13 @@ func (s *CommonState) FinishPlan(steps []PlanStep) {
|
|||||||
s.PlanSteps = steps
|
s.PlanSteps = steps
|
||||||
s.CurrentStep = 0
|
s.CurrentStep = 0
|
||||||
s.Phase = PhaseWaitingConfirm
|
s.Phase = PhaseWaitingConfirm
|
||||||
|
s.ClearTerminalOutcome()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfirmPlan 表示用户已确认计划,流程进入执行阶段。
|
// ConfirmPlan 表示用户已确认计划,流程进入执行阶段。
|
||||||
func (s *CommonState) ConfirmPlan() {
|
func (s *CommonState) ConfirmPlan() {
|
||||||
s.Phase = PhaseExecuting
|
s.Phase = PhaseExecuting
|
||||||
|
s.ClearTerminalOutcome()
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartDirectExecute 进入无 plan 的直接执行(ReAct)模式。
|
// StartDirectExecute 进入无 plan 的直接执行(ReAct)模式。
|
||||||
@@ -102,6 +151,7 @@ func (s *CommonState) StartDirectExecute() {
|
|||||||
s.PlanSteps = nil
|
s.PlanSteps = nil
|
||||||
s.CurrentStep = 0
|
s.CurrentStep = 0
|
||||||
s.Phase = PhaseExecuting
|
s.Phase = PhaseExecuting
|
||||||
|
s.ClearTerminalOutcome()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RejectPlan 表示用户拒绝当前计划,清空计划并回退到 planning。
|
// RejectPlan 表示用户拒绝当前计划,清空计划并回退到 planning。
|
||||||
@@ -109,6 +159,7 @@ func (s *CommonState) RejectPlan() {
|
|||||||
s.PlanSteps = nil
|
s.PlanSteps = nil
|
||||||
s.CurrentStep = 0
|
s.CurrentStep = 0
|
||||||
s.Phase = PhasePlanning
|
s.Phase = PhasePlanning
|
||||||
|
s.ClearTerminalOutcome()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AdvanceStep 推进到下一个计划步骤,并返回是否仍有剩余步骤。
|
// AdvanceStep 推进到下一个计划步骤,并返回是否仍有剩余步骤。
|
||||||
@@ -118,8 +169,86 @@ func (s *CommonState) AdvanceStep() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Done 标记整个任务流程已经结束。
|
// Done 标记整个任务流程已经结束。
|
||||||
|
//
|
||||||
|
// 说明:
|
||||||
|
// 1. 若此前已经写入 aborted / exhausted 等终止结果,这里只负责兜底维持 PhaseDone,不覆盖已有语义;
|
||||||
|
// 2. 只有在尚未写入任何终止结果时,才默认补成 completed。
|
||||||
func (s *CommonState) Done() {
|
func (s *CommonState) Done() {
|
||||||
s.Phase = PhaseDone
|
s.Phase = PhaseDone
|
||||||
|
if s.TerminalOutcome != nil {
|
||||||
|
s.TerminalOutcome.Normalize()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.TerminalOutcome = &FlowTerminalOutcome{
|
||||||
|
Status: FlowTerminalStatusCompleted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abort 将当前流程标记为“业务语义上的主动终止”。
|
||||||
|
//
|
||||||
|
// 步骤说明:
|
||||||
|
// 1. 统一写入 PhaseDone,保证 graph 后续直接进入 deliver 收口;
|
||||||
|
// 2. UserMessage 作为最终可见文案,必须尽量完整,避免 deliver 再二次猜测;
|
||||||
|
// 3. InternalReason 只用于排查,允许比用户文案更技术化。
|
||||||
|
func (s *CommonState) Abort(stage, code, userMessage, internalReason string) {
|
||||||
|
s.Phase = PhaseDone
|
||||||
|
s.TerminalOutcome = &FlowTerminalOutcome{
|
||||||
|
Status: FlowTerminalStatusAborted,
|
||||||
|
Stage: stage,
|
||||||
|
Code: code,
|
||||||
|
UserMessage: userMessage,
|
||||||
|
InternalReason: internalReason,
|
||||||
|
}
|
||||||
|
s.TerminalOutcome.Normalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exhaust 将当前流程标记为“安全边界触发的被动停止”。
|
||||||
|
func (s *CommonState) Exhaust(stage, userMessage, internalReason string) {
|
||||||
|
s.Phase = PhaseDone
|
||||||
|
s.TerminalOutcome = &FlowTerminalOutcome{
|
||||||
|
Status: FlowTerminalStatusExhausted,
|
||||||
|
Stage: stage,
|
||||||
|
Code: "round_exhausted",
|
||||||
|
UserMessage: userMessage,
|
||||||
|
InternalReason: internalReason,
|
||||||
|
}
|
||||||
|
s.TerminalOutcome.Normalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearTerminalOutcome 清空上一轮遗留的终止结果。
|
||||||
|
func (s *CommonState) ClearTerminalOutcome() {
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.TerminalOutcome = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasTerminalOutcome 判断当前是否已经写入正式终止结果。
|
||||||
|
func (s *CommonState) HasTerminalOutcome() bool {
|
||||||
|
return s != nil && s.TerminalOutcome != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TerminalStatus 返回当前终止结果的状态枚举。
|
||||||
|
func (s *CommonState) TerminalStatus() FlowTerminalStatus {
|
||||||
|
if s == nil || s.TerminalOutcome == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return s.TerminalOutcome.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCompleted 判断当前是否属于“正常完成”。
|
||||||
|
func (s *CommonState) IsCompleted() bool {
|
||||||
|
return s.TerminalStatus() == FlowTerminalStatusCompleted
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAborted 判断当前是否属于“主动中止”。
|
||||||
|
func (s *CommonState) IsAborted() bool {
|
||||||
|
return s.TerminalStatus() == FlowTerminalStatusAborted
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExhaustedTerminal 判断当前是否属于“轮次耗尽收口”。
|
||||||
|
func (s *CommonState) IsExhaustedTerminal() bool {
|
||||||
|
return s.TerminalStatus() == FlowTerminalStatusExhausted
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasPlan 判断当前 state 是否已经持有一份完整计划。
|
// HasPlan 判断当前 state 是否已经持有一份完整计划。
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ const (
|
|||||||
|
|
||||||
// ExecuteActionDone 表示整个任务已完成,可以进入最终交付。
|
// ExecuteActionDone 表示整个任务已完成,可以进入最终交付。
|
||||||
ExecuteActionDone ExecuteAction = "done"
|
ExecuteActionDone ExecuteAction = "done"
|
||||||
|
|
||||||
|
// ExecuteActionAbort 表示本轮流程应立即终止,并进入 deliver 做正式收口。
|
||||||
|
ExecuteActionAbort ExecuteAction = "abort"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecuteDecision 是 execute prompt 单轮产出的统一决策结构。
|
// ExecuteDecision 是 execute prompt 单轮产出的统一决策结构。
|
||||||
@@ -43,6 +46,7 @@ type ExecuteDecision struct {
|
|||||||
Reason string `json:"reason,omitempty"`
|
Reason string `json:"reason,omitempty"`
|
||||||
GoalCheck string `json:"goal_check,omitempty"`
|
GoalCheck string `json:"goal_check,omitempty"`
|
||||||
ToolCall *ToolCallIntent `json:"tool_call,omitempty"`
|
ToolCall *ToolCallIntent `json:"tool_call,omitempty"`
|
||||||
|
Abort *AbortIntent `json:"abort,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize 统一清洗 execute 决策中的字符串字段。
|
// Normalize 统一清洗 execute 决策中的字符串字段。
|
||||||
@@ -57,6 +61,9 @@ func (d *ExecuteDecision) Normalize() {
|
|||||||
if d.ToolCall != nil {
|
if d.ToolCall != nil {
|
||||||
d.ToolCall.Normalize()
|
d.ToolCall.Normalize()
|
||||||
}
|
}
|
||||||
|
if d.Abort != nil {
|
||||||
|
d.Abort.Normalize()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate 校验 execute 决策的最小合法性。
|
// Validate 校验 execute 决策的最小合法性。
|
||||||
@@ -77,6 +84,9 @@ func (d *ExecuteDecision) Validate() error {
|
|||||||
|
|
||||||
switch d.Action {
|
switch d.Action {
|
||||||
case ExecuteActionContinue:
|
case ExecuteActionContinue:
|
||||||
|
if d.Abort != nil {
|
||||||
|
return fmt.Errorf("continue 动作不应携带 abort")
|
||||||
|
}
|
||||||
if d.ToolCall != nil {
|
if d.ToolCall != nil {
|
||||||
return d.ToolCall.Validate()
|
return d.ToolCall.Validate()
|
||||||
}
|
}
|
||||||
@@ -85,22 +95,73 @@ func (d *ExecuteDecision) Validate() error {
|
|||||||
if d.ToolCall != nil {
|
if d.ToolCall != nil {
|
||||||
return fmt.Errorf("ask_user 动作不应携带 tool_call")
|
return fmt.Errorf("ask_user 动作不应携带 tool_call")
|
||||||
}
|
}
|
||||||
|
if d.Abort != nil {
|
||||||
|
return fmt.Errorf("ask_user 动作不应携带 abort")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
case ExecuteActionConfirm:
|
case ExecuteActionConfirm:
|
||||||
if d.ToolCall == nil {
|
if d.ToolCall == nil {
|
||||||
return fmt.Errorf("confirm 动作必须携带待确认的 tool_call")
|
return fmt.Errorf("confirm 动作必须携带待确认的 tool_call")
|
||||||
}
|
}
|
||||||
|
if d.Abort != nil {
|
||||||
|
return fmt.Errorf("confirm 动作不应同时携带 abort")
|
||||||
|
}
|
||||||
return d.ToolCall.Validate()
|
return d.ToolCall.Validate()
|
||||||
case ExecuteActionNextPlan, ExecuteActionDone:
|
case ExecuteActionNextPlan, ExecuteActionDone:
|
||||||
if d.ToolCall != nil {
|
if d.ToolCall != nil {
|
||||||
return fmt.Errorf("%s 动作不应携带 tool_call", d.Action)
|
return fmt.Errorf("%s 动作不应携带 tool_call", d.Action)
|
||||||
}
|
}
|
||||||
|
if d.Abort != nil {
|
||||||
|
return fmt.Errorf("%s 动作不应携带 abort", d.Action)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
|
case ExecuteActionAbort:
|
||||||
|
if d.ToolCall != nil {
|
||||||
|
return fmt.Errorf("abort 动作不应携带 tool_call")
|
||||||
|
}
|
||||||
|
if d.Abort == nil {
|
||||||
|
return fmt.Errorf("abort 动作必须携带 abort 字段")
|
||||||
|
}
|
||||||
|
return d.Abort.Validate()
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("未知 execute action: %s", d.Action)
|
return fmt.Errorf("未知 execute action: %s", d.Action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AbortIntent 表示 execute 阶段声明的正式终止意图。
|
||||||
|
//
|
||||||
|
// 说明:
|
||||||
|
// 1. code 是稳定机器码,便于后续前端/埋点识别终止类型;
|
||||||
|
// 2. user_message 是最终给用户看的收口文案;
|
||||||
|
// 3. internal_reason 只用于日志排查,允许更技术化。
|
||||||
|
type AbortIntent struct {
|
||||||
|
Code string `json:"code,omitempty"`
|
||||||
|
UserMessage string `json:"user_message"`
|
||||||
|
InternalReason string `json:"internal_reason,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize 清洗终止意图中的稳定字段。
|
||||||
|
func (a *AbortIntent) Normalize() {
|
||||||
|
if a == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.Code = strings.TrimSpace(a.Code)
|
||||||
|
a.UserMessage = strings.TrimSpace(a.UserMessage)
|
||||||
|
a.InternalReason = strings.TrimSpace(a.InternalReason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate 校验终止意图的最小可用性。
|
||||||
|
func (a *AbortIntent) Validate() error {
|
||||||
|
if a == nil {
|
||||||
|
return fmt.Errorf("abort 不能为空")
|
||||||
|
}
|
||||||
|
a.Normalize()
|
||||||
|
if a.UserMessage == "" {
|
||||||
|
return fmt.Errorf("abort.user_message 不能为空")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ToolCallIntent 表示 execute 阶段申报的工具调用意图。
|
// ToolCallIntent 表示 execute 阶段申报的工具调用意图。
|
||||||
//
|
//
|
||||||
// 设计目的:
|
// 设计目的:
|
||||||
|
|||||||
@@ -257,9 +257,13 @@ func (n *AgentNodes) Deliver(ctx context.Context, st *newagentmodel.AgentGraphSt
|
|||||||
// 中断(confirm/ask_user)路径不写,避免把中间态暴露给前端。
|
// 中断(confirm/ask_user)路径不写,避免把中间态暴露给前端。
|
||||||
if st.Deps.WriteSchedulePreview != nil && st.ScheduleState != nil {
|
if st.Deps.WriteSchedulePreview != nil && st.ScheduleState != nil {
|
||||||
flowState := st.EnsureFlowState()
|
flowState := st.EnsureFlowState()
|
||||||
if err := st.Deps.WriteSchedulePreview(ctx, st.ScheduleState, flowState.UserID, flowState.ConversationID, flowState.TaskClassIDs); err != nil {
|
if flowState != nil && flowState.IsCompleted() {
|
||||||
// 写缓存失败不阻断主流程,降级为仅 log。
|
if err := st.Deps.WriteSchedulePreview(ctx, st.ScheduleState, flowState.UserID, flowState.ConversationID, flowState.TaskClassIDs); err != nil {
|
||||||
log.Printf("[WARN] deliver: 写入排程预览缓存失败 chat=%s: %v", flowState.ConversationID, err)
|
// 写缓存失败不阻断主流程,降级为仅 log。
|
||||||
|
log.Printf("[WARN] deliver: 写入排程预览缓存失败 chat=%s: %v", flowState.ConversationID, err)
|
||||||
|
}
|
||||||
|
} else if flowState != nil {
|
||||||
|
log.Printf("[DEBUG] deliver: skip schedule preview chat=%s terminal_status=%s", flowState.ConversationID, flowState.TerminalStatus())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,12 +85,10 @@ func RunDeliverNode(ctx context.Context, input DeliverNodeInput) error {
|
|||||||
deliverStatusBlockID,
|
deliverStatusBlockID,
|
||||||
deliverStageName,
|
deliverStageName,
|
||||||
"done",
|
"done",
|
||||||
"任务已完成。",
|
"本轮流程已结束。",
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
||||||
// 5. 标记流程结束。
|
|
||||||
flowState.Done()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +99,15 @@ func generateDeliverSummary(
|
|||||||
flowState *newagentmodel.CommonState,
|
flowState *newagentmodel.CommonState,
|
||||||
conversationContext *newagentmodel.ConversationContext,
|
conversationContext *newagentmodel.ConversationContext,
|
||||||
) string {
|
) string {
|
||||||
|
if flowState != nil {
|
||||||
|
switch {
|
||||||
|
case flowState.IsAborted():
|
||||||
|
return normalizeSpeak(buildAbortSummary(flowState))
|
||||||
|
case flowState.IsExhaustedTerminal():
|
||||||
|
return normalizeSpeak(buildExhaustedSummary(flowState))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if client == nil {
|
if client == nil {
|
||||||
return buildMechanicalSummary(flowState)
|
return buildMechanicalSummary(flowState)
|
||||||
}
|
}
|
||||||
@@ -125,6 +132,38 @@ func generateDeliverSummary(
|
|||||||
return normalizeSpeak(result.Text)
|
return normalizeSpeak(result.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildAbortSummary 生成“流程已终止”的统一交付文案。
|
||||||
|
//
|
||||||
|
// 说明:
|
||||||
|
// 1. 第二轮开始,abort 的用户可见文案由终止方提前写入 CommonState;
|
||||||
|
// 2. deliver 不再重新猜测或改写业务异常,只做最终收口;
|
||||||
|
// 3. 若历史快照缺失 user_message,则回退到一份通用说明,避免前端收到空白结果。
|
||||||
|
func buildAbortSummary(state *newagentmodel.CommonState) string {
|
||||||
|
if state == nil || state.TerminalOutcome == nil {
|
||||||
|
return "本轮流程已终止。"
|
||||||
|
}
|
||||||
|
if msg := strings.TrimSpace(state.TerminalOutcome.UserMessage); msg != "" {
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
return "本轮流程已终止,请根据当前提示检查后再继续。"
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildExhaustedSummary 生成“轮次耗尽”的统一收口文案。
|
||||||
|
func buildExhaustedSummary(state *newagentmodel.CommonState) string {
|
||||||
|
if state == nil {
|
||||||
|
return "本轮执行已达到安全轮次上限,当前先停止继续操作。"
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := "本轮执行已达到安全轮次上限,当前先停止继续操作。"
|
||||||
|
if state.TerminalOutcome != nil && strings.TrimSpace(state.TerminalOutcome.UserMessage) != "" {
|
||||||
|
prefix = strings.TrimSpace(state.TerminalOutcome.UserMessage)
|
||||||
|
}
|
||||||
|
if !state.HasPlan() {
|
||||||
|
return prefix
|
||||||
|
}
|
||||||
|
return prefix + "\n\n" + strings.TrimSpace(buildMechanicalSummary(state))
|
||||||
|
}
|
||||||
|
|
||||||
// buildMechanicalSummary 在 LLM 不可用时,机械拼接一份最小可用总结。
|
// buildMechanicalSummary 在 LLM 不可用时,机械拼接一份最小可用总结。
|
||||||
func buildMechanicalSummary(state *newagentmodel.CommonState) string {
|
func buildMechanicalSummary(state *newagentmodel.CommonState) string {
|
||||||
if state == nil {
|
if state == nil {
|
||||||
@@ -138,7 +177,7 @@ func buildMechanicalSummary(state *newagentmodel.CommonState) string {
|
|||||||
return "任务流程已结束。"
|
return "任务流程已结束。"
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.Exhausted() {
|
if state.IsExhaustedTerminal() {
|
||||||
sb.WriteString(fmt.Sprintf("任务因执行轮次耗尽提前结束,已完成 %d/%d 步。\n", current, total))
|
sb.WriteString(fmt.Sprintf("任务因执行轮次耗尽提前结束,已完成 %d/%d 步。\n", current, total))
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString("所有计划步骤已执行完毕。\n")
|
sb.WriteString("所有计划步骤已执行完毕。\n")
|
||||||
@@ -153,7 +192,7 @@ func buildMechanicalSummary(state *newagentmodel.CommonState) string {
|
|||||||
sb.WriteString(fmt.Sprintf("%s %s\n", marker, strings.TrimSpace(step.Content)))
|
sb.WriteString(fmt.Sprintf("%s %s\n", marker, strings.TrimSpace(step.Content)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.Exhausted() && current < total {
|
if state.IsExhaustedTerminal() && current < total {
|
||||||
sb.WriteString("\n如需继续完成剩余步骤,可以告诉我继续。")
|
sb.WriteString("\n如需继续完成剩余步骤,可以告诉我继续。")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,8 +131,14 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
|
|||||||
|
|
||||||
// 4. 消耗一轮预算,并检查是否耗尽。
|
// 4. 消耗一轮预算,并检查是否耗尽。
|
||||||
if !flowState.NextRound() {
|
if !flowState.NextRound() {
|
||||||
// 轮次耗尽,强制进入交付阶段。
|
// 1. 轮次耗尽属于安全边界触发的被动停止,不应伪装成“正常完成”。
|
||||||
flowState.Done()
|
// 2. 这里统一写入 exhausted 终止结果,让 deliver 阶段按未完成收口。
|
||||||
|
// 3. 后续 graph 只需围绕 CommonState 的终止结果路由,无需再猜测原因。
|
||||||
|
flowState.Exhaust(
|
||||||
|
executeStageName,
|
||||||
|
"本轮执行已达到安全轮次上限,当前先停止继续操作。如需继续,我可以在你确认后接着处理剩余步骤。",
|
||||||
|
"execute rounds exhausted before task completion",
|
||||||
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +238,7 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
|
|||||||
conversationContext,
|
conversationContext,
|
||||||
rawText,
|
rawText,
|
||||||
fmt.Sprintf("你的执行决策不合法:%s", err.Error()),
|
fmt.Sprintf("你的执行决策不合法:%s", err.Error()),
|
||||||
"合法的 action 包括:continue(继续当前步骤)、ask_user(追问用户)、confirm(写操作确认)、next_plan(推进到下一步)、done(任务完成)。",
|
"合法的 action 包括:continue(继续当前步骤)、ask_user(追问用户)、confirm(写操作确认)、next_plan(推进到下一步)、done(任务完成)、abort(正式终止本轮流程)。",
|
||||||
)
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -279,8 +285,9 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
|
|||||||
if speakText != "" {
|
if speakText != "" {
|
||||||
isConfirmWithCard := decision.Action == newagentmodel.ExecuteActionConfirm && !input.AlwaysExecute
|
isConfirmWithCard := decision.Action == newagentmodel.ExecuteActionConfirm && !input.AlwaysExecute
|
||||||
isAskUser := decision.Action == newagentmodel.ExecuteActionAskUser
|
isAskUser := decision.Action == newagentmodel.ExecuteActionAskUser
|
||||||
|
isAbort := decision.Action == newagentmodel.ExecuteActionAbort
|
||||||
|
|
||||||
if !isConfirmWithCard && !isAskUser {
|
if !isConfirmWithCard && !isAskUser && !isAbort {
|
||||||
// 推流给前端
|
// 推流给前端
|
||||||
if err := emitter.EmitPseudoAssistantText(
|
if err := emitter.EmitPseudoAssistantText(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -292,11 +299,15 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
|
|||||||
return fmt.Errorf("执行文案推送失败: %w", err)
|
return fmt.Errorf("执行文案推送失败: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 始终写入历史(confirm 卡片场景下也写,保证上下文连续)
|
// 1. confirm / ask_user 的 speak 仍要写入历史,避免下一轮 LLM 丢失自己的执行上下文。
|
||||||
conversationContext.AppendHistory(&schema.Message{
|
// 2. abort 不在这里写历史,避免先输出中间 speak,再在 deliver 收到第二份终止文案。
|
||||||
Role: schema.Assistant,
|
// 3. ask_user 只是不在这里伪流式推送,真正的对外展示仍由 PendingInteraction.DisplayText 承担。
|
||||||
Content: speakText,
|
if !isAbort {
|
||||||
})
|
conversationContext.AppendHistory(&schema.Message{
|
||||||
|
Role: schema.Assistant,
|
||||||
|
Content: speakText,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. 按 LLM 决策执行动作,后端信任 LLM 判断,不做语义校验。
|
// 7. 按 LLM 决策执行动作,后端信任 LLM 判断,不做语义校验。
|
||||||
@@ -347,6 +358,12 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
|
|||||||
flowState.Done()
|
flowState.Done()
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
case newagentmodel.ExecuteActionAbort:
|
||||||
|
// 1. abort 是 execute 层的正式终止协议。
|
||||||
|
// 2. 这里只负责把终止结果写入 CommonState,真正的用户收口统一交给 deliver。
|
||||||
|
// 3. 这样 rough_build / execute / 后续其他 stop 条件都能走同一套图内收口。
|
||||||
|
return handleExecuteActionAbort(decision, flowState)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// 1. LLM 输出了不支持的 action,不应直接报错终止,而应给它修正机会。
|
// 1. LLM 输出了不支持的 action,不应直接报错终止,而应给它修正机会。
|
||||||
// 2. 使用通用修正函数追加错误反馈,让 Graph 继续循环。
|
// 2. 使用通用修正函数追加错误反馈,让 Graph 继续循环。
|
||||||
@@ -359,7 +376,7 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
|
|||||||
conversationContext,
|
conversationContext,
|
||||||
llmOutput,
|
llmOutput,
|
||||||
fmt.Sprintf("你输出的 action \"%s\" 不是合法的执行动作。", decision.Action),
|
fmt.Sprintf("你输出的 action \"%s\" 不是合法的执行动作。", decision.Action),
|
||||||
"合法的 action 包括:continue(继续当前步骤)、ask_user(追问用户)、next_plan(推进到下一步)、done(任务完成)。",
|
"合法的 action 包括:continue(继续当前步骤)、ask_user(追问用户)、confirm(写操作确认)、next_plan(推进到下一步)、done(任务完成)、abort(正式终止本轮流程)。",
|
||||||
)
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -441,6 +458,37 @@ func handleExecuteActionConfirm(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleExecuteActionAbort 处理 execute 阶段声明的正式终止请求。
|
||||||
|
//
|
||||||
|
// 职责边界:
|
||||||
|
// 1. 这里只负责把 abort 协议落到 CommonState;
|
||||||
|
// 2. 不直接向用户发最终文案,避免和 deliver 收口重复;
|
||||||
|
// 3. 若模型未提供 internal_reason,则回退到 decision.Reason 作为排查信息。
|
||||||
|
func handleExecuteActionAbort(
|
||||||
|
decision *newagentmodel.ExecuteDecision,
|
||||||
|
flowState *newagentmodel.CommonState,
|
||||||
|
) error {
|
||||||
|
if decision == nil || decision.Abort == nil {
|
||||||
|
return fmt.Errorf("abort 动作缺少终止信息")
|
||||||
|
}
|
||||||
|
if flowState == nil {
|
||||||
|
return fmt.Errorf("abort 动作缺少流程状态")
|
||||||
|
}
|
||||||
|
|
||||||
|
internalReason := strings.TrimSpace(decision.Abort.InternalReason)
|
||||||
|
if internalReason == "" {
|
||||||
|
internalReason = strings.TrimSpace(decision.Reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
flowState.Abort(
|
||||||
|
executeStageName,
|
||||||
|
decision.Abort.Code,
|
||||||
|
decision.Abort.UserMessage,
|
||||||
|
internalReason,
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// executeToolCall 执行工具调用并记录证据。
|
// executeToolCall 执行工具调用并记录证据。
|
||||||
//
|
//
|
||||||
// 职责边界:
|
// 职责边界:
|
||||||
@@ -698,7 +746,7 @@ func summarizeScheduleStateForDebug(state *newagenttools.ScheduleState) string {
|
|||||||
|
|
||||||
total := len(state.Tasks)
|
total := len(state.Tasks)
|
||||||
pendingNoSlot := 0
|
pendingNoSlot := 0
|
||||||
pendingWithSlot := 0
|
suggestedTotal := 0
|
||||||
existingTotal := 0
|
existingTotal := 0
|
||||||
taskItemWithSlot := 0
|
taskItemWithSlot := 0
|
||||||
eventWithSlot := 0
|
eventWithSlot := 0
|
||||||
@@ -707,14 +755,12 @@ func summarizeScheduleStateForDebug(state *newagenttools.ScheduleState) string {
|
|||||||
t := &state.Tasks[i]
|
t := &state.Tasks[i]
|
||||||
hasSlot := len(t.Slots) > 0
|
hasSlot := len(t.Slots) > 0
|
||||||
|
|
||||||
switch t.Status {
|
switch {
|
||||||
case "pending":
|
case newagenttools.IsPendingTask(*t):
|
||||||
if hasSlot {
|
pendingNoSlot++
|
||||||
pendingWithSlot++
|
case newagenttools.IsSuggestedTask(*t):
|
||||||
} else {
|
suggestedTotal++
|
||||||
pendingNoSlot++
|
case newagenttools.IsExistingTask(*t):
|
||||||
}
|
|
||||||
case "existing":
|
|
||||||
existingTotal++
|
existingTotal++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -729,10 +775,10 @@ func summarizeScheduleStateForDebug(state *newagenttools.ScheduleState) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"tasks=%d pending_no_slot=%d pending_with_slot=%d existing=%d task_item_with_slot=%d event_with_slot=%d",
|
"tasks=%d pending=%d suggested=%d existing=%d task_item_with_slot=%d event_with_slot=%d",
|
||||||
total,
|
total,
|
||||||
pendingNoSlot,
|
pendingNoSlot,
|
||||||
pendingWithSlot,
|
suggestedTotal,
|
||||||
existingTotal,
|
existingTotal,
|
||||||
taskItemWithSlot,
|
taskItemWithSlot,
|
||||||
eventWithSlot,
|
eventWithSlot,
|
||||||
|
|||||||
@@ -76,6 +76,18 @@ func renderStateSummary(state *newagentmodel.CommonState) string {
|
|||||||
|
|
||||||
sb.WriteString(fmt.Sprintf("当前阶段:%s\n", state.Phase))
|
sb.WriteString(fmt.Sprintf("当前阶段:%s\n", state.Phase))
|
||||||
sb.WriteString(fmt.Sprintf("当前轮次:%d/%d\n", state.RoundUsed, state.MaxRounds))
|
sb.WriteString(fmt.Sprintf("当前轮次:%d/%d\n", state.RoundUsed, state.MaxRounds))
|
||||||
|
if state.HasTerminalOutcome() && state.TerminalOutcome != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("终止结果:%s\n", state.TerminalOutcome.Status))
|
||||||
|
if strings.TrimSpace(state.TerminalOutcome.Stage) != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf("终止阶段:%s\n", state.TerminalOutcome.Stage))
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(state.TerminalOutcome.Code) != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf("终止代码:%s\n", state.TerminalOutcome.Code))
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(state.TerminalOutcome.UserMessage) != "" {
|
||||||
|
sb.WriteString(fmt.Sprintf("终止说明:%s\n", state.TerminalOutcome.UserMessage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !state.HasPlan() {
|
if !state.HasPlan() {
|
||||||
sb.WriteString("当前完整 plan:暂无。\n")
|
sb.WriteString("当前完整 plan:暂无。\n")
|
||||||
|
|||||||
@@ -167,6 +167,125 @@ func BuildExecuteReActContractText() string {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildExecuteDecisionContractTextV2 返回第二轮 abort 协议补齐后的执行输出契约。
|
||||||
|
func BuildExecuteDecisionContractTextV2() string {
|
||||||
|
return strings.TrimSpace(fmt.Sprintf(`
|
||||||
|
输出协议(严格 JSON):
|
||||||
|
- speak:给用户看的话;若 action=%s,通常留空,最终收口交给 deliver
|
||||||
|
- action:只能是 %s / %s / %s / %s / %s / %s
|
||||||
|
- reason:给后端和日志看的简短说明
|
||||||
|
- goal_check:输出 %s 或 %s 时必填,对照 done_when 逐条验证
|
||||||
|
- tool_call:输出 %s 时可附带写工具意图(需 confirm),输出 %s 时可附带读工具调用
|
||||||
|
- abort:仅在输出 %s 时必填,格式为 {"code":"稳定机器码","user_message":"给用户看的终止说明","internal_reason":"给日志看的原因"}
|
||||||
|
- tool_call 与 abort 互斥,禁止同时出现
|
||||||
|
|
||||||
|
合法示例:
|
||||||
|
{
|
||||||
|
"speak": "我来查一下本周的安排。",
|
||||||
|
"action": "%s",
|
||||||
|
"reason": "需要先调用 get_overview 获取当前数据",
|
||||||
|
"tool_call": {
|
||||||
|
"name": "get_overview",
|
||||||
|
"arguments": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"speak": "查询完成。",
|
||||||
|
"action": "%s",
|
||||||
|
"reason": "已拿到当前周课程列表",
|
||||||
|
"goal_check": "已通过 get_overview 确认本周课程列表,满足完成条件"
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"speak": "",
|
||||||
|
"action": "%s",
|
||||||
|
"reason": "粗排结果存在业务异常,当前不应继续微调",
|
||||||
|
"abort": {
|
||||||
|
"code": "rough_build_pending_remaining",
|
||||||
|
"user_message": "初始排课方案构建异常:粗排后仍有任务未获得初始落位。本轮先终止,请检查粗排算法或任务数据。",
|
||||||
|
"internal_reason": "pending tasks remain after rough build"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
newagentmodel.ExecuteActionAbort,
|
||||||
|
newagentmodel.ExecuteActionContinue,
|
||||||
|
newagentmodel.ExecuteActionAskUser,
|
||||||
|
newagentmodel.ExecuteActionConfirm,
|
||||||
|
newagentmodel.ExecuteActionNextPlan,
|
||||||
|
newagentmodel.ExecuteActionDone,
|
||||||
|
newagentmodel.ExecuteActionAbort,
|
||||||
|
newagentmodel.ExecuteActionNextPlan,
|
||||||
|
newagentmodel.ExecuteActionDone,
|
||||||
|
newagentmodel.ExecuteActionConfirm,
|
||||||
|
newagentmodel.ExecuteActionContinue,
|
||||||
|
newagentmodel.ExecuteActionAbort,
|
||||||
|
newagentmodel.ExecuteActionContinue,
|
||||||
|
newagentmodel.ExecuteActionNextPlan,
|
||||||
|
newagentmodel.ExecuteActionAbort,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildExecuteReActContractTextV2 返回第二轮 abort 协议补齐后的 ReAct 输出契约。
|
||||||
|
func BuildExecuteReActContractTextV2() string {
|
||||||
|
return strings.TrimSpace(fmt.Sprintf(`
|
||||||
|
输出协议(严格 JSON):
|
||||||
|
- speak:给用户看的话(可以是分析结果、中间进展、或最终回复);若 action=%s,通常留空
|
||||||
|
- action:只能是 %s / %s / %s / %s / %s
|
||||||
|
- reason:给后端和日志看的简短说明
|
||||||
|
- goal_check:输出 %s 时必填,总结任务完成证据
|
||||||
|
- tool_call:输出 %s 时可附带写工具意图(需 confirm),输出 %s 时可附带读工具调用
|
||||||
|
- abort:仅在输出 %s 时必填,格式为 {"code":"稳定机器码","user_message":"给用户看的终止说明","internal_reason":"给日志看的原因"}
|
||||||
|
- tool_call 与 abort 互斥,禁止同时出现
|
||||||
|
|
||||||
|
合法示例:
|
||||||
|
{
|
||||||
|
"speak": "我来查一下今天的安排。",
|
||||||
|
"action": "%s",
|
||||||
|
"reason": "需要调用 get_overview 查询",
|
||||||
|
"tool_call": {
|
||||||
|
"name": "get_overview",
|
||||||
|
"arguments": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"speak": "已将概率论移到周三第1-2节。",
|
||||||
|
"action": "%s",
|
||||||
|
"reason": "用户要求移动课程,写操作需确认",
|
||||||
|
"tool_call": {
|
||||||
|
"name": "move",
|
||||||
|
"arguments": {"task_id": 5, "new_day": 3, "new_slot_start": 1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"speak": "",
|
||||||
|
"action": "%s",
|
||||||
|
"reason": "当前流程不应继续执行,需要正式终止",
|
||||||
|
"abort": {
|
||||||
|
"code": "domain_abort",
|
||||||
|
"user_message": "当前流程无法继续执行,本轮先终止。",
|
||||||
|
"internal_reason": "execute declared abort"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
newagentmodel.ExecuteActionAbort,
|
||||||
|
newagentmodel.ExecuteActionContinue,
|
||||||
|
newagentmodel.ExecuteActionAskUser,
|
||||||
|
newagentmodel.ExecuteActionConfirm,
|
||||||
|
newagentmodel.ExecuteActionDone,
|
||||||
|
newagentmodel.ExecuteActionAbort,
|
||||||
|
newagentmodel.ExecuteActionDone,
|
||||||
|
newagentmodel.ExecuteActionConfirm,
|
||||||
|
newagentmodel.ExecuteActionContinue,
|
||||||
|
newagentmodel.ExecuteActionAbort,
|
||||||
|
newagentmodel.ExecuteActionContinue,
|
||||||
|
newagentmodel.ExecuteActionConfirm,
|
||||||
|
newagentmodel.ExecuteActionAbort,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// BuildExecuteMessages 组装执行阶段的 messages。
|
// BuildExecuteMessages 组装执行阶段的 messages。
|
||||||
func BuildExecuteMessages(state *newagentmodel.CommonState, ctx *newagentmodel.ConversationContext) []*schema.Message {
|
func BuildExecuteMessages(state *newagentmodel.CommonState, ctx *newagentmodel.ConversationContext) []*schema.Message {
|
||||||
if state != nil && state.HasPlan() {
|
if state != nil && state.HasPlan() {
|
||||||
@@ -215,9 +334,10 @@ func BuildExecuteUserPrompt(state *newagentmodel.CommonState) string {
|
|||||||
sb.WriteString("3. 若当前步骤已完成,请输出 action=next_plan,并填写 goal_check 说明完成依据。\n")
|
sb.WriteString("3. 若当前步骤已完成,请输出 action=next_plan,并填写 goal_check 说明完成依据。\n")
|
||||||
sb.WriteString("4. 若整个任务已完成,请输出 action=done,并填写 goal_check 总结整体证据。\n")
|
sb.WriteString("4. 若整个任务已完成,请输出 action=done,并填写 goal_check 总结整体证据。\n")
|
||||||
sb.WriteString("5. 若缺少关键用户信息且现有上下文无法补足,请输出 action=ask_user。\n")
|
sb.WriteString("5. 若缺少关键用户信息且现有上下文无法补足,请输出 action=ask_user。\n")
|
||||||
sb.WriteString("6. 输出 next_plan 或 done 时,goal_check 不能为空,必须对照 done_when 逐条验证。\n")
|
sb.WriteString("6. 若你判断当前流程应正式终止,而不是继续执行、追问或写工具,请输出 action=abort,并附带 abort 字段。\n")
|
||||||
|
sb.WriteString("7. 输出 next_plan 或 done 时,goal_check 不能为空,必须对照 done_when 逐条验证。\n")
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
sb.WriteString(BuildExecuteDecisionContractText())
|
sb.WriteString(BuildExecuteDecisionContractTextV2())
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString("当前 plan 已存在,但当前步骤索引无效;请不要擅自执行其他步骤。\n")
|
sb.WriteString("当前 plan 已存在,但当前步骤索引无效;请不要擅自执行其他步骤。\n")
|
||||||
}
|
}
|
||||||
@@ -248,9 +368,10 @@ func BuildExecuteReActUserPrompt(state *newagentmodel.CommonState) string {
|
|||||||
sb.WriteString("- 需要查询/读取数据 → action=continue + tool_call(读工具)\n")
|
sb.WriteString("- 需要查询/读取数据 → action=continue + tool_call(读工具)\n")
|
||||||
sb.WriteString("- 需要修改/写入数据 → action=confirm + tool_call(写工具,需用户确认)\n")
|
sb.WriteString("- 需要修改/写入数据 → action=confirm + tool_call(写工具,需用户确认)\n")
|
||||||
sb.WriteString("- 缺少关键信息 → action=ask_user\n")
|
sb.WriteString("- 缺少关键信息 → action=ask_user\n")
|
||||||
sb.WriteString("- 任务完成 → action=done + goal_check\n\n")
|
sb.WriteString("- 任务完成 → action=done + goal_check\n")
|
||||||
|
sb.WriteString("- 当前流程应正式终止 → action=abort + abort\n\n")
|
||||||
|
|
||||||
sb.WriteString(BuildExecuteReActContractText())
|
sb.WriteString(BuildExecuteReActContractTextV2())
|
||||||
|
|
||||||
return strings.TrimSpace(sb.String())
|
return strings.TrimSpace(sb.String())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user