Assign stable index to citations. Use a strong prompt preamble for our app agents

This commit is contained in:
Yiorgis Gozadinos 2026-02-20 11:02:46 +02:00
parent 643ed20d6f
commit a7850e2210
No known key found for this signature in database
4 changed files with 76 additions and 3 deletions

View file

@ -65,9 +65,20 @@ def get_client() -> HaikuRAG:
# Create skill, toolset, and agent
skill = create_skill(db_path=db_path, config=Config)
toolset = SkillToolset(skills=[skill])
AGENT_PREAMBLE = """You are a helpful research assistant powered by haiku.rag, a knowledge base system.
CRITICAL RULES:
1. For greetings or casual chat: respond directly WITHOUT using any tools
2. NEVER make up information - always use tools to get facts from the knowledge base
3. For questions: Use the "ask" tool - it handles search and citation automatically
4. For searches: Use the "search" tool - copy the ENTIRE tool response to your output INCLUDING content snippets
5. When you use the "ask" tool, summarize the key findings and always include citations in your response
"""
agent = Agent(
os.getenv("HAIKU_CHAT_MODEL", "openai:gpt-4o"),
instructions=toolset.system_prompt,
instructions=AGENT_PREAMBLE + toolset.system_prompt,
toolsets=[toolset],
)

View file

@ -54,6 +54,16 @@ except ImportError: # pragma: no cover
RAG_STATE_NAMESPACE = "rag"
AGENT_PREAMBLE = """You are a helpful research assistant powered by haiku.rag, a knowledge base system.
CRITICAL RULES:
1. For greetings or casual chat: respond directly WITHOUT using any tools
2. NEVER make up information - always use tools to get facts from the knowledge base
3. For questions: Use the "ask" tool - it handles search and citation automatically
4. For searches: Use the "search" tool - copy the ENTIRE tool response to your output INCLUDING content snippets
5. When you use the "ask" tool, summarize the key findings and always include citations in your response
"""
class ChatApp(App):
"""Textual TUI for conversational RAG."""
@ -158,7 +168,7 @@ class ChatApp(App):
self._toolset = SkillToolset(skills=[self._skill])
self._agent = Agent(
self._model,
instructions=self._toolset.system_prompt,
instructions=AGENT_PREAMBLE + self._toolset.system_prompt,
toolsets=[self._toolset],
)
self._state = self._toolset.build_state_snapshot()

View file

@ -172,6 +172,10 @@ def create_skill(
answer, citations = await rag.ask(question)
if ctx.deps and ctx.deps.state and isinstance(ctx.deps.state, RAGState):
next_index = len(ctx.deps.state.citations) + 1
for citation in citations:
citation.index = next_index
next_index += 1
ctx.deps.state.citations.extend(citations)
ctx.deps.state.qa_history.append(
QAHistoryEntry(question=question, answer=answer, citations=citations)

View file

@ -203,7 +203,55 @@ class TestAskTool:
assert len(state.qa_history) == 1
assert isinstance(state.qa_history[0], QAHistoryEntry)
assert state.qa_history[0].question == "What is AI?"
assert state.qa_history[0].citations == citations
async def test_ask_assigns_citation_indices(self, rag_db, monkeypatch):
from haiku.rag.skills.rag import RAGState, create_skill
first_citations = [
Citation(
document_id="d1",
chunk_id="c1",
document_uri="test://doc1",
content="First.",
),
Citation(
document_id="d2",
chunk_id="c2",
document_uri="test://doc2",
content="Second.",
),
]
second_citations = [
Citation(
document_id="d3",
chunk_id="c3",
document_uri="test://doc3",
content="Third.",
),
]
call_count = 0
async def mock_ask(self, question):
nonlocal call_count
call_count += 1
if call_count == 1:
return ("Answer 1", first_citations)
return ("Answer 2", second_citations)
monkeypatch.setattr(HaikuRAG, "ask", mock_ask)
skill = create_skill(db_path=rag_db)
ask = _get_tool(skill, "ask")
state = RAGState()
ctx = _make_ctx(state)
await ask(ctx, question="First question")
assert state.citations[0].index == 1
assert state.citations[1].index == 2
await ask(ctx, question="Second question")
assert state.citations[2].index == 3
class TestAnalyzeTool: