From 48ad418f06bf715b4ec5c665635f1a0b9ceb907c Mon Sep 17 00:00:00 2001 From: mlogclub Date: Tue, 14 Apr 2026 11:45:55 +0800 Subject: [PATCH] feat: enhance handoff reason extraction and improve trace data handling in run log service --- internal/ai/runtime/reply_helpers_test.go | 18 +++++++ internal/ai/runtime/reply_runlog_service.go | 52 +++++++++++---------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/internal/ai/runtime/reply_helpers_test.go b/internal/ai/runtime/reply_helpers_test.go index 803b381..ed1961e 100644 --- a/internal/ai/runtime/reply_helpers_test.go +++ b/internal/ai/runtime/reply_helpers_test.go @@ -85,6 +85,24 @@ func TestGraphPlanReason(t *testing.T) { } } +func TestExtractHandoffReason(t *testing.T) { + summary := &Summary{ + TraceData: `{ + "graphTools": { + "items": [ + { + "toolCode":"` + toolx.GraphHandoffConversation.Code + `", + "arguments":{"reason":" 用户明确要求人工处理 "} + } + ] + } + }`, + } + if got := extractHandoffReason(summary); got != "用户明确要求人工处理" { + t.Fatalf("unexpected handoff reason: %q", got) + } +} + type fakeErr string func (e fakeErr) Error() string { diff --git a/internal/ai/runtime/reply_runlog_service.go b/internal/ai/runtime/reply_runlog_service.go index a074a7f..73cbbaf 100644 --- a/internal/ai/runtime/reply_runlog_service.go +++ b/internal/ai/runtime/reply_runlog_service.go @@ -208,10 +208,14 @@ func extractToolSearchTrace(summary *Summary) string { return "" } trace := parseRuntimeTraceData(summary.TraceData) - if len(trace.ToolSearch.Items) == 0 || len(trace.ToolSearch.Raw) == 0 { + if len(trace.ToolSearch.Items) == 0 { return "" } - return string(trace.ToolSearch.Raw) + buf, err := json.Marshal(trace.ToolSearch) + if err != nil { + return "" + } + return string(buf) } func extractGraphToolTrace(summary *Summary) string { @@ -219,10 +223,14 @@ func extractGraphToolTrace(summary *Summary) string { return "" } trace := parseRuntimeTraceData(summary.TraceData) - if len(trace.GraphTools.Items) == 0 || len(trace.GraphTools.Raw) == 0 { + if len(trace.GraphTools.Items) == 0 { return "" } - return string(trace.GraphTools.Raw) + buf, err := json.Marshal(trace.GraphTools) + if err != nil { + return "" + } + return string(buf) } func firstToolSearchTargetToolCode(summary *Summary) string { @@ -262,8 +270,11 @@ func extractHandoffReason(summary *Summary) string { if len(item.Arguments) == 0 { return "" } - reason, _ := item.Arguments["reason"].(string) - return strings.TrimSpace(reason) + var args runtimeTraceHandoffArguments + if err := json.Unmarshal(item.Arguments, &args); err != nil { + return "" + } + return strings.TrimSpace(args.Reason) } return "" } @@ -302,41 +313,34 @@ func graphPlanReason(summary *Summary) string { type runtimeTraceProjection struct { ToolSearch struct { - Raw json.RawMessage `json:"-"` Items []struct { TargetToolCode string `json:"targetToolCode"` CandidateToolCodes []string `json:"candidateToolCodes"` } `json:"items"` } `json:"toolSearch"` GraphTools struct { - Raw json.RawMessage `json:"-"` Items []struct { - ToolCode string `json:"toolCode"` - Arguments map[string]any `json:"arguments"` - RecommendedAction string `json:"recommendedAction"` - RiskLevel string `json:"riskLevel"` - TicketDraftReady bool `json:"ticketDraftReady"` + ToolCode string `json:"toolCode"` + Arguments json.RawMessage `json:"arguments"` + RecommendedAction string `json:"recommendedAction"` + RiskLevel string `json:"riskLevel"` + TicketDraftReady bool `json:"ticketDraftReady"` } `json:"items"` } `json:"graphTools"` } +type runtimeTraceHandoffArguments struct { + Reason string `json:"reason"` +} + func parseRuntimeTraceData(raw string) runtimeTraceProjection { raw = strings.TrimSpace(raw) if raw == "" { return runtimeTraceProjection{} } - var payload map[string]json.RawMessage - if err := json.Unmarshal([]byte(raw), &payload); err != nil { - return runtimeTraceProjection{} - } var trace runtimeTraceProjection - if toolSearchRaw, ok := payload["toolSearch"]; ok && len(toolSearchRaw) > 0 { - trace.ToolSearch.Raw = append(json.RawMessage(nil), toolSearchRaw...) - _ = json.Unmarshal(toolSearchRaw, &trace.ToolSearch) - } - if graphToolsRaw, ok := payload["graphTools"]; ok && len(graphToolsRaw) > 0 { - trace.GraphTools.Raw = append(json.RawMessage(nil), graphToolsRaw...) - _ = json.Unmarshal(graphToolsRaw, &trace.GraphTools) + if err := json.Unmarshal([]byte(raw), &trace); err != nil { + return runtimeTraceProjection{} } return trace }