From fb42101c101eb7f56ccc20c4f4dbc654cb984291 Mon Sep 17 00:00:00 2001 From: mlogclub Date: Sun, 12 Apr 2026 11:36:36 +0800 Subject: [PATCH] feat: refactor tool result summary handling by introducing BuildReducedToolResultSummary and updating related methods --- .../internal/impl/adapter/mcp_tool_adapter.go | 33 +------- .../impl/adapter/tool_result_reducer.go | 75 +++++++++++++++++++ internal/ai/runtime/tools/tool_search_tool.go | 30 +------- 3 files changed, 78 insertions(+), 60 deletions(-) create mode 100644 internal/ai/runtime/internal/impl/adapter/tool_result_reducer.go diff --git a/internal/ai/runtime/internal/impl/adapter/mcp_tool_adapter.go b/internal/ai/runtime/internal/impl/adapter/mcp_tool_adapter.go index 43b7814..eddc817 100644 --- a/internal/ai/runtime/internal/impl/adapter/mcp_tool_adapter.go +++ b/internal/ai/runtime/internal/impl/adapter/mcp_tool_adapter.go @@ -62,7 +62,7 @@ func (t *MCPTool) InvokableRun(ctx context.Context, argumentsInJSON string, opts if err != nil { return "", err } - return buildToolResultSummary(result), nil + return BuildReducedToolResultSummary(result), nil } func buildToolInfo(definition MCPToolDefinition, metadata *mcps.ToolInfo) *schema.ToolInfo { @@ -146,37 +146,6 @@ func mergeFixedArguments(arguments map[string]any, fixedArgs map[string]string) return ret } -func buildToolResultSummary(result *mcps.ToolCallResult) string { - if result == nil { - return "" - } - lines := make([]string, 0, len(result.Content)+2) - if result.IsError { - lines = append(lines, "tool returned an error") - } - if result.StructuredContent != nil { - if data, err := json.Marshal(result.StructuredContent); err == nil { - lines = append(lines, string(data)) - } - } - for _, item := range result.Content { - switch item.Type { - case "text": - if text := strings.TrimSpace(item.Text); text != "" { - lines = append(lines, text) - } - default: - if item.Data == nil { - continue - } - if data, err := json.Marshal(item.Data); err == nil { - lines = append(lines, string(data)) - } - } - } - return strings.TrimSpace(strings.Join(lines, "\n")) -} - func BuildModelToolName(definition MCPToolDefinition) string { if strings.TrimSpace(definition.ModelName) != "" { return strings.TrimSpace(definition.ModelName) diff --git a/internal/ai/runtime/internal/impl/adapter/tool_result_reducer.go b/internal/ai/runtime/internal/impl/adapter/tool_result_reducer.go new file mode 100644 index 0000000..73885c8 --- /dev/null +++ b/internal/ai/runtime/internal/impl/adapter/tool_result_reducer.go @@ -0,0 +1,75 @@ +package adapter + +import ( + "encoding/json" + "fmt" + "strings" + + "cs-agent/internal/ai/mcps" +) + +const ( + maxToolResultSummaryChars = 4000 + maxToolResultSegments = 12 +) + +// BuildReducedToolResultSummary returns a bounded text summary for MCP tool results. +// It keeps the main payload visible to the model while preventing a single large tool +// response from exhausting too much context. +func BuildReducedToolResultSummary(result *mcps.ToolCallResult) string { + if result == nil { + return "" + } + segments := collectToolResultSegments(result) + if len(segments) == 0 { + return "" + } + text := strings.TrimSpace(strings.Join(segments, "\n")) + if text == "" { + return "" + } + runes := []rune(text) + if len(runes) <= maxToolResultSummaryChars { + return text + } + truncated := strings.TrimSpace(string(runes[:maxToolResultSummaryChars])) + return fmt.Sprintf("%s\n\n[tool result reduced: original_length=%d, kept_length=%d]", truncated, len(runes), maxToolResultSummaryChars) +} + +func collectToolResultSegments(result *mcps.ToolCallResult) []string { + segments := make([]string, 0, len(result.Content)+2) + if result.IsError { + segments = append(segments, "tool returned an error") + } + if result.StructuredContent != nil { + if data, err := json.Marshal(result.StructuredContent); err == nil { + segments = appendNonBlankSegment(segments, string(data)) + } + } + for _, item := range result.Content { + if len(segments) >= maxToolResultSegments { + segments = append(segments, "[tool result reduced: remaining segments omitted]") + break + } + switch item.Type { + case "text": + segments = appendNonBlankSegment(segments, item.Text) + default: + if item.Data == nil { + continue + } + if data, err := json.Marshal(item.Data); err == nil { + segments = appendNonBlankSegment(segments, string(data)) + } + } + } + return segments +} + +func appendNonBlankSegment(input []string, value string) []string { + value = strings.TrimSpace(value) + if value == "" { + return input + } + return append(input, value) +} diff --git a/internal/ai/runtime/tools/tool_search_tool.go b/internal/ai/runtime/tools/tool_search_tool.go index 4d3ca7a..3fa025f 100644 --- a/internal/ai/runtime/tools/tool_search_tool.go +++ b/internal/ai/runtime/tools/tool_search_tool.go @@ -8,6 +8,7 @@ import ( "strings" "cs-agent/internal/ai/mcps" + impladapter "cs-agent/internal/ai/runtime/internal/impl/adapter" "cs-agent/internal/ai/runtime/registry" "cs-agent/internal/pkg/toolx" @@ -282,32 +283,5 @@ func cloneArguments(input map[string]any) map[string]any { } func buildToolCallResultSummary(result *mcps.ToolCallResult) string { - if result == nil { - return "" - } - lines := make([]string, 0, len(result.Content)+2) - if result.IsError { - lines = append(lines, "tool returned an error") - } - if result.StructuredContent != nil { - if data, err := json.Marshal(result.StructuredContent); err == nil { - lines = append(lines, string(data)) - } - } - for _, item := range result.Content { - switch item.Type { - case "text": - if text := strings.TrimSpace(item.Text); text != "" { - lines = append(lines, text) - } - default: - if item.Data == nil { - continue - } - if data, err := json.Marshal(item.Data); err == nil { - lines = append(lines, string(data)) - } - } - } - return strings.TrimSpace(strings.Join(lines, "\n")) + return impladapter.BuildReducedToolResultSummary(result) }