// builtin.go - 内置只读工具 // Logan.Li // analyze_output:对冗长命令输出做摘要/错误提取,避免 token 爆炸,辅助 LLM 聚焦关键线索。 package tools import ( "context" "fmt" "strings" ) // AnalyzeOutputTool 输出分析工具(只读,Safe) // Logan.Li type AnalyzeOutputTool struct{} // NewAnalyzeOutputTool 创建输出分析工具 // Logan.Li func NewAnalyzeOutputTool() *AnalyzeOutputTool { return &AnalyzeOutputTool{} } // Name 工具名 // Logan.Li func (t *AnalyzeOutputTool) Name() string { return "analyze_output" } // Description 给 LLM 的说明 // Logan.Li func (t *AnalyzeOutputTool) Description() string { return "分析一段(可能很长的)命令输出文本:统计行数、提取疑似错误/告警行、必要时截断保留首尾。" + "当 ssh_execute 或 pod_execute 返回内容过长时调用此工具提炼关键信息再继续推理。" } // Parameters 参数定义 // Logan.Li func (t *AnalyzeOutputTool) Parameters() map[string]interface{} { return map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "output": map[string]interface{}{ "type": "string", "description": "待分析的文本输出", }, "focus": map[string]interface{}{ "type": "string", "description": "可选关注点,如 error、memory、disk", }, }, "required": []string{"output"}, } } // RiskLevel 静态风险(只读安全) // Logan.Li func (t *AnalyzeOutputTool) RiskLevel() RiskLevel { return RiskSafe } // Execute 执行输出分析 // Logan.Li func (t *AnalyzeOutputTool) Execute(ctx context.Context, args map[string]interface{}) (*Result, error) { output := StringParam(args, "output") focus := strings.ToLower(StringParam(args, "focus")) if output == "" { return &Result{Success: true, Output: "(空输出)"}, nil } lines := strings.Split(output, "\n") total := len(lines) // 超长截断:保留前 40 + 后 40 行,中间用省略标注 truncated := false if total > 120 { head := lines[:40] tail := lines[total-40:] lines = append(append(head, fmt.Sprintf("... [已省略 %d 行] ...", total-80)), tail...) truncated = true } // 提取疑似错误/告警行 errorKeywords := []string{"error", "fail", "exception", "traceback", "denied", "permission", "timeout", "refused", "unavailable", "critical", "fatal", "错误", "失败", "拒绝", "超时", "无法", "异常"} if focus != "" { errorKeywords = append([]string{focus}, errorKeywords...) } var errorLines []string for _, line := range lines { low := strings.ToLower(line) for _, kw := range errorKeywords { if strings.Contains(low, kw) { errorLines = append(errorLines, strings.TrimSpace(line)) break } } } if len(errorLines) > 20 { errorLines = errorLines[:20] } // 组装分析摘要 var sb strings.Builder sb.WriteString(fmt.Sprintf("总行数: %d", total)) if truncated { sb.WriteString("(已截断保留首尾)") } sb.WriteString("\n") if len(errorLines) > 0 { sb.WriteString(fmt.Sprintf("疑似错误/告警行 (%d):\n", len(errorLines))) for _, l := range errorLines { sb.WriteString(" " + l + "\n") } } else { sb.WriteString("未发现明显错误关键词\n") } sb.WriteString("\n输出片段:\n" + strings.Join(lines, "\n")) return &Result{Success: true, Output: sb.String()}, nil }