serverdaun commited on
Commit
ac640bc
·
1 Parent(s): fb1de84

add research agent and use it as research manager

Browse files
Files changed (2) hide show
  1. src/research_agent.py +51 -0
  2. src/research_manager.py +13 -73
src/research_agent.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agents import Agent, ModelSettings
2
+
3
+ from planner import planner_agent
4
+ from report_generator import ReportData, writer_agent
5
+ from web_search import search_agent
6
+
7
+ INSTRUCTIONS = (
8
+ "You are a senior research orchestrator tasked with answering complex user questions by calling the "
9
+ "appropriate tools. You have access to the following tools:\n\n"
10
+ "1. planner_agent(input: str) -> WebSearchPlan - Produce up to five web-search queries that, when executed, "
11
+ "will help address the user's request.\n"
12
+ "3. search_agent(input: str) -> str - Run a single web search and return a concise summary of the results.\n"
13
+ "4. writer_agent(input: str) -> ReportData - Synthesise the final markdown report. This **must** be the last "
14
+ "tool you call; treat its output as your final hand-off to the user.\n\n"
15
+ "Workflow guidelines:\n"
16
+ "> Think step-by-step and decide which tool to invoke next. \n"
17
+ "> After gathering clarifications (if any), call planner_agent once to devise your search strategy.\n"
18
+ "> Next, iterate through search_agent calls to collect evidence. You may call search_agent multiple times to "
19
+ "cover each planned search term.\n"
20
+ "> If, after reviewing the results, you believe the information is still insufficient, you may make additional "
21
+ "calls to planner_agent or search_agent. LIMIT yourself to a maximum of **two** extra tool calls beyond your "
22
+ "initial plan/execution.\n"
23
+ "> Once satisfied, call writer_agent **exactly once** and return its markdown_report to the user as your final "
24
+ "answer.\n"
25
+ "> Never reveal chain-of-thought or internal reasoning. Keep your responses concise and focused on using the "
26
+ "tools effectively.\n\n"
27
+ "Remember: you have at most two extra tool invocations if you are unhappy with the intermediate results. Choose "
28
+ "them wisely."
29
+ )
30
+
31
+ planner_tool = planner_agent.as_tool(
32
+ tool_name="planner_agent",
33
+ tool_description="Produce up to five web-search queries that, when executed, will help address the user's request.",
34
+ )
35
+ web_search_tool = search_agent.as_tool(
36
+ tool_name="search_agent",
37
+ tool_description="Run a single web search and return a concise summary of the results.",
38
+ )
39
+ writer_tool = writer_agent.as_tool(
40
+ tool_name="writer_agent", tool_description="Synthesise the markdown report."
41
+ )
42
+ tools = [planner_tool, web_search_tool, writer_tool]
43
+
44
+ research_agent = Agent(
45
+ name="Research Agent",
46
+ instructions=INSTRUCTIONS,
47
+ tools=tools,
48
+ model="gpt-4o-mini",
49
+ model_settings=ModelSettings(tool_choice="auto"),
50
+ output_type=ReportData,
51
+ )
src/research_manager.py CHANGED
@@ -1,90 +1,30 @@
1
- import asyncio
2
-
3
  from agents import Runner, gen_trace_id, trace
4
 
5
  from clarifier import ClarifyingQuestions, clarifier_agent
6
- from planner import WebSearchItem, WebSearchPlan, planner_agent
7
- from report_generator import ReportData, writer_agent
8
- from web_search import search_agent
9
 
10
 
11
  class ResearchManager:
12
 
13
  async def run(self, query: str, clarifications: str | None = None):
14
- """Run the deep research process, yielding status updates and the final report.
15
-
16
- If *clarifications* are provided (the user's answers to the clarifying questions), we will use them to
17
- augment the planning and reporting stages. Otherwise this behaves exactly like the previous implementation.
18
- """
19
  trace_id = gen_trace_id()
20
  with trace("Research trace", trace_id=trace_id):
21
- print("Starting research...")
22
-
23
- # Combine the original query with any clarification the user has supplied.
24
- if clarifications:
25
- combined_query = (
26
- f"Original query: {query}\n\nUser clarifications:\n{clarifications}"
27
- )
28
- else:
29
- combined_query = query
30
-
31
- search_plan = await self.plan_searches(combined_query)
32
- yield "Searches planned, starting to search..."
33
- search_results = await self.perform_searches(search_plan)
34
- yield "Searches complete, writing report..."
35
- report = await self.write_report(combined_query, search_results)
36
- yield report.markdown_report
37
 
38
- async def plan_searches(self, query: str) -> WebSearchPlan:
39
- """Plan the searches to perform for the query"""
40
- print("Planning searches...")
41
- result = await Runner.run(
42
- planner_agent,
43
- f"Query: {query}",
44
- )
45
- print(f"Will perform {len(result.final_output.searches)} searches")
46
- return result.final_output_as(WebSearchPlan)
47
-
48
- async def perform_searches(self, search_plan: WebSearchPlan) -> list[str]:
49
- """Perform the searches to perform for the query"""
50
- print("Searching...")
51
- num_completed = 0
52
- tasks = [
53
- asyncio.create_task(self.search(item)) for item in search_plan.searches
54
- ]
55
- results = []
56
- for task in asyncio.as_completed(tasks):
57
- result = await task
58
- if result is not None:
59
- results.append(result)
60
- num_completed += 1
61
- print(f"Searching... {num_completed}/{len(tasks)} completed")
62
- print("Finished searching")
63
- return results
64
-
65
- async def search(self, item: WebSearchItem) -> str | None:
66
- """Perform a search for the query"""
67
- input = f"Search term: {item.query}\nReason for searching: {item.reason}"
68
- try:
69
- result = await Runner.run(
70
- search_agent,
71
- input,
72
  )
73
- return str(result.final_output)
74
- except Exception:
75
- return None
76
 
77
- async def write_report(self, query: str, search_results: list[str]) -> ReportData:
78
- """Write the report for the query"""
79
- print("Thinking about report...")
80
- input = f"{query}\nSummarized search results: {search_results}"
81
- result = await Runner.run(
82
- writer_agent,
83
- input,
84
- )
85
 
86
- print("Finished writing report")
87
- return result.final_output_as(ReportData)
88
 
89
  async def get_clarifying_questions(self, query: str) -> list[str]:
90
  """Generate clarifying questions for a given query."""
 
 
 
1
  from agents import Runner, gen_trace_id, trace
2
 
3
  from clarifier import ClarifyingQuestions, clarifier_agent
4
+ from report_generator import ReportData
5
+ from research_agent import research_agent
 
6
 
7
 
8
  class ResearchManager:
9
 
10
  async def run(self, query: str, clarifications: str | None = None):
11
+ """Run the deep-research pipeline using `research_agent`."""
 
 
 
 
12
  trace_id = gen_trace_id()
13
  with trace("Research trace", trace_id=trace_id):
14
+ print("Starting research with ResearchAgent…")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ combined_query = (
17
+ f"Original query: {query}\n\nUser clarifications:\n{clarifications}"
18
+ if clarifications
19
+ else query
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  )
21
+ yield "Research in progress…"
 
 
22
 
23
+ result = await Runner.run(research_agent, combined_query)
24
+ report: ReportData = result.final_output_as(ReportData)
 
 
 
 
 
 
25
 
26
+ yield "Report generated. Rendering markdown…"
27
+ yield report.markdown_report
28
 
29
  async def get_clarifying_questions(self, query: str) -> list[str]:
30
  """Generate clarifying questions for a given query."""