4. 进阶:Tool use 工具调用

Tool use 是 AI Agent 的核心能力。通过定义工具,让 AI 可以调用外部 API、查询数据库、执行代码等。

4.1 工作原理

用户:"北京现在几度?"

1. 你把问题 + 工具定义 → 发给 LLM
2. LLM 判断需要用 get_weather 工具,返回工具调用请求
3. 你的代码实际执行 get_weather("北京"),得到结果
4. 把结果返回给 LLM
5. LLM 根据结果生成最终回复:"北京现在25度,晴天"

LLM 不能直接调用工具,它只是"建议"调用哪个工具、传什么参数。 实际执行由你的代码完成,这一点很重要。

4.2 定义并使用工具

import { generateText, stepCountIs, tool } from 'ai'
import { createDeepSeek } from '@ai-sdk/deepseek'
import { z } from 'zod'
import 'dotenv/config'

const deepseek = createDeepSeek({
    apiKey: process.env.DEEPSEEK_API_KEY,
})

// 定义工具
const tools: any = {
    // 工具1:获取天气(模拟)
    get_weather: tool({
        description: '获取指定城市的当前天气信息',
        // ⚠️ 注意:AI SDK v6 把工具参数字段从 parameters 改名成了 inputSchema
        //    如果还用旧名 parameters,schema 会被忽略,模型 API 会报:
        //    "schema must be a JSON Schema of type: object, got type: null"
        inputSchema: z.object({
            city: z.string().describe('城市名称,如:北京、上海'),
        }),
        execute: async function ({ city }) {
            // 实际项目中这里调用真实的天气 API
            console.log(`[工具调用] 查询城市天气: ${city}`)
            return {
                city,
                temperature: 25,
                condition: '晴天',
                humidity: '60%',
            }
        },
    }),

    // 工具2:计算器
    calculate: tool({
        description: '执行数学计算',
        inputSchema: z.object({
            expression: z.string().describe('数学表达式,如:2 + 3 * 4'),
        }),
        execute: async ({ expression }) => {
            console.log(`[工具调用] 计算: ${expression}`)
            // 注意:eval 在生产环境有安全风险,这里仅做演示
            const result = eval(expression)
            return { expression, result }
        },
    }),
}

async function main() {
    const result = await generateText({
        model: deepseek('deepseek-v4-flash'),
        tools,
        // stopWhen 控制最多执行几轮工具调用
        // 默认是1(只调用一次),设置更大的数值支持多步执行
        stopWhen: stepCountIs(3),
        messages: [
            {
                role: 'user',
                content: '北京现在天气怎么样?另外帮我算一下 (73 + 27) * 3 等于多少?',
            },
        ],
    })

    console.log('\n最终回复:', result.text)
    console.log('\n另一个使用方式:', result.output)
}

main()

4.3 工具定义的最佳实践

// ✅ 好的工具定义:description 清晰,参数有描述
const good_tool = tool({
  description: '在数据库中搜索用户信息,支持按姓名或邮箱查询',
  inputSchema: z.object({
    query: z.string().describe('搜索关键词,可以是用户名或邮箱地址'),
    limit: z.number().optional().describe('返回结果数量,默认10条'),
  }),
  execute: async ({ query, limit = 10 }) => { /* ... */ },
})

// ❌ 差的工具定义:描述模糊,参数没有说明
const bad_tool = tool({
  description: '查询用户',  // 太模糊,AI 不知道什么时候该用
  inputSchema: z.object({
    q: z.string(),  // 没有 describe,AI 不清楚传什么
  }),
  execute: async ({ q }) => { /* ... */ },
})