Assign stable index to citations. Use a strong prompt preamble for our app agents
This commit is contained in:
parent
643ed20d6f
commit
a7850e2210
4 changed files with 76 additions and 3 deletions
|
|
@ -65,9 +65,20 @@ def get_client() -> HaikuRAG:
|
||||||
# Create skill, toolset, and agent
|
# Create skill, toolset, and agent
|
||||||
skill = create_skill(db_path=db_path, config=Config)
|
skill = create_skill(db_path=db_path, config=Config)
|
||||||
toolset = SkillToolset(skills=[skill])
|
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(
|
agent = Agent(
|
||||||
os.getenv("HAIKU_CHAT_MODEL", "openai:gpt-4o"),
|
os.getenv("HAIKU_CHAT_MODEL", "openai:gpt-4o"),
|
||||||
instructions=toolset.system_prompt,
|
instructions=AGENT_PREAMBLE + toolset.system_prompt,
|
||||||
toolsets=[toolset],
|
toolsets=[toolset],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,16 @@ except ImportError: # pragma: no cover
|
||||||
|
|
||||||
RAG_STATE_NAMESPACE = "rag"
|
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):
|
class ChatApp(App):
|
||||||
"""Textual TUI for conversational RAG."""
|
"""Textual TUI for conversational RAG."""
|
||||||
|
|
@ -158,7 +168,7 @@ class ChatApp(App):
|
||||||
self._toolset = SkillToolset(skills=[self._skill])
|
self._toolset = SkillToolset(skills=[self._skill])
|
||||||
self._agent = Agent(
|
self._agent = Agent(
|
||||||
self._model,
|
self._model,
|
||||||
instructions=self._toolset.system_prompt,
|
instructions=AGENT_PREAMBLE + self._toolset.system_prompt,
|
||||||
toolsets=[self._toolset],
|
toolsets=[self._toolset],
|
||||||
)
|
)
|
||||||
self._state = self._toolset.build_state_snapshot()
|
self._state = self._toolset.build_state_snapshot()
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,10 @@ def create_skill(
|
||||||
answer, citations = await rag.ask(question)
|
answer, citations = await rag.ask(question)
|
||||||
|
|
||||||
if ctx.deps and ctx.deps.state and isinstance(ctx.deps.state, RAGState):
|
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.citations.extend(citations)
|
||||||
ctx.deps.state.qa_history.append(
|
ctx.deps.state.qa_history.append(
|
||||||
QAHistoryEntry(question=question, answer=answer, citations=citations)
|
QAHistoryEntry(question=question, answer=answer, citations=citations)
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,55 @@ class TestAskTool:
|
||||||
assert len(state.qa_history) == 1
|
assert len(state.qa_history) == 1
|
||||||
assert isinstance(state.qa_history[0], QAHistoryEntry)
|
assert isinstance(state.qa_history[0], QAHistoryEntry)
|
||||||
assert state.qa_history[0].question == "What is AI?"
|
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:
|
class TestAnalyzeTool:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue