Introduction
What is Qadra?
Qadra is a multitenant workload orchestration platform designed for enterprise AI workflows. Unlike traditional chat applications, Qadra treats AI agents as specialized team members that perform discrete tasks within structured pipelines.
The Problem with Traditional AI Applications
Most LLM applications follow this pattern:
- User asks a question
- System retrieves "relevant" documents via RAG
- Everything gets stuffed into a prompt
- Model generates a response
- Repeat
This approach has fundamental problems:
Context Window Abuse: Models have limited context windows. Traditional RAG dumps retrieved documents into the prompt, hoping the model figures out what's relevant. As complexity grows, you hit limits—and even before that, quality degrades because signal gets lost in noise.
Hallucination at Scale: When you ask a model to do everything (retrieve, reason, synthesize, format) in one shot, it hallucinates. The model confidently produces plausible-sounding nonsense because it has no structured way to verify claims against facts.
No Accumulation: Each interaction starts fresh. A research analyst who spent an hour gathering data doesn't pass that structured knowledge to the due diligence specialist—instead, you re-retrieve, re-prompt, and hope for consistency.
Prompt Spaghetti: As applications grow, prompts become unmaintainable. You end up with 10,000-token system prompts trying to handle every edge case, version-controlled in git with no testing strategy.
Core Thesis
Agents are programs that sometimes call models, not prompts that do everything.
This single idea changes everything:
Programs have structure. An agent has an identity (display name, avatar), a defined purpose (description), and behavioral instructions (system prompt). This is inspectable, testable, versionable.
Programs compose. Complex workflows aren't giant prompts—they're pipelines of specialized agents, or visual flow graphs that orchestrate multiple agents with branching, conditions, and human-in-the-loop gates. The research analyst hands off to the due diligence specialist, who hands off to the memo writer. Each does one job well.
Programs are bounded. An agent doesn't see "everything"—it sees exactly what the orchestration layer provides. Context flows forward through pipeline stages, accumulated and structured. This is demand-driven context, not supply-driven context dump.
Qadra's Approach
-
Persona-driven Agents: Each agent is a named specialist with a system prompt that defines its behavior, a description of its capabilities, and an identity (display name, avatar). Agents call LLMs with scoped context—they are team members, not catch-all assistants.
-
Flow-based Orchestration: Work is coordinated through visual flow graphs (built with a drag-and-drop flow builder) or sequential pipelines. Flow graphs support branching, conditions, loops, HTTP calls, human-in-the-loop gates, knowledge lookups, and sub-flow execution—all without writing code.
-
Demand-driven Context: Context flows forward through pipeline stages. Each stage receives the accumulated output of prior stages. No stage sees everything—only what was produced upstream. This prevents context window abuse and keeps signal-to-noise high.
-
Fact Grounding: Responses are verified against a knowledge graph (SPO triples). Claims that can't be grounded are flagged. This catches hallucinations before they reach users.
-
Knowledge Core Ingestion: Documents are analyzed, chunked, embedded, and routed into the appropriate store—structured facts become SPO triples, narrative content becomes vector chunks. The ingestion pipeline understands the difference and routes accordingly.
-
Conversations with Epistemic Grounding: Users can interact with agents directly through conversations. When an agent responds, the system automatically injects relevant knowledge graph context (facts, contradiction detections, knowledge gaps) so the agent's response is grounded in what the organization actually knows.
First Use Case: Investment Banking
Qadra's initial deployment focuses on investment banking workflows:
Deal → Research → Due Diligence → Investment Memo → Review
Why investment banking? These workflows are:
- High-stakes: A bad investment memo can lose millions. Accuracy matters.
- Structured: Deals follow predictable stages with clear handoffs.
- Knowledge-intensive: Analysts synthesize data from dozens of sources.
- Compliance-heavy: Every decision needs an audit trail.
This is the opposite of casual chat—it's exactly where "prompts that do everything" fail hardest.
Each stage has specialized agents that produce artifacts:
| Agent | Stage | What They Do | Why Specialized |
|---|---|---|---|
| Research Analyst | Research | Gathers market data, competitive analysis | Needs access to market databases, knows how to extract relevant metrics |
| Due Diligence Specialist | Due Diligence | Validates claims, assesses risks | Cross-references facts against knowledge graph, flags ungrounded claims |
| Memo Writer | Investment Memo | Synthesizes findings into recommendation | Formats for IC consumption, knows what decision-makers need |
| Reviewer | Review | Final quality check | Fresh eyes, catches inconsistencies between sections |
Each agent is an expert at one thing. The research analyst doesn't worry about IC formatting—that's the memo writer's job. This separation of concerns is why agents are specialized personas with focused system prompts, not monolithic catch-all assistants.
This is NOT a Chat App
Understanding what Qadra is NOT is as important as understanding what it is:
| Qadra IS | Qadra is NOT | Why It Matters |
|---|---|---|
| Workload orchestration | A chatbot framework | Work has structure, stages, and completion criteria |
| Agents as team members | AI assistants | Agents have roles, expertise, and responsibilities |
| Pipelines with stages | Conversation threads | Progress is measurable, handoffs are explicit |
| Structured artifacts | Free-form responses | Outputs are typed, versioned, and auditable |
| Multi-model coordination | Single-model prompting | Right model for each task, selected by orchestration |
| Fact-grounded outputs | Hopeful generation | Claims are verified against knowledge graph |
Why This Distinction Matters
Chat apps optimize for engagement. They want users talking to the AI as much as possible. Success is measured in messages sent, sessions started, time spent.
Qadra optimizes for outcomes. A workload has a goal (produce an investment memo). Success is measured in workloads completed, accuracy of outputs, time-to-decision.
This changes everything about the architecture:
- No conversation history bloat: Workloads have structured context, not message logs
- No "let me think about that": Agents either have the capability or they don't
- No hallucination tolerance: Ungrounded claims are flagged, not glossed over
- Conversations are supplementary: Users can chat with agents directly, but the core value is in orchestrated workflows that produce auditable artifacts
Key Differentiators
Persona-based Agents
Agents in Qadra are not prompt templates. They are named specialists—team members with defined identities, behavioral instructions, and scoped responsibilities.
What makes a Qadra agent different from a prompt?
| Prompt Template | Qadra Agent |
|---|---|
| "You are a helpful research analyst..." | Named persona with system prompt, display name, avatar, description |
| Hopes the model understands context | Receives scoped context from pipeline stages or flow state |
| Output is whatever the model says | Output is structured, captured as artifacts, forwarded to downstream stages |
| Testing = vibes | Testing = predictable orchestration with observable state at each node |
| Versioning = copy-paste | Versioning = semantic versions in database |
An agent has four components:
-
Identity: Display name, avatar, and description. This is the agent's face—how it appears in the UI, how other parts of the system discover and reference it. Example: "Research Analyst" with a description of "Gathers market data and competitive analysis."
-
System Prompt: Behavioral instructions that define how the agent thinks, responds, and operates. This is not a one-shot prompt—it's persistent guidance that shapes every interaction the agent has within a workload or conversation.
-
Orchestration Controls: How the agent participates in larger workflows.
control_typedetermines what happens after the agent completes (retain control, hand off to parent, or return to pipeline start).output_visibilitycontrols whether output is user-facing or kept internal.max_calls_per_parent_agentprevents runaway loops. -
Versioning: Agents support version numbers. New versions can be deployed alongside old ones, with
is_activecontrolling which version is live. This enables safe iteration without breaking running workloads.
Visual Flow Builder
Qadra includes a visual flow builder for designing agent workflows as directed graphs. Flows are stored as ReactFlow JSONB and executed by an event-driven graph executor.
Flow nodes provide the building blocks:
| Node Type | Purpose |
|---|---|
| Start / End | Entry and exit points of the graph |
| Agent | Invoke an agent with context from the flow state |
| Condition | Branch based on expressions evaluated against flow state |
| HumanInput | Pause execution and wait for user input before continuing |
| Loop | Iterate over a collection or repeat until a condition is met |
| Http | Make external HTTP calls |
| ExecuteFlow | Run a sub-flow (composition) |
| Transform | Manipulate flow state data |
| Delay | Wait a specified duration |
| KnowledgeLookup | Query the knowledge graph or vector store |
| Research | Autonomous external research |
| Extract | LLM-driven structured parameter extraction from unstructured text |
| Artifact | Produce a structured artifact from a template |
Flows support human-in-the-loop patterns: when a HumanInput node is reached, execution pauses and waits for user response via the API. This enables approval gates, review steps, and interactive decision points within automated workflows.
Knowledge Graph Integration
Traditional RAG has a fundamental problem: it treats all knowledge as bags of text chunks. "Apple was founded by Steve Jobs in 1976" becomes a chunk that might or might not be retrieved when you ask about Apple's founding.
Qadra uses a dual-storage approach that separates facts from narrative:
SPO Index (Subject-Predicate-Object): Structured facts stored as triples.
(Apple, founded_by, Steve Jobs)
(Apple, founding_year, 1976)
(Steve Jobs, co-founded, Apple)
Facts are queryable, linkable, and verifiable. When an agent claims "Apple was founded in 1976," the system can check: does the triple (Apple, founding_year, 1976) exist? This is grounding—the antidote to hallucination.
Vector Store: Embeddings for semantic similarity search.
Narrative content that doesn't decompose into facts—analysis, opinions, context—is chunked, embedded, and stored in pgvector with HNSW indexes. This enables "find me content similar to X" queries.
Why both? They serve different purposes:
| Query Type | Use SPO | Use Vectors |
|---|---|---|
| "When was Apple founded?" | Yes | |
| "What's Apple's founding story?" | Yes | |
| "Did Jobs found Apple?" | Yes | |
| "Find similar companies to Apple" | Yes | |
| "Verify this claim about Apple" | Yes |
The ingestion pipeline analyzes content and routes intelligently:
| Content Type | Routing | Example |
|---|---|---|
| Structured facts | SPO triples | "Revenue was $100B in Q4" |
| Narrative | Vector chunks | "The company has shown remarkable growth..." |
| Mixed | Both | Wikipedia article (facts + narrative) |
This isn't just optimization—it's architectural. Facts go where they can be verified. Narrative goes where it can be searched. The system knows the difference.
Conversations with Epistemic Grounding
While Qadra's core is workload orchestration, users can also interact with agents directly through conversations. These are ad-hoc chat threads—not pipeline executions—but they benefit from the same knowledge infrastructure.
When a user sends a message in a conversation, the system automatically queries the knowledge graph for relevant SPO triples, detects contradictions, and identifies knowledge gaps. This epistemic context is injected into the agent's prompt as structured facts, so the agent's response is grounded in the organization's actual knowledge base rather than the LLM's training data.
Conversations also serve as the interface for flow execution: when the LLM determines that a user's intent maps to an existing flow, it can trigger flow execution inline and narrate the real results back to the user. Progress streams in real time via Server-Sent Events.
Multitenancy
Enterprise AI isn't one company—it's many. Each customer needs complete isolation: their agents, their knowledge, their workflows, their data.
Qadra is multitenant by design, not by afterthought:
Organization (Tenant)
├── Users ← Multi-tenant via junction table
├── API Keys ← Tenant-scoped, revocable
├── Agents ← Custom specialists for this org
├── Pipelines ← Workflows specific to their processes
├── Workloads ← Their active and completed work
│ ├── Tasks ← Agent assignments
│ └── Artifacts ← Produced documents
├── Agent Flows ← Visual workflow definitions
│ └── Flow Executions
├── Conversations ← Direct agent interactions
├── SPO Index ← Their private knowledge graph
└── Vector Store ← Their private embeddings
Why multitenancy matters:
| Concern | How Qadra Handles It |
|---|---|
| Data isolation | Every query includes tenant_id. Cross-tenant queries are impossible at the repository layer. |
| Knowledge isolation | SPO triples and vector embeddings are tenant-scoped. Acme's facts never leak to Beta Corp. |
| Agent isolation | Tenants define their own agents. "Research Analyst" means different things to different orgs. |
| Billing isolation | LLM costs, storage, and compute are tracked per-tenant via usage records. |
| Compliance | Audit logs are tenant-scoped. SOC 2 auditors see only their customer's data. |
Row-level security is enforced at the repository layer, not application code:
#![allow(unused)] fn main() { // Every repository method requires tenant_id async fn get_workload(&self, tenant_id: Uuid, id: Uuid) -> Result<Workload> { sqlx::query!("SELECT * FROM workloads WHERE tenant_id = $1 AND id = $2", tenant_id, id) .fetch_one(&self.pool) .await } }
There's no way to accidentally query across tenants because tenant_id is a required parameter on every operation. This is defense in depth—even if application code has a bug, the database layer enforces isolation.
Technology Stack
Every technology choice has a reason. We optimized for correctness, observability, and operational simplicity.
| Layer | Technology | Purpose | Why This Choice |
|---|---|---|---|
| Core | Rust + NATS | Data layer, NATS handlers | Memory safety without GC pauses. Async-first. Compiles to single binary. |
| Gateway | Node.js + Express | HTTP server, JWT, CORS | Thin layer for auth and routing. Communicates with Rust Core via NATS. |
| Intelligence | Python Agent | LLM orchestration | Python ecosystem for ML/LLM tooling. Handles workload execution, agent loops. |
| Frontend | React + Vite | SPA with flow builder | ReactFlow for visual flow editing. Tailwind + zustand for state. |
| Database | PostgreSQL 16 | Source of truth, pgvector | ACID transactions, native vector search, mature ecosystem |
| Cache/Events | Redis Stack | Caching, pub/sub, streams | Sub-millisecond reads, consumer groups for reliable delivery |
| Messaging | NATS | Service communication | Request-reply between Rust Core, Gateway, and Python Agent |
| Audit | MongoDB | Decision trees, traces | Schema-flexible for evolving audit formats, append-optimized |
| Gateway | Kong | JWT validation, rate limiting | Offloads auth from application, proven at scale |
| Observability | OTel + Grafana | Metrics, logs, traces | Industry standard, vendor-neutral, correlates across services |
Why These Specific Choices?
Rust for the data layer: Investment banking workflows are long-running and high-stakes. Rust's memory safety eliminates entire classes of bugs (use-after-free, null pointers). No garbage collector means predictable latency—no surprise pauses during critical operations. All data operations (CRUD, auth, flows, knowledge) go through Rust Core via NATS.
PostgreSQL over specialized databases: One database for relational data AND vector search (pgvector). This eliminates sync complexity between separate vector databases. Transactions work across both—you can atomically insert a document and its embeddings.
NATS for service communication: The gateway, Rust Core, and Python Agent communicate via NATS request-reply. This decouples the HTTP layer from the data layer and allows horizontal scaling of any component independently.
Redis for events, not Kafka: Our event volumes don't justify Kafka's operational complexity. Redis Streams provide durable, acknowledged delivery with consumer groups. If we outgrow Redis, the abstraction layer allows swapping implementations.
MongoDB for audit only: Audit logs have different access patterns (append-heavy, rarely queried, schema-evolving). Keeping them separate from PostgreSQL prevents table bloat and allows independent scaling.