Enhance skill debugging and response structure by adding skill name and allowed tool codes, and refactor related data handling

This commit is contained in:
mlogclub
2026-04-09 13:38:34 +08:00
parent 28d18ab6c0
commit 03c1131006
8 changed files with 144 additions and 75 deletions
+1
View File
@@ -73,6 +73,7 @@ func buildSkillDebugRunResponse(req request.SkillDebugRunRequest, summary *Summa
resp.ReplyText = summary.ReplyText resp.ReplyText = summary.ReplyText
resp.PlanReason = summary.PlanReason resp.PlanReason = summary.PlanReason
resp.SkillRouteTrace = summary.SkillRouteTrace resp.SkillRouteTrace = summary.SkillRouteTrace
resp.SkillAllowedToolCodes = append([]string(nil), summary.SkillAllowedToolCodes...)
resp.ToolCodes = append([]string(nil), summary.ToolCodes...) resp.ToolCodes = append([]string(nil), summary.ToolCodes...)
resp.InvokedToolCodes = append([]string(nil), summary.InvokedToolCodes...) resp.InvokedToolCodes = append([]string(nil), summary.InvokedToolCodes...)
resp.CheckPointID = summary.CheckPointID resp.CheckPointID = summary.CheckPointID
+26 -2
View File
@@ -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.Provider = string(req.AIConfig.Provider)
collector.Data.Model.Name = req.AIConfig.ModelName collector.Data.Model.Name = req.AIConfig.ModelName
summary.SelectedSkillCode = "" summary.SelectedSkillCode = ""
summary.SelectedSkillName = ""
summary.SkillRouteReason = strings.TrimSpace(req.SkillRouteReason) summary.SkillRouteReason = strings.TrimSpace(req.SkillRouteReason)
summary.SkillRouteTrace = strings.TrimSpace(req.SkillRouteTrace) summary.SkillRouteTrace = strings.TrimSpace(req.SkillRouteTrace)
if req.SelectedSkill != nil { if req.SelectedSkill != nil {
summary.SelectedSkillCode = strings.TrimSpace(req.SelectedSkill.Code) 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) agent, err := s.agentFactory.BuildCustomerServiceAgent(ctx, req.AIAgent, req.AIConfig, req.SelectedSkill, filteredToolDefs, req.ExtraTools, req.ExtraToolCodes, collector)
if err != nil { if err != nil {
@@ -287,6 +295,22 @@ func filterToolDefinitionsBySkill(definitions []adapter.MCPToolDefinition, skill
} }
func parseJSONArraySet(raw string) map[string]struct{} { 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) raw = strings.TrimSpace(raw)
if raw == "" { if raw == "" {
return nil return nil
@@ -295,13 +319,13 @@ func parseJSONArraySet(raw string) map[string]struct{} {
if err := json.Unmarshal([]byte(raw), &items); err != nil { if err := json.Unmarshal([]byte(raw), &items); err != nil {
return nil return nil
} }
ret := make(map[string]struct{}, len(items)) ret := make([]string, 0, len(items))
for _, item := range items { for _, item := range items {
item = strings.TrimSpace(item) item = strings.TrimSpace(item)
if item == "" { if item == "" {
continue continue
} }
ret[item] = struct{}{} ret = append(ret, item)
} }
return ret return ret
} }
+21 -19
View File
@@ -36,23 +36,25 @@ type InterruptContextSummary struct {
} }
type Summary struct { type Summary struct {
RunID string RunID string
Status string Status string
ReplyText string ReplyText string
SelectedSkillCode string SelectedSkillCode string
SkillRouteReason string SelectedSkillName string
SkillRouteTrace string SkillRouteReason string
ModelName string SkillRouteTrace string
PromptTokens int SkillAllowedToolCodes []string
CompletionTokens int ModelName string
HistoryMessageCount int PromptTokens int
RetrieverCount int CompletionTokens int
ToolCallCount int HistoryMessageCount int
ToolCodes []string RetrieverCount int
InvokedToolCodes []string ToolCallCount int
CheckPointID string ToolCodes []string
Interrupted bool InvokedToolCodes []string
Interrupts []InterruptContextSummary CheckPointID string
TraceData string Interrupted bool
ErrorMessage string Interrupts []InterruptContextSummary
TraceData string
ErrorMessage string
} }
@@ -21,9 +21,10 @@ type RetrieverTraceItem struct {
} }
type RuntimeTraceData struct { type RuntimeTraceData struct {
Version string `json:"version"` Version string `json:"version"`
Status string `json:"status"` Status string `json:"status"`
RunID string `json:"runId,omitempty"` RunID string `json:"runId,omitempty"`
Skill SkillTraceData `json:"skill,omitempty"`
Interrupt struct { Interrupt struct {
CheckPointID string `json:"checkPointId,omitempty"` CheckPointID string `json:"checkPointId,omitempty"`
Items []InterruptTraceContext `json:"items,omitempty"` Items []InterruptTraceContext `json:"items,omitempty"`
@@ -56,6 +57,14 @@ type RuntimeTraceData struct {
} `json:"error"` } `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 InterruptTraceContext struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
ID string `json:"id"` ID string `json:"id"`
+20 -18
View File
@@ -124,24 +124,26 @@ func toSummary(summary *engine.Summary) *Summary {
return nil return nil
} }
ret := &Summary{ ret := &Summary{
RunID: summary.RunID, RunID: summary.RunID,
Status: summary.Status, Status: summary.Status,
ReplyText: summary.ReplyText, ReplyText: summary.ReplyText,
PlannedSkillCode: strings.TrimSpace(summary.SelectedSkillCode), PlannedSkillCode: strings.TrimSpace(summary.SelectedSkillCode),
PlanReason: strings.TrimSpace(summary.SkillRouteReason), PlannedSkillName: strings.TrimSpace(summary.SelectedSkillName),
SkillRouteTrace: strings.TrimSpace(summary.SkillRouteTrace), PlanReason: strings.TrimSpace(summary.SkillRouteReason),
ModelName: summary.ModelName, SkillRouteTrace: strings.TrimSpace(summary.SkillRouteTrace),
PromptTokens: summary.PromptTokens, SkillAllowedToolCodes: append([]string(nil), summary.SkillAllowedToolCodes...),
CompletionTokens: summary.CompletionTokens, ModelName: summary.ModelName,
HistoryMessageCount: summary.HistoryMessageCount, PromptTokens: summary.PromptTokens,
RetrieverCount: summary.RetrieverCount, CompletionTokens: summary.CompletionTokens,
ToolCallCount: summary.ToolCallCount, HistoryMessageCount: summary.HistoryMessageCount,
ToolCodes: append([]string(nil), summary.ToolCodes...), RetrieverCount: summary.RetrieverCount,
InvokedToolCodes: append([]string(nil), summary.InvokedToolCodes...), ToolCallCount: summary.ToolCallCount,
CheckPointID: summary.CheckPointID, ToolCodes: append([]string(nil), summary.ToolCodes...),
Interrupted: summary.Interrupted, InvokedToolCodes: append([]string(nil), summary.InvokedToolCodes...),
TraceData: summary.TraceData, CheckPointID: summary.CheckPointID,
ErrorMessage: summary.ErrorMessage, Interrupted: summary.Interrupted,
TraceData: summary.TraceData,
ErrorMessage: summary.ErrorMessage,
} }
if len(summary.Interrupts) > 0 { if len(summary.Interrupts) > 0 {
ret.Interrupts = make([]InterruptContextSummary, 0, len(summary.Interrupts)) ret.Interrupts = make([]InterruptContextSummary, 0, len(summary.Interrupts))
+21 -19
View File
@@ -37,23 +37,25 @@ type InterruptContextSummary struct {
} }
type Summary struct { type Summary struct {
RunID string RunID string
Status string Status string
ReplyText string ReplyText string
PlannedSkillCode string PlannedSkillCode string
PlanReason string PlannedSkillName string
SkillRouteTrace string PlanReason string
ModelName string SkillRouteTrace string
PromptTokens int SkillAllowedToolCodes []string
CompletionTokens int ModelName string
HistoryMessageCount int PromptTokens int
RetrieverCount int CompletionTokens int
ToolCallCount int HistoryMessageCount int
ToolCodes []string RetrieverCount int
InvokedToolCodes []string ToolCallCount int
CheckPointID string ToolCodes []string
Interrupted bool InvokedToolCodes []string
Interrupts []InterruptContextSummary CheckPointID string
TraceData string Interrupted bool
ErrorMessage string Interrupts []InterruptContextSummary
TraceData string
ErrorMessage string
} }
+29 -1
View File
@@ -2,6 +2,7 @@ package skills
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@@ -157,13 +158,40 @@ func buildSkillRoutePrompt(userMessage string, candidates []models.SkillDefiniti
lines = append(lines, "") lines = append(lines, "")
lines = append(lines, "候选 Skills") lines = append(lines, "候选 Skills")
for _, item := range candidates { 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, "")
lines = append(lines, "请只输出一个 skillCode 或 NONE。") lines = append(lines, "请只输出一个 skillCode 或 NONE。")
return strings.Join(lines, "\n") 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 { func normalizeRouteDecision(raw string) string {
raw = strings.TrimSpace(raw) raw = strings.TrimSpace(raw)
if raw == "" { if raw == "" {
+14 -13
View File
@@ -21,19 +21,20 @@ type SkillDefinitionResponse struct {
} }
type SkillDebugRunResponse struct { type SkillDebugRunResponse struct {
SkillCode string `json:"skillCode"` SkillCode string `json:"skillCode"`
SkillName string `json:"skillName"` SkillName string `json:"skillName"`
ReplyText string `json:"replyText"` ReplyText string `json:"replyText"`
PlanReason string `json:"planReason"` PlanReason string `json:"planReason"`
SkillRouteTrace string `json:"skillRouteTrace"` SkillRouteTrace string `json:"skillRouteTrace"`
ToolCodes []string `json:"toolCodes"` SkillAllowedToolCodes []string `json:"skillAllowedToolCodes"`
InvokedToolCodes []string `json:"invokedToolCodes"` ToolCodes []string `json:"toolCodes"`
CheckPointID string `json:"checkPointId"` InvokedToolCodes []string `json:"invokedToolCodes"`
Interrupted bool `json:"interrupted"` CheckPointID string `json:"checkPointId"`
TraceData string `json:"traceData"` Interrupted bool `json:"interrupted"`
ErrorMessage string `json:"errorMessage"` TraceData string `json:"traceData"`
ConversationID int64 `json:"conversationId"` ErrorMessage string `json:"errorMessage"`
AIAgentID int64 `json:"aiAgentId"` ConversationID int64 `json:"conversationId"`
AIAgentID int64 `json:"aiAgentId"`
} }
type AgentRunLogResponse struct { type AgentRunLogResponse struct {