diff --git a/cmd/testdata/aiagent/init.go b/cmd/testdata/aiagent/init.go index b94f29c..46060bd 100644 --- a/cmd/testdata/aiagent/init.go +++ b/cmd/testdata/aiagent/init.go @@ -140,11 +140,11 @@ func getDefaultSkillIDs() (string, error) { skillItem := repositories.SkillDefinitionRepository.Take( sqls.DB(), "code = ? AND status = ?", - skill.TestGreetingSkillCode, + skill.AfterSalesEscalationSkillCode, enums.StatusOk, ) if skillItem == nil { - return "", fmt.Errorf("default test skill not found: %s", skill.TestGreetingSkillCode) + return "", fmt.Errorf("default test skill not found: %s", skill.AfterSalesEscalationSkillCode) } return utils.JoinInt64s([]int64{skillItem.ID}), nil } diff --git a/cmd/testdata/skill/init.go b/cmd/testdata/skill/init.go index 6060ed5..190e5b0 100644 --- a/cmd/testdata/skill/init.go +++ b/cmd/testdata/skill/init.go @@ -10,7 +10,7 @@ import ( "github.com/mlogclub/simple/sqls" ) -const TestGreetingSkillCode = "testdata_greeting_skill" +const AfterSalesEscalationSkillCode = "after_sales_escalation_skill" type InitResult struct { Created int @@ -47,13 +47,45 @@ func buildSeedItems() []models.SkillDefinition { now := time.Now() return []models.SkillDefinition{ { - Code: TestGreetingSkillCode, - Name: "测试问候Skill", - Description: "用于测试客服 Agent 的 skill 路由。适合处理问候、机器人自我介绍、询问你能做什么、明确提到测试 skill 的问题。", - Instruction: "你是一个用于本地测试的客服 Skill。当用户在打招呼、询问你是谁、问你能做什么,或者明确提到测试 skill 时,请直接用中文回复:这是测试 Skill 的固定回复,说明 Agent 已经成功路由到 Skill 能力。", - Priority: 100, - Status: enums.StatusOk, - Remark: "Local testdata seed", + Code: AfterSalesEscalationSkillCode, + Name: "售后升级处理", + Description: "处理报障、投诉、售后跟进、建单、转人工等升级诉求。只在用户明确需要售后介入或问题升级处理时命中,不处理普通问候、产品介绍或泛咨询。", + Instruction: `你是“售后升级处理”专项 Skill,负责承接需要升级处理的客服诉求。 + +你的职责边界: +1. 仅处理以下场景:报障、投诉、问题久未解决、明确要求建单、明确要求转人工、要求售后继续跟进。 +2. 如果用户只是普通咨询、产品使用提问、寒暄、问候、闲聊,说明当前不属于本 Skill 的职责,避免误判为升级处理。 + +你的处理规则: +1. 先判断用户是否已经明确表达升级诉求;如果还不明确,先用简洁中文追问关键事实,例如订单号、设备/产品名称、故障现象、已尝试过的操作、期望处理方式。 +2. 如果用户要投诉、报障或售后跟进,但信息比较散乱,优先调用 graph/prepare_ticket_draft 整理工单草稿,再根据缺失字段继续追问。 +3. 只有在用户明确希望提交工单、投诉单、报障单,且标题与问题描述已经足够清晰时,才调用 graph/create_ticket_with_confirmation。 +4. 只有在用户明确要求人工客服,或你已经判断必须人工继续处理时,才调用 graph/handoff_to_human。 +5. 禁止只在文本里声称“已经建单”或“已经转人工”,相关动作必须通过对应工具执行。 +6. 如果信息不足以建单或转人工,先澄清,不要直接升级动作。 + +回复要求: +1. 全程使用中文,语气专业、简洁、像真实客服。 +2. 优先围绕问题定位和升级处理推进,不要输出与当前诉求无关的自我介绍。 +3. 如果进入确认流程,明确告知用户你将协助提交或转接,并等待确认结果。`, + Examples: `[ + "设备今天开始一直离线,重启也没用,帮我提个工单", + "这个问题三天了还没解决,我要投诉一下", + "麻烦转人工,你这边解决不了", + "售后什么时候联系我?这个故障还没有人跟进", + "帮我登记一下报障,产品型号是AX300,无法联网", + "我要申请售后处理,这个问题反复出现", + "你好,你是谁", + "这个产品怎么安装" +]`, + ToolWhitelist: `[ + "graph/prepare_ticket_draft", + "graph/create_ticket_with_confirmation", + "graph/handoff_to_human" +]`, + Priority: 100, + Status: enums.StatusOk, + Remark: "after-sales escalation skill", AuditFields: models.AuditFields{ CreatedAt: now, CreateUserID: 0, diff --git a/internal/services/skill_definition_service.go b/internal/services/skill_definition_service.go index f9297ff..3fafa11 100644 --- a/internal/services/skill_definition_service.go +++ b/internal/services/skill_definition_service.go @@ -148,17 +148,17 @@ func (s *skillDefinitionService) UpdateSkillDefinition(req request.UpdateSkillDe return errorsx.InvalidParam("Skill 编码已存在") } return repositories.SkillDefinitionRepository.Updates(sqls.DB(), req.ID, map[string]any{ - "code": normalized.Code, - "name": normalized.Name, - "description": normalized.Description, - "instruction": normalized.Instruction, - "examples": mustMarshalSkillStringArray(normalized.Examples), - "allowed_tool_codes": mustMarshalSkillStringArray(normalized.ToolWhitelist), - "priority": resolveSkillPriorityForService(normalized.Priority, current.Priority), - "remark": normalized.Remark, - "update_user_id": operator.UserID, - "update_user_name": operator.Username, - "updated_at": time.Now(), + "code": normalized.Code, + "name": normalized.Name, + "description": normalized.Description, + "instruction": normalized.Instruction, + "examples": mustMarshalSkillStringArray(normalized.Examples), + "tool_whitelist": mustMarshalSkillStringArray(normalized.ToolWhitelist), + "priority": resolveSkillPriorityForService(normalized.Priority, current.Priority), + "remark": normalized.Remark, + "update_user_id": operator.UserID, + "update_user_name": operator.Username, + "updated_at": time.Now(), }) }