跳转到内容

RKLLM AI 模型部署指南

修改履历

NO版本修改内容修改日期
1Ver1.0新建2026/06/22

本文档信息可能会因文档改进而在未事先通知的情况下变更。最新版请参照本公司网站。

未经株式会社日昇科技书面许可,严禁以任何形式复制本文档。

1. AI模型部署

DeepSeek-R1,是幻方量化旗下AI公司深度求索(DeepSeek)研发的推理模型 。 DeepSeek-R1采用强化学习进行后训练,旨在提升推理能力,尤其擅长数学、代码和自然语言推 理等复杂任务。作为国产AI大数据模型的代表,凭借其卓越的推理能力和高效的文本生成技术, 在全球人工智能领域引发广泛关注。

本文主要说明DeepSeek-R1如何离线运行在EASY-EAI-Nano-TB(RV1126B)硬件上, RV1126B具有优异的端侧AI能效比与极高的性价比,是AI落地的不二之选。

注意,要求使用有2GB以上内存的EAI1126B-Core-T方可以正常运行。

图1 EAI1126B-Core-T正面

DeepSeek-R1 与主要模型的基准测试对比

图:DeepSeek-R1 与主要模型的基准测试对比

2. 快速上手

2.1. 准备工作

2.1.1. 硬件准备

需准备EASY EAI Nano-TB开发板,Type-C数据线、网线。可以基于MobaXterm的ssh远程 桌面登录调试。首先使用网线把EASY EAI Nano-TB的千兆以太网接口与连着路由LAN口的交换 机或者路由器的LAN口连接,如下图所示。 RV1126B 板卡的 LAN 连接拓扑

图:RV1126B 板卡的 LAN 连接拓扑

以及串口连接。 RV1126B 板卡的 Type-C 串口连接方式

图:RV1126B 板卡的 Type-C 串口连接方式

2.1.2. 开发环境准备

如果您初次阅读此文档,请阅读《入门指南/开发环境准备/Easy-Eai编译环境准备与更 新》,并按照其相关的操作,进行编译环境的部署。

在PC端Ubuntu系统中执行run脚本,进入EASY-EAI编译环境,具体如下所示。

Terminal window
cd ~/develop_environment
./run.sh

EASY-EAI 开发环境启动后的终端界面

图:EASY-EAI 开发环境启动后的终端界面

2.2. 源码下载以及例程编译

本节提供转换成功的大模型文件 deepseek_r1_rv1126b_w4a16.rkllm 以及对应的 C/C++ 程序部署代码,可通过以下两种方式下载。

图:AI 模型部署程序包的 Google Drive 文件夹

下载程序包移至ubuntu环境后,执行以下指令解压:

Terminal window
tar -xvf deepseek-demo.tar.bz2

下载解压后如下图所示: deepseek-demo 源码目录结构

图:deepseek-demo 源码目录结构

在EASY-EAI 编译环境下,进入到对应的例程目录执行编译操作,具体命令如下所示:

Terminal window
cd /opt/nfsroot/rknn-toolkit2/deepseek-demo
./build.sh

deepseek-demo 示例程序编译终端输出

图:deepseek-demo 示例程序编译终端输出

同时,把可执行程序目录deepseek-demo_release/复制到开发板/userdata目录上:

Terminal window
cp deepseek-demo_release/ /mnt/userdata/ -rf

而且把librkllmrt.so也同步到板子/usr/lib环境里面:

Terminal window
cp lib/librkllmrt.so /mnt/usr/lib

将可执行文件和 librkllmrt.so 复制到开发板的终端输出

图:将可执行文件和 librkllmrt.so 复制到开发板的终端输出

2.3. 开发板运行大模型

通过串口调试或ssh调试,进入板卡后台,定位到例程部署的位置,如下所示:

Terminal window
cd /userdata/deepseek-demo_release/

开发板上的 deepseek-demo_release 目录确认

图:开发板上的 deepseek-demo_release 目录确认

运行例程命令如下所示:

Terminal window
ulimit -HSn 102400
sudo ./deepseek-demo deepseek_r1_rv1126b_w4a16.rkllm 256 512

在开发板上启动 RKLLM Demo 的终端输出

图:在开发板上启动 RKLLM Demo 的终端输出

至此可以进行对话测试了,试着输入“0”测试预设的问题。回答如下所示: 开发板上 DeepSeek-R1 生成回答的终端输出

图:开发板上 DeepSeek-R1 生成回答的终端输出

3. RKLLM算法例程

例程目录为deepseek-demo/src/llm_demo.cpp,操作流程如下。 RKLLM Demo 程序的处理流程

图:RKLLM Demo 程序的处理流程

具体代码如下所示:

// Copyright (c) 2025 by Rockchip Electronics Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <unistd.h>
#include <string>
#include "rkllm.h"
#include <fstream>
#include <iostream>
#include <csignal>
#include <vector>
using namespace std;
LLMHandle llmHandle = nullptr;
void exit_handler(int signal)
{
if (llmHandle != nullptr)
{
{
cout << "程序即将退出" << endl;
LLMHandle _tmp = llmHandle;
llmHandle = nullptr;
rkllm_destroy(_tmp);
}
}
exit(signal);
}
int callback(RKLLMResult *result, void *userdata, LLMCallState state)
{
if (state == RKLLM_RUN_FINISH)
{
printf("\n");
} else if (state == RKLLM_RUN_ERROR) {
printf("\\run error\n");
} else if (state == RKLLM_RUN_NORMAL) {
/* ================================================================================================================
若使用GET_LAST_HIDDEN_LAYER功能,callback接口会回传内存指针:last_hidden_layer,token数量:num_tokens与隐藏层大小:embd_size
通过这三个参数可以取得last_hidden_layer中的数据
注:需要在当前callback中获取,若未及时获取,下一次callback会将该指针释放
===============================================================================================================*/
if (result->last_hidden_layer.embd_size != 0 && result->last_hidden_layer.num_tokens != 0) {
int data_size = result->last_hidden_layer.embd_size * result->last_hidden_layer.num_tokens * sizeof(float);
printf("\ndata_size:%d",data_size);
std::ofstream outFile("last_hidden_layer.bin", std::ios::binary);
if (outFile.is_open()) {
outFile.write(reinterpret_cast<const char*>(result->last_hidden_layer.hidden_states), data_size);
outFile.close();
std::cout << "Data saved to output.bin successfully!" << std::endl;
} else {
std::cerr << "Failed to open the file for writing!" << std::endl;
}
}
printf("%s", result->text);
}
return 0;
}
int main(int argc, char **argv)
{
if (argc < 4) {
std::cerr << "Usage: " << argv[0] << " model_path max_new_tokens max_context_len\n";
return 1;
}
signal(SIGINT, exit_handler);
printf("rkllm init start\n");
//设置参数及初始化
RKLLMParam param = rkllm_createDefaultParam();
param.model_path = argv[1];
//设置采样参数
param.top_k = 1;
param.top_p = 0.95;
param.temperature = 0.8;
param.repeat_penalty = 1.1;
param.frequency_penalty = 0.0;
param.presence_penalty = 0.0;
param.max_new_tokens = std::atoi(argv[2]);
param.max_context_len = std::atoi(argv[3]);
param.skip_special_token = true;
param.extend_param.base_domain_id = 0;
param.extend_param.embed_flash = 1;
int ret = rkllm_init(&llmHandle, &param, callback);
if (ret == 0){
printf("rkllm init success\n");
} else {
printf("rkllm init failed\n");
exit_handler(-1);
}
vector<string> pre_input;
pre_input.push_back("现有一笼子,里面有鸡和兔子若干只,数一数,共有头14个,腿38条,求鸡和兔子各有多少只?");
pre_input.push_back("有28位小朋友排成一行,从左边开始数第10位是学豆,从右边开始数他是第几位?");
cout << "\n**********************可输入以下问题对应序号获取回答/或自定义输入********************\n"
<< endl;
for (int i = 0; i < (int)pre_input.size(); i++)
{
cout << "[" << i << "] " << pre_input[i] << endl;
}
cout << "\n*************************************************************************\n"
<< endl;
RKLLMInput rkllm_input;
memset(&rkllm_input, 0, sizeof(RKLLMInput)); // 将所有内容初始化为 0
// 初始化 infer 参数结构体
RKLLMInferParam rkllm_infer_params;
memset(&rkllm_infer_params, 0, sizeof(RKLLMInferParam)); // 将所有内容初始化为 0
// 1. 初始化并设置 LoRA 参数(如果需要使用 LoRA)
// RKLLMLoraAdapter lora_adapter;
// memset(&lora_adapter, 0, sizeof(RKLLMLoraAdapter));
// lora_adapter.lora_adapter_path = "qwen0.5b_fp16_lora.rkllm";
// lora_adapter.lora_adapter_name = "test";
// lora_adapter.scale = 1.0;
// ret = rkllm_load_lora(llmHandle, &lora_adapter);
// if (ret != 0) {
// printf("\nload lora failed\n");
// }
// 加载第二个lora
// lora_adapter.lora_adapter_path = "Qwen2-0.5B-Instruct-all-rank8-F16-LoRA.gguf";
// lora_adapter.lora_adapter_name = "knowledge_old";
// lora_adapter.scale = 1.0;
// ret = rkllm_load_lora(llmHandle, &lora_adapter);
// if (ret != 0) {
// printf("\nload lora failed\n");
// }
// RKLLMLoraParam lora_params;
// lora_params.lora_adapter_name = "test"; // 指定用于推理的 lora 名称
// rkllm_infer_params.lora_params = &lora_params;
// 2. 初始化并设置 Prompt Cache 参数(如果需要使用 prompt cache)
// RKLLMPromptCacheParam prompt_cache_params;
// prompt_cache_params.save_prompt_cache = true; // 是否保存 prompt cache
// prompt_cache_params.prompt_cache_path = "./prompt_cache.bin"; // 若需要保存prompt cache, 指定 cache 文件路径
// rkllm_infer_params.prompt_cache_params = &prompt_cache_params;
// rkllm_load_prompt_cache(llmHandle, "./prompt_cache.bin"); // 加载缓存的cache
rkllm_infer_params.mode = RKLLM_INFER_GENERATE;
// By default, the chat operates in single-turn mode (no context retention)
// 0 means no history is retained, each query is independent
rkllm_infer_params.keep_history = 0;
//The model has a built-in chat template by default, which defines how prompts are formatted
//for conversation. Users can modify this template using this function to customize the
//system prompt, prefix, and postfix according to their needs.
// rkllm_set_chat_template(llmHandle, "", "<|User|>", "<|Assistant|>");
while (true)
{
std::string input_str;
printf("\n");
printf("user: ");
std::getline(std::cin, input_str);
if (input_str == "exit")
{
break;
}
if (input_str == "clear")
{
ret = rkllm_clear_kv_cache(llmHandle, 1, nullptr, nullptr);
if (ret != 0)
{
printf("clear kv cache failed!\n");
}
continue;
}
for (int i = 0; i < (int)pre_input.size(); i++)
{
if (input_str == to_string(i))
{
input_str = pre_input[i];
cout << input_str << endl;
}
}
rkllm_input.input_type = RKLLM_INPUT_PROMPT;
rkllm_input.role = "user";
rkllm_input.prompt_input = (char *)input_str.c_str();
printf("robot: ");
// 若要使用普通推理功能,则配置rkllm_infer_mode为RKLLM_INFER_GENERATE或不配置参数
rkllm_run(llmHandle, &rkllm_input, &rkllm_infer_params, NULL);
}
rkllm_destroy(llmHandle);
return 0;
}