도구 호출
도구 호출 (Tool Calling)
vLLM은 현재 이름있는 함수 호출(named function calling)을 지원하고, chat completion API의 tool_choice 필드에 대해 auto, required(vllm>=0.8.3부터), none 옵션을 지원해요.
빠른 시작 (Quickstart)
도구 호출을 켠 상태로 서버를 시작해요. 이 예제는 Meta의 Llama 3.1 8B 모델을 쓰므로, vLLM 예제 디렉토리의 llama3_json 도구 호출 채팅 템플릿을 사용해야 해요.
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--enable-auto-tool-choice \
--tool-call-parser llama3_json \
--chat-template examples/tool_chat_template_llama3.1_json.jinja
이제 모델이 사용 가능한 도구를 쓰도록 만드는 요청을 보내볼게요.
from openai import OpenAI
import json
client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
def get_weather(location: str, unit: str):
return f"Getting the weather for {location} in {unit}..."
tool_functions = {"get_weather": get_weather}
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City and state, e.g., 'San Francisco, CA'"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location", "unit"],
},
},
},
]
response = client.chat.completions.create(
model=client.models.list().data[0].id,
messages=[{"role": "user", "content": "What's the weather like in San Francisco?"}],
tools=tools,
tool_choice="auto",
)
tool_call = response.choices[0].message.tool_calls[0].function
print(f"Function called: {tool_call.name}")
print(f"Arguments: {tool_call.arguments}")
print(f"Result: {tool_functions[tool_call.name](**json.loads(tool_call.arguments))}")
이 예제에서 보여주는 것은 다음과 같아요.
- 도구 호출을 켠 상태로 서버 설정하기
- 도구 호출을 처리할 실제 함수 정의하기
tool_choice="auto"로 요청 보내기- 구조화된 응답 처리하고 그에 해당하는 함수 실행하기
tool_choice={"type": "function", "function": {"name": "get_weather"}}를 설정하면 이름있는 함수 호출로 특정 함수를 지정할 수도 있어요. 이 경우 구조화된 출력 백엔드를 사용하므로, 처음 사용할 때 FSM이 컴파일되면서 몇 초(혹은 그 이상)의 레이턴시가 발생하고 이후 요청부터는 캐시됩니다.
호출 측의 책임이라는 점을 기억하세요.
- 요청에 적절한 도구를 정의해야 하고
- 채팅 메시지에 관련 맥락을 포함해야 하며
- 애플리케이션 로직에서 도구 호출을 처리해야 해요
병렬 도구 호출과 모델별 파서에 대한 더 고급 사용법은 아래 섹션을 참고하세요.
이름있는 함수 호출 (Named Function Calling)
vLLM은 chat completion API에서 이름있는 함수 호출을 기본적으로 지원해요. vLLM이 지원하는 대부분의 구조화된 출력 백엔드와 함께 동작해요. 파싱 가능한 유효한 함수 호출을 보장하지만, 그 품질이 항상 높은 것은 아니라는 점을 유의하세요.
vLLM은 구조화된 출력을 사용해 응답이 tools 파라미터에 정의된 JSON 스키마의 도구 파라미터 객체와 일치하도록 해요. 최상의 결과를 위해, 예상 출력 형식/스키마를 프롬프트에 명시해 모델의 의도된 생성을 구조화된 출력 백엔드가 강제하는 스키마와 일치시키는 걸 권장합니다.
이름있는 함수를 쓰려면 chat completion 요청의 tools 파라미터에 함수를 정의하고, tool_choice 파라미터에 도구 중 하나의 name을 지정하면 돼요.
필수 함수 호출 (Required Function Calling)
vLLM은 chat completion API의 tool_choice='required' 옵션을 지원해요. 이름있는 함수 호출과 비슷하게 구조화된 출력을 사용하므로 기본으로 켜져 있고 지원되는 모든 모델에서 동작해요. 다만 대안 디코딩 백엔드 지원은 V1 엔진의 로드맵에 있어요.
tool_choice='required'를 설정하면 모델은 tools 파라미터에 지정된 도구 목록을 기반으로 하나 이상의 도구 호출을 생성하는 것이 보장돼요. 도구 호출 수는 사용자 질의에 따라 달라지고, 출력 형식은 tools 파라미터에 정의된 스키마를 엄격히 따릅니다.
없음 함수 호출 (None Function Calling)
vLLM은 chat completion API의 tool_choice='none' 옵션을 지원해요. 이 옵션을 설정하면 요청에 도구가 정의되어 있어도 모델은 도구 호출을 생성하지 않고 일반 텍스트 콘텐츠로만 응답해요.
참고: 요청에 도구가 지정되면 tool_choice 설정과 무관하게 vLLM은 기본적으로 도구 정의를 프롬프트에 포함해요. tool_choice='none'일 때 도구 정의를 제외하려면 --exclude-tools-when-tool-choice-none 옵션을 쓰세요.
제약 디코딩 동작 (Constrained Decoding Behavior)
vLLM이 도구 파라미터 스키마를 생성 중에 강제하는지 여부는 tool_choice 모드와 도구별 strict 필드에 따라 달라져요.
tool_choice 값 |
스키마 제약 디코딩 | 동작 |
|---|---|---|
| 이름있는 함수 | 예 (구조화된 출력 백엔드 경유) | 인자가 함수의 파라미터 스키마를 따르는 유효한 JSON임이 보장됨 |
"required" |
예 (구조화된 출력 백엔드 경유) | 이름있는 함수와 동일. 모델은 최소 하나의 도구 호출을 만들어야 함 |
"auto" |
최소 하나의 도구에 strict: true가 설정된 경우에만 |
structural-tag 파서는 strict: true로 옵트인한 도구에 대해 도구 호출 인자를 제약함. 그 외엔 모델이 자유롭게 생성하고 원시 텍스트에서 도구 호출을 추출함 |
"none" |
해당 없음 | 도구 호출을 생성하지 않음 |
스트릭트 모드 (Strict Mode)
tool_choice="required"나 이름있는 함수 호출의 경우 strict 필드와 무관하게 structural-tag 제약이 항상 적용돼요. 반면 tool_choice="auto"에서는 최소 하나의 도구에 strict: true를 설정하면 structural-tag 제약을 옵트인하는 것이고, 그렇지 않으면 모델이 자유롭게 생성하고 원시 텍스트에서 도구 호출을 추출해요. strict 필드는 Chat Completion, Responses, Anthropic Messages 세 API 표면 모두에서 지원됩니다.
스트릭트 스키마 강제를 위한 최상의 호환성을 위해 OpenAI strict-schema 스타일로 도구 파라미터 스키마를 정의하는 걸 권장해요.
parameters의 각 객체에additionalProperties를false로 설정properties의 모든 필드를 required로 표시- 선택 필드를
null허용으로 표현 (예:{"type": ["string", "null"]})
vLLM은 VLLM_ENFORCE_STRICT_TOOL_CALLING 환경 변수(기본값 true)로 전역 토글도 제공해요. false로 설정하면 도구별 strict 필드와 무관하게 vLLM은 도구 호출에 structural tag를 붙이지 않아요. 이 환경 변수는 structural-tag 기반 도구 호출에만 영향을 주고, 이름있는 함수 호출이나 tool_choice="required"가 사용하는 스키마 유래 구조화된 출력에는 영향을 주지 않습니다.
VLLM_ENFORCE_STRICT_TOOL_CALLING=false vllm serve ...
자동 함수 호출 (Automatic Function Calling)
이 기능을 켜려면 다음 플래그를 설정해야 해요.
--enable-auto-tool-choice— 필수 Auto tool choice. 모델이 적절하다고 판단할 때 스스로 도구 호출을 생성하도록 하고 싶다고 vLLM에 알려주는 플래그예요.--tool-call-parser— 사용할 도구 파서를 고르는 플래그(아래 나열). 앞으로 추가 파서가 계속 추가될 예정이에요.--tool-parser-plugin— 선택 사용자가 정의한 도구 파서를 vllm에 등록하는 데 쓰는 플러그인. 등록된 이름을--tool-call-parser에 지정할 수 있어요.--chat-template— 선택 auto tool choice용.tool역할 메시지와 이전에 생성된 도구 호출이 포함된assistant역할 메시지를 처리하는 채팅 템플릿 경로예요.
tool_choice="auto"에서 스키마 수준 제약은 VLLM_ENFORCE_STRICT_TOOL_CALLING=true(기본값)와 최소 하나의 strict: true 도구가 모두 필요해요. 이 조건이 충족되고 선택된 파서가 structural tag를 지원하면 vLLM은 도구 호출 인자를 제약해요. 그렇지 않으면 원시 텍스트에서 도구 호출을 추출하므로 인자가 가끔 형식이 어긋나거나 함수의 파라미터 스키마를 위반할 수 있어요.
지원되는 모델별 파서
- Hermes 모델:
NousResearch/Hermes-2-Pro-*,NousResearch/Hermes-2-Theta-*,NousResearch/Hermes-3-*등, 플래그--tool-call-parser hermes - Mistral 모델:
mistralai/Mistral-7B-Instruct-v0.3등, 플래그--tool-call-parser mistral - 그리고 Llama(
pythonic), ToolACE 등 다양한 파서가 있어요.
대부분의 경우 --chat-template으로 모델에 맞는 채팅 템플릿을 지정하는 걸 권장합니다. Llama의 작은 모델들은 도구 호출 형식을 올바르게 생성하지 못하는 경우가 잦으니 주의하세요.
도구 호출 성능 벤치마킹
실제 도구 호출 트래픽에서 서빙 레이턴시와 처리량을 측정하려면 BFCL(Berkeley Function Calling Leaderboard) 데이터셋을 vllm bench serve와 함께 사용하세요. 전체 서버+클라이언트 명령은 BFCL 예제를 참고해요.
도구 파서 플러그인 작성법 (How to Write a Tool Parser Plugin)
도구 파서 플러그인은 하나 이상의 ToolParser 구현을 담은 Python 파일이에요. vllm/tool_parsers/hermes_tool_parser.py의 Hermes2ProToolParser와 비슷하게 작성하면 됩니다.
요약하면 다음과 같은 구조예요.
class ExampleToolParser(ToolParser):
def __init__(self, tokenizer: TokenizerLike):
super().__init__(tokenizer)
def adjust_request(self, request):
return request
def extract_tool_calls_streaming(self, previous_text, current_text, ...):
return delta
def extract_tool_calls(self, model_output, request):
return ExtractedToolCallInformation(
tools_called=False, tool_calls=[], content=text)
# register the tool parser to ToolParserManager
ToolParserManager.register_lazy_module(
name="example",
module_path="vllm.tool_parsers.example",
class_name="ExampleToolParser",
)
이 플러그인은 커맨드라인에서 아래처럼 사용할 수 있어요.
--enable-auto-tool-choice \
--tool-parser-plugin <absolute path of the plugin file>
--tool-call-parser example \
--chat-template <your chat template> \