说说【AI大模型接入SDK】项目的数据结构设计

今天翻到一篇不错的技术分享,看完之后自己也琢磨了一下,把思路梳理记录下来。

🎬 个人主页:艾莉丝努力练剑

❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学过程记录
Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:

文章目录

1.1.5 common.h 基础文件配置 1.2 需求分析:需要定义哪些数据结构 1.3 Token 概念、换算、计费规则 1.4 工程与数据结构设计思想完整复盘 附录:完整 common.h 全部整合代码 结尾

1 ~> AI‑Model‑Access‑Tech 大模型接入 SDK

1.1 工程前期准备干活

1.1.1 API‑Key 资源准备

  • 原始计划可用模型:DeepSeek、ChatGPT、Gemini
  • 实际替换情况
Gemini 存在反中访问限制,替换为通义千问
  • ChatGPT 无法完成充值,替换为 mino 模型
API‑Key 定位:连接大模型服务的身份凭证,是调用 API 的第一步;拿到密钥后阅读官方 API 文档,确认请求地址、请求体格式,即可发起模型调用。

1.1.2 Git 仓库与本地项目创建完整操作流程

操作终端:bit@bit08

# 1.进入will工作目录
cd will

# 2.创建项目根目录
mkdir ai-model-acess-tech
cd ai-model-acess-tech

# 3.拉取老师提供的远程码云仓库
git clone https://gitee.com/zhibite-edu/ai-model-acess-tech.git

  • git clone 执行输出日志
remote:Enumerating objects:4,done.
remote:Counting objects:100%(4/4),done.
remote:Compressing objects:100%(4/4),done.
Cloning into'ai-model-acess-tech'...
Receiving objects:100%(4/4),4.91 KiB|4.91 MiB/s,done.
  • 克隆完成后目录内包含 git 版本控制相关文件:.git文件夹、.gitignoreLICENSE
业务坑:.git版本控制元文件不希望出现在业务代码树中。 解决方式:在克隆得到的仓库目录内部,新建业务工程目录,所有业务代码放入该子目录。

# 进入clone下来的仓库目录
cd ai-model-acess-tech
# 创建业务项目文件夹
mkdir AIModelAcessTech
cd AIModelAcessTech

  • 用户 alice 项目路径:Alice/ai-model-acess/ai-model-acess/AIModeAcess
  • 个人码云仓库地址:艾莉丝努力练剑 /ai-model-acess,项目含义:SDK 接入 AI 大模型项目。

1.1.3 SDK 目录创建与工程结构

接入模型的业务代码最终编译成为静态库,静态库源代码放置在 SDK 目录。

AIModelAcessTech
└─ sdk
   ├─ include        # 对外头文件,编译安装时对外拷贝
   │  └─ common.h    # 公共结构体定义头文件
   └─ src            # cpp源文件,编译静态库,对外不发布

1.1.4 头文件源文件分离的设计目的

1. SDK 编译输出静态库,安装部署的时候,只拷贝静态库文件 + include 下的头文件,src 源文件不需要对外分发。
2. CMakeLists.txt 构建脚本编写更加方便,头文件统一集中管理。

1.1.5 common.h 基础文件配置

1. 文件路径:sdk/include/common.h
2. #pragma once:头文件保护,防止重复包含。
3. 使用命名空间 ai_chat_sdk,隔离本 SDK 全部类型,避免和外部项目符号冲突。

#pragma once
#include
#include

namespace ai_chat_sdk {

// 所有结构体写在此命名空间内部

} // end ai_chat_sdk

1.2 需求分析:需要定义哪些数据结构

业务场景:对接多家大模型(DeepSeek、mino、千问、Ollama 本地模型) 虽然各个厂商模型 API 细节不一样,但存在大量公共配置、公共业务对象。 需要管理的业务对象:

1. 模型调用配置:模型名称、temperature 温度、max_tokens、apikey、服务端点 base url
2. 聊天消息:角色 role、消息 content、消息 id、消息时间戳
3. 会话管理:每一轮对话是一个会话;会话绑定模型、保存历史消息列表、创建时间、更新时间。

这些结构体在 SDK 多个模块都会复用,统一放在 common.h 头文件。

1.2.1 Message 消息结构体

业务含义:保存单条对话消息,对应 LLM 接口 messages 数组内单条元素。 字段迭代演进过程

1. 第一轮最简版本:只保留_role_content
2. 迭代增加_messageId,用于消息管理、消息定位
3. 迭代增加_timestamp消息发送时间戳,需要引入``头文件
4. 构造函数:业务层只传入 role 和 content;messageId、timestamp 由 SDK 内部自动生成填充。

完整代码:

#pragma once
#include
#include
#include

namespace ai_chat_sdk {

/**
 * @brief 单条对话消息结构体
 */
struct Message
{
    std::string _messageId;   // 消息唯一ID
    std::string _role;        // 角色:user / assistant / system
    std::string _content;     // 消息文本内容
    std::time_t _timestamp;   // 消息发送时间戳

    /**
     * @brief 构造函数,业务层只提供角色和内容
     * @param role 消息角色
     * @param content 消息文本
     */
    Message(const std::string& role, const std::string& content)
        : _role(role), _content(content), _timestamp(std::time(nullptr))
    {}
};

} // end ai_chat_sdk

1.2.2 Config 模型调用基础配置结构体

核心思考:APIKey 不放入 Config 基类
  • 云端 API 模型(deepseek、千问、mino)需要 API‑Key
  • Ollama 本地部署模型,不需要 API‑Key。 因此基类只存放全部模型通用参数;云端特有参数使用子类继承扩展。
成员说明

1. _modelName:要调用的模型名字
2. _temperature:采样温度,默认 0.7,取值范围 0~2
数值越大,输出随机性越高,想象力天马行空;
3. 数值越小,输出严谨、确定性高。
4. 官方建议:不要同时修改 temperature 与 top_p 两个参数。 | 使用场景 | temperature 参考值 | | ---- | ---- | | 代码生成 / 数学解题 | 0.0 | | 数据抽取 / 数据分析 | 1.0 | | 通用对话 | 1.3 | | 翻译 | 1.3 | | 创意写作、诗歌创作 | 1.5 |

_maxTokens:单次请求最大输出 token 数量,默认 2048;受模型总上下文窗口限制。虚析构函数virtual ~Config() = default;:开启 RTTI 运行时类型识别,支持多态向下转型。

/**
 * @brief LLM基础配置基类,所有模型通用运行参数
 */
struct Config
{
    std::string _modelName;
    double _temperature = 0.7;
    int _maxTokens = 2048;

    // 虚析构,支持多态RTTI
    virtual ~Config() = default;
};

/**
 * @brief 云端API调用模型的配置,继承Config,增加apikey
 * @note Ollama本地模型不使用该结构体
 */
struct APIConfig : public Config
{
    std::string _apiKey; // 云端服务身份密钥
};

1.2.3 ModelInfo 模型元信息结构体

业务定位:用于前端模型选择界面,描述模型静态信息,不包含运行时调用参数。 字段:

1. _modelName:模型名称
2. _modelDesc:模型功能描述文本
3. _provider:模型厂商、提供者
4. _endpoint:API 服务 Base URL(前置 URL,接口根地址,如https://api.deepseek.com
5. _isAvailable:标记模型是否初始化就绪可用,默认 false。

构造函数:提供带默认参数的构造,方便实例化。

/**
 * @brief LLM模型元信息,用于展示模型列表信息
 */
struct ModelInfo
{
    std::string _modelName;
    std::string _modelDesc;
    std::string _provider;
    std::string _endpoint;
    bool _isAvailable = false;

    ModelInfo(const std::string& name,
              const std::string& desc = "",
              const std::string& provider = "",
              const std::string& endpoint = "")
        : _modelName(name),
          _modelDesc(desc),
          _provider(provider),
          _endpoint(endpoint),
          _isAvailable(false)
    {}
};

1.2.4 Session 会话结构体

业务含义:代表一次完整对话会话;一个会话绑定一个模型,内部维护多条历史消息,用于多轮对话。 字段说明

1. _sessionId:会话唯一 ID
2. _modelName:会话绑定的模型名称
3. _messagesstd::vector存储会话全部历史消息
4. _createdAt:会话创建时间戳,对象实例化时刻生成
5. _updatedAt:会话最终更新时间戳;每新增一条消息,就更新此字段,用于历史会话列表展示最近会话时间。

构造函数设计要点
  • 构造函数仅接收 modelName;
  • _sessionId不能在构造函数直接赋值,需要 SDK 业务层手动生成;
  • _createdAt对象创建时初始化;
  • _updatedAt初始化为创建时间,追加消息时手动刷新。
/**
 * @brief 会话结构体,保存一轮完整对话上下文
 */
struct Session
{
    std::string _sessionId;
    std::string _modelName;
    std::vector _messages;
    std::time_t _createdAt;
    std::time_t _updatedAt;

    Session(const std::string& modelName = "")
        : _modelName(modelName),
          _createdAt(std::time(nullptr)),
          _updatedAt(std::time(nullptr))
    {}
};

1.3 Token 概念、换算、计费规则

1.3.1 Token 基础概念

  • Token:大模型处理文本的最小单元,同时也是计费单元。
  • 直观理解:可以近似理解为 “词 / 字”,但不是严格一一对应。
  • 经验估算换算(不同模型分词器不一样,仅参考,不能作为精确计算依据)
英文字符:1 字符 ≈0.3 token
  • 中文字符:1 字符 ≈0.6 token
真实 token 消耗,以 API 返回 response 内usage字段为准。可以使用 tokenizer 工具做离线预计算。

1.3.2 DeepSeek 模型计费规则

1. 计费 =(输入 token 数量 + 输出 token 数量) × 对应单价;单位:百万 tokens。
2. 区分缓存命中输入、缓存未命中输入、输出三档价格。
3. 余额扣费顺序:优先扣赠送余额,赠送余额耗尽之后扣充值余额。
4. 模型上下文限制:deepseek‑chat 支持 128K 上下文窗口,输出存在最大长度限制。
5. 特殊说明:deepseek‑reasoner 开启 tools 函数调用时,底层实际降级为 deepseek‑chat 执行。

1.3.3 temperature 参数官方使用场景表

业务场景temperature 推荐取值代码生成 / 数学解题0.0数据抽取 / 分析1.0通用对话1.3翻译1.3创意类写作 / 诗歌创作1.5

注意:默认temperature=1.0,本项目结构体默认设置 0.7。

1.4 工程与数据结构设计思想完整复盘

1.4.1 继承设计

1. Config:基类,存放所有模型通用运行参数。
2. APIConfig : public Config:子类扩展云端模型特有 apiKey,适配 Ollama 无密钥场景,避免无效字段。

1.4.2 对象分层

1. ModelInfo:静态元数据层,模型描述、厂商、endpoint,用于 UI 展示,不参与调用时参数。
2. Config / APIConfig:运行调用参数层,发起请求时使用。
3. Message:消息最小单元。
4. Session:会话聚合层,聚合消息列表,维护会话时间,实现多轮对话上下文。

1.4.3 C++ 工程 SDK 设计要点

1. 头文件与源文件分离,编译静态库,交付产物:.a静态库 + .h头文件。
2. 使用命名空间隔离 SDK 全部类型,防止符号冲突。
3. 时间统一使用std::time_t标准库时间戳。
4. 基类提供虚析构,保证多态析构安全,开启 RTTI 支持运行时类型识别。

1.4.4 业务后续开发方向

定义完以上公共数据结构之后,下一步就可以开始编写不同模型的 API 请求封装逻辑,分别对接 DeepSeek、千问、mino、Ollama。

附录:完整 common.h 全部整合代码

#pragma once
#include
#include
#include

namespace ai_chat_sdk {

/**
 * @brief 单条对话消息结构体
 */
struct Message
{
    std::string _messageId;   // 消息唯一ID
    std::string _role;        // 角色:user / assistant / system
    std::string _content;     // 消息文本内容
    std::time_t _timestamp;   // 消息发送时间戳

    Message(const std::string& role, const std::string& content)
        : _role(role), _content(content), _timestamp(std::time(nullptr))
    {}
};

/**
 * @brief LLM基础配置基类,所有模型通用运行参数
 */
struct Config
{
    std::string _modelName;
    double _temperature = 0.7;
    int _maxTokens = 2048;

    virtual ~Config() = default;
};

/**
 * @brief 云端API调用模型的配置,继承Config,增加apikey
 * @note Ollama本地模型不使用该结构体
 */
struct APIConfig : public Config
{
    std::string _apiKey; // 云端服务身份密钥
};

/**
 * @brief LLM模型元信息,用于展示模型列表信息
 */
struct ModelInfo
{
    std::string _modelName;
    std::string _modelDesc;
    std::string _provider;
    std::string _endpoint;
    bool _isAvailable = false;

    ModelInfo(const std::string& name,
              const std::string& desc = "",
              const std::string& provider = "",
              const std::string& endpoint = "")
        : _modelName(name),
          _modelDesc(desc),
          _provider(provider),
          _endpoint(endpoint),
          _isAvailable(false)
    {}
};

/**
 * @brief 会话结构体,保存一轮完整对话上下文
 */
struct Session
{
    std::string _sessionId;
    std::string _modelName;
    std::vector _messages;
    std::time_t _createdAt;
    std::time_t _updatedAt;

    Session(const std::string& modelName = "")
        : _modelName(modelName),
          _createdAt(std::time(nullptr)),
          _updatedAt(std::time(nullptr))
    {}
};

} // end ai_chat_sdk


结尾

uu们,这里的东西到这里就全部结束了,艾莉丝在这里再次

艾莉丝努力练剑

C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主


👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。

❤️
【点赞】 让优质东西被更多人看见,让知识传递更有力量。


【收藏】 把核心知识点存好,在需要时随时查、随时用。

💬
【评论】 分享你的经验或疑问,评论区一起交流避坑!

不要忘记给博主“一键四连”哦!

“今日练剑达成!”

“技术之路难免有困惑,但同行的人会让前进更有方向。”

结语:希望对学Linux相关东西的uu有所帮助,不要忘记给博主“一键四连”哦!

往期回顾

【AI接入大模型SDK】Deepseek API + Apifox

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡
>
૮₍ ˶ ˊ ᴥ ˋ˶₎ა

以上就是这次整理的全部内容,希望对你有所启发。如果有不同见解,欢迎在评论区交流讨论。

评论 (0)

暂无评论