后端: 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 迁移面
122 lines
3.5 KiB
Go
122 lines
3.5 KiB
Go
package agentnode
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"log"
|
||
"strings"
|
||
|
||
agentmodel "github.com/LoveLosita/smartflow/backend/services/agent/model"
|
||
"github.com/cloudwego/eino/schema"
|
||
)
|
||
|
||
// logNodeLLMContext 将某个节点即将送入 LLM 的完整消息上下文按统一格式打印到日志。
|
||
//
|
||
// 步骤化说明:
|
||
// 1. 统一输出 stage / phase / chat / round,方便按一次请求内的多次 LLM 调用串联排查;
|
||
// 2. 完整展开 messages,不做截断,保证问题复现时能直接对照 prompt 组装结果;
|
||
// 3. 该函数只负责调试日志,不参与任何业务判断,也不修改上下文内容。
|
||
func logNodeLLMContext(
|
||
stage string,
|
||
phase string,
|
||
flowState *agentmodel.CommonState,
|
||
messages []*schema.Message,
|
||
) {
|
||
chatID := ""
|
||
roundUsed := 0
|
||
if flowState != nil {
|
||
chatID = flowState.ConversationID
|
||
roundUsed = flowState.RoundUsed
|
||
}
|
||
|
||
log.Printf(
|
||
"[DEBUG] %s LLM context begin phase=%s chat=%s round=%d message_count=%d\n%s\n[DEBUG] %s LLM context end phase=%s chat=%s round=%d",
|
||
stage,
|
||
strings.TrimSpace(phase),
|
||
chatID,
|
||
roundUsed,
|
||
len(messages),
|
||
formatLLMMessagesForDebug(messages),
|
||
stage,
|
||
strings.TrimSpace(phase),
|
||
chatID,
|
||
roundUsed,
|
||
)
|
||
}
|
||
|
||
// formatLLMMessagesForDebug 将本轮送入 LLM 的完整消息上下文展开成可读多行日志。
|
||
//
|
||
// 说明:
|
||
// 1. 按消息索引逐条输出,便于和上游上下文构造步骤逐项对齐;
|
||
// 2. 完整输出 content / reasoning_content / tool_calls / extra,不做截断;
|
||
// 3. 仅用于调试打点,不参与业务决策。
|
||
func formatLLMMessagesForDebug(messages []*schema.Message) string {
|
||
if len(messages) == 0 {
|
||
return "(empty messages)"
|
||
}
|
||
|
||
var sb strings.Builder
|
||
for i, msg := range messages {
|
||
sb.WriteString(fmt.Sprintf("----- message[%d] -----\n", i))
|
||
if msg == nil {
|
||
sb.WriteString("role: <nil>\n\n")
|
||
continue
|
||
}
|
||
|
||
sb.WriteString(fmt.Sprintf("role: %s\n", msg.Role))
|
||
|
||
if strings.TrimSpace(msg.ToolCallID) != "" {
|
||
sb.WriteString(fmt.Sprintf("tool_call_id: %s\n", msg.ToolCallID))
|
||
}
|
||
if strings.TrimSpace(msg.ToolName) != "" {
|
||
sb.WriteString(fmt.Sprintf("tool_name: %s\n", msg.ToolName))
|
||
}
|
||
|
||
if len(msg.ToolCalls) > 0 {
|
||
sb.WriteString("tool_calls:\n")
|
||
for j, call := range msg.ToolCalls {
|
||
sb.WriteString(fmt.Sprintf(" - [%d] id=%s type=%s function=%s\n", j, call.ID, call.Type, call.Function.Name))
|
||
sb.WriteString(" arguments:\n")
|
||
sb.WriteString(indentMultilineForDebug(call.Function.Arguments, " "))
|
||
sb.WriteString("\n")
|
||
}
|
||
}
|
||
|
||
if strings.TrimSpace(msg.ReasoningContent) != "" {
|
||
sb.WriteString("reasoning_content:\n")
|
||
sb.WriteString(indentMultilineForDebug(msg.ReasoningContent, " "))
|
||
sb.WriteString("\n")
|
||
}
|
||
|
||
sb.WriteString("content:\n")
|
||
sb.WriteString(indentMultilineForDebug(msg.Content, " "))
|
||
sb.WriteString("\n")
|
||
|
||
if len(msg.Extra) > 0 {
|
||
sb.WriteString("extra:\n")
|
||
raw, err := json.MarshalIndent(msg.Extra, "", " ")
|
||
if err != nil {
|
||
sb.WriteString(indentMultilineForDebug("<marshal_error>", " "))
|
||
} else {
|
||
sb.WriteString(indentMultilineForDebug(string(raw), " "))
|
||
}
|
||
sb.WriteString("\n")
|
||
}
|
||
|
||
sb.WriteString("\n")
|
||
}
|
||
return sb.String()
|
||
}
|
||
|
||
// indentMultilineForDebug 为多行文本统一添加前缀缩进,避免日志折行后难以阅读。
|
||
func indentMultilineForDebug(text, prefix string) string {
|
||
if text == "" {
|
||
return prefix + "<empty>"
|
||
}
|
||
lines := strings.Split(text, "\n")
|
||
for i := range lines {
|
||
lines[i] = prefix + lines[i]
|
||
}
|
||
return strings.Join(lines, "\n")
|
||
}
|