haiku.rag/src/haiku/rag/qa/agent.py
2025-08-15 21:13:17 +02:00

79 lines
2.6 KiB
Python

from pydantic import BaseModel, Field
from pydantic_ai import Agent, RunContext
from pydantic_ai.models.openai import OpenAIModel
from pydantic_ai.providers.ollama import OllamaProvider
from haiku.rag.client import HaikuRAG
from haiku.rag.config import Config
from haiku.rag.qa.prompts import SYSTEM_PROMPT, SYSTEM_PROMPT_WITH_CITATIONS
class SearchResult(BaseModel):
content: str = Field(description="The document text content")
score: float = Field(description="Relevance score (higher is more relevant)")
document_uri: str = Field(description="Source URI/path of the document")
class Dependencies(BaseModel):
model_config = {"arbitrary_types_allowed": True}
client: HaikuRAG
class QuestionAnswerAgent:
def __init__(
self,
client: HaikuRAG,
provider: str,
model: str,
use_citations: bool = False,
q: float = 0.0,
):
self._client = client
system_prompt = SYSTEM_PROMPT_WITH_CITATIONS if use_citations else SYSTEM_PROMPT
model_obj = self._get_model(provider, model)
self._agent = Agent(
model=model_obj,
deps_type=Dependencies,
system_prompt=system_prompt,
)
@self._agent.tool
async def search_documents(
ctx: RunContext[Dependencies],
query: str,
limit: int = 3,
) -> list[SearchResult]:
"""Search the knowledge base for relevant documents."""
search_results = await ctx.deps.client.search(query, limit=limit)
expanded_results = await ctx.deps.client.expand_context(search_results)
return [
SearchResult(
content=chunk.content,
score=score,
document_uri=chunk.document_uri or "",
)
for chunk, score in expanded_results
]
def _get_model(self, provider: str, model: str):
"""Get the appropriate model object for the provider."""
if provider == "openai":
return f"openai:{model}"
elif provider == "anthropic":
return f"anthropic:{model}"
elif provider == "ollama":
return OpenAIModel(
model_name=model,
provider=OllamaProvider(base_url=f"{Config.OLLAMA_BASE_URL}/v1"),
)
else:
raise ValueError(f"Unsupported provider: {provider}")
async def answer(self, question: str) -> str:
"""Answer a question using the RAG system."""
deps = Dependencies(client=self._client)
result = await self._agent.run(question, deps=deps)
return result.output