Function Calling 비교
왜 비교가 중요한가
섹션 제목: “왜 비교가 중요한가”에이전트 시스템을 설계할 때 특정 LLM 제공자에 종속(lock-in)되면, 더 나은 모델로 전환하거나 멀티 제공자 전략을 취할 때 비용이 큽니다. 세 주요 제공자의 Function Calling 방식을 이해하면 적절한 추상화 레이어를 설계할 수 있습니다.
도구 정의 형식 비교
섹션 제목: “도구 정의 형식 비교”세 제공자 모두 JSON Schema를 기반으로 하지만, 래핑 구조가 다릅니다.
OpenAI
섹션 제목: “OpenAI”const tools: OpenAI.Tool[] = [ { type: 'function', function: { name: 'get_weather', description: '특정 도시의 현재 날씨를 조회합니다', parameters: { type: 'object', properties: { city: { type: 'string', description: '도시명' }, unit: { type: 'string', enum: ['celsius', 'fahrenheit'] }, }, required: ['city'], }, }, },];
const response = await openai.chat.completions.create({ model: 'gpt-4o', messages: [{ role: 'user', content: '서울 날씨 알려줘' }], tools, tool_choice: 'auto', // 'none' | 'auto' | { type: 'function', function: { name } }});Anthropic
섹션 제목: “Anthropic”const tools: Anthropic.Tool[] = [ { name: 'get_weather', description: '특정 도시의 현재 날씨를 조회합니다', input_schema: { // OpenAI의 parameters → input_schema type: 'object', properties: { city: { type: 'string', description: '도시명' }, unit: { type: 'string', enum: ['celsius', 'fahrenheit'] }, }, required: ['city'], }, },];
const response = await anthropic.messages.create({ model: 'claude-opus-4-5', max_tokens: 1024, messages: [{ role: 'user', content: '서울 날씨 알려줘' }], tools, tool_choice: { type: 'auto' }, // { type: 'auto' | 'any' | 'tool', name?: string }});Google (Gemini)
섹션 제목: “Google (Gemini)”const tools: GoogleGenerativeAI.Tool[] = [ { functionDeclarations: [ // OpenAI의 tools 배열 내 items → functionDeclarations { name: 'get_weather', description: '특정 도시의 현재 날씨를 조회합니다', parameters: { type: 'OBJECT', // 대문자 타입 문자열 properties: { city: { type: 'STRING', description: '도시명' }, unit: { type: 'STRING', enum: ['celsius', 'fahrenheit'] }, }, required: ['city'], }, }, ], },];
const model = genai.getGenerativeModel({ model: 'gemini-2.0-flash', tools, toolConfig: { functionCallingConfig: { mode: 'AUTO' } },});도구 호출 응답 처리
섹션 제목: “도구 호출 응답 처리”각 제공자가 도구 호출 요청을 반환하는 방식도 다릅니다.
OpenAI 응답 처리
섹션 제목: “OpenAI 응답 처리”const message = response.choices[0].message;
if (message.tool_calls) { for (const toolCall of message.tool_calls) { const name = toolCall.function.name; const args = JSON.parse(toolCall.function.arguments); // 문자열 파싱 필요 const result = await executeToolCall(name, args);
// 도구 결과를 messages에 추가 messages.push({ role: 'tool', tool_call_id: toolCall.id, content: JSON.stringify(result), }); }}Anthropic 응답 처리
섹션 제목: “Anthropic 응답 처리”const content = response.content;
for (const block of content) { if (block.type === 'tool_use') { // OpenAI의 tool_calls → tool_use 블록 const name = block.name; const args = block.input; // 이미 객체, 파싱 불필요 const result = await executeToolCall(name, args);
// 도구 결과는 user role의 tool_result 블록으로 messages.push({ role: 'user', content: [ { type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(result), }, ], }); }}Google 응답 처리
섹션 제목: “Google 응답 처리”const candidates = response.candidates;const parts = candidates[0].content.parts;
for (const part of parts) { if (part.functionCall) { const name = part.functionCall.name; const args = part.functionCall.args; // 이미 객체 const result = await executeToolCall(name, args);
// functionResponse로 결과 전달 history.push({ role: 'function', parts: [{ functionResponse: { name, response: result } }], }); }}핵심 기능 비교 테이블
섹션 제목: “핵심 기능 비교 테이블”| 기능 | OpenAI | Anthropic | |
|---|---|---|---|
| 도구 정의 키 | tools[].function.parameters | tools[].input_schema | functionDeclarations[].parameters |
| 타입 표기 | 소문자 (string) | 소문자 (string) | 대문자 (STRING) |
| 응답 위치 | choices[0].message.tool_calls | content[].tool_use 블록 | candidates[0].content.parts[].functionCall |
| 인수 형식 | JSON 문자열 (파싱 필요) | 객체 (파싱 불필요) | 객체 (파싱 불필요) |
| 병렬 도구 호출 | 지원 (배열로 반환) | 지원 (여러 블록) | 지원 (여러 parts) |
| 강제 도구 사용 | tool_choice: {type: 'function', function: {name}} | tool_choice: {type: 'tool', name} | functionCallingConfig: {mode: 'ANY', allowedFunctionNames} |
| 스트리밍 중 도구 | 지원 | 지원 | 지원 |
추상화 레이어 설계
섹션 제목: “추상화 레이어 설계”제공자 차이를 흡수하는 통합 인터페이스를 만들면 모델 전환이 쉬워집니다.
interface UnifiedTool { name: string; description: string; parameters: JsonSchema; // 소문자 표준 JSON Schema}
interface UnifiedToolCall { id: string; name: string; arguments: Record<string, unknown>; // 항상 파싱된 객체}
// 제공자별 변환기function toAnthropicTools(tools: UnifiedTool[]): Anthropic.Tool[] { return tools.map((t) => ({ name: t.name, description: t.description, input_schema: t.parameters, }));}
function toOpenAITools(tools: UnifiedTool[]): OpenAI.Tool[] { return tools.map((t) => ({ type: 'function', function: { name: t.name, description: t.description, parameters: t.parameters, }, }));}
// 응답에서 도구 호출 추출 (제공자 무관)function extractToolCalls(response: ProviderResponse): UnifiedToolCall[] { if (isOpenAIResponse(response)) { return (response.choices[0].message.tool_calls ?? []).map((tc) => ({ id: tc.id, name: tc.function.name, arguments: JSON.parse(tc.function.arguments), })); } if (isAnthropicResponse(response)) { return response.content .filter((b) => b.type === 'tool_use') .map((b) => ({ id: b.id, name: b.name, arguments: b.input })); } // Google...}세 제공자의 Function Calling은 JSON Schema 기반이라는 공통점을 가지지만, 래핑 구조와 응답 형식이 다릅니다. OpenAI는 인수를 문자열로 반환하므로 파싱이 필요하고, Anthropic은 tool_use 블록, Google은 functionCall 파트를 사용합니다. 통합 인터페이스를 설계하면 모델 전환 비용을 크게 줄일 수 있습니다.