// agent_cli_history.go - Agent CLI 历史会话恢复为 LLM 对话上下文 // Logan.Li package controller import ( "encoding/json" "fmt" "ezops/model" "ezops/pkg/agentcli/llm" ) // buildConvHistoryFromMessages 将 DB 消息重建为追问用的 prior 历史(不含 system) // Logan.Li func buildConvHistoryFromMessages(msgs []model.AgentCLIMessage) []llm.Message { var out []llm.Message var pendingThought string var pendingToolCalls []llm.ToolCall var pendingToolCallIDs []string flushAssistant := func() { if pendingThought == "" && len(pendingToolCalls) == 0 { return } out = append(out, llm.Message{ Role: "assistant", Content: pendingThought, ToolCalls: append([]llm.ToolCall(nil), pendingToolCalls...), }) for _, tc := range pendingToolCalls { pendingToolCallIDs = append(pendingToolCallIDs, tc.ID) } pendingThought = "" pendingToolCalls = nil } for _, m := range msgs { switch m.Type { case "user": flushAssistant() out = append(out, llm.Message{Role: "user", Content: m.Content}) case "thought": pendingThought = m.Content case "tool_call": args := validateToolArgsJSON(m.ToolArgs) tcID := fmt.Sprintf("hist-%d", m.ID) pendingToolCalls = append(pendingToolCalls, llm.ToolCall{ ID: tcID, Type: "function", Function: llm.FunctionCall{Name: m.ToolName, Arguments: args}, }) case "tool_result": if pendingThought != "" || len(pendingToolCalls) > 0 { flushAssistant() } toolID := "" if len(pendingToolCallIDs) > 0 { toolID = pendingToolCallIDs[0] pendingToolCallIDs = pendingToolCallIDs[1:] } content := m.Content if content == "" { content = m.ToolOutput } out = append(out, llm.Message{ Role: "tool", Name: m.ToolName, Content: content, ToolCallID: toolID, }) case "done": flushAssistant() out = append(out, llm.Message{Role: "assistant", Content: m.Content}) case "error": flushAssistant() out = append(out, llm.Message{Role: "assistant", Content: "错误: " + m.Content}) default: // 忽略 progress/session 等 // Logan.Li } } flushAssistant() return out } // validateToolArgsJSON 校验工具参数 JSON,非法则回退空对象 // Logan.Li func validateToolArgsJSON(raw string) string { if raw == "" { return "{}" } var tmp map[string]interface{} if json.Unmarshal([]byte(raw), &tmp) != nil { return "{}" } return raw }