refactor: rename direct tools to MCP tools across the codebase for consistency
This commit is contained in:
@@ -61,7 +61,7 @@ func TestRegistryIncludesAllTicketDraftToolInputs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegistryTreatsAdministratorSelectedMCPToolsAsDirectTools(t *testing.T) {
|
func TestRegistryTreatsAdministratorSelectedMCPToolsAsAllowedTools(t *testing.T) {
|
||||||
definition, err := DefaultRegistry.Resolve("knowledge/search")
|
definition, err := DefaultRegistry.Resolve("knowledge/search")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Resolve returned error: %v", err)
|
t.Fatalf("Resolve returned error: %v", err)
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ func buildAIAgentResponseWithLocale(item *models.AIAgent, locale string) respons
|
|||||||
SkillIDs: utils.SplitInt64s(item.SkillIDs),
|
SkillIDs: utils.SplitInt64s(item.SkillIDs),
|
||||||
Skills: make([]response.AIAgentSkillResponse, 0),
|
Skills: make([]response.AIAgentSkillResponse, 0),
|
||||||
Teams: make([]response.AIAgentTeamResponse, 0),
|
Teams: make([]response.AIAgentTeamResponse, 0),
|
||||||
DirectTools: make([]response.AIAgentMCPToolResponse, 0),
|
MCPTools: make([]response.AIAgentMCPToolResponse, 0),
|
||||||
WorkflowBindings: make([]response.AIAgentWorkflowBindingResponse, 0),
|
WorkflowBindings: make([]response.AIAgentWorkflowBindingResponse, 0),
|
||||||
WorkflowVersionID: item.WorkflowVersionID,
|
WorkflowVersionID: item.WorkflowVersionID,
|
||||||
PublishedRevisionID: item.PublishedRevisionID,
|
PublishedRevisionID: item.PublishedRevisionID,
|
||||||
@@ -303,18 +303,15 @@ func buildAIAgentResponseWithLocale(item *models.AIAgent, locale string) respons
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if raw := strings.TrimSpace(item.AllowedMCPTools); raw != "" {
|
if raw := strings.TrimSpace(item.AllowedMCPTools); raw != "" {
|
||||||
var directTools []request.AIAgentMCPToolRequest
|
var mcpTools []request.AIAgentMCPToolRequest
|
||||||
if err := json.Unmarshal([]byte(raw), &directTools); err == nil {
|
if err := json.Unmarshal([]byte(raw), &mcpTools); err == nil {
|
||||||
for _, tool := range directTools {
|
for _, tool := range mcpTools {
|
||||||
toolCode := strings.TrimSpace(tool.ToolCode)
|
toolCode := strings.TrimSpace(tool.ToolCode)
|
||||||
if toolCode == "" {
|
if toolCode == "" {
|
||||||
toolCode = toolx.BuildMCPToolCode(tool.ServerCode, tool.ToolName)
|
toolCode = toolx.BuildMCPToolCode(tool.ServerCode, tool.ToolName)
|
||||||
}
|
}
|
||||||
toolCode = toolx.NormalizeToolCodeAlias(toolCode)
|
toolCode = toolx.NormalizeToolCodeAlias(toolCode)
|
||||||
if toolx.IsAutoInjectedToolCode(toolCode) {
|
if toolx.ResolveToolSourceType(toolCode) != enums.ToolSourceTypeMCP {
|
||||||
continue
|
|
||||||
}
|
|
||||||
if toolx.IsAgentDirectGraphToolCode(toolCode) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
serverCode := strings.TrimSpace(tool.ServerCode)
|
serverCode := strings.TrimSpace(tool.ServerCode)
|
||||||
@@ -338,7 +335,7 @@ func buildAIAgentResponseWithLocale(item *models.AIAgent, locale string) respons
|
|||||||
description = registeredDescription
|
description = registeredDescription
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret.DirectTools = append(ret.DirectTools, response.AIAgentMCPToolResponse{
|
ret.MCPTools = append(ret.MCPTools, response.AIAgentMCPToolResponse{
|
||||||
ToolCode: toolCode,
|
ToolCode: toolCode,
|
||||||
ServerCode: serverCode,
|
ServerCode: serverCode,
|
||||||
ToolName: toolName,
|
ToolName: toolName,
|
||||||
|
|||||||
@@ -552,7 +552,7 @@ type AIAgent struct {
|
|||||||
FallbackMessage string `gorm:"type:text"` // FallbackMessage 为知识不足回复文案。
|
FallbackMessage string `gorm:"type:text"` // FallbackMessage 为知识不足回复文案。
|
||||||
KnowledgeIDs string `gorm:"type:varchar(500);not null;default:''"` // KnowledgeIDs 为绑定的知识库ID列表,按顺序表示优先级。
|
KnowledgeIDs string `gorm:"type:varchar(500);not null;default:''"` // KnowledgeIDs 为绑定的知识库ID列表,按顺序表示优先级。
|
||||||
SkillIDs string `gorm:"type:varchar(500);not null;default:''"` // SkillIDs 为绑定的技能ID列表,按顺序表示允许路由的范围。
|
SkillIDs string `gorm:"type:varchar(500);not null;default:''"` // SkillIDs 为绑定的技能ID列表,按顺序表示允许路由的范围。
|
||||||
AllowedMCPTools string `gorm:"type:text"` // AllowedMCPTools 为允许 direct tool 路由的 MCP 工具白名单配置JSON。
|
AllowedMCPTools string `gorm:"type:text"` // AllowedMCPTools 为 Agent 允许调用的 MCP 工具白名单配置 JSON。
|
||||||
WorkflowVersionID int64 `gorm:"type:bigint;not null;default:0;index"` // WorkflowVersionID is retained as the single-workflow runtime pointer.
|
WorkflowVersionID int64 `gorm:"type:bigint;not null;default:0;index"` // WorkflowVersionID is retained as the single-workflow runtime pointer.
|
||||||
PublishedRevisionID int64 `gorm:"type:bigint;not null;default:0;index"` // PublishedRevisionID 为当前已发布 Agent 配置快照ID。
|
PublishedRevisionID int64 `gorm:"type:bigint;not null;default:0;index"` // PublishedRevisionID 为当前已发布 Agent 配置快照ID。
|
||||||
SortNo int `gorm:"type:int;not null;default:0;index"` // SortNo 为后台展示排序号。
|
SortNo int `gorm:"type:int;not null;default:0;index"` // SortNo 为后台展示排序号。
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ type CreateAIAgentRequest struct {
|
|||||||
FallbackMessage string `json:"fallbackMessage"`
|
FallbackMessage string `json:"fallbackMessage"`
|
||||||
KnowledgeBaseIDs []int64 `json:"knowledgeBaseIds"`
|
KnowledgeBaseIDs []int64 `json:"knowledgeBaseIds"`
|
||||||
SkillIDs []int64 `json:"skillIds"`
|
SkillIDs []int64 `json:"skillIds"`
|
||||||
DirectTools []AIAgentMCPToolRequest `json:"directTools"`
|
MCPTools []AIAgentMCPToolRequest `json:"mcpTools"`
|
||||||
WorkflowBindings []AIAgentWorkflowBindingRequest `json:"workflowBindings"`
|
WorkflowBindings []AIAgentWorkflowBindingRequest `json:"workflowBindings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ type AIAgentResponse struct {
|
|||||||
KnowledgeBaseIDs []int64 `json:"knowledgeBaseIds"`
|
KnowledgeBaseIDs []int64 `json:"knowledgeBaseIds"`
|
||||||
SkillIDs []int64 `json:"skillIds"`
|
SkillIDs []int64 `json:"skillIds"`
|
||||||
Skills []AIAgentSkillResponse `json:"skills"`
|
Skills []AIAgentSkillResponse `json:"skills"`
|
||||||
DirectTools []AIAgentMCPToolResponse `json:"directTools"`
|
MCPTools []AIAgentMCPToolResponse `json:"mcpTools"`
|
||||||
WorkflowBindings []AIAgentWorkflowBindingResponse `json:"workflowBindings"`
|
WorkflowBindings []AIAgentWorkflowBindingResponse `json:"workflowBindings"`
|
||||||
WorkflowVersionID int64 `json:"workflowVersionId"`
|
WorkflowVersionID int64 `json:"workflowVersionId"`
|
||||||
PublishedRevisionID int64 `json:"publishedRevisionId"`
|
PublishedRevisionID int64 `json:"publishedRevisionId"`
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ error.e0011: "The AI configuration is not enabled."
|
|||||||
error.e0012: "AI configuration not found."
|
error.e0012: "AI configuration not found."
|
||||||
error.e0014: "Checkpoint not found."
|
error.e0014: "Checkpoint not found."
|
||||||
error.e0015: "The checkpoint does not belong to this AI Agent."
|
error.e0015: "The checkpoint does not belong to this AI Agent."
|
||||||
error.e0016: "Direct Tool toolCode does not match serverCode."
|
error.e0016: "MCP Tool toolCode does not match serverCode."
|
||||||
error.e0017: "Direct Tool toolCode does not match toolName."
|
error.e0017: "MCP Tool toolCode does not match toolName."
|
||||||
error.e0018: "Invalid Direct Tool toolCode format."
|
error.e0018: "Invalid MCP Tool toolCode format."
|
||||||
error.e0019: "Direct Tool toolCode, serverCode, and toolName are required."
|
error.e0019: "MCP Tool toolCode, serverCode, and toolName are required."
|
||||||
error.e0020: "Direct Tools can only contain MCP tools."
|
error.e0020: "Only MCP Tools can be configured."
|
||||||
error.e0021: "Invalid Direct Tools configuration format."
|
error.e0021: "Invalid MCP Tool configuration."
|
||||||
error.e0022: "The Excel file is empty."
|
error.e0022: "The Excel file is empty."
|
||||||
error.e0023: "Failed to parse the Excel file."
|
error.e0023: "Failed to parse the Excel file."
|
||||||
error.e0024: "Failed to read the Excel file."
|
error.e0024: "Failed to read the Excel file."
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ error.e0011: "AI 配置未启用"
|
|||||||
error.e0012: "AI配置不存在"
|
error.e0012: "AI配置不存在"
|
||||||
error.e0014: "CheckPoint 不存在"
|
error.e0014: "CheckPoint 不存在"
|
||||||
error.e0015: "CheckPoint 与 AI Agent 不匹配"
|
error.e0015: "CheckPoint 与 AI Agent 不匹配"
|
||||||
error.e0016: "Direct Tool 的 toolCode 与 serverCode 不一致"
|
error.e0016: "MCP Tool 的 toolCode 与 serverCode 不一致"
|
||||||
error.e0017: "Direct Tool 的 toolCode 与 toolName 不一致"
|
error.e0017: "MCP Tool 的 toolCode 与 toolName 不一致"
|
||||||
error.e0018: "Direct Tool 的 toolCode 格式不合法"
|
error.e0018: "MCP Tool 的 toolCode 格式不合法"
|
||||||
error.e0019: "Direct Tool 的 toolCode、serverCode 和 toolName 不能为空"
|
error.e0019: "MCP Tool 的 toolCode、serverCode 和 toolName 不能为空"
|
||||||
error.e0020: "Direct Tools 仅允许配置 MCP 工具"
|
error.e0020: "仅允许配置 MCP Tool"
|
||||||
error.e0021: "Direct Tools 配置格式不合法"
|
error.e0021: "MCP Tool 配置格式不合法"
|
||||||
error.e0022: "Excel文件为空"
|
error.e0022: "Excel文件为空"
|
||||||
error.e0023: "Excel文件解析失败"
|
error.e0023: "Excel文件解析失败"
|
||||||
error.e0024: "Excel文件读取失败"
|
error.e0024: "Excel文件读取失败"
|
||||||
|
|||||||
@@ -238,17 +238,17 @@ func (s *aIAgentService) validatePublishableAgent(db *gorm.DB, agent *models.AIA
|
|||||||
if strings.TrimSpace(agent.AllowedMCPTools) == "" {
|
if strings.TrimSpace(agent.AllowedMCPTools) == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var directTools []request.AIAgentMCPToolRequest
|
var mcpTools []request.AIAgentMCPToolRequest
|
||||||
if err := json.Unmarshal([]byte(agent.AllowedMCPTools), &directTools); err != nil {
|
if err := json.Unmarshal([]byte(agent.AllowedMCPTools), &mcpTools); err != nil {
|
||||||
return errorsx.InvalidParam("ai agent direct tools are invalid")
|
return errorsx.InvalidParam("ai agent MCP tools are invalid")
|
||||||
}
|
}
|
||||||
for _, item := range directTools {
|
for _, item := range mcpTools {
|
||||||
definition, err := aitooling.DefaultRegistry.Resolve(item.ToolCode)
|
definition, err := aitooling.DefaultRegistry.Resolve(item.ToolCode)
|
||||||
if err != nil || definition.InputSchema == nil {
|
if err != nil || definition.InputSchema == nil {
|
||||||
return errorsx.InvalidParam("ai agent direct tool definition is unavailable")
|
return errorsx.InvalidParam("ai agent MCP tool definition is unavailable")
|
||||||
}
|
}
|
||||||
if definition.RequireConfirmation {
|
if definition.RequireConfirmation {
|
||||||
return errorsx.InvalidParam("ai agent direct tool requires confirmation and cannot be executed directly")
|
return errorsx.InvalidParam("ai agent MCP tool requires confirmation and cannot be executed directly")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -392,17 +392,17 @@ func (s *aIAgentService) buildAIAgentModel(id int64, req request.CreateAIAgentRe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
directTools, err := s.normalizeDirectTools(req.DirectTools)
|
mcpTools, err := s.normalizeMCPTools(req.MCPTools)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
directToolsJSON := ""
|
mcpToolsJSON := ""
|
||||||
if len(directTools) > 0 {
|
if len(mcpTools) > 0 {
|
||||||
buf, marshalErr := json.Marshal(directTools)
|
buf, marshalErr := json.Marshal(mcpTools)
|
||||||
if marshalErr != nil {
|
if marshalErr != nil {
|
||||||
return nil, errorsx.InvalidParamI18n("error.e0021")
|
return nil, errorsx.InvalidParamI18n("error.e0021")
|
||||||
}
|
}
|
||||||
directToolsJSON = string(buf)
|
mcpToolsJSON = string(buf)
|
||||||
}
|
}
|
||||||
return &models.AIAgent{
|
return &models.AIAgent{
|
||||||
Name: name,
|
Name: name,
|
||||||
@@ -424,7 +424,7 @@ func (s *aIAgentService) buildAIAgentModel(id int64, req request.CreateAIAgentRe
|
|||||||
FallbackMessage: strings.TrimSpace(req.FallbackMessage),
|
FallbackMessage: strings.TrimSpace(req.FallbackMessage),
|
||||||
KnowledgeIDs: utils.JoinInt64s(knowledgeBaseIDs),
|
KnowledgeIDs: utils.JoinInt64s(knowledgeBaseIDs),
|
||||||
SkillIDs: utils.JoinInt64s(skillIDs),
|
SkillIDs: utils.JoinInt64s(skillIDs),
|
||||||
AllowedMCPTools: directToolsJSON,
|
AllowedMCPTools: mcpToolsJSON,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,7 +541,7 @@ func (s *aIAgentService) normalizeSkillIDs(input []int64) ([]int64, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *aIAgentService) normalizeDirectTools(input []request.AIAgentMCPToolRequest) ([]request.AIAgentMCPToolRequest, error) {
|
func (s *aIAgentService) normalizeMCPTools(input []request.AIAgentMCPToolRequest) ([]request.AIAgentMCPToolRequest, error) {
|
||||||
if len(input) == 0 {
|
if len(input) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -552,20 +552,11 @@ func (s *aIAgentService) normalizeDirectTools(input []request.AIAgentMCPToolRequ
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if toolx.IsAutoInjectedToolCode(strings.TrimSpace(normalized.ToolCode)) {
|
if toolx.ResolveToolSourceType(normalized.ToolCode) != enums.ToolSourceTypeMCP {
|
||||||
continue
|
return nil, errorsx.InvalidParamI18n("error.e0020")
|
||||||
}
|
}
|
||||||
if spec, registered := toolx.GetRegisteredToolSpec(normalized.ToolCode); registered {
|
if err := ToolCatalogService.ValidateToolCode(normalized.ToolCode); err != nil {
|
||||||
if !spec.DirectAccess || spec.AutoInjected || (spec.Code != toolx.BuiltinConversationContext.Code && spec.Code != toolx.BuiltinKnowledgeRetrieve.Code && spec.Code != toolx.GraphTriageServiceRequest.Code && spec.Code != toolx.GraphAnalyzeConversation.Code && spec.Code != toolx.GraphPrepareTicketDraft.Code) {
|
return nil, err
|
||||||
return nil, errorsx.InvalidParamI18n("error.e0020")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if toolx.ResolveToolSourceType(normalized.ToolCode) != enums.ToolSourceTypeMCP {
|
|
||||||
return nil, errorsx.InvalidParamI18n("error.e0020")
|
|
||||||
}
|
|
||||||
if err := ToolCatalogService.ValidateToolCode(normalized.ToolCode); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
key := strings.TrimSpace(normalized.ToolCode)
|
key := strings.TrimSpace(normalized.ToolCode)
|
||||||
if _, exists := seen[key]; exists {
|
if _, exists := seen[key]; exists {
|
||||||
|
|||||||
@@ -203,32 +203,18 @@ func TestAIAgentServiceNormalizesToolPolicy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAIAgentServiceAllowsRegisteredReadDirectTool(t *testing.T) {
|
func TestAIAgentServiceRejectsNonMCPToolSelection(t *testing.T) {
|
||||||
tools, err := AIAgentService.normalizeDirectTools([]request.AIAgentMCPToolRequest{{ToolCode: toolx.BuiltinConversationContext.Code}})
|
for _, toolCode := range []string{
|
||||||
if err != nil {
|
toolx.BuiltinConversationContext.Code,
|
||||||
t.Fatalf("normalizeDirectTools: %v", err)
|
toolx.BuiltinKnowledgeRetrieve.Code,
|
||||||
}
|
toolx.GraphPrepareTicketDraft.Code,
|
||||||
if len(tools) != 1 || tools[0].ToolCode != toolx.BuiltinConversationContext.Code {
|
toolx.GraphAnalyzeConversation.Code,
|
||||||
t.Fatalf("unexpected normalized direct tools: %#v", tools)
|
toolx.GraphTriageServiceRequest.Code,
|
||||||
}
|
toolx.GraphHandoffConversation.Code,
|
||||||
tools, err = AIAgentService.normalizeDirectTools([]request.AIAgentMCPToolRequest{{ToolCode: toolx.BuiltinKnowledgeRetrieve.Code}})
|
} {
|
||||||
if err != nil || len(tools) != 1 || tools[0].ToolCode != toolx.BuiltinKnowledgeRetrieve.Code {
|
if _, err := AIAgentService.normalizeMCPTools([]request.AIAgentMCPToolRequest{{ToolCode: toolCode}}); err == nil {
|
||||||
t.Fatalf("expected registered knowledge retrieve tool to be allowed, tools=%#v err=%v", tools, err)
|
t.Fatalf("expected non-MCP tool %q to be rejected", toolCode)
|
||||||
}
|
}
|
||||||
tools, err = AIAgentService.normalizeDirectTools([]request.AIAgentMCPToolRequest{{ToolCode: toolx.GraphPrepareTicketDraft.Code}})
|
|
||||||
if err != nil || len(tools) != 1 || tools[0].ToolCode != toolx.GraphPrepareTicketDraft.Code {
|
|
||||||
t.Fatalf("expected registered ticket draft tool to be allowed, tools=%#v err=%v", tools, err)
|
|
||||||
}
|
|
||||||
tools, err = AIAgentService.normalizeDirectTools([]request.AIAgentMCPToolRequest{{ToolCode: toolx.GraphAnalyzeConversation.Code}})
|
|
||||||
if err != nil || len(tools) != 1 || tools[0].ToolCode != toolx.GraphAnalyzeConversation.Code {
|
|
||||||
t.Fatalf("expected registered conversation analysis tool to be allowed, tools=%#v err=%v", tools, err)
|
|
||||||
}
|
|
||||||
tools, err = AIAgentService.normalizeDirectTools([]request.AIAgentMCPToolRequest{{ToolCode: toolx.GraphTriageServiceRequest.Code}})
|
|
||||||
if err != nil || len(tools) != 1 || tools[0].ToolCode != toolx.GraphTriageServiceRequest.Code {
|
|
||||||
t.Fatalf("expected registered service triage tool to be allowed, tools=%#v err=%v", tools, err)
|
|
||||||
}
|
|
||||||
if _, err := AIAgentService.normalizeDirectTools([]request.AIAgentMCPToolRequest{{ToolCode: toolx.GraphHandoffConversation.Code}}); err == nil {
|
|
||||||
t.Fatal("expected unsupported graph direct tool to be rejected")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +336,7 @@ func TestAIAgentServiceAllowsPublishWithAdministratorSelectedMCPTool(t *testing.
|
|||||||
t.Fatalf("CreateAIAgent() error = %v", err)
|
t.Fatalf("CreateAIAgent() error = %v", err)
|
||||||
}
|
}
|
||||||
if err := sqls.DB().Model(&models.AIAgent{}).Where("id = ?", agent.ID).Update("allowed_mcp_tools", `[{"toolCode":"mcp/demo/write_order"}]`).Error; err != nil {
|
if err := sqls.DB().Model(&models.AIAgent{}).Where("id = ?", agent.ID).Update("allowed_mcp_tools", `[{"toolCode":"mcp/demo/write_order"}]`).Error; err != nil {
|
||||||
t.Fatalf("set direct tool: %v", err)
|
t.Fatalf("set MCP tool: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := AIAgentService.PublishAIAgent(agent.ID, operator); err != nil {
|
if _, err := AIAgentService.PublishAIAgent(agent.ID, operator); err != nil {
|
||||||
t.Fatalf("expected administrator-selected MCP tool to be publishable, got %v", err)
|
t.Fatalf("expected administrator-selected MCP tool to be publishable, got %v", err)
|
||||||
|
|||||||
@@ -63,15 +63,15 @@ import { cn } from "@/lib/utils"
|
|||||||
|
|
||||||
type RuntimeMode = "workflow" | "autonomous" | "hybrid"
|
type RuntimeMode = "workflow" | "autonomous" | "hybrid"
|
||||||
type SectionKey = "setup" | "persona" | "capability" | "service"
|
type SectionKey = "setup" | "persona" | "capability" | "service"
|
||||||
type DirectToolItem = CreateAIAgentPayload["directTools"][number]
|
type MCPToolItem = CreateAIAgentPayload["mcpTools"][number]
|
||||||
|
|
||||||
type DirectToolOption = {
|
type MCPToolOption = {
|
||||||
value: string
|
value: string
|
||||||
label: string
|
label: string
|
||||||
group: string
|
group: string
|
||||||
subtitle: string
|
subtitle: string
|
||||||
description?: string
|
description?: string
|
||||||
meta: DirectToolItem
|
meta: MCPToolItem
|
||||||
}
|
}
|
||||||
|
|
||||||
const runtimeModes: {
|
const runtimeModes: {
|
||||||
@@ -142,7 +142,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
const [selectedTeamIds, setSelectedTeamIds] = useState<number[]>([])
|
const [selectedTeamIds, setSelectedTeamIds] = useState<number[]>([])
|
||||||
const [selectedSkillIds, setSelectedSkillIds] = useState<number[]>([])
|
const [selectedSkillIds, setSelectedSkillIds] = useState<number[]>([])
|
||||||
const [selectedKnowledgeBaseIds, setSelectedKnowledgeBaseIds] = useState<number[]>([])
|
const [selectedKnowledgeBaseIds, setSelectedKnowledgeBaseIds] = useState<number[]>([])
|
||||||
const [directTools, setDirectTools] = useState<DirectToolItem[]>([])
|
const [mcpTools, setMCPTools] = useState<MCPToolItem[]>([])
|
||||||
const [workflowBindings, setWorkflowBindings] = useState<AIAgentWorkflowBindingInput[]>([])
|
const [workflowBindings, setWorkflowBindings] = useState<AIAgentWorkflowBindingInput[]>([])
|
||||||
|
|
||||||
const [aiConfigs, setAIConfigs] = useState<AIConfig[]>([])
|
const [aiConfigs, setAIConfigs] = useState<AIConfig[]>([])
|
||||||
@@ -197,7 +197,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
setSelectedTeamIds([])
|
setSelectedTeamIds([])
|
||||||
setSelectedSkillIds([])
|
setSelectedSkillIds([])
|
||||||
setSelectedKnowledgeBaseIds([])
|
setSelectedKnowledgeBaseIds([])
|
||||||
setDirectTools([])
|
setMCPTools([])
|
||||||
setWorkflowBindings([])
|
setWorkflowBindings([])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -226,7 +226,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
setSelectedTeamIds((detail.teams ?? []).map((team) => team.id))
|
setSelectedTeamIds((detail.teams ?? []).map((team) => team.id))
|
||||||
setSelectedSkillIds(detail.skillIds ?? [])
|
setSelectedSkillIds(detail.skillIds ?? [])
|
||||||
setSelectedKnowledgeBaseIds(detail.knowledgeBaseIds ?? [])
|
setSelectedKnowledgeBaseIds(detail.knowledgeBaseIds ?? [])
|
||||||
setDirectTools(detail.directTools ?? [])
|
setMCPTools(detail.mcpTools ?? [])
|
||||||
setWorkflowBindings(
|
setWorkflowBindings(
|
||||||
(detail.workflowBindings ?? []).map(
|
(detail.workflowBindings ?? []).map(
|
||||||
({ workflowVersionId, toolName, triggerInstruction, priority, enabled }) => ({
|
({ workflowVersionId, toolName, triggerInstruction, priority, enabled }) => ({
|
||||||
@@ -299,20 +299,14 @@ export function AIAgentConfigWorkbench({
|
|||||||
() => knowledgeBases.map((item) => ({ value: String(item.id), label: item.name })),
|
() => knowledgeBases.map((item) => ({ value: String(item.id), label: item.name })),
|
||||||
[knowledgeBases],
|
[knowledgeBases],
|
||||||
)
|
)
|
||||||
const directToolOptions = useMemo<DirectToolOption[]>(
|
const mcpToolOptions = useMemo<MCPToolOption[]>(
|
||||||
() =>
|
() =>
|
||||||
toolCatalog
|
toolCatalog
|
||||||
.filter(
|
.filter((tool) => !tool.autoInjected && tool.sourceType === "mcp")
|
||||||
(tool) =>
|
|
||||||
!tool.autoInjected &&
|
|
||||||
(tool.sourceType === "mcp" ||
|
|
||||||
tool.toolCode === "builtin/conversation_context" ||
|
|
||||||
tool.toolCode === "graph/prepare_ticket_draft"),
|
|
||||||
)
|
|
||||||
.map((tool) => ({
|
.map((tool) => ({
|
||||||
value: tool.toolCode,
|
value: tool.toolCode,
|
||||||
label: tool.title || tool.toolName,
|
label: tool.title || tool.toolName,
|
||||||
group: tool.sourceType === "builtin" ? "内置工具" : tool.serverCode,
|
group: tool.serverCode,
|
||||||
subtitle: tool.toolCode,
|
subtitle: tool.toolCode,
|
||||||
description: tool.description || undefined,
|
description: tool.description || undefined,
|
||||||
meta: {
|
meta: {
|
||||||
@@ -348,10 +342,10 @@ export function AIAgentConfigWorkbench({
|
|||||||
setNext([...current, id])
|
setNext([...current, id])
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDirectToolSelection(values: string[]) {
|
function setMCPToolSelection(values: string[]) {
|
||||||
setDirectTools(
|
setMCPTools(
|
||||||
values.flatMap((value) => {
|
values.flatMap((value) => {
|
||||||
const option = directToolOptions.find((item) => item.value === value)
|
const option = mcpToolOptions.find((item) => item.value === value)
|
||||||
return option ? [option.meta] : []
|
return option ? [option.meta] : []
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -430,7 +424,7 @@ export function AIAgentConfigWorkbench({
|
|||||||
fallbackMessage: fallbackMessage.trim(),
|
fallbackMessage: fallbackMessage.trim(),
|
||||||
knowledgeBaseIds: uniqueNumbers(selectedKnowledgeBaseIds),
|
knowledgeBaseIds: uniqueNumbers(selectedKnowledgeBaseIds),
|
||||||
skillIds: uniqueNumbers(selectedSkillIds),
|
skillIds: uniqueNumbers(selectedSkillIds),
|
||||||
directTools,
|
mcpTools,
|
||||||
workflowBindings,
|
workflowBindings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -775,16 +769,16 @@ export function AIAgentConfigWorkbench({
|
|||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
<FormSection
|
<FormSection
|
||||||
title="Direct Tool"
|
title="MCP Tool"
|
||||||
description="从工具分组中选择 Agent 可以直接调用的工具。"
|
description="选择允许 Agent 调用的 MCP Tool,选项与 MCP Tool 管理中的可用工具保持一致。"
|
||||||
>
|
>
|
||||||
<OptionCombobox
|
<OptionCombobox
|
||||||
multiple
|
multiple
|
||||||
values={directTools.map((tool) => tool.toolCode)}
|
values={mcpTools.map((tool) => tool.toolCode)}
|
||||||
options={directToolOptions}
|
options={mcpToolOptions}
|
||||||
placeholder="选择 Direct Tool"
|
placeholder="选择 MCP Tool"
|
||||||
emptyText="没有可用 Direct Tool"
|
emptyText="没有可用 MCP Tool"
|
||||||
onValuesChange={setDirectToolSelection}
|
onValuesChange={setMCPToolSelection}
|
||||||
/>
|
/>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -182,23 +182,23 @@ export default function DashboardAIAgentsPage() {
|
|||||||
label: t("aiAgent.columnCapabilities"),
|
label: t("aiAgent.columnCapabilities"),
|
||||||
render: (item) => {
|
render: (item) => {
|
||||||
const skills = item.skills ?? [];
|
const skills = item.skills ?? [];
|
||||||
const directTools = item.directTools ?? [];
|
const mcpTools = item.mcpTools ?? [];
|
||||||
const directToolServerCodes = Array.from(
|
const mcpServerCodes = Array.from(
|
||||||
new Set(directTools.map((tool) => tool.serverCode).filter(Boolean)),
|
new Set(mcpTools.map((tool) => tool.serverCode).filter(Boolean)),
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
<Badge variant="secondary">{skills.length} Skills</Badge>
|
<Badge variant="secondary">{skills.length} Skills</Badge>
|
||||||
<Badge variant="secondary">{directTools.length} Tools</Badge>
|
<Badge variant="secondary">{mcpTools.length} MCP Tools</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{directToolServerCodes.length === 0 ? (
|
{mcpServerCodes.length === 0 ? (
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
{t("aiAgent.noMcpServer")}
|
{t("aiAgent.noMcpServer")}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
directToolServerCodes.map((serverCode) => (
|
mcpServerCodes.map((serverCode) => (
|
||||||
<Badge key={serverCode} variant="outline">
|
<Badge key={serverCode} variant="outline">
|
||||||
{serverCode}
|
{serverCode}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ export type AIAgent = {
|
|||||||
knowledgeBaseIds: number[]
|
knowledgeBaseIds: number[]
|
||||||
skillIds: number[]
|
skillIds: number[]
|
||||||
skills: { id: number; name: string }[]
|
skills: { id: number; name: string }[]
|
||||||
directTools: {
|
mcpTools: {
|
||||||
toolCode: string
|
toolCode: string
|
||||||
serverCode: string
|
serverCode: string
|
||||||
toolName: string
|
toolName: string
|
||||||
@@ -282,7 +282,7 @@ export type CreateAIAgentPayload = {
|
|||||||
fallbackMessage: string
|
fallbackMessage: string
|
||||||
knowledgeBaseIds: number[]
|
knowledgeBaseIds: number[]
|
||||||
skillIds: number[]
|
skillIds: number[]
|
||||||
directTools: {
|
mcpTools: {
|
||||||
toolCode: string
|
toolCode: string
|
||||||
serverCode: string
|
serverCode: string
|
||||||
toolName: string
|
toolName: string
|
||||||
|
|||||||
@@ -1049,7 +1049,7 @@
|
|||||||
"loadTeamsFailed": "Could not load support teams.",
|
"loadTeamsFailed": "Could not load support teams.",
|
||||||
"loadKnowledgeFailed": "Could not load knowledge bases.",
|
"loadKnowledgeFailed": "Could not load knowledge bases.",
|
||||||
"loadSkillsFailed": "Could not load skills.",
|
"loadSkillsFailed": "Could not load skills.",
|
||||||
"loadDirectToolsFailed": "Could not load Direct Tools.",
|
"loadMCPToolsFailed": "Could not load MCP Tools.",
|
||||||
"builtinTools": "Built-in tools",
|
"builtinTools": "Built-in tools",
|
||||||
"ungrouped": "Ungrouped",
|
"ungrouped": "Ungrouped",
|
||||||
"notSelected": "Not selected",
|
"notSelected": "Not selected",
|
||||||
@@ -1090,7 +1090,7 @@
|
|||||||
"sectionCapabilities": "Capabilities",
|
"sectionCapabilities": "Capabilities",
|
||||||
"knowledgeTab": "Knowledge",
|
"knowledgeTab": "Knowledge",
|
||||||
"skillsTab": "Skills",
|
"skillsTab": "Skills",
|
||||||
"directToolsTab": "MCP tools",
|
"mcpToolsTab": "MCP Tools",
|
||||||
"knowledgeHint": "Select at least one knowledge base. You can adjust priority order.",
|
"knowledgeHint": "Select at least one knowledge base. You can adjust priority order.",
|
||||||
"selectKnowledge": "Select and add knowledge base",
|
"selectKnowledge": "Select and add knowledge base",
|
||||||
"searchKnowledge": "Search knowledge bases",
|
"searchKnowledge": "Search knowledge bases",
|
||||||
@@ -1109,15 +1109,15 @@
|
|||||||
"emptySkill": "No skills available",
|
"emptySkill": "No skills available",
|
||||||
"noSkillsHint": "Without skills, routing only uses knowledge bases or human handoff.",
|
"noSkillsHint": "Without skills, routing only uses knowledge bases or human handoff.",
|
||||||
"removeSkill": "Remove skill {name}",
|
"removeSkill": "Remove skill {name}",
|
||||||
"directToolsHint": "Use only for low-risk atomic lookups through external MCP tools.",
|
"mcpToolsHint": "Select the MCP Tools this Agent is allowed to call.",
|
||||||
"selectToolGroup": "Select tool group",
|
"selectToolGroup": "Select tool group",
|
||||||
"searchToolGroup": "Search tool groups",
|
"searchToolGroup": "Search tool groups",
|
||||||
"emptyToolGroup": "No tool groups available",
|
"emptyToolGroup": "No tool groups available",
|
||||||
"selectDirectTool": "Select Direct Tool",
|
"selectMCPTool": "Select MCP Tool",
|
||||||
"searchDirectTool": "Search Direct Tools",
|
"searchMCPTool": "Search MCP Tools",
|
||||||
"emptyDirectTool": "No Direct Tools available",
|
"emptyMCPTool": "No MCP Tools available",
|
||||||
"noDirectToolsHint": "Without Direct Tools, the agent will not call external or built-in tools directly. It will rely on knowledge bases, skills, and normal replies.",
|
"noMCPToolsHint": "Without MCP Tools, the Agent will not call external MCP Tools.",
|
||||||
"removeDirectTool": "Remove Direct Tool {name}"
|
"removeMCPTool": "Remove MCP Tool {name}"
|
||||||
},
|
},
|
||||||
"aiConfig": {
|
"aiConfig": {
|
||||||
"allStatuses": "All statuses",
|
"allStatuses": "All statuses",
|
||||||
|
|||||||
@@ -1049,7 +1049,7 @@
|
|||||||
"loadTeamsFailed": "加载客服组失败",
|
"loadTeamsFailed": "加载客服组失败",
|
||||||
"loadKnowledgeFailed": "加载知识库失败",
|
"loadKnowledgeFailed": "加载知识库失败",
|
||||||
"loadSkillsFailed": "加载 Skills 失败",
|
"loadSkillsFailed": "加载 Skills 失败",
|
||||||
"loadDirectToolsFailed": "加载 Direct Tools 失败",
|
"loadMCPToolsFailed": "加载 MCP Tool 失败",
|
||||||
"builtinTools": "内置工具",
|
"builtinTools": "内置工具",
|
||||||
"ungrouped": "未分组",
|
"ungrouped": "未分组",
|
||||||
"notSelected": "未选择",
|
"notSelected": "未选择",
|
||||||
@@ -1090,7 +1090,7 @@
|
|||||||
"sectionCapabilities": "能力配置",
|
"sectionCapabilities": "能力配置",
|
||||||
"knowledgeTab": "知识库",
|
"knowledgeTab": "知识库",
|
||||||
"skillsTab": "Skills",
|
"skillsTab": "Skills",
|
||||||
"directToolsTab": "MCP tools",
|
"mcpToolsTab": "MCP Tool",
|
||||||
"knowledgeHint": "至少选择一个知识库,可调整知识库优先级。",
|
"knowledgeHint": "至少选择一个知识库,可调整知识库优先级。",
|
||||||
"selectKnowledge": "选择并添加知识库",
|
"selectKnowledge": "选择并添加知识库",
|
||||||
"searchKnowledge": "搜索知识库",
|
"searchKnowledge": "搜索知识库",
|
||||||
@@ -1109,15 +1109,15 @@
|
|||||||
"emptySkill": "没有可添加的 Skill",
|
"emptySkill": "没有可添加的 Skill",
|
||||||
"noSkillsHint": "不绑定 Skill 时,自动路由只会走知识库或转人工。",
|
"noSkillsHint": "不绑定 Skill 时,自动路由只会走知识库或转人工。",
|
||||||
"removeSkill": "移除 Skill {name}",
|
"removeSkill": "移除 Skill {name}",
|
||||||
"directToolsHint": "仅用于外部 MCP 工具的低风险、原子化查询。",
|
"mcpToolsHint": "选择允许 Agent 调用的 MCP Tool。",
|
||||||
"selectToolGroup": "选择工具分组",
|
"selectToolGroup": "选择工具分组",
|
||||||
"searchToolGroup": "搜索工具分组",
|
"searchToolGroup": "搜索工具分组",
|
||||||
"emptyToolGroup": "没有可用的工具分组",
|
"emptyToolGroup": "没有可用的工具分组",
|
||||||
"selectDirectTool": "选择 Direct Tool",
|
"selectMCPTool": "选择 MCP Tool",
|
||||||
"searchDirectTool": "搜索 Direct Tool",
|
"searchMCPTool": "搜索 MCP Tool",
|
||||||
"emptyDirectTool": "没有可添加的 Direct Tool",
|
"emptyMCPTool": "没有可添加的 MCP Tool",
|
||||||
"noDirectToolsHint": "不配置 Direct Tool 时,Agent 不会直接调用外部或内置工具,只会依赖知识库、Skill 和普通回复。",
|
"noMCPToolsHint": "不配置 MCP Tool 时,Agent 不会调用外部 MCP Tool。",
|
||||||
"removeDirectTool": "移除 Direct Tool {name}"
|
"removeMCPTool": "移除 MCP Tool {name}"
|
||||||
},
|
},
|
||||||
"aiConfig": {
|
"aiConfig": {
|
||||||
"allStatuses": "全部状态",
|
"allStatuses": "全部状态",
|
||||||
|
|||||||
Reference in New Issue
Block a user