defmodule AgentSea.Embeddings.RetrievalTool do @moduledoc """ An `AgentSea.Tool` that lets an agent search a knowledge base — the retrieval half of RAG. It reads the `AgentSea.Embeddings` handle from the agent's execution context (`ctx.embeddings`), so the same tool module works against any store/embedder without per-instance config. Add it to an agent's `tools` and pass the handle when running: Agent.run(agent, "what is the refund policy?", %{embeddings: handle}) """ @behaviour AgentSea.Tool @default_k 3 @impl true def name, do: "search_knowledge" @impl true def description, do: "Search the knowledge base for passages relevant to a query. " <> "Returns the most relevant passages to ground your answer." @impl true def schema do [ query: [type: :string, required: true], k: [type: :integer] ] end @impl true def run(args, ctx) do case Map.get(ctx, :embeddings) do %AgentSea.Embeddings{} = handle -> query = arg(args, :query) k = arg(args, :k) || @default_k if is_binary(query) and query != "" do {:ok, format(AgentSea.Embeddings.search(handle, query, k))} else {:error, :missing_query} end _ -> {:error, :no_embeddings_in_context} end end defp arg(args, key), do: Map.get(args, to_string(key)) || Map.get(args, key) defp format([]), do: "No relevant passages found." defp format(hits) do hits |> Enum.with_index(1) |> Enum.map_join("\n", fn {hit, i} -> "#{i}. (score #{Float.round(hit.score, 3)}) #{hit.text}" end) end end