micro--go-micro
9dae4e34b7
* docs: add 'become a sponsor' call-to-action linking to Discord Now that there are a couple of sponsors, invite more: a short CTA under the Sponsors section in the README and on the landing page, pointing to the Discord to get in touch. * fix(health): remove duplicate RegistryCheck declaration Two PRs (#2957 and #2958) each added a RegistryCheck to the health package, leaving the package uncompilable on master (RegistryCheck redeclared: health/registry.go vs health/health.go). Keep the health.go implementation — it honors the check's context timeout so a hung registry (e.g. an unreachable etcd) reports down instead of blocking the probe — and remove the duplicate registry.go and its test. registry_check_test.go already covers healthy/down/nil/timeout/not-ready. * feat(agent): pluggable memory and custom tools Make agents compose the way services do — pluggable pieces with working defaults — by adding the two abstractions an agent needs beyond the model: - Memory: a pluggable interface for conversation memory. The default is store-backed and durable across restarts (the previous hardcoded behavior, now behind an interface); supply your own with WithMemory (in-memory, database, semantic store). NewMemory / NewInMemory provided. - Custom tools: WithTool registers any function as a tool the agent can call, so agents are no longer limited to orchestrating RPC services. Both exposed at the micro package (AgentMemory, AgentTool, NewMemory, NewInMemory). Behavior-preserving refactor of the agent's history into the default Memory; tests cover persistence, in-memory, clear, custom tool dispatch and errors. README + AGENT_DESIGN document the pluggable composition (model / memory / tools / guardrails). * blog: 'Doubling Down on Agents' (#20) The vision post for making agents a first-class framework the way services were: opinionated, batteries-included, pluggable. Frames an agent as a composition of model + memory + tools + guardrails with working defaults; introduces the new pluggable memory and custom tools; makes the microagents argument (an agent for everything, distributed like microservices); and lays out the three primitives — services, agents, workflows — as one substrate, with an honest list of the gaps still to fill (knowledge/retrieval, streaming, explicit loop). --------- Co-authored-by: Claude <noreply@anthropic.com>
159 行
4.4 KiB
Go
159 行
4.4 KiB
Go
package agent
|
|
|
|
import (
|
|
"context"
|
|
|
|
"go-micro.dev/v5/ai"
|
|
"go-micro.dev/v5/client"
|
|
"go-micro.dev/v5/registry"
|
|
"go-micro.dev/v5/store"
|
|
)
|
|
|
|
// Option configures an Agent.
|
|
type Option func(*Options)
|
|
|
|
// ApproveFunc decides whether an agent may execute a tool call before it
|
|
// runs. Returning false blocks the call; the reason is shown to the
|
|
// model so it can adapt. Use it for human-in-the-loop approval or policy
|
|
// checks. It is called for actions (service tools and delegate), not for
|
|
// the internal plan tool.
|
|
type ApproveFunc func(tool string, input map[string]any) (approved bool, reason string)
|
|
|
|
// ToolFunc handles a custom tool call. Return the result as a string
|
|
// (often JSON); return an error to report failure back to the model.
|
|
type ToolFunc func(ctx context.Context, input map[string]any) (string, error)
|
|
|
|
// customTool is a developer-registered tool beyond the agent's services.
|
|
type customTool struct {
|
|
def ai.Tool
|
|
handler ToolFunc
|
|
}
|
|
|
|
// Options holds agent configuration.
|
|
type Options struct {
|
|
Name string
|
|
Services []string
|
|
Prompt string
|
|
Provider string
|
|
Model string
|
|
APIKey string
|
|
Registry registry.Registry
|
|
Client client.Client
|
|
Store store.Store
|
|
HistoryLimit int
|
|
|
|
// Memory is the agent's conversation memory. Nil = the default
|
|
// store-backed memory (durable across restarts).
|
|
Memory Memory
|
|
|
|
// MaxSteps bounds the number of tool executions per Ask (0 =
|
|
// unbounded). Once exceeded, further tool calls are refused and the
|
|
// model is told to stop and summarize. A stopping condition.
|
|
MaxSteps int
|
|
// Approve gates each action before it runs. Nil = allow all.
|
|
Approve ApproveFunc
|
|
|
|
// tools are developer-registered custom tools (see WithTool).
|
|
tools []customTool
|
|
}
|
|
|
|
func newOptions(opts ...Option) Options {
|
|
o := Options{
|
|
Registry: registry.DefaultRegistry,
|
|
Client: client.DefaultClient,
|
|
Store: store.DefaultStore,
|
|
HistoryLimit: 50,
|
|
}
|
|
for _, opt := range opts {
|
|
opt(&o)
|
|
}
|
|
return o
|
|
}
|
|
|
|
// Name sets the agent name.
|
|
func Name(n string) Option {
|
|
return func(o *Options) { o.Name = n }
|
|
}
|
|
|
|
// Services sets which services this agent manages.
|
|
func Services(names ...string) Option {
|
|
return func(o *Options) { o.Services = names }
|
|
}
|
|
|
|
// Prompt sets the system prompt.
|
|
func Prompt(p string) Option {
|
|
return func(o *Options) { o.Prompt = p }
|
|
}
|
|
|
|
// Provider sets the LLM provider.
|
|
func Provider(p string) Option {
|
|
return func(o *Options) { o.Provider = p }
|
|
}
|
|
|
|
// Model sets the LLM model name.
|
|
func Model(m string) Option {
|
|
return func(o *Options) { o.Model = m }
|
|
}
|
|
|
|
// APIKey sets the API key for the LLM provider.
|
|
func APIKey(k string) Option {
|
|
return func(o *Options) { o.APIKey = k }
|
|
}
|
|
|
|
// WithRegistry sets the service registry.
|
|
func WithRegistry(r registry.Registry) Option {
|
|
return func(o *Options) { o.Registry = r }
|
|
}
|
|
|
|
// WithClient sets the RPC client.
|
|
func WithClient(c client.Client) Option {
|
|
return func(o *Options) { o.Client = c }
|
|
}
|
|
|
|
// WithStore sets the store for agent memory.
|
|
func WithStore(s store.Store) Option {
|
|
return func(o *Options) { o.Store = s }
|
|
}
|
|
|
|
// HistoryLimit sets the max conversation messages to retain.
|
|
func HistoryLimit(n int) Option {
|
|
return func(o *Options) { o.HistoryLimit = n }
|
|
}
|
|
|
|
// MaxSteps bounds tool executions per Ask (0 = unbounded). A stopping
|
|
// condition: beyond the limit, tool calls are refused and the model is
|
|
// told to stop and summarize.
|
|
func MaxSteps(n int) Option {
|
|
return func(o *Options) { o.MaxSteps = n }
|
|
}
|
|
|
|
// ApproveTool sets a human-in-the-loop / policy hook called before each
|
|
// action (service tools and delegate). Returning false blocks the call.
|
|
func ApproveTool(fn ApproveFunc) Option {
|
|
return func(o *Options) { o.Approve = fn }
|
|
}
|
|
|
|
// WithMemory sets the agent's conversation memory. The default is
|
|
// store-backed memory keyed by agent name; supply your own to use an
|
|
// in-process, database, or semantic store.
|
|
func WithMemory(m Memory) Option {
|
|
return func(o *Options) { o.Memory = m }
|
|
}
|
|
|
|
// WithTool registers a custom tool the agent can call, beyond the
|
|
// services it discovers — a local function, an external API, anything.
|
|
// properties is the JSON-schema map for the tool's parameters.
|
|
func WithTool(name, description string, properties map[string]any, handler ToolFunc) Option {
|
|
return func(o *Options) {
|
|
o.tools = append(o.tools, customTool{
|
|
def: ai.Tool{
|
|
Name: name,
|
|
OriginalName: name,
|
|
Description: description,
|
|
Properties: properties,
|
|
},
|
|
handler: handler,
|
|
})
|
|
}
|
|
}
|