Function Call,突破数据边界实现 “知行合一”

140 阅读3分钟

LLM的挑战

大语言模型(Large Language Model,简称 LLM)是一类基于深度学习技术、具备大规模参数的语言处理模型,其核心能力在于理解和生成自然语言,并通过海量数据训练获得泛化性极强的语言理解与生成能力。这使LLM过于依赖训练数据,从而带来以下问题和挑战:

  • 幻觉(Hallucination) :有时候会不懂装懂,生成与事实不符的内容。
  • 知识时效性:训练数据截止后无法自动更新。

解决方法-- FunctionCall

Function Call(函数调用) 提供了连接大语言模型(LLM)与外部工具的关键机制,它使LLM能够突破纯文本生成的限制,通过调用外部函数(如 API、工具、数据库等)来执行实际操作,实现更复杂的任务闭环。

函数调用的核心流程

  1. 注册工具:定义函数签名并提供实现。
  2. 首次调用:将用户问题和工具列表发送给模型。
  3. 工具执行:解析模型返回的工具调用请求,执行对应函数。
  4. 结果整合:将函数结果作为新输入再次调用模型,生成最终回答。

案例演示

以下是LLM使用Function Call获取实时股价的过程演示

一、环境准备与客户端初始化

load_dotenv('.env.local')  # 从环境文件加载 API 密钥

client = OpenAI(
  api_key=os.getenv('DEEPSEEK_API_KEY'),
  base_url="https://api.deepseek.com/v1",  # 指向 DeepSeek 接口
)
  • 关键点:通过 base_url 参数将 OpenAI 客户端指向第三方模型(DeepSeek)的 API 端点。

二、工具定义与注册

tools = [
  {
    "type": "function",
    "function": {
      "name": "get_closing_price",
      "description": "获取指定股票的收盘价",
      "parameters": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "股票名称",
          },
        },
        "required": ["name"],
      }
    }
  }
]
  • 关键点

    1. 使用 JSON Schema 格式定义函数签名。
    2. 通过 description 字段向模型解释函数用途。

三、函数实现

def get_closing_price(name):
  if name == '青岛啤酒':
    return '67.92'
  elif name == '贵州茅台':
    return '1488.21'
  else:
    return '未找到该股票'
  • 模拟实现:实际应用中应调用真实股票 API(如 Alpha Vantage、东方财富等)。

四、主流程:消息发送与函数调用

1. 首次请求用户问题

messages = [{"role": "user", "content": "青岛啤酒的收盘价是多少?"}]
response = send_message(messages)
  • 模型决策:DeepSeek 模型分析问题后,决定调用 get_closing_price 函数。

2. 解析工具调用请求

message = response.choices[0].message
if message.tool_calls:  # 检查是否有工具调用
  tool_call = message.tool_calls[0]
  if tool_call.function.name == "get_closing_price":
    # 解析参数并执行函数
    arguments_dict = json.loads(tool_call.function.arguments)
    price = get_closing_price(arguments_dict['name'])
    
    # 将函数执行结果添加到对话历史
    messages.append({
      "role": "tool",
      "content": price,
      "tool_call_id": tool_call.id
    })
  • 关键点

    1. 通过 tool_calls 字段获取模型选择的工具。
    2. 将函数执行结果作为 tool 角色的消息加入对话。

3. 二次请求生成最终回答

response = send_message(messages)  # 带着函数结果再次调用模型
print(response.choices[0].message.content)  # 输出:"青岛啤酒的收盘价是67.92元"
  • 模型行为:模型结合函数返回的价格数据,生成自然语言回答。

五、完整代码与运行结果

import json
from email import message
from openai import OpenAI
import os
from dotenv import load_dotenv
load_dotenv('.env.local')

client = OpenAI(
  api_key=os.getenv('DEEPSEEK_API_KEY'),
  base_url="https://api.deepseek.com/v1",
)

def send_message(messages):
  response = client.chat.completions.create(
    model='deepseek-reasoner',
    messages=messages,
    tools=tools,
    tool_choice='auto'
  )
  return response

# 打造一个函数调用的工具
tools = [
  {
    "type": "function",
    "function": {
      "name": "get_closing_price",
      "description": "获取指定股票的收盘价",
      "parameters": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "股票名称",
          },
        },
        "required": ["name"],
      }
    }
  }
]

# 定义函数
def get_closing_price(name):
  if name == '青岛啤酒':
    return '67.92'
  elif name == '贵州茅台':
    return '1488.21'
  else:
    return '未找到该股票'


if __name__ == '__main__': 
  messages = [{"role": "user", "content": "青岛啤酒的收盘价是多少?"}]
  response = send_message(messages)

  message = response.choices[0].message
  messages.append({
    "role": message.role,
    "content": message.content,
    "tool_calls": message.tool_calls
  })


  # print("回复:")
  # print(response.choices[0].message.content)

  # print("工具选择:")
  # print(response.choices[0].message.tool_calls)

  # LLM 已经确定了它要用的函数
  if response.choices[0].message.tool_calls != None:
    tool_call = response.choices[0].message.tool_calls[0]

    if tool_call.function.name == "get_closing_price":
      arguments_dict = json.loads(tool_call.function.arguments)  # {"name": "青岛啤酒"}
      price = get_closing_price(arguments_dict['name'])

      messages.append({
        "role": "tool",
        "content": price,
        "tool_call_id": tool_call.id
      })

    # print("messages:", messages)

    response = send_message(messages)

    print("回复:")
    print(response.choices[0].message.content)

image.png