Files
Cyrene_v2/internal/runtime/agent/agent.go
T

111 lines
4.4 KiB
Go

// Package agent implements the Agent entity of the Cyrene v2 runtime
// (DevDocs/design/04-运行时.md §1). An Agent is an instruction string plus an
// optional set of tools, an optional guardrail pipeline and an optional
// handoff, wired to a model Adapter (and an optional per-role Router). It
// executes a single step of the agent loop — assemble a request, run input and
// output guardrails, call the model and decode any tool calls — without owning
// the loop itself, which lives in internal/runtime/loop (M2).
package agent
import (
"errors"
"fmt"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/guardrail"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/tool"
)
// Sentinel errors returned by the Agent. Use errors.Is to test for them; they
// are returned directly or wrapped with %w so the underlying cause stays
// available.
var (
// ErrEmptyInstructions is returned by New when instructions is empty.
ErrEmptyInstructions = errors.New("agent: empty instructions")
// ErrBadOptions is returned by New when an option value is out of range.
ErrBadOptions = errors.New("agent: invalid options")
// ErrNoAdapter is returned by RunStep when no model Adapter was injected.
ErrNoAdapter = errors.New("agent: no model adapter")
)
// Agent is a single, self-contained runtime entity: an instruction string, an
// optional tool Dispatcher, an optional guardrail Pipeline, the model backend
// and an optional handoff placeholder. It is safe for concurrent use as long as
// its fields are not mutated after construction; RunStep builds a local Request
// and delegates to the (concurrency-safe) adapter, dispatcher and guardrail.
type Agent struct {
instructions string
tools *tool.Dispatcher
guards *guardrail.Pipeline
adapter model.Adapter
router *model.Router
recorder model.Recorder
handoff *Handoff
opts Options
}
// New builds an Agent from an instruction string, an optional tool Dispatcher,
// an optional guardrail Pipeline and zero or more options applied in order over
// the defaults (default MaxRounds is 8). It returns ErrEmptyInstructions when
// instructions is blank and ErrBadOptions when an option value is out of range.
//
// The concrete adapter/router used by RunStep are injected through the options
// (see WithModel); tools and guardrails may be nil to run a bare step, and the
// agent still works as long as an adapter is supplied before RunStep.
func New(instructions string, tools *tool.Dispatcher, guards *guardrail.Pipeline, opts ...Option) (*Agent, error) {
a := &Agent{
instructions: instructions,
tools: tools,
guards: guards,
opts: Options{MaxRounds: defaultMaxRounds},
}
if a.instructions == "" {
return nil, ErrEmptyInstructions
}
for _, o := range opts {
if o != nil {
o(a)
}
}
if err := validateOptions(a.opts); err != nil {
return nil, fmt.Errorf("agent: new: %w", err)
}
return a, nil
}
// Instructions returns the agent's instruction/system prompt.
func (a *Agent) Instructions() string { return a.instructions }
// Tools returns the agent's tool dispatcher, or nil when none was configured.
func (a *Agent) Tools() *tool.Dispatcher { return a.tools }
// Guardrails returns the agent's guardrail pipeline, or nil when none was
// configured.
func (a *Agent) Guardrails() *guardrail.Pipeline { return a.guards }
// Adapter returns the injected model adapter.
func (a *Agent) Adapter() model.Adapter { return a.adapter }
// Router returns the injected per-role router, or nil.
func (a *Agent) Router() *model.Router { return a.router }
// Model returns the configured model name. Empty means the injected Router or
// the adapter's own default decides.
func (a *Agent) Model() string { return a.opts.Model }
// Temperature returns the configured sampling temperature.
func (a *Agent) Temperature() float64 { return a.opts.Temperature }
// MaxRounds returns the tool-loop upper bound consumed by the loop (M2,
// external to this package).
func (a *Agent) MaxRounds() int { return a.opts.MaxRounds }
// Purpose returns the call-purpose label recorded on each CallRecord.
func (a *Agent) Purpose() string { return a.purpose() }
// MaxTokens returns the token cap passed to the model.
func (a *Agent) MaxTokens() int { return a.opts.MaxTokens }
// Options returns a copy of the agent's current config options.
func (a *Agent) Options() Options { return a.opts }