Ollama는 도구 호출(Tool Calling, 함수 호출이라고도 함)을 지원합니다. 이를 통해 모델이 도구를 호출하고 해당 결과를 자체 응답에 통합할 수 있습니다.
단일 도구 호출하기
단일 도구를 호출하고 후속 요청에 해당 응답을 포함합니다. '원샷(single-shot) 도구 호출'이라고도 합니다.
cURL
shell
curl -s http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{
"model": "qwen3",
"messages": [{"role": "user", "content": "What is the temperature in New York?"}],
"stream": false,
"tools": [
{
"type": "function",
"function": {
"name": "get_temperature",
"description": "도시의 현재 기온을 가져옵니다.",
"parameters": {
"type": "object",
"required": ["city"],
"properties": {
"city": {"type": "string", "description": "도시 이름"}
}
}
}
}
]
}'단일 도구 결과를 포함한 응답 생성
shell
curl -s http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{
"model": "qwen3",
"messages": [
{"role": "user", "content": "What is the temperature in New York?"},
{
"role": "assistant",
"tool_calls": [
{
"type": "function",
"function": {
"index": 0,
"name": "get_temperature",
"arguments": {"city": "New York"}
}
}
]
},
{"role": "tool", "tool_name": "get_temperature", "content": "22°C"}
],
"stream": false
}'Python
Ollama Python SDK 설치:
bash
# pip로 설치 시
pip install ollama -U
# uv로 설치 시
uv add ollamapython
from ollama import chat
def get_temperature(city: str) -> str:
"""도시의 현재 기온을 가져옵니다.
Args:
city: 도시 이름
Returns:
도시의 현재 기온
"""
temperatures = {
"New York": "22°C",
"London": "15°C",
"Tokyo": "18°C",
}
return temperatures.get(city, "Unknown")
messages = [{"role": "user", "content": "What is the temperature in New York?"}]
# 함수를 tools 목록에 직접 전달하거나 JSON 스키마로 전달할 수 있습니다.
response = chat(model="qwen3", messages=messages, tools=[get_temperature], think=True)
messages.append(response.message)
if response.message.tool_calls:
# 단일 도구 호출만 반환하는 모델에서만 사용하는 것이 권장됩니다.
call = response.message.tool_calls[0]
result = get_temperature(**call.function.arguments)
# 도구 결과를 messages에 추가합니다.
messages.append({"role": "tool", "tool_name": call.function.name, "content": str(result)})
final_response = chat(model="qwen3", messages=messages, tools=[get_temperature], think=True)
print(final_response.message.content)JavaScript
Ollama JavaScript 라이브러리 설치:
bash
# npm로 설치 시
npm i ollama
# bun으로 설치 시
bun i ollamatypescript
import ollama from 'ollama'
function getTemperature(city: string): string {
const temperatures: Record<string, string> = {
'New York': '22°C',
'London': '15°C',
'Tokyo': '18°C',
}
return temperatures[city] ?? 'Unknown'
}
const tools = [
{
type: 'function',
function: {
name: 'get_temperature',
description: '도시의 현재 기온을 가져옵니다.',
parameters: {
type: 'object',
required: ['city'],
properties: {
city: { type: 'string', description: '도시 이름' },
},
},
},
},
]
const messages = [{ role: 'user', content: "What is the temperature in New York?" }]
const response = await ollama.chat({
model: 'qwen3',
messages,
tools,
think: true,
})
messages.push(response.message)
if (response.message.tool_calls?.length) {
// 단일 도구 호출만 반환하는 모델에서만 사용하는 것이 권장됩니다.
const call = response.message.tool_calls[0]
const args = call.function.arguments as { city: string }
const result = getTemperature(args.city)
// 도구 결과를 messages에 추가합니다.
messages.push({ role: 'tool', tool_name: call.function.name, content: result })
// 최종 응답 생성
const finalResponse = await ollama.chat({ model: 'qwen3', messages, tools, think: true })
console.log(finalResponse.message.content)
}병렬 도구 호출
cURL
여러 도구 호출을 병렬로 요청한 다음, 모든 도구 응답을 모델에 다시 전송합니다.
shell
curl -s http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{
"model": "qwen3",
"messages": [{"role": "user", "content": "What are the current weather conditions and temperature in New York and London?"}],
"stream": false,
"tools": [
{
"type": "function",
"function": {
"name": "get_temperature",
"description": "Get the current temperature for a city",
"parameters": {
"type": "object",
"required": ["city"],
"properties": {
"city": {"type": "string", "description": "The name of the city"}
}
}
}
},
{
"type": "function",
"function": {
"name": "get_conditions",
"description": "Get the current weather conditions for a city",
"parameters": {
"type": "object",
"required": ["city"],
"properties": {
"city": {"type": "string", "description": "The name of the city"}
}
}
}
}
]
}'여러 도구 결과로 응답 생성
shell
curl -s http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{
"model": "qwen3",
"messages": [
{"role": "user", "content": "What are the current weather conditions and temperature in New York and London?"},
{
"role": "assistant",
"tool_calls": [
{
"type": "function",
"function": {
"index": 0,
"name": "get_temperature",
"arguments": {"city": "New York"}
}
},
{
"type": "function",
"function": {
"index": 1,
"name": "get_conditions",
"arguments": {"city": "New York"}
}
},
{
"type": "function",
"function": {
"index": 2,
"name": "get_temperature",
"arguments": {"city": "London"}
}
},
{
"type": "function",
"function": {
"index": 3,
"name": "get_conditions",
"arguments": {"city": "London"}
}
}
]
},
{"role": "tool", "tool_name": "get_temperature", "content": "22°C"},
{"role": "tool", "tool_name": "get_conditions", "content": "Partly cloudy"},
{"role": "tool", "tool_name": "get_temperature", "content": "15°C"},
{"role": "tool", "tool_name": "get_conditions", "content": "Rainy"}
],
"stream": false
}'Python
python
from ollama import chat
def get_temperature(city: str) -> str:
"""도시의 현재 온도를 가져옵니다.
Args:
city: 도시 이름
Returns:
도시의 현재 온도
"""
temperatures = {
"New York": "22°C",
"London": "15°C",
"Tokyo": "18°C"
}
return temperatures.get(city, "Unknown")
def get_conditions(city: str) -> str:
"""도시의 현재 날씨 상태를 가져옵니다.
Args:
city: 도시 이름
Returns:
도시의 현재 날씨 상태
"""
conditions = {
"New York": "Partly cloudy",
"London": "Rainy",
"Tokyo": "Sunny"
}
return conditions.get(city, "Unknown")
messages = [{'role': 'user', 'content': 'What are the current weather conditions and temperature in New York and London?'}]
# 파이썬 클라이언트는 함수를 자동으로 도구 스키마로 파싱하므로 직접 전달할 수 있습니다.
# 스키마도 tools 목록에 직접 전달할 수 있습니다.
response = chat(model='qwen3', messages=messages, tools=[get_temperature, get_conditions], think=True)
# 어시스턴트 메시지를 messages에 추가합니다.
messages.append(response.message)
if response.message.tool_calls:
# 각 도구 호출을 처리합니다.
for call in response.message.tool_calls:
# 해당 도구를 실행합니다.
if call.function.name == 'get_temperature':
result = get_temperature(**call.function.arguments)
elif call.function.name == 'get_conditions':
result = get_conditions(**call.function.arguments)
else:
result = 'Unknown tool'
# 도구 결과를 messages에 추가합니다.
messages.append({'role': 'tool', 'tool_name': call.function.name, 'content': str(result)})
# 최종 응답을 생성합니다.
final_response = chat(model='qwen3', messages=messages, tools=[get_temperature, get_conditions], think=True)
print(final_response.message.content)JavaScript
typescript
import ollama from 'ollama'
function getTemperature(city: string): string {
const temperatures: { [key: string]: string } = {
"New York": "22°C",
"London": "15°C",
"Tokyo": "18°C"
}
return temperatures[city] || "Unknown"
}
function getConditions(city: string): string {
const conditions: { [key: string]: string } = {
"New York": "Partly cloudy",
"London": "Rainy",
"Tokyo": "Sunny"
}
return conditions[city] || "Unknown"
}
const tools = [
{
type: 'function',
function: {
name: 'get_temperature',
description: '도시의 현재 온도를 가져옵니다.',
parameters: {
type: 'object',
required: ['city'],
properties: {
city: { type: 'string', description: '도시 이름' },
},
},
},
},
{
type: 'function',
function: {
name: 'get_conditions',
description: '도시의 현재 날씨 상태를 가져옵니다.',
parameters: {
type: 'object',
required: ['city'],
properties: {
city: { type: 'string', description: '도시 이름' },
},
},
},
}
]
const messages = [{ role: 'user', content: 'What are the current weather conditions and temperature in New York and London?' }]
const response = await ollama.chat({
model: 'qwen3',
messages,
tools,
think: true
})
// 어시스턴트 메시지를 messages에 추가합니다.
messages.push(response.message)
if (response.message.tool_calls) {
// 각 도구 호출을 처리합니다.
for (const call of response.message.tool_calls) {
// 해당 도구를 실행합니다.
let result: string
if (call.function.name === 'get_temperature') {
const args = call.function.arguments as { city: string }
result = getTemperature(args.city)
} else if (call.function.name === 'get_conditions') {
const args = call.function.arguments as { city: string }
result = getConditions(args.city)
} else {
result = 'Unknown tool'
}
// 도구 결과를 messages에 추가합니다.
messages.push({ role: 'tool', tool_name: call.function.name, content: result })
}
// 최종 응답을 생성합니다.
const finalResponse = await ollama.chat({ model: 'qwen3', messages, tools, think: true })
console.log(finalResponse.message.content)
}다중 턴 도구 호출 (에이전트 루프)
에이전트 루프를 사용하면 모델이 도구를 호출할 시기를 결정하고 해당 결과를 응답에 통합할 수 있습니다. 또한 모델이 루프 내에 있으며 여러 번의 도구 호출을 수행할 수 있다고 알려주는 것이 도움이 될 수 있습니다.
Python
python
from ollama import chat, ChatResponse
def add(a: int, b: int) -> int:
"""두 숫자를 더합니다."""
"""
Args:
a: 첫 번째 숫자
b: 두 번째 숫자
Returns:
두 숫자의 합
"""
return a + b
def multiply(a: int, b: int) -> int:
"""두 숫자를 곱합니다."""
"""
Args:
a: 첫 번째 숫자
b: 두 번째 숫자
Returns:
두 숫자의 곱
"""
return a * b
available_functions = {
'add': add,
'multiply': multiply,
}
messages = [{'role': 'user', 'content': 'What is (11434+12341)*412?'}]
while True:
response: ChatResponse = chat(
model='qwen3',
messages=messages,
tools=[add, multiply],
think=True,
)
messages.append(response.message)
print("Thinking: ", response.message.thinking)
print("Content: ", response.message.content)
if response.message.tool_calls:
for tc in response.message.tool_calls:
if tc.function.name in available_functions:
print(f"Calling {tc.function.name} with arguments {tc.function.arguments}")
result = available_functions[tc.function.name](./**tc.function.arguments)
print(f"Result: {result}")
# 도구 결과를 messages에 추가합니다.
messages.append({'role': 'tool', 'tool_name': tc.function.name, 'content': str(result)})
else:
# 더 이상 도구 호출이 없을 때 루프를 종료합니다.
break
# 업데이트된 메시지로 루프를 계속합니다.JavaScript
typescript
import ollama from 'ollama'
type ToolName = 'add' | 'multiply'
function add(a: number, b: number): number {
return a + b
}
function multiply(a: number, b: number): number {
return a * b
}
const availableFunctions: Record<ToolName, (a: number, b: number) => number> = {
add,
multiply,
}
const tools = [
{
type: 'function',
function: {
name: 'add',
description: '두 숫자를 더합니다.',
parameters: {
type: 'object',
required: ['a', 'b'],
properties: {
a: { type: 'integer', description: '첫 번째 숫자' },
b: { type: 'integer', description: '두 번째 숫자' },
},
},
},
},
{
type: 'function',
function: {
name: 'multiply',
description: '두 숫자를 곱합니다.',
parameters: {
type: 'object',
required: ['a', 'b'],
properties: {
a: { type: 'integer', description: '첫 번째 숫자' },
b: { type: 'integer', description: '두 번째 숫자' },
},
},
},
}
]
async function agentLoop() {
const messages = [{ role: 'user', content: 'What is (11434+12341)*412?' }]
while (true) {
const response = await ollama.chat({
model: 'qwen3',
messages,
tools,
think: true,
})
messages.push(response.message)
console.log('Thinking:', response.message.thinking)
console.log('Content:', response.message.content)
const toolCalls = response.message.tool_calls ?? []
if (toolCalls.length) {
for (const call of toolCalls) {
const fn = availableFunctions[call.function.name as ToolName]
if (!fn) {
continue
}
const args = call.function.arguments as { a: number; b: number }
console.log(`Calling ${call.function.name} with arguments`, args)
const result = fn(args.a, args.b)
console.log(`Result: ${result}`)
messages.push({ role: 'tool', tool_name: call.function.name, content: String(result) })
}
} else {
break
}
}
}
agentLoop().catch(console.error)스트리밍을 사용한 도구 호출
스트리밍 시 thinking, content, tool_calls의 모든 청크를 수집한 다음, 후속 요청에서 해당 필드와 도구 결과를 함께 반환합니다.
Python
python
from ollama import chat
def get_temperature(city: str) -> str:
"""Get the current temperature for a city
Args:
city: The name of the city
Returns:
The current temperature for the city
"""
temperatures = {
'New York': '22°C',
'London': '15°C',
}
return temperatures.get(city, 'Unknown')
messages = [{'role': 'user', 'content': "What is the temperature in New York?"}]
while True:
stream = chat(
model='qwen3',
messages=messages,
tools=[get_temperature],
stream=True,
think=True,
)
thinking = ''
content = ''
tool_calls = []
done_thinking = False
# 부분 필드를 누적합니다
for chunk in stream:
if chunk.message.thinking:
thinking += chunk.message.thinking
print(chunk.message.thinking, end='', flush=True)
if chunk.message.content:
if not done_thinking:
done_thinking = True
print('\n')
content += chunk.message.content
print(chunk.message.content, end='', flush=True)
if chunk.message.tool_calls:
tool_calls.extend(chunk.message.tool_calls)
print(chunk.message.tool_calls)
# 누적된 필드를 메시지에 추가합니다
if thinking or content or tool_calls:
messages.append({'role': 'assistant', 'thinking': thinking, 'content': content, 'tool_calls': tool_calls})
if not tool_calls:
break
for call in tool_calls:
if call.function.name == 'get_temperature':
result = get_temperature(**call.function.arguments)
else:
result = 'Unknown tool'
messages.append({'role': 'tool', 'tool_name': call.function.name, 'content': result})JavaScript
typescript
function getTemperature(city: string): string {
const temperatures: Record<string, string> = {
'New York': '22°C',
'London': '15°C',
}
return temperatures[city] ?? 'Unknown'
}
const getTemperatureTool = {
type: 'function',
function: {
name: 'get_temperature',
description: 'Get the current temperature for a city',
parameters: {
type: 'object',
required: ['city'],
properties: {
city: { type: 'string', description: 'The name of the city' },
},
},
},
}
async function agentLoop() {
const messages = [{ role: 'user', content: "What is the temperature in New York?" }]
while (true) {
const stream = await ollama.chat({
model: 'qwen3',
messages,
tools: [getTemperatureTool],
stream: true,
think: true,
})
let thinking = ''
let content = ''
const toolCalls: any[] = []
let doneThinking = false
for await (const chunk of stream) {
if (chunk.message.thinking) {
thinking += chunk.message.thinking
process.stdout.write(chunk.message.thinking)
}
if (chunk.message.content) {
if (!doneThinking) {
doneThinking = true
process.stdout.write('\n')
}
content += chunk.message.content
process.stdout.write(chunk.message.content)
}
if (chunk.message.tool_calls?.length) {
toolCalls.push(...chunk.message.tool_calls)
console.log(chunk.message.tool_calls)
}
}
if (thinking || content || toolCalls.length) {
messages.push({ role: 'assistant', thinking, content, tool_calls: toolCalls } as any)
}
if (!toolCalls.length) {
break
}
for (const call of toolCalls) {
if (call.function.name === 'get_temperature') {
const args = call.function.arguments as { city: string }
const result = getTemperature(args.city)
messages.push({ role: 'tool', tool_name: call.function.name, content: result } )
} else {
messages.push({ role: 'tool', tool_name: call.function.name, content: 'Unknown tool' } )
}
}
}
}
agentLoop().catch(console.error)
```
이 루프는 어시스턴트 응답을 스트리밍하고, 부분 필드를 누적한 다음, 이를 함께 다시 전달하고 도구 결과를 추가하여 모델이 답변을 완성할 수 있도록 합니다.
## Ollama Python SDK로 함수를 도구로 사용하기
Python SDK는 함수를 자동으로 도구 스키마로 파싱하므로 직접 전달할 수 있습니다.
필요한 경우 스키마를 직접 전달할 수도 있습니다.
```python
from ollama import chat
def get_temperature(city: str) -> str:
"""Get the current temperature for a city
Args:
city: The name of the city
Returns:
The current temperature for the city
"""
temperatures = {
'New York': '22°C',
'London': '15°C',
}
return temperatures.get(city, 'Unknown')
available_functions = {
'get_temperature': get_temperature,
}
# tools 목록의 일부로 함수를 직접 전달합니다
response = chat(model='qwen3', messages=messages, tools=available_functions.values(), think=True)