【ESP32接入国产大模型之Deepseek】
- 1. Deepseek大模型
- 1.1 了解Deepseek api
- 1.2 Http接口鉴权
- 1.3. 接口参数说明
- 1.3.1 请求体(request)参数
- 1.3.2 模型推理
- 2. 先决条件
- 2.1 环境配置
- 2.2 所需零件
- 3. 核心代码
- 3.1 源码分享
- 3.2 源码解析
- 3.3 连续对话
- 修改后的代码
- 代码说明
- 示例输出
- 注意事项
- 4. 上传验证
- 4.1 对话测试
- 4.2 报错
- 5. 总结
1. Deepseek大模型
ESP32接入国产大模型之Deepseek
首先声明没有恰饭广告,源代码已经匿名处理,制作细节非常完善,方便大家复刻才会提供快捷的相关链接跳转!!!😘😘😘
DeepSeek 是一个先进的人工智能平台,旨在提供强大的对话和探索功能。它已经发布了性能更强的版本,并且已经开源,用户可以在网页端、APP 和 API 上注册使用。DeepSeek V3 是其最新的超级模型,用户可以免费与之对话,体验其强大的功能。
DeepSeek 的目标是探索未知的领域,提供创新的解决方案和工具。用户可以通过官方推出的手机 App 快速访问更多工具和资源。这个平台适合那些希望利用先进 AI 技术来增强其工作和生活体验的用户。。本文将重点介绍如何通过ESP32S3接入国产大模型之Deepseek api。
【ESP32接入国产大模型之腾讯混元】
上一篇博客已经分享了
【ESP32接入国产大模型之豆包】
【ESP32接入国产大模型之星火】
【ESP32接入国产大模型之MiniMax】
【ESP32接入语言大模型之智谱清言】
【ESP32接入国产大模型之文心一言】
【ESP32接入语言大模型之通义千问】
【ESP32接入国产大模型之kimi】
【ESP32接入国产大模型之Deepseek】
下面是不标准测评,参考而已
模型 | 响应时间 | 内容质量 | 免费token次数 | 地址 |
---|---|---|---|---|
豆包 | 2s | 9分 | 50万 | https://www.volcengine.com/product/doubao |
讯飞星火 | 4s | 8分 | 1亿 | https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html |
MiniMax | 3s | 8分 | 500万 | https://www.minimaxi.com/ |
智谱清言 | 7s | 7分 | 300万 | https://open.bigmodel.cn/ |
文心一言 | 10s | 7分 | 500万 | https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Nlks5zkzu |
通义千问 | 8s | 7分 | 800万 | https://tongyi.aliyun.com/qianwen/ |
Kimi | 2s | 9分 | 50万 | https://platform.moonshot.cn/docs/guide/start-using-kimi-api |
混元 | 6s | 8分 | 50万 | https://cloud.tencent.com/document/product/1729/105701 |
Deepseek | 12s | 9分 | 50万 | https://cloud.tencent.com/document/product/1729/105701 |
这一次还是采用Platformio编程就会轻松许多开发。这样就可以把大模型装进口袋啦🤣🤣🤣
1.1 了解Deepseek api
为方便用户使用,我们提供了 原生 HTTP 来实现模型 API 的调用。
Deepseek api
1.2 Http接口鉴权
Deepseek API 兼容了 OpenAI 的接口规范,这意味着您可以直接使用 OpenAI 官方提供的 SDK 来调用混元大模型。您仅需要将 base_url 和 api_key 替换成混元的相关配置,不需要对应用做额外修改,即可无缝将您的应用切换到混元大模型。详见鉴权认证方式。
- apikey
进入 [API Key 管理]https://console.cloud.tencent.com/hunyuan/start)页面,在您有权限的项目下点击新建 API Key,即可生成长效 API Key。进入兼容OpenAI API KEY的创建页面,点击新建即可生成API KEY。
点击创建api key
API Key 签名鉴权方式要求在 HTTP 请求 header 中按如下方式添加 Authorization
header:
Authorization: Bearer $ARK_API_KEY
1.3. 接口参数说明
1.3.1 请求体(request)参数
curl https://api.deepseek.com/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <DeepSeek API Key>" \
-d '{
"model": "deepseek-chat",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
"stream": false
}'
注意
model选默认就好
1.3.2 模型推理
上下文拼接
在每一轮对话过程中,模型会输出思维链内容(reasoning_content)和最终回答(content)。在下一轮对话中,之前轮输出的思维链内容不会被拼接到上下文中,如下图所示:
2. 先决条件
在继续此项目之前,请确保检查以下先决条件。
我们将使用 Arduino IDE 对 ESP32/ESP8266 开发板进行编程,因此在继续本教程之前,请确保已在 Arduino IDE 中安装这些开发板。
2.1 环境配置
- Arduino IDE:下载并安装 Arduino IDE;
- ESP32 开发板库:在 Arduino IDE 中添加 ESP32 支持;
参考博客:【esp32c3配置arduino IDE教程】
为安装过程留出一些时间,具体时间可能因您的互联网连接而异。
2.2 所需零件
要学习本教程,您需要1个 ESP32 开发板或者ESP32C3,建议使用后者,笔者发现同样的代码后者可以轻松调用,ESP32不行(可能板子坏了)。
目前这是我使用的ESP32S3官方硬件👍👍👍(小小的身材有大大的力量)只需要35元加摄像头麦克风79元,后期我会整理相关专栏进行Arduino系统学习😘😘😘。有需要可以购买xiao开发板💕💕💕
-
SeeedXIAO ESP32S3 Sense硬件购买地址:https://s.click.taobao.com/lekazrt
-
ESP32-S3-CAM 核心开发板 N16R8 wifi蓝牙模块 OV2640摄像头硬件购买地址:https://s.click.taobao.com/1PTagos
3. 核心代码
3.1 源码分享
esp32S3 Arduino代码如下
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
// 替换为您的 WiFi 凭据
const char *ssid = "IQOO";
const char *password = "12345678";
// 替换为您的 DeepSeek API 密钥
const char* apiKey = "sk-ea62673968ca4a34a1a1f6a82e4";
// DeepSeek API 端点
const char* host = "api.deepseek.com";
const int httpsPort = 443;
// 创建 WiFiClientSecure 对象
WiFiClientSecure client;
// 设置超时时间 (单位:毫秒)
const unsigned long timeout = 10000;
// 函数声明
void connectToWiFi();
String askDeepSeek(String question);
void printResponse(String response);
void setup() {
Serial.begin(115200);
// 连接到 WiFi
connectToWiFi();
// 关闭证书鉴权
client.setInsecure();
Serial.println("初始化完成,请输入您的问题:");
}
void loop() {
// 检查串口是否有输入
if (Serial.available()) {
String question = Serial.readStringUntil('\n');
question.trim(); // 去除换行符和空格
if (question.length() > 0) {
Serial.println("正在向 DeepSeek 提问...");
String response = askDeepSeek(question);
printResponse(response);
Serial.println("\n请输入下一个问题:");
}
}
}
// 连接到 WiFi
void connectToWiFi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("正在连接到 WiFi...");
}
Serial.println("已连接到 WiFi");
}
// 向 DeepSeek 提问
String askDeepSeek(String question) {
String response = "";
// 连接到 DeepSeek API
if (!client.connect(host, httpsPort)) {
Serial.println("连接失败");
return "连接失败";
}
// 构建请求
String request = "POST /v1/chat/completions HTTP/1.1\r\n";
request += "Host: " + String(host) + "\r\n";
request += "Authorization: Bearer " + String(apiKey) + "\r\n";
request += "Content-Type: application/json\r\n";
request += "Connection: close\r\n";
// 构建请求体
DynamicJsonDocument doc(1024);
doc["model"] = "deepseek-chat";
doc["messages"][0]["role"] = "user";
doc["messages"][0]["content"] = question;
doc["stream"] = true;
String requestBody;
serializeJson(doc, requestBody);
request += "Content-Length: " + String(requestBody.length()) + "\r\n\r\n";
request += requestBody;
// 发送请求
client.print(request);
// 记录开始时间
unsigned long startTime = millis();
// 流式接收响应
while (client.connected()) {
// 检查超时
if (millis() - startTime > timeout) {
Serial.println("响应超时");
break;
}
// 读取数据
while (client.available()) {
String line = client.readStringUntil('\n');
if (line.startsWith("data: ")) {
String jsonData = line.substring(6);
DynamicJsonDocument doc(1024);
deserializeJson(doc, jsonData);
// 提取回复内容
if (doc.containsKey("choices")) {
String content = doc["choices"][0]["delta"]["content"];
response += content;
}
}
}
}
// 断开连接
client.stop();
return response;
}
// 打印回复内容
void printResponse(String response) {
Serial.println("DeepSeek 回复:");
Serial.println(response);
}
这段代码是一个基于Arduino平台(可能是ESP32或ESP8266)的示例,它通过WiFi连接到指定的无线网络,并利用HTTPClient库向智谱清言API发送POST请求以获取AI生成的回答。
3.2 源码解析
以下是详细的解释:
- 导入必要的库:
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
- 第一处修改定义Wi-Fi网络凭证:
// 1. Replace with your network credentials
const char *ssid = "IQOO";
const char *password = "12345678";
- 第二处修改定义要调用的APIkey:
// 2. Replace with your OpenAI API key
const char* apiKey = "sk-ea62673968ca4a34a1a1f6a82e4";
这段代码是一个用于ESP32微控制器的程序,旨在通过WiFi连接到DeepSeek API,并与之进行交互。以下是代码的主要功能概述:
-
WiFi连接:代码首先连接到指定的WiFi网络(
ssid
和password
)。如果连接丢失,它会尝试重新连接。 -
用户输入处理:通过串口监视器,用户可以输入问题或消息。这些输入被发送到DeepSeek API进行处理。
-
API请求:代码构建一个HTTP POST请求,包含用户输入的内容,并将其发送到DeepSeek API。请求使用JSON格式,并包含API密钥进行身份验证。
-
分块响应处理:由于API响应可能是分块的,代码实现了分块解析状态机来处理这些响应。它读取每个块的大小和数据,直到所有数据接收完毕。
-
响应解析:接收到的响应被解析为JSON格式,并提取出DeepSeek的回复内容。代码还提取并显示了使用的Token数量(输入、输出和总计)。
-
错误处理:代码包含了对连接失败、JSON解析错误和API错误的处理,确保在出现问题时能够提供有用的调试信息。
总体而言,这段代码展示了如何在ESP32上实现与远程API的交互,处理分块响应,并解析JSON数据。这对于需要与云服务进行通信的物联网(IoT)应用非常有用。
3.3 连续对话
为了实现上下文拼接功能,并让模型在每一轮对话中输出思维链内容,我们需要对代码进行以下改进:
-
维护对话历史:
- 使用一个数组或列表来存储用户和模型的对话内容。
- 每次对话时,将用户的问题和模型的回复添加到对话历史中。
-
拼接上下文:
- 在每次请求时,将对话历史作为上下文传递给 DeepSeek API。
-
提取思维链内容:
- 根据 DeepSeek API 的响应,提取模型输出的思维链内容(通常是一个字段,如
reasoning
或chain_of_thought
)。
- 根据 DeepSeek API 的响应,提取模型输出的思维链内容(通常是一个字段,如
以下是修改后的代码:
修改后的代码
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
// 替换为您的 WiFi 凭据
const char *ssid = "IQOO";
const char *password = "12345678";
// 替换为您的 DeepSeek API 密钥
const char* apiKey = "sk-ea62673968ca4a34a1a1f6a82e4";
// DeepSeek API 端点
const char* host = "api.deepseek.com";
const int httpsPort = 443;
// 创建 WiFiClientSecure 对象
WiFiClientSecure client;
// 设置超时时间 (单位:毫秒)
const unsigned long timeout = 10000;
// 对话历史
const int maxHistory = 10; // 最大对话轮次
String conversationHistory[maxHistory]; // 存储对话历史
int historyIndex = 0; // 当前对话历史索引
// 函数声明
void connectToWiFi();
String askDeepSeek(String question);
void printResponse(String response);
void addToHistory(String role, String content);
void printHistory();
void setup() {
Serial.begin(115200);
// 连接到 WiFi
connectToWiFi();
// 关闭证书鉴权
client.setInsecure();
Serial.println("初始化完成,请输入您的问题:");
}
void loop() {
// 检查串口是否有输入
if (Serial.available()) {
String question = Serial.readStringUntil('\n');
question.trim(); // 去除换行符和空格
if (question.length() > 0) {
// 将用户问题添加到对话历史
addToHistory("user", question);
Serial.println("正在向 DeepSeek 提问...");
String response = askDeepSeek(question);
printResponse(response);
// 将模型回复添加到对话历史
addToHistory("assistant", response);
// 打印当前对话历史
printHistory();
Serial.println("\n请输入下一个问题:");
}
}
}
// 连接到 WiFi
void connectToWiFi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("正在连接到 WiFi...");
}
Serial.println("已连接到 WiFi");
}
// 向 DeepSeek 提问
String askDeepSeek(String question) {
String response = "";
// 连接到 DeepSeek API
if (!client.connect(host, httpsPort)) {
Serial.println("连接失败");
return "连接失败";
}
// 构建请求
String request = "POST /v1/chat/completions HTTP/1.1\r\n";
request += "Host: " + String(host) + "\r\n";
request += "Authorization: Bearer " + String(apiKey) + "\r\n";
request += "Content-Type: application/json\r\n";
request += "Connection: close\r\n";
// 构建请求体
DynamicJsonDocument doc(1024);
doc["model"] = "deepseek-chat";
doc["stream"] = true;
// 添加对话历史
JsonArray messages = doc.createNestedArray("messages");
for (int i = 0; i < historyIndex; i++) {
JsonObject message = messages.createNestedObject();
message["role"] = i % 2 == 0 ? "user" : "assistant"; // 交替用户和助手角色
message["content"] = conversationHistory[i];
}
// 添加当前问题
JsonObject newMessage = messages.createNestedObject();
newMessage["role"] = "user";
newMessage["content"] = question;
String requestBody;
serializeJson(doc, requestBody);
request += "Content-Length: " + String(requestBody.length()) + "\r\n\r\n";
request += requestBody;
// 发送请求
client.print(request);
// 记录开始时间
unsigned long startTime = millis();
// 流式接收响应
while (client.connected()) {
// 检查超时
if (millis() - startTime > timeout) {
Serial.println("响应超时");
break;
}
// 读取数据
while (client.available()) {
String line = client.readStringUntil('\n');
if (line.startsWith("data: ")) {
String jsonData = line.substring(6);
DynamicJsonDocument doc(1024);
deserializeJson(doc, jsonData);
// 提取回复内容
if (doc.containsKey("choices")) {
String content = doc["choices"][0]["delta"]["content"];
response += content;
}
// 提取思维链内容(假设字段为 "reasoning")
if (doc.containsKey("choices") && doc["choices"][0].containsKey("delta") && doc["choices"][0]["delta"].containsKey("reasoning")) {
String reasoning = doc["choices"][0]["delta"]["reasoning"];
Serial.println("思维链: " + reasoning);
}
}
}
}
// 断开连接
client.stop();
return response;
}
// 打印回复内容
void printResponse(String response) {
Serial.println("DeepSeek 回复:");
Serial.println(response);
}
// 添加对话历史
void addToHistory(String role, String content) {
if (historyIndex < maxHistory) {
conversationHistory[historyIndex] = content;
historyIndex++;
} else {
// 如果历史记录已满,移除最早的记录
for (int i = 0; i < maxHistory - 1; i++) {
conversationHistory[i] = conversationHistory[i + 1];
}
conversationHistory[maxHistory - 1] = content;
}
}
// 打印对话历史
void printHistory() {
Serial.println("\n当前对话历史:");
for (int i = 0; i < historyIndex; i++) {
Serial.println((i % 2 == 0 ? "用户: " : "助手: ") + conversationHistory[i]);
}
}
代码说明
-
对话历史管理:
- 使用
conversationHistory
数组存储对话内容。 - 通过
addToHistory
函数将用户问题和模型回复添加到历史中。 - 如果历史记录已满,移除最早的记录以保持最大轮次。
- 使用
-
上下文拼接:
- 在每次请求时,将对话历史作为
messages
数组传递给 DeepSeek API。
- 在每次请求时,将对话历史作为
-
思维链提取:
- 假设 DeepSeek API 返回的思维链内容字段为
reasoning
,在流式接收时提取并打印。
- 假设 DeepSeek API 返回的思维链内容字段为
-
对话历史打印:
- 使用
printHistory
函数打印当前对话历史,方便调试和观察上下文。
- 使用
示例输出
初始化完成,请输入您的问题:
你好,DeepSeek!
正在向 DeepSeek 提问...
思维链: 用户打招呼,我需要回应问候。
DeepSeek 回复:
你好!我是 DeepSeek,很高兴为您服务。请问有什么可以帮您的?
当前对话历史:
用户: 你好,DeepSeek!
助手: 你好!我是 DeepSeek,很高兴为您服务。请问有什么可以帮您的?
请输入下一个问题:
注意事项
-
API 字段名称:
- 确保
reasoning
字段与 DeepSeek API 返回的思维链字段名称一致。如果不一致,请根据 API 文档修改。
- 确保
-
历史记录长度:
- 根据 ESP32 的内存限制,调整
maxHistory
的大小,避免内存不足。
- 根据 ESP32 的内存限制,调整
-
流式数据解析:
- 确保解析逻辑能够正确处理流式数据的分块传输。
希望这段代码能满足您的需求!如果有其他问题,请随时告诉我。
4. 上传验证
下面给出下载配置,请严格配置
platformio.ini文件
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32s3-cam]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
board_upload.flash_size = 16MB
board_build.partitions = default_16MB.csv
board_build.mcu = esp32s3
monitor_speed = 115200
upload_speed = 921600
lib_deps =
bblanchon/ArduinoJson@^7.3.0
4.1 对话测试
打开串口监视器,注意右下角选择回车符,选择115200波特率,输入你想问的问题,他就可以回答你
个人感觉混元比其他国内大模型响应有点快啦,大约10s返回,太爽啦!😘😘😘
4.2 报错
如果返回error ,大家对照列表查询错误代码,结合提示排查解决
5. 总结
🥳🥳🥳现在,我们在本教程中,您学习了如何使用ESP32接入语言大模型之Deepseek。🛹🛹🛹从而实现对外部世界进行感知,充分认识这个有机与无机的环境,后期会持续分享esp32跑freertos实用案列🥳🥳🥳科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣
如果你有任何问题,可以通过q group(945348278)加入鹏鹏小分队,期待与你思维的碰撞😘😘😘
鸣谢B站UP星汇极客
参考文献:ESP32对接DeepSeek API,实现AI大模型交互功能。