Refactor skill handling and improve runtime trace capabilities
- Removed the selectSkill method from prepareService and adjusted related logic in the Run method of Service. - Updated tool catalog to parse agent allowed tool codes directly. - Simplified Request and RunInput structures by removing unnecessary fields. - Enhanced the RuntimeTraceCollector to manage skill activation and visibility. - Introduced a new databaseSkillBackend to manage skill definitions and their metadata. - Added tests for skill backend functionalities to ensure correct behavior. - Updated various factory methods to accommodate changes in skill handling. - Improved documentation and descriptions for better clarity.
This commit is contained in:
@@ -24,8 +24,9 @@ type ToolMetadata struct {
|
||||
|
||||
type RuntimeTraceHandler struct {
|
||||
*adk.BaseChatModelAgentMiddleware
|
||||
collector *RuntimeTraceCollector
|
||||
toolMetadataBy map[string]ToolMetadata
|
||||
collector *RuntimeTraceCollector
|
||||
toolMetadataBy map[string]ToolMetadata
|
||||
skillMetadataBy map[string]SkillMetadata
|
||||
}
|
||||
|
||||
type graphAnalyzeConversationResult struct {
|
||||
@@ -65,11 +66,12 @@ type toolSearchSearchResult struct {
|
||||
Candidates []toolSearchCandidateResult `json:"candidates"`
|
||||
}
|
||||
|
||||
func NewRuntimeTraceHandler(collector *RuntimeTraceCollector, toolMetadataBy map[string]ToolMetadata) *RuntimeTraceHandler {
|
||||
func NewRuntimeTraceHandler(collector *RuntimeTraceCollector, toolMetadataBy map[string]ToolMetadata, skillMetadataBy map[string]SkillMetadata) *RuntimeTraceHandler {
|
||||
return &RuntimeTraceHandler{
|
||||
BaseChatModelAgentMiddleware: &adk.BaseChatModelAgentMiddleware{},
|
||||
collector: collector,
|
||||
toolMetadataBy: toolMetadataBy,
|
||||
skillMetadataBy: skillMetadataBy,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,10 +125,42 @@ func (h *RuntimeTraceHandler) WrapInvokableToolCall(_ context.Context, endpoint
|
||||
if metadata, ok := h.resolveToolMetadata(item.ToolName); ok && strings.TrimSpace(metadata.ToolCode) == toolx.BuiltinToolSearch.Code {
|
||||
h.collector.AddToolSearchItem(h.buildToolSearchTraceItem(argumentsInJSON, result, err))
|
||||
}
|
||||
if metadata, ok := h.resolveToolMetadata(item.ToolName); ok && strings.TrimSpace(metadata.ToolCode) == toolx.BuiltinSkill.Code {
|
||||
h.tryActivateSkill(argumentsInJSON)
|
||||
}
|
||||
return result, err
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *RuntimeTraceHandler) tryActivateSkill(argumentsInJSON string) {
|
||||
if h == nil || h.collector == nil {
|
||||
return
|
||||
}
|
||||
var args struct {
|
||||
Skill string `json:"skill"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(argumentsInJSON)), &args); err != nil {
|
||||
return
|
||||
}
|
||||
code := strings.TrimSpace(args.Skill)
|
||||
if code == "" {
|
||||
return
|
||||
}
|
||||
meta, ok := h.skillMetadataBy[code]
|
||||
if !ok {
|
||||
meta = SkillMetadata{Code: code}
|
||||
}
|
||||
buf, err := json.Marshal(map[string]any{
|
||||
"source": "eino_skill_tool",
|
||||
"skill": code,
|
||||
})
|
||||
routeTrace := ""
|
||||
if err == nil {
|
||||
routeTrace = string(buf)
|
||||
}
|
||||
h.collector.ActivateSkill(meta, "eino_skill_tool", routeTrace)
|
||||
}
|
||||
|
||||
func parseGraphToolOutcome(toolCode string, result string) (recommendedAction, riskLevel string, ticketDraftReady bool) {
|
||||
toolCode = strings.TrimSpace(toolCode)
|
||||
if toolCode == "" || strings.TrimSpace(result) == "" {
|
||||
|
||||
@@ -36,3 +36,32 @@ func TestExtractCandidateToolCodes(t *testing.T) {
|
||||
t.Fatalf("unexpected candidate codes: %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTryActivateSkill(t *testing.T) {
|
||||
collector := NewRuntimeTraceCollector()
|
||||
handler := &RuntimeTraceHandler{
|
||||
collector: collector,
|
||||
skillMetadataBy: map[string]SkillMetadata{
|
||||
"after_sales_escalation_skill": {
|
||||
Code: "after_sales_escalation_skill",
|
||||
Name: "售后升级",
|
||||
AllowedToolCodes: []string{"graph/handoff_to_human"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
handler.tryActivateSkill(`{"skill":"after_sales_escalation_skill"}`)
|
||||
|
||||
if collector.Data.Skill.Code != "after_sales_escalation_skill" {
|
||||
t.Fatalf("unexpected skill code: %#v", collector.Data.Skill)
|
||||
}
|
||||
if collector.Data.Skill.Name != "售后升级" {
|
||||
t.Fatalf("unexpected skill name: %#v", collector.Data.Skill)
|
||||
}
|
||||
if collector.Data.Skill.RouteReason != "eino_skill_tool" {
|
||||
t.Fatalf("unexpected route reason: %#v", collector.Data.Skill)
|
||||
}
|
||||
if len(collector.Data.Skill.AllowedToolCodes) != 1 || collector.Data.Skill.AllowedToolCodes[0] != "graph/handoff_to_human" {
|
||||
t.Fatalf("unexpected allowed tools: %#v", collector.Data.Skill.AllowedToolCodes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,43 @@ func (c *RuntimeTraceCollector) SetSkillMiddleware(enabled bool, toolName string
|
||||
c.Data.Skill.MiddlewareToolName = toolName
|
||||
}
|
||||
|
||||
type SkillMetadata struct {
|
||||
Code string
|
||||
Name string
|
||||
Description string
|
||||
AllowedToolCodes []string
|
||||
}
|
||||
|
||||
func (c *RuntimeTraceCollector) SetVisibleSkills(skills map[string]SkillMetadata) {
|
||||
if c == nil || len(skills) == 0 {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
codes := make([]string, 0, len(skills))
|
||||
for code := range skills {
|
||||
if code == "" {
|
||||
continue
|
||||
}
|
||||
codes = append(codes, code)
|
||||
}
|
||||
c.Data.Skill.VisibleCodes = append([]string(nil), codes...)
|
||||
}
|
||||
|
||||
func (c *RuntimeTraceCollector) ActivateSkill(skill SkillMetadata, routeReason string, routeTrace string) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.Data.Skill.Code = skill.Code
|
||||
c.Data.Skill.Name = skill.Name
|
||||
c.Data.Skill.Description = skill.Description
|
||||
c.Data.Skill.AllowedToolCodes = append([]string(nil), skill.AllowedToolCodes...)
|
||||
c.Data.Skill.RouteReason = routeReason
|
||||
c.Data.Skill.RouteTrace = routeTrace
|
||||
}
|
||||
|
||||
func (c *RuntimeTraceCollector) SetRetrieverSummary(summary RetrieverTraceSummary) {
|
||||
if c == nil {
|
||||
return
|
||||
|
||||
@@ -142,11 +142,13 @@ type RuntimeTraceData struct {
|
||||
type SkillTraceData struct {
|
||||
Code string `json:"code,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
RouteReason string `json:"routeReason,omitempty"`
|
||||
RouteTrace string `json:"routeTrace,omitempty"`
|
||||
AllowedToolCodes []string `json:"allowedToolCodes,omitempty"`
|
||||
MiddlewareEnabled bool `json:"middlewareEnabled,omitempty"`
|
||||
MiddlewareToolName string `json:"middlewareToolName,omitempty"`
|
||||
VisibleCodes []string `json:"visibleCodes,omitempty"`
|
||||
}
|
||||
|
||||
type InterruptTraceContext struct {
|
||||
|
||||
Reference in New Issue
Block a user