企业微信二次开发如何接入大模型工具?API接口实现智能任务调用的技术思路

最近做的企微二开,要让大模型不只是聊天,还能真调企微接口办事------客户说"帮我查下张三的订单",大模型理解后自动调企微的客户查询接口和业务系统的订单接口,把结果组织好回给客户。这就是 function calling 范式在企微场景的落地。和之前聊的意图解析层不同,那篇重点在意图识别和槽位,这篇重点在工具怎么注册给大模型、工具调用循环怎么跑、企微接口怎么被包装成工具。把调了哪些接口记下来。

Eyun 平台 开放的企微 API,统一 POST+JSON,鉴权用 App Token 加 appid(Authorization: Bearer eyk_xxxx),路径 {BASE_URL}/wx-api/api/<模块>/<动作>,响应封套 {code, data, detail, message, time},code 为 0 成功。

工具不是接口,是接口的包装

大模型不能直接调企微接口------接口参数复杂、鉴权要带 token、响应格式要解析。要把接口包装成"工具",工具定义清楚名字、描述、参数 schema,大模型按 schema 输出调用参数,我们的执行层拿参数调真接口。

工具注册:把企微接口暴露给大模型

每个企微接口包装成一个工具,注册到工具表:

python 复制代码
TOOLS = [
    {
        "name": "query_customer",
        "description": "按手机号或昵称查客户信息,返回客户姓名、电话、标签",
        "parameters": {
            "type": "object",
            "properties": {
                "keyword": {"type": "string", "description": "客户手机号或昵称"}
            },
            "required": ["keyword"]
        }
    },
    {
        "name": "send_message",
        "description": "给客户发文本消息",
        "parameters": {
            "type": "object",
            "properties": {
                "to_uin": {"type": "number", "description": "客户uin"},
                "content": {"type": "string", "description": "消息内容"}
            },
            "required": ["to_uin", "content"]
        }
    },
    {
        "name": "update_label",
        "description": "给客户打标签",
        "parameters": {
            "type": "object",
            "properties": {
                "uin": {"type": "number", "description": "客户uin"},
                "labels": {"type": "array", "items": {"type": "string"}, "description": "标签名列表"}
            },
            "required": ["uin", "labels"]
        }
    }
]

工具描述要清晰------大模型靠描述判断什么时候调这个工具。描述写模糊,模型调错或不调。

工具执行:参数到接口调用

大模型输出工具名和参数后,执行层拿参数调真企微接口:

python 复制代码
import requests

BASE = "https://api.eyun.com"
HEADERS = {"Authorization": "Bearer eyk_xxxx", "Content-Type": "application/json"}

def execute_tool(appid, tool_name, args):
    if tool_name == "query_customer":
        return query_customer(appid, args["keyword"])
    elif tool_name == "send_message":
        return send_message(appid, args["to_uin"], args["content"])
    elif tool_name == "update_label":
        return update_label(appid, args["uin"], args["labels"])
    else:
        return {"error": f"unknown tool: {tool_name}"}

def query_customer(appid, keyword):
    if keyword.isdigit() and len(keyword) >= 11:
        resp = requests.post(
            f"{BASE}/wx-api/api/contact/phoneNumberSearch",
            headers=HEADERS,
            json={"appid": appid, "phone": keyword}
        )
    else:
        resp = requests.post(
            f"{BASE}/wx-api/api/contact/search",
            headers=HEADERS,
            json={"appid": appid, "keyword": keyword}
        )
    data = resp.json()["data"]
    return {"name": data.get("nickName", ""), "phone": data.get("mobile", "")}

工具执行返回结构化数据,大模型拿数据组织自然语言回复。接口路径和参数在开发文档。

工具调用循环:多轮工具调用

大模型调一个工具拿到结果后,可能还要再调别的工具------查到客户后要查订单,查到订单后要发消息。工具调用循环:

python 复制代码
def run_conversation(appid, user_msg, max_steps=5):
    messages = [{"role": "user", "content": user_msg}]
    
    for step in range(max_steps):
        # 1. 大模型决定调什么工具
        resp = llm.chat(messages, tools=TOOLS)
        
        # 2. 模型要调工具
        if resp["tool_calls"]:
            for call in resp["tool_calls"]:
                # 3. 执行工具
                result = execute_tool(appid, call["name"], call["arguments"])
                # 4. 把结果给模型
                messages.append({"role": "tool", "tool_call_id": call["id"], "content": str(result)})
        else:
            # 5. 模型不调工具了,直接回用户
            return resp["content"]
    
    return "处理步骤太多,请简化问题"

max_steps 防止死循环------模型反复调工具不收敛。超过步数直接返回提示。不做步数限制,模型可能无限调工具烧 token。

工具权限:不是所有工具都能被调

大模型想调什么就调什么会出事------客户说"帮我删了张三",模型真去调删除接口。工具要按权限分级:

  • 只读工具(query_customer、search):模型可自由调

  • 写工具(send_message、update_label):要权限校验,且部分要人工确认

  • 高危工具(delete、disband):不注册给模型,模型调不到

python 复制代码
def execute_tool_safe(appid, tool_name, args, caller_role):
    tool = TOOL_REGISTRY.get(tool_name)
    if not tool:
        return {"error": "tool not found"}
    
    # 权限校验
    if tool["risk_level"] == "high" and caller_role != "admin":
        return {"error": "permission denied"}
    
    # 写操作要确认
    if tool["risk_level"] == "medium":
        return {"need_confirm": True, "preview": simulate(tool_name, args)}
    
    return execute_tool(appid, tool_name, args)

写操作返回"需要确认",前端给用户确认后再真执行。不做权限分级,模型乱调接口,事故必然。

工具结果回写:结构化数据给模型

工具执行返回的是结构化数据(JSON),模型要基于数据生成自然语言回复。返回给模型时要格式化清楚:

复制代码
# 工具返回给模型的内容
"工具 query_customer 返回:
客户姓名:张三
手机号:138****8888
标签:高意向、老客户"

结构化数据格式化成文本,模型更容易理解。直接丢 JSON 也行,但模型可能漏读字段。

上下文管理:工具调用历史

多轮工具调用,每轮的调用记录要存上下文,模型能回顾之前调了什么、拿到了什么:

python 复制代码
messages = [
    {"role": "user", "content": "查张三的订单"},
    {"role": "assistant", "tool_calls": [{"name": "query_customer", "arguments": {"keyword": "张三"}}]},
    {"role": "tool", "content": "客户uin: 8888, 姓名: 张三"},
    {"role": "assistant", "tool_calls": [{"name": "query_order", "arguments": {"uin": 8888}}]},
    {"role": "tool", "content": "订单SO-123, 金额300元"},
    {"role": "assistant", "content": "张三有一笔订单SO-123,金额300元"}
]

上下文让模型知道之前调过什么,不会重复调。不存上下文,模型每轮都从零开始,反复查同一客户。

写在最后

大模型工具接入这套东西,本质是把企微接口包装成工具注册给大模型------phoneNumberSearch、sendText、updateLabel 这些接口变成工具的执行体,大模型按工具 schema 输出参数,执行层调真接口。工具调用循环让模型能多轮调用,权限分级防止乱调。把工具注册对、循环跑稳、权限做严,大模型真能调企微接口办事------而不是只会聊天的摆设。

相关推荐
IT大白鼠1 小时前
彭大帅的AI运维助手——自然语言管理 Linux 集群与网络设备——第 0 篇 · 导读:把 Linux 运维交给 AI,到底靠谱吗
linux·运维·人工智能
程序员的账号1 小时前
《深度学习入门2自制框架》中文PDF+源代码+斋藤康毅
人工智能·深度学习·pdf
一木 之林1 小时前
多模态大模型一统精讲 NLP 和 CV:从 ViT、CLIP 到 BLIP、LLaVA 的三段式统一架构
人工智能·自然语言处理·架构
2601_966949651 小时前
量化回测中分钟数据应该如何保存?从文件到数据仓库的工程化设计
开发语言·python·pandas·量化交易·股票数据·quantdash
猎头南楼1 小时前
金融大模型与数据平台架构演进:从智能体到知识湖的工程化能力观察
人工智能·机器学习
IMPYLH1 小时前
HTML 的 <td> 元素
前端·html
林伽一1 小时前
从对比语言模型到智能体治理,AI基础设施迎来新一轮重构 | 2026年09月26日
人工智能·科技·ai
回眸&啤酒鸭1 小时前
【回眸】AI 电商盲盒怎么营收?从营销创新到运营提效
人工智能
huisheng_qaq1 小时前
【Python基础篇-05】深入理解python的异常处理与文件读写
python·异常处理·文件读写