Behaviour for query decomposition in Arcana.Pipeline.
The decomposer breaks complex questions into simpler sub-questions that can be searched independently, improving retrieval for multi-faceted queries.
Built-in Implementations
Arcana.Pipeline.Decomposer.LLM- Uses your LLM to decompose queries (default)
Implementing a Custom Decomposer
defmodule MyApp.KeywordDecomposer do
@behaviour Arcana.Pipeline.Decomposer
@impl true
def decompose(question, _opts) do
# Simple keyword-based decomposition
sub_questions =
question
|> String.split(~r/\s+(and|vs|versus|compared to)\s+/i)
|> Enum.map(&String.trim/1)
|> Enum.reject(&(&1 == ""))
{:ok, sub_questions}
end
endUsing a Custom Decomposer
Pipeline.new(question, repo: repo, llm: llm)
|> Pipeline.decompose(decomposer: MyApp.KeywordDecomposer)
|> Pipeline.search()Using an Inline Function
Pipeline.decompose(ctx,
decomposer: fn question, _opts ->
{:ok, [question]} # No decomposition
end
)
Summary
Callbacks
Decomposes a complex question into simpler sub-questions.
Callbacks
@callback decompose( question :: String.t(), opts :: keyword() ) :: {:ok, [String.t()]} | {:error, term()}
Decomposes a complex question into simpler sub-questions.
Parameters
question- The complex question to decomposeopts- Options passed toPipeline.decompose/2, including::llm- The LLM function (for LLM-based decomposers):prompt- Custom prompt function (for LLM-based decomposers)- Any other options passed to
Pipeline.decompose/2
Returns
{:ok, sub_questions}- List of simpler questions{:error, reason}- On failure, the original question is used as a single-item list