Current section
Files
Jump to
Current section
Files
examples/17_qdrant_rag.exs
#!/usr/bin/env elixir
Code.require_file("support/live_cli.exs", __DIR__)
defmodule Example17QdrantRAG do
@moduledoc false
alias GEPA.Adapters.GenericRAG
alias GEPA.Adapters.GenericRAG.VectorStore
alias GEPA.Adapters.GenericRAG.VectorStores.Qdrant
@example [
name: "GEPA Qdrant RAG Live Example",
script: "examples/17_qdrant_rag.exs",
summary:
"Runs Generic RAG with real Qdrant storage, real ReqLLM/Gemini embeddings, and real ASM/Gemini inference.",
required: []
]
def main(argv) do
argv = default_to_asm_gemini(argv)
config = LiveCLI.parse_or_halt(argv, @example)
max_metric_calls = cli_integer(argv, "--max-metric-calls") || 2
embedding_model = config.embedding_model || "gemini-embedding-001"
qdrant_url = config.qdrant_url || "http://localhost:6333"
collection = config.collection || "gepa_ex_qdrant_rag"
embedding_api_key = embedding_api_key!()
embedder = build_embedder(embedding_model, embedding_api_key, config.embedding_dimensions)
IO.puts(
LiveCLI.cost_warning(@example[:name], config.adapter, config.provider, max_metric_calls * 2)
)
IO.puts("""
GEPA Qdrant RAG Live Example
============================
Inference adapter/provider: #{config.adapter}/#{config.provider}
Inference model: #{config.model || "(provider default)"}
Embedding model: #{GEPA.Embeddings.model(embedder)}
Qdrant URL: #{qdrant_url}
Collection: #{collection}
Max metric calls: #{max_metric_calls}
""")
probe_vector = GEPA.Embeddings.embed!(embedder, "GEPA Qdrant dimension probe")
store =
Qdrant.new(
url: qdrant_url,
collection_name: collection,
embedder: embedder,
vector_size: length(probe_vector)
)
ensure_qdrant!(store, qdrant_url)
:ok = VectorStore.reset_collection(store)
{:ok, ids} = VectorStore.upsert_documents(store, corpus())
adapter =
GenericRAG.new(
vector_store: store,
llm_model: config.client,
embedder: embedder,
rag_config: %{
"retrieval_strategy" => "similarity",
"top_k" => 2,
"retrieval_weight" => 0.4,
"generation_weight" => 0.6,
"filters" => %{source: "integration_example"}
}
)
{:ok, result} =
GEPA.optimize(
seed_candidate: seed_candidate(),
trainset: trainset(),
valset: valset(),
adapter: adapter,
max_metric_calls: max_metric_calls,
reflection_llm: config.client,
reflection_minibatch_size: config.minibatch_size,
structured_output: false
)
IO.puts("""
Qdrant RAG Integration Complete
===============================
Inserted documents: #{Enum.join(ids, ", ")}
Best score: #{Float.round(GEPA.Result.best_score(result), 4)}
Iterations: #{result.i}
Total evaluations: #{result.total_num_evals}
Vector store info:
#{Jason.encode!(VectorStore.get_collection_info(store), pretty: true)}
""")
end
defp build_embedder(embedding_model, api_key, dimensions) do
GEPA.Embeddings.ReqLLM.new!(
provider: :gemini,
model: embedding_model,
api_key: api_key,
dimensions: dimensions
)
end
defp ensure_qdrant!(store, qdrant_url) do
case VectorStore.health_check(store) do
:ok -> :ok
{:error, reason} -> raise "Qdrant is not reachable at #{qdrant_url}: #{inspect(reason)}"
end
end
defp embedding_api_key! do
System.get_env("GEMINI_API_KEY") ||
System.get_env("GOOGLE_API_KEY") ||
raise("""
missing Gemini embedding API key; set GEMINI_API_KEY or GOOGLE_API_KEY.
Qdrant stores vectors only, so this example needs ReqLLM/Gemini to create embeddings.
""")
end
defp corpus do
[
%{
id: "gepa_core",
content:
"GEPA optimizes prompt components by evaluating candidates, reflecting on trajectories, and proposing improved instructions.",
metadata: %{topic: "gepa", source: "integration_example"}
},
%{
id: "qdrant_role",
content:
"Qdrant is the vector database in this example. It stores and searches embeddings but does not generate embeddings.",
metadata: %{topic: "qdrant", source: "integration_example"}
},
%{
id: "adapter_plan",
content:
"GEPA Elixir separates inference adapters, embedding adapters, vector-store adapters, and tracking adapters behind behaviours.",
metadata: %{topic: "architecture", source: "integration_example"}
}
]
end
defp trainset do
[
%GenericRAG.DataInst{
query: "What does GEPA optimize and how does it improve candidates?",
ground_truth_answer: "GEPA optimizes prompt components using evaluations and reflection.",
relevant_doc_ids: ["gepa_core"],
filters: %{source: "integration_example"}
},
%GenericRAG.DataInst{
query: "Does Qdrant create embeddings in this setup?",
ground_truth_answer:
"No. ReqLLM with Gemini creates embeddings; Qdrant stores and searches vectors.",
relevant_doc_ids: ["qdrant_role"],
filters: %{source: "integration_example"}
}
]
end
defp valset do
[
%GenericRAG.DataInst{
query: "What integration boundaries does the Elixir GEPA port use?",
ground_truth_answer:
"It separates inference, embeddings, vector stores, and tracking behind behaviours.",
relevant_doc_ids: ["adapter_plan"],
filters: %{source: "integration_example"}
}
]
end
defp seed_candidate do
%{
"answer_generation" =>
"Answer using only the provided context. Be direct.\n\nQuery: {query}\n\nContext:\n{context}"
}
end
defp default_to_asm_gemini(argv) do
cond do
match?(["--" | _rest], argv) ->
["--" | default_to_asm_gemini(tl(argv))]
option_present?(argv, "--help") ->
argv
option_present?(argv, "--adapter") ->
argv
true ->
["--adapter", "asm" | argv]
end
end
defp cli_integer(argv, option) do
argv
|> Enum.chunk_every(2, 1, :discard)
|> Enum.find_value(fn
[^option, value] -> String.to_integer(value)
_other -> nil
end)
end
defp option_present?(argv, option) do
Enum.any?(argv, &(&1 == option or String.starts_with?(&1, option <> "=")))
end
end
Example17QdrantRAG.main(System.argv())