Allow multiple tool calling rounds in QA agent

This commit is contained in:
Yiorgis Gozadinos 2025-07-08 12:51:58 +03:00
parent bea96f1b1b
commit 2f261a33f5
No known key found for this signature in database
5 changed files with 151 additions and 160 deletions

View file

@ -37,6 +37,9 @@ try:
messages: list[MessageParam] = [{"role": "user", "content": question}] messages: list[MessageParam] = [{"role": "user", "content": question}]
max_rounds = 5 # Prevent infinite loops
for _ in range(max_rounds):
response = await anthropic_client.messages.create( response = await anthropic_client.messages.create(
model=self._model, model=self._model,
max_tokens=4096, max_tokens=4096,
@ -88,25 +91,16 @@ try:
if tool_results: if tool_results:
messages.append({"role": "user", "content": tool_results}) messages.append({"role": "user", "content": tool_results})
else:
final_response = await anthropic_client.messages.create( # No tool use, return the response
model=self._model,
max_tokens=4096,
system=self._system_prompt,
messages=messages,
temperature=0.0,
)
if final_response.content:
first_content = final_response.content[0]
if isinstance(first_content, TextBlock):
return first_content.text
return ""
if response.content: if response.content:
first_content = response.content[0] first_content = response.content[0]
if isinstance(first_content, TextBlock): if isinstance(first_content, TextBlock):
return first_content.text return first_content.text
return "" return ""
# If we've exhausted max rounds, return empty string
return ""
except ImportError: except ImportError:
pass pass

View file

@ -14,14 +14,14 @@ class QuestionAnswerOllamaAgent(QuestionAnswerAgentBase):
async def answer(self, question: str) -> str: async def answer(self, question: str) -> str:
ollama_client = AsyncClient(host=Config.OLLAMA_BASE_URL) ollama_client = AsyncClient(host=Config.OLLAMA_BASE_URL)
# Define the search tool
messages = [ messages = [
{"role": "system", "content": self._system_prompt}, {"role": "system", "content": self._system_prompt},
{"role": "user", "content": question}, {"role": "user", "content": question},
] ]
# Initial response with tool calling max_rounds = 5 # Prevent infinite loops
for _ in range(max_rounds):
response = await ollama_client.chat( response = await ollama_client.chat(
model=self._model, model=self._model,
messages=messages, messages=messages,
@ -31,6 +31,8 @@ class QuestionAnswerOllamaAgent(QuestionAnswerAgentBase):
) )
if response.get("message", {}).get("tool_calls"): if response.get("message", {}).get("tool_calls"):
messages.append(response["message"])
for tool_call in response["message"]["tool_calls"]: for tool_call in response["message"]["tool_calls"]:
if tool_call["function"]["name"] == "search_documents": if tool_call["function"]["name"] == "search_documents":
args = tool_call["function"]["arguments"] args = tool_call["function"]["arguments"]
@ -47,7 +49,6 @@ class QuestionAnswerOllamaAgent(QuestionAnswerAgentBase):
context = "\n\n".join(context_chunks) context = "\n\n".join(context_chunks)
messages.append(response["message"])
messages.append( messages.append(
{ {
"role": "tool", "role": "tool",
@ -55,13 +56,9 @@ class QuestionAnswerOllamaAgent(QuestionAnswerAgentBase):
"tool_call_id": tool_call.get("id", "search_tool"), "tool_call_id": tool_call.get("id", "search_tool"),
} }
) )
final_response = await ollama_client.chat(
model=self._model,
messages=messages,
think=False,
options=OLLAMA_OPTIONS,
)
return final_response["message"]["content"]
else: else:
# No tool calls, return the response
return response["message"]["content"] return response["message"]["content"]
# If we've exhausted max rounds, return empty string
return ""

View file

@ -24,8 +24,6 @@ try:
async def answer(self, question: str) -> str: async def answer(self, question: str) -> str:
openai_client = AsyncOpenAI() openai_client = AsyncOpenAI()
# Define the search tool
messages: list[ChatCompletionMessageParam] = [ messages: list[ChatCompletionMessageParam] = [
ChatCompletionSystemMessageParam( ChatCompletionSystemMessageParam(
role="system", content=self._system_prompt role="system", content=self._system_prompt
@ -33,7 +31,9 @@ try:
ChatCompletionUserMessageParam(role="user", content=question), ChatCompletionUserMessageParam(role="user", content=question),
] ]
# Initial response with tool calling max_rounds = 5 # Prevent infinite loops
for _ in range(max_rounds):
response = await openai_client.chat.completions.create( response = await openai_client.chat.completions.create(
model=self._model, model=self._model,
messages=messages, messages=messages,
@ -70,7 +70,9 @@ try:
query = args.get("query", question) query = args.get("query", question)
limit = int(args.get("limit", 3)) limit = int(args.get("limit", 3))
search_results = await self._client.search(query, limit=limit) search_results = await self._client.search(
query, limit=limit
)
context_chunks = [] context_chunks = []
for chunk, score in search_results: for chunk, score in search_results:
@ -87,15 +89,12 @@ try:
tool_call_id=tool_call.id, tool_call_id=tool_call.id,
) )
) )
final_response = await openai_client.chat.completions.create(
model=self._model,
messages=messages,
temperature=0.0,
)
return final_response.choices[0].message.content or ""
else: else:
# No tool calls, return the response
return response_message.content or "" return response_message.content or ""
# If we've exhausted max rounds, return empty string
return ""
except ImportError: except ImportError:
pass pass

View file

@ -5,7 +5,8 @@ Your process:
1. When a user asks a question, use the search_documents tool to find relevant information 1. When a user asks a question, use the search_documents tool to find relevant information
2. Search with specific keywords and phrases from the user's question 2. Search with specific keywords and phrases from the user's question
3. Review the search results and their relevance scores 3. Review the search results and their relevance scores
4. Provide a comprehensive answer based only on the retrieved documents 4. If you need additional context, perform follow-up searches with different keywords
5. Provide a comprehensive answer based only on the retrieved documents
Guidelines: Guidelines:
- Base your answers strictly on the provided document content - Base your answers strictly on the provided document content
@ -15,5 +16,5 @@ Guidelines:
- If the retrieved documents don't contain sufficient information, clearly state: "I cannot find enough information in the knowledge base to answer this question." - If the retrieved documents don't contain sufficient information, clearly state: "I cannot find enough information in the knowledge base to answer this question."
- For complex questions, consider breaking them down and performing multiple searches - For complex questions, consider breaking them down and performing multiple searches
Be thorough but concise, and always maintain accuracy over completeness. Be concise, and always maintain accuracy over completeness. Prefer short, direct answers that are well-supported by the documents.
""" """