190 lines
4.0 KiB
Markdown
190 lines
4.0 KiB
Markdown
# Function Calling Patterns
|
|
|
|
**Last Updated**: 2025-10-25
|
|
|
|
Advanced patterns for implementing function calling (tool calling) with OpenAI's Chat Completions API.
|
|
|
|
---
|
|
|
|
## Basic Pattern
|
|
|
|
```typescript
|
|
const tools = [
|
|
{
|
|
type: 'function',
|
|
function: {
|
|
name: 'get_weather',
|
|
description: 'Get current weather for a location',
|
|
parameters: {
|
|
type: 'object',
|
|
properties: {
|
|
location: { type: 'string', description: 'City name' },
|
|
unit: { type: 'string', enum: ['celsius', 'fahrenheit'] },
|
|
},
|
|
required: ['location'],
|
|
},
|
|
},
|
|
},
|
|
];
|
|
```
|
|
|
|
---
|
|
|
|
## Advanced Patterns
|
|
|
|
### 1. Parallel Tool Calls
|
|
|
|
The model can call multiple tools simultaneously:
|
|
|
|
```typescript
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-5',
|
|
messages: [
|
|
{ role: 'user', content: 'What is the weather in SF and NYC?' }
|
|
],
|
|
tools: tools,
|
|
});
|
|
|
|
// Model may return multiple tool_calls
|
|
const toolCalls = completion.choices[0].message.tool_calls;
|
|
|
|
// Execute all in parallel
|
|
const results = await Promise.all(
|
|
toolCalls.map(call => executeFunction(call.function.name, call.function.arguments))
|
|
);
|
|
```
|
|
|
|
### 2. Dynamic Tool Generation
|
|
|
|
Generate tools based on runtime context:
|
|
|
|
```typescript
|
|
function generateTools(database: Database) {
|
|
const tables = database.getTables();
|
|
|
|
return tables.map(table => ({
|
|
type: 'function',
|
|
function: {
|
|
name: `query_${table.name}`,
|
|
description: `Query the ${table.name} table`,
|
|
parameters: {
|
|
type: 'object',
|
|
properties: table.columns.reduce((acc, col) => ({
|
|
...acc,
|
|
[col.name]: { type: col.type, description: col.description },
|
|
}), {}),
|
|
},
|
|
},
|
|
}));
|
|
}
|
|
```
|
|
|
|
### 3. Tool Chaining
|
|
|
|
Chain tool results:
|
|
|
|
```typescript
|
|
async function chatWithToolChaining(userMessage: string) {
|
|
let messages = [{ role: 'user', content: userMessage }];
|
|
|
|
while (true) {
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-5',
|
|
messages,
|
|
tools,
|
|
});
|
|
|
|
const message = completion.choices[0].message;
|
|
messages.push(message);
|
|
|
|
if (!message.tool_calls) {
|
|
return message.content; // Final answer
|
|
}
|
|
|
|
// Execute tool calls and add results
|
|
for (const toolCall of message.tool_calls) {
|
|
const result = await executeFunction(
|
|
toolCall.function.name,
|
|
toolCall.function.arguments
|
|
);
|
|
|
|
messages.push({
|
|
role: 'tool',
|
|
tool_call_id: toolCall.id,
|
|
content: JSON.stringify(result),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Error Handling in Tools
|
|
|
|
```typescript
|
|
async function executeFunction(name: string, argsString: string) {
|
|
try {
|
|
const args = JSON.parse(argsString);
|
|
|
|
switch (name) {
|
|
case 'get_weather':
|
|
return await getWeather(args.location, args.unit);
|
|
|
|
default:
|
|
return { error: `Unknown function: ${name}` };
|
|
}
|
|
} catch (error: any) {
|
|
return { error: error.message };
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5. Streaming with Tools
|
|
|
|
```typescript
|
|
const stream = await openai.chat.completions.create({
|
|
model: 'gpt-5',
|
|
messages,
|
|
tools,
|
|
stream: true,
|
|
});
|
|
|
|
for await (const chunk of stream) {
|
|
const delta = chunk.choices[0]?.delta;
|
|
|
|
// Check for tool calls in streaming
|
|
if (delta?.tool_calls) {
|
|
// Accumulate tool call data
|
|
console.log('Tool call chunk:', delta.tool_calls);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
✅ **Schema Design**:
|
|
- Provide clear descriptions for each parameter
|
|
- Use enum when options are limited
|
|
- Mark required vs optional parameters
|
|
|
|
✅ **Error Handling**:
|
|
- Return structured error objects
|
|
- Don't throw exceptions from tool functions
|
|
- Let the model handle error recovery
|
|
|
|
✅ **Performance**:
|
|
- Execute independent tool calls in parallel
|
|
- Cache tool results when appropriate
|
|
- Limit recursion depth to avoid infinite loops
|
|
|
|
❌ **Don't**:
|
|
- Expose sensitive internal functions
|
|
- Allow unlimited recursion
|
|
- Skip parameter validation
|
|
- Return unstructured error messages
|
|
|
|
---
|
|
|
|
**See Also**: Official Function Calling Guide (https://platform.openai.com/docs/guides/function-calling)
|