feat: recruiter-scannable cards + Poimen Memory deep dive page #14
+3
-1
@@ -29,7 +29,9 @@ export default function Home() {
|
||||
: undefined,
|
||||
videoUrl: '#',
|
||||
articleUrl: index === 4 ? 'https://lnkd.in/p/gm2PZkWw' : '#',
|
||||
deepDive: index === 2 ? { url: '/homelab', label: 'Explore the Architecture →' } : undefined,
|
||||
deepDive: index === 2 ? { url: '/homelab', label: 'Explore the Architecture →' }
|
||||
: index === 1 ? { url: '/poimen/memory', label: 'Explore the Memory System →' }
|
||||
: undefined,
|
||||
ciRepos: index === 0 ? [
|
||||
{ label: 'poimen-wf', repo: 'riotpiao-poimen/poimen-workflows', forgejoBase: 'https://forgejo.riotpiao.com/riotpiao-poimen/poimen-workflows' },
|
||||
] : index === 1 ? [
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { ArrowLeft } from 'lucide-react'
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
const skillCategories = [
|
||||
{
|
||||
title: 'Core Runtime',
|
||||
skills: ['Rust', 'actix-web', 'tokio', 'serde'],
|
||||
},
|
||||
{
|
||||
title: 'Storage & Search',
|
||||
skills: ['PostgreSQL (CloudNativePG)', 'pgvector (HNSW)', 'Cosine Similarity', 'BFS Graph Traversal'],
|
||||
},
|
||||
{
|
||||
title: 'LLM & Embeddings',
|
||||
skills: ['Ollama (ornith:35b)', 'TEI (nomic-embed)', '768-dim Embeddings'],
|
||||
},
|
||||
{
|
||||
title: 'Identity & Security',
|
||||
skills: ['Authentik OIDC', 'JWT Verification', 'SOPS Encrypted Secrets', 'RBAC'],
|
||||
},
|
||||
{
|
||||
title: 'Data Pipeline',
|
||||
skills: ['Fixed-Window Chunking', 'Entity Extraction', 'Fact Extraction', 'Reflection Filtering'],
|
||||
},
|
||||
{
|
||||
title: 'Infrastructure',
|
||||
skills: ['Kubernetes', 'ArgoCD', 'Kustomize', 'cert-manager'],
|
||||
},
|
||||
]
|
||||
|
||||
const sectionVariants = {
|
||||
hidden: { opacity: 0, y: 40 },
|
||||
visible: { opacity: 1, y: 0, transition: { duration: 0.6 } },
|
||||
}
|
||||
|
||||
export default function PoimenMemoryPage() {
|
||||
return (
|
||||
<main className="min-h-screen bg-white dark:bg-gray-950">
|
||||
<div className="max-w-6xl mx-auto px-6 py-20">
|
||||
<Link
|
||||
href="/#project-poimen-memory"
|
||||
className="inline-flex items-center gap-2 text-blue-600 dark:text-blue-400 hover:underline mb-8"
|
||||
>
|
||||
<ArrowLeft size={16} />
|
||||
Back to Projects
|
||||
</Link>
|
||||
|
||||
{/* Hero */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="mb-16"
|
||||
>
|
||||
<div className="text-sm font-semibold text-purple-600 dark:text-purple-400 uppercase tracking-wide mb-2">
|
||||
Poimen (Ποιμήν) · Memory System
|
||||
</div>
|
||||
<h1 className="text-5xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Teaching the Shepherd to Remember
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-400 mb-6">
|
||||
A Graph-RAG memory system that gives AI agents long-term recall, semantic search, and self-compacting knowledge.
|
||||
</p>
|
||||
<p className="text-lg text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||
Agents forget. Every conversation starts from zero unless you build memory into the system.
|
||||
Poimen Memory is a <strong>Rust-based Graph-RAG service</strong> that ingests conversations,
|
||||
extracts entities and relationships via LLM, stores them in a <strong>temporal knowledge graph</strong> backed
|
||||
by <strong>pgvector</strong>, and serves them back through <strong>three-tier retrieval</strong> — signature
|
||||
match at 50ms, graph-boosted hybrid search, and Obsidian fallback. The cache self-compacts:
|
||||
stale embeddings are evicted, frequently-accessed chunks are promoted, and the graph auto-reconciles
|
||||
on every write.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Section 1: Architecture Overview */}
|
||||
<motion.section
|
||||
variants={sectionVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
className="mb-16 border-l-4 border-purple-500 pl-8"
|
||||
>
|
||||
<div className="mb-6">
|
||||
<div className="text-sm font-semibold text-purple-600 dark:text-purple-400 uppercase tracking-wide mb-2">
|
||||
System Design
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
Architecture Overview
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="prose prose-lg dark:prose-invert max-w-none mb-6">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
The memory system runs as a <strong>Rust microservice</strong> inside the Kubernetes <code>poimen</code> namespace.
|
||||
An <strong>actix-web</strong> API server handles HTTP requests, authenticates via <strong>Authentik OIDC JWT</strong>,
|
||||
and routes to either the <strong>search path</strong> (direct pgvector query) or the <strong>ingest path</strong> (async
|
||||
pipeline through an internal queue → LLM extraction → graph persistence). All data lives in a
|
||||
<strong> CloudNativePG</strong> cluster with <strong>HNSW indexes</strong> for vector similarity.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Architecture Diagram */}
|
||||
<div className="mb-8">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-3">
|
||||
🏗️ System Architecture
|
||||
</h3>
|
||||
<div className="rounded-xl overflow-hidden border border-gray-200 dark:border-gray-700 shadow-lg">
|
||||
<iframe
|
||||
src="/poimen-memory/architecture.html"
|
||||
className="w-full bg-white dark:bg-gray-900"
|
||||
style={{ height: '700px', border: 'none' }}
|
||||
title="Poimen Memory Architecture"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
{/* Section 2: Ingest Pipeline */}
|
||||
<motion.section
|
||||
variants={sectionVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
className="mb-16 border-l-4 border-emerald-500 pl-8"
|
||||
>
|
||||
<div className="mb-6">
|
||||
<div className="text-sm font-semibold text-emerald-600 dark:text-emerald-400 uppercase tracking-wide mb-2">
|
||||
Data Flow
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
Ingest Pipeline
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="prose prose-lg dark:prose-invert max-w-none mb-6">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
Conversations arrive as <strong>episodes</strong> — raw message sequences. The pipeline splits into two
|
||||
parallel LLM extraction paths: <strong>entity extraction</strong> (people, tools, concepts with type + summary)
|
||||
and <strong>fact extraction</strong> (relationships between entity pairs as directed edges). A <strong>reflection
|
||||
pass</strong> filters hallucinated entities before persistence. The result is a <strong>temporal knowledge
|
||||
graph</strong> where every edge carries <code>t_valid</code> and <code>t_invalid</code> timestamps — knowledge
|
||||
that knows when it was true.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Dataflow Diagram */}
|
||||
<div className="mb-8">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-3">
|
||||
🔄 Ingest Data Flow
|
||||
</h3>
|
||||
<div className="rounded-xl overflow-hidden border border-gray-200 dark:border-gray-700 shadow-lg">
|
||||
<iframe
|
||||
src="/poimen-memory/dataflow.html"
|
||||
className="w-full bg-white dark:bg-gray-900"
|
||||
style={{ height: '700px', border: 'none' }}
|
||||
title="Poimen Ingest Pipeline"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
{/* Section 3: Request Lifecycle */}
|
||||
<motion.section
|
||||
variants={sectionVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
className="mb-16 border-l-4 border-cyan-500 pl-8"
|
||||
>
|
||||
<div className="mb-6">
|
||||
<div className="text-sm font-semibold text-cyan-600 dark:text-cyan-400 uppercase tracking-wide mb-2">
|
||||
Sequence
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
Ingest Request Lifecycle
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="prose prose-lg dark:prose-invert max-w-none mb-6">
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
The ingest path is <strong>fully async</strong>. The agent sends a <code>POST /memory/ingest</code> and
|
||||
gets a <strong>202 Accepted</strong> immediately — no blocking on LLM latency. A background worker
|
||||
polls jobs from the queue, runs two sequential LLM calls (entity extraction → fact extraction),
|
||||
cleans JSON responses of thinking tags and fences, then persists entities, edges, and embeddings
|
||||
to pgvector. The agent can query the graph within seconds of ingestion completing.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Sequence Diagram */}
|
||||
<div className="mb-8">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-3">
|
||||
⚡ Request Sequence
|
||||
</h3>
|
||||
<div className="rounded-xl overflow-hidden border border-gray-200 dark:border-gray-700 shadow-lg">
|
||||
<iframe
|
||||
src="/poimen-memory/sequence.html"
|
||||
className="w-full bg-white dark:bg-gray-900"
|
||||
style={{ height: '700px', border: 'none' }}
|
||||
title="Poimen Ingest Request Lifecycle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
{/* Section 4: Retrieval Strategy */}
|
||||
<motion.section
|
||||
variants={sectionVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
className="mb-16 border-l-4 border-orange-500 pl-8"
|
||||
>
|
||||
<div className="mb-6">
|
||||
<div className="text-sm font-semibold text-orange-600 dark:text-orange-400 uppercase tracking-wide mb-2">
|
||||
Search
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
Three-Tier Retrieval
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="prose prose-lg dark:prose-invert max-w-none">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 not-prose">
|
||||
<div className="bg-emerald-50 dark:bg-emerald-950/30 rounded-xl p-6 border border-emerald-200 dark:border-emerald-800">
|
||||
<div className="text-2xl mb-2">⚡</div>
|
||||
<h3 className="font-bold text-emerald-700 dark:text-emerald-400 mb-2">Tier 1: Signature Match</h3>
|
||||
<p className="text-sm text-gray-700 dark:text-gray-300">
|
||||
<strong>50ms</strong> exact entity lookup by name hash. Instant recall for known entities —
|
||||
no embedding computation needed.
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-cyan-50 dark:bg-cyan-950/30 rounded-xl p-6 border border-cyan-200 dark:border-cyan-800">
|
||||
<div className="text-2xl mb-2">🔍</div>
|
||||
<h3 className="font-bold text-cyan-700 dark:text-cyan-400 mb-2">Tier 2: Graph-Boosted Hybrid</h3>
|
||||
<p className="text-sm text-gray-700 dark:text-gray-300">
|
||||
<strong>HNSW cosine similarity</strong> + <strong>BFS graph traversal</strong>. RRF fusion ranks
|
||||
results across vector matches and graph neighbors. Context-aware retrieval that follows relationships.
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-orange-50 dark:bg-orange-950/30 rounded-xl p-6 border border-orange-200 dark:border-orange-800">
|
||||
<div className="text-2xl mb-2">📚</div>
|
||||
<h3 className="font-bold text-orange-700 dark:text-orange-400 mb-2">Tier 3: Obsidian Fallback</h3>
|
||||
<p className="text-sm text-gray-700 dark:text-gray-300">
|
||||
When graph search yields low confidence, falls back to <strong>wiki-link indexed</strong> Obsidian
|
||||
vault. Bidirectional link traversal surfaces related notes the graph hasn't captured yet.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
{/* Skills Grid */}
|
||||
<motion.section
|
||||
variants={sectionVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
className="mb-16"
|
||||
>
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-8">
|
||||
Technology Stack
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{skillCategories.map((category) => (
|
||||
<div
|
||||
key={category.title}
|
||||
className="bg-gray-50 dark:bg-gray-900 rounded-xl p-6 border border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
<h3 className="text-base font-bold text-gray-900 dark:text-white mb-3">
|
||||
{category.title}
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{category.skills.map((skill) => (
|
||||
<span
|
||||
key={skill}
|
||||
className="text-sm px-3 py-1.5 rounded-full bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.section>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
+10
-2
@@ -164,7 +164,11 @@
|
||||
"Graph-RAG with Wiki-Link Indexing (Rust, pgvector, OpenSearch): Built bidirectional link graph from [[wiki-link]] syntax during ingestion. PageRank-style score propagation boosts linked documents' relevance. RRF fusion merges HNSW cosine (pgvector) + BM25 lexical (OpenSearch). WikiScopedFilter constrains traversal to project boundaries.",
|
||||
"Three-Tier Context Retrieval (Actix-web, tokio): Async pipeline — Tier 1: MD5 signature match (<50ms), Tier 2: graph-boosted hybrid search with link-distance decay, Tier 3: Obsidian API fallback. Budget-aware assembly drops lower tiers first. Shingle-based Jaccard deduplication (>0.5) prevents redundant chunks.",
|
||||
"Hierarchical RBAC (Authentik OIDC, JWT, Kubernetes): Role → AccessRule[] → AccessScope model with project/visibility/owner/group constraints. JWT roles claim maps to YAML rules; AccessGuard.filter_resources() applies post-retrieval filtering. Dual-write indexer (eventual consistency via queue) maintains RBAC-aware views. SOPS/age encryption, ArgoCD deployment."
|
||||
]
|
||||
],
|
||||
"deepDive": {
|
||||
"label": "Explore the Memory System →",
|
||||
"url": "/poimen/memory"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Homelab: Production-Grade Kubernetes Platform",
|
||||
@@ -437,7 +441,11 @@
|
||||
"图-RAG维基链接索引化(Rust、pgvector、OpenSearch):从[[维基链接]]语法构建双向链接图。PageRank风格评分传播提升链接文档的相关性。RRF融合合并HNSW余弦相似度(pgvector)+ BM25词汇排名(OpenSearch)。WikiScopedFilter将遍历限制在项目边界内。",
|
||||
"三层上下文检索(Actix-web, tokio):异步管道——第1层:MD5签名匹配(<50ms),第2层:图增强混合搜索含链接距离衰减,第3层:Obsidian API兜底。预算感知的响应组装优先丢弃低优先层。基于瓦片的Jaccard去重(>0.5)防止冗余块。",
|
||||
"分层RBAC(Authentik OIDC、JWT、Kubernetes):角色→AccessRule[]→AccessScope模型,包含项目/可见性/所有者/组约束。JWT角色声明映射到YAML规则;AccessGuard.filter_resources()应用检索后过滤。双写索引器(通过队列保证最终一致性)维护RBAC感知视图。SOPS/age加密,ArgoCD部署。"
|
||||
]
|
||||
],
|
||||
"deepDive": {
|
||||
"label": "探索记忆系统 →",
|
||||
"url": "/poimen/memory"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "家庭实验室:生产级 Kubernetes 平台",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"diagram_type": "architecture",
|
||||
"meta": {
|
||||
"title": "Poimen Memory System",
|
||||
"quality_profile": "showcase",
|
||||
"viewBox": [1060, 560]
|
||||
},
|
||||
"components": [
|
||||
{ "id": "agent", "type": "external", "label": "AI Agent", "sublabel": "LLM client", "pos": [40, 220], "size": [130, 60] },
|
||||
{ "id": "api", "type": "backend", "label": "Memory API", "sublabel": "actix-web :8080", "pos": [270, 220], "size": [140, 60], "tag": "Rust" },
|
||||
{ "id": "auth", "type": "security", "label": "Authentik", "sublabel": "OIDC / JWT", "pos": [270, 60], "size": [140, 60] },
|
||||
{ "id": "worker", "type": "backend", "label": "Ingest Worker", "sublabel": "LLM pipeline", "pos": [540, 220], "size": [140, 60] },
|
||||
{ "id": "llm", "type": "external", "label": "LLM", "sublabel": "ornith:35b", "pos": [540, 380], "size": [140, 60] },
|
||||
{ "id": "pgvector", "type": "database", "label": "pgvector", "sublabel": "CNPG cluster", "pos": [540, 60], "size": [140, 60], "tag": "HNSW" },
|
||||
{ "id": "embed", "type": "external", "label": "Embeddings", "sublabel": "nomic-embed", "pos": [810, 220], "size": [140, 60] }
|
||||
],
|
||||
"boundaries": [
|
||||
{ "kind": "region", "label": "K8s: poimen", "wraps": ["api", "auth", "worker", "pgvector", "embed"] }
|
||||
],
|
||||
"connections": [
|
||||
{ "id": "c1", "from": "agent", "to": "api", "label": "HTTP", "variant": "emphasis" },
|
||||
{ "id": "c2", "from": "api", "to": "auth", "label": "verify JWT", "variant": "security" },
|
||||
{ "id": "c3", "from": "api", "to": "worker", "label": "enqueue", "labelAt": [445, 178] },
|
||||
{ "id": "c4", "from": "worker", "to": "llm", "label": "extract", "labelAt": [630, 356] },
|
||||
{ "id": "c5", "from": "worker", "to": "pgvector", "label": "persist" },
|
||||
{ "id": "c6", "from": "worker", "to": "embed", "label": "embed", "variant": "dashed" },
|
||||
{ "id": "c7", "from": "api", "to": "pgvector", "label": "search", "variant": "emphasis" }
|
||||
],
|
||||
"cards": [
|
||||
{ "dot": "emerald", "title": "Ingest", "items": ["Conversations ingested via HTTP", "LLM extracts entities + relationships", "Temporal graph persisted to pgvector"] },
|
||||
{ "dot": "cyan", "title": "Retrieval", "items": ["HNSW cosine similarity search", "BFS graph traversal for context"] },
|
||||
{ "dot": "rose", "title": "Auth", "items": ["Authentik OIDC JWT verification", "SOPS-encrypted K8s secrets"] }
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"diagram_type": "dataflow",
|
||||
"meta": {
|
||||
"title": "Poimen Ingest Pipeline",
|
||||
"quality_profile": "showcase",
|
||||
"viewBox": [1020, 540]
|
||||
},
|
||||
"stages": [
|
||||
{ "label": "Input" },
|
||||
{ "label": "Extract" },
|
||||
{ "label": "Store" },
|
||||
{ "label": "Serve" }
|
||||
],
|
||||
"nodes": [
|
||||
{ "id": "episode", "type": "external", "label": "Episode", "sublabel": "conversation text", "stage": 0, "row": 1, "tag": "messages" },
|
||||
{ "id": "entities", "type": "backend", "label": "Entity Extractor", "sublabel": "LLM + reflection", "stage": 1, "row": 0, "tag": "person / tool" },
|
||||
{ "id": "facts", "type": "backend", "label": "Fact Extractor", "sublabel": "LLM relationships", "stage": 1, "row": 2, "tag": "edges" },
|
||||
{ "id": "graph", "type": "database", "label": "Temporal Graph", "sublabel": "pgvector", "stage": 2, "row": 1, "tag": "HNSW" },
|
||||
{ "id": "query", "type": "backend", "label": "Query API", "sublabel": "hybrid search", "stage": 3, "row": 0, "tag": "BFS + cosine" },
|
||||
{ "id": "viz", "type": "frontend", "label": "Visualization", "sublabel": "React Flow", "stage": 3, "row": 2, "tag": "graph UI" }
|
||||
],
|
||||
"flows": [
|
||||
{ "id": "f1", "from": "episode", "to": "entities", "label": "text", "classification": "ingest", "variant": "emphasis" },
|
||||
{ "id": "f2", "from": "episode", "to": "facts", "label": "text", "classification": "ingest", "variant": "default" },
|
||||
{ "id": "f3", "from": "entities", "to": "graph", "label": "persist nodes", "classification": "write", "variant": "emphasis" },
|
||||
{ "id": "f4", "from": "facts", "to": "graph", "label": "persist edges", "classification": "write", "variant": "emphasis" },
|
||||
{ "id": "f5", "from": "graph", "to": "query", "label": "search", "classification": "read", "variant": "emphasis" },
|
||||
{ "id": "f6", "from": "graph", "to": "viz", "label": "graph data", "classification": "read", "variant": "dashed" }
|
||||
],
|
||||
"cards": [
|
||||
{ "dot": "emerald", "title": "Extraction", "items": ["LLM extracts entities with type + summary", "Second LLM call extracts edges between entities", "Reflection filters hallucinated entities"] },
|
||||
{ "dot": "cyan", "title": "Storage", "items": ["Temporal graph with bi-temporal edges", "768-dim HNSW embeddings for similarity"] },
|
||||
{ "dot": "orange", "title": "Retrieval", "items": ["BFS traversal + cosine similarity", "React Flow JSON for interactive graph"] }
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"diagram_type": "sequence",
|
||||
"meta": {
|
||||
"title": "Poimen Ingest Request Lifecycle",
|
||||
"quality_profile": "showcase",
|
||||
"viewBox": [1020, 620],
|
||||
"column_fit": "spread"
|
||||
},
|
||||
"participants": [
|
||||
{ "id": "agent", "type": "external", "label": "AI Agent", "sublabel": "client" },
|
||||
{ "id": "api", "type": "backend", "label": "Memory API", "sublabel": "actix-web" },
|
||||
{ "id": "queue", "type": "messagebus", "label": "Queue", "sublabel": "ingest jobs" },
|
||||
{ "id": "worker", "type": "backend", "label": "Worker", "sublabel": "pipeline" },
|
||||
{ "id": "llm", "type": "external", "label": "LLM", "sublabel": "Ollama" },
|
||||
{ "id": "db", "type": "database", "label": "pgvector", "sublabel": "postgres" }
|
||||
],
|
||||
"segments": [
|
||||
{ "from": 150, "to": 240, "label": "Ingest" },
|
||||
{ "from": 250, "to": 440, "label": "Extraction" },
|
||||
{ "from": 450, "to": 560, "label": "Persist" }
|
||||
],
|
||||
"messages": [
|
||||
{ "id": "ingest-req", "from": "agent", "to": "api", "y": 160, "label": "POST /memory/ingest", "variant": "emphasis" },
|
||||
{ "id": "enqueue", "from": "api", "to": "queue", "y": 185, "label": "enqueue job", "variant": "default" },
|
||||
{ "id": "accept", "from": "api", "to": "agent", "y": 210, "label": "202 pending", "variant": "return" },
|
||||
{ "id": "poll", "from": "worker", "to": "queue", "y": 258, "label": "poll job", "variant": "default" },
|
||||
{ "id": "job", "from": "queue", "to": "worker", "y": 290, "label": "episode records", "variant": "return" },
|
||||
{ "id": "extract-entities", "from": "worker", "to": "llm", "y": 315, "label": "extract entities", "variant": "emphasis" },
|
||||
{ "id": "entities-resp", "from": "llm", "to": "worker", "y": 345, "label": "JSON entities", "variant": "return" },
|
||||
{ "id": "extract-facts", "from": "worker", "to": "llm", "y": 375, "label": "extract facts (entity pairs)", "variant": "emphasis" },
|
||||
{ "id": "facts-resp", "from": "llm", "to": "worker", "y": 405, "label": "JSON edges", "variant": "return" },
|
||||
{ "id": "save-entities", "from": "worker", "to": "db", "y": 460, "label": "INSERT memory_entity", "variant": "default" },
|
||||
{ "id": "save-edges", "from": "worker", "to": "db", "y": 490, "label": "INSERT memory_edge", "variant": "default" },
|
||||
{ "id": "embed", "from": "worker", "to": "db", "y": 520, "label": "store embeddings", "variant": "dashed" },
|
||||
{ "id": "done", "from": "worker", "to": "queue", "y": 535, "label": "mark done", "variant": "return" }
|
||||
],
|
||||
"activations": [
|
||||
{ "participant": "api", "from": 155, "to": 220, "type": "backend" },
|
||||
{ "participant": "queue", "from": 180, "to": 295, "type": "messagebus" },
|
||||
{ "participant": "worker", "from": 255, "to": 555, "type": "backend" },
|
||||
{ "participant": "llm", "from": 310, "to": 350, "type": "external" },
|
||||
{ "participant": "llm", "from": 370, "to": 410, "type": "external" },
|
||||
{ "participant": "db", "from": 455, "to": 530, "type": "database" }
|
||||
],
|
||||
"cards": [
|
||||
{ "dot": "emerald", "title": "Async Ingest", "items": ["Agent gets 202 immediately, no blocking", "Worker polls jobs from queue independently", "Decoupled ingest from extraction latency"] },
|
||||
{ "dot": "cyan", "title": "LLM Extraction", "items": ["First call: extract named entities with types", "Second call: extract relationships between entity pairs", "JSON response cleaned of thinking tags and fences"] },
|
||||
{ "dot": "orange", "title": "Persistence", "items": ["Entities saved with type, summary, confidence", "Edges saved with temporal fields (t_valid, t_invalid)", "Embeddings stored for vector similarity search"] }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user