Responses API에서의 File Search
Responses API에서의 File Search
LiteLLM은 이제 Responses API에서 file_search를 지원합니다. 네이티브로 지원하는 제공자(OpenAI, Azure 등)와, 지원하지 않는 제공자(Anthropic, Bedrock 등 비네이티브 제공자)를 에뮬레이션으로 처리하는 두 경로 모두를 다룹니다. 단일 OpenAI 호환 출력 형태를 유지하면서 네이티브 패스스루 또는 에뮬레이션 폴백으로 요청을 라우팅해요.
출처: 문서
본문
이것이 무엇인가요?
file_search는 모델이 벡터 스토어에서 근거(grounded context)를 검색하고 인용문과 함께 답변하게 해줍니다. LiteLLM은 네이티브 패스스루나 에뮬레이션 폴백을 통해 요청을 라우팅하면서도 OpenAI 호환 출력 형태 하나를 유지해요.
두 가지 경로를 다룹니다:
| Path | When it runs | What LiteLLM does |
| Native passthrough | Provider natively supports file_search (OpenAI, Azure) | Decodes unified vector store ID → forwards to provider as-is |
| Emulated fallback | Provider doesn't support file_search (Anthropic, Bedrock, etc.) | Converts to a function tool → intercepts tool call → runs vector search → synthesizes OpenAI-format output |
tools[].vector_store_ids에서 LiteLLM은 제공자 네이티브 ID(예: vs_...)와 관리형 벡터 스토어 통합 ID(프록시 managed-vector 흐름의 URL 안전 base64 문자열)를 모두 허용합니다. 예: litellm.responses(..., tools=[{"type": "file_search", "vector_store_ids": ["bGl0ZWxsbV9wcm94eT..."]}]).
사용법
- LiteLLM Proxy
- LiteLLM SDK
1. config.yaml 설정
config.yaml
model_list:
- model_name: gpt-5.6-terra
litellm_params:
model: openai/gpt-5.6-terra
api_key: os.environ/OPENAI_API_KEY
- model_name: claude-sonnet
litellm_params:
model: anthropic/claude-sonnet-5
api_key: os.environ/ANTHROPIC_API_KEY
2. Proxy 시작
litellm --config config.yaml
3. file_search로 Responses API 호출
Proxy call
from openai import OpenAIclient = OpenAI(base_url="http://localhost:4000", api_key="***")response = client.responses.create(
model="claude-sonnet", # swap to "gpt-5.6-terra" for native path
input="What does LiteLLM support?",
tools=[{
"type": "file_search",
"vector_store_ids": ["vs_abc123"]
}],
include=["file_search_call.results"],)print(response.output)
1. 설치 및 키 설정
uv add litellmexport OPENAI_API_KEY="sk-..."export ANTHROPIC_API_KEY="sk-ant-..."
2. file_search로 Responses API 호출
SDK call
import litellmresponse = litellm.responses(
model="anthropic/claude-sonnet-5", # swap to openai/gpt-5.6-terra for native path
input="What does LiteLLM support?",
tools=[{
"type": "file_search",
"vector_store_ids": ["vs_abc123"]
}],
include=["file_search_call.results"],)print(response.output)
동작 매트릭스
| Path | SDK model | Proxy model | Behavior |
| Native passthrough | openai/gpt-5.6-terra | gpt-5.6-terra | Provider executes native file_search |
| Emulated fallback | anthropic/claude-sonnet-5 | claude-sonnet | LiteLLM converts to function tool and synthesizes OpenAI-format output |
아키텍처 다이어그램
전제 조건
uv tool install 'litellm[proxy]'export OPENAI_API_KEY="sk-..." # for native pathexport ANTHROPIC_API_KEY="sk-ant-..." # for emulated path
예시 응답 형태
출력 형식 검증
어느 경로로 실행됐든 응답은 항상 OpenAI Responses API 형식을 따릅니다:
{
"output": [
{
"type": "file_search_call",
"id": "fs_abc123",
"status": "completed",
"queries": ["What does LiteLLM support?"],
"search_results": null
},
{
"type": "message",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "LiteLLM is a unified interface...",
"annotations": [
{
"type": "file_citation",
"index": 150,
"file_id": "file-xxxx",
"filename": "knowledge.txt"
}
]
}
]
}
]
}
검증 스크립트:
Validate response structure
def validate_file_search_response(response):
"""Assert that response follows OpenAI file_search output format."""
output = response.output
assert len(output) >= 2, "Expected at least 2 output items"
# First item: file_search_call
fs_call = output[0]
fs_type = fs_call["type"] if isinstance(fs_call, dict) else fs_call.type
assert fs_type == "file_search_call", f"Expected file_search_call, got {fs_type}"
fs_status = fs_call["status"] if isinstance(fs_call, dict) else fs_call.status
assert fs_status == "completed"
# Second item: message
msg = output[1]
msg_type = msg["type"] if isinstance(msg, dict) else msg.type
assert msg_type == "message"
content = msg["content"] if isinstance(msg, dict) else msg.content
assert len(content) > 0
text_block = content[0]
text = text_block["text"] if isinstance(text_block, dict) else text_block.text
assert isinstance(text, str) and len(text) > 0
print("✅ Response structure valid")
print(f" Queries: {fs_call['queries'] if isinstance(fs_call, dict) else fs_call.queries}")
print(f" Answer length: {len(text)} chars")
annotations = text_block["annotations"] if isinstance(text_block, dict) else text_block.annotations
print(f" Citations: {len(annotations)}")validate_file_search_response(response)
Q&A
-
UnsupportedParamsError가 보이는 이유는? 보통file_search를 네이티브로 지원하지 않는 제공자에 전달했는데 에뮬레이션이 올바르게 라우팅하지 못했을 때 발생해요. 확인할 것:- 모델 문자열이 유효한지(예:
anthropic/claude-sonnet-5) custom_llm_provider가 올바르게 해석되어 LiteLLM이 제공자 설정을 로드할 수 있는지
- 모델 문자열이 유효한지(예:
-
벡터 검색이 결과를 반환하지 않는 이유는? 흔한 원인:
- 벡터 스토어 ID가 잘못됐거나 파일이 첨부되지 않음
- LiteLLM 관리형 스토어에서 파일 수집이 완료되지 않음(
status != completed) - 쿼리가 너무 좁음; 더 넓은 쿼리를 시도해 보세요
-
벡터 스토어 호출에서
403 Access denied가 발생하는 이유는? 호출자가 해당 벡터 스토어에 접근 권한이 없는 경우예요.- 스토어가 다른 팀에 속해 있을 수 있어요
- 크로스팀 접근이 필요하면 admin/proxy 키를 사용하세요
-
에뮬레이션 모드에서
annotations가 비어 있는 이유는?file_citation주석은 검색 결과에file_id메타데이터가 필요해요. 벡터 백엔드가 파일 수준 메타데이터를 반환하지 않으면 답변 텍스트는 생성되지만 인용문은 비어 있을 수 있어요.
다음으로 확인할 것
- Responses API 문서의 File Search 참조 - 전체 API 참조
- 벡터 스토어 관리 - 벡터 스토어 생성 및 관리
- 관리형 벡터 스토어 - 제공자별 설정