Files
smartmate/backend/services/agent/prompt/deliver_window.go
Losita d7184b776b Version: 0.9.75.dev.260505
后端:
1.收口阶段 6 agent 结构迁移,将 newAgent 内核与 agentsvc 编排层迁入 services/agent
- 切换 Agent 启动装配与 HTTP handler 直连 agent sv,移除旧 service agent bridge
- 补齐 Agent 对 memory、task、task-class、schedule 的 RPC 适配与契约字段
- 扩展 schedule、task、task-class RPC/contract 支撑 Agent 查询、写入与 provider 切流
- 更新迁移文档、README 与相关注释,明确 agent 当前切流点和剩余 memory 迁移面
2026-05-05 16:00:57 +08:00

104 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package agentprompt
import (
"fmt"
"strings"
agentmodel "github.com/LoveLosita/smartflow/backend/services/agent/model"
"github.com/cloudwego/eino/schema"
)
const deliverHistoryKindExecuteLoopClosed = "execute_loop_closed"
// sliceHistoryAfterLastExecuteLoopClosed 基于最后一个 execute_loop_closed 标记切出当前活跃窗口。
//
// 步骤化说明:
// 1. 先读取完整 history 快照,避免直接在 ConversationContext 原地切片,减少后续调用方误改底层数组的风险;
// 2. 从后往前找最后一个 execute_loop_closed确保拿到的是“最近一次已正常收口”的边界
// 3. 命中边界后只返回边界之后的消息,这样 deliver 看到的就是当前活跃轮次;
// 4. 若完全没有边界,说明会话尚未形成稳定闭环,此时退回全量 history避免误丢当前活跃上下文。
func sliceHistoryAfterLastExecuteLoopClosed(ctx *agentmodel.ConversationContext) []*schema.Message {
if ctx == nil {
return nil
}
history := ctx.HistorySnapshot()
if len(history) == 0 {
return nil
}
cut := -1
for i := len(history) - 1; i >= 0; i-- {
if isDeliverExecuteLoopClosedMarker(history[i]) {
cut = i
break
}
}
if cut < 0 {
return history
}
if cut+1 >= len(history) {
return nil
}
return history[cut+1:]
}
// isDeliverExecuteLoopClosedMarker 判断一条历史消息是否为 execute loop 正常收口边界。
//
// 职责边界:
// 1. 这里只识别 prompt 层真正关心的 execute_loop_closed 标记;
// 2. 不负责推断其他中断/恢复语义,避免把 confirm/ask_user 等同一轮过程误判成新边界;
// 3. 若消息结构不完整,则统一按“非边界”处理,保证切窗策略保守可回退。
func isDeliverExecuteLoopClosedMarker(msg *schema.Message) bool {
if msg == nil || msg.Extra == nil {
return false
}
kind, ok := msg.Extra[executeHistoryKindKey].(string)
if !ok {
return false
}
return strings.TrimSpace(kind) == deliverHistoryKindExecuteLoopClosed
}
// buildDeliverExecuteWindow 基于当前活跃 history 窗口渲染 deliver 节点要看的执行事实视图。
//
// 步骤化说明:
// 1. 先按 execute_loop_closed 切掉旧轮次,只保留当前仍活跃的执行窗口;
// 2. 再分别抽取“本轮真实对话流”和“本轮 ReAct 工具事实链”,避免 deliver 回看旧 deliver 总结;
// 3. 若本轮还没有工具调用,也要明确告诉模型“当前无工具事实”,避免它擅自脑补;
// 4. 整段文本只服务 deliver.msg2不改变四段式骨架也不回写任何状态。
func buildDeliverExecuteWindow(ctx *agentmodel.ConversationContext) string {
lines := []string{"本轮 execute 窗口:"}
historyWindow := sliceHistoryAfterLastExecuteLoopClosed(ctx)
if len(historyWindow) == 0 {
lines = append(lines, "- 当前没有可用的本轮执行窗口,请仅依据结果态工作区诚实收口。")
return strings.Join(lines, "\n")
}
turns := collectExecuteConversationTurns(historyWindow)
if len(turns) == 0 {
lines = append(lines, "- 本轮对话流:暂无。")
} else {
lines = append(lines, "本轮对话流:")
for _, turn := range turns {
lines = append(lines, fmt.Sprintf("- %s: %q", turn.Role, turn.Content))
}
}
loops := collectExecuteLoopRecords(historyWindow)
if len(loops) == 0 {
lines = append(lines, "- 本轮 ReAct 记录:暂无工具调用。")
return strings.Join(lines, "\n")
}
lines = append(lines, "本轮 ReAct 记录:")
for i, loop := range loops {
lines = append(lines, fmt.Sprintf("%d. thought/reason%s", i+1, loop.Thought))
lines = append(lines, fmt.Sprintf(" tool_call%s", renderExecuteToolCallText(loop.ToolName, loop.ToolArgs)))
lines = append(lines, fmt.Sprintf(" observation%s", loop.Observation))
}
return strings.Join(lines, "\n")
}