函数与工具

ai-lib为所有支持的提供商提供统一的函数调用。这允许你的AI模型调用外部函数和工具,实现更具交互性和功能性的应用程序。

基本函数调用

使用Tool结构体定义工具并将其附加到聊天请求:

use ai_lib::{AiClient, Provider, ChatCompletionRequest, Message, Content, Tool, FunctionCallPolicy};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = AiClient::new(Provider::OpenAI)?;

    // 定义天气工具
    let weather_tool = Tool::new_json(
        "get_weather",
        Some("获取城市的当前天气信息"),
        serde_json::json!({
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "城市名称"
                }
            },
            "required": ["location"]
        })
    );

    // 创建带工具的聊天请求
    let req = ChatCompletionRequest::new(
        client.default_chat_model(),
        vec![Message::user(Content::new_text("巴黎的天气怎么样?"))]
    )
    .with_functions(vec![weather_tool])
    .with_function_call(FunctionCallPolicy::Auto);

    let resp = client.chat_completion(req).await?;
    println!("响应: {}", resp.first_text()?);
    Ok(())
}

函数调用策略

控制何时以及如何调用函数:

use ai_lib::{FunctionCallPolicy, Tool};

// Auto:让模型决定何时调用函数
let auto_policy = FunctionCallPolicy::Auto;

// None:从不调用函数
let none_policy = FunctionCallPolicy::None;

// Required:总是调用特定函数
let required_policy = FunctionCallPolicy::Required("get_weather".to_string());

let req = ChatCompletionRequest::new(model, messages)
    .with_functions(vec![weather_tool])
    .with_function_call(auto_policy);

处理函数调用

当模型决定调用函数时,你需要处理执行:

use ai_lib::{Message, Role, Content, FunctionCall};

// 检查响应是否包含函数调用
if let Some(choice) = resp.choices.first() {
    if let Some(function_call) = &choice.message.function_call {
        match function_call.name.as_str() {
            "get_weather" => {
                // 提取参数
                let location = function_call.arguments
                    .get("location")
                    .and_then(|v| v.as_str())
                    .unwrap_or("unknown");

                // 执行你的函数
                let weather_data = get_weather_impl(location).await;

                // 创建工具响应消息
                let tool_message = Message {
                    role: Role::Assistant, // 注意:ai-lib使用Assistant角色进行工具响应
                    content: Content::new_json(serde_json::json!({
                        "temperature": weather_data.temp,
                        "condition": weather_data.condition,
                        "location": location
                    })),
                    function_call: None,
                };

                // 使用工具结果继续对话
                let follow_up_req = ChatCompletionRequest::new(
                    model,
                    vec![
                        Message::user(Content::new_text("巴黎的天气怎么样?")),
                        choice.message.clone(),
                        tool_message,
                    ]
                );

                let final_resp = client.chat_completion(follow_up_req).await?;
                println!("最终答案: {}", final_resp.first_text()?);
            }
            _ => println!("未知函数: {}", function_call.name),
        }
    } else {
        println!("无函数调用: {}", choice.message.content.as_text());
    }
}

多个工具

在单个请求中定义和使用多个工具:

let weather_tool = Tool::new_json(
    "get_weather",
    Some("获取天气信息"),
    serde_json::json!({
        "type": "object",
        "properties": {
            "location": {"type": "string"}
        },
        "required": ["location"]
    })
);

let news_tool = Tool::new_json(
    "get_news",
    Some("获取最新新闻标题"),
    serde_json::json!({
        "type": "object",
        "properties": {
            "topic": {"type": "string"},
            "count": {"type": "integer", "minimum": 1, "maximum": 10}
        },
        "required": ["topic"]
    })
);

let req = ChatCompletionRequest::new(model, messages)
    .with_functions(vec![weather_tool, news_tool])
    .with_function_call(FunctionCallPolicy::Auto);

流式函数调用

函数调用与流式响应配合使用:

use futures::StreamExt;

let mut stream = client.chat_completion_stream(req).await?;
let mut full_response = String::new();

while let Some(chunk) = stream.next().await {
    let c = chunk?;
    if let Some(delta) = c.choices[0].delta.content.clone() {
        full_response.push_str(&delta);
        print!("{delta}");
    }
    
    // 检查块中的函数调用
    if let Some(function_call) = &c.choices[0].delta.function_call {
        println!("\n检测到函数调用: {}", function_call.name);
    }
}

工具执行助手

这是一个带有工具执行助手的完整示例:

async fn execute_tool_call(function_call: &FunctionCall) -> serde_json::Value {
    match function_call.name.as_str() {
        "get_weather" => {
            let location = function_call.arguments
                .get("location")
                .and_then(|v| v.as_str())
                .unwrap_or("unknown");
            
            // 模拟天气API调用
            serde_json::json!({
                "temperature": 22.5,
                "condition": "sunny",
                "location": location
            })
        }
        "get_news" => {
            let topic = function_call.arguments
                .get("topic")
                .and_then(|v| v.as_str())
                .unwrap_or("general");
            
            serde_json::json!({
                "headlines": [
                    format!("突发: {} 新闻更新1", topic),
                    format!("最新: {} 新闻更新2", topic),
                ]
            })
        }
        _ => serde_json::json!({"error": "未知函数"})
    }
}

// 在你的对话循环中使用
let mut conversation = vec![Message::user(Content::new_text("东京的天气和科技新闻怎么样?"))];

loop {
    let req = ChatCompletionRequest::new(model, conversation.clone())
        .with_functions(vec![weather_tool, news_tool])
        .with_function_call(FunctionCallPolicy::Auto);

    let resp = client.chat_completion(req).await?;
    
    if let Some(choice) = resp.choices.first() {
        if let Some(function_call) = &choice.message.function_call {
            // 执行函数
            let result = execute_tool_call(function_call).await;
            
            // 将函数调用和结果添加到对话中
            conversation.push(choice.message.clone());
            conversation.push(Message {
                role: Role::Assistant,
                content: Content::new_json(result),
                function_call: None,
            });
        } else {
            // 无函数调用,完成
            println!("最终响应: {}", choice.message.content.as_text());
            break;
        }
    }
}

提供商兼容性

函数调用在所有主要提供商中都受支持:

  • OpenAI:GPT-4和GPT-3.5的完整支持
  • Anthropic:Claude 3工具使用
  • Google Gemini:函数调用支持
  • Groq:有限的函数调用支持
  • Mistral:兼容模型的函数调用

查看提供商页面了解具体能力。

最佳实践

  1. 清晰的描述:编写详细的函数描述,帮助模型理解何时使用它们
  2. 验证参数:执行前始终验证函数参数
  3. 错误处理:优雅地处理函数执行错误
  4. 速率限制:调用外部服务时注意API速率限制
  5. 安全性:永远不要执行不受信任的代码或进行不安全的系统调用

下一步

Build: 3de64ed · 2025-09-09T12:50:59.722Z · v0.21