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.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
+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.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
}
+21 -19
View File
@@ -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
}
@@ -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"`
+20 -18
View File
@@ -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))
+21 -19
View File
@@ -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
}
+29 -1
View File
@@ -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 == "" {
+14 -13
View File
@@ -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 {