Combining OpenAI API Responses web_search and file_search Tools

The OpenAI Responses API can search supplied files in a vector store (this is called retrieval-augmented generation, RAG), or search the Web.

I tried to combine the two tools into a single query but it just didn’t work:

  . . . 
  client = OpenAI(api_key=key)
  . . .
  # assumes a vector store with demo_store_id exists

  query = "Some question that might have an answer in
    a file in the vector store or maybe not?"
  response = client.responses.create(
    model = "gpt-4.1",
    tools = [
      { "type": "file_search",
        "vector_store_ids": [demo_store_id] },
      { "type": "web_search_preview" },
    ],

    input = [
      { "role": "developer",
        "content": "You answer questions." },
      { "role": "user",
        "content": query },
    ],
    temperature = 0.1,  # not creative
    top_p = 1.0,  # default max explore
    max_output_tokens = 100,
  )
  print(response.output[1].content[0].text)

I couldn’t make the syntax work and couldn’t find an answer in the OpenAI documentation. So I hacked together a sequential approach where I try a file_search first, and if that fails, I try a web_search:

  response1 = client.responses.create(
    model = "gpt-4.1",
    tools = [
      { "type": "file_search",
        "vector_store_ids": [demo_store_id] },
    ],

    input = [
      { "role": "developer",
        "content": "You answer questions." },
      { "role": "user",
        "content": query },
    ],
    temperature = 0.1,  # not creative
    top_p = 1.0,  # default max explore
    max_output_tokens = 100,
  )
  if str(response1.output).find("results=None") == -1:
    print(response1.output[1].content[0].text)
  else:  # try web search
    print("Answer not found in files; trying Web search ")
    response2 = client.responses.create(
      model = "gpt-4.1",
      tools = [{ "type": "web_search_preview" }],
      input = [
        { "role": "developer",
          "content": "You answer questions." },
        { "role": "user",
          "content": query },
      ],
      temperature = 0.1,  # not creative
      top_p = 1.0,  # default max explore
      max_output_tokens = 100,
    )

    if str(response2.output).find("results=None") == -1:
      print(response2.output[1].content[0].text)
    else:
      print("No answer found in Web search ")

This approach is definitely crude, but it suited my needs. The technique depends on a no-response having the string “results=None” in it, which works now but it might not in the future. The OpenAI APIs are being developed so quickly, I suspect it will eventually be possible to avoid this hack.

There are a lot of refinements I can think of. For example, instead of recreating the create() parameters (model, temperature, etc.) used in the first file search response for the fallback Web search response, I should be able to just create a single dictionary object of parameters and pass them to both create() calls.



When I was about six years old, I essentially learned to read from Batman comic books. They were my large language models.

Left: The first appearance of Batman was in “Detective Comics” #27 (May, 1939). Batman got his own title dated Spring 1940, and it is still going strong.

Center: A character called the Black Bat appeared in “Black Book Detective” magazine in July 1939. The Black Bat could see in the dark but in other respects was very similar to Batman. This magazine ran until summer 1953. (This cover is from Spring 1945).

Right: The publishers of Batman and the Black Bat threatened to sue each other, both claiming each copied the other, however they settled amicably because they likely both copied from an earlier character that first appeared in “Black Bat Detective Mysteries” in October 1933. That magazine only ran six issues. So, it’s probable that 1939 Batman and the 1939 Black Bat both copied from the original 1933 Black Bat.


This entry was posted in OpenAI. Bookmark the permalink.

Leave a Reply