feat(clustering): persistent enrichment cache in task_enrichments table

Each unique task title is now enriched by LiteLLM once and cached in the DB.
Subsequent agent compute cycles (every 12h) fetch the cache before calling
ml-serving; only new titles hit the tip-generator.

- DB: task_enrichments(content_hash PK, description, model, created_at)
- TS: fetchEnrichmentCache / persistEnrichments helpers in agent-outputs.ts;
  enrichment_cache passed in compute request, new_enrichments persisted from response
- Python: AgentComputeRequest.enrichment_cache / AgentComputeResponse.new_enrichments;
  AgentInput.enrichment_cache; _enrich_batch returns (descriptions, new_entries);
  cluster_tasks returns (clusters, new_enrichments)
- FocusAreaAgent stashes new_enrichments in signals_snapshot under _new_enrichments;
  compute_agent endpoint pops it before storing the snapshot

Closes part of #129

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 14:39:35 +00:00
parent 08d08ad7b0
commit 9ddeea6cac
9 changed files with 158 additions and 40 deletions

View File

@@ -189,6 +189,15 @@ export const agentOutputs = sqliteTable('agent_outputs', {
agentVersion: text('agent_version').notNull(), // bump to invalidate on logic changes
});
// Persistent cache for LLM-enriched task descriptions used by clustering.
// Keyed by MD5 of raw task content; avoids re-calling LiteLLM on every agent compute cycle.
export const taskEnrichments = sqliteTable('task_enrichments', {
contentHash: text('content_hash').primaryKey(),
description: text('description').notNull(),
model: text('model').notNull().default('tip-generator'),
createdAt: text('created_at').notNull(),
});
// Admin saved SQL queries.
export const savedQueries = sqliteTable('saved_queries', {
id: text('id').primaryKey(),