Qwen3:32B在Clawdbot中支持Function Calling:JSON Schema定义与调用实录
2026/3/19 12:33:42 网站建设 项目流程

Qwen3:32B在Clawdbot中支持Function Calling:JSON Schema定义与调用实录

1. 为什么需要在Clawdbot里用Qwen3:32B做函数调用

你有没有遇到过这样的情况:用户在聊天界面里说“帮我查一下今天北京的天气”,或者“把这份Excel里的销售额加总”,又或者“把这张截图里的文字转成表格”——这些都不是简单聊聊天,而是明确要执行某个具体动作。

传统对话模型只能“说”,但没法“做”。而Function Calling(函数调用)能力,就是让大模型从“嘴上功夫”升级为“动手达人”的关键一步。它能让模型理解用户意图后,自动选择并调用后台真实的服务接口,比如调用天气API、触发数据处理脚本、调用OCR服务识别图片文字。

Clawdbot作为一款轻量级、可嵌入的智能代理平台,最近完成了对Qwen3:32B模型的深度集成。这不是简单地把模型接进来跑通就行,而是真正打通了Function Calling全流程:从模型识别工具需求、生成符合规范的JSON结构,到Clawdbot准确解析、安全转发、执行回调,再到把结果自然融入对话流。整个链路稳定、低延迟、可调试,且完全基于私有部署环境。

本文不讲抽象原理,也不堆砌参数配置。我们直接带你走一遍真实场景下的完整调用过程:怎么定义一个能查快递的工具、模型如何理解并调用它、Clawdbot怎么把JSON请求发出去、又怎么把返回结果变成一句人话回复给用户。所有步骤都基于你能在本地复现的配置,代码可复制、路径可验证、效果可感知。

2. 环境准备与Clawdbot-Qwen3直连架构说明

2.1 整体通信链路:从用户输入到函数执行

Clawdbot本身不运行大模型,它是一个智能调度中枢。它通过HTTP协议,将用户消息转发给后端模型服务;模型返回带function_call字段的响应后,Clawdbot负责解析、构造请求、调用真实工具,并把结果重新送回模型补全最终回复。

整个链路如下:

用户 → Clawdbot Web界面(80端口) ↓ Clawdbot内部代理 → 转发至本地Ollama网关(18789端口) ↓ Ollama服务 → 加载Qwen3:32B模型(32B量化版,显存占用约20GB) ↓ 模型推理 → 输出含tool_calls的JSON响应 ↓ Clawdbot捕获响应 → 解析function name + arguments ↓ Clawdbot调用预设工具(如快递查询API)→ 获取真实数据 ↓ Clawdbot将结果封装为messages → 再次发给Qwen3:32B → 生成自然语言回复

这个设计的关键在于:模型只负责“决策”和“编排”,不碰真实数据;Clawdbot只负责“执行”和“粘合”,不参与推理。职责清晰,安全可控,也方便后续替换模型或扩展工具。

2.2 本地部署核心组件确认

在开始写函数定义前,请先确认你的环境已就绪:

  • Ollama已安装,且成功拉取qwen3:32b模型(命令:ollama pull qwen3:32b
  • Ollama服务正在运行,默认监听127.0.0.1:11434
  • Clawdbot已配置好反向代理,将http://localhost:18789映射到Ollama的http://localhost:11434(Clawdbot内部配置见下文)
  • 你有一个可用的快递查询工具(本文以模拟HTTP API为例,实际可替换为数据库查询、Python脚本等)

小提示:Clawdbot的代理配置位于其config.yaml中,关键段落如下:

model: provider: ollama base_url: "http://localhost:18789" # 注意:这是Clawdbot对外暴露的网关地址 model_name: "qwen3:32b"

实际上,Clawdbot启动时会自动在本地起一个轻量HTTP代理,监听18789端口,把所有请求透传给Ollama的11434端口。这样做的好处是:后续可统一加鉴权、日志、熔断,而不用改Ollama本身。

3. Function Calling实战:定义快递查询工具并完成一次完整调用

3.1 第一步:用JSON Schema定义“查快递”这个能力

Function Calling不是让模型随便调用什么函数,而是必须提前告诉它:“你能用哪些工具?每个工具有什么输入要求?”

这靠的就是JSON Schema描述。它像一份“工具说明书”,模型靠它理解该不该调、怎么调、传什么参数。

我们来定义一个最常用的工具:track_package(查快递)。

{ "name": "track_package", "description": "根据快递单号查询物流轨迹,返回最新状态和预计送达时间", "parameters": { "type": "object", "properties": { "tracking_number": { "type": "string", "description": "12位纯数字的快递单号,例如:123456789012" }, "carrier": { "type": "string", "enum": ["sf", "zto", "yto", "htky"], "description": "快递公司简称,sf=顺丰,zto=中通,yto=圆通,htky=百世汇通" } }, "required": ["tracking_number"] } }

注意几个细节:

  • name必须是合法的函数名(不能含空格、特殊符号),Clawdbot后续靠它匹配真实函数;
  • description是写给人看的,更是写给模型看的——越具体,模型越不容易误判;
  • parameters用标准JSON Schema语法,required字段明确哪些是必填项;
  • enum限制了可选值,能显著降低模型乱填carrier的风险。

Clawdbot中,这类工具定义统一放在tools/目录下,文件名为track_package.json。你也可以放多个JSON文件,Clawdbot启动时会自动加载全部。

3.2 第二步:在Clawdbot中注册真实执行逻辑

光有说明书不行,还得有“工人”。我们在Clawdbot里写一个Python函数,真正去调快递API:

# tools/track_package.py import requests import json def track_package(tracking_number: str, carrier: str = "sf") -> dict: """ 模拟调用快递100 API(实际使用请替换为真实密钥) 返回格式示例: { "status": "success", "data": { "logistics_company": "顺丰速运", "last_status": "派件中,预计今日送达", "estimated_delivery": "2025-04-05" } } """ # 这里是真实调用逻辑,为演示简化为mock mock_data = { "sf": {"logistics_company": "顺丰速运", "last_status": "派件中,预计今日送达", "estimated_delivery": "2025-04-05"}, "zto": {"logistics_company": "中通快递", "last_status": "已签收", "estimated_delivery": "2025-04-04"}, } return { "status": "success", "data": mock_data.get(carrier, mock_data["sf"]) }

Clawdbot会自动扫描tools/目录下所有.py文件,按文件名(去掉.py)匹配JSON Schema中的name。也就是说,track_package.py对应track_package.json,参数名、类型、返回结构都严格对齐。

3.3 第三步:发起一次真实对话,观察全程调用日志

现在,打开Clawdbot Web界面(通常是http://localhost:80),输入:

我的顺丰单号是123456789012,现在到哪了?

按下回车,Clawdbot后台会记录完整流程。我们截取关键日志片段来看:

[INFO] 用户输入: "我的顺丰单号是123456789012,现在到哪了?" [DEBUG] 发送给Qwen3:32B的messages: [ {"role": "user", "content": "我的顺丰单号是123456789012,现在到哪了?"} ] [DEBUG] Qwen3:32B返回: { "message": { "role": "assistant", "content": "", "tool_calls": [ { "function": { "name": "track_package", "arguments": "{\"tracking_number\": \"123456789012\", \"carrier\": \"sf\"}" } } ] } } [INFO] 解析tool_calls → 调用track_package(tracking_number='123456789012', carrier='sf') [INFO] 工具执行返回: {"status": "success", "data": {"logistics_company": "顺丰速运", "last_status": "派件中,预计今日送达", "estimated_delivery": "2025-04-05"}} [DEBUG] 构造新messages发回模型: [ {"role": "user", "content": "我的顺丰单号是123456789012,现在到哪了?"}, {"role": "assistant", "tool_calls": [...]}, {"role": "tool", "name": "track_package", "content": "{\"status\": \"success\", ...}"} ] [INFO] Qwen3:32B最终回复: "您的顺丰快件(单号123456789012)当前状态是【派件中,预计今日送达】,由顺丰速运承运。"

看到没?整个过程干净利落:

  • 模型没瞎猜,精准识别出单号和快递公司;
  • 参数格式完全合规,字符串被正确包裹、引号转义无误;
  • Clawdbot没卡在JSON解析上,毫秒级完成调用;
  • 最终回复自然流畅,没有暴露任何JSON痕迹。

这就是Function Calling落地的真实手感——不是Demo,是能进生产环境的闭环。

4. 常见问题与避坑指南(来自真实踩坑记录)

4.1 模型“假装调用”:返回了tool_calls,但arguments是空或乱码

这是新手最高频的问题。根本原因往往不是模型不行,而是Schema描述太模糊

❌ 错误写法:

"description": "查快递信息" "properties": { "number": { "type": "string" } }

正确写法(如前文所示):

  • 明确单号格式(“12位纯数字”);
  • 给carrier加enum约束;
  • required字段写全;
  • description里用例子强化认知(“例如:123456789012”)。

Qwen3:32B很聪明,但它依赖你给的“线索”。线索越准,它越靠谱。

4.2 Clawdbot报错“Tool not found”:明明写了py文件,却找不到

检查三件事:

  • 文件名是否和JSON的name字段完全一致(大小写、下划线都不能错);
  • .py文件是否放在tools/子目录下(不是tools/__init__.py那种包结构);
  • 函数签名是否严格匹配:参数名、类型注解(str,int等)、默认值都要对得上。

一个小技巧:在Clawdbot启动日志里搜索Loaded tool,能看到它到底加载了哪些工具。没出现,说明路径或命名有问题。

4.3 调用后模型不生成最终回复,卡在“thinking…”

大概率是工具返回内容格式不对。Clawdbot要求工具函数必须返回一个可JSON序列化的dict,且不能有datetimebytes等不可序列化类型。

如果你的工具里用了datetime.now(),记得转成字符串:

"timestamp": datetime.now().isoformat() # # 不要写成 "timestamp": datetime.now() # ❌ 报错

另外,确保返回字典里不要有中文key(虽然Python允许,但某些JSON库会出问题),统一用英文key。

5. 进阶建议:让Function Calling更稳、更快、更实用

5.1 给工具加超时与重试,别让一次失败拖垮整轮对话

默认情况下,Clawdbot调用工具是同步阻塞的。如果快递API响应慢,用户就得干等。建议在工具函数里主动加控制:

# tools/track_package.py import time import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry def track_package(tracking_number: str, carrier: str = "sf") -> dict: session = requests.Session() retry_strategy = Retry( total=2, backoff_factor=0.5, status_forcelist=[429, 500, 502, 503, 504], ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter) try: response = session.get( f"https://api.kuaidi100.com/api/v1/tracking?number={tracking_number}&type={carrier}", timeout=(3, 5) # connect=3s, read=5s ) return {"status": "success", "data": response.json()} except Exception as e: return {"status": "error", "message": f"查询失败:{str(e)}"}

这样,即使外部API抖动,Clawdbot也能快速失败、返回友好提示,而不是让用户等10秒。

5.2 多工具协同:让模型自己决定先查单号,再查运费

一个复杂任务常需多个工具配合。比如“帮我寄个文件到上海,选最便宜的快递”,就需要:

  • 先调get_shipping_options(查可选快递);
  • 再调calculate_freight(算各家公司运费);
  • 最后调generate_shipment_label(打单)。

你只需把这三个工具的JSON Schema都放进tools/目录,Qwen3:32B就能根据上下文自动规划调用顺序。Clawdbot会按tool_calls数组顺序依次执行,无需你写流程引擎。

5.3 日志可视化:把每次调用变成可追踪、可分析的数据

Clawdbot默认日志是文本流。建议把关键事件(用户输入、模型决策、工具调用、最终回复)写入结构化日志,比如JSON Lines格式:

{"timestamp":"2025-04-05T10:23:45Z","event":"tool_call","tool":"track_package","args":{"tracking_number":"123456789012"},"duration_ms":128} {"timestamp":"2025-04-05T10:23:45Z","event":"final_reply","content":"您的顺丰快件...","tokens_used":217}

有了这些数据,你就能回答真实业务问题:

  • 哪些工具调用最多?说明用户最需要什么能力;
  • 平均耗时多少?哪里有优化空间;
  • 错误集中在哪个环节?是模型误判,还是工具不稳定?

这才是Function Calling走向规模化应用的真正起点。

6. 总结:Function Calling不是功能,而是工作流重构的开始

回顾这一整套实践,我们没碰一行Ollama源码,没改Clawdbot核心逻辑,只是靠三样东西就跑通了生产级函数调用:

  • 一份严谨的JSON Schema(告诉模型“你能做什么”);
  • 一个干净的Python函数(告诉Clawdbot“具体怎么做”);
  • 一次真实的端到端对话(验证“它真的能干活”)。

Qwen3:32B在这里扮演的是“智能调度员”角色——它不存储数据,不连接数据库,不调用API,但它能读懂用户模糊的指令,拆解成精确的动作,再把动作交给合适的“工人”去执行。这种“大模型+小工具”的组合,比训练一个巨无霸全能模型更轻量、更安全、更可控。

你现在完全可以照着本文,5分钟内搭起自己的第一个Function Calling流程:定义一个查天气的工具,写个调用OpenWeatherMap API的函数,然后问“北京明天热不热?”——答案会从模型嘴里说出来,而背后,是真实世界的数据在流动。

技术的价值,从来不在参数多大、显存多猛,而在于它能不能让一句“帮我查一下”,变成一件立刻办成的事。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询