Graph Feedback
EMA weights production wire-up.
Graph feedback closes the learning loop: when an agent session completes with a known outcome (accepted, rework, or rejected), feedbackWeight on every node and edge that was surfaced during context injection is updated via an Exponential Moving Average (EMA). Over time, frequently-useful entities rank higher in future queries; entities that led to rework or rejection rank lower.
Architecture
EMA update formula
The feedback weight update uses a standard EMA:
new_weight = alpha * new_score + (1 - alpha) * current_weightWhere:
alphadefaults to0.1(slow decay - the graph is meant to be stable)new_scoreis derived from the outcome:accepted → 1.0,rework → 0.5,rejected → 0.0current_weightis the currentfeedbackWeighton the node or edge (default0.5)
Both node and edge feedbackWeight columns are updated independently. The edge weight adjustment influences triplet scoring for future queries via the feedbackInfluence parameter (default 0.3).
Production wire-up
The production entry point is applyFeedback in graph-feedback.ts. It:
- Reads
context_injection_logsto find the node IDs and edge keys that were surfaced to the agent during context injection - Resolves the project ID via Redis session-project mapping
- Constructs
PgGraphStore.withScope({ orgId, projectId })to bind tenant scope - Constructs
PgFeedbackHistoryStoreforgraph_feedback_historywrites - Creates a
GraphFeedbackServicewith the Voyage document embedder (whenVOYAGE_API_KEYis set) for triplet embedding updates - Delegates to
GraphFeedbackService.applyFeedback
import { applyFeedback } from '@/lib/memory/graph-feedback'
// Called from session-lifecycle-hooks.ts after outcome resolution
await applyFeedback({
orgId: 'org_...',
sessionId: 'sess_...',
outcome: 'accepted', // 'accepted' | 'rework' | 'rejected'
})Fallthrough cases
The function exits cleanly without throwing when:
orgIdis undefined- No
context_injection_logsrow exists for the session (context injection skipped) - Both
graphNodeIdsandgraphEdgeKeysarrays are empty (graph was disabled or returned no triplets) resolveProjectIdForSessionreturns null (Redis state expired; a structured warning is logged)
All fallthrough cases are expected during normal operation - not every session injects graph context, and not every org has graph enabled.
Triplet embeddings
When a VOYAGE_API_KEY is configured and embeddings are enabled, the feedback service also embeds the triplet string "sourceName → relationshipName → targetName" and writes it to graph_triplet_embeddings via store.upsertTripletEmbedding. These vectors power triplet-level cosine retrieval (the <=> operator on the HNSW index) in addition to node-level similarity.
When no embedder is available, the feedback path still runs fully - node and edge feedbackWeight updates apply regardless. The triplet embedding step is silently skipped.
Feedback history
Every feedback update appends a row to graph_feedback_history via PgFeedbackHistoryStore. The history table powers the trend chart in the memory analytics dashboard (feedback impact sub-view) and is the data source for the graph_feedback audit event.
Audit trail
Each applyFeedback call emits a graph.feedback event to the hash-chained audit log. The event includes the session ID, outcome, scope, and counts of node/edge updates.
GraphFeedbackService API (advanced)
For direct service use (e.g. the graph_improve MCP tool):
import { GraphFeedbackService } from '@/lib/graph/feedback/service'
const service = new GraphFeedbackService({
store: pgGraphStore.withScope({ orgId, projectId }),
history: pgFeedbackHistoryStore,
audit: graphFeedbackAuditHook,
embedder: optionalEmbedder, // (text) => Promise<number[] | null>
})
const result = await service.applyFeedback({
sessionId,
outcome: 'rework',
usedNodeIds: ['node-uuid-1', 'node-uuid-2'],
usedEdgeKeys: [
{ sourceId: 'node-uuid-1', targetId: 'node-uuid-2', relationshipName: 'depends_on' }
],
scope: { orgId, projectId },
alpha: 0.1, // optional, default 0.1
})
// result: { nodeUpdates, edgeUpdates, tripletEmbeddings, durationMs }Related pages
- Context Injection - surfaces nodes to agent, populates injection log
- Hybrid Search -
feedbackWeightinfluences triplet scoring - MCP Tools -
graph_improvetool exposes this service to agents - Memory Feedback - parallel feedback loop for observation weights