feat: refactor agent factory and introduce helper functions for tool metadata and instruction assembly

This commit is contained in:
mlogclub
2026-04-14 09:47:42 +08:00
parent 0ea113c621
commit d87253dddd
3 changed files with 97 additions and 39 deletions
@@ -96,38 +96,8 @@ func (f *AgentFactory) BuildCustomerServiceAgent(ctx context.Context, input Buil
handlers = append(handlers, skillHandler) handlers = append(handlers, skillHandler)
} }
if input.Collector != nil { if input.Collector != nil {
toolMetadataBy := make(map[string]einocallbacks.ToolMetadata, len(input.DynamicMCPToolDefinitions)+len(input.StaticToolMetadata)) toolMetadataBy := buildRuntimeTraceToolMetadata(input.DynamicMCPToolDefinitions, input.StaticToolMetadata, input.SelectedSkill)
for _, item := range input.DynamicMCPToolDefinitions {
toolMetadataBy[item.ModelName] = einocallbacks.ToolMetadata{
ToolCode: item.ToolCode,
ServerCode: item.ServerCode,
ToolName: item.ToolName,
SourceType: "mcp",
}
}
for modelName, metadata := range input.StaticToolMetadata {
modelName = strings.TrimSpace(modelName)
metadata.ToolCode = strings.TrimSpace(metadata.ToolCode)
metadata.ServerCode = strings.TrimSpace(metadata.ServerCode)
metadata.ToolName = strings.TrimSpace(metadata.ToolName)
metadata.SourceType = strings.TrimSpace(metadata.SourceType)
if modelName == "" || metadata.ToolCode == "" {
continue
}
toolMetadataBy[modelName] = einocallbacks.ToolMetadata{
ToolCode: metadata.ToolCode,
ServerCode: metadata.ServerCode,
ToolName: metadata.ToolName,
SourceType: metadata.SourceType,
}
}
if input.SelectedSkill != nil { if input.SelectedSkill != nil {
toolMetadataBy[toolx.BuiltinSkill.Name] = einocallbacks.ToolMetadata{
ToolCode: toolx.BuiltinSkill.Code,
ServerCode: toolx.BuiltinSkill.ServerCode,
ToolName: toolx.BuiltinSkill.Name,
SourceType: toolx.BuiltinSkill.SourceType,
}
input.Collector.SetSkillMiddleware(true, toolx.BuiltinSkill.Name) input.Collector.SetSkillMiddleware(true, toolx.BuiltinSkill.Name)
} }
handlers = append(handlers, einocallbacks.NewRuntimeTraceHandler(input.Collector, toolMetadataBy)) handlers = append(handlers, einocallbacks.NewRuntimeTraceHandler(input.Collector, toolMetadataBy))
@@ -179,11 +149,7 @@ func assembleAgentInstruction(aiAgent *models.AIAgent, selectedSkill *models.Ski
if aiAgent != nil { if aiAgent != nil {
baseInstruction = strings.TrimSpace(aiAgent.SystemPrompt) baseInstruction = strings.TrimSpace(aiAgent.SystemPrompt)
} }
appendixParts := make([]string, 0, 2) appendixParts := buildInstructionAppendices(selectedSkill, toolDefinitions, extraToolCodes)
if skillInstruction := buildSelectedSkillActivationInstruction(selectedSkill); skillInstruction != "" {
appendixParts = append(appendixParts, skillInstruction)
}
appendixParts = append(appendixParts, toolx.BuildToolAppendices(len(toolDefinitions) > 0, extraToolCodes)...)
return NewInstructionAssembler().Assemble(InstructionAssemblerInput{ return NewInstructionAssembler().Assemble(InstructionAssemblerInput{
AgentInstruction: baseInstruction, AgentInstruction: baseInstruction,
SkillInstruction: firstAppendixPart(appendixParts), SkillInstruction: firstAppendixPart(appendixParts),
@@ -0,0 +1,72 @@
package factory
import (
"strings"
einoadapter "cs-agent/internal/ai/runtime/internal/impl/adapter"
einocallbacks "cs-agent/internal/ai/runtime/internal/impl/callbacks"
"cs-agent/internal/ai/runtime/registry"
"cs-agent/internal/models"
"cs-agent/internal/pkg/toolx"
)
func buildRuntimeTraceToolMetadata(
dynamicToolDefinitions []einoadapter.MCPToolDefinition,
staticToolMetadata map[string]registry.ToolMetadata,
selectedSkill *models.SkillDefinition,
) map[string]einocallbacks.ToolMetadata {
ret := make(map[string]einocallbacks.ToolMetadata, len(dynamicToolDefinitions)+len(staticToolMetadata)+1)
for _, item := range dynamicToolDefinitions {
modelName := strings.TrimSpace(item.ModelName)
if modelName == "" {
continue
}
ret[modelName] = einocallbacks.ToolMetadata{
ToolCode: strings.TrimSpace(item.ToolCode),
ServerCode: strings.TrimSpace(item.ServerCode),
ToolName: strings.TrimSpace(item.ToolName),
SourceType: "mcp",
}
}
for modelName, metadata := range staticToolMetadata {
modelName = strings.TrimSpace(modelName)
metadata.ToolCode = strings.TrimSpace(metadata.ToolCode)
metadata.ServerCode = strings.TrimSpace(metadata.ServerCode)
metadata.ToolName = strings.TrimSpace(metadata.ToolName)
metadata.SourceType = strings.TrimSpace(metadata.SourceType)
if modelName == "" || metadata.ToolCode == "" {
continue
}
ret[modelName] = einocallbacks.ToolMetadata{
ToolCode: metadata.ToolCode,
ServerCode: metadata.ServerCode,
ToolName: metadata.ToolName,
SourceType: metadata.SourceType,
}
}
if selectedSkill != nil {
ret[toolx.BuiltinSkill.Name] = einocallbacks.ToolMetadata{
ToolCode: toolx.BuiltinSkill.Code,
ServerCode: toolx.BuiltinSkill.ServerCode,
ToolName: toolx.BuiltinSkill.Name,
SourceType: toolx.BuiltinSkill.SourceType,
}
}
return ret
}
func buildInstructionAppendices(selectedSkill *models.SkillDefinition, toolDefinitions []einoadapter.MCPToolDefinition, extraToolCodes map[string]string) []string {
appendixParts := make([]string, 0, 2)
if skillInstruction := buildSelectedSkillActivationInstruction(selectedSkill); skillInstruction != "" {
appendixParts = append(appendixParts, skillInstruction)
}
toolCodes := make([]string, 0, len(toolDefinitions)+len(extraToolCodes))
for _, item := range toolDefinitions {
toolCodes = append(toolCodes, item.ToolCode)
}
for _, item := range extraToolCodes {
toolCodes = append(toolCodes, item)
}
appendixParts = append(appendixParts, toolx.BuildToolAppendicesForCodes(len(toolDefinitions) > 0, toolCodes)...)
return appendixParts
}
+23 -3
View File
@@ -279,7 +279,12 @@ func NormalizeToolCodeAlias(toolCode string) string {
} }
func BuildToolAppendices(hasDynamicMCPTools bool, toolCodes map[string]string) []string { func BuildToolAppendices(hasDynamicMCPTools bool, toolCodes map[string]string) []string {
return BuildToolAppendicesForCodes(hasDynamicMCPTools, toolCodesFromMap(toolCodes))
}
func BuildToolAppendicesForCodes(hasDynamicMCPTools bool, toolCodes []string) []string {
ret := make([]string, 0, len(toolCodes)+1) ret := make([]string, 0, len(toolCodes)+1)
normalizedToolCodes := NormalizeToolCodes(toolCodes)
if hasDynamicMCPTools && strings.TrimSpace(BuiltinToolSearch.Appendix) != "" { if hasDynamicMCPTools && strings.TrimSpace(BuiltinToolSearch.Appendix) != "" {
ret = append(ret, BuiltinToolSearch.Appendix) ret = append(ret, BuiltinToolSearch.Appendix)
} }
@@ -290,7 +295,7 @@ func BuildToolAppendices(hasDynamicMCPTools bool, toolCodes map[string]string) [
if spec.Code == BuiltinToolSearch.Code { if spec.Code == BuiltinToolSearch.Code {
continue continue
} }
if hasToolCode(toolCodes, spec.Code) { if containsNormalizedToolCode(normalizedToolCodes, spec.Code) {
ret = append(ret, spec.Appendix) ret = append(ret, spec.Appendix)
} }
} }
@@ -330,18 +335,33 @@ func IsImpliedAllowedToolCode(toolCode string, allowedToolCodes map[string]struc
} }
func hasToolCode(toolCodes map[string]string, target string) bool { func hasToolCode(toolCodes map[string]string, target string) bool {
return containsNormalizedToolCode(toolCodesFromMap(toolCodes), target)
}
func containsNormalizedToolCode(toolCodes []string, target string) bool {
target = strings.TrimSpace(target) target = strings.TrimSpace(target)
if target == "" || len(toolCodes) == 0 { if target == "" || len(toolCodes) == 0 {
return false return false
} }
for _, item := range toolCodes { for _, item := range NormalizeToolCodes(toolCodes) {
if NormalizeToolCodeAlias(strings.TrimSpace(item)) == target { if item == target {
return true return true
} }
} }
return false return false
} }
func toolCodesFromMap(toolCodes map[string]string) []string {
if len(toolCodes) == 0 {
return nil
}
ret := make([]string, 0, len(toolCodes))
for _, item := range toolCodes {
ret = append(ret, item)
}
return ret
}
func NormalizeToolCodes(items []string) []string { func NormalizeToolCodes(items []string) []string {
if len(items) == 0 { if len(items) == 0 {
return nil return nil