nyxcore-systems
6 min read

From Checkpoints to Cognition: Architecting Persistent Workflow Insights for AI Agents

Our latest challenge: equipping our AI agents with persistent, searchable memory. Dive into how we designed WorkflowInsight with pgvector, hybrid search, and a collaborative agentic team to unlock deeper context for future workflows.

AILLMVector DatabasesPostgreSQLtRPCWorkflow AutomationAgentic SystemsSoftware Architecture

Every developer knows the feeling: you've just wrestled a complex problem to the ground, implemented an elegant solution, and then... it's done. The lessons learned, the nuanced trade-offs, the "aha!" moments often remain uncaptured, fading into the ether of project history. What if our AI agents faced the same dilemma?

This was the core challenge driving our latest development push: how do we give our AI-driven workflows a persistent, searchable memory? Not just transient checkpoints, but actionable insights that can inform and enhance future tasks. We call it the Workflow Memory System.

The Vision: A Brain for Our Workflows

Our goal was clear: enable our agents to review past workflow outputs, extract key insights, and store them as WorkflowInsight records. These insights would then be available for vector-based search and selection, acting as a contextual source (via a simple {{memory}} template variable) when creating new workflows. Imagine an agent, kicking off a new task, and proactively querying its own institutional knowledge. Powerful, right?

The journey began with a dedicated team of three agents: an architect to map the landscape, an ML expert to design the data model and retrieval strategy, and a UX developer to craft the user experience.

Building the Foundation: A Dedicated Insight Model

The architect's initial research quickly revealed a critical gap: our existing MemoryEntry model was designed for transient checkpoints, not persistent, searchable knowledge. It lacked foreign keys to projects, its searchVector was never populated, and there was no mechanism for consolidation or writing back insights derived from completed workflows.

This confirmed our intuition: we needed a dedicated WorkflowInsight model. Our ML expert took the lead on schema design, proposing a robust solution:

  • WorkflowInsight Model: A standalone entity, distinct from MemoryEntry.
  • pgvector Embeddings: Each insight would store a 1536-dimensional embedding, generated from its content, allowing for semantic search.
  • HNSW Index: Crucial for efficient similarity search over large datasets.
  • RLS Policy: Row-Level Security to ensure insights are only visible to authorized users/projects.
  • tsvector Trigger: For traditional full-text search capabilities, complementing vector search.
  • Pain-Solution Pairing: A clever addition – insights could be linked via pairedInsightId, allowing us to capture common problems and their effective solutions.
  • Hybrid Search: The real power move. Our retrieval strategy combines 70% vector similarity search with 30% full-text search, offering the best of both worlds – semantic relevance and keyword precision.
typescript
// Conceptual Prisma schema for WorkflowInsight
model WorkflowInsight {
  id               String      @id @default(uuid())
  projectId        String
  title            String
  content          String
  embedding        Unsupported("vector(1536)") // Using pgvector
  searchVector     Unsupported("tsvector") // Populated by trigger
  tags             String[]
  createdAt        DateTime    @default(now())
  updatedAt        DateTime    @updatedAt
  pairedInsightId  String?     @unique // Link pain to solution, or vice-versa

  @@index([projectId])
  @@index([searchVector], type: Gin) // For tsvector
  @@index([embedding], type: Gin(ops: "vector_cosine_ops")) // For pgvector with HNSW
}

Bridging the Gap: From Review to Retrieval

Once the backend schema and services (saveInsights, insights.search tRPC procedures) were in motion, the UX developer brought the system to life for the users:

  1. SaveInsightsDialog: After a workflow's review step is approved, a dialog appears, allowing users to select key points from the output, add tags, assign them to a project, and save them as WorkflowInsight records.
  2. MemoryPicker: On the "create new workflow" page, a dedicated component lets users search and filter existing insights. Selected insights appear as chips, with a collapsible preview for quick context.
  3. {{memory}} Template Variable: The selected insights are then dynamically injected into the workflow's prompt via this variable, giving the new workflow direct access to relevant past knowledge.

This ensures a seamless flow: insight generation during review, and proactive insight consumption during new workflow creation.

Refining the Agent's Mind: A Continuous Improvement

Beyond the new memory system, we also shipped a significant improvement to our agent's core "thinking" process. Our recreateFromKeyPoint function now operates in a true refinement mode. Instead of regenerating an output from scratch, the LLM receives its previous output along with specific review feedback. This allows for iterative improvements, using a REFINE_SEPARATOR to intelligently strip or replace problematic blocks. This leads to more coherent and less "hallucinated" revisions.

typescript
// Simplified conceptual code for refinement
async function refineOutput(previousOutput: string, feedback: string): Promise<string> {
  const prompt = `
    You previously generated the following output:
    ---
    ${previousOutput}
    ---

    Here is specific feedback for refinement:
    ---
    ${feedback}
    ---

    Please revise the output based on the feedback. Use the REFINE_SEPARATOR to indicate sections to be replaced or new sections.

    Example:
    OLD SECTION TO REPLACE
    ${REFINE_SEPARATOR}
    NEW REVISED SECTION

    ${REFINE_SEPARATOR}
    ADD NEW SECTION HERE
    `;
  // Call LLM with prompt
  // Process LLM response, applying replacements based on REFINE_SEPARATOR
  return llmResponse;
}

Lessons from the Trenches: Shell Gotchas

No development session is complete without a few head-scratching moments. This time, Zsh's glob expansion was the culprit:

  • The npx tsx Trap: Trying to run an npx tsx command with an exclamation mark (!) directly in the code, e.g., npx tsx -e 'console.log("Hello!")'. Zsh interpreted ! as history expansion, leading to syntax errors.
    • Workaround: For complex one-liners or scripts, it's safer to write the code to a temporary file and execute that: npx tsx /path/to/temp.ts.
  • Bracketed Paths with git add: Attempting git add src/app/(dashboard)/dashboard/discussions/[id]/page.tsx also triggered Zsh's glob expansion on the [id].
    • Workaround: Always quote paths containing special characters like brackets when using git add (or other shell commands): git add "src/app/(dashboard)/dashboard/discussions/[id]/page.tsx".

These are small but frustrating time-sinks that serve as a good reminder to be mindful of shell behavior!

What's Next on the Roadmap

The immediate next steps involve bringing all these pieces together:

  1. Backend Completion: Architect finishing the WorkflowInsight Prisma model, tRPC procedures, and RLS SQL.
  2. Database Migration: Running npm run db:push && npm run db:generate to apply schema changes.
  3. UI Integration: Our UI developer will build and integrate the SaveInsightsDialog and MemoryPicker components.
  4. End-to-End Testing: Running a full workflow, saving insights, creating a new workflow with the memory picker, and verifying {{memory}} injection.
  5. Phase 2 (Future): Deeper integration with pgvector extension, a dedicated embedding service (potentially BYOK - Bring Your Own Key), and advanced HNSW index tuning for massive scale.

We're incredibly excited about giving our AI agents a lasting, searchable memory. This isn't just about storing data; it's about building a foundation for truly intelligent, context-aware automation. Stay tuned for more updates as we continue to build out this powerful system!

json
{"thingsDone":["Implemented LLM refinement mode with REFINE_SEPARATOR","Fixed Badge variant type error","Designed WorkflowInsight model with pgvector, HNSW, RLS, tsvector, and hybrid search","Designed SaveInsightsDialog, MemoryPicker, and {{memory}} template variable","Formed agentic design team (architect, ML expert, UX developer)"],"pains":["Zsh glob expansion issues with '!' in npx tsx commands","Zsh glob expansion issues with bracketed paths in git add"],"successes":["Successful architectural discovery of memory gaps","Robust schema design for persistent insights","Clear UX design for insight capture and retrieval","Working LLM refinement mode"],"techStack":["TypeScript","Next.js","tRPC","PostgreSQL","Prisma","pgvector","LLMs (likely OpenAI/Anthropic)","Zsh"]}