Function Call 函数调用高阶方法
- [JSON Schema - 函数的"说明书"](#JSON Schema - 函数的"说明书")
- [一、意图识别:当你有多个相似的函数时,AI 如何准确选择?](#一、意图识别:当你有多个相似的函数时,AI 如何准确选择?)
- 工具函数
- [首先,我们需要一个自动生成 JSON Schema 的工具函数](#首先,我们需要一个自动生成 JSON Schema 的工具函数)
- [auto_functions 的局限性](#auto_functions 的局限性)
- 问题本质
- 现实场景中的影响
- [技巧一:优化 JSON Schema](#技巧一:优化 JSON Schema)
- 技巧2:使用分类标记
- 技巧3:增加权重提示
- 技巧4:使用否定句
- [技巧 5:组合](#技巧 5:组合)
- 解决方案2:干预模型选择
- [方法1:自查 Prompts](#方法1:自查 Prompts)
- 方法2:关键词匹配(最可靠!)
- 二、海量函数调用
- 第一步:分类(按业务模块分层)
- [第二步:粗略对齐 + 精准对齐](#第二步:粗略对齐 + 精准对齐)
- 分类方法2:按关键词分类
- [方法3 高级方案:向量数据库 + RAG](#方法3 高级方案:向量数据库 + RAG)
- 示例
- [RAG + 向量数据库的工作流程](#RAG + 向量数据库的工作流程)
- 二、函数并发调用
- 四、响应速度优化:缓存、流式输出
- 总结
为什么需要"进阶"技巧?
基础的 Function Call 很简单,但在实际应用中会遇到很多挑战:
- 意图识别难:如何让 AI 准确选择正确的函数?
- 函数太多:当有 100+ 个函数时,如何管理?
- 性能问题:如何提高响应速度?
- 并发处理:如何同时处理多个任务?
c
挑战1 - 意图识别问题
├─ 问题分析
├─ 优化 JSON Schema 的 4 大技巧
└─ 干预模型选择的方法
↓
挑战2 - 海量函数调用
├─ 函数分层策略
├─ 粗略对齐 + 精准对齐
└─ 向量数据库 + RAG 方案
↓
挑战3 - 函数并发调用
├─ 串行调用实战
└─ 并行调用实战(3种方法)
↓
挑战4 - 响应优化
├─ 缓存方案
└─ 流式响应
↓
综合实战与最佳实践
├─ 完整项目:智能数学计算器
├─ 最佳实践总结
└─ 常见陷阱与调试技巧
✅ 理解 Function Call 的完整工作原理
✅ 编写高质量的函数描述(JSON Schema)
✅ 解决 AI 函数选择错误的问题
✅ 管理 50+ 甚至 100+ 个函数
✅ 实现高性能的并发调用
✅ 独立开发完整的 Function Call 应用
JSON Schema - 函数的"说明书"
JSON Schema 是一种标准格式,用于描述函数的功能和参数。
类比一下:
- Python 函数:AI 看不懂
- JSON Schema:用 AI 能理解的语言描述函数
为什么需要?
AI 模型无法直接读取和理解 Python 代码。我们需要用一种"AI 友好"的格式告诉它:
- 这个函数叫什么名字?
- 这个函数是做什么的?
- 这个函数需要什么参数?
- 哪些参数是必需的?
如何编写?
标准的 JSON Schema 格式:
json
{
"type": "function", # 固定写法:表示这是一个函数
"function": {
"name": "函数名", # 函数的名称
"description": "函数功能描述", # 详细描述函数的作用
"parameters": {
"type": "object", # 固定写法:参数是一个对象
"properties": { # 参数的具体定义
"参数1": {
"type": "string", # 参数类型(string/number/boolean等)
"description": "参数1的说明"
},
"参数2": {
"type": "number",
"description": "参数2的说明"
}
},
"required": ["参数1"] # 必需参数列表
}
}
}
实战示例 1:简单的加法函数
Python 函数:
python
def add(a, b):
"""计算两个数的和"""
return a + b
对应的 JSON Schema:
json
{
"type": "function",
"function": {
"name": "add",
"description": "计算两个数的和",
"parameters": {
"type": "object",
"properties": {
"a": {
"type": "number",
"description": "第一个加数"
},
"b": {
"type": "number",
"description": "第二个加数"
}
},
"required": ["a", "b"]
}
}
}
💡 重点总结:
- JSON Schema 是 AI 理解函数的"翻译器"
description字段非常重要,要写清楚!required数组指定哪些参数是必需的
Tools - AI 的"技能库"
Tools 是一个包含所有可调用函数描述的列表(数组)。
简单理解:
- 单个 JSON Schema = 一个工具的说明书
- Tools = 整个工具箱(包含多个工具的说明书)
告诉 AI:"你有哪些工具可以用"。
假设我们有两个函数:add(加法)和 multiply(乘法)
python
# 这是一个 Tools 列表,包含两个函数的描述
tools = [
{
# 第一个工具:加法函数
"type": "function",
"function": {
"name": "add",
"description": "计算两个数的和",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "number", "description": "第一个加数"},
"b": {"type": "number", "description": "第二个加数"}
},
"required": ["a", "b"]
}
}
},
{
# 第二个工具:乘法函数
"type": "function",
"function": {
"name": "multiply",
"description": "计算两个数的乘积",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "number", "description": "第一个乘数"},
"b": {"type": "number", "description": "第二个乘数"}
},
"required": ["a", "b"]
}
}
}
]
当我们调用 AI 时,需要把这个 tools 列表传递给它:
python
response = client.chat.completions.create(
model="glm-4",
messages=messages,
tools=tools, # ← 在这里传递工具列表
tool_choice="auto"
)
AI 会根据用户的问题,从 tools 列表中选择合适的函数。
Tool Choice - "使用工具的策略"
Tool Choice 参数用于控制 AI 如何选择和使用工具。
四种模式
| 模式 | 含义 | 使用场景 |
|---|---|---|
"auto" |
AI 自动判断是否需要调用函数 | 最常用,让 AI 自主决定 |
"required" |
强制 AI 必须调用某个函数 | 确定需要执行函数时 |
"none" |
禁止 AI 调用任何函数 | 只想要对话,不想执行操作 |
"function_name" |
指定调用某个特定函数 | 明确知道要用哪个函数 |
示例对比
示例 1:auto 模式
python
response = client.chat.completions.create(
model="glm-4",
messages=[{"role": "user", "content": "1 + 1 等于几?"}],
tools=tools,
tool_choice="auto" # AI 自动判断是否需要调用函数
)
# AI 可能会调用 add(1, 1),也可能直接回答 "2"
示例 2:required 模式
python
response = client.chat.completions.create(
model="glm-4",
messages=[{"role": "user", "content": "1 + 1 等于几?"}],
tools=tools,
tool_choice="required" # 强制 AI 必须调用函数
)
# AI 一定会调用 add(1, 1),不会直接回答
示例 3:指定函数名
python
response = client.chat.completions.create(
model="glm-4",
messages=[{"role": "user", "content": "计算一下"}],
tools=tools,
tool_choice={"type": "function", "function": {"name": "add"}}
)
# 强制 AI 调用 add 函数(即使用户问题不明确)
💡 推荐做法:
- 大多数情况下使用
"auto",让 AI 智能选择 - 只在非常明确的场景下使用
"required"或指定函数名
一、意图识别:当你有多个相似的函数时,AI 如何准确选择?
c
解决方案
├── 1. 优化 JSON Schema(治本)
│ ├── 技巧1:使用关键词(最重要)
│ ├── 技巧2:使用分类标记
│ ├── 技巧3:增加权重提示
│ └── 技巧4:使用否定句
└── 2. 干预模型选择(治标)
├── 方法1:自查 Prompts
└── 方法2:关键词匹配(最可靠)
-
设计函数时:
- 使用清晰、独特的函数名
- 添加分类前缀(如
user_,admin_) - 写详细的文档字符串
-
编写 JSON Schema 时:
- 包含丰富的使用场景说明
- 明确排除场景(告诉 AI 何时不用)
- 添加权重提示词
-
核心业务功能:
- 使用关键词匹配确保准确性
- 不要完全依赖 AI 的判断
- 代码控制 > AI 理解
工具函数
auto_functions:自动生成 JSON Schema- 基于
inspect模块实现 - 适合简单场景,复杂需求需手动调整
首先,我们需要一个自动生成 JSON Schema 的工具函数
在实际开发中,手写 JSON Schema 太麻烦了。我们可以使用一个工具函数自动生成:
python
import inspect # Python 内置库,用于检查活动对象(函数、类等)
import json
from typing import List, Callable
def auto_functions(func_list: List[Callable]) -> List[dict]:
"""
自动生成函数的 JSON Schema 描述
参数:
func_list: Python 函数列表
返回:
JSON Schema 格式的工具描述列表
"""
# 步骤1:创建空列表,用于存储所有函数的描述
tools_description = []
# 步骤2:遍历每个函数
for func in func_list:
# ===== 步骤2.1:获取函数的签名信息 =====
# inspect.signature() 可以获取函数的详细信息
# 包括:参数名、参数类型、默认值等
sig = inspect.signature(func)
func_params = sig.parameters # 获取所有参数
# ===== 步骤2.2:初始化参数描述结构 =====
parameters = {
'type': 'object', # JSON Schema 固定格式
'properties': {}, # 存储每个参数的详细信息
'required': [] # 存储必需参数的名称
}
# ===== 步骤2.3:遍历函数的每个参数 =====
for param_name, param in func_params.items():
# 添加参数描述
parameters['properties'][param_name] = {
# 参数的说明文字
# 如果参数有类型注解且注解有文档,使用文档;否则为空
'description': (
param.annotation.__doc__
if param.annotation is not inspect._empty
else ""
),
# 参数的类型
# 如果有类型注解,转换为字符串;否则标记为 'Any'
'type': (
str(param.annotation)
if param.annotation != param.empty
else 'Any'
)
}
# 判断参数是否必需
# 如果参数没有默认值,说明它是必需的
if param.default == param.empty:
parameters['required'].append(param_name)
# ===== 步骤2.4:构建单个函数的完整描述 =====
func_dict = {
"type": "function", # 固定值:表示这是一个函数
"function": {
"name": func.__name__, # 函数名
"description": func.__doc__.strip(), # 函数的文档字符串
"parameters": parameters # 参数描述
}
}
# 步骤3:添加到结果列表
tools_description.append(func_dict)
# 步骤4:返回所有函数的描述
return tools_description
为什么需要这个函数?
- 自动化:避免手动编写重复的 JSON Schema
- 减少错误:自动提取函数信息,避免手写错误
- 易于维护:修改 Python 函数后,自动更新 Schema
auto_functions 的局限性
局限1:依赖文档字符串
- 如果函数没有写文档字符串,
description会是空的 - 解决方案:养成写文档字符串的习惯
局限2:类型注解的处理
- 复杂类型(如
List[str])会被转成字符串形式 - 可能不符合 JSON Schema 的标准
- 解决方案:为复杂类型手动编写 Schema
局限3:无法处理特殊需求
- 例如:参数之间的依赖关系、条件约束等
- 解决方案:在生成后手动修改 Schema
问题本质
AI 进行函数选择时,主要依据:
- 函数名的相似度 :
machine_learning_1和machine_learning_2非常相似 - 描述的语义相似度:"机器学习"和"深度学习"在语义上高度相关
- 关键词匹配:用户问题中的关键词与函数描述的匹配程度
当两个函数的名称和描述都很相似时,AI 很容易"选错"。
现实场景中的影响
在实际应用中,这种错误可能导致:
- 银行系统:用户要"开信用卡",AI 却调用了"开储蓄卡"函数
- 客服系统:用户要"退款",AI 却调用了"退货"函数
- 订单系统:用户要"取消订单",AI 却调用了"修改订单"函数
后果可能很严重!
技巧一:优化 JSON Schema
第一种解决方案是:提升 JSON Schema 的质量,让 AI 更容易区分不同的函数。
记住这个核心原则:高质量的 JSON Schema = 清晰的函数命名 + 详细的描述 + 独特的标识
让每个函数名包含独特的、不易混淆的关键词。
技巧1:使用关键词(最重要!)
💡 最佳实践:
- 为重要业务功能设计独特的触发词
- 在函数名和描述中都包含这些触发词
- 让触发词简短、易记、不易混淆
❌ 不好的命名:
python
def machine_learning_1():
"""解释机器学习是什么"""
pass
def machine_learning_2():
"""描述机器学习和深度学习的区别"""
pass
问题:
- 函数名几乎一样,只有数字不同
- AI 无法从函数名判断功能差异
✅ 好的命名:
python
def explain_machine_learning():
"""解释机器学习是什么"""
pass
def compare_ml_and_dl():
"""对比机器学习和深度学习的区别"""
pass
优势:
- 函数名直接表达功能:
explain(解释)vscompare(对比) - 包含核心关键词:
machine_learningvsml_and_dl - 一眼就能看出区别
技巧2:使用分类标记
使用前缀或后缀对函数进行分类,让 AI 一眼就能看出函数所属的类别。
分类方式:
- 用户功能:
user_xxx - 管理员功能:
admin_xxx - 数据功能:
data_xxx
效果:
- 当用户说"我要注册"时,AI 会优先考虑
user_开头的函数 - 当管理员说"查看日志"时,AI 会优先考虑
admin_开头的函数 - 分类清晰,减少混淆
技巧3:增加权重提示
在函数描述中添加重要性标记,帮助 AI 判断优先级。
示例
python
def create_order():
"""
【核心功能】【经常使用】【优先考虑】
创建新订单
这是系统最常用的功能之一,用户下单时必须使用。
请在用户提到"购买"、"下单"、"买东西"等关键词时调用此函数。
"""
pass
def cancel_order():
"""
【次要功能】
取消已创建的订单
用于用户主动取消订单的场景。
只有当用户明确提到"取消订单"时才调用。
"""
pass
权重提示词:
【核心功能】:最重要的功能【经常使用】:使用频率高【优先考虑】:有歧义时优先选择【次要功能】:不常用的功能
效果:
- AI 会更倾向于选择标记为"核心"、"优先"的函数
- 减少误选不常用功能的概率
技巧4:使用否定句
明确告诉 AI 什么时候不该用这个函数。
示例
python
def explain_machine_learning():
"""
解释机器学习的基本概念
用途:当用户询问"什么是机器学习"、"机器学习的定义"时使用
⚠️ 注意:当用户询问深度学习时,请勿使用此函数!
深度学习相关问题请使用 compare_ml_and_dl 函数。
"""
pass
def compare_ml_and_dl():
"""
对比机器学习和深度学习的区别
用途:当用户询问"深度学习是什么"、"机器学习和深度学习的区别"时使用
⚠️ 注意:仅当问题涉及"深度学习"时使用此函数!
"""
pass
关键点:
- 明确排除场景:告诉 AI 哪些情况不要用
- 提供替代建议:告诉 AI 应该用哪个函数
技巧 5:组合
让我们把所有技巧结合起来,重新设计之前的两个函数:
python
def ML_Basic_Explain():
"""
【机器学习-基础概念】解释机器学习是什么
功能:介绍机器学习的基本定义和核心原理
使用场景:
- 用户询问"什么是机器学习"
- 用户询问"机器学习的定义"
- 用户询问"ML是什么"(ML是机器学习的缩写)
⚠️ 重要提示:
- 不要在涉及深度学习的问题中使用此函数
- 深度学习相关问题请使用 DL_Compare_With_ML 函数
"""
answer = """机器学习是人工智能的一个分支,研究计算机如何自动从数据中学习,
提升性能并做出预测。它通过算法让计算机提炼知识,优化任务执行,而无需明确编程。"""
return answer
def DL_Compare_With_ML():
"""
【深度学习-对比分析】对比机器学习和深度学习
功能:详细说明深度学习的特点,以及它与机器学习的区别
使用场景:
- 用户询问"什么是深度学习"
- 用户询问"深度学习和机器学习的区别"
- 用户询问"DL是什么"(DL是深度学习的缩写)
⚠️ 重要提示:
- 只要问题涉及深度学习(Deep Learning),就使用此函数
- 即使只问"深度学习是什么",也应该用这个函数
"""
answer = """机器学习涉及算法从数据中学习模式以做出预测或决策。
深度学习是机器学习的一个子集,专注于使用深层神经网络,
来处理复杂的数据结构,实现高级功能如图像识别和自然语言理解。"""
return answer
改进总结:
- ✅ 使用分类前缀:
ML_vsDL_ - ✅ 函数名清晰:
ExplainvsCompare - ✅ 详细的使用场景说明
- ✅ 明确的排除提示
解决方案2:干预模型选择
有时候,即使优化了 JSON Schema,AI 仍然可能选错。这时我们需要在代码层面干预 AI 的选择过程。
方法1:自查 Prompts
原理
在真正调用函数之前,先问 AI 一个问题:"这个问题你能不能回答?"
- 如果 AI 说"能",就直接让它回答,不调用函数
- 如果 AI 说"不能",再调用函数
这样可以避免 AI 错误地调用函数。
python
def function_call_with_check(prompt, tools):
"""
在调用函数前,先让 AI 自查是否需要外部工具
参数:
prompt: 用户问题
tools: 可用的工具列表
"""
# 第一步:询问 AI 是否能从知识库回答
judge_prompt = prompt + "\n\n这个问题的答案是否在你的语料库里?\
请回答'这个问题答案在我的语料库里'或者'这个问题的答案不在我的语料库里',\
不要回答其他额外的文字"
# 构建消息
message = [
{"role": "user", "content": judge_prompt}
]
# 调用 AI 进行自查
response_check = client.chat.completions.create(
model="glm-4",
messages=message
)
# 获取 AI 的回答
check_result = response_check.choices[0].message.content
# 第二步:根据自查结果决定是否调用函数
if "这个问题答案在我的语料库里" in check_result:
# AI 认为可以直接回答,不调用函数
message = [{"role": "user", "content": prompt}]
response = client.chat.completions.create(
model="glm-4",
messages=message
)
print(response.choices[0].message.content)
else:
# AI 认为需要外部工具,调用函数
message = [{"role": "user", "content": prompt}]
response = client.chat.completions.create(
model="glm-4",
messages=message,
tools=tools,
tool_choice="auto"
)
print(response.choices[0].message.tool_calls[0].function.name)
优势:
- 减少不必要的函数调用
- 避免 AI 在可以直接回答时仍然调用函数
- 提高响应速度(直接回答比调用函数快)
注意事项 :
⚠️ AI 可能会"说谎"!有时候 AI 会认为自己知道答案,但实际上答案是错的。所以这个方法要谨慎使用。
方法2:关键词匹配(最可靠!)
依赖编程逻辑,而不是依赖 AI 的理解。
在用户提问时,先用代码进行关键词匹配,直接指定调用哪个函数,而不让 AI 选择。
实现代码
python
def function_call_with_keywords(prompt, tools, keyword_mapping):
"""
使用关键词匹配来选择函数
参数:
prompt: 用户问题
tools: 可用的工具列表
keyword_mapping: 关键词到函数名的映射
"""
# 遍历关键词映射
selected_function = None
for keywords, function_name in keyword_mapping.items():
# 检查用户问题中是否包含关键词
if any(keyword in prompt for keyword in keywords):
selected_function = function_name
break
# 构建消息
message = [{"role": "user", "content": prompt}]
if selected_function:
# 找到匹配的函数,强制调用
response = client.chat.completions.create(
model="glm-4",
messages=message,
tools=tools,
tool_choice={"type": "function", "function": {"name": selected_function}}
)
else:
# 没有匹配的函数,让 AI 自动选择
response = client.chat.completions.create(
model="glm-4",
messages=message,
tools=tools,
tool_choice="auto"
)
return response
使用示例:餐厅点餐系统
python
# 定义关键词映射
keyword_mapping = {
("点餐", "下单", "要吃", "我要"): "create_order",
("取消", "不要了", "退单"): "cancel_order",
("查询", "看看", "订单状态"): "query_order",
}
# 测试
prompt1 = "我要点餐"
response1 = function_call_with_keywords(prompt1, tools, keyword_mapping)
# 直接调用 create_order 函数
prompt2 = "帮我取消订单"
response2 = function_call_with_keywords(prompt2, tools, keyword_mapping)
# 直接调用 cancel_order 函数
优势:
- ✅ 最可靠:100% 准确,不依赖 AI 的理解
- ✅ 可控性强:完全由代码控制
- ✅ 响应快:不需要 AI 思考选择
劣势:
- ❌ 需要维护关键词库
- ❌ 无法处理复杂的自然语言
- ❌ 关键词可能冲突
💡 最佳实践:
- 为核心业务功能设置关键词匹配
- 为容易混淆的功能设置关键词匹配
- 将关键词匹配作为第一道防线 ,让 AI 选择作为兜底方案
二、海量函数调用
我们解决了意图识别的问题。但那只是在函数数量较少(2-10个)的情况下。
当你的应用规模扩大,函数数量达到 50+、100+ 甚至更多时,新的问题就出现了。
| 函数数量 | AI 选择准确率 | 平均响应时间 |
|---|---|---|
| 5个以内 | ~95% | 1-2秒 |
| 10-20个 | ~85% | 2-3秒 |
| 30-50个 | ~70% | 3-5秒 |
| 50-100个 | ~50% | 5-8秒 |
| 100个以上 | ~30% | 8秒以上 |
问题分析:
-
Token 限制
- AI 一次能处理的文本量有限
- 100个函数的 JSON Schema 可能超过 token 上限
- 即使没超过,也会占用大量 token,影响性能
-
语义相似度混淆
- 函数越多,相似的函数越多
- AI 更容易混淆
- 例如:
query_user_by_idvsquery_user_by_namevsquery_user_by_email
-
计算性能下降
- AI 需要在海量函数中进行语义匹配
- 计算复杂度呈指数级增长
- 响应速度明显变慢
c
解决方案
├── 函数数量 < 50
│ └── 直接使用 Function Call(第1章方法)
├── 函数数量 50-200
│ └── 分层对齐策略
│ ├── 按业务模块分组
│ ├── 按关键词分组
│ └── 粗略对齐 + 精准对齐
└── 函数数量 > 200
└── RAG + 向量数据库
├── 离线:函数描述向量化
├── 在线:检索相关函数
└── 小范围内精准匹配
核心技巧
- 分而治之:把大问题分解成小问题
- 两步对齐:先确定类别,再精准选择
- 关键词优先:能用关键词就不用 AI
- 优雅降级:无法处理时给出合理回复
案例1:电商平台
c
用户模块:注册、登录、修改资料、找回密码... (10+ 个函数)
商品模块:搜索、详情、评价、收藏... (15+ 个函数)
订单模块:创建、支付、取消、退款、查询... (20+ 个函数)
购物车模块:添加、删除、修改数量... (10+ 个函数)
物流模块:查询物流、地址管理... (10+ 个函数)
客服模块:提交工单、查看回复... (10+ 个函数)
数据分析:销售报表、用户行为分析... (15+ 个函数)
--------------------------------
总计:90+ 个函数
当函数数量超过一定规模时:
- ❌ 不能把所有函数一股脑塞给 AI
- ❌ 不能期望 AI 在海量函数中准确选择
- ✅ 必须主动管理和组织这些函数
- ✅ 必须分步骤、分层次让 AI 选择
核心思想:化整为零,分而治之
就像图书馆用分类系统管理图书一样:
c
图书馆
├── 文学区
│ ├── 中国文学
│ ├── 外国文学
│ └── 诗歌散文
├── 科技区
│ ├── 计算机
│ ├── 物理
│ └── 生物
└── 历史区
├── 中国历史
└── 世界历史
我们也要把函数进行分层管理。
c
用户提问
↓
第一步:粗略对齐(确定大类)
↓
选择一个小的函数组(10-20个)
↓
第二步:精准对齐(在小范围内选择)
↓
执行函数
↓
返回结果
第一步:分类(按业务模块分层)
按业务模块分层
按业务模块分层,55个函数 → 分成4组,每组10-15个
python
# ========== 基础数学运算 ==========
def Basic_add(a, b):
"""【基础数学】计算两个数的和"""
return a + b
def Basic_subtract(a, b):
"""【基础数学】计算两个数的差"""
return a - b
def Basic_multiply(a, b):
"""【基础数学】计算两个数的积"""
return a * b
def Basic_divide(a, b):
"""【基础数学】计算两个数的商"""
if b == 0:
raise ValueError("除数不能为零")
return a / b
def Basic_power(a, b):
"""【基础数学】计算 a 的 b 次方"""
return a ** b
# ... 更多基础数学函数(共约15个)
# ========== 三角函数运算 ==========
def Tri_func_sin(a):
"""【三角函数】计算正弦值"""
import math
return math.sin(a)
def Tri_func_cos(a):
"""【三角函数】计算余弦值"""
import math
return math.cos(a)
def Tri_func_tan(a):
"""【三角函数】计算正切值"""
import math
return math.tan(a)
# ... 更多三角函数(共约10个)
# ========== 概率统计 ==========
def Prob_comb(n, k):
"""【概率论】计算组合数 C(n, k)"""
import math
return math.comb(n, k)
def Prob_perm(n, k):
"""【概率论】计算排列数 P(n, k)"""
import math
return math.perm(n, k)
# ... 更多概率统计函数(共约15个)
# ========== 高等数学 ==========
def Advanced_Math_derivative(func, a, h=1e-5):
"""【高等数学】计算函数在某点的导数"""
return (func(a + h) - func(a - h)) / (2 * h)
def Advanced_Math_integral(func, a, b, n=1000):
"""【高等数学】计算定积分"""
width = (b - a) / n
total = 0.5 * (func(a) + func(b))
for i in range(1, n):
total += func(a + i * width)
return total * width
# ... 更多高等数学函数(共约15个)
分组结果:
- 基础数学:15个函数
- 三角函数:10个函数
- 概率统计:15个函数
- 高等数学:15个函数
python
# 第一步:按模块分组函数
# 这里我们使用原教程中的数学函数作为示例
# 基础数学函数列表(索引0-16,每个名字都是有一个数学函数的实现)
basic_math_functions = [
Basic_add, Basic_subtract, Basic_divide, Basic_multiply,
Basic_power, Basic_sqrt, Basic_mod, Basic_abs_val,
Basic_factorial, Basic_gcd, Basic_lcm, Basic_exp,
Basic_log, Basic_log10, Basic_round_val,
Basic_level_ceil, Basic_level_floor
]
# 三角函数列表(索引17-27)
trig_functions = [
Tri_func_sin_val, Tri_func_cos_val, Tri_func_tan_val,
Tri_func_asin_val, Tri_func_acos_val, Tri_func_atan_val,
Tri_func_sinh_val, Tri_func_cosh_val, Tri_func_tanh_val,
Tri_func_degrees, Tri_func_radians
]
# 概率统计函数列表(索引28-44)
prob_functions = [
Prob_comb, Prob_perm, Prob_factorial_prob,
Prob_binom_pmf, Prob_binom_cdf,
Prob_poisson_pmf, Prob_poisson_cdf,
Prob_uniform_pdf, Prob_uniform_cdf,
Prob_normal_pdf, Prob_normal_cdf,
Prob_expon_pdf, Prob_expon_cdf,
Prob_geom_pmf, Prob_geom_cdf,
Prob_beta_pdf, Prob_beta_cdf
]
# 高等数学函数列表(索引45+)
advanced_math_functions = [
Advanced_Math_Derivative, Advanced_Math_integral,
Advanced_Math_double_integral, Advanced_Math_partial_derivative,
Advanced_Math_gradient, Advanced_Math_laplacian,
Advanced_Math_divergence, Advanced_Math_curl,
Advanced_Math_jacobian, Advanced_Math_hessian,
Advanced_Math_taylor_series, Advanced_Math_fourier_series,
Advanced_Math_laplace_transform, Advanced_Math_fourier_transform
]
# 第二步:为每个分组生成 JSON Schema
# 使用我们之前的 auto_functions 工具函数
# 生成各组的 tools
basic_math_tools = auto_functions(basic_math_functions) # 基础数学工具
trig_tools = auto_functions(trig_functions) # 三角函数工具
prob_tools = auto_functions(prob_functions) # 概率统计工具
advanced_math_tools = auto_functions(advanced_math_functions) # 高等数学工具
# 第三步:创建函数类型映射字典
# 这个字典用于快速获取对应类型的工具列表
function_type_mapping = {
"基础数学": basic_math_tools,
"三角函数": trig_tools,
"概率论": prob_tools,
"高等数学": advanced_math_tools,
"其他": auto_functions(basic_math_functions + trig_functions +
prob_functions + advanced_math_functions) # 兜底方案
}
# 第四步:创建函数名到函数体的映射
# 将所有函数合并到一个字典中,方便执行
all_functions = (basic_math_functions + trig_functions +
prob_functions + advanced_math_functions)
available_math_functions = {func.__name__: func for func in all_functions}
为什么这么设计?
- 分组清晰:每个分组有明确的功能边界
- 易于扩展:添加新函数只需加入对应分组
- 性能优化:每次只给 AI 10-15个函数,而不是55个
第二步:粗略对齐 + 精准对齐
现在我们有了分组,接下来实现两步对齐策略。
c
用户问题:"计算 1314520 除以 520 等于几?"
↓
粗略对齐:判断问题属于哪个类别
→ AI 分析:"这是基础的除法运算"
→ 选择:基础数学工具组(15个函数)
↓
精准对齐:在小范围内选择具体函数
→ AI 从15个基础数学函数中选择
→ 选中:Basic_divide 函数
↓
执行函数并返回结果
python
def math_calculate(prompt, function_type_mapping, available_functions):
"""
智能数学计算函数:使用分层对齐策略
参数:
prompt: 用户的问题
function_type_mapping: 函数类型到工具列表的映射
available_functions: 函数名到函数体的映射
返回:
计算结果
"""
# ===== 第一步:粗略对齐(确定函数类别) =====
# 构建判断问题类别的提示词
judge_prompt = prompt + "\n\n请问上述问题最接近哪一类数学问题?\
请回应:基础数学、或三角函数、或概率论、或高等数学、或其他。\
不要回应别的任何文字。"
# 构建消息
message = [
{"role": "user", "content": judge_prompt}
]
# 调用 AI 进行意图识别(粗略对齐)
response_first_align = client.chat.completions.create(
model="glm-4", # 使用的模型
messages=message # 传入消息
)
# 获取 AI 的判断结果
func_type = response_first_align.choices[0].message.content
print(f"初步意图识别结果: {func_type}\n")
# ===== 第二步:根据类别选择对应的函数组 =====
# 默认值:如果无法识别,使用"其他"(包含所有函数)
current_function_tools = None
# 使用关键词匹配确定使用哪个函数组
if "基础数学" in func_type:
current_function_tools = function_type_mapping["基础数学"]
elif "三角函数" in func_type:
current_function_tools = function_type_mapping["三角函数"]
elif "概率论" in func_type:
current_function_tools = function_type_mapping["概率论"]
elif "高等数学" in func_type:
current_function_tools = function_type_mapping["高等数学"]
else:
# 无法识别类别,使用所有函数(兜底)
current_function_tools = function_type_mapping["其他"]
# ===== 第三步:精准对齐(在小范围内选择具体函数) =====
# 重新构建消息(带有提示信息)
message = [
{
"role": "assistant",
"content": "当你被问到数学问题时,尝试调用 Tools 来解决问题。"
}
]
message.append({"role": "user", "content": prompt})
# 在选定的函数组中进行精准匹配
response_second_align = client.chat.completions.create(
model="glm-4", # 使用的模型
messages=message, # 传入消息
tools=current_function_tools, # 传入筛选后的工具列表(关键!)
tool_choice="auto" # 让 AI 自动选择
)
# ===== 第四步:执行函数 =====
try:
# 获取函数调用信息
function_calls = response_second_align.choices[0].message.tool_calls[0]
print(f"被调用的函数是: {function_calls.function.name}\n")
# 根据函数名获取函数体
function_to_call = available_functions[function_calls.function.name]
# 解析函数参数(从 JSON 字符串转为字典)
function_args = json.loads(function_calls.function.arguments)
# 执行函数
function_response = function_to_call(**function_args)
# ===== 第五步:格式化返回结果 =====
# 重新构建消息,包含函数执行结果
message = []
message.append({
"role": "assistant",
"content": f"你使用了 tools 工具,最终获得的答案是 {function_response}"
})
message.append({
"role": "user",
"content": prompt + " 请仅仅回答题目的答案(如果是数字,则只显示数字),不要包括其他描述"
})
# 第三次调用 AI,生成友好的回复
response_final = client.chat.completions.create(
model="glm-4",
messages=message
)
# 返回最终结果
print(response_final.choices[0].message.content)
return response_final.choices[0].message.content
except Exception as e:
# 如果函数调用失败
print(f"\n函数调用失败!错误信息: {str(e)}")
print("请检查函数定义或重新设计解决方案")
return None
代码详解要点:
-
第一步:粗略对齐
- 用 prompt 让 AI 判断问题类别
- 关键:不提供 tools,只让 AI 做分类
- 结果:得到 "基础数学"、"三角函数" 等类别
-
第二步:选择函数组
- 根据类别从
function_type_mapping获取对应工具组 - 关键:缩小范围,从 55个函数 → 10-15个函数
- 根据类别从
-
第三步:精准对齐
- 在小范围内让 AI 选择具体函数
- 关键:只传入筛选后的 tools,不是全部
- 准确率大幅提升!
-
异常处理
- 使用 try-except 捕获错误
- 给出明确的错误提示
分类方法2:按关键词分类
除了按业务模块分组,还可以使用关键词分组。
关键词匹配逻辑:
python
def select_function_group_by_keyword(prompt):
"""
根据关键词选择函数组
参数:
prompt: 用户问题
返回:
对应的函数组
"""
# 定义关键词到函数组的映射
keyword_to_group = {
("注册", "登录", "账户", "密码"): user_functions,
("订单", "下单", "支付", "退款"): order_functions,
("商品", "搜索", "购买", "评价"): product_functions,
("购物车", "加入", "清空"): cart_functions
}
# 遍历关键词映射
for keywords, function_group in keyword_to_group.items():
# 检查提示词中是否包含任何关键词
if any(keyword in prompt for keyword in keywords):
return function_group
# 如果没有匹配,返回所有函数
return user_functions + order_functions + product_functions + cart_functions
优势:
- 直接、快速
- 不依赖 AI 判断
- 适合关键词明确的业务场景
劣势:
- 需要维护关键词库
- 无法处理复杂的自然语言
方法3 高级方案:向量数据库 + RAG
当函数数量超过 1000+ 时,即使分层对齐也可能力不从心。这时需要更高级的方案。
RAG(Retrieval-Augmented Generation) = 检索增强生成
通俗理解:
- 检索(Retrieval):先在数据库中搜索相关信息
- 增强(Augmented):用搜索到的信息增强输入
- 生成(Generation):AI 基于增强后的输入生成回复
向量数据库:存储文本的数字化表示(向量)的数据库。
想象你要整理一堆照片:
- 传统方法:按日期、地点分类(手动标签)
- 向量方法:计算机"看懂"照片内容,自动找相似的
文本也一样:
- 每段文本可以转换成一组数字(向量)
- 意思相近的文本,向量也相近
- 通过计算向量距离,找到最相关的内容
示例
c
文本1:"我想买一台手机" → 向量:[0.2, 0.8, 0.3, ...]
文本2:"购买智能手机" → 向量:[0.25, 0.75, 0.35, ...]
文本3:"今天天气真好" → 向量:[-0.5, 0.1, 0.9, ...]
计算相似度:
- 文本1 和 文本2:相似度很高(都在说买手机)
- 文本1 和 文本3:相似度很低(话题不同)
RAG + 向量数据库的工作流程
c
准备阶段(离线):
1. 把所有函数的描述转换成向量
2. 存储到向量数据库中
运行阶段(在线):
1. 用户提问:"我要计算导数"
2. 把问题也转换成向量
3. 在向量数据库中搜索最相似的函数描述
4. 返回 Top-K 个最相关的函数(例如 K=5)
5. 只把这 K 个函数给 AI 选择
6. AI 在小范围内准确选择
RAG 方案的优势
-
可扩展性强
- 支持 10000+ 函数
- 性能不会随函数数量显著下降
-
准确率高
- 基于语义相似度匹配
- 比关键词匹配更智能
-
自动化
- 不需要手动分类
- 添加新函数自动适配### 特殊问题处理
问题1:当没有合适的函数时
有时候,用户的问题可能超出了我们提供的函数范围。
python
prompt = "请帮我写一首诗"
math_calculate(prompt, function_type_mapping, available_math_functions)
# 可能会出现:
# - AI 随意选择一个函数(错误)
# - 程序报错(不友好)
解决方案:增加"无法处理"选项
python
def math_calculate_with_fallback(prompt, function_type_mapping, available_functions):
"""
带降级处理的数学计算函数
"""
# 第一步:先判断是否是数学问题
check_prompt = prompt + "\n\n这是一个数学计算问题吗?请回答'是'或'否',不要回答其他内容。"
message = [{"role": "user", "content": check_prompt}]
response_check = client.chat.completions.create(
model="glm-4",
messages=message
)
# 如果不是数学问题,直接让 AI 回答,不调用函数
if "否" in response_check.choices[0].message.content:
print("检测到这不是数学问题,将直接回答:\n")
message = [{"role": "user", "content": prompt}]
response = client.chat.completions.create(
model="glm-4",
messages=message
)
print(response.choices[0].message.content)
return response.choices[0].message.content
# 如果是数学问题,继续正常的 Function Call 流程
else:
return math_calculate(prompt, function_type_mapping, available_functions)
优势:
- 优雅降级:无法处理时给出合理回复
- 避免错误:不会强行调用不相关的函数
- 用户体验好:无论什么问题都能得到回应
二、函数并发调用
当需要同时执行多个任务时,如何提高效率?串行 vs 并行的选择和实现!
注意事项 :
⚠️ 并发数不是越多越好!
- 太多并发可能导致 API 限流
- 建议控制在 5-10 个并发
- 可以使用
asyncio.Semaphore限制并发数
常见陷阱
-
❌ 过度并发:导致 API 限流
- 解决:使用
asyncio.Semaphore限制并发数
- 解决:使用
-
❌ 忽略错误:一个任务失败导致整体失败
- 解决:用 try-except 包裹每个任务
-
❌ 混淆场景:有依赖的任务强行并行
- 解决:先分析依赖关系,再决定并行策略
四、响应速度优化:缓存、流式输出
| 优化方法 | 适用场景 | 提升幅度 |
|---|---|---|
| 缓存 | 高频重复 | 100-3000x |
| 流式 | 长文本 | 体验提升5x |
| JSONL代替JSON | 所有场景 | 60% |
| 分层对齐 | 50+函数 | 70% |
| 并发调用 | 多任务 | 4-10x |
想象你在餐厅点餐后等待上菜:
场景1:漫长的等待(响应慢)
c
你点完餐 → 等待30分钟 → 终于上菜
期间:
- 不知道厨房在做什么
- 不知道还要等多久
- 焦虑、不耐烦
- 可能会投诉或离开
场景2:优化的体验(响应快)
c
你点完餐 → 5分钟后上菜
或者:
你点完餐 → 服务员告诉你"大约需要15分钟"
→ 10分钟后服务员说"菜马上好"
→ 15分钟后上菜
期间:
- 知道进度
- 心里有数
- 体验良好
AI 应用中的类似问题:
- 用户提问后,等待20秒才看到回复 → 用户可能关闭页面
- 复杂的 Function Call 需要多次 AI 调用 → 累积延迟很长
- 相同的问题重复问,每次都要重新计算 → 浪费资源
总结
c
Function Call 技能树
│
├── 基础技能 ⭐⭐⭐
│ ├── 编写 JSON Schema
│ ├── 理解调用流程
│ └── 执行函数并返回结果
│
├── 进阶技能 ⭐⭐⭐⭐
│ ├── 优化函数命名和描述
│ ├── 处理意图识别问题
│ └── 实现分层对齐机制
│
├── 高级技能 ⭐⭐⭐⭐⭐
│ ├── 管理 100+ 函数
│ ├── 实现并发调用
│ ├── 综合性能优化
│ └── 设计完整应用架构
│
└── 专家技能 ⭐⭐⭐⭐⭐⭐
├── RAG + 向量数据库
├── 自定义评估体系
└── 企业级系统设计