diff --git a/authors.yaml b/authors.yaml index e3779b7812..0a576cd229 100644 --- a/authors.yaml +++ b/authors.yaml @@ -376,3 +376,8 @@ alexl-oai: name: "Alex Lowden" website: "https://www.linkedin.com/in/alex-lowden01/" avatar: "https://avatars.githubusercontent.com/u/215167546" + +glojain: + name: "Glory Jain" + website: "https://www.linkedin.com/in/gloryjain/" + avatar: "https://media.licdn.com/dms/image/v2/C4E03AQH72n6Sm5q69Q/profile-displayphoto-shrink_400_400/profile-displayphoto-shrink_400_400/0/1557995338725?e=1756339200&v=beta&t=FGTXiCZwTZvqHCY-wd8It15EDf11Rex1oLlBKRGHNtY" \ No newline at end of file diff --git a/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/README.md b/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/README.md new file mode 100644 index 0000000000..46f0f45bbb --- /dev/null +++ b/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/README.md @@ -0,0 +1,123 @@ +# MCP for Deep Research + +This is a minimal example of a Deep Research style MCP server for searching and fetching files from the OpenAI file storage service. + +For a reference of _how_ to call this service from the Responses API, with Deep Research see [this cookbook](https://cookbook.openai.com/examples/deep_research_api/introduction_to_deep_research_api). To see how to call the MCP server with the Agents SDK, checkout [this cookbook](https://cookbook.openai.com/examples/deep_research_api/how_to_use_deep_research_API_agents)! + +The Deep Research agent relies specifically on Search and Fetch tools. Search should look through your object store for a set of specfic, top-k IDs. Fetch, is a tool that takes objectIds as arguments and pulls back the relevant resources. + +## Set up & run + +Store your internal file(s) in [OpenAI Vector Storage](https://platform.openai.com/storage/vector_stores/) + +Python setup: + +```shell +python3 -m venv env +source env/bin/activate +pip install -r requirements.txt +``` + +Run the server: + +```shell +python main.py +``` + +The server will start on `http://0.0.0.0:8000/sse/` using SSE transport. If you want to reach the server from the public internet, there are a variety of ways to do that including with ngrok: + +```shell +brew install ngrok +ngrok config add-authtoken +ngrok http 8000 +``` + +You should now be able to reach your local server from your client. + +## Files + +- `main.py`: Main server code + +## Example Flow diagram for MCP Server + +```mermaid +flowchart TD + subgraph Connection_Setup + A1[MCP Server starts up
listening on /sse/] --> A2[Client opens SSE connection] + A2 --> A3[Server confirms SSE connection] + end + + subgraph Tool_Discovery + A3 --> B1[Client asks 'What tools do you support?'] + B1 --> B2[Server replies with Search & Fetch schemas] + B2 --> B3[Client stores schemas in context] + end + + subgraph Search_Fetch_Loop + B3 --> C1[Client issues search call] + C1 --> C2[MCP Server routes to Search Tool] + C2 --> C3[Search Tool queries Data Store
returns one hit] + C3 --> C4[Client issues fetch call] + C4 --> C5[MCP Server routes to Fetch Tool] + C5 --> C6[Fetch Tool retrieves document text] + C6 --> C7[Client refines/repeats search
cost-effectiveness, market revenue…] + C7 --> C1 + end +``` + +## Example request + +```python +# system_message includes reference to internal file lookups for MCP. +system_message = """ +You are a professional researcher preparing a structured, data-driven report on behalf of a global health economics team. Your task is to analyze the health question the user poses. + +Do: +- Focus on data-rich insights: include specific figures, trends, statistics, and measurable outcomes (e.g., reduction in hospitalization costs, market size, pricing trends, payer adoption). +- When appropriate, summarize data in a way that could be turned into charts or tables, and call this out in the response (e.g., "this would work well as a bar chart comparing per-patient costs across regions"). +- Prioritize reliable, up-to-date sources: peer-reviewed research, health organizations (e.g., WHO, CDC), regulatory agencies, or pharmaceutical earnings reports. +- Include an internal file lookup tool to retrieve information from our own internal data sources. If you've already retrieved a file, do not call fetch again for that same file. Prioritize inclusion of that data. +- Include inline citations and return all source metadata. + +Be analytical, avoid generalities, and ensure that each section supports data-backed reasoning that could inform healthcare policy or financial modeling. +""" + +user_query = "Research the economic impact of semaglutide on global healthcare systems." + +response = client.responses.create( + model="o3-deep-research-2025-06-26", + input=[ + { + "role": "developer", + "content": [ + { + "type": "input_text", + "text": system_message, + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "input_text", + "text": user_query, + } + ] + } + ], + reasoning={ + "summary": "auto" + }, + tools=[ + { + "type": "web_search_preview" + }, + { # ADD MCP TOOL SUPPORT + "type": "mcp", + "server_label": "internal_file_lookup", + "server_url": "http://0.0.0.0:8000/sse/", # Update to the location of *your* MCP server + "require_approval": "never" + } + ] +) \ No newline at end of file diff --git a/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/main.py b/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/main.py new file mode 100644 index 0000000000..6a6084cfe6 --- /dev/null +++ b/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/main.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 +""" +Sample MCP Server for Deep Research API Integration + +This server implements the Model Context Protocol (MCP) with search and fetch +capabilities designed to work with ChatGPT's deep research feature. +""" + +import logging +from typing import Dict, List, Any +from fastmcp import FastMCP +from openai import OpenAI + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# OpenAI configuration +OPENAI_API_KEY = "" +VECTOR_STORE_ID = "" #OpenAI Vector Store ID https://platform.openai.com/storage/vector_stores/ + +# Initialize OpenAI client +openai_client = OpenAI(api_key=OPENAI_API_KEY) if OPENAI_API_KEY else None + +# No local data storage needed - using OpenAI Vector Store only + + +def create_server(): + """Create and configure the MCP server with search and fetch tools.""" + + # Initialize the FastMCP server + mcp = FastMCP(name="Sample Deep Research MCP Server", + instructions=""" + This MCP server provides search and document retrieval capabilities for deep research. + Use the search tool to find relevant documents based on keywords, then use the fetch + tool to retrieve complete document content with citations. + """) + + @mcp.tool() + async def search(query: str) -> Dict[str, List[Dict[str, Any]]]: + """ + Search for documents using OpenAI Vector Store search. + + This tool searches through the vector store to find semantically relevant matches. + Returns a list of search results with basic information. Use the fetch tool to get + complete document content. + + Args: + query: Search query string. Natural language queries work best for semantic search. + + Returns: + Dictionary with 'results' key containing list of matching documents. + Each result includes id, title, text snippet, and optional URL. + """ + if not query or not query.strip(): + return {"results": []} + + if not openai_client: + logger.error("OpenAI client not initialized - API key missing") + raise ValueError( + "OpenAI API key is required for vector store search") + + # Search the vector store using OpenAI API + logger.info( + f"Searching vector store {VECTOR_STORE_ID} for query: '{query}'") + + response = openai_client.vector_stores.search( + vector_store_id=VECTOR_STORE_ID, query=query) + + results = [] + + # Process the vector store search results + if hasattr(response, 'data') and response.data: + for i, item in enumerate(response.data): + # Extract file_id, filename, and content from the VectorStoreSearchResponse + item_id = getattr(item, 'file_id', f"vs_{i}") + item_filename = getattr(item, 'filename', f"Document {i+1}") + + # Extract text content from the content array + content_list = getattr(item, 'content', []) + text_content = "" + if content_list and len(content_list) > 0: + # Get text from the first content item + first_content = content_list[0] + if hasattr(first_content, 'text'): + text_content = first_content.text + elif isinstance(first_content, dict): + text_content = first_content.get('text', '') + + if not text_content: + text_content = "No content available" + + # Create a snippet from content + text_snippet = text_content[:200] + "..." if len( + text_content) > 200 else text_content + + result = { + "id": item_id, + "title": item_filename, + "text": text_snippet, + "url": f"https://platform.openai.com/storage/files/{item_id}" + } + + results.append(result) + + logger.info(f"Vector store search returned {len(results)} results") + return {"results": results} + + @mcp.tool() + async def fetch(id: str) -> Dict[str, Any]: + """ + Retrieve complete document content by ID for detailed analysis and citation. + + This tool fetches the full document content from OpenAI Vector Store or local storage. + Use this after finding relevant documents with the search tool to get complete + information for analysis and proper citation. + + Args: + id: File ID from vector store (file-xxx) or local document ID + + Returns: + Complete document with id, title, full text content, optional URL, and metadata + + Raises: + ValueError: If the specified ID is not found + """ + if not id: + raise ValueError("Document ID is required") + + if not openai_client: + logger.error("OpenAI client not initialized - API key missing") + raise ValueError( + "OpenAI API key is required for vector store file retrieval") + + logger.info(f"Fetching content from vector store for file ID: {id}") + + # Fetch file content from vector store + content_response = openai_client.vector_stores.files.content( + vector_store_id=VECTOR_STORE_ID, file_id=id) + + # Get file metadata + file_info = openai_client.vector_stores.files.retrieve( + vector_store_id=VECTOR_STORE_ID, file_id=id) + + # Extract content from paginated response + file_content = "" + if hasattr(content_response, 'data') and content_response.data: + # Combine all content chunks from FileContentResponse objects + content_parts = [] + for content_item in content_response.data: + if hasattr(content_item, 'text'): + content_parts.append(content_item.text) + file_content = "\n".join(content_parts) + else: + file_content = "No content available" + + # Use filename as title and create proper URL for citations + filename = getattr(file_info, 'filename', f"Document {id}") + + result = { + "id": id, + "title": filename, + "text": file_content, + "url": f"https://platform.openai.com/storage/files/{id}", + "metadata": None + } + + # Add metadata if available from file info + if hasattr(file_info, 'attributes') and file_info.attributes: + result["metadata"] = file_info.attributes + + logger.info(f"Successfully fetched vector store file: {id}") + return result + + return mcp + + +def main(): + """Main function to start the MCP server.""" + # Verify OpenAI client is initialized + if not openai_client: + logger.error( + "OpenAI API key not found. Please set OPENAI_API_KEY environment variable." + ) + raise ValueError("OpenAI API key is required") + + logger.info(f"Using vector store: {VECTOR_STORE_ID}") + + # Create the MCP server + server = create_server() + + # Configure and start the server + logger.info("Starting MCP server on 0.0.0.0:8000") + logger.info("Server will be accessible via SSE transport") + logger.info("Connect this server to ChatGPT Deep Research for testing") + + try: + # Use FastMCP's built-in run method with SSE transport + server.run(transport="sse", host="0.0.0.0", port=8000) + except KeyboardInterrupt: + logger.info("Server stopped by user") + except Exception as e: + logger.error(f"Server error: {e}") + raise + + +if __name__ == "__main__": + main() diff --git a/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/requirements.txt b/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/requirements.txt new file mode 100644 index 0000000000..2dd22aeab4 --- /dev/null +++ b/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/requirements.txt @@ -0,0 +1,15 @@ +# Core dependencies for the Deep Research MCP Server +fastmcp>=2.9.0 +openai>=1.88.0 +uvicorn>=0.34.3 + +# Additional dependencies that may be required +pydantic>=2.0.0 +typing-extensions>=4.0.0 +httpx>=0.23.0 +python-multipart>=0.0.9 +sse-starlette>=1.6.1 +starlette>=0.27.0 + +# Optional but recommended for production +python-dotenv>=1.0.0 diff --git a/examples/deep_research_api/introduction_to_deep_research_api.ipynb b/examples/deep_research_api/introduction_to_deep_research_api.ipynb new file mode 100644 index 0000000000..030d644b21 --- /dev/null +++ b/examples/deep_research_api/introduction_to_deep_research_api.ipynb @@ -0,0 +1,751 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "PrEXW8RfyIWT" + }, + "source": [ + "# Introduction to the Deep Research API\n", + "\n", + "## Background\n", + "\n", + "The Deep Research API enables you to automate complex research workflows that require reasoning, planning, and synthesis across real-world information. It is designed to take a high-level query and return a structured, citation-rich report by leveraging an agentic model capable of decomposing the task, performing web searches, and synthesizing results.\n", + "\n", + "Unlike ChatGPT where this process is abstracted away, the API provides direct programmatic access. When you send a request, the model autonomously plans sub-questions, uses tools like web search and code execution, and produces a final structured response. This cookbook will provide a brief introduction to the Deep Research API and how to use it.\n", + "\n", + "You can access Deep Research via the `responses` endpoint using the following models:\n", + "\n", + "- `o3-deep-research-2025-06-26`: Optimized for in-depth synthesis and higher-quality output \n", + "- `o4-mini-deep-research-2025-06-26`: Lightweight and faster, ideal for latency-sensitive use cases" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xBNu6irQyIWV" + }, + "source": [ + "## Setup\n", + "\n", + "### Install requirements\n", + "Install the latest version of the OpenAI Python SDK." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7wLQixR4yIWV" + }, + "outputs": [], + "source": [ + "!pip install --upgrade openai" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9SGiGt_byIWW" + }, + "source": [ + "### Authenticate\n", + "Import the OpenAI client and initialize with your API key." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "oStGzoHQyIWW" + }, + "outputs": [], + "source": [ + "from openai import OpenAI\n", + "OPENAI_API_KEY=\"\" # YOUR OPENAI_API_KEY\n", + "#OPENAI_API_KEY = os.environ.get(\"OPENAI_API_KEY\")\n", + "client = OpenAI(api_key=OPENAI_API_KEY)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BHi7r3PSyIWW" + }, + "source": [ + "## Getting started\n", + "\n", + "Let’s walk through an example of a Deep Research API call. Imagine we’re working at a healthcare financial services firm tasked with producing an in-depth report on the economic implications of recent medications used to treat type 2 diabetes and obesity—particularly semaglutide. Our goal is to synthesize clinical outcomes, cost-effectiveness, and regional pricing data into a structured, citation-backed analysis that could inform investment, payer strategy, or policy recommendations.\n", + "\n", + "To get started, let's:\n", + "- Put our role in the system message, outlining what type of report we'd like to generate\n", + "- Set the summary paramter to \"auto\" for now for the best available summary. (If you'd like for your report to more detailed, you can set summary to detailed)\n", + "- Include the required tool web_search_preview and optionally add code_interpreter.\n", + "- Set the background parameter to True. Since a Deep Research task can take several minutes to execute, enabling background mode will allow you to run the request asynchronously without having to worry about timeouts or other connectivity issues." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "a2BW00TpyIWX" + }, + "outputs": [], + "source": [ + "system_message = \"\"\"\n", + "You are a professional researcher preparing a structured, data-driven report on behalf of a global health economics team. Your task is to analyze the health question the user poses.\n", + "\n", + "Do:\n", + "- Focus on data-rich insights: include specific figures, trends, statistics, and measurable outcomes (e.g., reduction in hospitalization costs, market size, pricing trends, payer adoption).\n", + "- When appropriate, summarize data in a way that could be turned into charts or tables, and call this out in the response (e.g., “this would work well as a bar chart comparing per-patient costs across regions”).\n", + "- Prioritize reliable, up-to-date sources: peer-reviewed research, health organizations (e.g., WHO, CDC), regulatory agencies, or pharmaceutical earnings reports.\n", + "- Include inline citations and return all source metadata.\n", + "\n", + "Be analytical, avoid generalities, and ensure that each section supports data-backed reasoning that could inform healthcare policy or financial modeling.\n", + "\"\"\"\n", + "\n", + "user_query = \"Research the economic impact of semaglutide on global healthcare systems.\"\n", + "\n", + "response = client.responses.create(\n", + " model=\"o3-deep-research\",\n", + " input=[\n", + " {\n", + " \"role\": \"developer\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"input_text\",\n", + " \"text\": system_message,\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"input_text\",\n", + " \"text\": user_query,\n", + " }\n", + " ]\n", + " }\n", + " ],\n", + " reasoning={\n", + " \"summary\": \"auto\"\n", + " },\n", + " tools=[\n", + " {\n", + " \"type\": \"web_search_preview\"\n", + " },\n", + " {\n", + " \"type\": \"code_interpreter\",\n", + " \"container\": {\n", + " \"type\": \"auto\",\n", + " \"file_ids\": []\n", + " }\n", + " }\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ODuaGA3tyIWX" + }, + "source": [ + "## Parse the Response\n", + "\n", + "The Deep Research API response includes a structured final answer along with inline citations, summaries of the reasoning steps, and source metadata.\n", + "\n", + "### Extract the Final Report Output\n", + "\n", + "Here's the main text output of this report.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "q1Yq8iFoyIWX" + }, + "outputs": [], + "source": [ + "# Access the final report from the response object\n", + "print(response.output[-1].content[0].text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FT9bWGq9yIWX" + }, + "source": [ + "### Access Inline Citations and Metadata\n", + "Inline citations in the response text are annotated and linked to their corresponding source metadata. Each annotation contains:\n", + "- start_index and end_index: the character span in the text the citation refers to\n", + "- title: a brief title of the source\n", + "- url: the full source URL\n", + "\n", + "This structure will allow you to build a citation list or bibliography, add clickable hyperlinks in downstream apps, and highlight & trace data-backed claims in your report." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "hPej_N9CyIWY" + }, + "outputs": [], + "source": [ + "annotations = response.output[-1].content[0].annotations\n", + "for i, citation in enumerate(annotations):\n", + " print(f\"Citation {i+1}:\")\n", + " print(f\" Title: {citation.title}\")\n", + " print(f\" URL: {citation.url}\")\n", + " print(f\" Location: chars {citation.start_index}–{citation.end_index}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6GxsB5iwyIWY" + }, + "source": [ + "### Inspect Intermediate Steps\n", + "The Deep Research API also exposes all intermediate steps taken by the agent, including reasoning steps, web search calls, and code executions. You can use these to debug, analyze, or visualize how the final answer was constructed.\n", + "Each intermediate step is stored in `response.output`, and the `type` field indicates what kind it is.\n", + "\n", + "#### Reasoning Step\n", + "These represent internal summaries or plans generated by the model as it reasons through sub-questions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Lbv_Ow_yyIWY" + }, + "outputs": [], + "source": [ + "# Find the first reasoning step\n", + "reasoning = next(item for item in response.output if item.type == \"reasoning\")\n", + "for s in reasoning.summary:\n", + " print(s.text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ilzzPiDVyIWY" + }, + "source": [ + "#### Web Search Call\n", + "These show what search queries were executed and can help you trace what information the model retrieved." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "4CzHgTgDyIWY" + }, + "outputs": [], + "source": [ + "# Find the first web search step\n", + "search = next(item for item in response.output if item.type == \"web_search_call\")\n", + "print(\"Query:\", search.action[\"query\"])\n", + "print(\"Status:\", search.status)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "r7FBRad0yIWY" + }, + "source": [ + "#### Code Execution\n", + "If the model used the code interpreter (e.g. for parsing data or generating charts), those steps will appear as type \"code_interpreter_call\" or similar." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HPXRm_aeyIWY" + }, + "outputs": [], + "source": [ + "# Find a code execution step (if any)\n", + "code_step = next((item for item in response.output if item.type == \"code_interpreter_call\"), None)\n", + "if code_step:\n", + " print(code_step.input)\n", + " print(code_step.output)\n", + "else:\n", + " print(\"No code execution steps found.\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VJ2vmC27Hkef" + }, + "source": [ + "#### Model Context Protocol (MCP)\n", + "\n", + "Suppose you would like to pull in your own internal documents as part of a Deep Research task. The Deep Research models and the Responses API both support MCP-based tools, so you can extend them to query your private knowledge stores or other 3rd party services.\n", + "\n", + "In the example below, we configure an MCP tool that lets Deep Research fetch your organizations internal semaglutide studies on demand. The MCP server is a proxy for the OpenAI File Storage service that automagically vectorizes your uploaded files for performant retrieval.\n", + "\n", + "If you would like to see _how_ we built this simple MCP server. Refer to [this related cookbook]()." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "I71wHbNmHlUE" + }, + "outputs": [], + "source": [ + "# system_message includes reference to internal file lookups for MCP.\n", + "system_message = \"\"\"\n", + "You are a professional researcher preparing a structured, data-driven report on behalf of a global health economics team. Your task is to analyze the health question the user poses.\n", + "\n", + "Do:\n", + "- Focus on data-rich insights: include specific figures, trends, statistics, and measurable outcomes (e.g., reduction in hospitalization costs, market size, pricing trends, payer adoption).\n", + "- When appropriate, summarize data in a way that could be turned into charts or tables, and call this out in the response (e.g., “this would work well as a bar chart comparing per-patient costs across regions”).\n", + "- Prioritize reliable, up-to-date sources: peer-reviewed research, health organizations (e.g., WHO, CDC), regulatory agencies, or pharmaceutical earnings reports.\n", + "- Include an internal file lookup tool to retrieve information from our own internal data sources. If you’ve already retrieved a file, do not call fetch again for that same file. Prioritize inclusion of that data.\n", + "- Include inline citations and return all source metadata.\n", + "\n", + "Be analytical, avoid generalities, and ensure that each section supports data-backed reasoning that could inform healthcare policy or financial modeling.\n", + "\"\"\"\n", + "\n", + "user_query = \"Research the economic impact of semaglutide on global healthcare systems.\"\n", + "\n", + "response = client.responses.create(\n", + " model=\"o3-deep-research-2025-06-26\",\n", + " input=[\n", + " {\n", + " \"role\": \"developer\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"input_text\",\n", + " \"text\": system_message,\n", + " }\n", + " ]\n", + " },\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"input_text\",\n", + " \"text\": user_query,\n", + " }\n", + " ]\n", + " }\n", + " ],\n", + " reasoning={\n", + " \"summary\": \"auto\"\n", + " },\n", + " tools=[\n", + " {\n", + " \"type\": \"web_search_preview\"\n", + " },\n", + " { # ADD MCP TOOL SUPPORT\n", + " \"type\": \"mcp\",\n", + " \"server_label\": \"internal_file_lookup\",\n", + " \"server_url\": \"https:///sse/\", # Update to the location of *your* MCP server\n", + " \"require_approval\": \"never\"\n", + " }\n", + " ]\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ufEBTxsEba4R" + }, + "source": [ + "## Reviewing your response\n", + "\n", + "First 100 characters of your Research Report, followed by Citations and MCP tool calls. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "DO_IXFt8USPk" + }, + "outputs": [], + "source": [ + "# Grab the full report text once\n", + "report_text = response.output[-1].content[0].text\n", + "\n", + "print(\"REPORT EXCERPT:\")\n", + "print(report_text[:100]) # first 100 chars\n", + "print(\"--------\")\n", + "\n", + "annotations = response.output[-1].content[0].annotations\n", + "target_url = \"https://platform.openai.com/storage/files\"\n", + "\n", + "for citation in annotations:\n", + " if citation.url.startswith(target_url):\n", + " start, end = citation.start_index, citation.end_index\n", + "\n", + " # extract exactly the cited span\n", + " excerpt = report_text[start:end]\n", + "\n", + " # extract up to 100 chars immediately before the citation\n", + " pre_start = max(0, start - 100)\n", + " preceding_txt = report_text[pre_start:start]\n", + "\n", + " print(\"MCP CITATION SAMPLE:\")\n", + " print(f\" Title: {citation.title}\")\n", + " print(f\" URL: {citation.url}\")\n", + " print(f\" Location: chars {start}–{end}\")\n", + " print(f\" Preceding: {preceding_txt!r}\")\n", + " print(f\" Excerpt: {excerpt!r}\")\n", + " break\n", + "\n", + "print(\"--------\")\n", + "\n", + "\n", + "# EXAMPLE MCP CITATION\n", + "\n", + "# REPORT EXCERPT:\n", + "# # Introduction\n", + "# Semaglutide – a glucagon-like peptide-1 (GLP-1) analogue – has rapidly become a blo\n", + "# --------\n", + "# MCP CITATION SAMPLE:\n", + "# Title: Document file-WqbCdYNqNzGuFfCAeWyZfp\n", + "# URL: https://platform.openai.com/storage/files/file-WqbCdYNqNzGuFfCAeWyZfp\n", + "# Location: chars 237–331\n", + "# Preceding: 'and obesity due to its potent clinical efficacy (often inducing ~10–15% body weight loss in trials) '\n", + "# Excerpt: '([platform.openai.com](https://platform.openai.com/storage/files/file-WqbCdYNqNzGuFfCAeWyZfp))'\n", + "\n", + "\n", + "# print the MCP tool calls\n", + "calls = [\n", + " (item.name, item.server_label, item.arguments)\n", + " for item in response.output\n", + " if item.type == \"mcp_call\" and item.arguments\n", + "]\n", + "for name, server, args in calls:\n", + " print(f\"{name}@{server} → {args}\")\n", + "print(\"--------\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9wQ3mKWjyIWY" + }, + "source": [ + "## Clarifying Questions in ChatGPT vs. the Deep Research API\n", + "If you’ve used Deep Research in ChatGPT, you may have noticed that it often asks follow-up questions after you submit a query. This is intentional: ChatGPT uses an intermediate model (like gpt-4.1) to help clarify your intent and gather more context (such as your preferences, goals, or constraints) before the research process begins. This extra step helps the system tailor its web searches and return more relevant and targeted results.\n", + "\n", + "In contrast, the Deep Research API skips this clarification step. As a developer, you can configure this processing step to rewrite the user prompt or ask a set of clarifying questions, since the model expects fully-formed prompts up front and will not ask for additional context or fill in missing information; it simply starts researching based on the input it receives.\n", + "\n", + "To get strong, reliable outputs from the API, you can use two approaches.\n", + "- Use a prompt rewriter using another lightweight model (e.g., gpt-4.1) to expand or specify user queries before passing them to the research model.\n", + "- Include all relevant details: desired scope, comparisons, metrics, regions, preferred sources, and expected output format.\n", + "\n", + "This setup gives developers full control over how research tasks are framed, but also places greater responsibility on the quality of the input prompt. Here's an example of a generic rewriting_prompt to better direct the subsequent deep research query.\n", + "\n", + "```mermaid\n", + "flowchart TD\n", + " %% Interactive ChatGPT Flow with Clarifier\n", + " subgraph ChatGPT Session\n", + " direction TB\n", + " U1([User Query])\n", + " C[Clarifier
lightweight model]\n", + " Q[Clarifying Questions]\n", + " UQ([User’s Answers])\n", + " R[Rewriter
lightweight model]\n", + " EP[Enriched Prompt]\n", + " DR[Deep Research
o3-deep-research]\n", + " O[Research Results]\n", + "\n", + " U1 --> C\n", + " C --> Q\n", + " Q --> UQ\n", + " UQ --> R\n", + " R --> EP\n", + " EP --> DR\n", + " DR --> O\n", + " end\n", + "\n", + " %% Deep Research API Flow (no built-in clarifier)\n", + " subgraph Deep Research API\n", + " direction TB\n", + " U2([User Query])\n", + " R2[Rewriter
optional]\n", + " EP2[Enriched Prompt]\n", + " DR2[Deep Research API
o3-deep-research]\n", + " O2[Research Results]\n", + "\n", + " U2 -->|include all details up-front| EP2\n", + " EP2 --> DR2\n", + " DR2 --> O2\n", + "\n", + " U2 -->|use prompt rewriter| R2\n", + " R2 --> EP2\n", + " end\n", + "```\n", + "\n", + "Here's an example of a rewriting prompt:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "USn2Qmj4yIWY" + }, + "outputs": [], + "source": [ + "suggested_rewriting_prompt = \"\"\"\n", + "You will be given a research task by a user. Your job is to produce a set of instructions for a researcher that will complete the task. Do NOT complete the task yourself, just provide instructions on how to complete it.\n", + "\n", + "GUIDELINES:\n", + "1. **Maximize Specificity and Detail**\n", + "- Include all known user preferences and explicitly list key attributes or dimensions to consider.\n", + "- It is of utmost importance that all details from the user are included in the instructions.\n", + "\n", + "2. **Fill in Unstated But Necessary Dimensions as Open-Ended**\n", + "- If certain attributes are essential for a meaningful output but the user has not provided them, explicitly state that they are open-ended or default to no specific constraint.\n", + "\n", + "3. **Avoid Unwarranted Assumptions**\n", + "- If the user has not provided a particular detail, do not invent one.\n", + "- Instead, state the lack of specification and guide the researcher to treat it as flexible or accept all possible options.\n", + "\n", + "4. **Use the First Person**\n", + "- Phrase the request from the perspective of the user.\n", + "\n", + "5. **Tables**\n", + "- If you determine that including a table will help illustrate, organize, or enhance the information in the research output, you must explicitly request that the researcher provide them.\n", + "Examples:\n", + "- Product Comparison (Consumer): When comparing different smartphone models, request a table listing each model's features, price, and consumer ratings side-by-side.\n", + "- Project Tracking (Work): When outlining project deliverables, create a table showing tasks, deadlines, responsible team members, and status updates.\n", + "- Budget Planning (Consumer): When creating a personal or household budget, request a table detailing income sources, monthly expenses, and savings goals.\n", + "Competitor Analysis (Work): When evaluating competitor products, request a table with key metrics, such as market share, pricing, and main differentiators.\n", + "\n", + "6. **Headers and Formatting**\n", + "- You should include the expected output format in the prompt.\n", + "- If the user is asking for content that would be best returned in a structured format (e.g. a report, plan, etc.), ask the researcher to format as a report with the appropriate headers and formatting that ensures clarity and structure.\n", + "\n", + "7. **Language**\n", + "- If the user input is in a language other than English, tell the researcher to respond in this language, unless the user query explicitly asks for the response in a different language.\n", + "\n", + "8. **Sources**\n", + "- If specific sources should be prioritized, specify them in the prompt.\n", + "- For product and travel research, prefer linking directly to official or primary websites (e.g., official brand sites, manufacturer pages, or reputable e-commerce platforms like Amazon for user reviews) rather than aggregator sites or SEO-heavy blogs.\n", + "- For academic or scientific queries, prefer linking directly to the original paper or official journal publication rather than survey papers or secondary summaries.\n", + "- If the query is in a specific language, prioritize sources published in that language.\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "GYDGTRDWyIWZ" + }, + "outputs": [], + "source": [ + "response = client.responses.create(\n", + " instructions=suggested_rewriting_prompt,\n", + " model=\"gpt-4.1-2025-04-14\",\n", + " input=\"help me plan a trip to france\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SViKko1ByIWZ" + }, + "outputs": [], + "source": [ + "new_query = response.output[0].content[0].text\n", + "print(new_query)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7IErtJlHyIWZ" + }, + "source": [ + "In this instance, a user submitted a generic or open-ended query without specifying key details like travel dates, destination preferences, budget, interests, or travel companions; the rewriting prompt rewrote the query so Deep Research will attempt to generate a broad and inclusive response that anticipates common use cases.\n", + "\n", + "While this behavior can be helpful in surfacing a wide range of options, it often leads to verbosity, higher latency, and increased token usage, as the model must account for many possible scenarios. This is especially true for queries that trigger complex planning or synthesis tasks (e.g. multi-destination travel itineraries, comparative research, product selection).\n", + "\n", + "Instead of proceeding immediately with a broad research plan, let's trying using a lighter weight model to gently ask clarification questions from the user before generating a full answer and then using the rewriting prompt for clearer output for the model." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "v7zuwZUFyIWZ" + }, + "outputs": [], + "source": [ + "suggested_clariying_prompt = \"\"\"\"\n", + "You will be given a research task by a user. Your job is NOT to complete the task yet, but instead to ask clarifying questions that would help you or another researcher produce a more specific, efficient, and relevant answer.\n", + "\n", + "GUIDELINES:\n", + "1. **Maximize Relevance**\n", + "- Ask questions that are *directly necessary* to scope the research output.\n", + "- Consider what information would change the structure, depth, or direction of the answer.\n", + "\n", + "2. **Surface Missing but Critical Dimensions**\n", + "- Identify essential attributes that were not specified in the user’s request (e.g., preferences, time frame, budget, audience).\n", + "- Ask about each one *explicitly*, even if it feels obvious or typical.\n", + "\n", + "3. **Do Not Invent Preferences**\n", + "- If the user did not mention a preference, *do not assume it*. Ask about it clearly and neutrally.\n", + "\n", + "4. **Use the First Person**\n", + "- Phrase your questions from the perspective of the assistant or researcher talking to the user (e.g., “Could you clarify...” or “Do you have a preference for...”)\n", + "\n", + "5. **Use a Bulleted List if Multiple Questions**\n", + "- If there are multiple open questions, list them clearly in bullet format for readability.\n", + "\n", + "6. **Avoid Overasking**\n", + "- Prioritize the 3–6 questions that would most reduce ambiguity or scope creep. You don’t need to ask *everything*, just the most pivotal unknowns.\n", + "\n", + "7. **Include Examples Where Helpful**\n", + "- If asking about preferences (e.g., travel style, report format), briefly list examples to help the user answer.\n", + "\n", + "8. **Format for Conversational Use**\n", + "- The output should sound helpful and conversational—not like a form. Aim for a natural tone while still being precise.\n", + "\"\"\"\n", + "\n", + "\n", + "response = client.responses.create(\n", + " instructions=suggested_clariying_prompt,\n", + " model=\"gpt-4.1-2025-04-14\",\n", + " input=\"help me plan a trip to france\",\n", + ")\n", + "\n", + "new_query = response.output[0].content[0].text\n", + "print(new_query)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "9gpjXiFDyIWZ" + }, + "outputs": [], + "source": [ + "user_follow_up = \"\"\"I'd like to travel in August. I'd like to visit Paria and Nice. I'd like to keep it under $1500 for a 7 day trip without including flights.\n", + "I'm going with my friend. we're both in our mid-twenties. i like history, really good french food and wine, and hiking\n", + "\"\"\"\n", + "instructions_for_DR = client.responses.create(\n", + " instructions=suggested_rewriting_prompt,\n", + " model=\"gpt-4.1-2025-04-14\",\n", + " input=user_follow_up,\n", + ")\n", + "instructions_for_deep_research = instructions_for_DR.output[0].content[0].text\n", + "print(instructions_for_deep_research)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sBScr2AWyIWZ" + }, + "outputs": [], + "source": [ + "deep_research_call = client.responses.create(\n", + " model=\"o4-mini-deep-research-2025-06-26\",\n", + " input=[\n", + " {\n", + " \"role\": \"developer\",\n", + " \"content\": [\n", + " {\n", + " \"type\": \"input_text\",\n", + " \"text\": instructions_for_deep_research,\n", + " }\n", + " ]\n", + " },\n", + " ],\n", + " reasoning={\n", + " \"summary\": \"auto\"\n", + " },\n", + " tools=[\n", + " {\n", + " \"type\": \"web_search_preview\"\n", + " },\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cUg83AnbyIWZ" + }, + "outputs": [], + "source": [ + "# Access the final report from the response object\n", + "print(deep_research_call.output[-1].content[0].text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8Lf3L_u7HGZc" + }, + "source": [ + "And there you have it! A deep research report crafted for your upcoming trip to France!\n", + "\n", + "In this notebook, we explored how to use the Deep Research API to automate complex, real-world research tasks, from analyzing the economic impact of semaglutide to planning a trip to France that works for you. Deep Research shines when you need structured, citation-backed answers grounded in real-world evidence. Some standout use cases include:\n", + "- Product comparisons and market analyses\n", + "- Competitive intelligence and strategy reports\n", + "- Technical literature reviews and policy synthesis\n", + "\n", + "\n", + "Whether you're looking to build research agents, generate structured reports, or integrate high-quality synthesis into your workflows, we hope the examples here help you get started.\n", + "\n", + "What's next? [Deep Research Agents](https://cookbook.openai.com/examples/deep_research_api/introduction_to_deep_research_api_agents)" + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [ + "9wQ3mKWjyIWY" + ], + "provenance": [] + }, + "kernelspec": { + "display_name": "openai", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/examples/deep_research_api/introduction_to_deep_research_api_agents.ipynb b/examples/deep_research_api/introduction_to_deep_research_api_agents.ipynb new file mode 100644 index 0000000000..f45fdbce5f --- /dev/null +++ b/examples/deep_research_api/introduction_to_deep_research_api_agents.ipynb @@ -0,0 +1,590 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "dv8-mnmnj0Wp" + }, + "source": [ + "# Deep Research Agents Cookbook\n", + "\n", + "This cookbook demonstrates how to build Agentic research workflows using the OpenAI Deep Research API and the OpenAI [Agents SDK](https://openai.github.io/openai-agents-python/). It is a continuation of [a fundamentals cookbook](https://cookbook.openai.com/examples/deep_research_api/introduction_to_deep_research_api), if you have not already familiarized yourself with that content, please consider doing so.\n", + "\n", + "You’ll learn how to orchestrate single and multi-agent pipelines, enrich user queries to maximize output quality, stream research progress, integrate web search and [MCP for internal file search](https://cookbook.openai.com/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/README), and architect a robust research application.\n", + "\n", + "Consider using Deep Research Agents for tasks that require planning, synthesis, tool use, or multi-step reasoning. Do not use Deep Research for trivial fact lookups, simple Q&A, or short-form chat, a vanilla openai.responsesAPI would be faster and cheaper." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "x6gLS5aVj0Wr" + }, + "source": [ + "### Prerequisites\n", + "* OpenAI API key (set as OPENAI_API_KEY in your environment)\n", + "* Agents SDK and OpenAI Python SDK\n", + "\n", + "### Setup\n", + "*Install dependencies*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FWE9uQq4j0Ws", + "outputId": "99c15803-0506-4464-d624-a31b5bc809a4" + }, + "outputs": [], + "source": [ + "%pip install --upgrade \"openai>=1.88\" \"openai-agents>=0.0.19\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o9lWqn_Wj0Wt" + }, + "source": [ + "### Import libraries and configure client\n", + "\n", + "**Zero Data Retention**\n", + "\n", + "We disable Data Retention through the os.environ setting below. This allows Enterprises to operate in a Zero Data Retention environment with Deep Research. If Data Retention is _not_ an active constraint for you, then consider keeping it enabled so you can have automated tracability for your agent workflows and deep integration with other platform tools like evaluations and fine tuning." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "OWnnTNZJj0Wt" + }, + "outputs": [], + "source": [ + "import os\n", + "from agents import Agent, Runner, WebSearchTool, RunConfig, set_default_openai_client, HostedMCPTool\n", + "from typing import List, Dict, Optional\n", + "from pydantic import BaseModel\n", + "from openai import AsyncOpenAI\n", + "\n", + "# Use env var for API key and set a long timeout\n", + "client = AsyncOpenAI(api_key=\"\", timeout=600.0)\n", + "set_default_openai_client(client)\n", + "os.environ[\"OPENAI_AGENTS_DISABLE_TRACING\"] = \"1\" # Disable tracing for Zero Data Retention (ZDR) Organizations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4Omyb04nj0Wy" + }, + "source": [ + "### Basic Deep Research Agent\n", + "\n", + "The Basic Research Agent performs Deep Research using the o4-mini-deep-research-alpha model. It has native WebSearch access to the public internet and streams its findings directly back into the notebook. In this case we are using the `o4-mini-deep-research-alpha` model, because it is faster than the full o3 deep research model, with acceptable intelligence.\n", + "\n", + "**Learning objective:**\n", + "\n", + "After this, you can run a single-agent research task and stream its progress." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "c91rFNYWj0Wy", + "outputId": "6f2e3bbe-f321-4a8e-b7df-6b6c5bade65a" + }, + "outputs": [], + "source": [ + "# Define the research agent\n", + "research_agent = Agent(\n", + " name=\"Research Agent\",\n", + " model=\"o4-mini-deep-research-2025-06-26\",\n", + " tools=[WebSearchTool()],\n", + " instructions=\"You perform deep empirical research based on the user's question.\"\n", + ")\n", + "\n", + "# Async function to run the research and print streaming progress\n", + "async def basic_research(query):\n", + " print(f\"Researching: {query}\")\n", + " result_stream = Runner.run_streamed(\n", + " research_agent,\n", + " query\n", + " )\n", + "\n", + " async for ev in result_stream.stream_events():\n", + " if ev.type == \"agent_updated_stream_event\":\n", + " print(f\"\\n--- switched to agent: {ev.new_agent.name} ---\")\n", + " print(f\"\\n--- RESEARCHING ---\")\n", + " elif (\n", + " ev.type == \"raw_response_event\"\n", + " and hasattr(ev.data, \"item\")\n", + " and hasattr(ev.data.item, \"action\")\n", + " ):\n", + " action = ev.data.item.action or {}\n", + " if action.get(\"type\") == \"search\":\n", + " print(f\"[Web search] query={action.get('query')!r}\")\n", + "\n", + " # streaming is complete → final_output is now populated\n", + " return result_stream.final_output\n", + "\n", + "# Run the research and print the result\n", + "result = await basic_research(\"Research the economic impact of semaglutide on global healthcare systems.\")\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f4wk2sulj0Wy" + }, + "source": [ + "### Multi-Agent Research with Clarification\n", + "\n", + "Multi-Agent Deep Research\n", + "\n", + "Consider how you might further improve the Research quality \"Deep Research\" produces. In this case, we are leveraging a multi-agent architecture to enrich the prompt with _more information_ about the users query and what we expect to see in the final research report, before submitting it to a deep research agent." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AlZ6rxFn7C0d" + }, + "source": [ + "\n", + "## Sub-Agent Prompt enrichment\n", + "\n", + "The supporting Agent prompts are specifically designed to improve the quality of the final research output by providing structure and rigor to the users intial query." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "glkoOX6q6Ph9" + }, + "outputs": [], + "source": [ + "# ─────────────────────────────────────────────────────────────\n", + "# Prompts\n", + "# ─────────────────────────────────────────────────────────────\n", + "\n", + "CLARIFYING_AGENT_PROMPT = \"\"\"\n", + " If the user hasn't specifically asked for research (unlikely), ask them what research they would like you to do.\n", + "\n", + " GUIDELINES:\n", + " 1. **Be concise while gathering all necessary information** Ask 2–3 clarifying questions to gather more context for research.\n", + " - Make sure to gather all the information needed to carry out the research task in a concise, well-structured manner. Use bullet points or numbered lists if appropriate for clarity. Don't ask for unnecessary information, or information that the user has already provided.\n", + " 2. **Maintain a Friendly and Non-Condescending Tone**\n", + " - For example, instead of saying “I need a bit more detail on Y,” say, “Could you share more detail on Y?”\n", + " 3. **Adhere to Safety Guidelines**\n", + " \"\"\"\n", + "\n", + "RESEARCH_INSTRUCTION_AGENT_PROMPT = \"\"\"\n", + "\n", + " Based on the following guidelines, take the users query, and rewrite it into detailed research instructions. OUTPUT ONLY THE RESEARCH INSTRUCTIONS, NOTHING ELSE. Transfer to the research agent.\n", + "\n", + " GUIDELINES:\n", + " 1. **Maximize Specificity and Detail**\n", + " - Include all known user preferences and explicitly list key attributes or dimensions to consider.\n", + " - It is of utmost importance that all details from the user are included in the expanded prompt.\n", + "\n", + " 2. **Fill in Unstated But Necessary Dimensions as Open-Ended**\n", + " - If certain attributes are essential for a meaningful output but the user has not provided them, explicitly state that they are open-ended or default to “no specific constraint.”\n", + "\n", + " 3. **Avoid Unwarranted Assumptions**\n", + " - If the user has not provided a particular detail, do not invent one.\n", + " - Instead, state the lack of specification and guide the deep research model to treat it as flexible or accept all possible options.\n", + "\n", + " 4. **Use the First Person**\n", + " - Phrase the request from the perspective of the user.\n", + "\n", + " 5. **Tables**\n", + " - If you determine that including a table will help illustrate, organize, or enhance the information in your deep research output, you must explicitly request that the deep research model provide them.\n", + " Examples:\n", + " - Product Comparison (Consumer): When comparing different smartphone models, request a table listing each model’s features, price, and consumer ratings side-by-side.\n", + " - Project Tracking (Work): When outlining project deliverables, create a table showing tasks, deadlines, responsible team members, and status updates.\n", + " - Budget Planning (Consumer): When creating a personal or household budget, request a table detailing income sources, monthly expenses, and savings goals.\n", + " Competitor Analysis (Work): When evaluating competitor products, request a table with key metrics—such as market share, pricing, and main differentiators.\n", + "\n", + " 6. **Headers and Formatting**\n", + " - You should include the expected output format in the prompt.\n", + " - If the user is asking for content that would be best returned in a structured format (e.g. a report, plan, etc.), ask the Deep Research model to “Format as a report with the appropriate headers and formatting that ensures clarity and structure.”\n", + "\n", + " 7. **Language**\n", + " - If the user input is in a language other than English, tell the model to respond in this language, unless the user query explicitly asks for the response in a different language.\n", + "\n", + " 8. **Sources**\n", + " - If specific sources should be prioritized, specify them in the prompt.\n", + " - Prioritize Internal Knowledge. Only retrieve a single file once.\n", + " - For product and travel research, prefer linking directly to official or primary websites (e.g., official brand sites, manufacturer pages, or reputable e-commerce platforms like Amazon for user reviews) rather than aggregator sites or SEO-heavy blogs.\n", + " - For academic or scientific queries, prefer linking directly to the original paper or official journal publication rather than survey papers or secondary summaries.\n", + " - If the query is in a specific language, prioritize sources published in that language.\n", + "\n", + " IMPORTANT: Ensure that the complete payload to this function is valid JSON\n", + " IMPORTANT: SPECIFY REQUIRED OUTPUT LANGUAGE IN THE PROMPT\n", + " \"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "I1dXCfxa6sf1" + }, + "source": [ + "# Four-Agent Deep Research Pipeline\n", + "\n", + "1. **Triage Agent** \n", + " - Inspects the user’s query \n", + " - If context is missing, routes to the Clarifier Agent; otherwise routes to the Instruction Agent \n", + "\n", + "2. **Clarifier Agent** \n", + " - Asks follow-up questions \n", + " - Waits for user (or mock) answers \n", + "\n", + "3. **Instruction Builder Agent** \n", + " - Converts the enriched input into a precise research brief \n", + "\n", + "4. **Research Agent** (`o3-deep-research`) \n", + " - Performs web-scale empirical research with `WebSearchTool`\n", + " - Performs a search against internal knowledge store using MCP, if there are relevant documents, the agent incorporates those relevant snippets in its reference material. \n", + " - Streams intermediate events for transparency\n", + " - Outputs final Research Artifact (which we later parse)\n", + "\n", + "```mermaid\n", + "flowchart LR\n", + " subgraph Triage\n", + " TA[Triage Agent
• Inspect query
• Route based on context]\n", + " end\n", + " subgraph Clarifier\n", + " CA[Clarifier Agent
• Ask follow‐up questions
• Receive answers]\n", + " end\n", + " subgraph Instruction\n", + " IA[Instruction Builder Agent
• Build precise research brief]\n", + " end\n", + " subgraph Research\n", + " RA[Research Agent
o3‐deep‐research
• WebSearchTool
• Internal MCP search
• Stream events
• Output Artifact]\n", + " end\n", + "\n", + " TA -->|Missing context| CA\n", + " TA -->|Context OK| IA\n", + " CA --> IA\n", + " IA --> RA\n", + "```\n", + "\n", + "For more insight into _how_ the MCP server is build. [See this resource.](https://cookbook.openai.com/examples/deep_research_api/how_to_build_a_deep_research_mcp_server/README )" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "y-8WVGMBj0Wz" + }, + "outputs": [], + "source": [ + "# ─────────────────────────────────────────────────────────────\n", + "# Structured outputs (needed only for Clarifying agent)\n", + "# ─────────────────────────────────────────────────────────────\n", + "class Clarifications(BaseModel):\n", + " questions: List[str]\n", + "\n", + "# ─────────────────────────────────────────────────────────────\n", + "# Agents\n", + "# ─────────────────────────────────────────────────────────────\n", + "research_agent = Agent(\n", + " name=\"Research Agent\",\n", + " model=\"o3-deep-research-2025-06-26\",\n", + " instructions=\"Perform deep empirical research based on the user's instructions.\",\n", + " tools=[WebSearchTool(),\n", + " HostedMCPTool(\n", + " tool_config={\n", + " \"type\": \"mcp\",\n", + " \"server_label\": \"file_search\",\n", + " \"server_url\": \"https:///sse\",\n", + " \"require_approval\": \"never\",\n", + " }\n", + " )\n", + " ]\n", + ")\n", + "\n", + "instruction_agent = Agent(\n", + " name=\"Research Instruction Agent\",\n", + " model=\"gpt-4o-mini\",\n", + " instructions=RESEARCH_INSTRUCTION_AGENT_PROMPT,\n", + " handoffs=[research_agent],\n", + ")\n", + "\n", + "clarifying_agent = Agent(\n", + " name=\"Clarifying Questions Agent\",\n", + " model=\"gpt-4o-mini\",\n", + " instructions=CLARIFYING_AGENT_PROMPT,\n", + " output_type=Clarifications,\n", + " handoffs=[instruction_agent],\n", + ")\n", + "\n", + "triage_agent = Agent(\n", + " name=\"Triage Agent\",\n", + " instructions=(\n", + " \"Decide whether clarifications are required.\\n\"\n", + " \"• If yes → call transfer_to_clarifying_questions_agent\\n\"\n", + " \"• If no → call transfer_to_research_instruction_agent\\n\"\n", + " \"Return exactly ONE function-call.\"\n", + " ),\n", + " handoffs=[clarifying_agent, instruction_agent],\n", + ")\n", + "\n", + "\n", + "# ─────────────────────────────────────────────────────────────\n", + "# Auto-clarify helper\n", + "# ─────────────────────────────────────────────────────────────\n", + "async def basic_research(\n", + " query: str,\n", + " mock_answers: Optional[Dict[str, str]] = None,\n", + " verbose: bool = False,\n", + "):\n", + " stream = Runner.run_streamed(\n", + " triage_agent,\n", + " query,\n", + " run_config=RunConfig(tracing_disabled=True),\n", + " )\n", + "\n", + " async for ev in stream.stream_events():\n", + " if isinstance(getattr(ev, \"item\", None), Clarifications):\n", + " reply = []\n", + " for q in ev.item.questions:\n", + " ans = (mock_answers or {}).get(q, \"No preference.\")\n", + " reply.append(f\"**{q}**\\n{ans}\")\n", + " stream.send_user_message(\"\\n\\n\".join(reply))\n", + " continue\n", + " if verbose:\n", + " print(ev)\n", + "\n", + " #return stream.final_output\n", + " return stream\n", + "\n", + "# ─────────────────────────────────────────────────────────────\n", + "# Example run\n", + "# ─────────────────────────────────────────────────────────────\n", + "result = await basic_research(\n", + " \"Research the economic impact of semaglutide on global healthcare systems.\",\n", + " mock_answers={}, # or provide canned answers\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pEVDHxRzjvJM" + }, + "source": [ + "## Agent Interaction Flow\n", + "\n", + "Although provided natively through Agent SDK traces you may want to print human-readable high-level agent interaction flow with tool calls. Run print_agent_interaction to get a simplified readable sequence of agent steps, including: Agent name, Type of event (handoff, tool call, message output), Brief tool call info (tool name and arguments).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7YZ_ibZIic_u", + "outputId": "23c97975-94f2-47e0-ea0f-7475b46c4f6c" + }, + "outputs": [], + "source": [ + "import json\n", + "\n", + "def parse_agent_interaction_flow(stream):\n", + " print(\"=== Agent Interaction Flow ===\")\n", + " count = 1\n", + "\n", + " for item in stream.new_items:\n", + " # Agent name, fallback if missing\n", + " agent_name = getattr(item.agent, \"name\", \"Unknown Agent\") if hasattr(item, \"agent\") else \"Unknown Agent\"\n", + "\n", + " if item.type == \"handoff_call_item\":\n", + " func_name = getattr(item.raw_item, \"name\", \"Unknown Function\")\n", + " print(f\"{count}. [{agent_name}] → Handoff Call: {func_name}\")\n", + " count += 1\n", + "\n", + " elif item.type == \"handoff_output_item\":\n", + " print(f\"{count}. [{agent_name}] → Handoff Output\")\n", + " count += 1\n", + "\n", + " elif item.type == \"mcp_list_tools_item\":\n", + " print(f\"{count}. [{agent_name}] → mcp_list_tools_item\")\n", + " count += 1\n", + "\n", + " elif item.type == \"reasoning_item\":\n", + " print(f\"{count}. [{agent_name}] → Reasoning step\")\n", + " count += 1\n", + "\n", + " elif item.type == \"tool_call_item\":\n", + " tool_name = getattr(item.raw_item, \"name\", None)\n", + "\n", + " # Skip tool call if tool_name is missing or empty\n", + " if not isinstance(tool_name, str) or not tool_name.strip():\n", + " continue # skip silently\n", + "\n", + " tool_name = tool_name.strip()\n", + "\n", + " args = getattr(item.raw_item, \"arguments\", None)\n", + " args_str = \"\"\n", + "\n", + " if args:\n", + " try:\n", + " parsed_args = json.loads(args)\n", + " if parsed_args:\n", + " args_str = json.dumps(parsed_args)\n", + " except Exception:\n", + " if args.strip() and args.strip() != \"{}\":\n", + " args_str = args.strip()\n", + "\n", + " args_display = f\" with args {args_str}\" if args_str else \"\"\n", + "\n", + " print(f\"{count}. [{agent_name}] → Tool Call: {tool_name}{args_display}\")\n", + " count += 1\n", + "\n", + " elif item.type == \"message_output_item\":\n", + " print(f\"{count}. [{agent_name}] → Message Output\")\n", + " count += 1\n", + "\n", + " else:\n", + " print(f\"{count}. [{agent_name}] → {item.type}\")\n", + " count += 1\n", + "\n", + "# Example usage:\n", + "parse_agent_interaction_flow(result)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9mZW3GT5kCOw" + }, + "source": [ + "## Citations\n", + "\n", + "Below is a Python snippet to extract and print the URL citations related to the final output:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JCyfxC7siant", + "outputId": "28539b23-00db-4dfa-902b-cd80c67ba765" + }, + "outputs": [], + "source": [ + "def print_final_output_citations(stream, preceding_chars=50):\n", + " # Iterate over new_items in reverse to find the last message_output_item(s)\n", + " for item in reversed(stream.new_items):\n", + " if item.type == \"message_output_item\":\n", + " for content in getattr(item.raw_item, 'content', []):\n", + " if not hasattr(content, 'annotations') or not hasattr(content, 'text'):\n", + " continue\n", + " text = content.text\n", + " for ann in content.annotations:\n", + " if getattr(ann, 'type', None) == 'url_citation':\n", + " title = getattr(ann, 'title', '')\n", + " url = getattr(ann, 'url', '')\n", + " start = getattr(ann, 'start_index', None)\n", + " end = getattr(ann, 'end_index', None)\n", + "\n", + " if start is not None and end is not None and isinstance(text, str):\n", + " # Calculate preceding snippet start index safely\n", + " pre_start = max(0, start - preceding_chars)\n", + " preceding_text = text[pre_start:start].replace('\\n', ' ').strip()\n", + " excerpt = text[start:end].replace('\\n', ' ').strip()\n", + " print(\"# --------\")\n", + " print(\"# MCP CITATION SAMPLE:\")\n", + " print(f\"# Title: {title}\")\n", + " print(f\"# URL: {url}\")\n", + " print(f\"# Location: chars {start}–{end}\")\n", + " print(f\"# Preceding: '{preceding_text}'\")\n", + " print(f\"# Excerpt: '{excerpt}'\\n\")\n", + " else:\n", + " # fallback if no indices available\n", + " print(f\"- {title}: {url}\")\n", + " break\n", + "\n", + "# Usage\n", + "print_final_output_citations(result)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "sTeTcni5L-1s", + "outputId": "eb442687-0530-4198-d778-b7d0dcf07df0" + }, + "outputs": [], + "source": [ + "## Deep Research Research Report\n", + "\n", + "print(result.final_output)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8UJcBbp9j0Wz" + }, + "source": [ + "### Conclusion\n", + "\n", + "With the patterns in this notebook, you now have a foundation for building scalable, production-ready research workflows using OpenAI Deep Research Agents. The examples demonstrate not only how to orchestrate multi-agent pipelines and stream research progress, but also how to integrate web search and MCP for external knowledge access.\n", + "\n", + "By leveraging agentic workflows, you can move beyond simple Q&A to tackle complex, multi-step research tasks that require planning, synthesis, and tool use. The modular multi-agent design: triage, clarification, instruction, and research agents enables you to adapt these pipelines to a wide range of domains and use cases, from healthcare and finance to technical due diligence and market analysis.\n", + "\n", + "As the Deep Research API and Agents SDK continue to evolve, these patterns will help you stay at the forefront of automated, data-backed research. Whether you’re building internal knowledge tools, automating competitive intelligence, or supporting expert analysts, these workflows provide a strong, extensible starting point.\n", + "\n", + "**Happy researching!**\n" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.0" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/registry.yaml b/registry.yaml index fdc78fa2c9..aa1c4604fa 100644 --- a/registry.yaml +++ b/registry.yaml @@ -2195,3 +2195,42 @@ - evals-api - responses - evals + +- title: Introduction to deep research in the OpenAI API + path: examples/deep_research_api/introduction_to_deep_research_api.ipynb + date: 2025-06-25 + authors: + - glojain + - alwell-kevin + tags: + - deep-research-api + - responses + - mcp + - deep-research + +- title: Deep Research API with the Agents SDK + path: examples/deep_research_api/introduction_to_deep_research_api_agents.ipynb + date: 2025-06-25 + authors: + - alwell-kevin + - glojain + tags: + - deep-research-api + - mcp + - deep-research + - agents + - agents-sdk + +- title: Building a Deep Research MCP Server + path: examples/deep_research_api/how_to_build_a_deep_research_mcp_server/README.md + date: 2025-06-25 + authors: + - alwell-kevin + - glojain + tags: + - mcp + - deep-research-api + - responses + - deep-research + - agents + - agents-sdk