LM Studio的 Tool Use详解
·
1 究竟啥是“tool use”?
“工具使用”描述的是:
- LLM输出文本,请求调用函数(LLM无法直接执行代码)
- 你的代码执行这些函数
- 你的代码将结果反馈给LLM
2 重要提示
为“工具使用”选择的模型会极大影响性能。选择模型时的一些通用指导原则:
- 并非所有模型都能智能自主使用工具
- 模型参数越多越好(如70亿参数的模型通常比30亿参数模型表现更好)
- 很多情况下,https://model.lmstudio.ai/download/lmstudio-community/Qwen2.5-7B-Instruct-GGUF 模型表现良好
- 这些指导原则可能演变
3 Python示例
Python聊天机器人示例,通过类似OpenAI调用chat.completions API的方式来使用“工具”,使LM Studio模型能够查询维基百科。
3.1 前提条件
- PC安装Python
- 使用
pip install openai安装OpenAI Python包(仅用作LM Studio的包装器)
3.2 步骤
- 加载一个模型
- 启动服务器
- 使用“另存为Python文件”按钮保存下面的示例代码
- 将保存后显示的命令复制并粘贴到你的终端中,然后按回车键tool-use-example.py
"""
LM Studio工具使用演示:维基百科查询聊天机器人
展示LM Studio模型如何查询维基百科
"""
# 标准库导入
import itertools
import json
import shutil
import sys
import threading
import time
import urllib.parse
import urllib.request
# 第三方库导入
from openai import OpenAI
# 初始化LM Studio客户端
client = OpenAI(base_url="http://127.0.0.1:1234/v1", api_key="lm-studio")
MODEL = "llama-4-maverick-17b-128e-instruct"
def fetch_wikipedia_content(search_query: str) -> dict:
"""为给定的搜索查询获取维基百科内容"""
try:
# 搜索最相关的文章
search_url = "https://en.wikipedia.org/w/api.php"
search_params = {
"action": "query",
"format": "json",
"list": "search",
"srsearch": search_query,
"srlimit": 1,
}
url = f"{search_url}?{urllib.parse.urlencode(search_params)}"
with urllib.request.urlopen(url) as response:
search_data = json.loads(response.read().decode())
if not search_data["query"]["search"]:
return {
"status": "error",
"message": f"未找到关于“{search_query}”的维基百科文章",
}
# 从搜索结果中获取规范化的标题
normalized_title = search_data["query"]["search"][0]["title"]
# 现在使用规范化的标题获取实际内容
content_params = {
"action": "query",
"format": "json",
"titles": normalized_title,
"prop": "extracts",
"exintro": "true",
"explaintext": "true",
"redirects": 1,
}
url = f"{search_url}?{urllib.parse.urlencode(content_params)}"
with urllib.request.urlopen(url) as response:
data = json.loads(response.read().decode())
pages = data["query"]["pages"]
page_id = list(pages.keys())[0]
if page_id == "-1":
return {
"status": "error",
"message": f"未找到关于“{search_query}”的维基百科文章",
}
content = pages[page_id]["extract"].strip()
return {
"status": "success",
"content": content,
"title": pages[page_id]["title"],
}
except Exception as e:
return {"status": "error", "message": str(e)}
# 为LM Studio定义工具
WIKI_TOOL = {
"type": "function",
"function": {
"name": "fetch_wikipedia_content",
"description": (
"搜索维基百科并获取最相关文章的简介。"
"如果用户询问的内容很可能在维基百科上,始终使用此工具。"
"如果用户的搜索查询中有拼写错误,在搜索前进行纠正。"
),
"parameters": {
"type": "object",
"properties": {
"search_query": {
"type": "string",
"description": "用于查找维基百科文章的搜索查询",
},
},
"required": ["search_query"],
},
},
}
# 用于显示模型处理状态的类
class Spinner:
def __init__(self, message="正在处理..."):
self.spinner = itertools.cycle(["-", "/", "|", "\\"])
self.busy = False
self.delay = 0.1
self.message = message
self.thread = None
def write(self, text):
sys.stdout.write(text)
sys.stdout.flush()
def _spin(self):
while self.busy:
self.write(f"\r{self.message} {next(self.spinner)}")
time.sleep(self.delay)
self.write("\r\033[K") # 清除该行
def __enter__(self):
self.busy = True
self.thread = threading.Thread(target=self._spin)
self.thread.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.busy = False
time.sleep(self.delay)
if self.thread:
self.thread.join()
self.write("\r") # 将光标移到行首
def chat_loop():
"""
主聊天循环,处理用户输入并处理工具调用。
"""
messages = [
{
"role": "system",
"content": (
"你是一个可以检索维基百科文章的助手。"
"当被问及某个主题时,你可以检索维基百科文章并引用其中的信息。"
),
}
]
print(
"助手:"
"你好!我可以访问维基百科来帮助回答你关于历史、科学、人物、地点或概念的问题——或者我们也可以聊其他任何话题!"
)
print("(输入'quit'退出)")
while True:
user_input = input("\n你:").strip()
if user_input.lower() == "quit":
break
messages.append({"role": "user", "content": user_input})
try:
with Spinner("正在思考..."):
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=[WIKI_TOOL],
)
if response.choices[0].message.tool_calls:
# 处理所有工具调用
tool_calls = response.choices[0].message.tool_calls
# 将所有工具调用添加到消息中
messages.append(
{
"role": "assistant",
"tool_calls": [
{
"id": tool_call.id,
"type": tool_call.type,
"function": tool_call.function,
}
for tool_call in tool_calls
],
}
)
# 处理每个工具调用并添加结果
for tool_call in tool_calls:
args = json.loads(tool_call.function.arguments)
result = fetch_wikipedia_content(args["search_query"])
# 以格式化的方式打印维基百科内容
terminal_width = shutil.get_terminal_size().columns
print("\n" + "=" * terminal_width)
if result["status"] == "success":
print(f"\n维基百科文章:{result['title']}")
print("-" * terminal_width)
print(result["content"])
else:
print(
f"\n获取维基百科内容时出错:{result['message']}"
)
print("=" * terminal_width + "\n")
messages.append(
{
"role": "tool",
"content": json.dumps(result),
"tool_call_id": tool_call.id,
}
)
# 流式传输工具调用后的响应
print("\n助手:", end=" ", flush=True)
stream_response = client.chat.completions.create(
model=MODEL, messages=messages, stream=True
)
collected_content = ""
for chunk in stream_response:
if chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
print(content, end="", flush=True)
collected_content += content
print() # 流式传输完成后换行
messages.append(
{
"role": "assistant",
"content": collected_content,
}
)
else:
# 处理常规响应
print("\n助手:", response.choices[0].message.content)
messages.append(
{
"role": "assistant",
"content": response.choices[0].message.content,
}
)
except Exception as e:
print(
f"\n与LM Studio服务器聊天时出错!\n\n"
f"请确保:\n"
f"1. LM Studio服务器在127.0.0.1:1234(主机名:端口)运行\n"
f"2. 已下载模型“{MODEL}”\n"
f"3. 已加载模型“{MODEL}”,或者已启用即时模型加载功能\n\n"
f"错误详情:{str(e)}\n"
"有关更多信息,请参阅https://lmstudio.ai/docs/basics/server"
)
exit(1)
if __name__ == "__main__":
chat_loop()
启动服务器,运行上述脚本,与一个能够查询维基百科的模型进行聊天:
-> % python tool-use-example.py
助手:你好!我可以访问维基百科来帮助回答你关于历史、科学、人物、地点或概念的问题——或者我们也可以聊其他任何话题!
(输入'quit'退出)
你:
4 使用curl的示例
一个模型使用curl工具请求调用工具的情况。
curl http://127.0.0.1:1234/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llama-4-maverick-17b-128e-instruct",
"messages": [{"role": "user", "content": "What dell products do you have under $50 in electronics?"}],
"tools": [
{
"type": "function",
"function": {
"name": "search_products",
"description": "根据各种标准搜索产品目录。每当客户询问产品的可用性、价格或规格时,使用此工具。",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索词或产品名称"
},
"category": {
"type": "string",
"description": "用于筛选的产品类别",
"enum": ["electronics", "clothing", "home", "outdoor"]
},
"max_price": {
"type": "number",
"description": "以美元为单位的最高价格"
}
},
"required": ["query"],
"additionalProperties": false
}
}
}
]
}'
/v1/chat/completions识别的所有参数都将生效,并且可用工具的数组应在tools字段中提供。
如果模型认为用户的消息最好通过调用工具来完成,那么在响应字段choices[0].message.tool_calls中将会提供一个工具调用请求对象的数组。
顶级响应对象的finish_reason字段也将填充为"tool_calls"。
上述curl请求的一个示例响应如下:
{
"id": "chatcmpl-zbpvqy4k1sax34dv62a2",
"object": "chat.completion",
"created": 1745678823,
"model": "llama-4-maverick-17b-128e-instruct",
"choices": [
{
"index": 0,
"logprobs": null,
"finish_reason": "tool_calls",
"message": {
"role": "assistant",
"tool_calls": [
{
"id": "169722375",
"type": "function",
"function": {
"name": "search_products",
"arguments": "{\"category\":\"electronics\",\"max_price\":50,\"query\":\"Dell\"}"
}
}
]
}
}
],
"usage": {
"prompt_tokens": 387,
"completion_tokens": 30,
"total_tokens": 417
},
"stats": {},
"system_fingerprint": "llama-4-maverick-17b-128e-instruct"
}%
上述响应可以理解为模型在说:
“请调用
search_products函数,参数如下:
query参数为’dell’category参数为’electronics’max_price参数为’50’然后把结果返回给我”
需要解析tool_calls字段来调用实际的函数或API,如Python示例代码中所示。
高层次流程
┌──────────────────────────┐
│ SETUP: LLM + Tool list │
└──────────┬───────────────┘
▼
┌──────────────────────────┐
│ Get user input │◄────┐
└──────────┬───────────────┘ │
▼ │
┌──────────────────────────┐ │
│ LLM prompted w/messages │ │
└──────────┬───────────────┘ │
▼ │
Needs tools? │
│ │ │
Yes No │
│ │ │
▼ └────────────┐ │
┌─────────────┐ │ │
│Tool Response│ │ │
└──────┬──────┘ │ │
▼ │ │
┌─────────────┐ │ │
│Execute tools│ │ │
└──────┬──────┘ │ │
▼ ▼ │
┌─────────────┐ ┌───────────┐
│Add results │ │ Normal │
│to messages │ │ response │
└──────┬──────┘ └─────┬─────┘
│ ▲
└───────────────────────┘
┌──────────────────────────┐
│ 设 置:LLM + 工具列表 │
└──────────┬───────────────┘
▼
┌──────────────────────────┐
│ 获取用户输入 │◄────┐
└──────────┬───────────────┘ │
▼ │
┌──────────────────────────┐ │
│ 用消息提示LLM │ │
└──────────┬───────────────┘ │
▼ │
需要工具吗? │
│ │ │
是 否 │
│ │ │
▼ └────────────┐ │
┌─────────────┐ │ │
│工具响应 │ │ │
└──────┬──────┘ │ │
▼ │ │
┌─────────────┐ │ │
│执行工具 │ │ │
└──────┬──────┘ │ │
▼ ▼ │
┌─────────────┐ ┌───────────┐
│将结果添加到消息│ │ 常规响应 │
└──────┬──────┘ └─────┬─────┘
│ ▲
└───────────────────────┘
5 工具列表和函数
- 编写供模型使用的函数
- 通过
/v1/chat/completions请求体的tools字段向模型提供这些函数及其参数的列表
如Python中:
# 函数定义
def search_products(query: str, category: str = None, max_price: float = None):
"""
根据标准在产品目录中搜索产品
参数:
query:搜索词
category:产品类别筛选条件
max_price:最高价格筛选条件
返回:
搜索结果
"""
# 此处为具体实现
pass
# 相应的工具列表定义
tools = [
{
"type": "function",
"function": {
"name": "search_products",
"description": "根据各种标准搜索产品目录",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索词或产品名称"
},
"category": {
"type": "string",
"description": "用于筛选的产品类别",
"enum": ["electronics", "clothing", "home", "outdoor"]
},
"max_price": {
"type": "number",
"description": "以美元为单位的最高价格"
}
},
"required": ["query"]
}
}
}
]
一般使用流程:
- 随API请求发送
tools列表 - 解析模型的
tool_calls响应 - 使用提供的参数执行匹配的函数
- 将结果返回给模型
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)