memory

package
v0.1.8-rc.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 11, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Index

Constants

View Source
const AccomplishmentType = "accomplishment"

Variables

This section is empty.

Functions

func FormatWisdomForPrompt

func FormatWisdomForPrompt(notes []WisdomNote) string

FormatWisdomForPrompt renders a list of wisdom notes as a prompt section. Used by buildAgentPrompt to inject consolidated lessons into context.

Types

type Episode

type Episode struct {
	Goal       string        `json:"goal"`
	Trajectory string        `json:"trajectory"`
	Status     EpisodeStatus `json:"status"`

	// Reflection is a verbal summary of why the episode succeeded or failed.
	// For failures, this is generated by the FailureReflector. For successes,
	// this is typically empty (the trajectory serves as the reference).
	Reflection string `json:"reflection,omitempty"`

	// Importance is an LLM-assigned 1-10 score indicating how significant
	// this experience is. Higher scores indicate novel or critical lessons.
	// Zero means no importance was assigned (backward-compatible default).
	Importance int `json:"importance,omitempty"`

	// CreatedAt records when the episode was stored. Used for recency-weighted
	// retrieval: newer episodes receive higher scores via exponential decay.
	CreatedAt time.Time `json:"created_at"`
}

Episode records a single subgoal-level experience for episodic memory. Each episode stores the goal, the full text trajectory, and the termination status. This granularity enables targeted in-context example retrieval per the paper.

func (Episode) String

func (ep Episode) String() string

String formats an episode for inclusion in an LLM prompt. Failure episodes include the reflection; successes show the trajectory.

type EpisodeConsolidator

type EpisodeConsolidator struct {
	// contains filtered or unexported fields
}

EpisodeConsolidator reads recent raw episodes, groups them, summarizes them into wisdom notes, and stores the results. It is designed to be called periodically (e.g., daily via cron or on startup).

func NewEpisodeConsolidator

func NewEpisodeConsolidator(cfg EpisodeConsolidatorConfig) *EpisodeConsolidator

NewEpisodeConsolidator creates a consolidator. Returns nil if any essential dependency is missing.

func (*EpisodeConsolidator) Consolidate

func (c *EpisodeConsolidator) Consolidate(ctx context.Context) int

Consolidate reads recent episodes, summarizes them into a single wisdom note, and stores it. It is idempotent for the same period — if called multiple times in the same day, only the first call produces a new note.

Returns the number of episodes consolidated, or 0 if nothing was done.

type EpisodeConsolidatorConfig

type EpisodeConsolidatorConfig struct {
	// Episodic is the source of raw episodes to consolidate.
	Episodic EpisodicMemory

	// Wisdom is the destination for consolidated wisdom notes.
	Wisdom WisdomStore

	// Summarizer generates the consolidated summary text.
	Summarizer EpisodeSummarizer

	// LookbackHours is how far back to look for episodes (default: 24).
	LookbackHours int
}

EpisodeConsolidatorConfig holds dependencies for creating a consolidator.

type EpisodeStatus

type EpisodeStatus string

EpisodeStatus records how an agent node terminated.

const (
	// EpisodeSuccess means the agent completed its subgoal.
	EpisodeSuccess EpisodeStatus = "success"
	// EpisodeFailure means the agent failed its subgoal.
	EpisodeFailure EpisodeStatus = "failure"
	// EpisodeExpand means the agent decomposed into sub-tasks.
	EpisodeExpand EpisodeStatus = "expand"
	// EpisodePending means the episode is awaiting human validation.
	// Episodes are initially stored as pending until a user reacts
	// (e.g. 👍 upgrades to success, 👎 downgrades to failure).
	EpisodePending EpisodeStatus = "pending"
)

type EpisodeSummarizer

type EpisodeSummarizer interface {
	// Summarize takes a batch of episodes and produces a concise wisdom summary.
	// Returns the summary text, or empty string if summarization fails.
	Summarize(ctx context.Context, episodes []Episode) string
}

EpisodeSummarizer generates a consolidated summary from a batch of episodes. The interface is defined in the memory package to avoid import cycles; the LLM-backed implementation lives in the reactree package.

type Episodes

type Episodes []Episode

Episodes is a domain type wrapping a slice of Episode. It provides behavior for summarizing, classifying, and formatting episodes for prompt injection — keeping that logic close to the data it operates on.

func (Episodes) HasFailures

func (eps Episodes) HasFailures() bool

HasFailures returns true if any episode has a failure status.

func (Episodes) HasSuccesses

func (eps Episodes) HasSuccesses() bool

HasSuccesses returns true if any episode has a success or pending status.

func (Episodes) Header

func (eps Episodes) Header() string

Header returns a contextual header based on the mix of episode statuses.

func (Episodes) Summarize

func (eps Episodes) Summarize() string

Summarize formats the episodes into a prompt-ready string. Each episode is rendered via its String() method, separated by blank lines.

type EpisodicMemory

type EpisodicMemory interface {
	// Store saves a completed episode into the memory.
	Store(ctx context.Context, episode Episode)

	// Retrieve finds the top-k most similar episodes for the given goal.
	Retrieve(ctx context.Context, goal string, k int) []Episode

	// RetrieveWeighted finds episodes for the given goal and ranks them
	// using a weighted score combining recency (exponential decay) and
	// importance. Returns the top-k highest-scoring episodes.
	RetrieveWeighted(ctx context.Context, goal string, k int) []Episode
}

EpisodicMemory stores and retrieves past subgoal-level experiences. Implementations can use embedding similarity (e.g., Sentence-BERT) to find the most relevant past experiences for a given goal.

func NewNoOpEpisodicMemory

func NewNoOpEpisodicMemory() EpisodicMemory

NewNoOpEpisodicMemory creates an episodic memory that does nothing. Store is a no-op and Retrieve/RetrieveWeighted always return nil. This is suitable for initial use cases that don't yet have an experience corpus, and avoids the overhead of constructing a real memory.Service backend.

type EpisodicMemoryConfig

type EpisodicMemoryConfig struct {
	// Service is the trpc-agent-go memory.Service backend.
	Service memory.Service

	// AppName identifies the application for memory scoping.
	AppName string

	// UserID identifies the user/session for memory scoping.
	UserID string
}

EpisodicMemoryConfig holds configuration for creating a memory.Service-backed EpisodicMemory implementation.

func DefaultEpisodicMemoryConfig

func DefaultEpisodicMemoryConfig() EpisodicMemoryConfig

func (EpisodicMemoryConfig) NewEpisodicMemory

func (cfg EpisodicMemoryConfig) NewEpisodicMemory() EpisodicMemory

NewServiceEpisodicMemory creates an EpisodicMemory backed by memory.Service. Episodes are stored as JSON-serialized content and searched by goal text.

func (EpisodicMemoryConfig) WithUserID

func (cfg EpisodicMemoryConfig) WithUserID(userID string) EpisodicMemoryConfig

WithUserID returns a copy of the config with the UserID set to the given value. This enables creating per-sender episodic memory from a shared base config.

type FailureReflectionRequest

type FailureReflectionRequest struct {
	// Goal is the task the agent was trying to accomplish.
	Goal string
	// ErrorOutput is the error text or failed output from the agent.
	ErrorOutput string
}

FailureReflectionRequest holds the inputs for generating a failure reflection.

type FailureReflector

type FailureReflector interface {
	// Reflect generates a 2-3 sentence reflection on why a task failed
	// and what the agent should try differently. Returns the reflection
	// text, or empty string if reflection is unavailable.
	Reflect(ctx context.Context, req FailureReflectionRequest) string
}

FailureReflector generates verbal reflections on agent failures. Instead of storing raw error text (which is nearly useless for future context), it asks a cheap LLM to summarize what went wrong and what the agent should try differently next time.

This is inspired by the Reflexion paper (Shinn et al., 2023) which showed that verbal reinforcement — storing reflections rather than raw trajectories — significantly improves agent self-correction.

The interface is defined here (memory package) to avoid import cycles; the LLM-backed implementation lives in the reactree package.

func NewNoOpFailureReflector

func NewNoOpFailureReflector() FailureReflector

NewNoOpFailureReflector creates a FailureReflector that always returns empty.

type ImportanceScorer

type ImportanceScorer interface {
	// Score returns a 1-10 importance score for the given episode.
	// Returns 0 if scoring is unavailable (best-effort).
	Score(ctx context.Context, req ImportanceScoringRequest) int
}

ImportanceScorer assigns a 1-10 importance score to an episode. Higher scores indicate novel, critical, or reusable lessons. Lower scores indicate routine or trivial experiences.

The interface lives here (memory package) to avoid import cycles; the LLM-backed implementation lives in the reactree package.

func NewNoOpImportanceScorer

func NewNoOpImportanceScorer() ImportanceScorer

NewNoOpImportanceScorer creates an ImportanceScorer that always returns 0.

type ImportanceScoringRequest

type ImportanceScoringRequest struct {
	// Goal is the task the agent was attempting.
	Goal string
	// Output is the trajectory or reflection text.
	Output string
	// Status is the episode outcome (success, failure, etc.)
	Status EpisodeStatus
}

ImportanceScoringRequest holds the inputs for scoring an episode's importance.

type PlanAdvisor

type PlanAdvisor interface {
	// Advise queries past experiences for each step goal and returns
	// advisory context to inject into each step.
	Advise(ctx context.Context, req PlanAdvisoryRequest) PlanAdvisoryResult
}

PlanAdvisor consults past experiences (episodic memory and wisdom) to generate advisory context for plan steps BEFORE they execute. This closes the gap where plan decomposition was "blind" to past outcomes — the LLM now sees relevant successes, failures, and lessons when executing each step of a multi-step plan.

The interface is defined here (memory package) to avoid import cycles; the wiring happens in the reactree package.

func NewNoOpPlanAdvisor

func NewNoOpPlanAdvisor() PlanAdvisor

NewNoOpPlanAdvisor creates a PlanAdvisor that returns no advisory.

func NewPlanAdvisor

func NewPlanAdvisor(

	episodic EpisodicMemory,

	wisdom WisdomStore) PlanAdvisor

NewPlanAdvisor creates a PlanAdvisor backed by episodic memory and an optional wisdom store. Returns a no-op advisor if episodic is nil.

type PlanAdvisoryRequest

type PlanAdvisoryRequest struct {
	// OverallGoal is the parent task's goal — used to retrieve
	// high-level wisdom notes.
	OverallGoal string

	// StepGoals maps step name → step goal text. Each step goal
	// is used to query episodic memory for relevant past experiences.
	StepGoals map[string]string
}

PlanAdvisoryRequest holds the inputs for generating plan-level advisory.

type PlanAdvisoryResult

type PlanAdvisoryResult struct {
	// Advisories maps step name → StepAdvisory.
	Advisories map[string]StepAdvisory
}

PlanAdvisoryResult holds the advisory output for all plan steps.

func (PlanAdvisoryResult) ForStep

func (r PlanAdvisoryResult) ForStep(stepName string) string

ForStep returns the formatted advisory for a specific step. Returns empty string if no advisory exists for that step.

func (PlanAdvisoryResult) StepsAdvised

func (r PlanAdvisoryResult) StepsAdvised() int

StepsAdvised returns the number of steps that received advisory.

type ReactionContext

type ReactionContext struct {
	// Goal is the agent's goal when the message was sent.
	Goal string `json:"goal"`
	// Output is the agent's output text (truncated for storage).
	Output string `json:"output"`
	// SenderKey identifies the user/channel ("platform:senderID:channelID").
	SenderKey string `json:"sender_key"`
}

ReactionContext captures the agent's goal and output when a message was sent, so that a later emoji reaction can be correlated back to the episode. Without this correlation, reactions would be meaningless because we wouldn't know which goal/output the user is approving.

type ReactionHandler

type ReactionHandler struct {
	// contains filtered or unexported fields
}

ReactionHandler processes incoming emoji reactions and converts them into episodic memory entries. This is the core of the "giving the LLM a cookie" mechanism: positive reactions store the episode as validated success, negative reactions store it as failure.

Without this handler, the system would rely solely on heuristic-based episode storage (looksLikeError), which is susceptible to memory poisoning from graceful LLM failures.

func NewReactionHandler

func NewReactionHandler(cfg ReactionHandlerConfig) *ReactionHandler

NewReactionHandler creates a handler that processes incoming reactions against the ledger and stores/updates episodes accordingly. Returns nil if either dependency is nil (safe to call HandleReaction on nil).

func (*ReactionHandler) HandleReaction

func (h *ReactionHandler) HandleReaction(ctx context.Context, msg messenger.IncomingMessage)

HandleReaction processes an incoming reaction message. It looks up the reacted message in the ledger, maps the emoji to an episode status, and stores the validated episode in episodic memory.

It is safe to call on a nil receiver (no-op).

type ReactionHandlerConfig

type ReactionHandlerConfig struct {
	Ledger   *ReactionLedger
	Episodic EpisodicMemory
}

ReactionHandlerConfig holds the dependencies needed to create a ReactionHandler.

type ReactionLedger

type ReactionLedger struct {
	// contains filtered or unexported fields
}

ReactionLedger is a DB-backed ledger that maps sent message IDs to the agent context that produced them. The send_message tool records entries; the reaction handler looks them up.

Without this ledger, incoming reactions (which only carry the reacted message ID) cannot be correlated to the agent's goal and output.

Backed by the generic short_memories table (memory_type = "reaction_ledger").

func NewReactionLedger

func NewReactionLedger(store *db.ShortMemoryStore) *ReactionLedger

NewReactionLedger creates a new DB-backed ledger. If store is nil, the ledger operates as a no-op (lookups always return false).

func (*ReactionLedger) Lookup

func (l *ReactionLedger) Lookup(ctx context.Context, messageID string) (ReactionContext, bool)

Lookup retrieves the agent context for a sent message ID. Returns the context and true if found and not expired, or zero value and false otherwise.

func (*ReactionLedger) Record

func (l *ReactionLedger) Record(ctx context.Context, messageID string, goal, output, senderKey string)

Record associates a sent message ID with the agent context that produced it.

type StepAdvisory

type StepAdvisory struct {
	// StepName identifies which plan step this advisory is for.
	StepName string

	// Episodes are the relevant past experiences for this step.
	Episodes Episodes

	// WisdomSection is the formatted wisdom notes (shared across steps).
	WisdomSection string
}

StepAdvisory holds the advisory context for a single plan step.

func (StepAdvisory) Format

func (sa StepAdvisory) Format() string

Format renders the advisory as a prompt-ready string. Returns empty if there is nothing to advise on.

type WisdomNote

type WisdomNote struct {
	// Summary is the consolidated text from multiple episodes.
	Summary string `json:"summary"`

	// Period is the time range this note covers (e.g., "2026-03-10").
	Period string `json:"period"`

	// EpisodeCount is how many raw episodes were distilled into this note.
	EpisodeCount int `json:"episode_count"`

	// CreatedAt is when this wisdom note was generated.
	CreatedAt time.Time `json:"created_at"`
}

WisdomNote is a concise, distilled summary of raw episodes from a time period (e.g., one day). Instead of injecting 20 raw episodes into a prompt, the consolidator batches them into 1-2 wisdom notes that capture the essential lessons.

Format:

"On <date>, you learned:
 - <lesson 1>
 - <lesson 2>
 ..."

type WisdomStore

type WisdomStore interface {
	// StoreWisdom saves a consolidated wisdom note.
	StoreWisdom(ctx context.Context, note WisdomNote)

	// RetrieveWisdom returns the most recent wisdom notes (up to limit).
	RetrieveWisdom(ctx context.Context, limit int) []WisdomNote
}

WisdomStore stores and retrieves consolidated wisdom notes. It uses the same memory.Service backend as episodic memory.

func NewNoOpWisdomStore

func NewNoOpWisdomStore() WisdomStore

NewNoOpWisdomStore creates a WisdomStore that discards all writes and returns no results.

type WisdomStoreConfig

type WisdomStoreConfig struct {
	Service memoryService.Service
	AppName string
	UserID  string
}

WisdomStoreConfig holds configuration for creating a WisdomStore.

func (WisdomStoreConfig) NewWisdomStore

func (cfg WisdomStoreConfig) NewWisdomStore() WisdomStore

NewWisdomStore creates a WisdomStore backed by memory.Service.

type WorkingMemory

type WorkingMemory struct {
	// contains filtered or unexported fields
}

WorkingMemory is a thread-safe key-value store shared across all agent nodes in a single ReAcTree run. It enables agents to share environment-specific observations (e.g., discovered file content, resource locations) without re-exploring the environment. Without this, each agent node would operate in isolation, potentially duplicating costly exploration actions.

func NewWorkingMemory

func NewWorkingMemory() *WorkingMemory

NewWorkingMemory creates an empty WorkingMemory instance.

func (*WorkingMemory) Clear

func (m *WorkingMemory) Clear()

Clear removes all entries from working memory.

func (*WorkingMemory) Keys

func (m *WorkingMemory) Keys() []string

Keys returns all keys currently stored in working memory. This is useful for agents to discover what observations are available.

func (*WorkingMemory) Recall

func (m *WorkingMemory) Recall(key string) (string, bool)

Recall retrieves the value for a given key from working memory. Returns the value and true if the key exists, or empty string and false otherwise.

func (*WorkingMemory) Snapshot

func (m *WorkingMemory) Snapshot() map[string]string

Snapshot returns a shallow copy of the current memory state. Useful for logging or passing a read-only view to prompts.

func (*WorkingMemory) Store

func (m *WorkingMemory) Store(key, value string)

Store saves a key-value pair into working memory. If the key already exists, its value is overwritten with the latest observation.

Directories

Path Synopsis
Code generated by counterfeiter.
Code generated by counterfeiter.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL