| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <title>The Agentic Symphony β Architecture Map</title> |
| <link rel="stylesheet" href="style.css"> |
| <script src="https://d3js.org/d3.v7.min.js"></script> |
| </head> |
| <body> |
|
|
| <nav> |
| <div class="brand"><span>♫</span> The Agentic Symphony</div> |
| <div class="links"> |
| <a href="#diagram" class="active">Diagram</a> |
| <a href="#analysis">Analysis</a> |
| <a href="#value-routes">Value Routes</a> |
| <a href="#layers">Layers</a> |
| <a href="https://github.com/danielrosehill/agentic-ai-architecture-map" target="_blank">GitHub</a> |
| </div> |
| </nav> |
|
|
| <div class="hero"> |
| <h1>The <span>Agentic</span> Symphony</h1> |
| <p>An interactive map of all the moving pieces in a production agentic AI system.</p> |
| <div class="meta">v<strong id="version">2.1.0</strong> · <strong id="node-count">38</strong> nodes · <strong id="conn-count">39</strong> connections · <strong>14</strong> layers</div> |
| </div> |
|
|
| |
| <div class="stats-banner" id="stats-banner"></div> |
|
|
| |
| <div class="diagram-wrap" id="diagram"> |
| <div class="diagram-toolbar"> |
| <button data-action="zoom-in" title="Zoom in">+</button> |
| <button data-action="zoom-out" title="Zoom out">−</button> |
| <button data-action="reset" title="Reset">1:1</button> |
| <button data-action="fullscreen" class="fullscreen-btn" title="Fullscreen">⛶ Fullscreen</button> |
| </div> |
| <div class="diagram-container" id="diagram-container"></div> |
| </div> |
|
|
| |
| <div class="export-bar"> |
| <button onclick="exportPNG()">📷 Export PNG</button> |
| <button onclick="exportPNG(4)">📷 Export PNG (4x Hi-Res)</button> |
| <button onclick="exportSVG()">📄 Export SVG</button> |
| <a class="btn" href="https://github.com/danielrosehill/agentic-ai-architecture-map" target="_blank">🔗 View Data Model on GitHub</a> |
| </div> |
|
|
| |
| <div class="page-section" id="analysis"> |
| <h2>Connection Analysis</h2> |
| <div class="tab-nav"> |
| <button class="tab-btn active" data-tab="all-connections">All Connections</button> |
| <button class="tab-btn" data-tab="by-pattern">By Invocation Pattern</button> |
| <button class="tab-btn" data-tab="key-insights">Key Insights</button> |
| </div> |
|
|
| <div class="tab-panel active" id="tab-all-connections"> |
| <div class="conn-grid" id="conn-grid"></div> |
| </div> |
|
|
| <div class="tab-panel" id="tab-by-pattern"> |
| <h3>Autonomous</h3> |
| <p>Agent-driven tool invocation β the agent intelligently chooses which tools to call and when.</p> |
| <div class="conn-grid" id="conn-autonomous"></div> |
|
|
| <h3>Deterministic</h3> |
| <p>Developer-wired tool invocation β tools are pre-configured at design time, not selected by the model.</p> |
| <div class="conn-grid" id="conn-deterministic"></div> |
|
|
| <h3>Bidirectional</h3> |
| <p>Two-way data flow β the connection carries data in both directions.</p> |
| <div class="conn-grid" id="conn-bidirectional"></div> |
|
|
| <h3>Transport</h3> |
| <p>Data flows through a pipeline β post-processed and indexed by a separate system, not the agent.</p> |
| <div class="conn-grid" id="conn-transport"></div> |
| </div> |
|
|
| <div class="tab-panel" id="tab-key-insights"> |
| <h3>The Agentic Loop</h3> |
| <p>The most important insight hidden by linear diagrams: agent execution is <strong>iterative</strong>. After each tool result, the agent returns to the model for another reasoning step. The loop (reason → act → observe → reason) may execute many times per request. This loop is the defining characteristic that separates agentic from automated systems.</p> |
|
|
| <h3>Autonomous vs. Deterministic Tool Invocation</h3> |
| <p>The connection from the agent layer to tools is <strong>not uniform</strong>. Agents autonomously choose tools (non-deterministic). Pipelines and workflows use tools that are wired in at design time (deterministic). This is arguably the defining line between “agentic” and “automated” AI systems.</p> |
|
|
| <h3>Memory is Not Monolithic</h3> |
| <p>The architecture captures four distinct memory patterns:</p> |
| <ul> |
| <li><strong>Mined memory</strong> — outputs post-processed into RAG by a separate pipeline (memory as transport layer)</li> |
| <li><strong>Ad-hoc memory</strong> — agent actively writes and reads memory artifacts (e.g. Claude Code’s MEMORY.md)</li> |
| <li><strong>Implicit memory</strong> — conversation history in the context window (session-scoped)</li> |
| <li><strong>Structured memory</strong> — facts in knowledge graphs or key-value stores</li> |
| </ul> |
|
|
| <h3>Gateway Routing Pattern</h3> |
| <p>In production, inference requests often route through a <strong>model gateway</strong> (OpenRouter, LiteLLM) before reaching the model endpoint. The gateway handles model selection, load balancing, failover, rate limiting, and cost tracking. A self-hosted LiteLLM instance can unify access to both cloud APIs and local model servers.</p> |
|
|
| <h3>Safety Applied at Multiple Points</h3> |
| <p>Safety guardrails don’t just constrain agents — they apply at <strong>three points</strong>: input filtering on prompts (injection detection), agent behavior constraints, and tool-level restrictions on MCP (preventing unauthorized actions).</p> |
|
|
| <h3>HITL Varies by Agent Type</h3> |
| <p>Human-in-the-loop patterns differ: <strong>per-action approval</strong> for agents (human approves each tool call), <strong>stage-gate</strong> for pipelines (review output before next stage), and <strong>exception-based</strong> for workflows (human notified only on low confidence).</p> |
| </div> |
| </div> |
|
|
| |
| <div class="page-section" id="value-routes"> |
| <h2>Overlooked Value Routes</h2> |
| <p>High-value data pathways that are simple but deliver outsized returns — typically invisible in architecture diagrams.</p> |
|
|
| <div class="value-route"> |
| <h4>1. Prompts → Prompt Library</h4> |
| <p>Good prompts are discovered through use, not designed upfront. Capturing and curating prompts that work well in production creates reusable institutional knowledge — “this is how we talk to models about X.”</p> |
| <pre>User/System Prompts → Stored Prompts → Prompt Library</pre> |
| <p>New agents and workflows can bootstrap from proven prompts instead of starting from scratch. It’s a form of organizational memory.</p> |
| </div> |
|
|
| <div class="value-route"> |
| <h4>2. Conversation-Driven Context Enrichment</h4> |
| <p>Conversations themselves are mined for context <em>about the user</em> — preferences, patterns, domain knowledge. A dedicated agentic workflow extracts, categorizes, and indexes this into the vector store.</p> |
| <pre>User Prompt → Conversations (DB) → [Mining Workflow] → Vector Store → Context (RAG) → Future Prompts</pre> |
| <p>This creates a second virtuous cycle distinct from generic context-mining:</p> |
| <ul> |
| <li><strong>General loop</strong>: agents → storage → context-store → agents (output-driven)</li> |
| <li><strong>User context loop</strong>: prompts → DB → mining workflow → vector DB → prompts (conversation-driven, per-user)</li> |
| </ul> |
| <p>The mining step is itself agentic — it uses LLMs to extract structured insights, not just raw embeddings.</p> |
| </div> |
|
|
| <div class="value-route"> |
| <h4>3. Outputs → Knowledge Management</h4> |
| <p>Every agent interaction potentially generates organizational knowledge. Without this route, that knowledge is trapped in individual conversation threads and lost. Agent outputs (research summaries, analyses, recommendations) flow to wikis and knowledge bases for organizational use.</p> |
| </div> |
| </div> |
|
|
| |
| <div class="page-section" id="layers"> |
| <h2>Architecture Layers</h2> |
| <div class="layer-grid" id="layer-grid"></div> |
| </div> |
|
|
| <footer> |
| <strong>The Agentic Symphony</strong> · v2.1.0 · |
| By <a href="https://danielrosehill.com" target="_blank">Daniel Rosehill</a> / Carrot Cake AI · |
| <a href="https://github.com/danielrosehill/agentic-ai-architecture-map" target="_blank">GitHub</a> · |
| MIT License |
| </footer> |
|
|
| <script> |
| |
| |
| |
| const ARCH = {"meta":{"title":"Agentic AI Architecture Map","version":"2.1.0"},"layers":[{"id":"prompts","label":"Prompts","position":"center","order":1,"color":"#4a6fa5","description":"The instruction layer \u2014 where human intent enters the system.","nodes":[{"id":"user-prompt","label":"User Prompt","description":"The actual question or instruction from the end user."},{"id":"system-prompt","label":"System Prompt","description":"Developer-defined instructions that shape the model\u2019s behavior, persona, and constraints. Also the injection point for RAG context."},{"id":"vendor-prompt","label":"Vendor Prompt","description":"Hidden instructions baked in by the provider \u2014 RLHF alignment, safety guardrails. The model is never truly vanilla."}]},{"id":"models","label":"Models","position":"center","order":2,"color":"#8b5cf6","description":"Foundation models that power reasoning and generation.","nodes":[{"id":"commercial-models","label":"Commercial","description":"Proprietary frontier models \u2014 Claude, GPT, Gemini, Cohere."},{"id":"open-source-models","label":"Open Source","description":"Open-weight models \u2014 Llama, Mistral, Qwen, DeepSeek, Phi."},{"id":"fine-tuned-models","label":"Fine-Tuned","description":"Task-specific models fine-tuned on your data."},{"id":"embedding-models","label":"Embedding","description":"Embedding models for vector representations \u2014 power the vector store and RAG, not agent reasoning."}]},{"id":"inference","label":"Inference","position":"center","order":3,"color":"#6366f1","description":"Where and how models are served \u2014 the compute layer. Gateway is optional: requests can route through a gateway or call endpoints directly.","nodes":[{"id":"model-gateway","label":"Gateway","description":"Model gateway/router \u2014 handles model selection, load balancing, failover, rate limiting, cost tracking. OpenRouter (hosted) or LiteLLM (self-hosted). Optional \u2014 agents can call endpoints directly."},{"id":"cloud-apis","label":"Cloud APIs","description":"Hosted API endpoints \u2014 Anthropic, OpenAI, Google, AWS Bedrock. Reachable via gateway or directly."},{"id":"self-hosted","label":"Self-Hosted","description":"Your own servers \u2014 vLLM, TGI, Ollama. Reachable via gateway or directly."},{"id":"on-prem","label":"On-Prem","description":"Air-gapped data center deployments."},{"id":"edge","label":"Edge","description":"Lightweight models on devices \u2014 low latency, offline."},{"id":"prompt-cache","label":"Cache","description":"Prompt/response caching \u2014 semantic dedup, cost reduction, latency improvement."}]},{"id":"context-store","label":"Context Store","position":"center","order":4,"color":"#2563eb","style":"dashed","description":"Knowledge layer \u2014 RAG and persistent memory.","nodes":[{"id":"context-rag","label":"Context (RAG)","description":"Retrieval-Augmented Generation \u2014 grounding AI in your documents."},{"id":"memory","label":"Memory","description":"Multiple forms: mined, ad-hoc, implicit, structured."},{"id":"vector-store","label":"Vector Store","description":"Embedding-based semantic search indexes."}]},{"id":"agents","label":"Agents","position":"center","order":5,"color":"#e85d26","description":"Orchestration and execution \u2014 where AI systems reason, plan, and act.","nodes":[{"id":"orchestration","label":"Orchestration","description":"Routes to agents, pipelines, or workflows. Handles failures and state."},{"id":"agents-node","label":"Agents","description":"Autonomous systems that reason, plan, act. Tool selection is intelligent and non-deterministic. Execution is a loop."},{"id":"pipelines","label":"Pipelines","description":"Multi-step chains. Tools wired at design time \u2014 no intelligent selection."},{"id":"workflows","label":"Workflows","description":"Event-triggered sequences. Tools pre-configured at each node."}]},{"id":"mcp","label":"MCP","position":"center","order":6,"color":"#f5a623","description":"Model Context Protocol \u2014 the open standard for connecting AI to tools and data.","nodes":[{"id":"mcp-protocol","label":"Model Context Protocol","description":"The open standard for connecting AI models to external tools, services, and data sources."},{"id":"tool-registry","label":"Tool Registry","description":"Discovery mechanism for available tools \u2014 in MCP this is tools/list."}]},{"id":"hitl","label":"Human-in-the-Loop","position":"center","order":7,"color":"#b91c1c","description":"Approval checkpoints \u2014 the brakes on the system.","style":"checkpoint","nodes":[{"id":"hitl-checkpoint","label":"Human-in-the-Loop","description":"Humans review and authorize agent actions before execution."}]},{"id":"actions","label":"Actions","position":"center","order":8,"color":"#2563eb","style":"dashed","description":"External systems agents act on \u2014 bidirectional: agents read and write.","nodes":[{"id":"your-data","label":"Your Data","description":"Salesforce, HubSpot, Google Drive, Notion \u2014 via MCP."},{"id":"external-services","label":"External Services","description":"Weather, time, public datasets."},{"id":"digital-wallets","label":"Digital Wallets","description":"Payment and financial actions \u2014 Stripe, crypto wallets, bank APIs."}]},{"id":"storage","label":"Storage","position":"left","order":9,"color":"#14b8a6","description":"Persistence \u2014 conversations, outputs, prompts.","nodes":[{"id":"conversations","label":"Conversations","description":"Persisted threads for auditing, replay, and context mining."},{"id":"outputs","label":"Outputs","description":"Generated text, structured data, files, artifacts."},{"id":"stored-prompts","label":"Prompts","description":"Prompt templates and versioned prompt libraries."},{"id":"postgres","label":"Postgres","description":"Production relational databases."},{"id":"data-lakes","label":"Data Lakes","description":"Large-scale unstructured storage \u2014 logs, documents, embeddings."}]},{"id":"frontends","label":"Frontends","position":"left","order":2,"color":"#4a6fa5","description":"User-facing interfaces.","nodes":[{"id":"frontends-card","label":"Frontends","description":"Chat windows, bots, web UIs, dashboards."}],"sub_items":["Chatbots","Slack","Telegram","Web UIs","Dashboards"]},{"id":"grounding","label":"Grounding","position":"left","order":5,"color":"#0891b2","description":"Real-time external data sources that ground agent responses in current information.","nodes":[{"id":"grounding-card","label":"Grounding","description":"External data sources that ground agent responses in current, real-world information."}],"sub_items":["Web Search","News Feeds","Real-Time Data"]},{"id":"safety","label":"Safety","position":"right","order":2,"color":"#b91c1c","description":"Constraints at multiple points: input, agent, tool-level.","nodes":[{"id":"safety-card","label":"Safety","description":"Guardrails and oversight mechanisms."}],"sub_items":["Guardrails","Security Harnesses","Input Filtering"]},{"id":"observability","label":"Observability","position":"right","order":3,"color":"#065f46","description":"Eval, logging, monitoring. Feeds back into prompt optimization.","nodes":[{"id":"observability-card","label":"Observability","description":"Tracking decisions, quality, actions, health."}],"sub_items":["Eval","Logging","Monitoring"]},{"id":"destinations","label":"Destinations","position":"left","order":9,"color":"#7c3aed","description":"Downstream consumers of stored outputs.","nodes":[{"id":"prompt-library","label":"Prompt Library","description":"Versioned, curated prompt templates for reuse."},{"id":"wiki-km","label":"Wiki / KM","description":"Organizational knowledge management."},{"id":"data-warehouse","label":"Data Warehouse","description":"Analytics, compliance, long-term retention."}]}],"connections":[{"id":"input-flow","from":"frontends-card","to":"user-prompt","label":"INPUT","style":"dashed","color":"#4a6fa5","description":"User requests flow from frontend to prompt layer."},{"id":"prompts-to-models","from":"prompts","to":"models","color":"#4a6fa5","description":"Assembled prompts (user + system + vendor + RAG context + tool defs) sent to model."},{"id":"models-to-inference","from":"models","to":"inference","color":"#8b5cf6","description":"Selected model served through chosen inference path."},{"id":"gateway-to-cloud-apis","from":"model-gateway","to":"cloud-apis","color":"#6366f1","description":"Gateway routes to cloud APIs based on model selection, cost, latency, or fallback rules."},{"id":"gateway-to-self-hosted","from":"model-gateway","to":"self-hosted","color":"#6366f1","description":"Gateway routes to self-hosted endpoints. LiteLLM unifies local and cloud access."},{"id":"direct-to-cloud-apis","from":"models","to":"cloud-apis","color":"#6366f1","style":"dashed","label":"DIRECT","description":"Direct inference path bypassing the gateway \u2014 agents call cloud API endpoints directly."},{"id":"direct-to-self-hosted","from":"models","to":"self-hosted","color":"#6366f1","style":"dashed","label":"DIRECT","description":"Direct inference path bypassing the gateway \u2014 agents call self-hosted endpoints directly."},{"id":"inference-to-agents","from":"inference","to":"agents","color":"#6366f1","description":"Inference results feed into the agent orchestration layer."},{"id":"orchestration-to-agents","from":"orchestration","to":"agents-node","style":"dashed","color":"#e85d26","description":"Dispatches to agents for intelligent, flexible execution."},{"id":"orchestration-to-pipelines","from":"orchestration","to":"pipelines","style":"dashed","color":"#e85d26","description":"Dispatches to pipelines when steps are known and fixed."},{"id":"orchestration-to-workflows","from":"orchestration","to":"workflows","style":"dashed","color":"#e85d26","description":"Dispatches to workflows for event-triggered execution."},{"id":"agentic-loop","from":"agents-node","to":"inference","style":"dashed","color":"#e85d26","label":"AGENTIC LOOP","description":"Iterative execution: after each tool result, agent returns to model. May loop many times per request.","invocation_pattern":"autonomous"},{"id":"agents-autonomous-to-mcp","from":"agents-node","to":"mcp","color":"#e85d26","label":"AUTONOMOUS","description":"Agents autonomously invoke tools \u2014 intelligent, non-deterministic selection.","invocation_pattern":"autonomous"},{"id":"pipelines-deterministic-to-mcp","from":"pipelines","to":"mcp","color":"#e85d26","style":"solid","label":"DETERMINISTIC","description":"Tools wired at design time. Developer decides sequence.","invocation_pattern":"deterministic"},{"id":"workflows-deterministic-to-mcp","from":"workflows","to":"mcp","color":"#e85d26","style":"solid","label":"DETERMINISTIC","description":"Tools pre-configured per workflow node, event-triggered.","invocation_pattern":"deterministic"},{"id":"mcp-to-hitl","from":"mcp","to":"hitl","color":"#f5a623","label":"TAKING ACTIONS","style":"solid","no_arrow":true,"description":"Actions pass through human approval before execution."},{"id":"hitl-to-actions","from":"hitl","to":"actions","color":"#b91c1c","description":"Approved actions proceed to external systems."},{"id":"actions-data-retrieval","from":"actions","to":"mcp-protocol","style":"dashed","color":"#2563eb","label":"DATA RETRIEVAL","description":"Data retrieved from external systems flows back through MCP. Actions are bidirectional.","invocation_pattern":"bidirectional"},{"id":"agents-to-storage","from":"agents","to":"storage","style":"dashed","color":"#14b8a6","description":"Agents persist conversations, outputs, and prompt data."},{"id":"storage-to-context","from":"storage","to":"context-store","style":"dashed","color":"#2563eb","label":"CONTEXT-MINING","description":"Mined memory pattern \u2014 outputs post-processed into retrievable context.","invocation_pattern":"transport"},{"id":"context-to-agents","from":"context-store","to":"agents","style":"dashed","color":"#2563eb","description":"Context feeds back into agents \u2014 the virtuous knowledge cycle."},{"id":"agents-adhoc-memory","from":"agents-node","to":"memory","style":"dashed","color":"#2563eb","label":"AD-HOC MEMORY","description":"Agent directly writes/reads memory artifacts. Agent decides what to remember.","invocation_pattern":"bidirectional"},{"id":"grounding-to-agents","from":"grounding-card","to":"agents","color":"#0891b2","style":"dashed","label":"GROUNDING","description":"Real-time external data \u2014 web search, news feeds, live APIs \u2014 grounds agent responses in current information."},{"id":"output-flow","from":"agents-node","to":"frontends-card","label":"OUTPUT","style":"dashed","color":"#e85d26","description":"Agent responses delivered back to user through frontend."},{"id":"safety-to-agents","from":"safety-card","to":"agents","color":"#b91c1c","description":"Safety guardrails constrain agent behavior."},{"id":"safety-to-prompts","from":"safety-card","to":"prompts","color":"#b91c1c","style":"dashed","description":"Input guardrails \u2014 injection detection, content filtering."},{"id":"safety-to-mcp","from":"safety-card","to":"mcp-protocol","color":"#b91c1c","style":"dashed","description":"Tool-level safety \u2014 restricting tool access, parameter validation."},{"id":"observability-to-agents","from":"observability-card","to":"agents","color":"#065f46","description":"Monitoring and logging of agent execution."},{"id":"observability-to-storage","from":"observability-card","to":"data-lakes","style":"dashed","color":"#065f46","description":"Traces, logs, metrics persisted for analysis."},{"id":"observability-to-prompts","from":"observability-card","to":"stored-prompts","style":"dashed","color":"#065f46","label":"PROMPT OPTIMIZATION","description":"Eval results drive prompt iteration and versioning."},{"id":"frontends-to-observability","from":"frontends-card","to":"observability-card","style":"dashed","color":"#4a6fa5","description":"User feedback and telemetry flow to observability."},{"id":"context-rag-to-system-prompt","from":"context-rag","to":"system-prompt","style":"dashed","color":"#2563eb","label":"CONTEXT INJECTION","description":"RAG results injected into system prompt."},{"id":"embedding-models-to-vector-store","from":"embedding-models","to":"vector-store","style":"dashed","color":"#8b5cf6","description":"Embedding models generate vectors for semantic search."},{"id":"stored-prompts-to-prompt-library","from":"stored-prompts","to":"prompt-library","style":"dashed","color":"#4a6fa5","description":"Curated prompts become a versioned library."},{"id":"outputs-to-wiki","from":"outputs","to":"wiki-km","style":"dashed","color":"#7c3aed","description":"Agent outputs published to knowledge management."},{"id":"outputs-to-data-warehouse","from":"outputs","to":"data-warehouse","style":"dashed","color":"#7c3aed","description":"Outputs flow to data warehouse for analytics and compliance."},{"id":"postgres-to-context-store","from":"postgres","to":"context-rag","style":"dashed","color":"#14b8a6","description":"DB records indexed into RAG pipeline."},{"id":"user-prompt-to-conversations","from":"user-prompt","to":"conversations","style":"dashed","color":"#4a6fa5","description":"Prompts persisted as conversations for context mining."},{"id":"conversations-to-postgres","from":"conversations","to":"postgres","style":"dashed","color":"#14b8a6","label":"USER CONTEXT MINING","description":"Conversations mined for user context \u2014 preferences, patterns, domain knowledge."}]}; |
| |
| |
| |
| |
| function getAllNodeIds() { |
| const ids = new Set(); |
| ARCH.layers.forEach(l => { ids.add(l.id); l.nodes.forEach(n => ids.add(n.id)); }); |
| return ids; |
| } |
| |
| function getNodeLabel(id) { |
| for (const l of ARCH.layers) { |
| if (l.id === id) return l.label; |
| for (const n of l.nodes) { if (n.id === id) return n.label; } |
| } |
| return id; |
| } |
| |
| function getNodeColor(id) { |
| for (const l of ARCH.layers) { |
| if (l.id === id) return l.color; |
| for (const n of l.nodes) { if (n.id === id) return l.color; } |
| } |
| return '#666'; |
| } |
| |
| |
| |
| |
| (function renderStats() { |
| let nodeCount = 0; |
| ARCH.layers.forEach(l => nodeCount += l.nodes.length); |
| const patterns = {}; |
| ARCH.connections.forEach(c => { |
| const p = c.invocation_pattern || 'standard'; |
| patterns[p] = (patterns[p] || 0) + 1; |
| }); |
| const html = [ |
| `<div class="stat"><div class="num">${ARCH.layers.length}</div><div class="label">Layers</div></div>`, |
| `<div class="stat"><div class="num">${nodeCount}</div><div class="label">Nodes</div></div>`, |
| `<div class="stat"><div class="num">${ARCH.connections.length}</div><div class="label">Connections</div></div>`, |
| `<div class="stat"><div class="num">${patterns.autonomous || 0}</div><div class="label">Autonomous</div></div>`, |
| `<div class="stat"><div class="num">${patterns.deterministic || 0}</div><div class="label">Deterministic</div></div>`, |
| `<div class="stat"><div class="num">${patterns.bidirectional || 0}</div><div class="label">Bidirectional</div></div>`, |
| ]; |
| document.getElementById('stats-banner').innerHTML = html.join(''); |
| })(); |
| |
| |
| |
| |
| function connCard(c) { |
| const tagClass = c.invocation_pattern ? `tag-${c.invocation_pattern}` : ''; |
| const tag = c.invocation_pattern ? `<span class="conn-tag ${tagClass}">${c.invocation_pattern}</span>` : ''; |
| return `<div class="conn-card"> |
| <div class="conn-label" style="color:${c.color || '#666'}">${c.label || c.id}</div> |
| <div class="conn-path">${getNodeLabel(c.from)} → ${getNodeLabel(c.to)}</div> |
| <div class="conn-desc">${c.description}</div> |
| ${tag} |
| </div>`; |
| } |
| |
| document.getElementById('conn-grid').innerHTML = ARCH.connections.map(connCard).join(''); |
| ['autonomous','deterministic','bidirectional','transport'].forEach(p => { |
| const el = document.getElementById('conn-' + p); |
| if (el) el.innerHTML = ARCH.connections.filter(c => c.invocation_pattern === p).map(connCard).join('') || '<p>None</p>'; |
| }); |
| |
| |
| |
| |
| document.getElementById('layer-grid').innerHTML = ARCH.layers.map(l => { |
| const chips = l.nodes.map(n => `<span class="node-chip" title="${n.description}">${n.label}</span>`).join(''); |
| return `<div class="layer-card" style="border-top-color:${l.color}"> |
| <h4 style="color:${l.color}">${l.label}</h4> |
| <p style="font-size:0.8rem;color:var(--text-muted);margin:0.25rem 0 0.5rem">${l.description}</p> |
| <div class="layer-nodes">${chips}</div> |
| </div>`; |
| }).join(''); |
| |
| |
| |
| |
| document.querySelectorAll('.tab-btn').forEach(btn => { |
| btn.addEventListener('click', () => { |
| btn.closest('.page-section').querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); |
| btn.closest('.page-section').querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active')); |
| btn.classList.add('active'); |
| document.getElementById('tab-' + btn.dataset.tab).classList.add('active'); |
| }); |
| }); |
| |
| |
| |
| |
| document.querySelectorAll('nav .links a[href^="#"]').forEach(a => { |
| a.addEventListener('click', e => { |
| e.preventDefault(); |
| document.querySelector(a.getAttribute('href'))?.scrollIntoView({ behavior: 'smooth' }); |
| document.querySelectorAll('nav .links a').forEach(x => x.classList.remove('active')); |
| a.classList.add('active'); |
| }); |
| }); |
| |
| |
| |
| |
| (function initDiagram() { |
| const container = document.getElementById('diagram-container'); |
| const W = 1000, H = 840; |
| const centerW = 420; |
| const centerX = (W - centerW) / 2; |
| const centerCX = W / 2; |
| const leftCX = 95; |
| const rightCX = W - 95; |
| |
| const svg = d3.select(container).append('svg') |
| .attr('viewBox', `0 0 ${W} ${H}`) |
| .attr('preserveAspectRatio', 'xMidYMid meet'); |
| |
| const g = svg.append('g'); |
| const zoom = d3.zoom().scaleExtent([0.3, 3]).on('zoom', e => g.attr('transform', e.transform)); |
| svg.call(zoom); |
| |
| |
| document.querySelectorAll('.diagram-toolbar button').forEach(btn => { |
| btn.addEventListener('click', e => { |
| e.stopPropagation(); |
| const action = btn.dataset.action; |
| if (action === 'zoom-in') svg.transition().duration(200).call(zoom.scaleBy, 1.3); |
| else if (action === 'zoom-out') svg.transition().duration(200).call(zoom.scaleBy, 0.7); |
| else if (action === 'reset') svg.transition().duration(200).call(zoom.transform, d3.zoomIdentity); |
| else if (action === 'fullscreen') { |
| const wrap = document.getElementById('diagram'); |
| document.fullscreenElement ? document.exitFullscreen() : wrap.requestFullscreen(); |
| } |
| }); |
| }); |
| |
| |
| let tipEl = null; |
| function showTip(evt, text) { |
| if (!tipEl) { tipEl = document.createElement('div'); tipEl.className = 'arch-tooltip'; document.body.appendChild(tipEl); } |
| tipEl.textContent = text; tipEl.style.opacity = '1'; |
| let left = evt.clientX - 120, top = evt.clientY - 60; |
| if (top < 8) top = evt.clientY + 20; |
| if (left < 8) left = 8; |
| if (left + 240 > window.innerWidth - 8) left = window.innerWidth - 248; |
| tipEl.style.left = left + 'px'; tipEl.style.top = top + 'px'; |
| } |
| function hideTip() { if (tipEl) tipEl.style.opacity = '0'; } |
| |
| |
| const defs = svg.append('defs'); |
| let gradCount = 0; |
| function makeGrad(c1, c2) { |
| const id = 'g' + (gradCount++); |
| const grad = defs.append('linearGradient').attr('id', id).attr('x1','0%').attr('y1','0%').attr('x2','100%').attr('y2','100%'); |
| grad.append('stop').attr('offset','0%').attr('stop-color', c1); |
| grad.append('stop').attr('offset','100%').attr('stop-color', c2); |
| return `url(#${id})`; |
| } |
| |
| function darken(hex, amt) { |
| let r = parseInt(hex.slice(1,3),16), gg = parseInt(hex.slice(3,5),16), b = parseInt(hex.slice(5,7),16); |
| r = Math.max(0, r - amt); gg = Math.max(0, gg - amt); b = Math.max(0, b - amt); |
| return '#' + [r,gg,b].map(v => v.toString(16).padStart(2,'0')).join(''); |
| } |
| |
| function chip(parent, x, y, label, color, tooltip) { |
| const cg = parent.append('g').style('cursor','default'); |
| const tw = label.length * 7.2 + 22; |
| const ch = 28; |
| const cx = x - tw/2; |
| cg.append('rect').attr('x',cx).attr('y',y).attr('width',tw).attr('height',ch).attr('rx',6).attr('fill', makeGrad(color, darken(color, 30))); |
| cg.append('text').attr('x',x).attr('y',y+ch/2+1).attr('text-anchor','middle').attr('dominant-baseline','middle') |
| .attr('fill','#fff').attr('font-family','system-ui,sans-serif').attr('font-size','11').attr('font-weight','600').text(label); |
| if (tooltip) { cg.on('mousemove', evt => showTip(evt, tooltip)).on('mouseleave', hideTip); } |
| return { x, y, w: tw, h: ch, cx: x, cy: y + ch/2 }; |
| } |
| |
| |
| const labelBg = '#f8f9fb'; |
| |
| function layerBox(parent, x, y, w, h, color, label, dashed, rightLabel) { |
| const bg = color + '0a'; |
| const rect = parent.append('rect').attr('x',x).attr('y',y).attr('width',w).attr('height',h).attr('rx',12).attr('fill',bg).attr('stroke',color).attr('stroke-width',2); |
| if (dashed) rect.attr('stroke-dasharray','6 4'); |
| parent.append('rect').attr('x',x+16).attr('y',y-8).attr('width',label.length*7.5+12).attr('height',16).attr('fill',labelBg); |
| parent.append('text').attr('x',x+22).attr('y',y+3).attr('fill',color).attr('font-family','system-ui').attr('font-size','10').attr('font-weight','600').attr('letter-spacing','0.1em').text(label.toUpperCase()); |
| if (rightLabel) { |
| parent.append('rect').attr('x',x+w-rightLabel.length*7-22).attr('y',y-8).attr('width',rightLabel.length*7+12).attr('height',16).attr('fill',labelBg); |
| parent.append('text').attr('x',x+w-16).attr('y',y+3).attr('text-anchor','end').attr('fill',color).attr('font-family','system-ui').attr('font-size','9').attr('font-weight','600').attr('letter-spacing','0.12em').text(rightLabel.toUpperCase()); |
| } |
| } |
| |
| function card(parent, cx, y, w, h, color, icon, title, tooltip) { |
| const cg = parent.append('g').style('cursor','default'); |
| cg.append('rect').attr('x',cx-w/2).attr('y',y).attr('width',w).attr('height',h).attr('rx',10).attr('fill', makeGrad(color, darken(color,30))); |
| cg.append('text').attr('x',cx).attr('y',y+h/2-6).attr('text-anchor','middle').attr('fill','#fff').attr('font-size','18').text(icon); |
| cg.append('text').attr('x',cx).attr('y',y+h/2+12).attr('text-anchor','middle').attr('fill','#fff').attr('font-family','system-ui').attr('font-size','12').attr('font-weight','700').text(title); |
| if (tooltip) { cg.on('mousemove', evt => showTip(evt, tooltip)).on('mouseleave', hideTip); } |
| return { cx, cy: y+h/2, y, h, w }; |
| } |
| |
| function subChips(parent, cx, y, items, color) { |
| const gap = 4; const ch = 20; |
| let totalW = items.reduce((s,t) => s + t.length*6+16+gap, -gap); |
| let sx = cx - totalW/2; |
| items.forEach(item => { |
| const cw = item.length*6+16; |
| parent.append('rect').attr('x',sx).attr('y',y).attr('width',cw).attr('height',ch).attr('rx',5).attr('fill',color+'14').attr('stroke',color+'40').attr('stroke-width',1.5); |
| parent.append('text').attr('x',sx+cw/2).attr('y',y+ch/2+1).attr('text-anchor','middle').attr('dominant-baseline','middle').attr('fill',color).attr('font-family','system-ui').attr('font-size','8.5').attr('font-weight','600').text(item); |
| sx += cw + gap; |
| }); |
| } |
| |
| function drawConn(arrowsG, x1, y1, x2, y2, color, opts={}) { |
| const mx = (x1+x2)/2, my = (y1+y2)/2; |
| let d; |
| if (Math.abs(y2-y1) > Math.abs(x2-x1)) d = `M${x1},${y1} C${x1},${my} ${x2},${my} ${x2},${y2}`; |
| else d = `M${x1},${y1} C${mx},${y1} ${mx},${y2} ${x2},${y2}`; |
| arrowsG.append('path').attr('d',d).attr('fill','none').attr('stroke',color).attr('stroke-width',2).attr('opacity',opts.opacity||0.7).attr('stroke-dasharray',opts.dashed?'6 4':'none'); |
| if (!opts.noArrow) { |
| const angle = Math.atan2(y2-my, x2-mx); |
| const hl = 7; |
| arrowsG.append('polygon').attr('points',`${x2},${y2} ${x2-hl*Math.cos(angle-0.4)},${y2-hl*Math.sin(angle-0.4)} ${x2-hl*Math.cos(angle+0.4)},${y2-hl*Math.sin(angle+0.4)}`).attr('fill',color).attr('opacity',opts.opacity||0.7); |
| } |
| if (opts.label) { |
| arrowsG.append('text').attr('x',mx).attr('y',my-6).attr('text-anchor','middle').attr('fill',color).attr('font-family','system-ui').attr('font-size','9').attr('font-weight','700').text(opts.label); |
| } |
| } |
| |
| |
| const arrowsG = g.append('g'); |
| const nodesG = g.append('g'); |
| |
| |
| |
| const pY=20, pH=55; |
| const mY=95, mH=60; |
| const iY=175, iH=85; |
| const csY=285, csH=65; |
| const aY=380, aH=85; |
| const mcpY=497, mcpH=55; |
| const hitlY=584, hitlH=28; |
| const actY=642, actH=75; |
| |
| |
| layerBox(nodesG, centerX, pY, centerW, pH, '#4a6fa5', 'Prompts'); |
| chip(nodesG, centerCX-105, pY+20, 'User Prompt', '#4a6fa5', 'The end user\u2019s question or instruction.'); |
| chip(nodesG, centerCX+5, pY+20, 'System Prompt', '#4a6fa5', 'Developer-defined behavioral instructions + RAG context injection point.'); |
| chip(nodesG, centerCX+120, pY+20, 'Vendor Prompt', '#6b8ab8', 'Hidden provider directives \u2014 RLHF, safety. Never truly vanilla.'); |
| drawConn(arrowsG, centerCX, pY+pH, centerCX, mY, '#4a6fa5'); |
| |
| |
| layerBox(nodesG, centerX, mY, centerW, mH, '#8b5cf6', 'Models'); |
| chip(nodesG, centerCX-120, mY+20, 'Commercial', '#8b5cf6', 'Claude, GPT, Gemini, Cohere.'); |
| chip(nodesG, centerCX-25, mY+20, 'Open Source', '#8b5cf6', 'Llama, Mistral, Qwen, DeepSeek, Phi.'); |
| chip(nodesG, centerCX+75, mY+20, 'Fine-Tuned', '#a78bfa', 'Task-specific models on your data.'); |
| chip(nodesG, centerCX+160, mY+20, 'Embedding', '#a78bfa', 'Vector representations for RAG \u2014 distinct from generative LLMs.'); |
| drawConn(arrowsG, centerCX, mY+mH, centerCX, iY, '#8b5cf6'); |
| |
| |
| layerBox(nodesG, centerX, iY, centerW, iH, '#6366f1', 'Inference', false, 'Gateway Optional'); |
| chip(nodesG, centerCX, iY+18, 'Gateway', '#6366f1', 'Optional model router \u2014 OpenRouter or LiteLLM. Routes to cloud or local endpoints.'); |
| chip(nodesG, centerCX-110, iY+55, 'Cloud APIs', '#6366f1', 'Anthropic, OpenAI, Google, Bedrock.'); |
| chip(nodesG, centerCX-15, iY+55, 'Self-Hosted', '#6366f1', 'vLLM, TGI, Ollama.'); |
| chip(nodesG, centerCX+75, iY+55, 'On-Prem', '#818cf8', 'Air-gapped deployments.'); |
| chip(nodesG, centerCX+140, iY+55, 'Edge', '#818cf8', 'On-device, offline.'); |
| |
| chip(nodesG, centerCX-170, iY+55, 'Cache', '#818cf8', 'Prompt/response caching \u2014 cost and latency reduction.'); |
| |
| |
| arrowsG.append('path').attr('d',`M${centerCX-15},${iY+34} L${centerCX-110},${iY+55}`).attr('stroke','#6366f1').attr('stroke-width',1.5).attr('opacity',0.5).attr('stroke-dasharray','3 2'); |
| arrowsG.append('path').attr('d',`M${centerCX+5},${iY+34} L${centerCX-15},${iY+55}`).attr('stroke','#6366f1').attr('stroke-width',1.5).attr('opacity',0.5).attr('stroke-dasharray','3 2'); |
| arrowsG.append('text').attr('x',centerCX-55).attr('y',iY+42).attr('text-anchor','middle').attr('fill','#6366f1').attr('font-family','system-ui').attr('font-size','7.5').attr('font-weight','700').text('VIA GATEWAY'); |
| |
| |
| arrowsG.append('path').attr('d',`M${centerCX-110},${iY+4} L${centerCX-110},${iY+55}`).attr('stroke','#6366f1').attr('stroke-width',1.5).attr('opacity',0.4).attr('stroke-dasharray','5 3'); |
| arrowsG.append('path').attr('d',`M${centerCX-15},${iY+4} L${centerCX-15},${iY+55}`).attr('stroke','#6366f1').attr('stroke-width',1.5).attr('opacity',0.4).attr('stroke-dasharray','5 3'); |
| arrowsG.append('text').attr('x',centerCX-65).attr('y',iY+11).attr('text-anchor','middle').attr('fill','#6366f1').attr('font-family','system-ui').attr('font-size','7.5').attr('font-weight','700').attr('opacity',0.6).text('DIRECT'); |
| |
| drawConn(arrowsG, centerCX, iY+iH, centerCX, csY, '#6366f1'); |
| |
| |
| layerBox(nodesG, centerX, csY, centerW, csH, '#2563eb', 'Context Store', true); |
| chip(nodesG, centerCX-70, csY+25, 'Context (RAG)', '#2563eb', 'Grounding AI in your documents.'); |
| chip(nodesG, centerCX+45, csY+25, 'Memory', '#2563eb', 'Mined, ad-hoc, implicit, structured.'); |
| chip(nodesG, centerCX+140, csY+25, 'Vector Store', '#2563eb', 'Semantic search indexes.'); |
| |
| |
| arrowsG.append('path') |
| .attr('d', `M${centerCX-70},${csY+25} C${centerCX-70},${csY-10} ${centerX-40},${pY+pH/2} ${centerX},${pY+pH/2}`) |
| .attr('fill','none').attr('stroke','#2563eb').attr('stroke-width',1.5).attr('stroke-dasharray','4 3').attr('opacity',0.35); |
| arrowsG.append('text').attr('x',centerX-20).attr('y',iY+10).attr('text-anchor','end').attr('fill','#2563eb').attr('font-family','system-ui').attr('font-size','8').attr('font-weight','700').text('CONTEXT INJECTION'); |
| |
| drawConn(arrowsG, centerCX, csY+csH, centerCX, aY, '#2563eb'); |
| |
| |
| layerBox(nodesG, centerX, aY, centerW, aH, '#e85d26', 'Agents', false, 'Backend'); |
| chip(nodesG, centerCX, aY+20, 'Orchestration', '#c94d1e', 'Routes to agents, pipelines, or workflows.'); |
| chip(nodesG, centerCX-85, aY+52, 'Agents', '#e85d26', 'Autonomous, non-deterministic tool use. Execution is a loop.'); |
| chip(nodesG, centerCX+5, aY+52, 'Pipelines', '#e85d26', 'Fixed steps, deterministic tool wiring.'); |
| chip(nodesG, centerCX+85, aY+52, 'Workflows', '#e85d26', 'Event-triggered, pre-configured tools.'); |
| drawConn(arrowsG, centerCX, aY+aH, centerCX, mcpY, '#e85d26'); |
| |
| |
| arrowsG.append('path') |
| .attr('d', `M${centerX-4},${aY+52+14} C${centerX-55},${aY+52+14} ${centerX-55},${iY+iH/2} ${centerX},${iY+iH/2}`) |
| .attr('fill','none').attr('stroke','#e85d26').attr('stroke-width',1.5).attr('stroke-dasharray','4 3').attr('opacity',0.5); |
| arrowsG.append('polygon') |
| .attr('points',`${centerX},${iY+iH/2} ${centerX-7},${iY+iH/2+4} ${centerX-7},${iY+iH/2-4}`) |
| .attr('fill','#e85d26').attr('opacity',0.5); |
| arrowsG.append('text').attr('x',centerX-60).attr('y',(aY+iY+iH)/2).attr('text-anchor','middle').attr('fill','#e85d26').attr('font-family','system-ui').attr('font-size','8').attr('font-weight','700').attr('transform',`rotate(-90,${centerX-60},${(aY+iY+iH)/2})`).text('AGENTIC LOOP'); |
| |
| |
| arrowsG.append('path') |
| .attr('d', `M${centerX+centerW+4},${csY+csH/2} C${centerX+centerW+45},${csY+csH/2} ${centerX+centerW+45},${aY+aH/2} ${centerX+centerW},${aY+aH/2}`) |
| .attr('fill','none').attr('stroke','#2563eb').attr('stroke-width',1.5).attr('stroke-dasharray','4 3').attr('opacity',0.4); |
| arrowsG.append('polygon') |
| .attr('points',`${centerX+centerW},${aY+aH/2} ${centerX+centerW+7},${aY+aH/2-4} ${centerX+centerW+7},${aY+aH/2+4}`) |
| .attr('fill','#2563eb').attr('opacity',0.4); |
| |
| |
| drawConn(arrowsG, centerCX+85, aY+52+14, centerCX+45, csY+25+28, '#2563eb', {dashed:true, opacity:0.35, label:'AD-HOC MEMORY'}); |
| |
| |
| layerBox(nodesG, centerX, mcpY, centerW, mcpH, '#f5a623', 'MCP', false); |
| chip(nodesG, centerCX-40, mcpY+18, 'Model Context Protocol', '#f5a623', 'Open standard for connecting AI to tools and data.'); |
| chip(nodesG, centerCX+125, mcpY+18, 'Tool Registry', '#e09800', 'tools/list \u2014 discovery for available tools.'); |
| |
| |
| const hitlW = 180; |
| nodesG.append('rect').attr('x',centerCX-hitlW/2).attr('y',hitlY).attr('width',hitlW).attr('height',hitlH).attr('rx',14).attr('fill','rgba(185,28,28,0.08)').attr('stroke','#b91c1c').attr('stroke-width',2).attr('stroke-dasharray','4 3'); |
| nodesG.append('text').attr('x',centerCX).attr('y',hitlY+hitlH/2+1).attr('text-anchor','middle').attr('dominant-baseline','middle').attr('fill','#b91c1c').attr('font-family','system-ui').attr('font-size','9.5').attr('font-weight','700').text('\u{1F6D1} HUMAN-IN-THE-LOOP'); |
| const tipG = nodesG.append('rect').attr('x',centerCX-hitlW/2).attr('y',hitlY).attr('width',hitlW).attr('height',hitlH).attr('fill','transparent'); |
| tipG.on('mousemove', evt => showTip(evt, 'Approval checkpoints. Pattern varies: per-action (agents), stage-gate (pipelines), exception-based (workflows).')).on('mouseleave', hideTip); |
| |
| drawConn(arrowsG, centerCX, mcpY+mcpH, centerCX, hitlY, '#f5a623', {noArrow:true}); |
| arrowsG.append('text').attr('x',centerCX+80).attr('y',(mcpY+mcpH+hitlY)/2+2).attr('text-anchor','start').attr('fill','#f5a623').attr('font-family','system-ui').attr('font-size','9').attr('font-weight','700').text('TAKING ACTIONS'); |
| drawConn(arrowsG, centerCX, hitlY+hitlH, centerCX, actY, '#b91c1c'); |
| |
| |
| layerBox(nodesG, centerX, actY, centerW, actH, '#2563eb', 'Actions', true); |
| chip(nodesG, centerCX-85, actY+28, 'Your Data', '#2563eb', 'Salesforce, HubSpot, Google Drive, Notion.'); |
| chip(nodesG, centerCX+15, actY+28, 'External Services', '#2563eb', 'Weather, time, public datasets.'); |
| chip(nodesG, centerCX+120, actY+28, 'Digital Wallets', '#1d4ed8', 'Stripe, crypto, bank APIs.'); |
| |
| |
| const fe = card(nodesG, leftCX, mY+20, 130, 65, '#4a6fa5', '\u{1F4BB}', 'Frontends', 'Chat windows, bots, web UIs, dashboards.'); |
| subChips(nodesG, leftCX, fe.y+fe.h+8, ['Chatbots','Slack'], '#4a6fa5'); |
| subChips(nodesG, leftCX, fe.y+fe.h+34, ['Telegram','Web UIs'], '#4a6fa5'); |
| subChips(nodesG, leftCX, fe.y+fe.h+60, ['Dashboards'], '#4a6fa5'); |
| |
| |
| const inX2 = centerCX-20; |
| arrowsG.append('path').attr('d',`M${leftCX+65},${fe.y+fe.h*0.3} C${leftCX+105},${fe.y+fe.h*0.3} ${inX2},${fe.y+fe.h*0.3} ${inX2},${pY}`) |
| .attr('fill','none').attr('stroke','#4a6fa5').attr('stroke-width',2).attr('stroke-dasharray','6 3').attr('opacity',0.6); |
| arrowsG.append('polygon').attr('points',`${inX2},${pY} ${inX2-5},${pY+8} ${inX2+5},${pY+8}`).attr('fill','#4a6fa5').attr('opacity',0.6); |
| arrowsG.append('text').attr('x',(leftCX+65+inX2)/2+15).attr('y',fe.y+fe.h*0.3-6).attr('text-anchor','middle').attr('fill','#4a6fa5').attr('font-family','system-ui').attr('font-size','9').attr('font-weight','700').text('INPUT'); |
| |
| |
| arrowsG.append('path').attr('d',`M${centerX},${aY+aH*0.65} C${(centerX+leftCX+65)/2},${aY+aH*0.65} ${(centerX+leftCX+65)/2},${fe.y+fe.h*0.7} ${leftCX+65},${fe.y+fe.h*0.7}`) |
| .attr('fill','none').attr('stroke','#e85d26').attr('stroke-width',2).attr('stroke-dasharray','6 3').attr('opacity',0.6); |
| arrowsG.append('polygon').attr('points',`${leftCX+65},${fe.y+fe.h*0.7} ${leftCX+72},${fe.y+fe.h*0.7-4} ${leftCX+72},${fe.y+fe.h*0.7+4}`).attr('fill','#e85d26').attr('opacity',0.6); |
| arrowsG.append('text').attr('x',(centerX+leftCX+65)/2).attr('y',aY+aH*0.65+14).attr('text-anchor','middle').attr('fill','#e85d26').attr('font-family','system-ui').attr('font-size','9').attr('font-weight','700').text('OUTPUT'); |
| |
| |
| const grC = card(nodesG, leftCX, aY+10, 130, 65, '#0891b2', '\u{1F310}', 'Grounding', 'Real-time external data that grounds agent responses.'); |
| subChips(nodesG, leftCX, grC.y+grC.h+8, ['Web Search'], '#0891b2'); |
| subChips(nodesG, leftCX, grC.y+grC.h+34, ['News Feeds'], '#0891b2'); |
| subChips(nodesG, leftCX, grC.y+grC.h+60, ['Real-Time Data'], '#0891b2'); |
| |
| drawConn(arrowsG, leftCX+65, grC.cy, centerX, aY+40, '#0891b2', {dashed:true, label:'GROUNDING'}); |
| |
| |
| const stCX = leftCX; |
| const stCardY = actY+10; |
| const storC = card(nodesG, stCX, stCardY, 130, 65, '#14b8a6', '\u{1F4BE}', 'Storage', 'Persistence \u2014 conversations, outputs, prompts, databases.'); |
| subChips(nodesG, stCX, storC.y+storC.h+8, ['Conversations'], '#14b8a6'); |
| subChips(nodesG, stCX, storC.y+storC.h+34, ['Outputs','Prompts'], '#14b8a6'); |
| subChips(nodesG, stCX, storC.y+storC.h+60, ['Postgres'], '#14b8a6'); |
| subChips(nodesG, stCX, storC.y+storC.h+86, ['Data Lakes'], '#14b8a6'); |
| |
| |
| drawConn(arrowsG, centerX, aY+aH-10, stCX+65, storC.cy, '#14b8a6', {dashed:true, opacity:0.5}); |
| |
| |
| arrowsG.append('path') |
| .attr('d', `M${stCX},${storC.y+10} C${stCX-40},${storC.y+10} ${stCX-40},${csY+csH/2} ${centerX},${csY+csH/2}`) |
| .attr('fill','none').attr('stroke','#2563eb').attr('stroke-width',1.5).attr('stroke-dasharray','4 3').attr('opacity',0.4); |
| arrowsG.append('polygon') |
| .attr('points',`${centerX},${csY+csH/2} ${centerX-7},${csY+csH/2+4} ${centerX-7},${csY+csH/2-4}`) |
| .attr('fill','#2563eb').attr('opacity',0.4); |
| arrowsG.append('text').attr('x',stCX-45).attr('y',(storC.y+csY+csH)/2).attr('text-anchor','middle').attr('fill','#2563eb').attr('font-family','system-ui').attr('font-size','8').attr('font-weight','700').attr('transform',`rotate(-90,${stCX-45},${(storC.y+csY+csH)/2})`).text('CONTEXT-MINING'); |
| |
| |
| const safetyC = card(nodesG, rightCX, mY+20, 130, 65, '#b91c1c', '\u{1F6E1}\uFE0F', 'Safety', 'Guardrails at prompts, agents, and tool level.'); |
| subChips(nodesG, rightCX, safetyC.y+safetyC.h+8, ['Guardrails'], '#b91c1c'); |
| subChips(nodesG, rightCX, safetyC.y+safetyC.h+34, ['Input Filtering'], '#b91c1c'); |
| |
| const obsC = card(nodesG, rightCX, aY-10, 130, 65, '#065f46', '\u{1F4CA}', 'Observability', 'Eval, logging, monitoring. Feeds into prompt optimization.'); |
| subChips(nodesG, rightCX, obsC.y+obsC.h+8, ['Eval','Logging'], '#065f46'); |
| subChips(nodesG, rightCX, obsC.y+obsC.h+34, ['Monitoring'], '#065f46'); |
| |
| |
| drawConn(arrowsG, rightCX-65, safetyC.cy, centerX+centerW, aY+30, '#b91c1c'); |
| drawConn(arrowsG, rightCX-65, safetyC.y+20, centerX+centerW, pY+pH/2, '#b91c1c', {dashed:true, opacity:0.35}); |
| |
| drawConn(arrowsG, rightCX-65, obsC.cy, centerX+centerW, aY+55, '#065f46'); |
| |
| |
| const destY = actY+10; |
| chip(nodesG, rightCX, destY+4, 'Prompt Library', '#4a6fa5', 'Versioned, reusable prompt templates.'); |
| chip(nodesG, rightCX, destY+38, 'Wiki / KM', '#14b8a6', 'Organizational knowledge from agent outputs.'); |
| chip(nodesG, rightCX, destY+72, 'Data Warehouse', '#6366f1', 'Analytics, compliance, long-term retention.'); |
| |
| |
| drawConn(arrowsG, stCX+65, storC.y+storC.h-10, rightCX-60, destY+4+14, '#4a6fa5', {dashed:true, opacity:0.4}); |
| drawConn(arrowsG, stCX+65, storC.y+storC.h-5, rightCX-50, destY+38+14, '#14b8a6', {dashed:true, opacity:0.4}); |
| drawConn(arrowsG, stCX+65, storC.y+storC.h, rightCX-60, destY+72+14, '#6366f1', {dashed:true, opacity:0.4}); |
| |
| |
| drawConn(arrowsG, centerCX+160, mY+20+28, centerCX+140, csY+25, '#8b5cf6', {dashed:true, opacity:0.35}); |
| |
| })(); |
| |
| |
| |
| |
| function exportPNG(scale) { |
| scale = scale || 2; |
| const svgEl = document.querySelector('.diagram-container svg'); |
| const svgData = new XMLSerializer().serializeToString(svgEl); |
| const canvas = document.createElement('canvas'); |
| const vb = svgEl.viewBox.baseVal; |
| canvas.width = vb.width * scale; |
| canvas.height = vb.height * scale; |
| const ctx = canvas.getContext('2d'); |
| ctx.fillStyle = '#f8f9fb'; |
| ctx.fillRect(0, 0, canvas.width, canvas.height); |
| const img = new Image(); |
| img.onload = () => { |
| ctx.drawImage(img, 0, 0, canvas.width, canvas.height); |
| const a = document.createElement('a'); |
| a.download = `agentic-symphony-${scale}x.png`; |
| a.href = canvas.toDataURL('image/png'); |
| a.click(); |
| }; |
| img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgData))); |
| } |
| |
| function exportSVG() { |
| const svgEl = document.querySelector('.diagram-container svg'); |
| const svgData = new XMLSerializer().serializeToString(svgEl); |
| const blob = new Blob([svgData], { type: 'image/svg+xml' }); |
| const a = document.createElement('a'); |
| a.download = 'agentic-symphony.svg'; |
| a.href = URL.createObjectURL(blob); |
| a.click(); |
| } |
| |
| </script> |
| </body> |
| </html> |
|
|