一、先搞懂:OpenAI Functions 核心特点是什么?

很多同学会把Tool Calling和普通对话混淆,其实OpenAI Functions风格的工具调用,有三个不可替代的核心优势,也是我们本次实现的重点:

1. 需求识别自动化

模型能自动识别用户输入中需要调用工具的场景,无需人工指定“该用哪个工具”。比如用户问“现在几点”,模型会自动匹配“时间查询工具”;问“123乘以456是多少”,则自动触发“计算器工具”,完全无需额外的判断逻辑。

2. 工具调用标准化

遵循OpenAI Functions的规范,将自定义工具(如计算器、时间查询)封装成统一格式,模型能快速识别工具的功能、参数要求,避免因工具格式不统一导致的调用失败,降低开发成本。

3. 多轮交互连贯化

结合对话记忆功能,模型能记住上下文交互记录。比如用户先问“现在几点”,再问“30分钟后是几点”,模型会结合上一轮获取的当前时间,调用计算器完成计算,实现连贯的对话体验,而非孤立的单次工具调用。

二、核心实现思路:4步搭建,无复杂依赖

本次实现不依赖第三方API(除本地模型外),核心基于LangChain框架,将“模型初始化、工具定义、提示词构建、Agent封装”四大步骤串联,全程极简,重点突出OpenAI Functions的实现逻辑。

第一步:模型适配——对接兼容OpenAI规范的本地模型

OpenAI Functions的核心是“模型能识别工具描述、生成符合规范的调用指令”,因此我们无需直接使用OpenAI官方模型,只要本地部署的模型兼容OpenAI的工具调用格式(如通义千问、DeepSeek等),即可通过LangChain的ChatOpenAI封装类完成对接。

这一步的关键是配置模型的接口地址、密钥(部分本地模型无需真实密钥)、温度系数和最大生成token数,确保模型输出稳定、符合工具调用规范。

第二步:工具定义——按OpenAI Functions规范封装本地工具

按照OpenAI Functions的要求,我们将自定义工具(计算器、时间查询)封装成“名称+功能+参数描述”的标准格式,让模型能清晰识别工具的用途和调用方式。

比如计算器工具,需要明确说明“用于执行简单数学运算,输入必须是合法的算术表达式”;时间查询工具则说明“无需参数,直接返回当前系统时间”。同时,为了保证安全性,我们还会对工具的输入进行校验(如计算器的表达式白名单),避免恶意代码注入。

第三步:提示词构建——融入对话记忆与工具调用逻辑

提示词是模型触发工具调用的关键,我们需要构建包含“系统提示、对话历史、用户输入、工具调用中间步骤”的完整模板。其中,系统提示明确告知模型“需用中文回答、可自动调用工具”;对话历史占位符用于保存多轮交互记录,实现记忆功能;工具调用中间步骤占位符则用于存储模型调用工具的过程和结果,确保交互连贯。

第四步:Agent封装——整合模型、工具与记忆,实现自动流转

这是实现OpenAI Functions风格工具调用的核心步骤:通过LangChain的create_tool_calling_agent方法,将模型、封装好的工具、提示词模板整合,创建一个能自动触发工具调用的Agent;再搭配对话记忆组件和Agent执行器,实现“用户输入→模型识别→工具调用→结果返回”的全流程自动化,同时限制最大工具调用次数,避免无限循环。

三、实际体验:轻量化交互,适配多种场景

搭建完成后,启动程序即可进行交互式对话,体验完全贴合OpenAI Functions的核心逻辑:

- 基础对话:用户输入“你好”,模型直接返回问候,无需调用工具;

- 工具调用:用户输入“123乘以456是多少”,模型自动调用计算器工具,返回计算结果;输入“现在几点”,自动调用时间查询工具,返回格式化时间;

- 多轮连贯:用户先问“现在几点”,再问“1小时后是几点”,模型结合上一轮的时间结果,调用计算器完成计算,实现连贯交互。

整个过程无需人工干预,模型能自主判断是否需要调用工具、调用哪个工具,完全复刻OpenAI Functions的使用体验,且纯本地运行,无需担心网络延迟和API调用成本。

四、总结与延伸

本次实现的核心价值,在于“用极简方式复刻OpenAI Functions的工具调用逻辑”——无需复杂的代码开发,借助LangChain的封装能力,快速对接本地模型,实现自动化工具调用和多轮对话记忆。

其实,OpenAI Functions的本质不是“调用工具”,而是“让模型学会判断需求、选择工具、处理结果”,这也是大模型从“语言生成”向“实际应用”落地的关键一步。

后续我们还可以基于这个框架,扩展更多工具(如天气查询、文件读取、API调用等),只需按照相同的规范封装工具,即可实现更多场景的自动化处理,真正让大模型成为高效的辅助工具。

代码实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

# -*- coding: utf-8 -*-

"""

基于 LangChain 的 Tool Calling 智能助手

- 对接兼容 OpenAI 工具调用格式的本地大模型(qwen3.5-27b-awq)

- 内置工具:数学计算器、获取当前时间(纯本地实现,无外部依赖)

- 支持多轮对话记忆,可连贯交互

- 核心架构:OpenAI 风格函数调用(Tool Calling)Agent

"""

import os

from datetime import datetime

from dotenv import load_dotenv

# 导入 LangChain 核心组件:工具调用 Agent、执行器

from langchain.agents import create_tool_calling_agent, AgentExecutor

# 导入工具封装类

from langchain_core.tools import Tool

# 导入兼容 OpenAI 接口的聊天模型

from langchain_openai import ChatOpenAI

# 导入提示词模板、对话占位符

from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder

# 导入对话记忆组件,用于保存历史对话

from langchain.memory import ConversationBufferMemory

# 加载 .env 文件中的环境变量(当前代码未使用,保留以支持扩展)

load_dotenv()

# ========================

# 1. 初始化大语言模型

# 说明:使用 ChatOpenAI 封装对接本地模型,因本地模型兼容 OpenAI 工具调用协议

# ========================

# 本地模型 API Key(按需替换,部分本地部署无需真实密钥)

DEEPSEEK_API_KEY = "123"

llm = ChatOpenAI(

    api_key=DEEPSEEK_API_KEY,

    base_url="http://192.168.0.100:8085/v1",  # 本地模型接口地址

    model="qwen3.5-27b-awq",                  # 模型名称

    temperature=0.3,                          # 温度系数,值越低回答越确定

    max_tokens=1024,                          # 最大生成 token 数

)

# ========================

# 2. 定义本地工具函数

# 工具会被 Agent 自动调用,用于处理模型无法直接完成的任务

# ========================

def calculate(expression: str-str:

    """

    安全计算简单数学表达式

    :param expression: 数学表达式字符串,例如 '2 + 3 * 4'

    :return: 计算结果或错误信息

    """

    try:

        # 白名单校验:仅允许数字、运算符、括号和空格,防止恶意代码执行

        allowed = set("0123456789+-*/(). ")

        if not all(c in allowed for in expression):

            return "错误:表达式包含非法字符"

        # 安全执行表达式,禁用内置函数防止代码注入

        result = eval(expression, {"__builtins__": {}}, {})

        return str(result)

    except Exception as e:

        return f"计算失败: {str(e)}"

def get_current_time(dummy: str = "") -str:

    """

    获取当前系统日期和时间

    :param dummy: 占位参数,适配工具调用格式

    :return: 格式化的当前时间字符串

    """

    return datetime.now().strftime("%Y年%m%d %H:%M:%S")

# 将自定义函数注册为 LangChain 标准工具

tools = [

    Tool(

        name="calculate",

        func=calculate,

        description="执行简单数学运算。输入必须是合法的算术表达式字符串,如 '15 * 6'。",

    ),

    Tool(

        name="get_current_time",

        func=get_current_time,

        description="获取当前系统时间。无需传入实际参数。",

    ),

]

# ========================

# 3. 构建提示词模板

# 包含系统提示、对话历史、用户输入、工具调用中间步骤

# ========================

prompt = ChatPromptTemplate.from_messages([

    ("system""你是一个智能助手,能调用工具完成用户请求。请始终用中文回答。"),

    MessagesPlaceholder("chat_history"),   # 对话历史占位符,实现多轮记忆

    ("human""{input}"),                 # 用户当前输入占位符

    MessagesPlaceholder("agent_scratchpad"),  # 工具调用过程占位符,Agent 内部使用

])

# ========================

# 4. 创建 Tool Calling Agent 及执行器

# ========================

# 创建支持工具调用的 Agent(OpenAI 函数调用格式)

agent = create_tool_calling_agent(llm, tools, prompt)

# 初始化对话记忆:存储聊天历史,保持多轮交互连贯性

memory = ConversationBufferMemory(

    memory_key="chat_history",    # 与提示词中占位符名称一致

    return_messages=True,          # 返回消息对象而非字符串,保证格式兼容

)

# 创建 Agent 执行器:负责运行 Agent、调用工具、管理记忆

agent_executor = AgentExecutor(

    agent=agent,

    tools=tools,

    memory=memory,

    verbose=True,                  # 打印详细执行过程,方便调试

    handle_parsing_errors=True,    # 自动处理解析错误,提升稳定性

    max_iterations=5,              # 最大工具调用次数,防止无限循环

)

# ========================

# 5. 启动交互式对话

# 循环接收用户输入,调用 Agent 并输出回答

# ========================

if __name__ == "__main__":

    print("🤖 本地模型 Tool Calling 助手启动!")

    print("💡 示例:你好、现在几点?、123 乘以 456 等于多少?")

    print("🛑 输入 quit / exit 可退出程序。\n")

    while True:

        user_input = input("👤 你: ").strip()

        # 退出条件判断

        if not user_input or user_input.lower() in ["quit""exit"]:

            print("👋 再见!")

            break

        try:

            # 调用 Agent 执行器获取回答

            response = agent_executor.invoke({"input": user_input})

            # 打印原始响应(调试用)

            print("原始模型响应:", response)

            # 输出最终回答

            print(f"🤖 助手: {response['output']}\n")

        except Exception as e:

            print(f"❌ 错误: {e}\n")

对话内容:

🤖 本地模型 Tool Calling 助手启动!
💡 示例:你好、现在几点?、123 乘以 456 等于多少?
🛑 输入 quit / exit 可退出程序。

👤 你: 你好


> Entering new AgentExecutor chain...


你好!我是你的智能助手,很高兴为你服务。

我可以帮助你:
- 进行数学计算
- 获取当前时间
- 回答各种问题
- 完成其他任务

请问有什么我可以帮你的吗?

> Finished chain.
原始模型响应: {'input': '你好', 'chat_history': [HumanMessage(content='你好'), AIMessage(content='\n\n你好!我是你的智能助手,很高兴为你服务。\n\n我可以帮助你:\n- 进行数学计算\n- 获取当前时间\n- 回答各种问题\n- 完成其他任务\n\n请问有什么我可以帮你的吗?')], 'output': '\n\n你好!我是你的智能助手,很高兴为你服务。\n\n我可以帮助你:\n- 进行数学计算\n- 获取当前时间\n- 回答各种问题\n- 完成其他任务\n\n请问有什么我可以帮你的吗?'}
🤖 助手: 

你好!我是你的智能助手,很高兴为你服务。

我可以帮助你:
- 进行数学计算
- 获取当前时间
- 回答各种问题
- 完成其他任务

请问有什么我可以帮你的吗?

👤 你: 现在几点?


> Entering new AgentExecutor chain...

Invoking: `get_current_time` with ``
responded: 



2026年0413 10:31:00

现在是 2026 年 4 月 13 日 10:31:00。

> Finished chain.
原始模型响应: {'input': '现在几点?', 'chat_history': [HumanMessage(content='你好'), AIMessage(content='\n\n你好!我是你的智能助手,很高兴为你服务。\n\n我可以帮助你:\n- 进行数学计算\n- 获取当前时间\n- 回答各种问题\n- 完成其他任务\n\n请问有什么我可以帮你的吗?'), HumanMessage(content='现在几点?'), AIMessage(content='\n\n现在是 2026 年 4 月 13 日 10:31:00。')], 'output': '\n\n现在是 2026 年 4 月 13 日 10:31:00。'}
🤖 助手: 

现在是 2026 年 4 月 13 日 10:31:00。

👤 你: 123 乘以 456 等于多少?


> Entering new AgentExecutor chain...

Invoking: `calculate` with `123 * 456`
responded: 



56088

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐