work hard work smart

专注于AI+Java后端开发。 不断总结,举一反三。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

摘要:大语言模型只能聊天不能做事?Function Call 技术让大模型能够调用外部函数、执行实际操作。本文深度解析其核心原理、两种实现方式和生产实践。

一、痛点:大模型的"残疾"困境

场景重现

假设你问 AI:"订单 20240115001 现在到哪了?"

传统模型的局限:无法访问外部系统,只能回答"我不知道"

Function Call 的突破

用户:订单 20240115001 到哪了?
大模型 → 调用 trackOrder("20240115001")
       → 返回:"已发货,顺丰 SF1234567890"
       → 回答:"您的订单已发货,顺丰快递 SF1234567890"

二、什么是 Function Call?

Function Call 让大模型在对话中主动识别需要调用的外部函数,自动生成参数,并基于返回结果生成回答。

核心流程

graph LR A[用户提问] --> B[大模型分析意图] B --> C{需要调用函数?} C -->|是| D[生成函数调用] D --> E[框架执行函数] E --> F[返回结果给大模型] F --> G[生成最终回答] C -->|否| G

三、Function Call 的工作原理

1. 函数注册:Java → JSON Schema

框架通过注解将函数转换为大模型能理解的 JSON Schema:

public LogisticsInfo trackOrder(OrderTrackRequest request) {
    return logisticsService.query(request.orderId());
}

// 自动生成的 JSON Schema
{
  "name": "trackOrder",
  "description": "根据订单号查询物流状态",
  "parameters": {
    "orderId": {
      "type": "string",
      "description": "订单编号,比如 20240115001"
    }
  }
}

2. 调用流程

用户提问 → 框架发送函数描述给大模型
         → 大模型返回函数调用请求
         → 框架执行业务函数
         → 将结果返回大模型
         → 大模型生成最终回答

四、为什么需要 Function Call?

场景 问题 解决方案
实时数据 模型不知道实时汇率 调用 API 获取最新数据
业务集成 无法访问订单系统 调用企业内部服务
精确计算 大模型数学计算易出错 调用计算器函数
权限控制 敏感操作需要验证 框架拦截+权限检查

五、Spring AI 实战:函数注册方式

1. 引入依赖

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>

2. 配置文件

spring:
  ai:
    openai:
      api-key: your-api-key
      base-url: https://dashscope.aliyuncs.com/compatible-mode/
      chat:
        options:
          model: qwen-plus
          temperature: 0.7

3. 定义 Request/Response

public record StockQueryRequest(
    @JsonProperty(required = true, value = "productName")
    @JsonPropertyDescription("商品名称,比如 iPhone 15 Pro") 
    String productName
) {}

public record StockInfo(int quantity, String warehouse) {}

4. 注册函数(Spring AI 特性)

@Configuration
public class FunctionConfig {
    
    @Bean
    @Description("根据商品名称查询库存")
    public Function<StockQueryRequest, StockInfo> checkStockFunction(
            InventoryService inventoryService) {
        return inventoryService::checkProductStock;
    }
}

Spring AI 关键点

  • @Bean:将函数注册为 Spring Bean
  • @Description:函数描述,Spring AI 会自动转换为 JSON Schema
  • Function<Request, Response>:Spring AI 标准函数接口

5. 调用函数(Spring AI API)

@RestController
@RequestMapping("/function")
public class FunctionCallController {

    @Autowired
    private OpenAiChatModel chatModel;

    @GetMapping("/chat")
    public String chat(@RequestParam("query") String query) {
        return chatModel.call(
            Prompt.builder()
                .functions("checkStockFunction")  // Spring AI 函数名
                .userMessage(query)
                .build()
        ).getContent();
    }
}

测试

curl "http://localhost:8013/function/chat?query=iPhone15Pro库存多少?"
# 输出:iPhone 15 Pro 当前库存 156 台,存放于上海仓。

六、Spring AI 实战:工具注解方式

更简洁的方式,Spring AI 提供了 @Tool 注解:

public class InventoryTools {
    @Tool(description = "根据商品名称查询库存")
    public String checkStock(
            @ToolParam(description = "商品名称,比如 iPhone 15 Pro") 
            String productName) {
        // 查询逻辑
        return "库存 " + quantity + " 台,仓库:" + warehouse;
    }
}

Spring AI 关键点

  • @Tool:Spring AI 工具注解,自动注册为可用函数
  • @ToolParam:参数描述,Spring AI 自动生成 JSON Schema

调用方式

chatModel.call(
    Prompt.builder()
        .tools(new InventoryTools())  // Spring AI 直接传入工具实例
        .userMessage(query)
        .build()
).getContent();

两种方式对比

维度 函数注册(@Bean) 工具注解(@Tool)
代码量 较多(需 Configuration) 简洁
灵活性 高(依赖注入)
Spring 集成 完全集成 部分集成
适用场景 复杂业务 简单功能

七、高级应用

1. 多函数协同

// 注册多个函数,大模型自动选择
@Bean @Description("查询库存")
public Function<StockQueryRequest, StockInfo> checkStock(...) { ... }

@Bean @Description("计算折扣价格")
public Function<PriceRequest, PriceResponse> calculatePrice(...) { ... }

// 用户:"iPhone 15 Pro 有货吗?买 10 台多少钱?"
// 大模型自动调用 checkStock 和 calculatePrice

2. 动态注册

// 根据用户权限动态注册函数
Prompt.Builder builder = Prompt.builder();
builder.functions("checkStock", "calculatePrice");  // 所有用户

if (user.isAdmin()) {
    builder.functions("deleteProduct");  // 仅管理员
}

八、局限性

  1. 依赖模型支持:GPT-3.5/4、Qwen、Claude 支持,早期模型不支持
  2. 函数数量限制:函数描述占用 Token,建议按需注册
  3. 复杂参数:文件、图片等复杂对象处理麻烦

替代方案:ReAct 模式

通过 Prompt 引导模型按格式思考,不依赖原生 Function Call:

Thought: 我需要做什么
Action: 使用什么工具
Action Input: 工具参数
Observation: 工具返回结果
Final Answer: 最终回答
维度 Function Call ReAct
实现复杂度
可靠性 高(结构化)
灵活性 高(任意模型)

九、Spring AI 配置与完整示例

完整配置

server:
  port: 8013
  
spring:
  ai:
    openai:
      api-key: sk-xxx
      base-url: https://dashscope.aliyuncs.com/compatible-mode/
      chat:
        options:
          model: qwen-plus
          temperature: 0.7
          maxTokens: 5000

Spring AI 完整代码

// 1. Service 层
@Service
public class InventoryService {
    public StockInfo checkProductStock(StockQueryRequest request) {
        int quantity = database.queryStock(request.productName());
        String warehouse = database.getWarehouseLocation(request.productName());
        return new StockInfo(quantity, warehouse);
    }

    public record StockQueryRequest(
        @JsonProperty(required = true, value = "productName")
        @JsonPropertyDescription("商品名称,比如 iPhone 15 Pro") 
        String productName
    ) {}

    public record StockInfo(int quantity, String warehouse) {}
}

// 2. 配置层
@Configuration
public class FunctionConfig {
    @Bean
    @Description("根据商品名称查询库存")
    public Function<StockQueryRequest, StockInfo> checkStockFunction(
            InventoryService inventoryService) {
        return inventoryService::checkProductStock;
    }
}

// 3. 控制器层
@RestController
@RequestMapping("/function")
public class FunctionCallController {
    @Autowired
    private OpenAiChatModel chatModel;

    @GetMapping("/chat")
    public String chat(@RequestParam("query") String query) {
        return chatModel.call(
            Prompt.builder()
                .functions("checkStockFunction")
                .userMessage(query)
                .build()
        ).getContent();
    }
}

十、最佳实践

✅ DO

  1. 描述要具体@Description("查询库存数量和仓库位置")@Description("查询库存")
  2. 参数带示例@JsonPropertyDescription("商品名,比如 iPhone 15 Pro")
  3. 按需注册:根据场景注册函数,不要一次性全注册
  4. 异常处理:捕获异常,返回友好错误信息

❌ DON'T

  1. 简单问题不调用:"你好" 直接回答,不要调用函数
  2. 返回不要过大:分页返回,不要一次返回 10 万条数据
  3. 注意权限:敏感操作需要权限验证

十一、总结

Function Call 让大模型从"聊天机器人"进化为"智能助手":

  • 核心价值:实时数据、业务集成、精确计算、权限控制
  • 两种实现:函数注册(复杂业务)、工具注解(简单功能)
  • 关键要点:描述要具体、参数带示例、按需注册、异常处理

参考资源