Add initial_context support to chat agent
This commit is contained in:
parent
e73486b7a8
commit
dff489f75f
7 changed files with 122 additions and 1 deletions
|
|
@ -32,6 +32,13 @@ def create_chat_agent(config: AppConfig) -> Agent[ChatDeps, str]:
|
||||||
retries=3,
|
retries=3,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@agent.system_prompt
|
||||||
|
async def add_initial_context(ctx: RunContext[ChatDeps]) -> str:
|
||||||
|
"""Add initial_context to system prompt when available."""
|
||||||
|
if ctx.deps.session_state and ctx.deps.session_state.initial_context:
|
||||||
|
return f"\nBACKGROUND CONTEXT:\n{ctx.deps.session_state.initial_context}"
|
||||||
|
return ""
|
||||||
|
|
||||||
@agent.tool
|
@agent.tool
|
||||||
async def search(
|
async def search(
|
||||||
ctx: RunContext[ChatDeps],
|
ctx: RunContext[ChatDeps],
|
||||||
|
|
@ -86,6 +93,11 @@ def create_chat_agent(config: AppConfig) -> Agent[ChatDeps, str]:
|
||||||
qa_history=(
|
qa_history=(
|
||||||
ctx.deps.session_state.qa_history if ctx.deps.session_state else []
|
ctx.deps.session_state.qa_history if ctx.deps.session_state else []
|
||||||
),
|
),
|
||||||
|
initial_context=(
|
||||||
|
ctx.deps.session_state.initial_context
|
||||||
|
if ctx.deps.session_state
|
||||||
|
else None
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Return detailed results for the agent to present
|
# Return detailed results for the agent to present
|
||||||
|
|
@ -182,9 +194,14 @@ def create_chat_agent(config: AppConfig) -> Agent[ChatDeps, str]:
|
||||||
# Build and run the conversational research graph
|
# Build and run the conversational research graph
|
||||||
graph = build_conversational_graph(config=ctx.deps.config)
|
graph = build_conversational_graph(config=ctx.deps.config)
|
||||||
|
|
||||||
|
initial_context = (
|
||||||
|
ctx.deps.session_state.initial_context if ctx.deps.session_state else None
|
||||||
|
)
|
||||||
|
|
||||||
context = ResearchContext(
|
context = ResearchContext(
|
||||||
original_question=question,
|
original_question=question,
|
||||||
qa_responses=existing_qa,
|
qa_responses=existing_qa,
|
||||||
|
initial_context=initial_context,
|
||||||
)
|
)
|
||||||
state = ResearchState(
|
state = ResearchState(
|
||||||
context=context,
|
context=context,
|
||||||
|
|
@ -238,6 +255,11 @@ def create_chat_agent(config: AppConfig) -> Agent[ChatDeps, str]:
|
||||||
qa_history=(
|
qa_history=(
|
||||||
ctx.deps.session_state.qa_history if ctx.deps.session_state else []
|
ctx.deps.session_state.qa_history if ctx.deps.session_state else []
|
||||||
),
|
),
|
||||||
|
initial_context=(
|
||||||
|
ctx.deps.session_state.initial_context
|
||||||
|
if ctx.deps.session_state
|
||||||
|
else None
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Format answer with citation references and confidence
|
# Format answer with citation references and confidence
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ class ChatSessionState(BaseModel):
|
||||||
session_id: str = ""
|
session_id: str = ""
|
||||||
citations: list[CitationInfo] = []
|
citations: list[CitationInfo] = []
|
||||||
qa_history: list[QAResponse] = []
|
qa_history: list[QAResponse] = []
|
||||||
|
initial_context: str | None = None
|
||||||
|
|
||||||
|
|
||||||
def format_conversation_context(qa_history: list[QAResponse]) -> str:
|
def format_conversation_context(qa_history: list[QAResponse]) -> str:
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ class ResearchContext(BaseModel):
|
||||||
qa_responses: list[Any] = Field(
|
qa_responses: list[Any] = Field(
|
||||||
default_factory=list, description="Structured QA pairs used during research"
|
default_factory=list, description="Structured QA pairs used during research"
|
||||||
)
|
)
|
||||||
|
initial_context: str | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="Optional background context provided at session start",
|
||||||
|
)
|
||||||
|
|
||||||
def add_qa_response(self, qa: "SearchAnswer") -> None:
|
def add_qa_response(self, qa: "SearchAnswer") -> None:
|
||||||
"""Add a structured QA response."""
|
"""Add a structured QA response."""
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ from haiku.rag.utils import build_prompt, get_model
|
||||||
|
|
||||||
def format_context_for_prompt(context: ResearchContext) -> str:
|
def format_context_for_prompt(context: ResearchContext) -> str:
|
||||||
"""Format the research context as XML for inclusion in prompts."""
|
"""Format the research context as XML for inclusion in prompts."""
|
||||||
context_data = {
|
context_data: dict[str, object] = {
|
||||||
"original_question": context.original_question,
|
"original_question": context.original_question,
|
||||||
"unanswered_questions": context.sub_questions,
|
"unanswered_questions": context.sub_questions,
|
||||||
"qa_responses": [
|
"qa_responses": [
|
||||||
|
|
@ -52,6 +52,8 @@ def format_context_for_prompt(context: ResearchContext) -> str:
|
||||||
for qa in context.qa_responses
|
for qa in context.qa_responses
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
if context.initial_context:
|
||||||
|
context_data["initial_context"] = context.initial_context
|
||||||
return format_as_xml(context_data, root_tag="research_context")
|
return format_as_xml(context_data, root_tag="research_context")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -61,6 +63,9 @@ def format_conversational_context_for_prompt(context: ResearchContext) -> str:
|
||||||
"question": context.original_question,
|
"question": context.original_question,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if context.initial_context:
|
||||||
|
context_data["initial_context"] = context.initial_context
|
||||||
|
|
||||||
# Only include conversation_history if there are qa_responses
|
# Only include conversation_history if there are qa_responses
|
||||||
if context.qa_responses:
|
if context.qa_responses:
|
||||||
context_data["conversation_history"] = [
|
context_data["conversation_history"] = [
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,17 @@ def test_chat_session_state():
|
||||||
assert state.qa_history == []
|
assert state.qa_history == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_chat_agent_has_dynamic_system_prompt():
|
||||||
|
"""Test that chat agent registers a dynamic system prompt for initial_context."""
|
||||||
|
agent = create_chat_agent(Config)
|
||||||
|
# The agent should have at least one system prompt function registered
|
||||||
|
# (the add_initial_context function)
|
||||||
|
assert len(agent._system_prompt_functions) >= 1
|
||||||
|
# Verify it's the add_initial_context function
|
||||||
|
func_names = [r.function.__name__ for r in agent._system_prompt_functions]
|
||||||
|
assert "add_initial_context" in func_names
|
||||||
|
|
||||||
|
|
||||||
def test_citation_info():
|
def test_citation_info():
|
||||||
"""Test CitationInfo model."""
|
"""Test CitationInfo model."""
|
||||||
citation = CitationInfo(
|
citation = CitationInfo(
|
||||||
|
|
|
||||||
|
|
@ -248,3 +248,22 @@ def test_build_document_filter_escapes_quotes():
|
||||||
def test_max_qa_history_constant():
|
def test_max_qa_history_constant():
|
||||||
"""Test MAX_QA_HISTORY constant value."""
|
"""Test MAX_QA_HISTORY constant value."""
|
||||||
assert MAX_QA_HISTORY == 50
|
assert MAX_QA_HISTORY == 50
|
||||||
|
|
||||||
|
|
||||||
|
def test_chat_session_state_initial_context():
|
||||||
|
"""Test ChatSessionState accepts initial_context."""
|
||||||
|
from haiku.rag.agents.chat.state import ChatSessionState
|
||||||
|
|
||||||
|
state = ChatSessionState(
|
||||||
|
session_id="test-session",
|
||||||
|
initial_context="This is background knowledge about the topic.",
|
||||||
|
)
|
||||||
|
assert state.initial_context == "This is background knowledge about the topic."
|
||||||
|
|
||||||
|
|
||||||
|
def test_chat_session_state_initial_context_defaults_to_none():
|
||||||
|
"""Test ChatSessionState initial_context defaults to None."""
|
||||||
|
from haiku.rag.agents.chat.state import ChatSessionState
|
||||||
|
|
||||||
|
state = ChatSessionState(session_id="test-session")
|
||||||
|
assert state.initial_context is None
|
||||||
|
|
|
||||||
|
|
@ -44,3 +44,62 @@ async def test_graph_end_to_end(allow_model_requests, temp_db_path, qa_corpus):
|
||||||
assert result.executive_summary
|
assert result.executive_summary
|
||||||
|
|
||||||
client.close()
|
client.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_research_context_initial_context():
|
||||||
|
"""Test ResearchContext accepts initial_context."""
|
||||||
|
context = ResearchContext(
|
||||||
|
original_question="What is X?",
|
||||||
|
initial_context="Background: X is a concept in domain Y.",
|
||||||
|
)
|
||||||
|
assert context.initial_context == "Background: X is a concept in domain Y."
|
||||||
|
|
||||||
|
|
||||||
|
def test_research_context_initial_context_defaults_to_none():
|
||||||
|
"""Test ResearchContext initial_context defaults to None."""
|
||||||
|
context = ResearchContext(original_question="What is X?")
|
||||||
|
assert context.initial_context is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_context_for_prompt_includes_initial_context():
|
||||||
|
"""Test format_context_for_prompt includes initial_context in output."""
|
||||||
|
from haiku.rag.agents.research.graph import format_context_for_prompt
|
||||||
|
|
||||||
|
context = ResearchContext(
|
||||||
|
original_question="What is X?",
|
||||||
|
initial_context="Background: X is a concept in domain Y.",
|
||||||
|
)
|
||||||
|
result = format_context_for_prompt(context)
|
||||||
|
assert "Background: X is a concept in domain Y." in result
|
||||||
|
assert "initial_context" in result
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_context_for_prompt_excludes_initial_context_when_none():
|
||||||
|
"""Test format_context_for_prompt excludes initial_context when None."""
|
||||||
|
from haiku.rag.agents.research.graph import format_context_for_prompt
|
||||||
|
|
||||||
|
context = ResearchContext(original_question="What is X?")
|
||||||
|
result = format_context_for_prompt(context)
|
||||||
|
assert "initial_context" not in result
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_conversational_context_for_prompt_includes_initial_context():
|
||||||
|
"""Test format_conversational_context_for_prompt includes initial_context."""
|
||||||
|
from haiku.rag.agents.research.graph import format_conversational_context_for_prompt
|
||||||
|
|
||||||
|
context = ResearchContext(
|
||||||
|
original_question="What is X?",
|
||||||
|
initial_context="Background: X is a concept in domain Y.",
|
||||||
|
)
|
||||||
|
result = format_conversational_context_for_prompt(context)
|
||||||
|
assert "Background: X is a concept in domain Y." in result
|
||||||
|
assert "initial_context" in result
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_conversational_context_for_prompt_excludes_initial_context_when_none():
|
||||||
|
"""Test format_conversational_context_for_prompt excludes initial_context when None."""
|
||||||
|
from haiku.rag.agents.research.graph import format_conversational_context_for_prompt
|
||||||
|
|
||||||
|
context = ResearchContext(original_question="What is X?")
|
||||||
|
result = format_conversational_context_for_prompt(context)
|
||||||
|
assert "initial_context" not in result
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue