4.1.6. Retriever
We design a simple retriever containing only a run method which encompasses the planning step and a while loop to execute each tool call that the orchestrator makes before returning a final answer.
python
import json
class MultiStepRetriever:
"""Retrieve information in multiple steps using an OpenAI client."""
def __init__(self, client: AsyncOpenAI):
self.client = client
# This helps us simplify our tool calling functionality in run()
self.function_map = {
"factual_qa": factual_qa,
"trend_analysis": trend_analysis
}
async def run(self, user_question: str) -> tuple[str, dict]:
"""Run the multi-step retrieval process for a user question."""
# -------------------------------------------------------
# Step 1: Generate initial plan
# -------------------------------------------------------
initial_plan = await initial_planner(user_question=user_question)
# -------------------------------------------------------
# Step 2: Make initial model call
# -------------------------------------------------------
retriever_user_prompt = (
"You are a helpful assistant. "
"You are provided with a user question: \n\n"
f"{user_question} \n\n"
"You have access to a set of tools. You may choose to use these tools to retrieve information to "
"help you answer the user's question. These tools allow you to query a knowledge graph that contains "
"information that has been extracted from companies' earnings call transcripts. "
"You should not use your own memory of these companies to answer questions. "
"When returning an answer to the user, all of your content must be derived from the content "
"you have retrieved from the tools used. This is to ensure that is is accurate, as the data in "
"this knowledge graph has been carefully check to ensure its accuracy. The knowledge graph contains "
"data spanning from 2016-2020. \n\n"
"You are provided with a plan of action as follows: \n"
f"{initial_plan} \n\n"
"You should generally stick to this plan to help you answer the question, though you may deviate "
"from it should you deem it suitable. You may make more than one tool call."
)
input_messages = [
{"role":"user", "content":retriever_user_prompt}
]
response = await self.client.responses.create(
model="gpt-4.1",
input=input_messages,
tools=tools,
parallel_tool_calls=False,
)
# -------------------------------------------------------
# Step 3: While loop until no more tool calls are made
# -------------------------------------------------------
tools_used = {}
while response.output[0].type == "function_call":
tool_call = response.output[0]
args = json.loads(tool_call.arguments)
name = tool_call.name
if name in self.function_map:
tool_func = self.function_map[name]
tool_response_text = await tool_func(**args)
input_messages.append(tool_call)
input_messages.append({
"type": "function_call_output",
"call_id": tool_call.call_id,
"output": tool_response_text
})
tools_used[name] = [args, tool_response_text]
response = await self.client.responses.create(
model="gpt-4.1",
input=input_messages,
tools=tools,
parallel_tool_calls=False
)
return response.output_text, tools_usedWe can now run our MultiStepRetriever.
We observe that the answer returned is detailed, and includes a detailed walkthrough of how AMD's research priorities evolved from 2016 to 2020, with references to the underlying quotes that were used to derive these answers.
python
retriever = MultiStepRetriever(client=client)
answer, tools_used = await retriever.run(user_question="How have AMD's research & development priorities changed over time?")
print(answer)We can also inspect the tools used by our MultiStepRetriever to answer this query.
python
for key, value in tools_used.items():
if value:
print(f"{key}: {value[0]}")
else:
print(f"{key}: [empty list]")Appendix section A.5, "Scaling and Productionizing our Retrieval Agent" outlines guidelines for taking the Retrieval Agent we've built into production.