企业微信二次开发如何接入大模型工具?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 输出参数,执行层调真接口。工具调用循环让模型能多轮调用,权限分级防止乱调。把工具注册对、循环跑稳、权限做严,大模型真能调企微接口办事------而不是只会聊天的摆设。

相关推荐
跨境数据猎手44 分钟前
2027最新跨境电商独立站模式拆解
大数据·人工智能
博谷1 小时前
2026AI信源证据链怎么建?BUGOOAI布谷GEO的T0-T3分级与追溯方法
人工智能
悟天特斯1 小时前
智慧园区的“体验升级“:从管理工具到服务生态的人本运营
大数据·人工智能
织码weavecodes3 小时前
织码在线教育系统--主观题 AI 智能阅卷:批量初评与教师复核如何协同
人工智能
考虑考虑8 小时前
JDK26中的Map.ofLazy
java·后端·java ee
剪刀石头布啊9 小时前
vue-react为何要虚拟dom
前端
剪刀石头布啊9 小时前
ahooks常用的一些库,我们使用一下
前端
剪刀石头布啊9 小时前
从git更新本地提交记录到更新已经推送到远端记录
前端
Shockang9 小时前
llama.cpp 本地运行 9B 模型:部署、工具调用与微调数据
人工智能