# How Embedding Models Function Within the Embabel Framework: Architecture and Code Examples

> Discover how embedding models work in the Embabel framework. Explore their architecture and code examples to integrate them seamlessly into RAG pipelines.

- Repository: [Embabel/embabel-agent](https://github.com/embabel/embabel-agent)
- Tags: architecture
- Published: 2026-08-09

---

**Embedding models function within the Embabel framework as interchangeable Spring beans wrapped by the `EmbeddingService` interface, enabling seamless integration into RAG pipelines with automatic observability tracing.**

The embabel/embabel-agent repository treats embedding generation as a first-class infrastructure concern, abstracting vectorization behind a unified service layer that supports multiple providers. Understanding how embedding models function within the Embabel framework reveals a modular architecture compatible with OpenAI, Azure, OCI Gen AI, ONNX, and custom implementations while maintaining consistent configuration and monitoring patterns.

## Architectural Layers

Embabel organizes embedding support into four distinct layers, each handled by specific components in the codebase.

### Configuration Management

The framework determines which concrete embedding model to instantiate through property-driven configuration. The `OciGenAiEnvironmentPostProcessor` class reads the property `embabel.models.default-embedding-model` and injects it into the Spring environment as `DEFAULT_EMBEDDING_PROPERTY`. This approach allows operators to switch models without modifying application code.

### Service Abstraction

At the core of the system lies the `EmbeddingService` interface, implemented primarily by `SpringAiEmbeddingService`. This wrapper bridges Embabel's internal API with Spring AI's `EmbeddingModel`, exposing the model name and provider while standardizing the `embed(texts)` method signature. Because the service is a Spring bean, it supports scoping (singleton, prototype) and runtime replacement.

### Observability Integration

Every embedding invocation generates distributed tracing data through `EmbabelSpanEventListener`. This component creates a span named `embabel.embedding` and annotates it with attributes including `gen_ai.operation.name` and `embabel.llm.cost`, enabling monitoring of token usage and model performance in production environments.

### RAG Pipeline Consumption

Components such as `RagService` and `LuceneSearchOperations` consume the `EmbeddingService` to vectorize documents and queries. The `LuceneSearchOperations` class, for example, converts text lists into `EmbeddingRequest` objects and processes the resulting `EmbeddingResponse` for indexing or similarity search.

## Execution Flow

When an agent invokes an embedding operation, the framework executes a five-step lifecycle:

1. **Property Resolution** – `OciGenAiEnvironmentPostProcessor` resolves the default model name from configuration properties.
2. **Bean Creation** – Spring instantiates a `SpringAiEmbeddingService` bean containing the chosen `EmbeddingModel` implementation.
3. **Invocation** – RAG components like `HyDEQueryGenerator` call `embeddingService.embed(texts)`, which delegates to `EmbeddingModel.call(EmbeddingRequest)`.
4. **Observability** – `EmbabelSpanEventListener` intercepts the call and emits the `embabel.embedding` span with model metadata.
5. **Result Propagation** – Generated float vectors return to the caller for downstream indexing or LLM prompting.

## Code Implementation Examples

### Defining a Custom Embedding Bean

You can register custom embedding models by defining a bean that returns `SpringAiEmbeddingService`:

```kotlin
@Configuration
class MyEmbeddingConfig {
    @Bean
    fun myEmbeddingService(): EmbeddingService {
        // Wrap a Spring AI model (could be an OCI Gen AI client, ONNX, etc.)
        return SpringAiEmbeddingService(
            name = "my-embedding-model",
            provider = "my-provider",
            model = MyConcreteEmbeddingModel()   // implements org.springframework.ai.embedding.EmbeddingModel
        )
    }
}

```

*Source:* [[`FakeAiConfiguration.kt`](https://github.com/embabel/embabel-agent/blob/main/FakeAiConfiguration.kt)](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-test-support/embabel-agent-test-internal/src/main/kotlin/com/embabel/common/test/ai/config/FakeAiConfiguration.kt)

### Consuming Embeddings in RAG Components

The following pattern demonstrates how RAG services utilize the embedding layer:

```java
@Service
public class LuceneSearchOperations {
    private final EmbeddingService embeddingService;

    public LuceneSearchOperations(EmbeddingService embeddingService) {
        this.embeddingService = embeddingService;
    }

    public List<Vector> embedTexts(List<String> texts) {
        // Convert to Spring AI request
        EmbeddingRequest request = new EmbeddingRequest(texts, EmbeddingOptions.builder().build());
        EmbeddingResponse response = embeddingService.getModel().call(request);
        return response.getResult();
    }
}

```

*Source:* [[`LuceneSearchOperationsTestBase.kt`](https://github.com/embabel/embabel-agent/blob/main/LuceneSearchOperationsTestBase.kt)](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-rag/embabel-agent-rag-lucene/src/test/kotlin/com/embabel/agent/rag/lucene/LuceneSearchOperationsTestBase.kt)

### Testing Embedding Observability

The `EmbabelSpanEventListener` creates traceable events for every embedding call:

```java
@Test
@DisplayName("embedding invocation becomes a span with model and input tokens")
void embeddingInvocationSpan() {
    listener().onProcessEvent(embeddingEvent());
    Map<String, String> kv = kvOf("embabel.embedding");
    assertEquals("embeddings", kv.get("gen_ai.operation.name"));
    assertEquals("text-embedding-3", kv.get("gen_ai.request.model"));
}

```

*Source:* [[`EmbabelSpanEventListenerTest.java`](https://github.com/embabel/embabel-agent/blob/main/EmbabelSpanEventListenerTest.java)](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-observability/src/test/java/com/embabel/agent/observability/tracing/EmbabelSpanEventListenerTest.java)

### Implementing Fake Models for Testing

For unit tests, `FakeEmbeddingModel` provides deterministic embeddings without external API calls:

```kotlin
data class FakeEmbeddingModel(
    private val dimensions: Int = 8
) : EmbeddingModel {
    override fun call(request: EmbeddingRequest): EmbeddingResponse {
        val output = LinkedList<Embedding>()
        request.text.forEachIndexed { i, _ ->
            output.add(Embedding(generateRandomFloatArray(dimensions), i))
        }
        return EmbeddingResponse(output)
    }
}

```

*Source:* [[`FakeEmbeddingModel.kt`](https://github.com/embabel/embabel-agent/blob/main/FakeEmbeddingModel.kt)](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-test-support/embabel-agent-test-common/src/main/kotlin/com/embabel/common/test/ai/FakeEmbeddingModel.kt)

## Summary

- **Embedding models function within the Embabel framework** as pluggable Spring beans managed through the `EmbeddingService` interface, decoupling vectorization logic from RAG pipelines.
- The `SpringAiEmbeddingService` class bridges Embabel's API with Spring AI's `EmbeddingModel`, supporting providers including OCI Gen AI, OpenAI, and ONNX.
- Configuration occurs through the `OciGenAiEnvironmentPostProcessor`, which reads `embabel.models.default-embedding-model` to determine the active implementation.
- Every embedding operation emits an `embabel.embedding` span via `EmbabelSpanEventListener`, capturing model name and cost attributes for production monitoring.
- The `FakeEmbeddingModel` enables lightweight testing without external dependencies, generating deterministic vectors for unit test suites.

## Frequently Asked Questions

### What interface defines embedding operations in Embabel?

The `EmbeddingService` interface defines the contract for embedding operations, implemented by `SpringAiEmbeddingService` to wrap Spring AI's `EmbeddingModel`. This abstraction allows any concrete model—whether OpenAI, Azure, or custom ONNX implementations—to function within the Embabel framework without changing consumer code.

### How does Embabel determine which embedding model to use?

The `OciGenAiEnvironmentPostProcessor` class reads the property `embabel.models.default-embedding-model` during application startup and injects it into the Spring environment. Spring then instantiates the corresponding `EmbeddingModel` bean and wraps it in a `SpringAiEmbeddingService`, making the model available for injection into RAG components.

### Can I trace embedding costs and token usage in production?

Yes. The `EmbabelSpanEventListener` automatically creates an `embabel.embedding` span for every embedding request, recording attributes such as `gen_ai.operation.name`, `gen_ai.request.model`, and `embabel.llm.cost`. This integration enables monitoring of embedding expenses and performance characteristics through distributed tracing systems.

### How do I test components that depend on embeddings without calling external APIs?

Embabel provides `FakeEmbeddingModel` in the test-support module, which implements Spring AI's `EmbeddingModel` interface to generate deterministic random vectors. This fake implementation allows unit tests for classes like `LuceneSearchOperations` to run quickly and reliably without network dependencies or API costs.