如何为冰石机器人扩展大模型工具:以天气查询为例

一、为什么要扩展工具?

冰石机器人( 智能客服系统)内置了发消息、查订单等通用工具,但每个行业的业务千差万别——查快递价、查库存、查天气、对接 ERP……不可能全部写进主程序。

系统的解法是:extensions/ 目录下开发自定义扩展,注册成与大模型对接的 Tool。用户在微信 / 企业微信 / 闲鱼里说一句「查一下北京天气」,大模型识别意图后调用你的 weather_query 工具,工具查完天气再主动把结果发回当前会话。

整条链路如下:

微信/企微/闲鱼天气接口weather_query工具大模型用户微信/企微/闲鱼天气接口weather_query工具大模型用户查一下北京天气tool_calls weather_query city=北京HTTP 查询天气温度/湿度/风力send_text_to_customer【北京天气】...好的,已为您查询并发送

本文用一个完整的天气查询工具走通全流程:写代码 → 配置提示词 → 后台启用 → 看效果。


二、扩展目录放哪里?

运行方式扩展目录
源码开发backend/extensions/weather_query/
打包 exe<exe 同目录>/extensions/weather_query/

每个扩展至少要有 __init__.pyextension.py(含 register(ctx) 入口)。推荐结构:

extensions/weather_query/
  __init__.py
  extension.py       # 注册入口
  tool.py            # 大模型看到的工具定义
  service.py         # 查天气业务逻辑
  city_codes.py      # 城市名 -> 城市编码
  message_sender.py  # 发消息到当前会话

三、天气接口选型(国内、免 API Key)

国内很多教程用过 wthrcdn.etouch.cn,但该接口近年稳定性较差。本文采用双通道策略:

优先级接口特点
主通道http://t.weather.itboy.net/api/weather/city/{citykey}国内 CDN、无需注册 Key、返回中文 JSON
备用https://wttr.in/{城市}?format=j1&lang=zh支持中文城市名,免 Key,主通道失败时使用

主通道需要城市编码(如北京 101010100),我们在 city_codes.py 维护常见城市映射;未收录的城市自动走 wttr.in。

主通道返回示例(节选):

{
  "status": 200,
  "cityInfo": { "city": "北京市", "citykey": "101010100" },
  "data": {
    "wendu": "26.5",
    "shidu": "28%",
    "quality": "优",
    "forecast": [
      { "type": "多云", "high": "高温 29℃", "low": "低温 20℃", "notice": "阴晴之间,谨防紫外线侵扰" }
    ]
  }
}

四、逐步实现

4.1 城市编码表 city_codes.py

CITY_CODE_MAP = {
    "北京": "101010100",
    "上海": "101020100",
    "广州": "101280101",
    "深圳": "101280601",
    # ... 可按需继续添加
}

def normalize_city_name(name: str) -> str:
    s = (name or "").strip()
    for suffix in ("市", "省", "自治区"):
        if s.endswith(suffix):
            s = s[: -len(suffix)]
    return s.strip()

def resolve_city_code(city: str) -> str | None:
    key = normalize_city_name(city)
    return CITY_CODE_MAP.get(key)

用户说「北京市」「北京」都能正确匹配。

4.2 天气服务 service.py

核心逻辑:先 itboy,失败再 wttr.in,最后格式化成可读文本。

import httpx

ITBOY_URL = "http://t.weather.itboy.net/api/weather/city/{citykey}"

class WeatherQueryService:
    async def query(self, city: str) -> dict:
        citykey = resolve_city_code(city)
        if citykey:
            try:
                async with httpx.AsyncClient(timeout=15) as client:
                    resp = await client.get(ITBOY_URL.format(citykey=citykey))
                    payload = resp.json()
                if payload.get("status") == 200:
                    return self._parse_itboy(payload, city)
            except Exception:
                pass  # 走备用通道
        return await self._query_wttr(city)

def format_weather_message(data: dict) -> str:
    return "\n".join([
        f"【{data['city']}天气】",
        f"天气:{data['weather_type']}",
        f"当前气温:{data['temperature']}℃",
        f"湿度:{data.get('humidity', '—')}",
        f"空气质量:{data.get('quality', '—')}",
    ])

格式化后的消息示例:

【北京市天气】
天气:多云
当前气温:26.5℃
湿度:28%
空气质量:优
风力:西南风 2级
提示:各类人群可自由活动
阴晴之间,谨防紫外线侵扰

4.3 发消息 message_sender.py

工具查完天气后要发到当前会话,不能让用户自己再去别处看 JSON。做法与官方扩展示例 express_price_calc 一致:

  1. resolve_wechat_config(db, request) 判断当前是个人微信、企微还是闲鱼;
  2. 接收方默认取 request.group_id / request.group_name(即当前聊天);
  3. 分渠道调用底层发送 API。

个人微信:

from core.wechat_manager import get_wechat_service
from schemas.wechat import MessageSend, MessageType

get_wechat_service().send_message(
    MessageSend(chat_id=recipient_id, content=text, message_type=MessageType.TEXT)
)

企业微信:

wecom_service.send_text_message(
    guid=instance.guid,
    content=text,
    to_id=recipient_id,
    license_code=instance.license_code,
)

闲鱼:request.context.group_infoxianyu_chat_idxianyu_to_id 后调用 session.send_message(...)

统一封装为:

send_out = send_text_to_customer(db, request, recipient_id_override, message)
# 返回 {"ok": True/False, "channel": "...", "detail": "..."}

4.4 工具类 tool.py — 对接大模型

所有自定义工具必须继承 BaseTool,实现 5 个方法:

方法作用
get_name()工具唯一名:weather_query
get_description()给大模型看的说明,写清何时调用
get_tool_type()类型,这里用 DATA_QUERY
get_parameters()JSON Schema 参数定义
async execute(...)执行:查天气 → 发消息 → 返回结果

execute 核心流程:

async def execute(self, tool_call, request):
    city = tool_call.parameters.get("city")
    weather = await self._service.query(city)
    message = format_weather_message(weather)

    db = self._db_factory()
    try:
        send_out = send_text_to_customer(db, request, None, message)
    finally:
        db.close()

    return {
        "success": True,
        "city": city,
        "weather": weather,
        "message_sent": send_out.get("ok"),
        "formatted_message": message,
    }

注意: 工具已主动发消息,应在 get_description 里说明:提示词中宜设 requires_tool_result: false,避免大模型拿到结果后再发一遍重复内容。

4.5 注册入口 extension.py

from .service import WeatherQueryService
from .tool import WeatherQueryTool

_service = WeatherQueryService()

def register(ctx):
    tool = WeatherQueryTool(_service, ctx.db_factory)
    try:
        ctx.tool_registry.register_tool(tool)
        ctx.logger.info("[weather_query] registered weather_query")
    except Exception as e:
        ctx.logger.warning(f"[weather_query] failed: {e}")

def unregister(ctx):
    ctx.logger.info("[weather_query] unregister called")

系统启动时 ExtensionManager 扫描 extensions/ 下所有子目录,自动调用 register(ctx),工具进入全局 tool_registry


五、配置大模型提示词

工具代码写好只是第一步,还要让大模型知道什么时候调用

在管理后台 → 聊天配置 → 编辑对应群组/私聊的 大模型提示词,增加类似说明:

## 工具使用规则

当用户询问某地天气、气温、会不会下雨、穿衣建议等与天气相关的问题时:
1. 调用工具 weather_query,参数 city 填用户提到的城市名(如 北京、上海)。
2. 工具会自动查询天气并发送到当前会话,你只需简短确认即可,例如「好的,已为您查询并发送北京天气」。
3. 回复 JSON 时设置 requires_tool_result 为 false。

示例用户说法:
- 「查一下北京天气」
- 「上海今天热吗」
- 「深圳会不会下雨」

大模型输出格式(系统会自动解析):

{
  "reply_content": "好的,正在为您查询北京天气~",
  "reply_type": "text",
  "requires_tool_result": false,
  "tool_calls": [
    {
      "tool_name": "weather_query",
      "tool_type": "data_query",
      "parameters": { "city": "北京" },
      "description": "查询北京天气并发送给用户"
    }
  ],
  "confidence": 0.95
}

六、后台启用工具

  1. 进入 系统配置管理 → 对应账号(个人微信 / 企微 / 闲鱼)。
  2. 打开 启用工具调用功能
  3. 点击 选择工具,勾选 weather_query(或留空表示启用全部工具)。
  4. 保存配置。

验证是否注册成功:

GET /api/tool-calling/tools/weather_query

应返回工具名、描述和 citysend_to_customer 等参数 schema。

热重载扩展(开发阶段):

POST /api/v1/extensions/reload
Content-Type: application/json

{"name": "weather_query"}

日志中出现 [weather_query] registered weather_query 即表示成功。


七、效果展示

7.1 个人微信私聊

┌──────────────────────────────────────┐
│  用户                          21:35 │
│  帮我查一下北京天气                    │
├──────────────────────────────────────┤
│  冰石机器人                    21:35 │
│  好的,已为您查询并发送北京天气~        │
├──────────────────────────────────────┤
│  冰石机器人                    21:35 │
│  【北京市天气】                        │
│  天气:多云                           │
│  当前气温:26.5℃                      │
│  今日:低温 20℃ ~ 高温 29℃            │
│  湿度:28%                            │
│  空气质量:优                          │
│  风力:西南风 2级                      │
│  提示:各类人群可自由活动               │
│  阴晴之间,谨防紫外线侵扰               │
└──────────────────────────────────────┘

第二条是工具 send_text_to_customer 发出;第一条是大模型的简短确认。

7.2 企业微信群聊

流程相同。工具通过 resolve_wechat_config 识别 account_type=official,走企微第三方 send_text_message,消息出现在当前群或私聊窗口。

7.3 工具返回 JSON(供排查)

send_to_customer=false(测试用),execute 返回:

{
  "success": true,
  "city": "北京",
  "weather": {
    "city": "北京市",
    "source": "itboy",
    "temperature": "26.5",
    "weather_type": "多云",
    "quality": "优"
  },
  "formatted_message": "【北京市天气】\n天气:多云\n...",
  "message_sent": false,
  "send_status": "skipped"
}

八、本地快速测试

无需等用户发消息,可直接调测试接口:

POST /api/tool-calling/test?tool_name=weather_query
Content-Type: application/json

{
  "city": "上海",
  "send_to_customer": false
}

或在 Python 中:

import asyncio
from extensions.weather_query.service import WeatherQueryService, format_weather_message

async def main():
    svc = WeatherQueryService()
    data = await svc.query("杭州")
    print(format_weather_message(data))

asyncio.run(main())

九、常见问题

Q:城市不在映射表里怎么办?
A:自动降级到 wttr.in,支持大多数中文城市名。可在 city_codes.py 继续补充编码。

Q:工具注册了但大模型不调?
A:检查三点:① 后台是否勾选 weather_query;② 提示词是否写明调用条件;③ enable_tools_calling 是否开启。

Q:消息没发出去?
A:看返回的 send_statussend_error。个人微信需客户端在线;企微需实例 status=2(已登录);闲鱼需 context.group_infoxianyu_chat_id

Q:能否只返回 JSON 不让工具发消息?
A:可以,参数传 "send_to_customer": false,由大模型自己组织回复(需在提示词中说明)。


十、小结

扩展冰石机器人的大模型能力,本质上是四步:

  1. 写扩展extensions/<name>/ 下实现 BaseTool + register(ctx)
  2. 接业务:HTTP 调第三方 API(本文:国内免 Key 天气接口)
  3. 发消息:复用 resolve_wechat_config + 分渠道发送,把结果推回当前会话
  4. 配提示词 + 开工具:让大模型知道何时 tool_calls,后台勾选启用

天气查询只是入门示例。同一套模式可以扩展为:查物流、查库存、查课程表、算报价……业务逻辑换进 service.py,发消息逻辑复用 message_sender.py,就是属于你行业的专属大模型工具。


如果本文对你有帮助,欢迎点赞收藏。有问题可在评论区留言交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值