1. 项目概述
最近在测试FunctionGemma这个专门用于工具调用的轻量级大语言模型(0.27B参数),想看看它在中文场景下的实际表现。FunctionGemma是Gemma系列中针对函数调用优化的版本,理论上应该能更好地理解用户意图并准确调用预设工具函数。本文记录了从环境搭建到完整测试的全过程,包含详细的性能评估和问题分析。
2. 环境准备与部署
2.1 基础环境配置
测试环境使用了一台配备NVIDIA T4显卡(16GB显存)的Linux服务器,基础配置如下:
- Ubuntu 20.04 LTS
- Docker 24.0.7
- NVIDIA Driver 535.146.02
- CUDA 12.2
提示:确保已正确安装NVIDIA容器工具包,这是GPU加速的关键:
bash复制distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
&& curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
2.2 Ollama服务部署
Ollama是一个简化大模型本地部署的工具,支持多种开源模型。部署步骤如下:
- 拉取最新Ollama镜像:
bash复制docker pull ollama/ollama:latest
- 启动容器(注意挂载volume持久化模型数据):
bash复制docker run -d \
--gpus=all \
-v ollama:/root/.ollama \
-p 11435:11434 \
--name ollama \
ollama/ollama
- 下载FunctionGemma模型:
bash复制docker exec -it ollama ollama pull functiongemma
- 测试基础运行:
bash复制docker exec -it ollama ollama run functiongemma
注意事项:
- 端口映射11435:11434是为了避免与主机其他服务冲突
- 如果遇到GPU内存不足,可以添加
--shm-size=2g参数- 首次拉取模型可能需要较长时间(约5-10分钟)
3. 功能测试方案设计
3.1 测试目标与方法
设计了一套完整的评估方案,主要测试两个维度:
- 准确性:模型是否能正确识别意图并调用对应函数
- 性能:响应延迟和稳定性
测试用例覆盖三类常见场景:
- 导航指令(如"带我去会议室")
- 动作指令(如"握个手")
- 舞蹈指令(如"跳个街舞")
3.2 测试工具函数定义
按照OpenAI工具调用格式定义了三个测试函数:
python复制TOOLS = [
{
"type": "function",
"function": {
"name": "nav_execute",
"description": "导航执行服务,输入目标位置,导航至该位置",
"parameters": {
"type": "object",
"properties": {
"pos": {"type": "string", "description": "目标点位名称"}
},
"required": ["pos"]
}
}
},
{
"type": "function",
"function": {
"name": "action_execute",
"description": "动作执行服务,输入动作名称,执行该动作",
"parameters": {
"type": "object",
"properties": {
"action": {"type": "string", "description": "动作名称"}
},
"required": ["action"]
}
}
},
{
"type": "function",
"function": {
"name": "dance_execute",
"description": "舞蹈执行服务,输入舞蹈名称,执行该舞蹈",
"parameters": {
"type": "object",
"properties": {
"dance": {"type": "string", "description": "舞蹈名称"}
},
"required": ["dance"]
}
}
}
]
3.3 测试客户端实现
封装了专用的测试客户端类,主要功能包括:
- 带超时和错误处理的API调用
- 自动记录响应延迟
- 结果统计与分析
核心代码片段:
python复制class FunctionGemmaClient:
def __init__(self, base_url: str = "http://192.168.10.60:11435"):
self.base_url = base_url
self.chat_url = f"{base_url}/api/chat"
def call_with_tools(self, messages: List[Dict], tools: List[Dict]) -> Dict:
data = {
"model": "functiongemma",
"messages": messages,
"tools": tools,
"stream": False
}
start_time = time.time()
try:
response = requests.post(self.chat_url, json=data, timeout=30)
latency_ms = (time.time() - start_time) * 1000
if response.status_code == 200:
result = response.json()
result['latency_ms'] = latency_ms
return result
else:
return {"error": f"HTTP {response.status_code}", "latency_ms": latency_ms}
except Exception as e:
return {"error": str(e), "latency_ms": (time.time() - start_time) * 1000}
4. 测试执行与结果分析
4.1 测试执行过程
每个测试用例重复执行10次,共90次测试。关键测试逻辑:
python复制def test_single_case(self, test_case: TestCase) -> Dict:
results = []
latencies = []
for i in range(self.repetitions):
response = self.client.call_with_tools(
[{"role": "user", "content": test_case.query}],
TOOLS
)
eval_result = evaluate_accuracy(test_case, response)
results.append(eval_result)
latencies.append(response['latency_ms'])
time.sleep(0.5) # 避免请求过载
return {
'test_case': test_case.__dict__,
'results': results,
'statistics': {
'latency_mean': statistics.mean(latencies),
'latency_median': statistics.median(latencies),
'latency_stdev': statistics.stdev(latencies) if len(latencies) > 1 else 0
}
}
4.2 准确性测试结果
测试结果显示严重的准确性问题:
| 类别 | 测试用例 | 准确率 | 主要问题 |
|---|---|---|---|
| 导航 | 带我去会议室 | 0% | 全部误识别为action_execute |
| 导航 | 怎么去前台 | 0% | 参数提取错误 |
| 动作 | 握个手 | 100% | - |
| 动作 | 点个头 | 0% | 70%未调用工具 |
| 舞蹈 | 跳个舞 | 0% | 参数错误(dance="跳") |
| 舞蹈 | 跳个街舞 | 100% | - |
实操心得:模型对具体指令(如"街舞")表现良好,但对泛化表达(如"舞蹈")理解较差,这与训练数据分布可能有关。
4.3 性能测试结果
延迟统计(单位:ms):
| 指标 | 数值 |
|---|---|
| 平均延迟 | 543.88 |
| 中位数 | 506.64 |
| 最小值 | 458.78 |
| 最大值 | 3384.88 |
| 标准差 | 305.28 |
典型测试用例延迟对比:
python复制# 延迟最高的用例
"带我去会议室": {
"avg_latency": 803.37,
"max_latency": 3384.88
}
# 最稳定的用例
"执行芭蕾舞": {
"avg_latency": 494.64,
"stdev": 14.88
}
5. 问题诊断与优化建议
5.1 主要问题分析
-
导航功能失效:
- 根本原因:模型对中文导航关键词(如"去"、"带我")理解不足
- 证据:所有导航查询都被误分类为动作
-
参数提取问题:
- "跳个舞" →
{dance: "跳"} - "点个头" →
{action: "点个头"} - 模式:倾向于提取动词而非名词
- "跳个舞" →
-
工具调用决策不稳定:
- 相同查询有时调用工具,有时直接回答
- 特别是对边界案例(如"点个头")
5.2 优化方案
短期改进:
- 提示工程优化:
python复制# 修改工具描述示例
{
"name": "nav_execute",
"description": "当用户要求前往某地时调用此函数。关键词:去、到、带我、怎么走",
"parameters": {
"properties": {
"pos": {
"type": "string",
"enum": ["会议室", "前台", "办公室"],
"description": "必须是以下之一:会议室、前台、办公室"
}
}
}
}
- 后处理逻辑:
python复制def fix_common_errors(params):
if params.get("dance") == "跳":
params["dance"] = "舞蹈"
if params.get("action") == "点个头":
params["action"] = "点头"
return params
长期建议:
-
模型微调:
- 收集100-200个中文工具调用示例
- 使用LoRA进行轻量级微调
-
架构优化:
- 添加前置分类器判断是否需要工具调用
- 对模糊查询进行澄清确认
6. 结论与选型建议
经过全面测试,FunctionGemma 0.27B在当前版本存在明显局限:
优势:
- 极小的模型体积(适合边缘设备)
- 对具体指令响应准确
- 无需API密钥,完全本地运行
不足:
- 中文理解能力较弱
- 导航功能基本不可用
- 延迟波动较大
选型建议:
| 场景 | 推荐方案 |
|---|---|
| 英文环境/边缘设备 | 可尝试FunctionGemma |
| 中文生产环境 | 建议使用Qwen-4B |
| 高精度要求 | 考虑GPT-4工具调用 |
最终建议:如果必须使用FunctionGemma,建议配合完善的后处理逻辑和明确的用户引导,避免开放式查询。对于中文生产环境,目前仍有更成熟的选择。
