Documentation
¶
Index ¶
- Constants
- func FormatWisdomForPrompt(notes []WisdomNote) string
- type Episode
- type EpisodeConsolidator
- type EpisodeConsolidatorConfig
- type EpisodeStatus
- type EpisodeSummarizer
- type Episodes
- type EpisodicMemory
- type EpisodicMemoryConfig
- type FailureReflectionRequest
- type FailureReflector
- type ImportanceScorer
- type ImportanceScoringRequest
- type PlanAdvisor
- type PlanAdvisoryRequest
- type PlanAdvisoryResult
- type ReactionContext
- type ReactionHandler
- type ReactionHandlerConfig
- type ReactionLedger
- type StepAdvisory
- type WisdomNote
- type WisdomStore
- type WisdomStoreConfig
- type WorkingMemory
Constants ¶
const AccomplishmentType = "accomplishment"
const EpisodeVectorType = "episode"
EpisodeVectorType is the value stored in the "type" metadata field for vector-backed episodes. Exported so callers (e.g. ForgetUser) can filter on this value without embedding magic strings.
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"`
// SenderID is the raw sender identity (e.g. Slack user ID) that generated
// this episode. Stored as vector metadata so ForgetUser can find and delete
// a user's episodes across all visibility scopes. Must be set per-Store call;
// do not carry it from an instance field to avoid stale values in group channels
// where one EpisodicMemory instance serves multiple senders.
SenderID string `json:"sender_id,omitempty"`
}
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.
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 ¶
HasFailures returns true if any episode has a failure status.
func (Episodes) HasSuccesses ¶
HasSuccesses returns true if any episode has a success or pending status.
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 (keyword path).
Service memory.Service
// VectorStore is the vector store backend. When non-nil, episodic memory
// is backed by vector search instead of the keyword-based Service.
VectorStore vector.IStore
// AppName identifies the application for memory scoping (keyword path).
AppName string
// UserID is the visibility-scoped identity used for retrieval isolation
// (e.g. "private:alice" or "group:C123" from DeriveVisibility()).
// It determines which episodes are visible in a given context.
UserID string
}
EpisodicMemoryConfig holds configuration for creating an EpisodicMemory. When VectorStore is non-nil, NewEpisodicMemory returns a vector-backed implementation. Otherwise it falls back to the memory.Service keyword backend.
func DefaultEpisodicMemoryConfig ¶
func DefaultEpisodicMemoryConfig() EpisodicMemoryConfig
func (EpisodicMemoryConfig) NewEpisodicMemory ¶
func (cfg EpisodicMemoryConfig) NewEpisodicMemory() EpisodicMemory
NewEpisodicMemory creates an EpisodicMemory from the config. When VectorStore is non-nil, returns a vector-backed implementation. Otherwise returns the keyword-based memory.Service implementation.
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.
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.