From 19848d031b51532a43679f7a434a48606c7f6d35 Mon Sep 17 00:00:00 2001 From: mlogclub Date: Fri, 17 Apr 2026 18:57:34 +0800 Subject: [PATCH] refactor(skills): update routeSkillWithLLM to use RuntimeContext and streamline user message handling --- .../runtime/instruction/project_instruction.go | 1 + internal/ai/skills/matcher.go | 2 +- internal/ai/skills/router.go | 18 +++++++++++------- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/internal/ai/runtime/instruction/project_instruction.go b/internal/ai/runtime/instruction/project_instruction.go index 4334eec..42ee49e 100644 --- a/internal/ai/runtime/instruction/project_instruction.go +++ b/internal/ai/runtime/instruction/project_instruction.go @@ -3,6 +3,7 @@ package instruction // DefaultProjectInstruction 为当前项目统一注入的全局项目规则。 // // 默认回退到内置常量;运行时优先由 ProjectInstructionProvider 读取仓库中的 AGENTS.md。 +// TODO 这里是做什么的?为什么不直接读取AIAgent的? const DefaultProjectInstruction = `# AGENTS.md 本文件定义本项目内 AI Agent 的强制开发规则。除非用户明确要求偏离,否则必须遵循。 diff --git a/internal/ai/skills/matcher.go b/internal/ai/skills/matcher.go index 5bee18e..e09227c 100644 --- a/internal/ai/skills/matcher.go +++ b/internal/ai/skills/matcher.go @@ -53,7 +53,7 @@ func MatchSkill(execCtx context.Context, ctx RuntimeContext) (*models.SkillDefin } } - selected, routeTrace, err := routeSkillWithLLM(execCtx, ctx.AIConfig, ctx.UserMessage, candidates) + selected, routeTrace, err := routeSkillWithLLM(execCtx, ctx, candidates) if routeTrace != nil { trace.Status = routeTrace.Status trace.SelectedSkillCode = routeTrace.SelectedSkillCode diff --git a/internal/ai/skills/router.go b/internal/ai/skills/router.go index 100c239..8c7b04a 100644 --- a/internal/ai/skills/router.go +++ b/internal/ai/skills/router.go @@ -9,24 +9,28 @@ import ( "cs-agent/internal/ai" "cs-agent/internal/models" + + "github.com/mlogclub/simple/common/strs" ) -func routeSkillWithLLM(ctx context.Context, aiConfig models.AIConfig, userMessage string, candidates []models.SkillDefinition) (*models.SkillDefinition, *RouteTrace, error) { +const routeSkillSystemPrompt = `你是客服技能路由器。你只能在候选 Skill 中选择一个最合适的 skillCode,或者返回 NONE。 +只有当用户问题与 Skill 的职责边界明确匹配时才选择; +如果不明确、信息不足、多个 Skill 都不够确定,就返回 NONE。 +输出只能是 skillCode 或 NONE,不能输出其他内容。` + +func routeSkillWithLLM(ctx context.Context, runtimeCtx RuntimeContext, candidates []models.SkillDefinition) (*models.SkillDefinition, *RouteTrace, error) { trace := &RouteTrace{Status: "started"} if len(candidates) == 0 { trace.Status = "no_candidate" return nil, trace, nil } - userMessage = strings.TrimSpace(userMessage) - if userMessage == "" { + if strs.IsBlank(runtimeCtx.UserMessage) { trace.Status = "empty_user_message" return nil, trace, nil } - // TODO 系统体是指为什么不是配置在AIAgent中的? - systemPrompt := "你是客服技能路由器。你只能在候选 Skill 中选择一个最合适的 skillCode,或者返回 NONE。只有当用户问题与 Skill 的职责边界明确匹配时才选择;如果不明确、信息不足、多个 Skill 都不够确定,就返回 NONE。输出只能是 skillCode 或 NONE,不能输出其他内容。" - userPrompt := buildSkillRoutePrompt(userMessage, candidates) + userPrompt := buildSkillRoutePrompt(runtimeCtx.UserMessage, candidates) startedAt := time.Now() - result, err := ai.LLM.ChatWithConfig(ctx, aiConfig, systemPrompt, userPrompt) + result, err := ai.LLM.ChatWithConfig(ctx, runtimeCtx.AIConfig, routeSkillSystemPrompt, userPrompt) trace.LatencyMs = time.Since(startedAt).Milliseconds() if err != nil { trace.Status = "route_error"