第 3 课
工具调用、日志排查与开机自启
用 OpenAI tools 参数手动完成一轮查询订单的工具调用,用 lmstudio SDK 的 .act() 自动跑完乘法工具循环,用 lms log stream 看到套上对话模板的真实提示词;了解默认无鉴权、只监听本机的安全边界和 API Token,按官方文档写 systemd 开机自启单元,最后释放资源。
学习位置仅保存在当前浏览器,有效期 180 天。
图文教材
本课对照的官方页面:OpenAI 兼容接口的工具调用、Python SDK .act()、lms log stream、鉴权、llmster 开机自启。第 1 至 4 步在无显卡的 Linux 云主机上实测(llmster 0.0.27+1,openai 3.42.0,lmstudio 1.5.0),输出为节选;第 5 步测试机没有 systemd,按官方文档整理,未实测。
前两课让模型回答问题。这一课让模型调用你写的函数:先用 OpenAI 的 tools 参数手动完成一轮「模型要求调用、你执行、把结果交回去」,再用 LM Studio Python SDK 的 .act() 自动跑完整个循环。最后看清模型实际收到的提示词,并把服务设成开机自启。
第 1 步:准备环境
沿用第 2 课的虚拟环境和服务。如果关过机,先恢复:
export PATH="$HOME/.lmstudio/bin:$PATH"
lms daemon up
lms load ibm/granite-4-micro --context-length 8192 \
--identifier granite --ttl 3600 -y
lms server start
. venv/bin/activate
确认 lms ps 里有 granite,lms server status 显示 The server is running on port 1234.。
第 2 步:用 OpenAI tools 参数手动跑一轮工具调用
工具调用分两次请求:第一次把函数说明(JSON Schema)交给模型,模型返回「想调用哪个函数、参数是什么」;你在自己的代码里执行函数,把结果作为 role: "tool" 的消息追加进去,第二次请求时模型再据此回答。新建 tools.py:
import json
from openai import OpenAI
client = OpenAI(base_url="http://localhost:1234/v1",
api_key="lm-studio")
MODEL = "granite"
def get_delivery_date(order_id: str) -> str:
# 真实项目里这里查数据库;本例写死一条
return {"A1001": "2026-11-05"}.get(order_id, "未找到该订单")
tools = [{
"type": "function",
"function": {
"name": "get_delivery_date",
"description": "查询订单的预计送达日期",
"parameters": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"],
},
},
}]
messages = [{"role": "user", "content": "我的订单 A1001 哪天到?"}]
first = client.chat.completions.create(
model=MODEL, messages=messages, tools=tools, temperature=0)
msg = first.choices[0].message
print("finish_reason:", first.choices[0].finish_reason)
if msg.tool_calls:
call = msg.tool_calls[0]
args = json.loads(call.function.arguments)
print("模型请求调用:", call.function.name, args)
result = get_delivery_date(**args)
print("工具返回:", result)
messages.append(msg)
messages.append({"role": "tool", "tool_call_id": call.id,
"content": result})
final = client.chat.completions.create(
model=MODEL, messages=messages, tools=tools, temperature=0)
print("最终回答:", final.choices[0].message.content)
else:
print("模型没有调用工具:", msg.content)
运行:
python tools.py
对照结果:
finish_reason: tool_calls
模型请求调用: get_delivery_date {'order_id': 'A1001'}
工具返回: 2026-11-05
最终回答: 您的订单 A1001 预计于 2026‑11‑05 已送达。如有其他问题,
请随时告诉我。
finish_reason: tool_calls 说明模型没有直接回答,而是要求调用函数,参数也提取对了。最终回答里的日期来自你的函数,但「预计于……已送达」的说法自相矛盾,这是 3B 小模型的措辞问题。正式使用时,要么换更大的模型,要么让程序直接用工具返回的结构化结果,模型的话只当说明文字。
第 3 步:用 lmstudio SDK 的 .act() 自动循环
手动循环要自己判断 tool_calls、拼消息。LM Studio 的 Python SDK 提供 .act():传入普通 Python 函数,SDK 根据函数签名和 docstring 自动生成工具说明,并反复执行「模型请求 → 调函数 → 交回结果」,直到模型给出最终回答。新建 act.py:
import lmstudio as lms
def multiply(a: float, b: float) -> float:
"""Given two numbers a and b. Returns the product of them."""
return a * b
model = lms.llm("granite")
result = model.act(
"12345 乘以 54321 等于多少?请用工具计算。",
[multiply],
on_message=print,
)
print("rounds:", result.rounds)
lms.llm("granite") 直接拿到第 1 课加载的实例,SDK 默认连本机的 LM Studio,不用写地址。运行:
python act.py
对照结果(节选,SDK 打印的是 JSON 结构,这里只保留关键字段):
AssistantResponse ... "toolCallRequest": {
"arguments": {"a": 12345, "b": 54321},
"name": "multiply", ...}
ToolResultMessage ... "content": "670592745.0" ...
AssistantResponse ... "text": "12345 乘以 54321 的结果是 **670592745**。"
rounds: 2
(原始输出里的中文是 \u4e58\u4ee5 这样的转义,上面已还原成中文。)12345 × 54321 = 670592745,结果正确,因为乘法是你的函数算的,不是模型心算的。rounds: 2 表示跑了两轮:一轮请求调用,一轮给出回答。
第 4 步:用 lms log stream 看模型实际收到了什么
调试提示词、排查答非所问时,最有用的是看到模型真正收到的字符串。开一个新终端运行:
export PATH="$HOME/.lmstudio/bin:$PATH"
lms log stream --source model --filter input
回到原终端,随便发一个请求,比如第 2 课第 5 步那条「只回答:好」。日志终端会打印:
Streaming logs from LM Studio
timestamp: 11/2/2026, 12:19:58 AM
type: llm.prediction.input
modelIdentifier: ibm/granite-4-micro
modelPath: ibm/granite-4-micro
input:
<|start_of_role|>system<|end_of_role|>你是简洁的助手。<|end_of_text|>
<|start_of_role|>user<|end_of_role|>只回答:好<|end_of_text|>
<|start_of_role|>assistant<|end_of_role|>
(日志时间按测试机所在的北京时间显示。)这就是 Granite 的对话模板:每条消息套上角色标记,最后以 assistant 开头留给模型续写。第 1 课里 Prompt Tokens 比你输入的字多,就是这些标记占的。其他常用写法:
| 写法 | 看什么 |
|---|---|
--filter output | 模型输出 |
--filter input,output --json | 输入输出都看,JSON 格式便于存档 |
--filter output --stats | 输出加速度等统计 |
--source server | 服务端请求日志 |
按 Ctrl+C 退出。
第 5 步:安全提醒与开机自启(未实测)
安全提醒。 官方鉴权文档说明,LM Studio 默认不要求鉴权,任何能连上端口的程序都能调用模型。服务默认只监听 127.0.0.1,本机以外访问不到,这是安全的默认值。API Token 功能需要 LM Studio 0.4.0 及以上,按官方文档要在桌面版的 Developer 页面 → Server Settings 里打开「Require Authentication」,再用 Manage Tokens 创建;之后请求都要带 Authorization: Bearer $LM_API_TOKEN。纯命令行的 llmster 在官方页面上没有对应的开关说明,所以在没有鉴权的情况下,不要用 --bind 0.0.0.0 把服务开到公网或不受信任的网络。需要远程访问时,可以继续只监听本机,再用 SSH 端口转发(例如 ssh -L 1234:localhost:1234 用户@服务器)。这是通用做法,不是 LM Studio 文档的内容。
开机自启。 下面按官方 llmster 文档整理,用 systemd 在开机时启动 daemon、加载模型、打开服务。本课测试机没有 systemd,这一步未实测。官方示例加载的是 openai/gpt-oss-20b(需要更多内存),这里换成本课用的 ibm/granite-4-micro;alice 换成你的用户名。新建 /etc/systemd/system/lmstudio.service:
[Unit]
Description=LM Studio Server
[Service]
Type=oneshot
RemainAfterExit=yes
User=alice
Environment="HOME=/home/alice"
ExecStartPre=/home/alice/.lmstudio/bin/lms daemon up
ExecStartPre=/home/alice/.lmstudio/bin/lms load ibm/granite-4-micro --yes
ExecStart=/home/alice/.lmstudio/bin/lms server start
ExecStop=/home/alice/.lmstudio/bin/lms daemon down
[Install]
WantedBy=multi-user.target
启用并检查:
sudo systemctl daemon-reload
sudo systemctl enable lmstudio.service
sudo systemctl start lmstudio.service
systemctl status lmstudio
curl http://localhost:1234/v1/models
官方文档也说,可以去掉 lms load 那一行,改为依靠第 2 课的 JIT 按需加载和自动卸载。停用自启用 sudo systemctl disable lmstudio。
收尾。 本课练完后按顺序释放资源(以下已实测):
lms unload --all
lms server stop
lms daemon down
lms daemon status
对照结果:
Unloaded 1 model.
Stopped the server on port 1234.
Shutting down llmster...
Done.
LM Studio is not running
小结
- OpenAI 的
tools参数在 LM Studio 上照常可用:看finish_reason == "tool_calls",执行函数后用role: "tool"交回结果,再请求一次。 - lmstudio Python SDK 的
.act()把普通函数变成工具并自动跑完多轮,rounds告诉你跑了几轮。 - 小模型能正确提取参数,但措辞可能出错;关键数据以工具返回为准。
lms log stream --source model --filter input能看到套上模板后的真实提示词,是排查问题的第一步。- 默认没有鉴权、只监听本机;开放到网络之前先配好 API Token 或改用 SSH 转发。官方 systemd 单元可以实现开机自启,记得把模型和用户名换成你自己的。
到这里,你已经在一台没有图形界面的机器上把 LM Studio 变成了可编程的本地模型服务:OpenAI 兼容、支持结构化输出、有状态对话和工具调用。
