FEATURED · 精选文章

PHP接入DeepSeek R1:从API调用到多轮对话智能体实战

发布时间 / 2026/9/17 4:27:56
来源 / 创域科博编辑部
栏目 / 资讯中心
PHP接入DeepSeek R1:从API调用到多轮对话智能体实战 简介面向需要将大模型能力集成到PHP项目的开发者这是一份完整的DeepSeek R1接口对接演示方案既适合刚接触大模型API的初学者也方便有经验者快速移植。资源以实际可运行的PHP源码为主线清晰演示接口鉴权、请求封装与回复处理的完整链路并模仿微信PC端聊天界面在浏览器中即可展示类桌面端的交互效果。包内共30个文件核心为带详细注解的PHP接口文件与两个HTML演示页面另有25张PNG界面效果图供UI还原时对照以及两个网址快捷方式分别指向在线演示与安装说明所有素材压缩后仅5.56MB内容紧凑、目录直观。该资源已有218人浏览学习配套演示链接可直接体验对话效果下载后按说明解压部署即可完整复现并方便替换成本地接口地址进行二次开发。1. 用 PHP 接 DeepSeek R1先搞清楚它和普通聊天模型差在哪很多人在第一步就卡住照搬 ChatGPT 的 PHP 调用代码把 model 换成 DeepSeek R1结果发现响应里多了一个从未见过的字段回答还带着一段很长很长的“思考过程”。这不是 Bug而是 DeepSeek R1 作为推理模型的正常表现。它在给你答案之前会先做一个内部推理这部分内容通过reasoning_content单独返回和真正的回答content是分开的。这意味着 PHP 接入 R1 的核心工作不只是发一个 HTTP 请求而是要把“思考过程”和“最终回答”做正确的解析、展示和上下文管理否则多轮对话时会把推理内容一起回传token 消耗翻倍回答还可能上下文错乱。这篇文章从最小可运行的 PHP 请求写起逐步封装成带思考展示、支持多轮对话的智能体 DEMO最后给出流式输出和排错方法适合正在用 PHP 给老项目加 AI 能力的人也适合刚接触接口对接、想快速做出一个能演示的智能体程序的开发者。2. 跑通 DeepSeek R1 接口PHP 最小请求与鉴权要点2.1 DeepSeek R1 的 API 走 OpenAI 兼容格式base_url 和 model 的值别搞错DeepSeek R1 对外提供的接口是 OpenAI Chat Completions 兼容格式这意味着你用 PHP 的 cURL 直接 POST JSON 就能调通不需要装任何厂商专属 SDK。常用的请求地址是https://api.deepseek.com/chat/completions而模型标识这里有个容易被忽略的细节满血版 R1 对应的值是deepseek-reasoner不是deepseek-chat。deepseek-chat指向的是 DeepSeek-V3 系列响应里没有reasoning_content参数行为也不一样。所以接入标题里说的“满血版 R1”第一件事就是确认model参数写的是deepseek-reasoner。鉴权采用Authorization: Bearer API Key的方式API Key 需要从 DeepSeek 开放平台的账户后台创建。Key 本身是一串类似sk-开头的字符串PHP 端要把它放在请求头里不要放在 POST 正文中。调试阶段建议把 Key 写在环境变量或单独的配置文件中不要硬编码在源码里后面第 4 章会专门讲这一点。2.2 用 cURL 写最小请求三行核心配置的事PHP 环境里只要开启了curl扩展宝塔面板默认开启phpstudy 也是默认开启就可以不用任何第三方库直接发请求。下面这段代码是一个真正能跑通的最小示例建议存成minimal.php直接命令行执行?php $apiKey getenv(DEEPSEEK_API_KEY); // 从环境变量读取避免密钥写死在代码里 $payload [ model deepseek-reasoner, // 满血版 R1 的模型标识 messages [ [role user, content 用一句话介绍你自己] ], max_tokens 1024, // 限制推理 回答的总长度 stream false // 先关掉流式方便看完整返回结构 ]; $ch curl_init(https://api.deepseek.com/chat/completions); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER true, CURLOPT_POST true, CURLOPT_HTTPHEADER [ Content-Type: application/json, Authorization: Bearer . $apiKey ], CURLOPT_POSTFIELDS json_encode($payload), CURLOPT_TIMEOUT 120 // 推理模型思考时间长超时给足 ]); $response curl_exec($ch); $status curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status ! 200) { exit(HTTP {$status} 请求失败\n); } $data json_decode($response, true); echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);执行方式export DEEPSEEK_API_KEY你的key php minimal.php代码里有几个值值得解释。CURLOPT_TIMEOUT设成 120 秒是因为 R1 做复杂推理时从请求发出到第一个字节返回可能比普通模型慢很多用默认的 30 秒会经常超时。max_tokens控制的是“推理内容 回答内容”的总长度不是只算回答设太小会出现回答被截断的情况这个后面参数部分会细讲。stream先设为false让接口一次性返回完整 JSON方便确认响应结构。2.3 先看响应结构reasoning_content 和 content 是两条不同的内容正常情况下你会看到响应的choices[0].message里有三个关键字段role固定为assistantcontent是给用户看的最终回答reasoning_content是模型内部的思考过程。对 R1 来说reasoning_content往往比content长得多尤其在数学、逻辑推理、代码生成的场景下。如果你只关心最终答案取content即可但如果你在做的是智能体 DEMO把reasoning_content单独展示出来产品体验会好很多用户能看到模型在一步步分析问题而不是干等一个结果。多轮对话时还有一个容易踩的坑reasoning_content是 R1 特有的字段下一轮请求时不要把上一轮的reasoning_content拼进messages数组。API 对用户传入的历史消息只认role和content如果你把推理内容塞回去可能触发参数校验错误而且会成倍增加 token 消耗。正确做法是只保留每轮的content作为历史reasoning_content仅用于展示。3. 调深参数与上下文管理从单轮到多轮智能体对话3.1 温度、top_p 这些参数对 R1 的影响和普通模型不一样DeepSeek R1 是推理优先的模型它的回答风格和 GPT 系列不太一样参数调节上也有自己的边界。temperature这个参数在 R1 上官方建议设置为 0.6 附近但实际效果不会像普通对话模型那样“调高变活泼、调低变严谨”因为推理模型内部有一套独立的采样逻辑。我的经验是保持默认值或 0.6~0.7 即可不要在温度上花太多时间调优。top_p也是同理官方文档建议直接不调用默认值。还有一点要注意R1 官方明确建议不要和temperature同时调整top_p这类参数组合容易造成输出质量飘忽不定。真正要花心思的是max_tokens。因为响应里包含推理过程和最终回答两部分max_tokens设小了经常会出现推理过程完整但回答被腰斩的情况。比如你只给了 512R1 思考一个复杂问题可能推理就要占掉 400 多最后只剩几十个 token 给回答结果就是答案莫名其妙断在半句。我一般按任务的复杂程度分档简单问答给 1024代码生成和逻辑推理给 2048长文分析给 4096 甚至更高。注意这只是上限实际消耗以响应的usage字段为准不会每次都打满。frequency_penalty和presence_penalty这类参数在 R1 上可用但效果不显著初期不建议设置。以下是几个常用参数的推荐值可以直接作为配置模板参数推荐值说明modeldeepseek-reasoner满血版 R1不要用deepseek-chattemperature0.6R1 对温度不敏感改大改小意义有限max_tokens2048包含推理和回答复杂任务提高到 4096stream视场景聊天交互设true脚本批处理设falsetop_p默认不修改避免与温度参数互相干扰3.2 多轮对话的 messages 拼装维护数组、滑动窗口与超长截断智能体和普通单次 API 调用的核心区别在于多轮记忆。DeepSeek R1 的接口本身是无状态的它不记得任何历史对话每一次请求都要在messages参数里把之前的对话完整带上。所以 PHP 端真正的工作就是维护这个messages数组并把它按正确的顺序、正确的角色拼装出来。最基本的拼装规则是一个用户消息一个助手回复交替排列。系统提示词放在最前面角色是system。下面是一个可以在 PHP CLI 环境下直接跑的多轮对话循环它把 messages 数组维护在内存中每次把用户输入追加进去请求接口后把助手回复也追加进去?php $apiKey getenv(DEEPSEEK_API_KEY); $messages [ [role system, content 你是一个严谨的工程师助手回答保持简洁。] ]; while (true) { echo 你: ; $input trim(fgets(STDIN)); if ($input exit) break; if ($input ) continue; $messages[] [role user, content $input]; $ch curl_init(https://api.deepseek.com/chat/completions); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER true, CURLOPT_POST true, CURLOPT_HTTPHEADER [ Content-Type: application/json, Authorization: Bearer . $apiKey ], CURLOPT_POSTFIELDS json_encode([ model deepseek-reasoner, messages $messages, max_tokens 2048 ]), CURLOPT_TIMEOUT 120 ]); $response curl_exec($ch); curl_close($ch); $data json_decode($response, true); $reply $data[choices][0][message][content] ?? (无输出); echo AI: {$reply}\n\n; $messages[] [role assistant, content $reply]; }这段代码的关键在于每次循环结束时把$reply只取content不含reasoning_content追加到$messages这样下一轮用户提问时接口能拿到完整的上下文。实际生产环境里还会遇到两个问题一是对话轮次多了messages 数组越来越大token 消耗线性增长二是超长之后 API 会返回 400 错误提示超过上下文长度。解决办法是做一个滑动窗口只保留最近 N 轮对话。常见做法是保留系统提示词 最近 10 轮对话超出部分直接丢弃。另一个做法是按字符数估算 token 数超过阈值时从最早的对话开始剔除直到总长回到安全范围。3.3 上下文超限报错遇到 maximum context length 后怎么压缩DeepSeek R1 的上下文窗口比较大但也不是无限的。当你的 messages 总长度超过限制时接口会返回类似This models maximum context length is ... tokens的 400 错误。这个报错在长对话和粘贴大段文本时非常常见处理方式不是盲目调大max_tokens而是要压缩输入。在 PHP 里做压缩性价比最高的方案是直接截断消息数组function trimMessages(array $messages, int $maxRounds 10): array { $system []; if (isset($messages[0]) $messages[0][role] system) { $system[] array_shift($messages); } // 保留最近 $maxRounds 轮一用户一助手算一轮 $messages array_slice($messages, -$maxRounds * 2); return array_merge($system, $messages); }注意array_slice取负偏移量是保留数组尾部元素正好对应最近的对话。如果截断后仍然超长就说明单轮输入本身就很大这时需要对用户输入做预处理比如去掉多余换行、压缩连续空格或者先调用一次 API 让 R1 对长文本做摘要再用摘要作为本轮输入。这里有一种值得推荐的折中做法把长文档拆成多段每段单独做向量化或摘要后拼接但这就超出本文 DEMO 的范畴了先记着“截断优先、摘要兜底”的原则即可。4. 完整智能体 DEMOPHP 封装 DeepSeek R1 客户端类与前端交互4.1 目录结构单入口单类放宝塔或 phpstudy 就能跑一个可以拿去用的智能体 DEMO不需要引入 composer 依赖也不需要框架纯 PHP 文件加一个前端页面就能跑。我建议的目录结构是这样的r1-demo/ ├── config.php # 存放 API Key 和默认参数 ├── DeepSeekR1Client.php # 封装的客户端类 ├── chat.php # 后端 API 入口接收前端请求 └── index.html # 聊天界面纯静态页面环境要求PHP 7.4 以上开启curl和json扩展。宝塔面板只需在 PHP 设置里确认 curl 扩展已启用phpstudy 同样默认开启。这个结构的好处是前后端分离DeepSeekR1Client.php可以单独拿到其他项目里复用chat.php负责处理 HTTP 请求和跨域。下面按文件逐个实现。4.2 DeepSeekR1Client 核心类封装请求、解析、多轮记忆?php class DeepSeekR1Client { private string $apiKey; private array $messages []; // 多轮对话记忆 public function __construct(string $apiKey, ?array $systemPrompt null) { $this-apiKey $apiKey; if ($systemPrompt) { $this-messages[] [role system, content $systemPrompt]; } } public function addMessage(string $role, string $content): void { $this-messages[] [role $role, content $content]; } public function chat(int $maxTokens 2048): array { $payload [ model deepseek-reasoner, messages $this-messages, max_tokens $maxTokens, stream false ]; $ch curl_init(https://api.deepseek.com/chat/completions); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER true, CURLOPT_POST true, CURLOPT_HTTPHEADER [ Content-Type: application/json, Authorization: Bearer . $this-apiKey ], CURLOPT_POSTFIELDS json_encode($payload), CURLOPT_TIMEOUT 120 ]); $response curl_exec($ch); $status curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status ! 200) { throw new RuntimeException(API 请求失败HTTP {$status}: . substr($response, 0, 500)); } $data json_decode($response, true); $message $data[choices][0][message]; // 只把最终回答追加进历史reasoning_content 仅返回给调用方 $this-messages[] [ role assistant, content $message[content] ]; return [ answer $message[content], thinking $message[reasoning_content] ?? ]; } }这个类把上文的几个要点都收进去了messages数组作为对话记忆长期保存在实例中每次chat()调用后自动把助手回复追加进去外部拿到的返回值里answer是最终回答thinking是推理过程。注意$this-messages[] [role assistant, ...]这一行的位置它必须在请求成功之后执行如果请求失败了不应该把任何内容记入历史否则下一轮对话会出现角色错乱。RuntimeException抛出的信息里带了响应 body 的前 500 个字符方便定位 400、401 这类错误的具体原因。4.3 后端入口与前端页面fetch 提交JSON 返回chat.php负责接收前端 POST 过来的消息调用客户端类然后把结果以 JSON 格式返回。这里有一个安全要点API Key 绝对不能出现在前端页面上所有请求必须由后端代理转发。?php require config.php; require DeepSeekR1Client.php; header(Content-Type: application/json; charsetutf-8); $input json_decode(file_get_contents(php://input), true); $userMessage trim($input[message] ?? ); if ($userMessage ) { http_response_code(400); echo json_encode([error 消息不能为空]); exit; } session_start(); // 用 session 保存客户端实例实现同一用户的多轮记忆 if (!isset($_SESSION[r1_client])) { $_SESSION[r1_client] new DeepSeekR1Client(DEEPSEEK_API_KEY); } $client $_SESSION[r1_client]; try { $result $client-chat(); echo json_encode([answer $result[answer], thinking $result[thinking]]); } catch (Exception $e) { http_response_code(500); echo json_encode([error $e-getMessage()]); }前端index.html用最简单的 fetch 提交不引入框架async function sendMessage() { const input document.getElementById(message); const msg input.value.trim(); if (!msg) return; input.value ; const res await fetch(chat.php, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ message: msg }) }); const data await res.json(); const thinking document.createElement(div); thinking.textContent 思考: data.thinking; const answer document.createElement(div); answer.textContent 回答: data.answer; document.getElementById(chatbox).appendChild(thinking, answer); }这里session_start()的使用很关键。PHP 的 session 默认按 Cookie 区分用户同一个浏览器刷新页面后$_SESSION[r1_client]里保存的 messages 历史还在对话上下文不会丢。但要注意 PHP session 有锁机制并发请求时会排队对 DEMO 来说完全够用如果做高并发生产系统建议把对话历史存到 Redis 或数据库而不是内存化的 session。4.4 密钥管理与常见误用为什么不能把 Key 放前端第四节的 DEMO 用config.php存 Key这是底线。有些初学者会直接把 Key 写成前端 JavaScript 的常量这等于把密钥公开给所有访问页面的人爬虫一扫就能从源码里提取然后盗刷你的额度。正确做法是 Key 只出现在 PHP 端通过后端转发请求。config.php的写法建议如下?php // 优先读环境变量本地跑的时候再往这个文件写默认值 define(DEEPSEEK_API_KEY, getenv(DEEPSEEK_API_KEY) ?: 这里填你的key);如果项目要提交到 Git记得把这一行里的真实 Key 删掉改成环境变量读取然后提交一份config.example.php。另外一个容易踩的坑是stream字段当前端页面直接调用chat.php时整个请求是等 R1 推理完成后才返回的用户体验是页面卡住几秒到十几秒。下一章会讲如何用流式输出让思考过程逐步显示出来这才接近智能体产品该有的交互形态。5. 进阶SSE 流式输出与排错验证三板斧5.1 用流式输出让 R1 的思考过程“动起来”把stream设为true后DeepSeek R1 的响应会变成 Server-Sent Events 格式一行以data:开头JSON 片段逐个到达。PHP 端需要改变处理方式从一次性curl_exec改成边接收边解析。这里给一个核心片段$payload[stream] true; $ch curl_init(https://api.deepseek.com/chat/completions); curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $chunk) { $buffer . $chunk; while (($pos strpos($buffer, \n)) ! false) { $line trim(substr($buffer, 0, $pos)); $buffer substr($buffer, $pos 1); if (str_starts_with($line, data:)) { $json json_decode(substr($line, 5), true); if (isset($json[choices][0][delta][reasoning_content])) { echo 思考中... . $json[choices][0][delta][reasoning_content]; ob_flush(); flush(); } } } return strlen($chunk); });前端接收流式数据时用fetch的response.body.getReader()读取逐步把content和reasoning_content渲染到页面上。注意ob_flush()和flush()要配合使用并且 PHP 的output_buffering如果开着需要先关闭它否则数据会积压在缓冲区里用户看到的效果和一次性返回没区别。5.2 三个必查日志位状态码、响应体、PHP 错误日志接入过程中大约 80% 的问题都集中在这三个位置。第一个是 HTTP 状态码401 代表 API Key 错误或过期402 代表账户余额不足429 代表请求频率超限。第二个是响应体中的error字段400 错误的具体原因都在里面比如上文的上下文超长、消息格式非法。第三个是 PHP 侧的 error_logcURL 报Could not resolve host或Failed to connect时先检查 php.ini 里curl.cainfo是否配置了 CA 证书很多 PHP 环境默认没配证书导致 HTTPS 请求直接失败。这种情况下可以把CURLOPT_SSL_VERIFYPEER临时设为false验证连通性但生产环境一定要配好证书。5.3 验证 DEMO 可用的两条命令命令行直接验证是排查问题最快的方式。先验证接口连通性和 Key 有效性curl -s https://api.deepseek.com/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer 你的key \ -d {model:deepseek-reasoner,messages:[{role:user,content:11?}],max_tokens:100} | jq .choices[0].message返回里有content且没有error字段说明接口和鉴权都正常。第二条验证 PHP 侧封装是否正常直接回到第 2 章的minimal.php把model改回deepseek-reasoner命令行执行时如果看到reasoning_content非空说明整条链路已经打通接下来可以放心去改业务逻辑了。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻