(ctx context.Context, in *SkillScanInput)
| 435 | } |
| 436 | |
| 437 | func (s *defaultSkillScanner) Scan(ctx context.Context, in *SkillScanInput) (*SkillScanOutput, error) { |
| 438 | if in == nil || strings.TrimSpace(in.SkillMD) == "" { |
| 439 | return nil, ErrSkillScanMissingSkillMD |
| 440 | } |
| 441 | if in.ScanChannel == nil { |
| 442 | return nil, ErrSkillScanNoWorkAIModelConfigured |
| 443 | } |
| 444 | if strings.TrimSpace(in.ScanModel) == "" { |
| 445 | return nil, ErrSkillScanNoWorkAIModelConfigured |
| 446 | } |
| 447 | |
| 448 | systemPrompt, userPrompt := buildSkillScanPrompt(in) |
| 449 | |
| 450 | request := &relaymodel.GeneralOpenAIRequest{ |
| 451 | Model: in.ScanModel, |
| 452 | MaxTokens: 1536, |
| 453 | Messages: []relaymodel.Message{ |
| 454 | {Role: "system", Content: systemPrompt}, |
| 455 | {Role: "user", Content: userPrompt}, |
| 456 | }, |
| 457 | } |
| 458 | |
| 459 | invoker := in.LLMInvoker |
| 460 | if invoker == nil { |
| 461 | invoker = newDefaultSkillLibraryLLMInvoker() |
| 462 | } |
| 463 | |
| 464 | responseText, callErr, openaiErr := invokeSkillLibraryLLMWithInvoker(ctx, invoker, in.ScanChannel, request) |
| 465 | if callErr != nil { |
| 466 | logger.Warnf(ctx, "【技能运行】扫描模型调用失败,启用基础兜底: skill_name=%s err=%v", in.SkillName, callErr) |
| 467 | return fallbackSkillScanOutput(in, fmt.Sprintf("llm call failed: %v", callErr)), nil |
| 468 | } |
| 469 | if openaiErr != nil { |
| 470 | logger.Warnf(ctx, "【技能运行】扫描模型返回错误,启用基础兜底: skill_name=%s err=%s", in.SkillName, openaiErr.Message) |
| 471 | return fallbackSkillScanOutput(in, fmt.Sprintf("llm scan failed: %s", openaiErr.Message)), nil |
| 472 | } |
| 473 | |
| 474 | parsed, err := parseSkillScanResultPayload(ctx, responseText) |
| 475 | if err != nil { |
| 476 | logger.Warnf(ctx, "【技能运行】扫描响应解析失败: skill_name=%s err=%v response=%q", in.SkillName, err, responseText) |
| 477 | return fallbackSkillScanOutput(in, err.Error()), nil |
| 478 | } |
| 479 | |
| 480 | payload, _ := json.Marshal(map[string]interface{}{ |
| 481 | "scan_model": in.ScanModel, |
| 482 | "entry_count": len(in.FileEntries), |
| 483 | "skill_md_size": len(in.SkillMD), |
| 484 | "risk_level": parsed.RiskLevel, |
| 485 | "llm_response": responseText, |
| 486 | }) |
| 487 | |
| 488 | output := &SkillScanOutput{ |
| 489 | RiskLevel: parsed.RiskLevel, |
| 490 | ScoreIntegrity: clampScore(float64(parsed.ScoreIntegrity)), |
| 491 | ScorePracticality: clampScore(float64(parsed.ScorePracticality)), |
| 492 | ScoreSafety: clampScore(float64(parsed.ScoreSafety)), |
| 493 | ScoreCodeQuality: clampScore(float64(parsed.ScoreCodeQuality)), |
| 494 | ScoreDocQuality: clampScore(float64(parsed.ScoreDocQuality)), |
nothing calls this directly
no test coverage detected