diff --git a/internal/ai/runtime/debug_run.go b/internal/ai/runtime/debug_run.go index 94db644..1987aa1 100644 --- a/internal/ai/runtime/debug_run.go +++ b/internal/ai/runtime/debug_run.go @@ -73,6 +73,7 @@ func buildSkillDebugRunResponse(req request.SkillDebugRunRequest, summary *Summa resp.ReplyText = summary.ReplyText resp.PlanReason = summary.PlanReason resp.SkillRouteTrace = summary.SkillRouteTrace + resp.SkillAllowedToolCodes = append([]string(nil), summary.SkillAllowedToolCodes...) resp.ToolCodes = append([]string(nil), summary.ToolCodes...) resp.InvokedToolCodes = append([]string(nil), summary.InvokedToolCodes...) resp.CheckPointID = summary.CheckPointID diff --git a/internal/ai/runtime/internal/engine/service.go b/internal/ai/runtime/internal/engine/service.go index 01356ae..76e2e67 100644 --- a/internal/ai/runtime/internal/engine/service.go +++ b/internal/ai/runtime/internal/engine/service.go @@ -95,11 +95,19 @@ func (s *Service) Run(ctx context.Context, req Request) (*Summary, error) { collector.Data.Model.Provider = string(req.AIConfig.Provider) collector.Data.Model.Name = req.AIConfig.ModelName summary.SelectedSkillCode = "" + summary.SelectedSkillName = "" summary.SkillRouteReason = strings.TrimSpace(req.SkillRouteReason) summary.SkillRouteTrace = strings.TrimSpace(req.SkillRouteTrace) if req.SelectedSkill != nil { summary.SelectedSkillCode = strings.TrimSpace(req.SelectedSkill.Code) + summary.SelectedSkillName = strings.TrimSpace(req.SelectedSkill.Name) + summary.SkillAllowedToolCodes = parseJSONArrayList(req.SelectedSkill.AllowedToolCodes) + collector.Data.Skill.Code = summary.SelectedSkillCode + collector.Data.Skill.Name = summary.SelectedSkillName + collector.Data.Skill.AllowedToolCodes = append([]string(nil), summary.SkillAllowedToolCodes...) } + collector.Data.Skill.RouteReason = summary.SkillRouteReason + collector.Data.Skill.RouteTrace = summary.SkillRouteTrace agent, err := s.agentFactory.BuildCustomerServiceAgent(ctx, req.AIAgent, req.AIConfig, req.SelectedSkill, filteredToolDefs, req.ExtraTools, req.ExtraToolCodes, collector) if err != nil { @@ -287,6 +295,22 @@ func filterToolDefinitionsBySkill(definitions []adapter.MCPToolDefinition, skill } func parseJSONArraySet(raw string) map[string]struct{} { + raw = strings.TrimSpace(raw) + if raw == "" { + return nil + } + items := parseJSONArrayList(raw) + if len(items) == 0 { + return nil + } + ret := make(map[string]struct{}, len(items)) + for _, item := range items { + ret[item] = struct{}{} + } + return ret +} + +func parseJSONArrayList(raw string) []string { raw = strings.TrimSpace(raw) if raw == "" { return nil @@ -295,13 +319,13 @@ func parseJSONArraySet(raw string) map[string]struct{} { if err := json.Unmarshal([]byte(raw), &items); err != nil { return nil } - ret := make(map[string]struct{}, len(items)) + ret := make([]string, 0, len(items)) for _, item := range items { item = strings.TrimSpace(item) if item == "" { continue } - ret[item] = struct{}{} + ret = append(ret, item) } return ret } diff --git a/internal/ai/runtime/internal/engine/types.go b/internal/ai/runtime/internal/engine/types.go index 5fafea1..46b4e6a 100644 --- a/internal/ai/runtime/internal/engine/types.go +++ b/internal/ai/runtime/internal/engine/types.go @@ -36,23 +36,25 @@ type InterruptContextSummary struct { } type Summary struct { - RunID string - Status string - ReplyText string - SelectedSkillCode string - SkillRouteReason string - SkillRouteTrace string - ModelName string - PromptTokens int - CompletionTokens int - HistoryMessageCount int - RetrieverCount int - ToolCallCount int - ToolCodes []string - InvokedToolCodes []string - CheckPointID string - Interrupted bool - Interrupts []InterruptContextSummary - TraceData string - ErrorMessage string + RunID string + Status string + ReplyText string + SelectedSkillCode string + SelectedSkillName string + SkillRouteReason string + SkillRouteTrace string + SkillAllowedToolCodes []string + ModelName string + PromptTokens int + CompletionTokens int + HistoryMessageCount int + RetrieverCount int + ToolCallCount int + ToolCodes []string + InvokedToolCodes []string + CheckPointID string + Interrupted bool + Interrupts []InterruptContextSummary + TraceData string + ErrorMessage string } diff --git a/internal/ai/runtime/internal/impl/callbacks/trace_callback.go b/internal/ai/runtime/internal/impl/callbacks/trace_callback.go index 374f87d..05b5cc7 100644 --- a/internal/ai/runtime/internal/impl/callbacks/trace_callback.go +++ b/internal/ai/runtime/internal/impl/callbacks/trace_callback.go @@ -21,9 +21,10 @@ type RetrieverTraceItem struct { } type RuntimeTraceData struct { - Version string `json:"version"` - Status string `json:"status"` - RunID string `json:"runId,omitempty"` + Version string `json:"version"` + Status string `json:"status"` + RunID string `json:"runId,omitempty"` + Skill SkillTraceData `json:"skill,omitempty"` Interrupt struct { CheckPointID string `json:"checkPointId,omitempty"` Items []InterruptTraceContext `json:"items,omitempty"` @@ -56,6 +57,14 @@ type RuntimeTraceData struct { } `json:"error"` } +type SkillTraceData struct { + Code string `json:"code,omitempty"` + Name string `json:"name,omitempty"` + RouteReason string `json:"routeReason,omitempty"` + RouteTrace string `json:"routeTrace,omitempty"` + AllowedToolCodes []string `json:"allowedToolCodes,omitempty"` +} + type InterruptTraceContext struct { Type string `json:"type,omitempty"` ID string `json:"id"` diff --git a/internal/ai/runtime/service.go b/internal/ai/runtime/service.go index dc8d18b..3e52068 100644 --- a/internal/ai/runtime/service.go +++ b/internal/ai/runtime/service.go @@ -124,24 +124,26 @@ func toSummary(summary *engine.Summary) *Summary { return nil } ret := &Summary{ - RunID: summary.RunID, - Status: summary.Status, - ReplyText: summary.ReplyText, - PlannedSkillCode: strings.TrimSpace(summary.SelectedSkillCode), - PlanReason: strings.TrimSpace(summary.SkillRouteReason), - SkillRouteTrace: strings.TrimSpace(summary.SkillRouteTrace), - ModelName: summary.ModelName, - PromptTokens: summary.PromptTokens, - CompletionTokens: summary.CompletionTokens, - HistoryMessageCount: summary.HistoryMessageCount, - RetrieverCount: summary.RetrieverCount, - ToolCallCount: summary.ToolCallCount, - ToolCodes: append([]string(nil), summary.ToolCodes...), - InvokedToolCodes: append([]string(nil), summary.InvokedToolCodes...), - CheckPointID: summary.CheckPointID, - Interrupted: summary.Interrupted, - TraceData: summary.TraceData, - ErrorMessage: summary.ErrorMessage, + RunID: summary.RunID, + Status: summary.Status, + ReplyText: summary.ReplyText, + PlannedSkillCode: strings.TrimSpace(summary.SelectedSkillCode), + PlannedSkillName: strings.TrimSpace(summary.SelectedSkillName), + PlanReason: strings.TrimSpace(summary.SkillRouteReason), + SkillRouteTrace: strings.TrimSpace(summary.SkillRouteTrace), + SkillAllowedToolCodes: append([]string(nil), summary.SkillAllowedToolCodes...), + ModelName: summary.ModelName, + PromptTokens: summary.PromptTokens, + CompletionTokens: summary.CompletionTokens, + HistoryMessageCount: summary.HistoryMessageCount, + RetrieverCount: summary.RetrieverCount, + ToolCallCount: summary.ToolCallCount, + ToolCodes: append([]string(nil), summary.ToolCodes...), + InvokedToolCodes: append([]string(nil), summary.InvokedToolCodes...), + CheckPointID: summary.CheckPointID, + Interrupted: summary.Interrupted, + TraceData: summary.TraceData, + ErrorMessage: summary.ErrorMessage, } if len(summary.Interrupts) > 0 { ret.Interrupts = make([]InterruptContextSummary, 0, len(summary.Interrupts)) diff --git a/internal/ai/runtime/types.go b/internal/ai/runtime/types.go index cd2467e..d3271d6 100644 --- a/internal/ai/runtime/types.go +++ b/internal/ai/runtime/types.go @@ -37,23 +37,25 @@ type InterruptContextSummary struct { } type Summary struct { - RunID string - Status string - ReplyText string - PlannedSkillCode string - PlanReason string - SkillRouteTrace string - ModelName string - PromptTokens int - CompletionTokens int - HistoryMessageCount int - RetrieverCount int - ToolCallCount int - ToolCodes []string - InvokedToolCodes []string - CheckPointID string - Interrupted bool - Interrupts []InterruptContextSummary - TraceData string - ErrorMessage string + RunID string + Status string + ReplyText string + PlannedSkillCode string + PlannedSkillName string + PlanReason string + SkillRouteTrace string + SkillAllowedToolCodes []string + ModelName string + PromptTokens int + CompletionTokens int + HistoryMessageCount int + RetrieverCount int + ToolCallCount int + ToolCodes []string + InvokedToolCodes []string + CheckPointID string + Interrupted bool + Interrupts []InterruptContextSummary + TraceData string + ErrorMessage string } diff --git a/internal/ai/skills/matcher.go b/internal/ai/skills/matcher.go index af858d9..2d18740 100644 --- a/internal/ai/skills/matcher.go +++ b/internal/ai/skills/matcher.go @@ -2,6 +2,7 @@ package skills import ( "context" + "encoding/json" "fmt" "strings" "time" @@ -157,13 +158,40 @@ func buildSkillRoutePrompt(userMessage string, candidates []models.SkillDefiniti lines = append(lines, "") lines = append(lines, "候选 Skills:") for _, item := range candidates { - lines = append(lines, fmt.Sprintf("- skillCode=%s; name=%s; description=%s", strings.TrimSpace(item.Code), strings.TrimSpace(item.Name), strings.TrimSpace(item.Description))) + line := fmt.Sprintf("- skillCode=%s; name=%s; description=%s", strings.TrimSpace(item.Code), strings.TrimSpace(item.Name), strings.TrimSpace(item.Description)) + if examples := parseSkillExamples(item.Examples); len(examples) > 0 { + line += "; examples=" + strings.Join(examples, " | ") + } + lines = append(lines, line) } lines = append(lines, "") lines = append(lines, "请只输出一个 skillCode 或 NONE。") return strings.Join(lines, "\n") } +func parseSkillExamples(raw string) []string { + raw = strings.TrimSpace(raw) + if raw == "" { + return nil + } + var items []string + if err := json.Unmarshal([]byte(raw), &items); err != nil { + return nil + } + ret := make([]string, 0, len(items)) + for _, item := range items { + item = strings.TrimSpace(item) + if item == "" { + continue + } + ret = append(ret, item) + if len(ret) >= 3 { + break + } + } + return ret +} + func normalizeRouteDecision(raw string) string { raw = strings.TrimSpace(raw) if raw == "" { diff --git a/internal/pkg/dto/response/skill_response.go b/internal/pkg/dto/response/skill_response.go index e014704..e28ea8e 100644 --- a/internal/pkg/dto/response/skill_response.go +++ b/internal/pkg/dto/response/skill_response.go @@ -21,19 +21,20 @@ type SkillDefinitionResponse struct { } type SkillDebugRunResponse struct { - SkillCode string `json:"skillCode"` - SkillName string `json:"skillName"` - ReplyText string `json:"replyText"` - PlanReason string `json:"planReason"` - SkillRouteTrace string `json:"skillRouteTrace"` - ToolCodes []string `json:"toolCodes"` - InvokedToolCodes []string `json:"invokedToolCodes"` - CheckPointID string `json:"checkPointId"` - Interrupted bool `json:"interrupted"` - TraceData string `json:"traceData"` - ErrorMessage string `json:"errorMessage"` - ConversationID int64 `json:"conversationId"` - AIAgentID int64 `json:"aiAgentId"` + SkillCode string `json:"skillCode"` + SkillName string `json:"skillName"` + ReplyText string `json:"replyText"` + PlanReason string `json:"planReason"` + SkillRouteTrace string `json:"skillRouteTrace"` + SkillAllowedToolCodes []string `json:"skillAllowedToolCodes"` + ToolCodes []string `json:"toolCodes"` + InvokedToolCodes []string `json:"invokedToolCodes"` + CheckPointID string `json:"checkPointId"` + Interrupted bool `json:"interrupted"` + TraceData string `json:"traceData"` + ErrorMessage string `json:"errorMessage"` + ConversationID int64 `json:"conversationId"` + AIAgentID int64 `json:"aiAgentId"` } type AgentRunLogResponse struct {