简介:这是一份Coze Agent接入微信的可运行源码包,目标受众是希望为个人微信搭建自动化回复能力的开发者与运维人员,适用于企业客户服务、群运营、私聊助手等实际场景。该zip压缩包共3个文件,主要包含Coze机器人配置源码、HTML说明页面以及gitignore项目配置文件,整体体积仅7KB,结构轻巧、便于直接下载后对照调整。源码完整覆盖从创建Coze Bot、设置人设与回复逻辑、发布并获取API令牌,到微信机器人服务启动验证的关键逻辑,同时提供Docker容器化部署和docker-compose多服务编排的配置说明,可帮助使用者快速跑通群聊与私聊两种场景的自动应答流程。包内还隐含了应对接口鉴权、服务常驻运行等问题的处理细节,适合有一定编程基础、希望将大模型能力接入日常IM工具的中级开发者自行扩展。目前已有138人学习下载,可作为快速上手的参考范例。
1. Coze Agent接入微信:一套PHP源码让Agent在微信里直接对话
最近帮朋友做客服助手,折腾了一圈发现:Coze上的Agent能力很强,但想让它出现在微信对话框旁边,绕不开“微信内部环境”这堵墙。市面上大多教程只讲Coze平台内部怎么配工作流,没人告诉你Agent怎么以H5形态在微信里跑通。这份源码的核心思路很直接——用PHP伪造微信浏览器头信息,把Coze Agent发布成一个能在微信内直接打开的对话页面,用户扫码或点链接就能和Agent对话,不需要开发小程序,不需要认证服务号。适合手里已有一个Coze Agent、想快速让它在微信里可用的开发者,也适合想研究“网页如何嵌入微信生态”这一套兼容做法的从业者。接下来我把原理、代码结构和部署排错全部拆开讲。
2. 为什么选“伪UA + PHP中转”这条路:三种接入方案的成本对比
2.1 Coze Agent的开放能力边界:API接口与Token计费
在动手写代码前,先得确认Coze Agent对外提供的是什么。Agent在Coze平台上搭建好后,可以发布为API服务,这意味着你可以通过HTTP请求把用户的话发给Agent,再把Agent的回复接回来。这本质上是把Coze当成一个“AI后端”,你只需要处理网络请求和用户会话。
这套能力有几个关键约束。第一,API是按Token计费的,不是按条数,所以长对话、长回复都会消耗更多额度,批量测试时要留意费用。第二,Coze的API是无状态的,服务端不会主动记你和某个用户聊到哪了,所有对话历史都要由调用方自己保存并在每次请求时传上去。这两点决定了接入端的架构:你必须有一个“会话管理”角色,不能只写一个转发接口就完事。
我拿到的这份源码里,会话历史是用文件存储的,按user_id分文件存放,每次请求前把历史读出来拼进请求体。这个设计虽然简陋,但足够个人和小团队用,不用上Redis。如果你未来要支撑大量并发用户,再把这个文件存储换成数据库或内存缓存即可。
2.2 微信内的三条路:小程序、公众号接口、H5伪UA,为什么最后这个最省事
要让微信用户用上Coze Agent,常见方案有三条。第一条是开发微信小程序,把Agent的对话界面做进小程序里。这条路体验最好,但要注册小程序账号、过审核、写前端页面,一套下来至少一周,而且不能用网页技术栈直接套。第二条是公众号的客服消息接口,通过接收用户消息再调用Coze API回复,这条路的限制是只能用于认证服务号,个人订阅号没有客服消息权限,而且消息格式有诸多限制。第三条就是我用的方案:做一个H5页面,部署在公网服务器上,用户在微信里打开链接,这个页面本质上是一个聊天界面,前端把用户输入发给后台PHP,PHP再转发给Coze。
第三条路之所以最省事,是因为它不依赖任何微信开放平台的能力,不需要审核,不要求企业主体,只要你的页面在微信内置浏览器里能正常打开。微信的webview对H5是几乎完全放开的,唯一的硬要求是HTTPS协议,这点放在后面部署章节细说。但有一个细节要注意:微信内置浏览器会识别部分非微信浏览器的请求特征,某些服务(比如微信支付、公众号OAuth)会校验User-Agent是否来自微信客户端,而反过来,一些页面为了让自己在微信里表现正常,也会伪装成微信的UA。这份源码里做的就是后者——让页面携带微信UA,从而规避某些场景下对非微信浏览器的限制。
注意:伪造UA只是为了页面能在微信内正常运行,不涉及任何绕过安全机制的操作,别把它想复杂了。
2.3 源码整体结构:config配置、请求转发、前端界面三块如何分工
拿到这份源码包后,解压出来是三个核心文件加一个数据目录。config.php存放Coze的API Token、Bot ID、API地址;index.php是前端聊天页面,包含HTML、CSS和一点JavaScript;agent_api.php是后端转发脚本,接收前端发来的消息、拼接历史、请求Coze接口、返回结果;data目录用来存会话历史文件。整体流程是:用户在微信点开index.php页面→输入消息→JavaScript用fetch或ajax发到agent_api.php→agent_api.php调Coze API→拿到回复后返回前端显示。
先看config.php的配置内容,这是整个接入的第一步。
<?php // config.php —— 全局配置 // Coze开放平台 -> 个人令牌页面 生成的API Token define('COZE_API_TOKEN', 'pat_xxxxxxxxxxxxxxxxxxxx'); // Agent发布后,在Bot详情页拿到的Bot ID define('COZE_BOT_ID', '74xxxxxxxxxxxx'); // Coze API的base地址,注意区分国内版和国际版 define('COZE_API_BASE', 'https://api.coze.cn'); // 单轮回复超时时间(秒),工作流复杂时可以调大 define('COZE_TIMEOUT', 120); // 历史消息最多保留多少条,超出部分截断,省Token define('MAX_HISTORY', 20); ?>这里的COZE_API_TOKEN相当于你的身份凭证,所有请求都靠它鉴权,泄露后别人可以无限刷你的额度,所以这个值绝对不要写在前端JS里,只能留在服务端。COZE_BOT_ID是Agent被调用时的标识,如果你在Coze平台建了多个Agent,靠它区分调哪个。COZE_API_BASE是接口地址,国内版和海外版的主域名不一样,按你注册的平台选。
3. 核心代码逐段拆解:从聊天界面到Coze响应回显的完整调用链
3.1 前端聊天页:不依赖框架,一个HTML页面在微信里跑起来
index.php是整个接入的门面,用户看到的就是这个页面。考虑到微信内置浏览器的兼容性,没有用Vue或React这类框架,直接原生HTML加JavaScript,避免构建和兼容问题。页面上半部分是消息展示区,滚动查看对话记录,下半部分是输入框和发送按钮,回车也能发送。
前端逻辑里最重要的一块是发起请求和渲染回复。发送消息时,把当前输入的内容通过fetchPOST给agent_api.php,然后等待返回的JSON,把assistant角色的回复追加到消息区。这里有一个经验:微信webview对fetch的支持没问题,但如果你要兼容更老的环境,用XMLHttpRequest更保险。
// index.php 内的核心请求函数 async function sendMessage() { const input = document.getElementById('userInput'); const text = input.value.trim(); if (!text) return; appendMessage('user', text); input.value = ''; const loading = appendMessage('assistant', '正在思考...'); try { const resp = await fetch('agent_api.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: text }) }); const data = await resp.json(); if (data.code === 0) { loading.textContent = data.reply; } else { loading.textContent = '请求失败:' + data.msg; } } catch (e) { loading.textContent = '网络异常,请重试'; } }这个函数做了三件事:把用户输入渲染到页面,调用后端接口,用后端返回的结果替换掉“正在思考”占位文本。注意这是非流式写法,也就是等Coze完整回复生成后才一次性显示,好处是逻辑简单、不容易断,坏处是长回复时用户要等一会儿。真人对话场景下,如果Agent回答很长,可以考虑改成SSE流式,但我建议第一版先用这种同步方式跑通,再优化体验。
3.2 后端转发层:拼接历史、调用Coze API、处理返回格式
agent_api.php是整个接入的“中间人”。它接收前端POST过来的JSON数据,取出message字段,再根据当前用户的标识(这里直接用请求来源IP加随机数做user_id)读取对应的历史记录文件,拼接出完整的请求体,然后用cURL向Coze的对话接口发请求。响应解析后,把最新一条助手消息返回给前端,同时把这一轮对话追加到历史记录里。
这里有一个很容易踩的坑:Coze的历史消息格式要求按角色交替排列,而且第一条必须是user消息。如果不按这个规则来,Agent会丢失上下文甚至直接报参数错误。源码里专门写了一个normalizeHistory函数来处理这个问题,见下面的代码。
<?php // agent_api.php 核心转发逻辑 function callCoze($userId, $userMessage) { $history = loadHistory($userId); // Coze要求历史列表以user角色开头,且user/assistant交替 $messages = []; foreach ($history as $i => $msg) { if ($i === 0 && $msg['role'] !== 'user') { continue; // 丢弃第一条非user记录,避免参数错误 } $messages[] = $msg; } $messages[] = ['role' => 'user', 'content' => $userMessage]; $postData = [ 'bot_id' => COZE_BOT_ID, 'user_id' => $userId, 'stream' => false, 'messages' => $messages ]; $ch = curl_init(COZE_API_BASE . '/v3/chat'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . COZE_API_TOKEN, 'Content-Type: application/json' ], CURLOPT_POSTFIELDS => json_encode($postData), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => COZE_TIMEOUT ]); $resp = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { return ['code' => -1, 'msg' => 'Coze接口返回HTTP ' . $httpCode]; } $data = json_decode($resp, true); // 同步模式下的回复在data.answer字段 $reply = $data['data']['answer'] ?? '未获取到回复'; saveHistory($userId, $userMessage, $reply); return ['code' => 0, 'reply' => $reply]; } ?>这段代码里值得注意的参数有三个。CURLOPT_TIMEOUT设成了120秒,这个值很关键,如果Agent挂了复杂工作流或知识库检索,响应时间会明显变长,默认的30秒很容易超时。stream设为false是明确告诉Coze我们要完整的同步返回,不是流式分片,这样解析逻辑只用取data.answer一个字段就够了。max_history限制在20条是为了控制请求体大小,因为每次请求都要把历史重新传一遍,历史越长,接口响应越慢、Token消耗越高。
3.3 会话历史存储:文件存储的读写设计与并发安全
会话历史用文件存储,每个用户一个独立的json文件,文件名由userId加上.json后缀组成。loadHistory读取文件内容并解码成数组,saveHistory则把更新后的数组编码后写回文件。这里有一个并发的隐患:如果同一用户连续快速发送两条消息,可能出现两个PHP进程同时读写同一个文件,造成历史丢失。源码里用了一个简单的文件锁来避免这个问题。
<?php // 文件存储的读写封装 function saveHistory($userId, $userMsg, $botMsg) { $file = __DIR__ . '/data/' . md5($userId) . '.json'; $fp = fopen($file, 'c+'); if (flock($fp, LOCK_EX)) { // 独占锁,防止并发写串 $content = file_get_contents($file); $history = $content ? json_decode($content, true) : []; $history[] = ['role' => 'user', 'content' => $userMsg]; $history[] = ['role' => 'assistant', 'content' => $botMsg]; // 只保留最近MAX_HISTORY*2条(user+assistant成对算1条) if (count($history) > MAX_HISTORY * 2) { $history = array_slice($history, -MAX_HISTORY * 2); } ftruncate($fp, 0); fwrite($fp, json_encode($history)); fflush($fp); flock($fp, LOCK_UN); } fclose($fp); } ?>注意这里用md5对userId做哈希后再作为文件名,而不是直接拼接,这样能避免userId里的特殊字符破坏文件路径。数据目录data需要给PHP进程写权限,部署时如果发现页面能发消息但历史不生效,大概率是data目录权限没设对,后面避坑章节会再提到。这个文件存储方案在个人使用场景下完全够用,但如果用户量到了几百人同时在线,就要考虑改成MySQL或Redis了。
4. 从本地到公网:部署上线与微信内联调的全流程
4.1 服务器要求与基础环境配置
这份源码对服务器要求不高,一台最低配的云主机或者虚拟主机都能跑。PHP版本要求7.4以上,因为用到了箭头语法和Null合并运算符的简写。需要开启curl扩展和json扩展,这两个在常规PHP环境里默认就有。Nginx或Apache都可以,如果用的是宝塔面板这类集成环境,直接建一个PHP站点然后上传源码即可。
一个容易忽略的点是跨域问题。因为前端页面和后端接口在同一个域名下,所以不存在跨域。但如果你想把前端页面部署到不同的域名或端口(比如微信里打开的是a.com,接口在b.com),就需要在agent_api.php里加CORS响应头。这里默认不开启,保持简单。
4.2 HTTPS是硬门槛:微信webview的拦截机制
在微信里测试接入前,先把HTTPS搞定。微信内置浏览器对所有非HTTPS的页面都会显示“已停止访问”的拦截页,这不是你的代码问题,是平台策略问题。如果服务器上没有现成的SSL证书,可以去申请免费证书,或者用宝塔面板的一键申请功能。证书部署完成后,记得加一条HTTP到HTTPS的301跳转,这样即使有人输入http链接,也会被带到https版本。
还有一个细节:HTTPS证书链要完整。有些免费证书如果只部署了域名证书而没部署中间证书,PC浏览器可能不报错,但微信的webview校验更严格,会提示证书无效。检查方法很简单,浏览器打开页面后点地址栏的锁图标,看证书链是否完整。如果手机上打不开而电脑能打开,十有八九是这个问题。
4.3 域名与备案:微信内打开的“隐形门槛”
HTTPS之外,域名也要能正常访问。这里分两种情况:如果你的服务器在国内,域名必须完成ICP备案,否则微信内打开会直接提示“该页面无法访问”或跳转到备案拦截页。如果服务器在境外,可以不备案,但访问速度会慢一些,而且某些地区的网络环境可能不稳定。
域名和服务器都准备好后,把源码上传到站点根目录,访问https://你的域名/index.php,在手机浏览器里先测试一遍。这里建议先用系统浏览器测,因为系统浏览器的报错信息比微信webview更明确,能看到具体的网络错误和响应状态。确认系统浏览器能正常对话后,再用微信扫码打开同一个链接。
4.4 微信内联调:从报错排查到完整对话验证
微信内打开页面的首次测试,建议先在PC端微信里打开一遍,再在手机端测。PC端微信的webview调试相对方便,能右键查看元素。真正的手机端测试要重点观察三点:页面是否正常加载、输入框能否弹出键盘、消息发送后是否正常返回。如果发送后一直显示“正在思考”,大概率是HTTPS证书问题或后端PHP报错。这时可以去服务器上看PHP错误日志,路径通常在/var/log/php-fpm/或站点目录下的runtime日志里。
调试过程中,我习惯在agent_api.php的入口处加一行临时日志,把接收到的请求参数写到文件里,方便确认到底是前端没发出请求、还是后端请求Coze失败。这个临时日志上线后记得删掉,避免暴露Token等敏感信息。
5. 避坑与常见问题排查:五条真实踩过的坑
5.1 微信里页面一直在转圈,后端PHP报404
现象:微信内打开链接白屏或一直在加载,后台服务器日志看到请求路径为404。
原因:源码里的路由规则和Nginx的伪静态配置冲突。如果站点用的是Nginx,且配置了try_files规则,index.php可能没有被正确解析,请求直接落到了文件系统上。
解决:在Nginx站点配置里加一条location / { try_files $uri $uri/ /index.php?$query_string; },然后reload配置。如果是Apache,确认.htaccess文件存在且AllowOverride开启。这个问题在虚拟主机上不常见,但在自己配的Nginx服务器上很容易忽略。
5.2 对话正常但Agent“失忆”,每次回复都不带上下文
现象:Agent能回答每一轮问题,但完全不记得刚才聊过什么,每轮都像第一次对话。
原因:saveHistory的历史拼接逻辑没生效。常见情况是data目录不存在或没有写权限,导致历史文件写入失败,每次都读到一个空数组。
解决:确认data目录存在且PHP进程有写权限,执行chmod -R 755 data,如果还不行就chmod -R 777 data(仅限调试环境)。另一个原因是userId取得不对,如果每次请求userId都变化,那么历史文件也对应不同的用户,等于每次都是新会话。前端在页面加载时应该生成一个固定的userId存到localStorage,后续请求一直带同一个值。
5.3 长回复被截断或只显示一半
现象:Agent回复的内容比较长时,前端只显示了一部分,或者出现了奇怪的字符断点。
原因:后端用了同步模式,但PHP的curl响应缓冲区或前端的内存限制截断了内容。另外,Coze返回的文本里可能带换行和特殊字符,直接插入HTML时被浏览器吞掉了。
解决:后端确保不设置过小的内存限制,可以临时加一句ini_set('memory_limit', '256M')。前端渲染时,把回复里的换行符转换成<br>,并把<、>、&等字符做HTML转义,避免被解析成标签。这段处理代码虽然不起眼,但几乎每个接入Coze的人都会遇到。
5.4 并发访问时历史记录丢失
现象:用户快速连续发送多条消息,发现后面的回复引用了错误的前文,或者历史记录里出现只有user没有assistant的奇数条记录。
原因:文件存储没有锁保护,两个并发请求同时读取同一个历史文件,各自拼写后写回,后写的覆盖了先写的。
解决:使用前面3.3节里展示的flock文件锁。如果你改用了Redis存储,用Redis的INCR和EXPIRE配合做锁。一个更简单的做法是给每个用户的历史写入加一个“串行化”:只保留最后一次写入的结果,放弃即时一致性,在个人场景下完全可以接受。
5.5 Coze接口偶发超时,页面报“网络异常”
现象:对话偶尔正常,偶尔卡住十几秒后前端报错。服务器日志显示curl请求超时或HTTP 5xx。
原因:Coze的Agent如果挂载了知识库、插件或工作流,处理时间会波动。默认30秒超时时间不够,或者Coze那边发生了服务端限流。
解决:把CURLOPT_TIMEOUT提到120秒(甚至更长)。同时注意,Coze有并发频率限制,如果测试时狂点发送,会触发限流,返回429状态码。建议在前端做“发送后禁用按钮直到响应返回”的控制,避免同一瞬间并发多个请求。另外,Coze的额度用尽时也会报错,注意看返回的JSON里的错误信息。
提示:遇到超时不要先怀疑代码,先看Coze平台那边的调用记录,确定是请求没出去还是响应太慢,能省一半排查时间。
6. 进阶技巧与验证方法:让Agent接入从“能跑”到“好用”
整条链路跑通之后,剩下的都是体验层面的打磨。第一个值得做的是多轮对话的上下文压缩。现在MAX_HISTORY设的是20条,但Agent聊到长对话时,20条历史也能占不少Token,而且Coze的模型注意力会被无关信息分散。我一般会在历史记录里做“摘要前置”:每隔几轮把之前的对话用一句话总结放在最前面,再拼接最近的具体消息。Coze本身没有提供这个能力,但你可以自己调一次Agent让它总结,然后把总结结果存进历史文件,效果很明显。
第二个值得做的是用户身份的扩展。现在代码里userId默认是前端生成的一个随机字符串,但这意味着用户换设备就丢身份。如果是公司内部分享,建议改成企业微信的userId或者手机号作为标识。如果是对外服务,可以用微信的OAuth换取openid,但这一步需要公众号的支持,个人开发者可以先放一放。
第三个是对话体验优化。同步等待最大的问题是长回复期间用户没有反馈,可以把前端改成“打字机效果”——虽然后端还是同步返回,但前端拿到完整文本后逐字显示,至少让用户觉得AI在“写”而不是干等。我在生产环境里试过,这种方式比SSE流式实现简单得多,体验也够用。
验证这套接入是否稳定,我通常跑一个固定流程:连续发10组问题,每组包含一个2-3轮的多轮对话,观察是否有历史丢失、回复截断和超时。然后换一个弱网环境(手机开飞行模式再关数据)测一次,确认前端能给出明确的错误提示而不是卡死在“正在思考”。这套流程跑下来,基本可以放心交付了。
做接入这件事,最怕的就是“能通就行”的心态。一个Agent接入微信,涉及的前端兼容、HTTPS、会话存储、Coze接口特性,每一层都可能出问题。我曾经因为少配了一条Nginx规则,在微信里排查了整整一下午,最后发现只是伪静态没开。从那以后,我每次部署都强制走一遍部署清单:域名证书→伪静态→目录权限→PHP扩展→后端日志,确认无误再让微信出场。希望这一整套拆解,能帮你少走一些我走过的弯路。
如果你正准备把Coze Agent接到微信里,这份可运行源码是一个不错的起点——它把最繁琐的兼容性问题和通信逻辑都处理好了,你只需要填入自己的Bot ID和Token就能跑起来。希望帮到你。
本文还有配套的精品资源,点击获取