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:

  1. User asks a question
  2. System retrieves "relevant" documents via RAG
  3. Everything gets stuffed into a prompt
  4. Model generates a response
  5. 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:

AgentStageWhat They DoWhy Specialized
Research AnalystResearchGathers market data, competitive analysisNeeds access to market databases, knows how to extract relevant metrics
Due Diligence SpecialistDue DiligenceValidates claims, assesses risksCross-references facts against knowledge graph, flags ungrounded claims
Memo WriterInvestment MemoSynthesizes findings into recommendationFormats for IC consumption, knows what decision-makers need
ReviewerReviewFinal quality checkFresh 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 ISQadra is NOTWhy It Matters
Workload orchestrationA chatbot frameworkWork has structure, stages, and completion criteria
Agents as team membersAI assistantsAgents have roles, expertise, and responsibilities
Pipelines with stagesConversation threadsProgress is measurable, handoffs are explicit
Structured artifactsFree-form responsesOutputs are typed, versioned, and auditable
Multi-model coordinationSingle-model promptingRight model for each task, selected by orchestration
Fact-grounded outputsHopeful generationClaims 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 TemplateQadra Agent
"You are a helpful research analyst..."Named persona with system prompt, display name, avatar, description
Hopes the model understands contextReceives scoped context from pipeline stages or flow state
Output is whatever the model saysOutput is structured, captured as artifacts, forwarded to downstream stages
Testing = vibesTesting = predictable orchestration with observable state at each node
Versioning = copy-pasteVersioning = 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_type determines what happens after the agent completes (retain control, hand off to parent, or return to pipeline start). output_visibility controls whether output is user-facing or kept internal. max_calls_per_parent_agent prevents runaway loops.

  • Versioning: Agents support version numbers. New versions can be deployed alongside old ones, with is_active controlling 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 TypePurpose
Start / EndEntry and exit points of the graph
AgentInvoke an agent with context from the flow state
ConditionBranch based on expressions evaluated against flow state
HumanInputPause execution and wait for user input before continuing
LoopIterate over a collection or repeat until a condition is met
HttpMake external HTTP calls
ExecuteFlowRun a sub-flow (composition)
TransformManipulate flow state data
DelayWait a specified duration
KnowledgeLookupQuery the knowledge graph or vector store
ResearchAutonomous external research
ExtractLLM-driven structured parameter extraction from unstructured text
ArtifactProduce 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 TypeUse SPOUse 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 TypeRoutingExample
Structured factsSPO triples"Revenue was $100B in Q4"
NarrativeVector chunks"The company has shown remarkable growth..."
MixedBothWikipedia 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:

ConcernHow Qadra Handles It
Data isolationEvery query includes tenant_id. Cross-tenant queries are impossible at the repository layer.
Knowledge isolationSPO triples and vector embeddings are tenant-scoped. Acme's facts never leak to Beta Corp.
Agent isolationTenants define their own agents. "Research Analyst" means different things to different orgs.
Billing isolationLLM costs, storage, and compute are tracked per-tenant via usage records.
ComplianceAudit 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.

LayerTechnologyPurposeWhy This Choice
CoreRust + NATSData layer, NATS handlersMemory safety without GC pauses. Async-first. Compiles to single binary.
GatewayNode.js + ExpressHTTP server, JWT, CORSThin layer for auth and routing. Communicates with Rust Core via NATS.
IntelligencePython AgentLLM orchestrationPython ecosystem for ML/LLM tooling. Handles workload execution, agent loops.
FrontendReact + ViteSPA with flow builderReactFlow for visual flow editing. Tailwind + zustand for state.
DatabasePostgreSQL 16Source of truth, pgvectorACID transactions, native vector search, mature ecosystem
Cache/EventsRedis StackCaching, pub/sub, streamsSub-millisecond reads, consumer groups for reliable delivery
MessagingNATSService communicationRequest-reply between Rust Core, Gateway, and Python Agent
AuditMongoDBDecision trees, tracesSchema-flexible for evolving audit formats, append-optimized
GatewayKongJWT validation, rate limitingOffloads auth from application, proven at scale
ObservabilityOTel + GrafanaMetrics, logs, tracesIndustry 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.