无需GPU无需AI基础跟着代码一步步理解大模型推理全流程 前言为什么写这篇博客最近大模型LLM火得一塌糊涂ChatGPT、Claude、通义千问…但你有没有想过这些AI到底是怎么运行的能不能在本地跑一个属于自己的AI代码层面AI是如何思考并回答问题的今天我就带你从零开始用C和llama.cpp库实现一个完整的本地AI聊天机器人。不需要任何AI专业知识我会用最通俗的语言解释每个概念。 读完这篇博客你将掌握✅ AI聊天程序的核心架构和运行流程✅ 如何加载GGUF格式的大模型文件✅ 词表Vocabulary、Token、上下文Context等核心概念✅ 采样器Sampler的配置和调优✅ CPU/GPU推理切换和性能优化✅ 完整的可运行C代码含详细注释️ 准备工作环境搭建1. 获取llama.cpp库首先你需要下载llama.cpp源码并编译gitclone https://github.com/ggerganov/llama.cpp.gitcdllama.cppmkdirbuildcdbuild cmake..cmake--build.--configRelease编译后你会得到llama.h头文件和对应的库文件。2. 下载模型文件GGUF格式推荐从Hugging Face下载量化后的模型推荐模型按速度排序 1. Qwen2.5-1.5B-Q3_K_S.gguf → 最快适合普通CPU 2. Qwen2.5-3B-Q3_K_S.gguf → 中等速度 3. Qwen2.5-7B-Q4_K_M.gguf → 较慢需要高性能CPU/GPU小贴士文件名中的Q3_K_S表示量化级别数字越小模型越小越快。 完整代码带超详细注释版以下代码是完整的聊天程序我加了你能看懂的所有注释#includellama.h// llama.cpp 核心引擎#includecstdio// 标准输入输出#includecstring// 字符串操作#includeiostream// C 流输入输出#includestring// std::string 字符串#includevector// std::vector 动态数组#includewindows.h// Windows API设置控制台编码/** * 打印程序使用说明 */staticvoidprint_usage(int,char**argv){printf(\n使用示例:\n);printf(\n %s -m 模型文件.gguf [-c 上下文大小] [-ngl GPU层数]\n,argv[0]);printf(\n);}intmain(intargc,char**argv){// // 第一步设置控制台编码让中文正常显示// SetConsoleOutputCP(CP_UTF8);SetConsoleCP(CP_UTF8);std::setlocale(LC_ALL,.UTF-8);// // 第二步配置模型参数核心配置// // 模型文件路径请修改为你自己的路径std::string model_pathE:\\Project\\AI\\Qwen3.5-4B-GGUF\\Qwen3.5-4B-Q3_K_S.gguf;intngl0;// 0 全部使用CPU推理推荐无显卡用户// 99 全部加载到GPU需要NVIDIA显卡intn_ctx2048;// 上下文窗口大小AI能记住的最大token数// 2048 ≈ 1500个中文字// // 第三步设置日志级别只看错误信息// llama_log_set([](enumggml_log_levellevel,constchar*text,void*){if(levelGGML_LOG_LEVEL_ERROR){fprintf(stderr,%s,text);// 只打印错误}},nullptr);// // 第四步加载硬件加速后端自动检测CPU/GPU// ggml_backend_load_all();// // 第五步加载AI模型最关键的步骤// llama_model_params model_paramsllama_model_default_params();model_params.n_gpu_layersngl;// 设置GPU层数0表示纯CPUllama_model*modelllama_model_load_from_file(model_path.c_str(),model_params);if(!model){fprintf(stderr,错误无法加载模型文件\n);return1;}// 获取模型的词表文字↔数字的对照表constllama_vocab*vocabllama_model_get_vocab(model);// // 第六步创建推理上下文AI的工作台// llama_context_params ctx_paramsllama_context_default_params();ctx_params.n_ctxn_ctx;// 上下文大小ctx_params.n_batch512;// 批处理大小减小可降低内存占用ctx_params.n_threads8;// CPU线程数建议等于CPU核心数ctx_params.n_threads_batch8;// 批处理线程数llama_context*ctxllama_init_from_model(model,ctx_params);if(!ctx){fprintf(stderr,错误创建推理上下文失败\n);return1;}// // 第七步配置采样器控制AI如何选词// // 采样器链 多个策略的组合llama_sampler*smplllama_sampler_chain_init(llama_sampler_chain_default_params());// 策略1Min-P采样过滤掉概率太低的词0.05表示低于最高概率5%的词被淘汰llama_sampler_chain_add(smpl,llama_sampler_init_min_p(0.05f,1));// 策略2温度采样控制创意度0.8表示中等保守llama_sampler_chain_add(smpl,llama_sampler_init_temp(0.8f));// 策略3概率分布采样按概率随机选不死板llama_sampler_chain_add(smpl,llama_sampler_init_dist(LLAMA_DEFAULT_SEED));// // 第八步定义核心生成函数AI如何说话// autogenerate[](conststd::stringprompt)-std::string{std::string response;// 检查是否首次生成用于决定分词模式constboolis_firstllama_memory_seq_pos_max(llama_get_memory(ctx),0)-1;// --- 8.1 将文字转为数字分词 ---// 第一次调用计算需要多少个tokenconstintn_prompt_tokens-llama_tokenize(vocab,prompt.c_str(),prompt.size(),NULL,0,is_first,true);std::vectorllama_tokenprompt_tokens(n_prompt_tokens);// 第二次调用真正执行分词if(llama_tokenize(vocab,prompt.c_str(),prompt.size(),prompt_tokens.data(),prompt_tokens.size(),is_first,true)0){fprintf(stderr,分词失败\n);return;}// --- 8.2 准备批处理数据 ---llama_batch batchllama_batch_get_one(prompt_tokens.data(),prompt_tokens.size());llama_token new_token_id;// --- 8.3 自回归生成一个字一个字地蹦 ---while(true){// 检查上下文空间是否足够intn_ctxllama_n_ctx(ctx);intn_ctx_usedllama_memory_seq_pos_max(llama_get_memory(ctx),0)1;if(n_ctx_usedbatch.n_tokensn_ctx){fprintf(stderr,上下文已满\n);break;}// AI推理计算最耗时的步骤intretllama_decode(ctx,batch);if(ret!0){fprintf(stderr,推理失败\n);break;}// 采样从概率分布中选一个tokennew_token_idllama_sampler_sample(smpl,ctx,-1);// 检查是否遇到结束标记if(llama_vocab_is_eog(vocab,new_token_id)){break;// AI认为话说完了}// 将token转回文字解码charbuf[256];intnllama_token_to_piece(vocab,new_token_id,buf,sizeof(buf),0,true);if(n0)continue;std::stringpiece(buf,n);printf(%s,piece.c_str());// 实时打印fflush(stdout);responsepiece;// 准备下一次迭代只输入刚生成的tokenbatchllama_batch_get_one(new_token_id,1);}returnresponse;};// // 第九步启动对话循环聊天界面// std::vectorllama_chat_messagemessages;// 系统指令告诉AI如何回答问题messages.push_back({system,请直接用中文回答不要使用think标签不要展示推理过程。});std::vectorcharformatted(llama_n_ctx(ctx));intprev_len0;printf(\n AI聊天机器人已启动输入问题开始对话直接回车退出。\n\n);while(true){// --- 9.1 获取用户输入 ---printf(\033[32m \033[0m);// 绿色提示符std::string user;std::getline(std::cin,user);if(user.empty()){break;// 空输入退出}// --- 9.2 应用聊天模板格式化对话历史 ---constchar*tmplllama_model_chat_template(model,nullptr);messages.push_back({user,strdup(user.c_str())});intnew_lenllama_chat_apply_template(tmpl,messages.data(),messages.size(),true,formatted.data(),formatted.size());if(new_len(int)formatted.size()){formatted.resize(new_len);new_lenllama_chat_apply_template(tmpl,messages.data(),messages.size(),true,formatted.data(),formatted.size());}if(new_len0){fprintf(stderr,应用聊天模板失败\n);return1;}// --- 9.3 提取本次的提示词 ---std::stringprompt(formatted.begin()prev_len,formatted.begin()new_len);// --- 9.4 AI生成回答 ---printf(\033[33m);// 黄色输出AI回答std::string responsegenerate(prompt);printf(\n\033[0m);// 重置颜色// --- 9.5 保存对话历史 ---messages.push_back({assistant,strdup(response.c_str())});prev_lenllama_chat_apply_template(tmpl,messages.data(),messages.size(),false,nullptr,0);if(prev_len0){fprintf(stderr,应用聊天模板失败\n);return1;}}// // 第十步释放资源好习惯// for(automsg:messages){free(const_castchar*(msg.content));}llama_sampler_free(smpl);llama_free(ctx);llama_model_free(model);printf(\n 再见\n);return0;} 核心概念详解小白必读1. Token是什么为什么AI不认识字AI实际上只认识数字不认识文字。所以我们需要一个翻译官——词表Vocabulary。你好 → 分词器 → [1234, 5678] → AI处理 → [8765] → 分词器 → 好通俗比喻Token就像中文的偏旁部首AI通过组合偏旁来理解词语。2. 上下文Context是什么上下文就是AI的短期记忆。它决定了AI能记住多少前面的对话内容。n_ctx 2048AI能记住约2048个token如果对话太长AI会忘记最早的内容3. 采样器Sampler如何工作每次AI生成下一个词时会计算所有词的概率词概率“我”30%“你”25%“他”20%……采样器决定选哪个词Temperature温度控制创意度高温度1.2AI更爱冒险可能选冷门词低温度0.5AI更保守选最常见的词Min-P淘汰太冷门的词概率低于阈值4. 聊天模板是做什么的不同模型训练方式不同它们期待的对话格式也不同ChatML格式 |im_start|user 你好|im_end| |im_start|assistant 你好|im_end| Llama 3格式 |start_header_id|user|end_header_id| 你好|eot_id| |start_header_id|assistant|end_header_id| 你好|eot_id|聊天模板就是格式化器自动把对话整理成模型需要的格式。⚡ 性能优化技巧CPU推理优化优化项设置效果使用小模型1.5B-3B参数速度提升3-5倍降低量化级别Q3_K_S速度提升2倍设置线程数n_threads CPU核心数充分利用CPU减小批处理n_batch 512降低内存占用减小上下文n_ctx 1024速度提升30%完整优化配置intngl0;// 纯CPU模式intn_ctx1024;// 减小上下文ctx_params.n_batch512;// 减小批处理ctx_params.n_threads8;// 设置CPU线程数 性能参考7B模型硬件推理速度体验评价普通4核CPU1-2 token/秒 较慢需耐心高性能8核CPU5-8 token/秒 可接受RTX 3060 (6GB)20-30 token/秒 流畅RTX 4090 (24GB)60-100 token/秒 极速1 token ≈ 0.5-1个中文字 运行你的第一个AI对话编译命令# 使用gg-stdc17-O2llamaTest.cpp-ollamaTest -I./llama.cpp/include -L./llama.cpp/build-lllama-lggml# 使用Visual Studio# 直接打开项目添加llama.cpp的包含目录和库目录运行./llamaTest对话示例 AI聊天机器人已启动输入问题开始对话直接回车退出。 你好请介绍一下你自己 你好我是基于通义千问模型开发的AI助手可以回答各种问题... 什么是机器学习 机器学习是人工智能的一个分支它让计算机通过数据学习... 常见问题解决Q1: 程序报错 “无法加载模型”检查模型文件路径是否正确使用绝对路径确认模型文件是GGUF格式检查文件是否完整几个GBQ2: 中文输出是乱码确保代码中有SetConsoleOutputCP(CP_UTF8)在运行前执行chcp 65001Windows终端Q3: 推理速度太慢使用更小的模型1.5B代替7B使用更低量化级别Q3_K_S如果CPU不支持AVX2编译时用-DGGML_AVX2OFFQ4: 程序内存占用太大减小n_ctx到 1024 或 512减小n_batch到 256 进阶学习资源llama.cpp官方文档GitHub仓库GGUF格式详解了解模型文件结构采样策略论文Temperature、Top-K、Top-P 的原理量化技术了解Q3_K_S、Q4_K_M等技术细节 结语通过这篇博客你已经掌握了✅ 一个完整的本地AI聊天机器人代码✅ 大模型推理的核心概念Token、上下文、采样✅ CPU/GPU推理切换和优化技巧✅ 100行C代码的逐行理解
从零开始:手把手教你用C++实现本地AI聊天机器人(llama.cpp实战)
发布时间:2026/7/6 6:26:25
无需GPU无需AI基础跟着代码一步步理解大模型推理全流程 前言为什么写这篇博客最近大模型LLM火得一塌糊涂ChatGPT、Claude、通义千问…但你有没有想过这些AI到底是怎么运行的能不能在本地跑一个属于自己的AI代码层面AI是如何思考并回答问题的今天我就带你从零开始用C和llama.cpp库实现一个完整的本地AI聊天机器人。不需要任何AI专业知识我会用最通俗的语言解释每个概念。 读完这篇博客你将掌握✅ AI聊天程序的核心架构和运行流程✅ 如何加载GGUF格式的大模型文件✅ 词表Vocabulary、Token、上下文Context等核心概念✅ 采样器Sampler的配置和调优✅ CPU/GPU推理切换和性能优化✅ 完整的可运行C代码含详细注释️ 准备工作环境搭建1. 获取llama.cpp库首先你需要下载llama.cpp源码并编译gitclone https://github.com/ggerganov/llama.cpp.gitcdllama.cppmkdirbuildcdbuild cmake..cmake--build.--configRelease编译后你会得到llama.h头文件和对应的库文件。2. 下载模型文件GGUF格式推荐从Hugging Face下载量化后的模型推荐模型按速度排序 1. Qwen2.5-1.5B-Q3_K_S.gguf → 最快适合普通CPU 2. Qwen2.5-3B-Q3_K_S.gguf → 中等速度 3. Qwen2.5-7B-Q4_K_M.gguf → 较慢需要高性能CPU/GPU小贴士文件名中的Q3_K_S表示量化级别数字越小模型越小越快。 完整代码带超详细注释版以下代码是完整的聊天程序我加了你能看懂的所有注释#includellama.h// llama.cpp 核心引擎#includecstdio// 标准输入输出#includecstring// 字符串操作#includeiostream// C 流输入输出#includestring// std::string 字符串#includevector// std::vector 动态数组#includewindows.h// Windows API设置控制台编码/** * 打印程序使用说明 */staticvoidprint_usage(int,char**argv){printf(\n使用示例:\n);printf(\n %s -m 模型文件.gguf [-c 上下文大小] [-ngl GPU层数]\n,argv[0]);printf(\n);}intmain(intargc,char**argv){// // 第一步设置控制台编码让中文正常显示// SetConsoleOutputCP(CP_UTF8);SetConsoleCP(CP_UTF8);std::setlocale(LC_ALL,.UTF-8);// // 第二步配置模型参数核心配置// // 模型文件路径请修改为你自己的路径std::string model_pathE:\\Project\\AI\\Qwen3.5-4B-GGUF\\Qwen3.5-4B-Q3_K_S.gguf;intngl0;// 0 全部使用CPU推理推荐无显卡用户// 99 全部加载到GPU需要NVIDIA显卡intn_ctx2048;// 上下文窗口大小AI能记住的最大token数// 2048 ≈ 1500个中文字// // 第三步设置日志级别只看错误信息// llama_log_set([](enumggml_log_levellevel,constchar*text,void*){if(levelGGML_LOG_LEVEL_ERROR){fprintf(stderr,%s,text);// 只打印错误}},nullptr);// // 第四步加载硬件加速后端自动检测CPU/GPU// ggml_backend_load_all();// // 第五步加载AI模型最关键的步骤// llama_model_params model_paramsllama_model_default_params();model_params.n_gpu_layersngl;// 设置GPU层数0表示纯CPUllama_model*modelllama_model_load_from_file(model_path.c_str(),model_params);if(!model){fprintf(stderr,错误无法加载模型文件\n);return1;}// 获取模型的词表文字↔数字的对照表constllama_vocab*vocabllama_model_get_vocab(model);// // 第六步创建推理上下文AI的工作台// llama_context_params ctx_paramsllama_context_default_params();ctx_params.n_ctxn_ctx;// 上下文大小ctx_params.n_batch512;// 批处理大小减小可降低内存占用ctx_params.n_threads8;// CPU线程数建议等于CPU核心数ctx_params.n_threads_batch8;// 批处理线程数llama_context*ctxllama_init_from_model(model,ctx_params);if(!ctx){fprintf(stderr,错误创建推理上下文失败\n);return1;}// // 第七步配置采样器控制AI如何选词// // 采样器链 多个策略的组合llama_sampler*smplllama_sampler_chain_init(llama_sampler_chain_default_params());// 策略1Min-P采样过滤掉概率太低的词0.05表示低于最高概率5%的词被淘汰llama_sampler_chain_add(smpl,llama_sampler_init_min_p(0.05f,1));// 策略2温度采样控制创意度0.8表示中等保守llama_sampler_chain_add(smpl,llama_sampler_init_temp(0.8f));// 策略3概率分布采样按概率随机选不死板llama_sampler_chain_add(smpl,llama_sampler_init_dist(LLAMA_DEFAULT_SEED));// // 第八步定义核心生成函数AI如何说话// autogenerate[](conststd::stringprompt)-std::string{std::string response;// 检查是否首次生成用于决定分词模式constboolis_firstllama_memory_seq_pos_max(llama_get_memory(ctx),0)-1;// --- 8.1 将文字转为数字分词 ---// 第一次调用计算需要多少个tokenconstintn_prompt_tokens-llama_tokenize(vocab,prompt.c_str(),prompt.size(),NULL,0,is_first,true);std::vectorllama_tokenprompt_tokens(n_prompt_tokens);// 第二次调用真正执行分词if(llama_tokenize(vocab,prompt.c_str(),prompt.size(),prompt_tokens.data(),prompt_tokens.size(),is_first,true)0){fprintf(stderr,分词失败\n);return;}// --- 8.2 准备批处理数据 ---llama_batch batchllama_batch_get_one(prompt_tokens.data(),prompt_tokens.size());llama_token new_token_id;// --- 8.3 自回归生成一个字一个字地蹦 ---while(true){// 检查上下文空间是否足够intn_ctxllama_n_ctx(ctx);intn_ctx_usedllama_memory_seq_pos_max(llama_get_memory(ctx),0)1;if(n_ctx_usedbatch.n_tokensn_ctx){fprintf(stderr,上下文已满\n);break;}// AI推理计算最耗时的步骤intretllama_decode(ctx,batch);if(ret!0){fprintf(stderr,推理失败\n);break;}// 采样从概率分布中选一个tokennew_token_idllama_sampler_sample(smpl,ctx,-1);// 检查是否遇到结束标记if(llama_vocab_is_eog(vocab,new_token_id)){break;// AI认为话说完了}// 将token转回文字解码charbuf[256];intnllama_token_to_piece(vocab,new_token_id,buf,sizeof(buf),0,true);if(n0)continue;std::stringpiece(buf,n);printf(%s,piece.c_str());// 实时打印fflush(stdout);responsepiece;// 准备下一次迭代只输入刚生成的tokenbatchllama_batch_get_one(new_token_id,1);}returnresponse;};// // 第九步启动对话循环聊天界面// std::vectorllama_chat_messagemessages;// 系统指令告诉AI如何回答问题messages.push_back({system,请直接用中文回答不要使用think标签不要展示推理过程。});std::vectorcharformatted(llama_n_ctx(ctx));intprev_len0;printf(\n AI聊天机器人已启动输入问题开始对话直接回车退出。\n\n);while(true){// --- 9.1 获取用户输入 ---printf(\033[32m \033[0m);// 绿色提示符std::string user;std::getline(std::cin,user);if(user.empty()){break;// 空输入退出}// --- 9.2 应用聊天模板格式化对话历史 ---constchar*tmplllama_model_chat_template(model,nullptr);messages.push_back({user,strdup(user.c_str())});intnew_lenllama_chat_apply_template(tmpl,messages.data(),messages.size(),true,formatted.data(),formatted.size());if(new_len(int)formatted.size()){formatted.resize(new_len);new_lenllama_chat_apply_template(tmpl,messages.data(),messages.size(),true,formatted.data(),formatted.size());}if(new_len0){fprintf(stderr,应用聊天模板失败\n);return1;}// --- 9.3 提取本次的提示词 ---std::stringprompt(formatted.begin()prev_len,formatted.begin()new_len);// --- 9.4 AI生成回答 ---printf(\033[33m);// 黄色输出AI回答std::string responsegenerate(prompt);printf(\n\033[0m);// 重置颜色// --- 9.5 保存对话历史 ---messages.push_back({assistant,strdup(response.c_str())});prev_lenllama_chat_apply_template(tmpl,messages.data(),messages.size(),false,nullptr,0);if(prev_len0){fprintf(stderr,应用聊天模板失败\n);return1;}}// // 第十步释放资源好习惯// for(automsg:messages){free(const_castchar*(msg.content));}llama_sampler_free(smpl);llama_free(ctx);llama_model_free(model);printf(\n 再见\n);return0;} 核心概念详解小白必读1. Token是什么为什么AI不认识字AI实际上只认识数字不认识文字。所以我们需要一个翻译官——词表Vocabulary。你好 → 分词器 → [1234, 5678] → AI处理 → [8765] → 分词器 → 好通俗比喻Token就像中文的偏旁部首AI通过组合偏旁来理解词语。2. 上下文Context是什么上下文就是AI的短期记忆。它决定了AI能记住多少前面的对话内容。n_ctx 2048AI能记住约2048个token如果对话太长AI会忘记最早的内容3. 采样器Sampler如何工作每次AI生成下一个词时会计算所有词的概率词概率“我”30%“你”25%“他”20%……采样器决定选哪个词Temperature温度控制创意度高温度1.2AI更爱冒险可能选冷门词低温度0.5AI更保守选最常见的词Min-P淘汰太冷门的词概率低于阈值4. 聊天模板是做什么的不同模型训练方式不同它们期待的对话格式也不同ChatML格式 |im_start|user 你好|im_end| |im_start|assistant 你好|im_end| Llama 3格式 |start_header_id|user|end_header_id| 你好|eot_id| |start_header_id|assistant|end_header_id| 你好|eot_id|聊天模板就是格式化器自动把对话整理成模型需要的格式。⚡ 性能优化技巧CPU推理优化优化项设置效果使用小模型1.5B-3B参数速度提升3-5倍降低量化级别Q3_K_S速度提升2倍设置线程数n_threads CPU核心数充分利用CPU减小批处理n_batch 512降低内存占用减小上下文n_ctx 1024速度提升30%完整优化配置intngl0;// 纯CPU模式intn_ctx1024;// 减小上下文ctx_params.n_batch512;// 减小批处理ctx_params.n_threads8;// 设置CPU线程数 性能参考7B模型硬件推理速度体验评价普通4核CPU1-2 token/秒 较慢需耐心高性能8核CPU5-8 token/秒 可接受RTX 3060 (6GB)20-30 token/秒 流畅RTX 4090 (24GB)60-100 token/秒 极速1 token ≈ 0.5-1个中文字 运行你的第一个AI对话编译命令# 使用gg-stdc17-O2llamaTest.cpp-ollamaTest -I./llama.cpp/include -L./llama.cpp/build-lllama-lggml# 使用Visual Studio# 直接打开项目添加llama.cpp的包含目录和库目录运行./llamaTest对话示例 AI聊天机器人已启动输入问题开始对话直接回车退出。 你好请介绍一下你自己 你好我是基于通义千问模型开发的AI助手可以回答各种问题... 什么是机器学习 机器学习是人工智能的一个分支它让计算机通过数据学习... 常见问题解决Q1: 程序报错 “无法加载模型”检查模型文件路径是否正确使用绝对路径确认模型文件是GGUF格式检查文件是否完整几个GBQ2: 中文输出是乱码确保代码中有SetConsoleOutputCP(CP_UTF8)在运行前执行chcp 65001Windows终端Q3: 推理速度太慢使用更小的模型1.5B代替7B使用更低量化级别Q3_K_S如果CPU不支持AVX2编译时用-DGGML_AVX2OFFQ4: 程序内存占用太大减小n_ctx到 1024 或 512减小n_batch到 256 进阶学习资源llama.cpp官方文档GitHub仓库GGUF格式详解了解模型文件结构采样策略论文Temperature、Top-K、Top-P 的原理量化技术了解Q3_K_S、Q4_K_M等技术细节 结语通过这篇博客你已经掌握了✅ 一个完整的本地AI聊天机器人代码✅ 大模型推理的核心概念Token、上下文、采样✅ CPU/GPU推理切换和优化技巧✅ 100行C代码的逐行理解