llm-cpp

#include "llm_*.hpp" · C++17 · MIT

给 C++ 加上大模型功能,一次一个 .hpp。

26 个单头文件库,涵盖流式输出、重试、缓存、费用估算、RAG、重排序、链路追踪、结构化输出和 Agent。把需要的文件复制进项目即可。不需要 SDK,不需要包管理器,也不需要框架。

26
个头文件
14
只需
标准库
12
只需
libcurl
210-572
每个头文件
的行数
llm_stream.hppllm_cost.hppllm_guard.hpp
llm_cache.hppC++17共 210 行
x64 Native Tools
C:\demo> curl -fsSLO https://gitlab.com/mattbusel/llm-cache/-/raw/main/include/llm_cache.hpp
C:\demo> cl /nologo /std:c++17 /EHsc cache.cpp && cache.exe
cache.cpp
What is RAII?            -> answer #1
what is raii?            -> answer #1
Explain move semantics   -> answer #2
What is SFINAE?          -> answer #3
What is RAII?            -> answer #4

api calls 4 | hits 1 | misses 4 | evictions 2
01

一个文件就是全部安装。

每个库都有自己的仓库,但你的项目只需要其中的 include/llm_<name>.hpp。在任何地方 include 它即可获得声明;只在一个 .cpp 里定义 LLM_<NAME>_IMPLEMENTATION,就会编译出实现代码。

gitlab.com/mattbusel/llm-cache仓库

  • examples/
  • └─ include/
  •    └─ llm_cache.hpp库本体
  • CMakeLists.txt
  • README.md
  • LICENSE

your-project/你的项目

  • src/
  • ├─ main.cpp
  • └─ llm_impl_cache.cpp // #define ..._IMPLEMENTATION
  • third_party/
  • └─ llm_cache.hpp
  • CMakeLists.txt // 无需改动

每个头文件都按实际行数等比例绘制。最大的一个是 572 行,全部 26 个加起来也只有 8,948 行,所以出问题时,你打开的是一个坐下来就能读完的文件。

仅标准库使用 libcurl
  1. 572format
  2. 537parse
  3. 481stream
  4. 469finetune
  5. 441json
  6. 388audio
  7. 385agent
  8. 379embed
  9. 362rag
  10. 362batch
  11. 357cost
  12. 351chat
  13. 337vision
  14. 334ab
  15. 315eval
  16. 313guard
  17. 309pool
  18. 302rank
  19. 290compress
  20. 267retry
  21. 261log
  22. 248trace
  23. 248mock
  24. 219router
  25. 211template
  26. 210cache
02

库目录。

“无”表示完全离线,只用标准库。“libcurl”表示实现代码会通过 HTTPS 调用 OpenAI 和/或 Anthropic。勾选你想要的库,安装部分会替你写好命令。

// 我想要……

llm_stream.hpp481 行

llm-stream

通过 SSE 逐 token 流式接收 OpenAI 和 Anthropic 的聊天回复

依赖
libcurl
分组
核心
#define LLM_STREAM_IMPLEMENTATION
llm_retry.hpp267 行

llm-retry

带抖动的指数退避、服务商故障切换和熔断器

依赖
无
分组
核心
#define LLM_RETRY_IMPLEMENTATION
llm_cost.hpp357 行

llm-cost

内置 OpenAI 和 Anthropic 模型的近似 Token 计数与费用估算,外加预算检查

依赖
无
分组
核心
#define LLM_COST_IMPLEMENTATION
运行结果
llm_cache.hpp210 行

llm-cache

带 TTL 和命中/未命中统计的 LRU 响应缓存,相同的提示词不再调用 API

依赖
无
分组
核心
#define LLM_CACHE_IMPLEMENTATION
运行结果
llm_format.hpp572 行

llm-format

定义 schema,用它校验模型返回的 JSON,不符合就重新提示,直到输出合规

依赖
无
分组
核心
#define LLM_FORMAT_IMPLEMENTATION
运行结果
llm_json.hpp441 行

llm-json

小巧的 JSON 解析与构建器,用于请求体和模型输出

依赖
无
分组
核心
#define LLM_JSON_IMPLEMENTATION
运行结果
llm_parse.hpp537 行

llm-parse

去除 HTML 和 Markdown 标记,提取标题、链接、小标题和代码块,并对文本分块

依赖
无
分组
数据
#define LLM_PARSE_IMPLEMENTATION
llm_embed.hpp379 行

llm-embed

OpenAI 向量嵌入(Embedding)、余弦/点积/欧氏距离相似度,以及一个小型磁盘向量库

依赖
libcurl
分组
数据
#define LLM_EMBED_IMPLEMENTATION
llm_rag.hpp362 行

llm-rag

端到端 RAG:分块、嵌入、持久化索引、检索 top-k 并作答

依赖
libcurl
分组
数据
#define LLM_RAG_IMPLEMENTATION
llm_rank.hpp302 行

llm-rank

用离线 BM25、LLM 相关性打分或两者混合的方式对段落重排序

libcurl(需要链接;BM25 本身离线运行)

依赖
libcurl
分组
数据
#define LLM_RANK_IMPLEMENTATION
llm_compress.hpp290 行

llm-compress

压缩对话历史:头部/尾部/智能截断、滑动窗口、LLM 摘要

无(只有启用 LLM_COMPRESS_SUMMARIZE 时才需要 libcurl)

依赖
无
分组
数据
#define LLM_COMPRESS_IMPLEMENTATION
运行结果
llm_batch.hpp362 行

llm-batch

用线程池批量跑完一个 JSONL 提示词文件,支持限流和可续跑的检查点

依赖
libcurl
分组
数据
#define LLM_BATCH_IMPLEMENTATION
llm_log.hpp261 行

llm-log

把每次调用记录为结构化 JSONL 日志,含延迟、token 数和费用,并支持查询和汇总

依赖
无
分组
运维
#define LLM_LOG_IMPLEMENTATION
llm_trace.hpp248 行

llm-trace

支持父子嵌套的 RAII span,带 token 和费用属性,可导出 OTLP 风格的 JSON

依赖
无
分组
运维
#define LLM_TRACE_IMPLEMENTATION
llm_pool.hpp309 行

llm-pool

带优先级队列的工作线程池,支持每分钟请求数和每分钟 token 数限制

依赖
无
分组
运维
#define LLM_POOL_IMPLEMENTATION
llm_mock.hpp248 行

llm-mock

模拟 LLM:可按脚本、模式匹配、随机或回显方式返回,并模拟延迟和流式输出

依赖
无
分组
运维
#define LLM_MOCK_IMPLEMENTATION
llm_eval.hpp315 行

llm-eval

把一个提示词跑 N 次,衡量一致性,对比模型或提示词,并给回复打分

依赖
libcurl
分组
运维
#define LLM_EVAL_IMPLEMENTATION
llm_ab.hpp334 行

llm-ab

用 Welch's t 检验、Cohen's d 和自定义评分器对提示词或模型做 A/B 测试

依赖
libcurl
分组
运维
#define LLM_AB_IMPLEMENTATION
llm_chat.hpp351 行

llm-chat

多轮对话,按 token 预算裁剪,固定系统提示词,可保存和恢复

依赖
libcurl
分组
应用
#define LLM_CHAT_IMPLEMENTATION
llm_agent.hpp385 行

llm-agent

工具调用 Agent 循环:把 C++ lambda 注册为工具,让模型来调用

依赖
libcurl
分组
应用
#define LLM_AGENT_IMPLEMENTATION
llm_vision.hpp337 行

llm-vision

把图片(文件或 URL)连同提示词发送给 OpenAI 或 Anthropic 的视觉模型

依赖
libcurl
分组
应用
#define LLM_VISION_IMPLEMENTATION
llm_template.hpp211 行

llm-template

Mustache 风格的提示词模板,支持循环、条件和按 token 预算截断

依赖
无
分组
应用
#define LLM_TEMPLATE_IMPLEMENTATION
llm_router.hpp219 行

llm-router

根据复杂度评分,以及成本、延迟、质量或预算策略,为每个提示词挑选模型

依赖
无
分组
应用
#define LLM_ROUTER_IMPLEMENTATION
llm_guard.hpp313 行

llm-guard

检测并清除个人信息(邮箱、电话、SSN、卡号、API 密钥),并为提示词注入风险打分

依赖
无
分组
应用
#define LLM_GUARD_IMPLEMENTATION
运行结果
llm_audio.hpp388 行

llm-audio

通过 OpenAI API 实现 Whisper 语音转写与翻译,以及文本转语音

依赖
libcurl
分组
应用
#define LLM_AUDIO_IMPLEMENTATION
llm_finetune.hpp469 行

llm-finetune

OpenAI 微调全流程:写 JSONL、上传、创建、轮询、取消、列出模型

依赖
libcurl
分组
应用
#define LLM_FINETUNE_IMPLEMENTATION

没有符合筛选条件的头文件。

03

真实代码,真实输出。

这里挑了 6 个离线库,每个都是完整程序,实现宏就写在同一个文件里。旁边的输出就是它实际打印的内容;除了注释中注明的地方,没有任何模拟。

llm-cache (210 行,无依赖)。相同的提示词不再调用 API。键默认不区分大小写,容量满时淘汰最久未使用的条目。

cache.cpp
#define LLM_CACHE_IMPLEMENTATION
#include "llm_cache.hpp"
#include <cstdio>

int main() {
    llm::CacheConfig cfg;
    cfg.max_entries = 2;                  // tiny, to show LRU eviction
    llm::ResponseCache cache(cfg);

    int api_calls = 0;
    auto ask = [&](const std::string& prompt) {
        return cache.get_or_compute(prompt, [&] {
            ++api_calls;                  // your real model call goes here
            return "answer #" + std::to_string(api_calls);
        });
    };

    for (const char* p : {"What is RAII?", "what is raii?",
                          "Explain move semantics", "What is SFINAE?",
                          "What is RAII?"})
        std::printf("%-24s -> %s\n", p, ask(p).c_str());

    auto s = cache.stats();
    std::printf("\napi calls %d | hits %zu | misses %zu | evictions %zu\n",
                api_calls, s.hits, s.misses, s.evictions);
}
x64 Native Tools
C:\demo> cl /nologo /std:c++17 /EHsc /O2 cache.cpp
cache.cpp
C:\demo> cache.exe
What is RAII?            -> answer #1
what is raii?            -> answer #1
Explain move semantics   -> answer #2
What is SFINAE?          -> answer #3
What is RAII?            -> answer #4

api calls 4 | hits 1 | misses 4 | evictions 2
C:\demo> 

使用 MSVC 19.44 x64(/std:c++17 /EHsc /O2)针对每个库当前的头文件编译,于 2026-09-28 运行。源码:examples/offline,每次 CI 运行都会用 g++ 重新构建。价格来自 llm-cost 的内置价格表。

04

组合使用多个库。

头文件可以在任何地方并排 include。唯一需要分开放的是实现。

每个 .cpp 只放一个实现。

有几个头文件使用了相同的内部辅助函数名(例如 llm::detail::json_escape),所以在同一个翻译单元里定义两个 *_IMPLEMENTATION 宏可能会编译失败。llm-log 和 llm-stream 就是这样的一对。

llm_impl.cppLOG + STREAM 放在同一个文件:可能失败
llm_impl_log.cpp#define LLM_LOG_IMPLEMENTATION
llm_impl_retry.cpp#define LLM_RETRY_IMPLEMENTATION
llm_impl_stream.cpp#define LLM_STREAM_IMPLEMENTATION
已于 2026-09-25 验证:全部 26 个实现各放在自己的 .cpp 里,可以编译并链接成一个二进制文件(MSVC 19.44 x64,libcurl 来自 vcpkg)。每次推送时 CI 都会用 g++ 再验证一遍。
main.cpp:流式输出,失败时重试,记录调用
#include "llm_log.hpp"
#include "llm_retry.hpp"
#include "llm_stream.hpp"
#include <cstdlib>
#include <iostream>

int main() {
    const char* key = std::getenv("OPENAI_API_KEY");
    if (!key) { std::cerr << "set OPENAI_API_KEY\n"; return 1; }

    llm::Config cfg;
    cfg.api_key = key;
    cfg.model   = "gpt-4o-mini";
    const std::string prompt = "Explain backpressure in one paragraph.";

    llm::Logger logger(llm::LogConfig{"calls.jsonl"});
    llm::Logger::ScopedCall call(logger, cfg.model, prompt);   // written on scope exit

    auto result = llm::with_retry<std::string>([&]() -> std::string {
        std::string text, error;
        llm::stream(prompt, cfg,
            [&](std::string_view tok) { std::cout << tok << std::flush; text += tok; },
            nullptr,
            [&](std::string_view err) { error = err; });
        if (!error.empty()) throw llm::LLMError{0, error, true};   // retry
        return text;
    });

    call.set_response(result.value);
    std::cout << "\n(" << result.attempts_used << " attempt(s))\n";
}
05

安装命令,替你写好。

在库目录里挑选头文件。下面的命令会把它们下载到 third_party/,为每个实现单独建一个 .cpp,并且只在你选的库需要时才加上 -lcurl。