diff --git a/cmd/testdata/aiagent/init.go b/cmd/testdata/aiagent/init.go index a836282..c341692 100644 --- a/cmd/testdata/aiagent/init.go +++ b/cmd/testdata/aiagent/init.go @@ -142,14 +142,12 @@ func getDefaultTeamIDs() string { } func getDefaultSkillIDs() (string, error) { - skillItem := repositories.SkillDefinitionRepository.Take( + skillItem := repositories.SkillDefinitionRepository.FindOne( sqls.DB(), - "code = ? AND status = ?", - skill.AfterSalesEscalationSkillCode, - enums.StatusOk, + sqls.NewCnd().Where("status = ?", enums.StatusOk).Desc("id"), ) if skillItem == nil { - return "", fmt.Errorf("default test skill not found: %s", skill.AfterSalesEscalationSkillCode) + return "", fmt.Errorf("default test skill not found") } return utils.JoinInt64s([]int64{skillItem.ID}), nil } diff --git a/cmd/testdata/seeds/skill.go b/cmd/testdata/seeds/skill.go index d3145aa..7e87c2e 100644 --- a/cmd/testdata/seeds/skill.go +++ b/cmd/testdata/seeds/skill.go @@ -5,10 +5,7 @@ import ( "agent-desk/internal/pkg/enums" ) -const AfterSalesEscalationSkillCode = "after_sales_escalation_skill" - type SkillDefinitionSeed struct { - Code string Name string Description string Instruction string @@ -22,7 +19,6 @@ func SkillDefinitionSeeds(lang seedlang.Language) []SkillDefinitionSeed { if lang == seedlang.English { return []SkillDefinitionSeed{ { - Code: AfterSalesEscalationSkillCode, Name: "After-sales Escalation", Description: "Handles incidents, complaints, after-sales follow-up, ticket creation, and human handoff requests. Match only when the user clearly needs after-sales intervention or escalation; do not match ordinary greetings, product introductions, or general inquiries.", Instruction: `You are the dedicated "After-sales Escalation" skill responsible for customer support requests that require escalation. @@ -65,7 +61,6 @@ Response requirements: } return []SkillDefinitionSeed{ { - Code: AfterSalesEscalationSkillCode, Name: "售后升级处理", Description: "处理报障、投诉、售后跟进、建单、转人工等升级诉求。只在用户明确需要售后介入或问题升级处理时命中,不处理普通问候、产品介绍或泛咨询。", Instruction: `你是“售后升级处理”专项 Skill,负责承接需要升级处理的客服诉求。 diff --git a/cmd/testdata/skill/init.go b/cmd/testdata/skill/init.go index f18c9ed..d2192bc 100644 --- a/cmd/testdata/skill/init.go +++ b/cmd/testdata/skill/init.go @@ -6,13 +6,12 @@ import ( "agent-desk/internal/models" "agent-desk/internal/repositories" "fmt" + "strings" "time" "github.com/mlogclub/simple/sqls" ) -const AfterSalesEscalationSkillCode = seeds.AfterSalesEscalationSkillCode - type InitResult struct { Created int Updated int @@ -24,7 +23,7 @@ func Init(lang seedlang.Language) (*InitResult, error) { for _, item := range seedItems { itemCopy := item if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error { - existing := repositories.SkillDefinitionRepository.Take(ctx.Tx, "code = ?", itemCopy.Code) + existing := repositories.SkillDefinitionRepository.Take(ctx.Tx, "name = ?", strings.TrimSpace(itemCopy.Name)) if existing != nil { if err := ctx.Tx.Model(existing).Updates(&itemCopy).Error; err != nil { return err @@ -50,7 +49,6 @@ func buildModels(lang seedlang.Language) []models.SkillDefinition { items := make([]models.SkillDefinition, 0, len(seedItems)) for _, seed := range seedItems { items = append(items, models.SkillDefinition{ - Code: seed.Code, Name: seed.Name, Description: seed.Description, Instruction: seed.Instruction, diff --git a/internal/ai/application/runtime/summary_builder.go b/internal/ai/application/runtime/summary_builder.go index 440f595..a5af3a5 100644 --- a/internal/ai/application/runtime/summary_builder.go +++ b/internal/ai/application/runtime/summary_builder.go @@ -13,7 +13,7 @@ func toSummary(summary *executor.RunResult) *Summary { RunID: summary.RunID, Status: summary.Status, ReplyText: summary.ReplyText, - PlannedSkillCode: strings.TrimSpace(summary.SelectedSkillCode), + PlannedSkillID: summary.SelectedSkillID, PlannedSkillName: strings.TrimSpace(summary.SelectedSkillName), PlanReason: strings.TrimSpace(summary.SkillRouteReason), SkillRouteTrace: strings.TrimSpace(summary.SkillRouteTrace), diff --git a/internal/ai/application/runtime/types.go b/internal/ai/application/runtime/types.go index ec39927..f2beb1c 100644 --- a/internal/ai/application/runtime/types.go +++ b/internal/ai/application/runtime/types.go @@ -33,7 +33,7 @@ type Summary struct { RunID string Status string ReplyText string - PlannedSkillCode string + PlannedSkillID int64 PlannedSkillName string PlanReason string SkillRouteTrace string diff --git a/internal/ai/runtime/debug_run.go b/internal/ai/runtime/debug_run.go index 1515074..55386a2 100644 --- a/internal/ai/runtime/debug_run.go +++ b/internal/ai/runtime/debug_run.go @@ -2,6 +2,7 @@ package runtime import ( "context" + "fmt" "strings" applicationruntime "agent-desk/internal/ai/application/runtime" @@ -28,6 +29,12 @@ func DebugRunSkill(ctx context.Context, req request.SkillDebugRunRequest) (*resp if aiConfig == nil { return nil, errorsx.InvalidParamI18n("error.e0008") } + skill := svc.SkillDefinitionService.Get(req.SkillDefinitionID) + if skill == nil || skill.Status != enums.StatusOk { + return nil, errorsx.InvalidParamI18n("error.e0054") + } + debugAgent := *aiAgent + debugAgent.SkillIDs = fmt.Sprintf("%d", skill.ID) var conversation *models.Conversation if req.ConversationID > 0 { if conversation = svc.ConversationService.Get(req.ConversationID); conversation == nil { @@ -45,13 +52,13 @@ func DebugRunSkill(ctx context.Context, req request.SkillDebugRunRequest) (*resp summary, err := Service.Run(ctx, applicationruntime.Request{ Conversation: *conversation, UserMessage: message, - AIAgent: *aiAgent, + AIAgent: debugAgent, AIConfig: *aiConfig, }) if err != nil { - return buildSkillDebugRunResponse(req, summary, nil), err + return buildSkillDebugRunResponse(req, summary, skill), err } - return buildSkillDebugRunResponse(req, summary, nil), nil + return buildSkillDebugRunResponse(req, summary, skill), nil } func DebugResumeSkill(ctx context.Context, req request.SkillDebugResumeRequest) (*response.SkillDebugRunResponse, error) { @@ -125,14 +132,14 @@ func buildSkillDebugRunResponse(req request.SkillDebugRunRequest, summary *appli AIAgentID: req.AIAgentID, } if skill != nil { - resp.SkillCode = skill.Code + resp.SkillDefinitionID = skill.ID resp.SkillName = skill.Name } if summary == nil { return resp } - if resp.SkillCode == "" { - resp.SkillCode = strings.TrimSpace(summary.PlannedSkillCode) + if resp.SkillDefinitionID <= 0 { + resp.SkillDefinitionID = summary.PlannedSkillID } resp.ReplyText = summary.ReplyText resp.PlanReason = summary.PlanReason @@ -159,7 +166,7 @@ func buildSkillDebugResumeResponse(req request.SkillDebugResumeRequest, summary if summary == nil { return resp } - resp.SkillCode = strings.TrimSpace(summary.PlannedSkillCode) + resp.SkillDefinitionID = summary.PlannedSkillID resp.SkillName = strings.TrimSpace(summary.PlannedSkillName) resp.ReplyText = summary.ReplyText resp.PlanReason = summary.PlanReason diff --git a/internal/ai/runtime/executor/service.go b/internal/ai/runtime/executor/service.go index cc34fe2..c8d6ed8 100644 --- a/internal/ai/runtime/executor/service.go +++ b/internal/ai/runtime/executor/service.go @@ -213,7 +213,7 @@ func syncSkillSummaryFromCollector(summary *RunResult, collector *callbacks.Runt return } trace := collector.Data.Skill - summary.SelectedSkillCode = strings.TrimSpace(trace.Code) + summary.SelectedSkillID = trace.ID summary.SelectedSkillName = strings.TrimSpace(trace.Name) summary.SkillRouteReason = strings.TrimSpace(trace.RouteReason) summary.SkillRouteTrace = strings.TrimSpace(trace.RouteTrace) diff --git a/internal/ai/runtime/executor/types.go b/internal/ai/runtime/executor/types.go index 9eb3290..2ae97fe 100644 --- a/internal/ai/runtime/executor/types.go +++ b/internal/ai/runtime/executor/types.go @@ -33,7 +33,7 @@ type RunResult struct { RunID string Status string ReplyText string - SelectedSkillCode string + SelectedSkillID int64 SelectedSkillName string SkillRouteReason string SkillRouteTrace string diff --git a/internal/ai/runtime/instruction/helpers.go b/internal/ai/runtime/instruction/helpers.go index 0d9633a..e297966 100644 --- a/internal/ai/runtime/instruction/helpers.go +++ b/internal/ai/runtime/instruction/helpers.go @@ -16,7 +16,7 @@ func BuildSelectedSkillActivationInstruction(skill *models.SkillDefinition) stri } lines := []string{ "当前命中的专项技能:", - fmt.Sprintf("- code: %s", strings.TrimSpace(skill.Code)), + fmt.Sprintf("- id: %d", skill.ID), fmt.Sprintf("- name: %s", strings.TrimSpace(skill.Name)), } if desc := strings.TrimSpace(skill.Description); desc != "" { @@ -36,7 +36,7 @@ func BuildSkillDocument(skill *models.SkillDefinition, toolDefinitions []runtime } lines := []string{ "当前命中的专项技能:", - fmt.Sprintf("- code: %s", strings.TrimSpace(skill.Code)), + fmt.Sprintf("- id: %d", skill.ID), fmt.Sprintf("- name: %s", strings.TrimSpace(skill.Name)), } if desc := strings.TrimSpace(skill.Description); desc != "" { diff --git a/internal/ai/runtime/internal/impl/callbacks/agent_trace_handler.go b/internal/ai/runtime/internal/impl/callbacks/agent_trace_handler.go index 32fd458..11fd55f 100644 --- a/internal/ai/runtime/internal/impl/callbacks/agent_trace_handler.go +++ b/internal/ai/runtime/internal/impl/callbacks/agent_trace_handler.go @@ -3,6 +3,7 @@ package callbacks import ( "context" "encoding/json" + "strconv" "strings" "time" @@ -142,17 +143,19 @@ func (h *RuntimeTraceHandler) tryActivateSkill(argumentsInJSON string) { if err := json.Unmarshal([]byte(strings.TrimSpace(argumentsInJSON)), &args); err != nil { return } - code := strings.TrimSpace(args.Skill) - if code == "" { + skillKey := strings.TrimSpace(args.Skill) + if skillKey == "" { return } - meta, ok := h.skillMetadataBy[code] + meta, ok := h.skillMetadataBy[skillKey] if !ok { - meta = SkillMetadata{Code: code} + if id, err := strconv.ParseInt(skillKey, 10, 64); err == nil { + meta = SkillMetadata{ID: id} + } } buf, err := json.Marshal(map[string]any{ - "source": "eino_skill_tool", - "skill": code, + "source": "eino_skill_tool", + "skillId": skillKey, }) routeTrace := "" if err == nil { diff --git a/internal/ai/runtime/internal/impl/callbacks/agent_trace_handler_test.go b/internal/ai/runtime/internal/impl/callbacks/agent_trace_handler_test.go index 364902c..08e62f6 100644 --- a/internal/ai/runtime/internal/impl/callbacks/agent_trace_handler_test.go +++ b/internal/ai/runtime/internal/impl/callbacks/agent_trace_handler_test.go @@ -42,18 +42,18 @@ func TestTryActivateSkill(t *testing.T) { handler := &RuntimeTraceHandler{ collector: collector, skillMetadataBy: map[string]SkillMetadata{ - "after_sales_escalation_skill": { - Code: "after_sales_escalation_skill", + "44": { + ID: 44, Name: "售后升级", AllowedToolCodes: []string{"graph/handoff_to_human"}, }, }, } - handler.tryActivateSkill(`{"skill":"after_sales_escalation_skill"}`) + handler.tryActivateSkill(`{"skill":"44"}`) - if collector.Data.Skill.Code != "after_sales_escalation_skill" { - t.Fatalf("unexpected skill code: %#v", collector.Data.Skill) + if collector.Data.Skill.ID != 44 { + t.Fatalf("unexpected skill id: %#v", collector.Data.Skill) } if collector.Data.Skill.Name != "售后升级" { t.Fatalf("unexpected skill name: %#v", collector.Data.Skill) diff --git a/internal/ai/runtime/internal/impl/callbacks/runlog_callback.go b/internal/ai/runtime/internal/impl/callbacks/runlog_callback.go index ff5329a..4612b47 100644 --- a/internal/ai/runtime/internal/impl/callbacks/runlog_callback.go +++ b/internal/ai/runtime/internal/impl/callbacks/runlog_callback.go @@ -52,7 +52,7 @@ func (c *RuntimeTraceCollector) SetSkillMiddleware(enabled bool, toolName string } type SkillMetadata struct { - Code string + ID int64 Name string Description string AllowedToolCodes []string @@ -64,20 +64,20 @@ func (c *RuntimeTraceCollector) SetVisibleSkills(skills map[string]SkillMetadata } c.mu.Lock() defer c.mu.Unlock() - codes := make([]string, 0, len(skills)) - for code := range skills { - if code == "" { + ids := make([]int64, 0, len(skills)) + for _, skill := range skills { + if skill.ID <= 0 { continue } - codes = append(codes, code) + ids = append(ids, skill.ID) } - c.Data.Skill.VisibleCodes = append([]string(nil), codes...) + c.Data.Skill.VisibleIDs = append([]int64(nil), ids...) } func (c *RuntimeTraceCollector) ActivateSkill(skill SkillMetadata, routeReason string, routeTrace string) { c.mu.Lock() defer c.mu.Unlock() - c.Data.Skill.Code = skill.Code + c.Data.Skill.ID = skill.ID c.Data.Skill.Name = skill.Name c.Data.Skill.Description = skill.Description c.Data.Skill.AllowedToolCodes = append([]string(nil), skill.AllowedToolCodes...) diff --git a/internal/ai/runtime/internal/impl/callbacks/trace_callback.go b/internal/ai/runtime/internal/impl/callbacks/trace_callback.go index 3ff7701..018f7ce 100644 --- a/internal/ai/runtime/internal/impl/callbacks/trace_callback.go +++ b/internal/ai/runtime/internal/impl/callbacks/trace_callback.go @@ -152,7 +152,7 @@ type RuntimeTraceData struct { } type SkillTraceData struct { - Code string `json:"code,omitempty"` + ID int64 `json:"id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` RouteReason string `json:"routeReason,omitempty"` @@ -161,7 +161,7 @@ type SkillTraceData struct { FilteredToolCodes []string `json:"filteredToolCodes,omitempty"` MiddlewareEnabled bool `json:"middlewareEnabled,omitempty"` MiddlewareToolName string `json:"middlewareToolName,omitempty"` - VisibleCodes []string `json:"visibleCodes,omitempty"` + VisibleIDs []int64 `json:"visibleIds,omitempty"` } type InterruptTraceContext struct { diff --git a/internal/ai/runtime/internal/impl/factory/agent_handler_service.go b/internal/ai/runtime/internal/impl/factory/agent_handler_service.go index 0d3171c..c9e3126 100644 --- a/internal/ai/runtime/internal/impl/factory/agent_handler_service.go +++ b/internal/ai/runtime/internal/impl/factory/agent_handler_service.go @@ -38,19 +38,19 @@ func NewAgentHandlerService(skillMiddleware *SkillMiddlewareService) *AgentHandl func (s *AgentHandlerService) Build(ctx context.Context, input BuildAgentHandlersInput) ([]adk.ChatModelAgentMiddleware, error) { handlers := make([]adk.ChatModelAgentMiddleware, 0, 4) - skillMetadataByCode := buildRuntimeSkillMetadataMap(input.AIAgent) - toolMetadataBy := buildRuntimeTraceToolMetadata(input.DynamicToolDefinitions, input.StaticToolMetadata, len(skillMetadataByCode) > 0) - traceSkillMetadata := make(map[string]einocallbacks.SkillMetadata, len(skillMetadataByCode)) - for code, item := range skillMetadataByCode { - traceSkillMetadata[code] = einocallbacks.SkillMetadata{ - Code: item.Code, + skillMetadataByID := buildRuntimeSkillMetadataMap(input.AIAgent) + toolMetadataBy := buildRuntimeTraceToolMetadata(input.DynamicToolDefinitions, input.StaticToolMetadata, len(skillMetadataByID) > 0) + traceSkillMetadata := make(map[string]einocallbacks.SkillMetadata, len(skillMetadataByID)) + for id, item := range skillMetadataByID { + traceSkillMetadata[id] = einocallbacks.SkillMetadata{ + ID: item.ID, Name: item.Name, Description: item.Description, AllowedToolCodes: append([]string(nil), item.AllowedToolCodes...), } } if input.Collector != nil { - if len(skillMetadataByCode) > 0 { + if len(skillMetadataByID) > 0 { input.Collector.SetSkillMiddleware(true, toolx.BuiltinSkill.Name) } input.Collector.SetVisibleSkills(traceSkillMetadata) @@ -66,7 +66,7 @@ func (s *AgentHandlerService) Build(ctx context.Context, input BuildAgentHandler } handlers = append(handlers, toolSearchHandler) } - if len(skillMetadataByCode) > 0 { + if len(skillMetadataByID) > 0 { skillHandler, err := s.skillMiddleware.Build(ctx, input.AIAgent, input.InstructionToolDefinitions) if err != nil { return nil, err diff --git a/internal/ai/runtime/internal/impl/factory/skill_middleware_backend.go b/internal/ai/runtime/internal/impl/factory/skill_middleware_backend.go index d616319..b5e0025 100644 --- a/internal/ai/runtime/internal/impl/factory/skill_middleware_backend.go +++ b/internal/ai/runtime/internal/impl/factory/skill_middleware_backend.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "strconv" "strings" runtimeinstruction "agent-desk/internal/ai/runtime/instruction" @@ -17,7 +18,7 @@ import ( ) type runtimeSkillMetadata struct { - Code string + ID int64 Name string Description string AllowedToolCodes []string @@ -25,7 +26,7 @@ type runtimeSkillMetadata struct { type databaseSkillBackend struct { toolDefinitions []runtimetooling.MCPToolDefinition - skillsByCode map[string]models.SkillDefinition + skillsByID map[string]models.SkillDefinition order []string } @@ -36,18 +37,18 @@ func newDatabaseSkillBackend(aiAgent models.AIAgent, toolDefinitions []runtimeto } ret := &databaseSkillBackend{ toolDefinitions: append([]runtimetooling.MCPToolDefinition(nil), toolDefinitions...), - skillsByCode: make(map[string]models.SkillDefinition, len(visibleSkills)), + skillsByID: make(map[string]models.SkillDefinition, len(visibleSkills)), order: make([]string, 0, len(visibleSkills)), } for _, item := range visibleSkills { - code := strings.TrimSpace(item.Code) - if code == "" { + id := strconv.FormatInt(item.ID, 10) + if id == "" { continue } - ret.skillsByCode[code] = item - ret.order = append(ret.order, code) + ret.skillsByID[id] = item + ret.order = append(ret.order, id) } - if len(ret.skillsByCode) == 0 { + if len(ret.skillsByID) == 0 { return nil, fmt.Errorf("no visible skills available") } return ret, nil @@ -58,13 +59,13 @@ func (b *databaseSkillBackend) List(_ context.Context) ([]einoskill.FrontMatter, return nil, nil } ret := make([]einoskill.FrontMatter, 0, len(b.order)) - for _, code := range b.order { - item, ok := b.skillsByCode[code] + for _, id := range b.order { + item, ok := b.skillsByID[id] if !ok { continue } ret = append(ret, einoskill.FrontMatter{ - Name: strings.TrimSpace(item.Code), + Name: strconv.FormatInt(item.ID, 10), Description: skillListDescription(item), }) } @@ -79,13 +80,13 @@ func (b *databaseSkillBackend) Get(_ context.Context, name string) (einoskill.Sk if name == "" { return einoskill.Skill{}, fmt.Errorf("skill name is empty") } - item, ok := b.skillsByCode[name] + item, ok := b.skillsByID[name] if !ok { return einoskill.Skill{}, fmt.Errorf("skill %q not found", name) } return einoskill.Skill{ FrontMatter: einoskill.FrontMatter{ - Name: strings.TrimSpace(item.Code), + Name: strconv.FormatInt(item.ID, 10), Description: skillListDescription(item), }, Content: runtimeinstruction.BuildSkillDocument(&item, filterSkillToolDefinitions(b.toolDefinitions, &item)), @@ -105,7 +106,7 @@ func loadVisibleSkills(aiAgent models.AIAgent) []models.SkillDefinition { ret := make([]models.SkillDefinition, 0, len(ids)) for _, id := range ids { item, ok := byID[id] - if !ok || item.Status != enums.StatusOk || strings.TrimSpace(item.Code) == "" { + if !ok || item.Status != enums.StatusOk || item.ID <= 0 { continue } ret = append(ret, item) @@ -120,12 +121,12 @@ func buildRuntimeSkillMetadataMap(aiAgent models.AIAgent) map[string]runtimeSkil } ret := make(map[string]runtimeSkillMetadata, len(visibleSkills)) for _, item := range visibleSkills { - code := strings.TrimSpace(item.Code) - if code == "" { + if item.ID <= 0 { continue } - ret[code] = runtimeSkillMetadata{ - Code: code, + id := strconv.FormatInt(item.ID, 10) + ret[id] = runtimeSkillMetadata{ + ID: item.ID, Name: strings.TrimSpace(item.Name), Description: skillListDescription(item), AllowedToolCodes: parseSkillToolWhitelist(item.ToolWhitelist), @@ -145,7 +146,7 @@ func skillListDescription(item models.SkillDefinition) string { if name := strings.TrimSpace(item.Name); name != "" { return name } - return strings.TrimSpace(item.Code) + return fmt.Sprintf("Skill %d", item.ID) } func parseSkillToolWhitelist(raw string) []string { diff --git a/internal/ai/runtime/internal/impl/factory/skill_middleware_backend_test.go b/internal/ai/runtime/internal/impl/factory/skill_middleware_backend_test.go index f121949..23994e9 100644 --- a/internal/ai/runtime/internal/impl/factory/skill_middleware_backend_test.go +++ b/internal/ai/runtime/internal/impl/factory/skill_middleware_backend_test.go @@ -19,7 +19,6 @@ func TestDatabaseSkillBackendListAndGet(t *testing.T) { setupSkillBackendTestDB(t) createSkillDefinitionForTest(t, models.SkillDefinition{ ID: 1, - Code: "after_sales_escalation_skill", Name: "售后升级", Description: "处理转人工和升级诉求", Instruction: "请优先判断是否需要转人工。", @@ -28,7 +27,6 @@ func TestDatabaseSkillBackendListAndGet(t *testing.T) { }) createSkillDefinitionForTest(t, models.SkillDefinition{ ID: 2, - Code: "disabled_skill", Name: "禁用技能", Description: "不会被暴露", Instruction: "noop", @@ -47,15 +45,15 @@ func TestDatabaseSkillBackendListAndGet(t *testing.T) { if err != nil { t.Fatalf("List returned error: %v", err) } - if len(matters) != 1 || matters[0].Name != "after_sales_escalation_skill" { + if len(matters) != 1 || matters[0].Name != "1" { t.Fatalf("unexpected matters: %#v", matters) } - skill, err := backend.Get(context.Background(), "after_sales_escalation_skill") + skill, err := backend.Get(context.Background(), "1") if err != nil { t.Fatalf("Get returned error: %v", err) } - if skill.Name != "after_sales_escalation_skill" { + if skill.Name != "1" { t.Fatalf("unexpected skill name: %#v", skill) } if skill.Content == "" || !containsAll(skill.Content, "处理转人工和升级诉求", "graph/handoff_to_human") { @@ -67,7 +65,6 @@ func TestHasVisibleSkills(t *testing.T) { setupSkillBackendTestDB(t) createSkillDefinitionForTest(t, models.SkillDefinition{ ID: 3, - Code: "enabled_skill", Name: "启用技能", Description: "可见", Instruction: "noop", @@ -75,7 +72,6 @@ func TestHasVisibleSkills(t *testing.T) { }) createSkillDefinitionForTest(t, models.SkillDefinition{ ID: 4, - Code: "deleted_skill", Name: "删除技能", Description: "不可见", Instruction: "noop", diff --git a/internal/ai/runtime/internal/impl/factory/tool_filter_middleware.go b/internal/ai/runtime/internal/impl/factory/tool_filter_middleware.go index d6590d3..4edbebf 100644 --- a/internal/ai/runtime/internal/impl/factory/tool_filter_middleware.go +++ b/internal/ai/runtime/internal/impl/factory/tool_filter_middleware.go @@ -15,7 +15,7 @@ import ( "github.com/cloudwego/eino/schema" ) -const activeSkillRunLocalKey = "runtime_active_skill_code" +const activeSkillRunLocalKey = "runtime_active_skill_id" type RuntimeToolFilterMiddleware struct { *adk.BaseChatModelAgentMiddleware @@ -72,7 +72,7 @@ func (m *RuntimeToolFilterMiddleware) WrapInvokableToolCall(_ context.Context, e return result, err } if strings.TrimSpace(metadata.ToolCode) == toolx.BuiltinSkill.Code { - _ = m.setActiveSkill(ctx, skillCodeFromArguments(argumentsInJSON)) + _ = m.setActiveSkill(ctx, skillIDFromArguments(argumentsInJSON)) return result, nil } if strings.TrimSpace(metadata.ToolCode) == toolx.BuiltinToolSearch.Code { @@ -90,7 +90,7 @@ func (m *RuntimeToolFilterMiddleware) WrapInvokableToolCall(_ context.Context, e } func (m *RuntimeToolFilterMiddleware) blockToolCall(metadata einocallbacks.ToolMetadata, argumentsInJSON string, activeSkill einocallbacks.SkillMetadata) error { - err := fmt.Errorf("tool %s is not allowed for active skill %s", strings.TrimSpace(metadata.ToolCode), strings.TrimSpace(activeSkill.Code)) + err := fmt.Errorf("tool %s is not allowed for active skill %d", strings.TrimSpace(metadata.ToolCode), activeSkill.ID) if m.collector != nil { m.collector.AddToolItem(einocallbacks.ToolTraceItem{ ToolCode: strings.TrimSpace(metadata.ToolCode), @@ -106,12 +106,12 @@ func (m *RuntimeToolFilterMiddleware) blockToolCall(metadata einocallbacks.ToolM return err } -func (m *RuntimeToolFilterMiddleware) setActiveSkill(ctx context.Context, skillCode string) error { - skillCode = strings.TrimSpace(skillCode) - if skillCode == "" { +func (m *RuntimeToolFilterMiddleware) setActiveSkill(ctx context.Context, skillID string) error { + skillID = strings.TrimSpace(skillID) + if skillID == "" { return nil } - return adk.SetRunLocalValue(ctx, activeSkillRunLocalKey, skillCode) + return adk.SetRunLocalValue(ctx, activeSkillRunLocalKey, skillID) } func (m *RuntimeToolFilterMiddleware) resolveActiveSkill(ctx context.Context) (einocallbacks.SkillMetadata, bool) { @@ -122,15 +122,15 @@ func (m *RuntimeToolFilterMiddleware) resolveActiveSkill(ctx context.Context) (e if err != nil || !found { return einocallbacks.SkillMetadata{}, false } - code, ok := value.(string) + skillID, ok := value.(string) if !ok { return einocallbacks.SkillMetadata{}, false } - code = strings.TrimSpace(code) - if code == "" { + skillID = strings.TrimSpace(skillID) + if skillID == "" { return einocallbacks.SkillMetadata{}, false } - skill, ok := m.skillMetadataBy[code] + skill, ok := m.skillMetadataBy[skillID] if !ok { return einocallbacks.SkillMetadata{}, false } @@ -179,12 +179,12 @@ func resolveActiveSkillMetadata(ctx context.Context, skills map[string]einocallb if err != nil || !found { return einocallbacks.SkillMetadata{}, false } - code, ok := value.(string) + skillID, ok := value.(string) if !ok { return einocallbacks.SkillMetadata{}, false } - code = strings.TrimSpace(code) - skill, ok := skills[code] + skillID = strings.TrimSpace(skillID) + skill, ok := skills[skillID] if !ok || len(skill.AllowedToolCodes) == 0 { return skill, false } @@ -330,7 +330,7 @@ func resolveRuntimeToolMetadata(toolName string, toolMetadataByName map[string]e return metadata, ok } -func skillCodeFromArguments(argumentsInJSON string) string { +func skillIDFromArguments(argumentsInJSON string) string { var args struct { Skill string `json:"skill"` } diff --git a/internal/ai/runtime/reply_helpers_test.go b/internal/ai/runtime/reply_helpers_test.go index 5498736..994bef2 100644 --- a/internal/ai/runtime/reply_helpers_test.go +++ b/internal/ai/runtime/reply_helpers_test.go @@ -26,7 +26,7 @@ func TestSummaryPrimaryToolCodePrefersToolSearchTarget(t *testing.T) { } func TestToRunLogFinalAction(t *testing.T) { - if got := toRunLogFinalAction(&applicationruntime.Summary{PlannedSkillCode: "refund", ReplyText: "ok"}); got != "skill" { + if got := toRunLogFinalAction(&applicationruntime.Summary{PlannedSkillID: 44, ReplyText: "ok"}); got != "skill" { t.Fatalf("expected skill final action, got %q", got) } diff --git a/internal/ai/runtime/reply_runlog_service.go b/internal/ai/runtime/reply_runlog_service.go index 519b025..50a448f 100644 --- a/internal/ai/runtime/reply_runlog_service.go +++ b/internal/ai/runtime/reply_runlog_service.go @@ -46,7 +46,7 @@ func (s *replyRunLogService) Write(input replyRunLogInput) { AIConfigID: input.AIAgent.AIConfigID, UserMessage: strings.TrimSpace(input.Question), PlannedAction: plannedAction, - PlannedSkillCode: strings.TrimSpace(summaryPlannedSkillCode(input.Summary)), + PlannedSkillID: summaryPlannedSkillID(input.Summary), PlannedSkillName: strings.TrimSpace(summaryPlannedSkillName(input.Summary)), SkillRouteTrace: strings.TrimSpace(summarySkillRouteTrace(input.Summary)), ToolSearchTrace: extractToolSearchTrace(input.Summary), @@ -90,7 +90,7 @@ func buildRunLogPlan(summary *applicationruntime.Summary) (plannedAction, planne if summary == nil { return "", "", "" } - if skillCode := strings.TrimSpace(summaryPlannedSkillCode(summary)); skillCode != "" { + if summaryPlannedSkillID(summary) > 0 { reason := strings.TrimSpace(summary.PlanReason) if reason == "" { reason = "skill_selected" @@ -138,7 +138,7 @@ func toRunLogFinalAction(summary *applicationruntime.Summary) string { if summary == nil { return "" } - if skillCode := strings.TrimSpace(summaryPlannedSkillCode(summary)); skillCode != "" && strings.TrimSpace(summary.ReplyText) != "" { + if summaryPlannedSkillID(summary) > 0 && strings.TrimSpace(summary.ReplyText) != "" { return "skill" } if graphToolCode := firstGraphToolCode(summary); graphToolCode != "" && strings.TrimSpace(summary.ReplyText) != "" { @@ -167,11 +167,11 @@ func buildRunLogReplyText(summary *applicationruntime.Summary) string { return strings.TrimSpace(summary.ReplyText) } -func summaryPlannedSkillCode(summary *applicationruntime.Summary) string { +func summaryPlannedSkillID(summary *applicationruntime.Summary) int64 { if summary == nil { - return "" + return 0 } - return strings.TrimSpace(summary.PlannedSkillCode) + return summary.PlannedSkillID } func summaryPlannedSkillName(summary *applicationruntime.Summary) string { diff --git a/internal/ai/runtime/reply_service_test.go b/internal/ai/runtime/reply_service_test.go index 0acf40e..07cdab6 100644 --- a/internal/ai/runtime/reply_service_test.go +++ b/internal/ai/runtime/reply_service_test.go @@ -72,8 +72,8 @@ func TestResolveReplyTimeout(t *testing.T) { func TestBuildRunLogPlan(t *testing.T) { summary := &applicationruntime.Summary{ - PlannedSkillCode: "faq_router", - PlanReason: "manual", + PlannedSkillID: 44, + PlanReason: "manual", } action, toolCode, reason := buildRunLogPlan(summary) if action != "skill" || toolCode != "" || reason != "manual" { diff --git a/internal/ai/skills/candidate_loader.go b/internal/ai/skills/candidate_loader.go index 312ebb9..3bde7ca 100644 --- a/internal/ai/skills/candidate_loader.go +++ b/internal/ai/skills/candidate_loader.go @@ -16,8 +16,11 @@ var newCandidateLoader = func() *candidateLoader { type candidateLoader struct { } -func (l *candidateLoader) findManualSkillDefinition(skillCode string) *models.SkillDefinition { - return repositories.SkillDefinitionRepository.GetByCode(sqls.DB(), skillCode) +func (l *candidateLoader) findManualSkillDefinition(skillDefinitionID int64) *models.SkillDefinition { + if skillDefinitionID <= 0 { + return nil + } + return repositories.SkillDefinitionRepository.Get(sqls.DB(), skillDefinitionID) } func (l *candidateLoader) loadCandidateSkills(aiAgent models.AIAgent) []models.SkillDefinition { diff --git a/internal/ai/skills/log_test.go b/internal/ai/skills/log_test.go index 7524500..eef2e96 100644 --- a/internal/ai/skills/log_test.go +++ b/internal/ai/skills/log_test.go @@ -11,12 +11,11 @@ import ( func TestBuildRunLogMatchedPlan(t *testing.T) { log := BuildRunLog( RuntimeContext{ - AIAgent: models.AIAgent{ID: 22}, - AIConfig: models.AIConfig{ID: 33}, - ConversationID: 11, - ManualSkillCode: "manual_refund", - IntentCode: "refund", - UserMessage: "我要退款", + AIAgent: models.AIAgent{ID: 22}, + AIConfig: models.AIConfig{ID: 33}, + ConversationID: 11, + ManualSkillDefinitionID: 44, + UserMessage: "我要退款", }, &ExecutionPlan{ AIAgent: models.AIAgent{ID: 22}, @@ -25,10 +24,7 @@ func TestBuildRunLogMatchedPlan(t *testing.T) { ModelName: "gpt-test", Provider: enums.AIProviderOpenAI, }, - Skill: &models.SkillDefinition{ - ID: 44, - Code: "refund_skill", - }, + Skill: &models.SkillDefinition{ID: 44}, MatchReason: "llm_route", }, &ExecutionTrace{Status: "ok"}, @@ -41,7 +37,7 @@ func TestBuildRunLogMatchedPlan(t *testing.T) { if log.ConversationID != 11 || log.AIAgentID != 22 || log.AIConfigID != 33 { t.Fatalf("unexpected ids in run log: %#v", log) } - if !log.Matched || !log.FinalSelected || log.SkillCode != "refund_skill" { + if !log.Matched || !log.FinalSelected || log.SkillDefinitionID != 44 { t.Fatalf("expected matched skill log, got %#v", log) } if log.MatchReason != "llm_route" { diff --git a/internal/ai/skills/matcher.go b/internal/ai/skills/matcher.go index 9124151..2f31515 100644 --- a/internal/ai/skills/matcher.go +++ b/internal/ai/skills/matcher.go @@ -2,13 +2,10 @@ package skills import ( "context" - "strings" "agent-desk/internal/models" "agent-desk/internal/pkg/enums" "agent-desk/internal/pkg/errorsx" - - "github.com/mlogclub/simple/common/strs" ) type intentTriggerConfig struct { @@ -18,45 +15,34 @@ type intentTriggerConfig struct { // MatchSkill 对单个 SkillDefinition 执行命中判断。 func MatchSkill(execCtx context.Context, ctx RuntimeContext) (*models.SkillDefinition, string, *RouteTrace, error) { loader := newCandidateLoader() - if strs.IsNotBlank(ctx.ManualSkillCode) { - skill := loader.findManualSkillDefinition(ctx.ManualSkillCode) + if ctx.ManualSkillDefinitionID > 0 { + skill := loader.findManualSkillDefinition(ctx.ManualSkillDefinitionID) if skill == nil || skill.Status != enums.StatusOk { return nil, "", nil, errorsx.InvalidParamI18n("error.e0054") } - return skill, "manual_skill_code", &RouteTrace{ - Status: "manual_selected", - SelectedSkillCode: skill.Code, + return skill, "manual_skill_id", &RouteTrace{ + Status: "manual_selected", + SelectedSkillID: skill.ID, }, nil } candidates := loader.loadCandidateSkills(ctx.AIAgent) trace := &RouteTrace{ - Status: "started", - CandidateSkillCodes: make([]string, 0, len(candidates)), + Status: "started", + CandidateSkillIDs: make([]int64, 0, len(candidates)), } for _, item := range candidates { - trace.CandidateSkillCodes = append(trace.CandidateSkillCodes, item.Code) + trace.CandidateSkillIDs = append(trace.CandidateSkillIDs, item.ID) } if len(candidates) == 0 { trace.Status = "no_candidate" return nil, "no_enabled_skill_bound", trace, nil } - intentCode := strings.TrimSpace(ctx.IntentCode) - if intentCode != "" { - for _, item := range candidates { - if strings.EqualFold(strings.TrimSpace(item.Code), intentCode) { - trace.Status = "intent_selected" - trace.SelectedSkillCode = item.Code - return &item, "intent_code", trace, nil - } - } - } - selected, routeTrace, err := routeSkillWithLLM(execCtx, ctx, candidates) if routeTrace != nil { trace.Status = routeTrace.Status - trace.SelectedSkillCode = routeTrace.SelectedSkillCode + trace.SelectedSkillID = routeTrace.SelectedSkillID trace.RawDecision = routeTrace.RawDecision trace.LatencyMs = routeTrace.LatencyMs trace.Error = routeTrace.Error diff --git a/internal/ai/skills/router.go b/internal/ai/skills/router.go index 4060ba9..85a7156 100644 --- a/internal/ai/skills/router.go +++ b/internal/ai/skills/router.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "strconv" "strings" "time" @@ -13,10 +14,10 @@ import ( "github.com/mlogclub/simple/common/strs" ) -const routeSkillSystemPrompt = `你是客服技能路由器。你只能在候选 Skill 中选择一个最合适的 skillCode,或者返回 NONE。 +const routeSkillSystemPrompt = `你是客服技能路由器。你只能在候选 Skill 中选择一个最合适的 skillId,或者返回 NONE。 只有当用户问题与 Skill 的职责边界明确匹配时才选择; 如果不明确、信息不足、多个 Skill 都不够确定,就返回 NONE。 -输出只能是 skillCode 或 NONE,不能输出其他内容。` +输出只能是 skillId 或 NONE,不能输出其他内容。` func routeSkillWithLLM(ctx context.Context, runtimeCtx RuntimeContext, candidates []models.SkillDefinition) (*models.SkillDefinition, *RouteTrace, error) { trace := &RouteTrace{Status: "started"} @@ -43,10 +44,16 @@ func routeSkillWithLLM(ctx context.Context, runtimeCtx RuntimeContext, candidate trace.Status = "not_matched" return nil, trace, nil } + selectedID, parseErr := strconv.ParseInt(decision, 10, 64) + if parseErr != nil || selectedID <= 0 { + trace.Status = "invalid_decision" + trace.Error = fmt.Sprintf("invalid route decision: %s", decision) + return nil, trace, nil + } for _, item := range candidates { - if strings.EqualFold(item.Code, decision) { + if item.ID == selectedID { trace.Status = "llm_selected" - trace.SelectedSkillCode = item.Code + trace.SelectedSkillID = item.ID return &item, trace, nil } } @@ -62,14 +69,14 @@ func buildSkillRoutePrompt(userMessage string, candidates []models.SkillDefiniti lines = append(lines, "") lines = append(lines, "候选 Skills:") for _, item := range candidates { - line := fmt.Sprintf("- skillCode=%s; name=%s; description=%s", strings.TrimSpace(item.Code), strings.TrimSpace(item.Name), strings.TrimSpace(item.Description)) + line := fmt.Sprintf("- skillId=%d; name=%s; description=%s", item.ID, 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。") + lines = append(lines, "请只输出一个 skillId 或 NONE。") return strings.Join(lines, "\n") } diff --git a/internal/ai/skills/router_test.go b/internal/ai/skills/router_test.go index 2a2f0e1..d3ad22c 100644 --- a/internal/ai/skills/router_test.go +++ b/internal/ai/skills/router_test.go @@ -18,7 +18,7 @@ func TestParseSkillExamples(t *testing.T) { } func TestNormalizeRouteDecision(t *testing.T) { - if got := normalizeRouteDecision("```refund_skill```\n补充说明"); got != "refund_skill" { + if got := normalizeRouteDecision("```44```\n补充说明"); got != "44" { t.Fatalf("unexpected normalized decision: %q", got) } if got := normalizeRouteDecision(" none "); got != "NONE" { @@ -29,20 +29,20 @@ func TestNormalizeRouteDecision(t *testing.T) { func TestBuildSkillRoutePrompt(t *testing.T) { prompt := buildSkillRoutePrompt("我要申请退款", []models.SkillDefinition{ { - Code: "refund_skill", + ID: 44, Name: "退款处理", Description: "负责退款和退货相关问题", Examples: `["退款进度","退货运费"]`, }, }) - if !strings.Contains(prompt, "skillCode=refund_skill") { - t.Fatalf("expected prompt to include skill code, got %q", prompt) + if !strings.Contains(prompt, "skillId=44") { + t.Fatalf("expected prompt to include skill id, got %q", prompt) } if !strings.Contains(prompt, "examples=退款进度 | 退货运费") { t.Fatalf("expected prompt to include examples, got %q", prompt) } - if !strings.Contains(prompt, "请只输出一个 skillCode 或 NONE。") { + if !strings.Contains(prompt, "请只输出一个 skillId 或 NONE。") { t.Fatalf("expected prompt to include output constraint, got %q", prompt) } } diff --git a/internal/ai/skills/runlog_service.go b/internal/ai/skills/runlog_service.go index 3e05ef7..8e503d4 100644 --- a/internal/ai/skills/runlog_service.go +++ b/internal/ai/skills/runlog_service.go @@ -19,13 +19,12 @@ type RunLogService struct{} // Build 根据执行计划与运行结果构建 Skill 运行日志。 func (s *RunLogService) Build(ctx RuntimeContext, plan *ExecutionPlan, trace *ExecutionTrace, err error) *models.SkillRunLog { log := &models.SkillRunLog{ - ConversationID: ctx.ConversationID, - AIAgentID: ctx.AIAgent.ID, - ManualSkillCode: ctx.ManualSkillCode, - IntentCode: ctx.IntentCode, - UserMessage: ctx.UserMessage, - TraceData: s.buildTraceData(trace), - CreatedAt: time.Now(), + ConversationID: ctx.ConversationID, + AIAgentID: ctx.AIAgent.ID, + ManualSkillID: ctx.ManualSkillDefinitionID, + UserMessage: ctx.UserMessage, + TraceData: s.buildTraceData(trace), + CreatedAt: time.Now(), } if plan != nil { log.AIConfigID = plan.AIConfig.ID @@ -34,7 +33,6 @@ func (s *RunLogService) Build(ctx RuntimeContext, plan *ExecutionPlan, trace *Ex if plan.Skill != nil { log.SkillDefinitionID = plan.Skill.ID - log.SkillCode = plan.Skill.Code log.Matched = true log.FinalSelected = true log.MatchReason = plan.MatchReason diff --git a/internal/ai/skills/types.go b/internal/ai/skills/types.go index cc671cd..27127ea 100644 --- a/internal/ai/skills/types.go +++ b/internal/ai/skills/types.go @@ -4,12 +4,11 @@ import "agent-desk/internal/models" // RuntimeContext 表示一次 Skill 运行的输入上下文。 type RuntimeContext struct { - AIAgent models.AIAgent // AIAgent 为当前请求所属的 AI Agent,必填。 - AIConfig models.AIConfig // AIConfig 为当前请求实际使用的模型配置,必填。 - UserMessage string // UserMessage 为当前用户输入。 - ConversationID int64 // ConversationID 为当前会话 ID,无会话上下文时为 0。 - ManualSkillCode string // ManualSkillCode 为显式指定的 Skill 编码。 - IntentCode string // IntentCode 为上游识别出的意图编码。 + AIAgent models.AIAgent // AIAgent 为当前请求所属的 AI Agent,必填。 + AIConfig models.AIConfig // AIConfig 为当前请求实际使用的模型配置,必填。 + UserMessage string // UserMessage 为当前用户输入。 + ConversationID int64 // ConversationID 为当前会话 ID,无会话上下文时为 0。 + ManualSkillDefinitionID int64 // ManualSkillDefinitionID 为显式指定的 Skill 定义ID。 } // ExecutionPlan 表示 Skill Runtime 计算出的最终路由结果。 @@ -35,12 +34,12 @@ type ExecutionTrace struct { } type RouteTrace struct { - Status string `json:"status"` - CandidateSkillCodes []string `json:"candidateSkillCodes,omitempty"` - SelectedSkillCode string `json:"selectedSkillCode,omitempty"` - RawDecision string `json:"rawDecision,omitempty"` - LatencyMs int64 `json:"latencyMs,omitempty"` - Error string `json:"error,omitempty"` + Status string `json:"status"` + CandidateSkillIDs []int64 `json:"candidateSkillIds,omitempty"` + SelectedSkillID int64 `json:"selectedSkillId,omitempty"` + RawDecision string `json:"rawDecision,omitempty"` + LatencyMs int64 `json:"latencyMs,omitempty"` + Error string `json:"error,omitempty"` } type PromptTrace struct { diff --git a/internal/builders/agent_run_log_builder.go b/internal/builders/agent_run_log_builder.go index defb4a5..fc10e9f 100644 --- a/internal/builders/agent_run_log_builder.go +++ b/internal/builders/agent_run_log_builder.go @@ -22,7 +22,7 @@ func BuildAgentRunLog(item *models.AgentRunLog) response.AgentRunLogResponse { AIConfigID: item.AIConfigID, UserMessage: item.UserMessage, PlannedAction: item.PlannedAction, - PlannedSkillCode: item.PlannedSkillCode, + PlannedSkillID: item.PlannedSkillID, PlannedSkillName: item.PlannedSkillName, SkillRouteTrace: item.SkillRouteTrace, ToolSearchTrace: item.ToolSearchTrace, diff --git a/internal/builders/skill_builder.go b/internal/builders/skill_builder.go index cdfd747..ed53208 100644 --- a/internal/builders/skill_builder.go +++ b/internal/builders/skill_builder.go @@ -19,7 +19,6 @@ func BuildSkillDefinitionResponse(item *models.SkillDefinition) response.SkillDe } return response.SkillDefinitionResponse{ ID: item.ID, - Code: item.Code, Name: item.Name, Description: item.Description, Instruction: item.Instruction, diff --git a/internal/handlers/dashboard/agent_run_log_handler.go b/internal/handlers/dashboard/agent_run_log_handler.go index 32084e3..3bb2d84 100644 --- a/internal/handlers/dashboard/agent_run_log_handler.go +++ b/internal/handlers/dashboard/agent_run_log_handler.go @@ -25,7 +25,7 @@ func AgentRunLogAnyList(ctx *gin.Context) { params.QueryFilter{ParamName: "requestId"}, params.QueryFilter{ParamName: "aiAgentId"}, params.QueryFilter{ParamName: "plannedAction"}, - params.QueryFilter{ParamName: "plannedSkillCode", Op: params.Like}, + params.QueryFilter{ParamName: "plannedSkillId"}, params.QueryFilter{ParamName: "graphToolCode"}, params.QueryFilter{ParamName: "interruptType"}, params.QueryFilter{ParamName: "resumeSource"}, diff --git a/internal/handlers/dashboard/ai_agent_handler.go b/internal/handlers/dashboard/ai_agent_handler.go index 2184c4b..570f69f 100644 --- a/internal/handlers/dashboard/ai_agent_handler.go +++ b/internal/handlers/dashboard/ai_agent_handler.go @@ -215,7 +215,6 @@ func buildAIAgentResponseWithLocale(item *models.AIAgent, locale string) respons if skill := services.SkillDefinitionService.Get(id); skill != nil { ret.Skills = append(ret.Skills, response.AIAgentSkillResponse{ ID: skill.ID, - Code: skill.Code, Name: skill.Name, }) } diff --git a/internal/handlers/dashboard/skill_definition_handler.go b/internal/handlers/dashboard/skill_definition_handler.go index c97b3f0..3b671fa 100644 --- a/internal/handlers/dashboard/skill_definition_handler.go +++ b/internal/handlers/dashboard/skill_definition_handler.go @@ -28,7 +28,6 @@ func SkillDefinitionAnyList(ctx *gin.Context) { cnd := params.NewPagedSqlCnd(ctx, params.QueryFilter{ParamName: "status"}, params.QueryFilter{ParamName: "name", Op: params.Like}, - params.QueryFilter{ParamName: "code", Op: params.Like}, ).Desc("id") if _, ok := params.Get(ctx, "status"); !ok { cnd.Where("status <> ?", enums.StatusDeleted) diff --git a/internal/models/models.go b/internal/models/models.go index f9b3475..fe9bcd8 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -818,37 +818,34 @@ type KnowledgeFeedback struct { // SkillDefinition 表示可由后台配置并参与运行时路由的 Skill 定义。 type SkillDefinition struct { - ID int64 `gorm:"primaryKey;autoIncrement"` // ID 为 Skill 主键。 - Code string `gorm:"type:varchar(100);not null;default:'';uniqueIndex"` // Code 为 Skill 的稳定唯一编码,供程序内部引用和路由判断使用,例如 refund_skill。 - Name string `gorm:"type:varchar(100);not null;default:'';index"` // Name 为 Skill 的展示名称,用于后台列表、配置页和人工选择场景。 - Description string `gorm:"type:varchar(255);not null;default:''"` // Description 为 Skill 的简要说明,用于描述该 Skill 的适用场景和职责边界。 - Instruction string `gorm:"type:longtext"` // Instruction 为 Skill 的主体说明文档存储字段,使用 Markdown 编写,供 Agent 理解任务目标、步骤和工具使用要求。 - Examples string `gorm:"type:text"` // Examples 为示例问法 JSON 数组字符串。 - ToolWhitelist string `gorm:"type:text"` // ToolWhitelist 为允许使用的工具编码 JSON 数组字符串。 - Status enums.Status `gorm:"type:int;not null;default:0;index"` // Status 为 Skill 当前状态,使用全局通用状态:0启用 1禁用 2删除。 - Remark string `gorm:"type:text"` // Remark 为后台备注,用于记录配置说明、维护信息或内部协作信息。 + ID int64 `gorm:"primaryKey;autoIncrement"` // ID 为 Skill 主键。 + Name string `gorm:"type:varchar(100);not null;default:'';index"` // Name 为 Skill 的展示名称,用于后台列表、配置页和人工选择场景。 + Description string `gorm:"type:varchar(255);not null;default:''"` // Description 为 Skill 的简要说明,用于描述该 Skill 的适用场景和职责边界。 + Instruction string `gorm:"type:longtext"` // Instruction 为 Skill 的主体说明文档存储字段,使用 Markdown 编写,供 Agent 理解任务目标、步骤和工具使用要求。 + Examples string `gorm:"type:text"` // Examples 为示例问法 JSON 数组字符串。 + ToolWhitelist string `gorm:"type:text"` // ToolWhitelist 为允许使用的工具编码 JSON 数组字符串。 + Status enums.Status `gorm:"type:int;not null;default:0;index"` // Status 为 Skill 当前状态,使用全局通用状态:0启用 1禁用 2删除。 + Remark string `gorm:"type:text"` // Remark 为后台备注,用于记录配置说明、维护信息或内部协作信息。 AuditFields } // SkillRunLog 表示一次 Skill 运行过程的审计日志。 type SkillRunLog struct { - ID int64 `gorm:"primaryKey;autoIncrement"` // ID 为 Skill 运行日志主键。 - ConversationID int64 `gorm:"type:bigint;not null;default:0;index"` // ConversationID 为关联会话ID,无会话上下文时为0。 - AIAgentID int64 `gorm:"type:bigint;not null;default:0;index"` // AIAgentID 为本次运行所属的 AI Agent ID。 - AIConfigID int64 `gorm:"type:bigint;not null;default:0;index"` // AIConfigID 为本次运行实际使用的 AI 配置ID。 - SkillDefinitionID int64 `gorm:"type:bigint;not null;default:0;index"` // SkillDefinitionID 为最终命中的 Skill 定义ID,未命中时为0。 - SkillCode string `gorm:"type:varchar(100);not null;default:'';index"` // SkillCode 为最终命中的 Skill 编码,未命中时为空。 - ManualSkillCode string `gorm:"type:varchar(100);not null;default:'';index"` // ManualSkillCode 为本次请求显式指定的 Skill 编码。 - IntentCode string `gorm:"type:varchar(100);not null;default:'';index"` // IntentCode 为上游传入的意图编码。 - UserMessage string `gorm:"type:longtext"` // UserMessage 为本次请求的用户输入内容。 - Matched bool `gorm:"not null;default:false;index"` // Matched 表示本次请求是否命中了 Skill。 - MatchReason string `gorm:"type:varchar(500);not null;default:''"` // MatchReason 为命中或未命中的原因说明。 - FinalSelected bool `gorm:"not null;default:false;index"` // FinalSelected 表示该日志记录的 Skill 是否为最终选中的执行 Skill。 - UsedModel string `gorm:"type:varchar(100);not null;default:''"` // UsedModel 为本次实际调用的模型名称。 - UsedProvider enums.AIProvider `gorm:"type:varchar(50);not null;default:''"` // UsedProvider 为本次实际调用的模型供应商。 - ErrorMessage string `gorm:"type:text"` // ErrorMessage 为运行过程中的错误信息。 - TraceData string `gorm:"type:text"` // TraceData 为 Skill 执行链路追踪数据JSON。 - CreatedAt time.Time `gorm:"type:datetime;not null;index"` // CreatedAt 为运行日志创建时间。 + ID int64 `gorm:"primaryKey;autoIncrement"` // ID 为 Skill 运行日志主键。 + ConversationID int64 `gorm:"type:bigint;not null;default:0;index"` // ConversationID 为关联会话ID,无会话上下文时为0。 + AIAgentID int64 `gorm:"type:bigint;not null;default:0;index"` // AIAgentID 为本次运行所属的 AI Agent ID。 + AIConfigID int64 `gorm:"type:bigint;not null;default:0;index"` // AIConfigID 为本次运行实际使用的 AI 配置ID。 + SkillDefinitionID int64 `gorm:"type:bigint;not null;default:0;index"` // SkillDefinitionID 为最终命中的 Skill 定义ID,未命中时为0。 + ManualSkillID int64 `gorm:"type:bigint;not null;default:0;index"` // ManualSkillID 为本次请求显式指定的 Skill 定义ID。 + UserMessage string `gorm:"type:longtext"` // UserMessage 为本次请求的用户输入内容。 + Matched bool `gorm:"not null;default:false;index"` // Matched 表示本次请求是否命中了 Skill。 + MatchReason string `gorm:"type:varchar(500);not null;default:''"` // MatchReason 为命中或未命中的原因说明。 + FinalSelected bool `gorm:"not null;default:false;index"` // FinalSelected 表示该日志记录的 Skill 是否为最终选中的执行 Skill。 + UsedModel string `gorm:"type:varchar(100);not null;default:''"` // UsedModel 为本次实际调用的模型名称。 + UsedProvider enums.AIProvider `gorm:"type:varchar(50);not null;default:''"` // UsedProvider 为本次实际调用的模型供应商。 + ErrorMessage string `gorm:"type:text"` // ErrorMessage 为运行过程中的错误信息。 + TraceData string `gorm:"type:text"` // TraceData 为 Skill 执行链路追踪数据JSON。 + CreatedAt time.Time `gorm:"type:datetime;not null;index"` // CreatedAt 为运行日志创建时间。 } // AgentRunLog 表示一次客服 Agent 自动运行的总链路日志。 @@ -861,7 +858,7 @@ type AgentRunLog struct { AIConfigID int64 `gorm:"type:bigint;not null;default:0;index"` UserMessage string `gorm:"type:longtext"` PlannedAction string `gorm:"type:varchar(30);not null;default:'';index"` - PlannedSkillCode string `gorm:"type:varchar(100);not null;default:'';index"` + PlannedSkillID int64 `gorm:"type:bigint;not null;default:0;index"` PlannedSkillName string `gorm:"type:varchar(100);not null;default:''"` SkillRouteTrace string `gorm:"type:text"` ToolSearchTrace string `gorm:"type:text"` diff --git a/internal/pkg/dto/request/skill_request.go b/internal/pkg/dto/request/skill_request.go index ccde269..65feebb 100644 --- a/internal/pkg/dto/request/skill_request.go +++ b/internal/pkg/dto/request/skill_request.go @@ -2,12 +2,10 @@ package request type SkillDefinitionListRequest struct { Name string `json:"name"` - Code string `json:"code"` Status int `json:"status"` } type CreateSkillDefinitionRequest struct { - Code string `json:"code"` Name string `json:"name"` Description string `json:"description"` Instruction string `json:"instruction"` @@ -35,10 +33,10 @@ type UpdateSkillDefinitionStatusRequest struct { } type SkillDebugRunRequest struct { - AIAgentID int64 `json:"aiAgentId"` - ConversationID int64 `json:"conversationId"` - SkillCode string `json:"skillCode"` - UserMessage string `json:"userMessage"` + AIAgentID int64 `json:"aiAgentId"` + ConversationID int64 `json:"conversationId"` + SkillDefinitionID int64 `json:"skillDefinitionId"` + UserMessage string `json:"userMessage"` } type SkillDebugResumeRequest struct { diff --git a/internal/pkg/dto/response/ai_response.go b/internal/pkg/dto/response/ai_response.go index 84c4e97..d0f3a4f 100644 --- a/internal/pkg/dto/response/ai_response.go +++ b/internal/pkg/dto/response/ai_response.go @@ -12,7 +12,6 @@ type AIAgentTeamResponse struct { type AIAgentSkillResponse struct { ID int64 `json:"id"` - Code string `json:"code"` Name string `json:"name"` } diff --git a/internal/pkg/dto/response/skill_response.go b/internal/pkg/dto/response/skill_response.go index 6135439..f7e1d03 100644 --- a/internal/pkg/dto/response/skill_response.go +++ b/internal/pkg/dto/response/skill_response.go @@ -4,7 +4,6 @@ import "time" type SkillDefinitionResponse struct { ID int64 `json:"id"` - Code string `json:"code"` Name string `json:"name"` Description string `json:"description"` Instruction string `json:"instruction"` @@ -20,24 +19,24 @@ 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"` - ToolWhitelist []string `json:"toolWhitelist"` - ExposedToolCodes []string `json:"exposedToolCodes"` - InvokedToolCodes []string `json:"invokedToolCodes"` - ToolSearchTrace string `json:"toolSearchTrace"` - GraphToolTrace string `json:"graphToolTrace"` - GraphToolCode string `json:"graphToolCode"` - InterruptType string `json:"interruptType"` - CheckPointID string `json:"checkPointId"` - Interrupted bool `json:"interrupted"` - TraceData string `json:"traceData"` - ErrorMessage string `json:"errorMessage"` - ConversationID int64 `json:"conversationId"` - AIAgentID int64 `json:"aiAgentId"` + SkillDefinitionID int64 `json:"skillDefinitionId"` + SkillName string `json:"skillName"` + ReplyText string `json:"replyText"` + PlanReason string `json:"planReason"` + SkillRouteTrace string `json:"skillRouteTrace"` + ToolWhitelist []string `json:"toolWhitelist"` + ExposedToolCodes []string `json:"exposedToolCodes"` + InvokedToolCodes []string `json:"invokedToolCodes"` + ToolSearchTrace string `json:"toolSearchTrace"` + GraphToolTrace string `json:"graphToolTrace"` + GraphToolCode string `json:"graphToolCode"` + InterruptType string `json:"interruptType"` + 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 { @@ -49,7 +48,7 @@ type AgentRunLogResponse struct { AIConfigID int64 `json:"aiConfigId"` UserMessage string `json:"userMessage"` PlannedAction string `json:"plannedAction"` - PlannedSkillCode string `json:"plannedSkillCode"` + PlannedSkillID int64 `json:"plannedSkillId"` PlannedSkillName string `json:"plannedSkillName"` SkillRouteTrace string `json:"skillRouteTrace"` ToolSearchTrace string `json:"toolSearchTrace"` diff --git a/internal/pkg/i18nx/locales/en-US.yml b/internal/pkg/i18nx/locales/en-US.yml index e3abd7b..eb9b466 100644 --- a/internal/pkg/i18nx/locales/en-US.yml +++ b/internal/pkg/i18nx/locales/en-US.yml @@ -54,8 +54,8 @@ error.e0053: "Skill not found." error.e0054: "Skill not found or not enabled." error.e0055: "Enter a Skill name." error.e0056: "Skill is not enabled." -error.e0057: "Enter a Skill code." -error.e0058: "This Skill code is already in use." +error.e0057: "Skill is required." +error.e0058: "This Skill already exists." error.e0059: "Web channel position must be left or right." error.e0060: "Invalid web channel configuration." error.e0061: "AI Agent ID is required." @@ -68,7 +68,7 @@ error.e0067: "Invalid index status." error.e0068: "openKfID is required." error.e0069: "This openKfId is already used by another channel." error.e0070: "Server code is required." -error.e0071: "Skill code is required." +error.e0071: "Skill definition ID is required." error.e0072: "Ticket is required." error.e0073: "The MCP server bound to this tool code does not exist or is not enabled." error.e0074: "Tool code is required." diff --git a/internal/pkg/i18nx/locales/zh-CN.yml b/internal/pkg/i18nx/locales/zh-CN.yml index f9013ab..8c84b6f 100644 --- a/internal/pkg/i18nx/locales/zh-CN.yml +++ b/internal/pkg/i18nx/locales/zh-CN.yml @@ -54,8 +54,8 @@ error.e0053: "Skill 不存在" error.e0054: "Skill 不存在或未启用" error.e0055: "Skill 名称不能为空" error.e0056: "Skill 未启用" -error.e0057: "Skill 编码不能为空" -error.e0058: "Skill 编码已存在" +error.e0057: "Skill 参数不能为空" +error.e0058: "Skill 已存在" error.e0059: "Web渠道配置 position 只能为 left 或 right" error.e0060: "Web渠道配置不合法" error.e0061: "aiAgentId不能为空" @@ -68,7 +68,7 @@ error.e0067: "indexStatus参数不合法" error.e0068: "openKfID不能为空" error.e0069: "openKfId 已被其他渠道使用" error.e0070: "serverCode不能为空" -error.e0071: "skillCode不能为空" +error.e0071: "skillDefinitionId不能为空" error.e0072: "ticket 不能为空" error.e0073: "toolCode 绑定的 MCP 服务不存在或未启用" error.e0074: "toolCode不能为空" diff --git a/internal/repositories/skill_definition_repository.go b/internal/repositories/skill_definition_repository.go index 59e0c54..ea292ce 100644 --- a/internal/repositories/skill_definition_repository.go +++ b/internal/repositories/skill_definition_repository.go @@ -101,10 +101,6 @@ func (r *skillDefinitionRepository) Delete(db *gorm.DB, id int64) { db.Delete(&models.SkillDefinition{}, "id = ?", id) } -func (r *skillDefinitionRepository) GetByCode(db *gorm.DB, code string) *models.SkillDefinition { - return r.FindOne(db, sqls.NewCnd().Where("code = ?", code)) -} - func (r *skillDefinitionRepository) GetByIDs(db *gorm.DB, ids []int64) map[int64]models.SkillDefinition { if len(ids) == 0 { return nil diff --git a/internal/services/skill_definition_service.go b/internal/services/skill_definition_service.go index ddf83b3..e0b1299 100644 --- a/internal/services/skill_definition_service.go +++ b/internal/services/skill_definition_service.go @@ -76,10 +76,6 @@ func (s *skillDefinitionService) Delete(id int64) { repositories.SkillDefinitionRepository.Delete(sqls.DB(), id) } -func (s *skillDefinitionService) GetByCode(code string) *models.SkillDefinition { - return repositories.SkillDefinitionRepository.GetByCode(sqls.DB(), code) -} - func (s *skillDefinitionService) GetByIDs(ids []int64) map[int64]models.SkillDefinition { return repositories.SkillDefinitionRepository.GetByIDs(sqls.DB(), ids) } @@ -92,11 +88,7 @@ func (s *skillDefinitionService) CreateSkillDefinition(req request.CreateSkillDe if err != nil { return nil, err } - if s.Take("code = ?", normalized.Code) != nil { - return nil, errorsx.InvalidParamI18n("error.e0058") - } item := &models.SkillDefinition{ - Code: normalized.Code, Name: normalized.Name, Description: normalized.Description, Instruction: normalized.Instruction, @@ -127,11 +119,7 @@ func (s *skillDefinitionService) UpdateSkillDefinition(req request.UpdateSkillDe if err != nil { return err } - if exists := s.Take("code = ? AND id <> ?", normalized.Code, req.ID); exists != nil { - return errorsx.InvalidParamI18n("error.e0058") - } return repositories.SkillDefinitionRepository.Updates(sqls.DB(), req.ID, map[string]any{ - "code": normalized.Code, "name": normalized.Name, "description": normalized.Description, "instruction": normalized.Instruction, @@ -146,15 +134,11 @@ func (s *skillDefinitionService) UpdateSkillDefinition(req request.UpdateSkillDe func (s *skillDefinitionService) normalizeSkillDefinitionRequest(req request.CreateSkillDefinitionRequest) (*request.CreateSkillDefinitionRequest, error) { normalized := &request.CreateSkillDefinitionRequest{ - Code: strings.TrimSpace(req.Code), Name: strings.TrimSpace(req.Name), Description: strings.TrimSpace(req.Description), Instruction: strings.TrimSpace(req.Instruction), Remark: strings.TrimSpace(req.Remark), } - if normalized.Code == "" { - return nil, errorsx.InvalidParamI18n("error.e0057") - } if normalized.Name == "" { return nil, errorsx.InvalidParamI18n("error.e0055") } diff --git a/internal/services/skill_runtime_service.go b/internal/services/skill_runtime_service.go index 075cb89..8c914ac 100644 --- a/internal/services/skill_runtime_service.go +++ b/internal/services/skill_runtime_service.go @@ -24,7 +24,7 @@ func (s *skillRuntimeService) DebugRun(ctx context.Context, req request.SkillDeb if req.AIAgentID <= 0 { return nil, errorsx.InvalidParamI18n("error.e0061") } - if strings.TrimSpace(req.SkillCode) == "" { + if req.SkillDefinitionID <= 0 { return nil, errorsx.InvalidParamI18n("error.e0071") } if strings.TrimSpace(req.UserMessage) == "" { diff --git a/web/app/dashboard/agent-run-logs/_components/detail.tsx b/web/app/dashboard/agent-run-logs/_components/detail.tsx index edc7180..d265317 100644 --- a/web/app/dashboard/agent-run-logs/_components/detail.tsx +++ b/web/app/dashboard/agent-run-logs/_components/detail.tsx @@ -122,7 +122,7 @@ export function AgentRunLogDetailDialog({ title={t("agentRunLog.planningStage")} lines={[ `plannedAction: ${activeLog.plannedAction || "-"}`, - `plannedSkillCode: ${activeLog.plannedSkillCode || "-"}`, + `plannedSkillId: ${activeLog.plannedSkillId || "-"}`, `plannedSkillName: ${activeLog.plannedSkillName || "-"}`, `graphToolCode: ${activeLog.graphToolCode || "-"}`, `recommendedAction: ${activeLog.recommendedAction || "-"}`, diff --git a/web/app/dashboard/agent-run-logs/page.tsx b/web/app/dashboard/agent-run-logs/page.tsx index 7821c1e..be8cf7e 100644 --- a/web/app/dashboard/agent-run-logs/page.tsx +++ b/web/app/dashboard/agent-run-logs/page.tsx @@ -239,14 +239,19 @@ export default function DashboardAgentRunLogsPage() {
- {item.plannedSkillCode || item.graphToolCode || item.plannedToolCode ? ( + {item.plannedSkillId || item.graphToolCode || item.plannedToolCode ? (
- {item.plannedSkillCode || item.graphToolCode || item.plannedToolCode} + {item.plannedSkillName || + (item.plannedSkillId + ? `Skill #${item.plannedSkillId}` + : "") || + item.graphToolCode || + item.plannedToolCode}
- {item.plannedSkillName ? ( + {item.plannedSkillId ? (
- {item.plannedSkillName} + Skill #{item.plannedSkillId}
) : item.handoffReason ? (
diff --git a/web/app/dashboard/skill-definition/_components/debug-dialog.tsx b/web/app/dashboard/skill-definition/_components/debug-dialog.tsx index 29931f0..521800e 100644 --- a/web/app/dashboard/skill-definition/_components/debug-dialog.tsx +++ b/web/app/dashboard/skill-definition/_components/debug-dialog.tsx @@ -33,7 +33,7 @@ import { useI18n } from "@/i18n/provider" type DebugDialogProps = { open: boolean - skillCode: string + skillDefinitionId: number skillName: string onOpenChange: (open: boolean) => void } @@ -96,7 +96,7 @@ function ResultBlock({ export function DebugDialog({ open, - skillCode, + skillDefinitionId, skillName, onOpenChange, }: DebugDialogProps) { @@ -106,9 +106,9 @@ export function DebugDialog({ return ( @@ -117,12 +117,12 @@ export function DebugDialog({ function DebugDialogBody({ open, - skillCode, + skillDefinitionId, skillName, onOpenChange, }: DebugDialogProps) { const t = useI18n() - const formId = `skill-debug-form-${skillCode}` + const formId = `skill-debug-form-${skillDefinitionId}` const [running, setRunning] = useState(false) const [resuming, setResuming] = useState(false) const [aiAgents, setAiAgents] = useState([]) @@ -198,7 +198,7 @@ function DebugDialogBody({ async function onSubmit(values: DebugForm) { const payload: SkillDebugRunPayload = { aiAgentId: Number(values.aiAgentId), - skillCode, + skillDefinitionId, userMessage: values.userMessage.trim(), } const conversationId = Number(values.conversationId) @@ -256,7 +256,7 @@ function DebugDialogBody({ Skill - + @@ -359,7 +359,7 @@ function DebugDialogBody({
- {result?.skillCode || skillCode} + {result?.skillName || skillName} {result?.graphToolCode ? ( {result.graphToolCode} ) : null} @@ -522,7 +522,7 @@ function DebugDialogBody({
- {resumeResult.skillCode || skillCode} + {resumeResult.skillName || skillName} {resumeResult.graphToolCode ? ( {resumeResult.graphToolCode} ) : null} diff --git a/web/app/dashboard/skill-definition/_components/edit.tsx b/web/app/dashboard/skill-definition/_components/edit.tsx index 2cc4175..91e24f1 100644 --- a/web/app/dashboard/skill-definition/_components/edit.tsx +++ b/web/app/dashboard/skill-definition/_components/edit.tsx @@ -37,7 +37,6 @@ type SkillEditDialogProps = { }; const emptyForm: EditForm = { - code: "", name: "", description: "", instruction: "", @@ -47,21 +46,15 @@ const emptyForm: EditForm = { function createSkillFormSchema(t: TFunction) { return z.object({ - code: z - .string() - .trim() - .min(1, t("skillDefinition.codeRequired")) - .regex(/^[a-zA-Z0-9_-]+$/, t("skillDefinition.codeInvalid")), - name: z.string().trim().min(1, t("skillDefinition.nameRequired")), - description: z.string().trim(), - instruction: z.string().trim().min(1, t("skillDefinition.instructionRequired")), - examplesText: z.string().trim(), - remark: z.string().trim(), + name: z.string().trim().min(1, t("skillDefinition.nameRequired")), + description: z.string().trim(), + instruction: z.string().trim().min(1, t("skillDefinition.instructionRequired")), + examplesText: z.string().trim(), + remark: z.string().trim(), }); } type EditForm = { - code: string; name: string; description: string; instruction: string; @@ -75,7 +68,6 @@ function buildForm(item: SkillDefinition | null): EditForm { } return { - code: item.code, name: item.name, description: item.description ?? "", instruction: item.instruction ?? "", @@ -89,7 +81,6 @@ function buildPayload( toolWhitelist: string[], ): CreateSkillDefinitionPayload { return { - code: form.code.trim(), name: form.name.trim(), description: form.description.trim(), instruction: form.instruction.trim(), @@ -276,32 +267,18 @@ function SkillEditDialogBody({ onSubmit={handleSubmit(onFormSubmit)} className="space-y-4" > -
- - {t("skillDefinition.code")} - - - - - - - {t("skillDefinition.name")} - - - - - -
+ + {t("skillDefinition.name")} + + + + + {t("skillDefinition.description")} diff --git a/web/app/dashboard/skill-definition/page.tsx b/web/app/dashboard/skill-definition/page.tsx index da917c3..897e275 100644 --- a/web/app/dashboard/skill-definition/page.tsx +++ b/web/app/dashboard/skill-definition/page.tsx @@ -73,14 +73,6 @@ export default function DashboardSkillsPage() { trim: true, className: "w-full sm:w-72", }, - { - name: "code", - label: t("skillDefinition.filterCode"), - placeholder: t("skillDefinition.filterCode"), - defaultValue: "", - trim: true, - className: "w-full sm:w-56", - }, { name: "status", label: t("skillDefinition.allStatus"), @@ -108,7 +100,6 @@ export default function DashboardSkillsPage() {
{item.name}
- {item.code} {t("skillDefinition.whitelistCount", { count: item.toolWhitelist.length, @@ -185,7 +176,6 @@ export default function DashboardSkillsPage() { fetchList={(query) => fetchSkillDefinitions({ name: typeof query.name === "string" ? query.name : undefined, - code: typeof query.code === "string" ? query.code : undefined, status: typeof query.status === "number" ? query.status : undefined, page: Number(query.page), limit: Number(query.limit), @@ -252,7 +242,7 @@ export default function DashboardSkillsPage() { /> { if (!open) setDebuggingItem(null); diff --git a/web/lib/api/admin.ts b/web/lib/api/admin.ts index 01d67fb..c4bf41c 100644 --- a/web/lib/api/admin.ts +++ b/web/lib/api/admin.ts @@ -232,7 +232,7 @@ export type AIAgent = { knowledgeIds: number[] knowledgeBaseNames: string[] skillIds: number[] - skills: { id: number; code: string; name: string }[] + skills: { id: number; name: string }[] directTools: { toolCode: string serverCode: string @@ -292,7 +292,6 @@ export type UpdateAdminQuickReplyPayload = CreateAdminQuickReplyPayload & { export type SkillDefinition = { id: number - code: string name: string description: string instruction: string @@ -308,7 +307,6 @@ export type SkillDefinition = { } export type CreateSkillDefinitionPayload = { - code: string name: string description: string instruction: string @@ -324,7 +322,7 @@ export type UpdateSkillDefinitionPayload = CreateSkillDefinitionPayload & { export type SkillDebugRunPayload = { aiAgentId: number conversationId?: number - skillCode: string + skillDefinitionId: number userMessage: string } @@ -336,7 +334,7 @@ export type SkillDebugResumePayload = { } export type SkillDebugRunResult = { - skillCode: string + skillDefinitionId: number skillName: string replyText: string planReason: string @@ -415,7 +413,7 @@ export type AgentRunLog = { aiConfigId: number userMessage: string plannedAction: string - plannedSkillCode: string + plannedSkillId: number plannedSkillName: string skillRouteTrace: string toolSearchTrace: string diff --git a/web/messages/en-US.json b/web/messages/en-US.json index cede3fc..ae8a4c3 100644 --- a/web/messages/en-US.json +++ b/web/messages/en-US.json @@ -1612,7 +1612,6 @@ "refresh": "Refresh", "new": "New", "filterName": "Filter by name", - "filterCode": "Filter by code", "searchStatus": "Search statuses", "emptyStatus": "No matching statuses", "query": "Search", @@ -1621,8 +1620,6 @@ "actions": "Actions", "loadingRows": "Loading skills...", "emptyRows": "No matching skills", - "codeRequired": "Enter a skill code.", - "codeInvalid": "Skill codes can contain letters, numbers, underscores, and hyphens only.", "nameRequired": "Enter a skill name.", "instructionRequired": "Enter skill instructions.", "editTitle": "Edit", @@ -1632,8 +1629,6 @@ "save": "Save", "create": "Create", "loading": "Loading...", - "code": "Code", - "codePlaceholder": "Example: refund_skill", "name": "Name", "namePlaceholder": "Example: Refund Handling", "description": "Description", diff --git a/web/messages/zh-CN.json b/web/messages/zh-CN.json index 99a565b..1c86ae0 100644 --- a/web/messages/zh-CN.json +++ b/web/messages/zh-CN.json @@ -1613,7 +1613,6 @@ "refresh": "刷新", "new": "新建", "filterName": "按名称筛选", - "filterCode": "按编码筛选", "searchStatus": "搜索状态", "emptyStatus": "未找到状态", "query": "查询", @@ -1622,8 +1621,6 @@ "actions": "操作", "loadingRows": "正在加载 Skill...", "emptyRows": "没有匹配的 Skill", - "codeRequired": "Skill 编码不能为空", - "codeInvalid": "Skill 编码仅支持字母、数字、下划线和中划线", "nameRequired": "Skill 名称不能为空", "instructionRequired": "技能说明不能为空", "editTitle": "编辑", @@ -1633,8 +1630,6 @@ "save": "保存", "create": "创建", "loading": "加载中...", - "code": "编码", - "codePlaceholder": "例如:refund_skill", "name": "名称", "namePlaceholder": "例如:退款处理", "description": "描述",