package sdk import ( "errors" "fmt" "strings" ) // ErrInvalidSkillField 表示技能定义里的某个字段取值非法:枚举越界,或声明自相 // 矛盾(例如同一个工具同时出现在 allowed-tools 与 disallowed-tools)。 // ParseInvocation / ParseContextMode 用它分类错误,调用方用 errors.Is 判定。 var ErrInvalidSkillField = errors.New("sdk: invalid skill field") // Step 是技能工作流里的一步(design/09 §6.2)。 type Step struct { // Name 是步骤标识,技能内唯一;depends 按它引用。 Name string // Prompt 是这一步交给模型的指令。 Prompt string // Depends 列出必须先完成的前序步骤名。 Depends []string } // Ref 是技能可以引用的外部素材(工具、文档、文件等)。 type Ref struct { // Name 标识该引用。 Name string // Type 是自由取值的种类,例如 "tool"、"doc"、"file"。 Type string // Value 是引用载荷或指针。 Value string // Optional 为真表示该引用缺失不算失败。 Optional bool } // Guardrail 是技能执行期间施加的约束(design/09 §6.4-4)。 type Guardrail struct { // ID 命名该约束。 ID string // Message 是约束内容(也是违规时如实回显的那一句)。 Message string // Enforced 为真表示违反即中止技能;为假表示只 WARN 后继续。 Enforced bool } // Skill 是可复用的高阶工作流:一组带提示词的步骤,配以基础提示词、引用与约束。 // 它比 Tool 更结构化,通常由多个步骤组合而成。 // // 实现必须并发安全。 type Skill interface { // Name 返回技能的唯一标识。 Name() string // Description 说明技能做什么;模型据此挑选技能。 Description() string // Prompt 返回技能的基础系统提示词。 Prompt() string // Steps 返回有序的工作流步骤。 Steps() []Step // Refs 返回技能可引用的素材。 Refs() []Ref // Guardrails 返回技能施加的约束。 Guardrails() []Guardrail } // Invocation 是技能的调用三态(design/09 §6.2 定稿,对齐 Agent Skills 的 // `disable-model-invocation` / `user-invocable` 两枚开关压成的一枚枚举)。 type Invocation string const ( // InvocationBoth:清单含 description,人与模型都可调(缺省)。 InvocationBoth Invocation = "both" // InvocationModelOnly:清单**不含** description(省上下文),只有模型可调。 InvocationModelOnly Invocation = "model-only" // InvocationUserOnly:清单含 description,**模型不可调**(只认显式调用)。 InvocationUserOnly Invocation = "user-only" ) // Valid 报告 i 是否是已知的调用态;空值不算合法(缺省由 OrDefault 补)。 func (i Invocation) Valid() bool { switch i { case InvocationBoth, InvocationModelOnly, InvocationUserOnly: return true default: return false } } // OrDefault 把空值补成 InvocationBoth(design/09 §6.2 的缺省列)。 func (i Invocation) OrDefault() Invocation { if i == "" { return InvocationBoth } return i } // ModelInvocable 报告模型是否可以调用该技能(user-only 不可调)。 func (i Invocation) ModelInvocable() bool { return i.OrDefault() != InvocationUserOnly } // UserInvocable 报告人是否可以显式调用该技能(model-only 不给人调)。 func (i Invocation) UserInvocable() bool { return i.OrDefault() != InvocationModelOnly } // ListingHasDescription 报告该调用态下清单是否要带 description // (model-only 不带:清单只给名字,省上下文)。 func (i Invocation) ListingHasDescription() bool { return i.OrDefault() != InvocationModelOnly } // ParseInvocation 解析磁盘/YAML 上的调用态字符串。空串取缺省 both;其余非法值 // 一律报错(绝不静默当成 both —— 那是把配置写错伪装成成功)。 func ParseInvocation(s string) (Invocation, error) { trimmed := Invocation(strings.TrimSpace(s)) if trimmed == "" { return InvocationBoth, nil } if !trimmed.Valid() { return "", fmt.Errorf("sdk: invocation %q: %w", s, ErrInvalidSkillField) } return trimmed, nil } // ContextMode 是技能的上下文模式(design/09 §6.2/§6.4-6)。 type ContextMode string const ( // ContextInline:在当前会话上下文内跑(缺省)。 ContextInline ContextMode = "inline" // ContextFork:在隔离子代理里跑,不看会话历史,结论回灌父会话。 ContextFork ContextMode = "fork" ) // Valid 报告 c 是否是已知的上下文模式;空值不算合法。 func (c ContextMode) Valid() bool { switch c { case ContextInline, ContextFork: return true default: return false } } // OrDefault 把空值补成 ContextInline(缺省)。 func (c ContextMode) OrDefault() ContextMode { if c == "" { return ContextInline } return c } // ParseContextMode 解析磁盘/YAML 上的上下文模式。空串取缺省 inline;其余非法值 // 报错(理由同 ParseInvocation)。 func ParseContextMode(s string) (ContextMode, error) { trimmed := ContextMode(strings.TrimSpace(s)) if trimmed == "" { return ContextInline, nil } if !trimmed.Valid() { return "", fmt.Errorf("sdk: context %q: %w", s, ErrInvalidSkillField) } return trimmed, nil } // SkillSpec 是技能定义的完整只读视图(design/09 §6.2 定稿字段)。 // // 它是 Skill 之上的**可选**扩展:未实现它的技能一律按各字段的缺省取值看待, // 因此既有的第三方 Skill 实现不需要改动即可继续注册与运行。 type SkillSpec interface { Skill // Version 是定义版本(形状迁移用),缺省 1。 Version() int // WhenToUse 是「何时使用」补充说明,与 Description 一起进清单并计入预算。 WhenToUse() string // Invocation 是调用三态。 Invocation() Invocation // Triggers 是触发词,与 Paths 一起决定自动激活。 Triggers() []string // Paths 是 glob;仅当工作文件命中时自动激活。 Paths() []string // AllowedTools 是**能力上限**(不是免审批名单),见 design/09 §6.4-3。 AllowedTools() []string // DisallowedTools 从上限里剔除。 DisallowedTools() []string // ContextMode 是上下文模式(对应 YAML 键 `context`;方法名不叫 Context 以 // 避免与标准库 context 混淆)。 ContextMode() ContextMode // Agent 是 context: fork 时使用的子代理类型。 Agent() string // Model 是可选模型覆盖;per-role 路由优先于它(design/04 §3)。 Model() string // Effort 是可选思考档位覆盖。 Effort() string // Metadata 是自由 KV,宿主不解释。 Metadata() map[string]string } // SkillView 把任意 Skill 归一化成 SkillSpec:实现了 SkillSpec 的原样返回, // 否则套一层全缺省的包装。宿主用它读技能字段,就不必到处写类型断言。 func SkillView(s Skill) SkillSpec { if s == nil { return nil } if spec, ok := s.(SkillSpec); ok { return spec } return plainSkill{Skill: s} } // plainSkill 为只实现基础 Skill 的技能补齐缺省字段。 type plainSkill struct{ Skill } func (p plainSkill) Version() int { return DefaultSkillVersion } func (p plainSkill) WhenToUse() string { return "" } func (p plainSkill) Invocation() Invocation { return InvocationBoth } func (p plainSkill) Triggers() []string { return nil } func (p plainSkill) Paths() []string { return nil } func (p plainSkill) AllowedTools() []string { return nil } func (p plainSkill) DisallowedTools() []string { return nil } func (p plainSkill) ContextMode() ContextMode { return ContextInline } func (p plainSkill) Agent() string { return "" } func (p plainSkill) Model() string { return "" } func (p plainSkill) Effort() string { return "" } func (p plainSkill) Metadata() map[string]string { return nil } // DefaultSkillVersion 是未声明 version 时的定义版本(design/09 §6.2 缺省列)。 const DefaultSkillVersion = 1