MagicAF follows a strict three-layer architecture. Each layer has a single responsibility, and layers communicate only through well-defined trait boundaries.

Layer Model

(EEyvmoibuderedndbciuens3F21gioSn·r··eDemRrosAaOAIvmsdtrGniaatcWfcilpehorenotreragesks·Air·tftpcrlrVp,LPaouelartwcciayoi<ttcdemoSuoaarpn,rrtpteSitBLVtoeua,LonriyarssleLye,dr,eer·rre>Ls·lumlRSteesrtuvyliptcePesa)rser

Layer 1 — Infrastructure

The infrastructure layer provides the fundamental AI primitives. Each primitive is defined as an async trait.

ModuleTraitPurpose
embeddingsEmbeddingServiceProduce dense vectors from text
vector_storeVectorStoreIndex, search, and delete vectors with JSON payloads
llmLlmServiceChat completion and text generation via OpenAI-compatible API

Concrete implementations ship in separate crates:

ImplementationCrateBackend
LocalEmbeddingServicemagicaf-coreAny OpenAI-compatible /v1/embeddings endpoint
QdrantVectorStoremagicaf-qdrantQdrant (REST API)
InMemoryVectorStoremagicaf-coreIn-process brute-force search (no external deps)
LocalLlmServicemagicaf-local-llmAny OpenAI-compatible /v1/chat/completions endpoint

Layer 2 — Orchestration

RAGWorkflow is the pipeline engine. It is fully generic over all service and adapter traits:

pub struct RAGWorkflow<S, V, L, EF, PB, RP, T>
where
    S: EmbeddingService,
    V: VectorStore,
    L: LlmService,
    EF: EvidenceFormatter,
    PB: PromptBuilder,
    RP: ResultParser<T>,
    T: Send,

The workflow executes a fixed six-step pipeline:

  1. Embed the query → EmbeddingService::embed_single
  2. Search the vector store → VectorStore::search
  3. Format evidence → EvidenceFormatter::format_evidence
  4. Build the prompt → PromptBuilder::build_prompt
  5. Invoke the LLM → LlmService::chat
  6. Parse the result → ResultParser::parse_result

Layer 3 — Adapters

Three traits form the domain extension seam. This is where your application-specific logic lives.

TraitInjected at StepDomain Responsibility
EvidenceFormatter3Convert search results → text block for the LLM
PromptBuilder4Assemble system + evidence + query → prompt
ResultParser<T>6Parse raw LLM text → typed domain result

Default implementations ship in magicaf-core for rapid prototyping:

  • DefaultEvidenceFormatter — pretty-prints JSON payloads
  • DefaultPromptBuilder — wraps evidence in <context> tags
  • RawResultParser — returns the raw LLM output as String
  • JsonResultParser<T> — deserializes JSON into any T: DeserializeOwned

Crate Dependency Graph

mm(aaVggeiiccctaaoffr--Scqtodorrreaenti(mtprla)itmsa(,gLilDcmTaSOfes-r,lvoiRccAaeGl-ielmnlpgmli)ne,adapters,config,errors)

Both implementation crates depend only on magicaf-core. Downstream applications depend on all three.

Guiding Principles

  1. Separation of concerns. Infrastructure is independent of orchestration, which is independent of domain logic.
  2. Trait-driven extensibility. Every major component is accessed through a trait — swapping implementations is a one-line config change.
  3. DTO-based public surface. Request/response types are plain structs with Serialize/Deserialize, easy to expose over FFI or serialize to JSON.
  4. Error codes for FFI. Every MagicError variant maps to a stable numeric code, enabling clean error propagation across language boundaries.