FunctionGemma工作流实战
停止编写正则表达式来解析您的LLM输出。本文介绍如何使用Ollama和FunctionGemma在本地运行确定性、并行工具执行的方法。
我们都有过与本地LLM相关的特定挫败点。聊天很有趣。总结很有用。但最终,你希望模型实际上做点什么。你希望它获取数据、运行计算或查询数据库——而不会出现语法幻觉。
长期以来,可靠的“函数调用”一直是大型封闭源代码模型(如GPT-4)的专属领域。如果你试图强迫一个标准的7B模型输出严格的JSON用于函数调用,你经常会得到损坏的括号或混合了对话填充的代码。
进入 FunctionGemma。
谷歌发布了这些专门的Gemma变体,明确训练用于工具使用。当与 Ollama 配对时,你可以构建一个本地代理,能够以惊人的可靠性处理复杂、多部分的查询,在一次操作中完成。
在这篇指南中,我将向你展示如何构建一个脚本,处理复杂的请求——检查天气、股票价格和计算健康指标——全部在一个过程中完成,完全在你的机器上运行。
1、为什么FunctionGemma是游戏规则改变者
如果你尝试过使用标准模型(如Llama 3或Mistral)构建代理,你就会知道其中的挣扎。你必须强烈地进行提示工程: “请仅输出JSON。不要说话。” 而且一半的时间,它们仍然失败。
FunctionGemma解决了这个问题,但好处不仅限于可靠性:
- 计算效率(不需要GPU农场): 你不需要用大锤子砸坚果。FunctionGemma有轻量级的尺寸(如2B和9B)。因为这些模型是专门为工具使用而不是“知道宇宙中的一切”而设计的,它们在消费级硬件上运行得非常快——即使是在标准CPU上。你可以在MacBook Air或Raspberry Pi上运行高频代理循环,而不会出现70B参数模型的延迟。
- 成本效益(零API费用): 在OpenAI或Anthropic上构建代理会很快变得昂贵。代理工作流程通常需要“思考”和“反思”,这会消耗大量token。在开发和测试阶段,你可能需要运行脚本500次来修复一个bug,云成本会迅速增加。FunctionGemma在本地免费运行。你可以每小时向模型发送数千个请求,而你的唯一成本是电力。
- 并行执行: 如果你问两个不同的问题,标准模型往往会冻结或依次回答。FunctionGemma可以同时触发多个工具调用,大大减少用户等待答案的总时间。
- 严格类型: 它比大多数通用模型更好地尊重你Python函数中的类型提示(字符串、浮点数、布尔值),这意味着当你的代码尝试解析参数时,出现的运行时错误更少。
2、环境搭建
你需要安装Python并在你的机器上运行Ollama。
- 拉取模型: 打开终端并获取模型(它轻量且高效):
ollama pull functiongemma
2. 安装依赖项: 我们使用标准的Ollama库和rich用于可读的终端输出。
pip install ollama rich
3、实现代码
这是完整的实现。我将其结构化为模块化,以便以后可以替换你自己的API。
3.1 定义工具
这里最关键的部分是文档字符串。LLM通过文档字符串来理解何时使用该工具以及参数的含义。
import json
from rich import print
from ollama import chat
# 专为工具使用优化的特定模型变体
model = 'functiongemma'
# --- 工具 ---
def get_weather(city: str) -> str:
"""
获取城市的当前天气。
参数:
city: 城市名称
"""
# 演示用的模拟数据
return json.dumps({
'city': city,
'temperature': 22,
'unit': 'celsius',
'condition': 'sunny'
})
def calculate_bmi(weight: float, height: float) -> str:
"""
计算身体质量指数(BMI)。
参数:
weight: 千克(kg)
height: 米(m)
"""
bmi = weight / (height * height)
return json.dumps({
'bmi': round(bmi, 2),
'status': 'Normal' if 18.5 <= bmi <= 24.9 else 'Check Chart'
})
def get_stock_price(ticker: str) -> str:
"""
获取给定股票代码的当前股价。
参数:
ticker: 股票符号(例如,AAPL,MSFT)
"""
# 模拟数据
prices = {'AAPL': 150.00, 'GOOGL': 2800.00, 'MSFT': 400.00}
price = prices.get(ticker.upper(), 0.00)
return json.dumps({'ticker': ticker, 'price': price, 'currency': 'USD'})
3.2 执行循环
这是代理的“大脑”。我们定义可用的工具,将它们传递给Ollama,并处理响应。
请注意我使用的输入提示:它干净、直接,并包含三个不同的意图,以压力测试模型的并行能力。
# 将字符串名称映射到实际函数以执行
available_tools = {
'get_weather': get_weather,
'calculate_bmi': calculate_bmi,
'get_stock_price': get_stock_price
}
# 一个复杂、多意图的提示
user_prompt = (
"班加罗尔的当前天气是什么? "
"另外,检查微软的股票价格,并 "
"计算体重70公斤、身高1.75米的人的BMI。"
)
messages = [{'role': 'user', 'content': user_prompt}]
print(f"[bold yellow]提示:[/bold yellow] {messages[0]['content']}")
# 1. 将查询发送给Ollama并定义工具
response = chat(
model,
messages=messages,
tools=[get_weather, calculate_bmi, get_stock_price]
)
if response.message.tool_calls:
# 2. 遍历工具调用(FunctionGemma支持并行调用)
for tool in response.message.tool_calls:
print(f'\n🔹 模型请求了工具: [bold cyan]{tool.function.name}[/bold cyan]')
print(f' 参数: {tool.function.arguments}')
# 3. 查找并执行
function_to_call = available_tools.get(tool.function.name)
if function_to_call:
# 解包参数 (**kwargs) 并运行
result = function_to_call(**tool.function.arguments)
print(f' 结果: [green]{result}[/green]')
# 4. 添加历史记录
# 我们将模型的请求和工具的输出添加到历史记录中
messages.append(response.message)
messages.append({'role': 'tool', 'content': result})
else:
print(f"[red]错误: 函数 {tool.function.name} 未找到[/red]")
# 5. 最终综合
# 将所有工具输出返回给模型以生成自然语言摘要
final = chat(model, messages=messages)
print('\n[bold]最终响应:[/bold]', final.message.content)
else:
# 标准聊天的备用方案
print('响应:', response.message.content)
4、输出
当你运行这个脚本时,你不会得到一个“这里是如何做到的”的通用回答。你会得到实际的执行。
模型解析自然语言,意识到它需要三个不同的工具,并并行发出调用:
- 提取位置: “班加罗尔”用于
get_weather。 - 识别实体: “微软”(映射到上下文)用于
get_stock_price。 - 解析指标: 70和1.75用于
calculate_bmi。
最终响应无缝地将这些不同的数据点结合在一起:
“班加罗尔目前天气晴朗,温度为22°C。微软(MSFT)的交易价格为400.00美元。对于体重70公斤、身高1.75米的人来说,BMI为22.86,属于正常范围。”
5、结束语
本地代理不再是科学实验。有了像 functiongemma 和 Ollama 这样的模型和运行器,你可以构建可靠、私有且快速的自动化工具。
你获得了结构化API的可靠性,同时又具备LLM的灵活性,而且全部在你自己的硬件上免费运行。下一次你构建CLI工具或个人助手时,跳过正则表达式,让模型处理逻辑吧。
原文链接:Local AI Agents: Multi-Step Workflows with Ollama and FunctionGemma
汇智网翻译整理,转载请标明出处