Skip to main content

Query Engine Module

The query engine module provides different strategies for answering questions using retrieved knowledge.

Quick Start

from praisonaiagents.knowledge.query_engine import (
    QueryMode,
    QueryResult,
    QueryEngineProtocol,
    get_query_engine_registry,
    decompose_question,
    SimpleQueryEngine,
    SubQuestionEngine
)

# Decompose complex questions
sub_questions = decompose_question(
    "What is Python and how do I install it?"
)
# Returns: ["What is Python?", "How do I install Python?"]

# Use sub-question engine
engine = SubQuestionEngine()
result = engine.query(
    "Compare Python and Java for web development",
    retriever=my_retriever
)

Query Modes

QueryMode Enum

from praisonaiagents.knowledge.query_engine import QueryMode

class QueryMode(Enum):
    DEFAULT = "default"           # Direct retrieval + synthesis
    SUB_QUESTION = "sub_question" # Decompose into sub-questions
    SUMMARIZE = "summarize"       # Summarize retrieved content
    SQL = "sql"                   # SQL query generation
    ROUTER = "router"             # Route to appropriate engine

Mode Comparison

ModeDescriptionBest For
defaultSimple retrieve + answerDirect questions
sub_questionDecompose complex queriesMulti-part questions
summarizeSummarize all resultsOverview queries
sqlGenerate SQL queriesStructured data
routerAuto-select best modeMixed workloads

Classes

QueryResult

Dataclass for query results.
@dataclass
class QueryResult:
    answer: str
    sources: List[Dict[str, Any]] = field(default_factory=list)
    sub_questions: Optional[List[str]] = None
    metadata: Dict[str, Any] = field(default_factory=dict)

QueryEngineProtocol

Protocol for query engine implementations.
class QueryEngineProtocol(Protocol):
    name: str
    mode: QueryMode
    
    def query(
        self,
        question: str,
        retriever: Any,
        top_k: int = 10,
        **kwargs
    ) -> QueryResult:
        """Answer a question using retrieved knowledge."""
        ...

SimpleQueryEngine

Basic retrieve-and-answer engine.
from praisonaiagents.knowledge.query_engine import SimpleQueryEngine

engine = SimpleQueryEngine()

result = engine.query(
    question="What is machine learning?",
    retriever=my_retriever,
    top_k=5
)

print(result.answer)
print(f"Sources: {len(result.sources)}")

SubQuestionEngine

Decomposes complex questions into sub-questions.
from praisonaiagents.knowledge.query_engine import SubQuestionEngine

engine = SubQuestionEngine()

result = engine.query(
    question="Compare the performance and ease of use of Python vs Java",
    retriever=my_retriever
)

print(f"Sub-questions: {result.sub_questions}")
print(f"Answer: {result.answer}")

Utility Functions

decompose_question

Break a complex question into simpler sub-questions.
from praisonaiagents.knowledge.query_engine import decompose_question

# Simple decomposition (keyword-based)
questions = decompose_question(
    "What is Python and how do I install it on Windows?"
)
# Returns: ["What is Python?", "How do I install Python on Windows?"]

# With LLM decomposition
questions = decompose_question(
    "Compare the pros and cons of microservices vs monolith",
    use_llm=True
)

QueryEngineRegistry

Registry for managing query engines.
from praisonaiagents.knowledge.query_engine import get_query_engine_registry

registry = get_query_engine_registry()

# List available engines
engines = registry.list_engines()  # ['default', 'sub_question', ...]

# Get engine by name
engine = registry.get("sub_question")

# Register custom engine
registry.register("custom", MyEngine)

Using with Knowledge

from praisonaiagents import Agent, Knowledge

# Configure query mode
agent = Agent(
    instructions="You are a helpful assistant",
    knowledge=["./docs/"],
    knowledge_config={
        "query_mode": "sub_question",  # or "default", "summarize"
    }
)

response = agent.chat("What are the benefits and drawbacks of this approach?")

Creating Custom Query Engines

from praisonaiagents.knowledge.query_engine import (
    QueryMode,
    QueryResult,
    get_query_engine_registry
)
from typing import Any

class MyQueryEngine:
    name = "my_engine"
    mode = QueryMode.DEFAULT
    
    def query(
        self,
        question: str,
        retriever: Any,
        top_k: int = 10,
        **kwargs
    ) -> QueryResult:
        # Retrieve relevant documents
        docs = retriever.retrieve(question, top_k=top_k)
        
        # Custom answer synthesis
        answer = self._synthesize(question, docs)
        
        return QueryResult(
            answer=answer,
            sources=[{"text": d.text} for d in docs]
        )
    
    def _synthesize(self, question: str, docs: list) -> str:
        # Your synthesis logic
        ...

# Register
registry = get_query_engine_registry()
registry.register("my_engine", MyQueryEngine)

Performance

  • decompose_question has a fast keyword-based mode (no API calls)
  • LLM-based decomposition requires API calls
  • Sub-question engine parallelizes sub-query retrieval