feat(capability/skills): 可复用工作流技能(Skill 模型 + Store + YAML Loader + Runner 展开 + 注册)
This commit is contained in:
1 parent
d947ec41a9
commit
915aa8a100
10 files changed
+1235
No files matched your search
@@ -0,0 +1,134 @@
|
||||
// Package skills implements the M5 reusable-workflow capability: a Skill is a
|
||||
// higher-order, persistable workflow — an ordered set of steps plus a base
|
||||
// prompt, references, and guardrails — that builds on pkg/plugins/sdk and is
|
||||
// more structured than a Tool (DevDocs/design/09 §0/§3).
|
||||
//
|
||||
// The package provides five pieces:
|
||||
//
|
||||
// - Skill: the concrete in-memory model implementing pkg/plugins/sdk.Skill.
|
||||
// - Store: persistence of skills in a SQLite skills table.
|
||||
// - Loader: loading Skill definitions from YAML (a single stream or a
|
||||
// configs/skills/*.yaml directory).
|
||||
// - Runner: expanding a Skill into an executable Workflow for the loop/agent
|
||||
// executor (the actual execution itself lands in M2/M12).
|
||||
// - Register: publishing skills to the global capability registry
|
||||
// (internal/capability/registry) as KindSkill capabilities.
|
||||
package skills
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/logger"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/pkg/plugins/sdk"
|
||||
)
|
||||
|
||||
// Sentinel errors returned by the skills package. Use errors.Is to test.
|
||||
var (
|
||||
// ErrInvalidSkill is returned when a skill is nil or has a blank name.
|
||||
ErrInvalidSkill = errors.New("skills: invalid skill")
|
||||
// ErrNotFound is returned by Store when a skill is absent.
|
||||
ErrNotFound = errors.New("skills: skill not found")
|
||||
// ErrNilRegistrar is returned by Register/NewRunner when no registry was
|
||||
// supplied.
|
||||
ErrNilRegistrar = errors.New("skills: nil registrar")
|
||||
// ErrNilDB is returned by NewStore when no *sql.DB was supplied.
|
||||
ErrNilDB = errors.New("skills: nil db")
|
||||
// ErrRunPending is returned by Runner.Run, which awaits M12 wiring.
|
||||
ErrRunPending = errors.New("skills: run pending")
|
||||
)
|
||||
|
||||
// Skill is the concrete, persistable form of a skill (see pkg/plugins/sdk.Skill):
|
||||
// a reusable workflow (ordered steps with dependencies), a base prompt,
|
||||
// references, and guardrails. It is more structured than a Tool and is the
|
||||
// higher-order capability described in DevDocs/design/09 §0.
|
||||
//
|
||||
// Skill is immutable after construction; its getters return copies of the slice
|
||||
// fields, so a *Skill is safe for concurrent use as the sdk.Skill contract
|
||||
// requires.
|
||||
type Skill struct {
|
||||
name string
|
||||
description string
|
||||
prompt string
|
||||
steps []sdk.Step
|
||||
refs []sdk.Ref
|
||||
guardrails []sdk.Guardrail
|
||||
}
|
||||
|
||||
// compile-time assertion that *Skill satisfies the SDK Skill interface.
|
||||
var _ sdk.Skill = (*Skill)(nil)
|
||||
|
||||
// New builds a Skill. It returns ErrInvalidSkill when name is blank. The step,
|
||||
// ref, and guardrail slices are copied, so the caller remains free to mutate
|
||||
// them afterwards.
|
||||
func New(name, description, prompt string, steps []sdk.Step, refs []sdk.Ref, guardrails []sdk.Guardrail) (*Skill, error) {
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return nil, fmt.Errorf("skills: new: %w", ErrInvalidSkill)
|
||||
}
|
||||
return &Skill{
|
||||
name: name,
|
||||
description: description,
|
||||
prompt: prompt,
|
||||
steps: cloneSteps(steps),
|
||||
refs: cloneRefs(refs),
|
||||
guardrails: cloneGuardrails(guardrails),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Name returns the unique skill identifier.
|
||||
func (s *Skill) Name() string { return s.name }
|
||||
|
||||
// Description explains what the Skill accomplishes; the model uses it to select
|
||||
// a Skill for a task.
|
||||
func (s *Skill) Description() string { return s.description }
|
||||
|
||||
// Prompt returns the base system prompt for the Skill.
|
||||
func (s *Skill) Prompt() string { return s.prompt }
|
||||
|
||||
// Steps returns a copy of the ordered workflow steps.
|
||||
func (s *Skill) Steps() []sdk.Step { return cloneSteps(s.steps) }
|
||||
|
||||
// Refs returns a copy of the references the Skill may draw on.
|
||||
func (s *Skill) Refs() []sdk.Ref { return cloneRefs(s.refs) }
|
||||
|
||||
// Guardrails returns a copy of the constraints the Skill enforces.
|
||||
func (s *Skill) Guardrails() []sdk.Guardrail { return cloneGuardrails(s.guardrails) }
|
||||
|
||||
func cloneSteps(in []sdk.Step) []sdk.Step {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]sdk.Step, len(in))
|
||||
copy(out, in)
|
||||
return out
|
||||
}
|
||||
|
||||
func cloneRefs(in []sdk.Ref) []sdk.Ref {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]sdk.Ref, len(in))
|
||||
copy(out, in)
|
||||
return out
|
||||
}
|
||||
|
||||
func cloneGuardrails(in []sdk.Guardrail) []sdk.Guardrail {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]sdk.Guardrail, len(in))
|
||||
copy(out, in)
|
||||
return out
|
||||
}
|
||||
|
||||
// sourcedLog returns a source-tagged logger suitable for this package, falling
|
||||
// back to a discard logger when l is nil so callers can pass nil safely.
|
||||
func sourcedLog(l *slog.Logger) *slog.Logger {
|
||||
if l == nil {
|
||||
l = slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
}
|
||||
return logger.WithSource(l, "capability/skills")
|
||||
}
|
||||
Reference in new issue
Block a user