基于昇腾 NPU 部署 Llama-3-8B 实战教程:从环境搭建到构建昇腾问答智能体
算力申请:https://ai.gitcode.com/ascend-tribe/openPangu-Ultra-MoE-718B-V1.1
模型地址:https://gitcode.com/test-oh-kb/Meta-Llama-3-8B-Instruct
前言
随着开源大模型生态的繁荣,越来越多的开发者开始探索在国产算力平台上运行主流模型。本文将详细记录如何在 昇腾(Ascend) 环境下,使用 ModelScope 社区提供的模型资源,通过AtomGit平台的notebook部署 Meta-Llama-3-8B-Instruct 模型,并基于此构建一个具备多轮对话能力的命令行智能体(Agent)。
一、 环境准备与硬件检查
在开始部署之前,首先确认 NPU 硬件状态是否正常。
1. 查看 NPU 信息
使用 npu-smi info 命令查看硬件详情。

● NPU为昇腾 Atlas 800T 。
● Health: 显示为 OK,硬件状态良好。
● Memory: 显存(HBM)总共 64GB,当前占用极低,足以运行 8B 规模的模型(半精度约需 16GB 显存)。
2. 安装 Python 依赖
我们需要安装 HuggingFace Transformers 生态库以及魔搭社区(ModelScope)SDK。
安装命令:
pip install transformers accelerate modelscope 安装日志摘要:

注意:环境需预装 torch 和 torch_npu。可以通过 python -c “import torch; import torch_npu; print(torch_npu.version)” 验证 NPU 插件是否就绪。
二、 模型下载
为了获得更快的下载速度,我们选择从国内的 ModelScope 魔搭社区 下载 Llama-3 模型。
1. 编写下载脚本 (download.py)
from modelscope import snapshot_download # 指定下载目录为当前文件夹下的 models 目录 print("正在从 ModelScope 下载 Meta-Llama-3-8B-Instruct...") model_dir = snapshot_download('LLM-Research/Meta-Llama-3-8B-Instruct', cache_dir='./models') print(f"模型已下载至: {model_dir}") 2.执行下载
运行:
python download.py 
可以看到,下载速度达到了约 50MB/s,迅速完成了 15GB 权重文件的下载。
三、 实践阶段 1:基础推理测试
首先,我先编写了一个简单的推理脚本,测试模型能否在 NPU 上成功加载并生成代码。
1. 推理代码 (test01.py)
为了便于理解,我将代码拆分为了环境配置、模型加载、数据预处理 和 推理生成 四个核心模组。
1. 模组一:环境配置与依赖导入
首先,我们需要引入必要的库,特别是 torch_npu,这是在昇腾卡上运行 PyTorch 的关键库。
import torch import torch_npu # 【关键】必须导入,用于激活 NPU 后端 from transformers import AutoTokenizer, AutoModelForCausalLM import time # --- 配置参数 --- # 模型存储路径(请确保路径下包含完整权重文件) MODEL_PATH = "./models/LLM-Research/Meta-Llama-3-8B-Instruct" # 指定计算设备,'npu:0' 代表第一张昇腾加速卡 DEVICE = "npu:0" print(f"[*] 正在初始化环境,目标设备: {DEVICE}") 2. 模组二:加载模型与分词器
Llama-3-8B 在 FP16(半精度)下大约需要 16GB 显存,该卡显存完全足够 。
def load_model(): print(f"[*] 正在加载 Tokenizer...") tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH) # 【优化】解决 Llama-3 缺失 Pad Token 的警告问题 if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token tokenizer.pad_token_id = tokenizer.eos_token_id print(f"[*] 正在加载模型到 NPU (预计耗时 1-2 分钟)...") model = AutoModelForCausalLM.from_pretrained( MODEL_PATH, dtype=torch.float16, # 【优化】使用 dtype 替代旧版 torch_dtype device_map=DEVICE # 自动将模型权重映射到 NPU ) return tokenizer, model # 执行加载 tokenizer, model = load_model() print("[*] 模型加载成功!") 3. 模组三:构建 Prompt 与对话模板
为了让 Llama-3 理解它是处于对话模式,我们需要使用 apply_chat_template 将自然语言转换为模型能理解的格式(包含 <|begin_of_text|> 等特殊标记)。
# 设定 System Prompt(系统人设)和 User Prompt(用户指令) prompt_content = ( "请用 Python 编写一个可以在终端运行的‘黑客帝国(The Matrix)’风格的代码雨特效脚本。" "要求:使用 curses 库实现,并添加详细中文注释。" ) messages = [ {"role": "system", "content": "你是一个专业的Python代码专家。请全程使用中文回答。"}, {"role": "user", "content": prompt_content}, ] # 将对话列表转换为 Token ID input_ids = tokenizer.apply_chat_template( messages, add_generation_prompt=True, # 自动添加引导模型输出的标记 return_tensors="pt" # 返回 PyTorch 张量 ).to(model.device) # 【关键】数据搬运到 NPU # 定义终止符(防止模型不停地生成) terminators = [ tokenizer.eos_token_id, tokenizer.convert_tokens_to_ids("<|eot_id|>") ] 4. 模组四:执行推理与解码
最后,调用 generate 函数生成内容,并计算推理耗时。
print(f"\n{'='*10} 开始 NPU 推理 {'='*10}") start_time = time.time() # 生成配置 outputs = model.generate( input_ids, max_new_tokens=1024, # 允许生成的最大长度 eos_token_id=terminators, do_sample=True, # 启用采样,增加多样性 temperature=0.7, # 温度系数:值越低越保守,值越高越发散 top_p=0.9, ) end_time = time.time() # 【逻辑解析】outputs 包含了输入+输出。我们需要切片提取新生成的部分。 # input_ids.shape[-1] 是输入 Prompt 的长度 generated_tokens = outputs[0][input_ids.shape[-1]:] response_text = tokenizer.decode(generated_tokens, skip_special_tokens=True) print(response_text) print(f"\n{'='*10} 性能统计 {'='*10}") print(f"耗时: {end_time - start_time:.2f}s | 生成长度: {len(generated_tokens)} tokens") # 显存清理(可选) torch.npu.empty_cache() 完整代码:
import torch import torch_npu # 必须导入,激活 NPU 后端 from transformers import AutoTokenizer, AutoModelForCausalLM import time # --- 配置部分 --- MODEL_PATH = "./models/LLM-Research/Meta-Llama-3-8B-Instruct" DEVICE = "npu:0" def run_inference(): print(f"[*] 正在加载模型到 {DEVICE} (这可能需要 1-2 分钟)...") # 1. 加载 Tokenizer tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH) # 2. 加载模型 model = AutoModelForCausalLM.from_pretrained( MODEL_PATH, torch_dtype=torch.float16, device_map=DEVICE ) print("[*] 模型加载成功!准备开始推理测试...") # 场景:要求模型编写一个具有视觉效果的脚本。 # 这不仅测试了它的代码逻辑,还测试了它对库(curses/random)的理解。 prompt = ( "请用 Python 编写一个可以在终端(Terminal)运行的‘黑客帝国(The Matrix)’风格的代码雨特效脚本。" "要求:\n" "1. 使用 curses 库实现;\n" "2. 代码需要完整的、可以直接运行;\n" "3. 在关键逻辑处添加详细的中文注释,解释实现原理。" ) # 或者你可以尝试这个逻辑题 Prompt: # prompt = "假设你是一个高情商的职场导师。我的老板刚刚在公开会议上严厉批评了我的方案,但我认为是他没看懂数据。请帮我起草一封回复邮件,既不卑不亢,又能委婉地引导他重新审视数据,同时保留他的面子。" messages = [ {"role": "system", "content": "你是一个专业的Python代码专家,擅长编写炫酷的终端特效代码。请全程使用中文回答。"}, {"role": "user", "content": prompt}, ] # 4. 预处理输入 input_ids = tokenizer.apply_chat_template( messages, add_generation_prompt=True, return_tensors="pt" ).to(model.device) terminators = [ tokenizer.eos_token_id, tokenizer.convert_tokens_to_ids("<|eot_id|>") ] print(f"\n{'='*10} 正在生成结果 (观察 NPU 推理速度) {'='*10}") # 5. 生成 (推理过程) start_time = time.time() outputs = model.generate( input_ids, max_new_tokens=1024, # 增加 token 限制,因为代码雨脚本可能较长 eos_token_id=terminators, do_sample=True, temperature=0.7, # 稍微提高创造性 top_p=0.9, ) end_time = time.time() # 6. 解码并打印 # --- Bug 修复说明 --- # 原代码 outputs0:] 是错误的语法。 # model.generate 返回的是 [batch_size, seq_len]。 # 我们需要取第一个 batch,并只保留 input_ids 长度之后的部分(即新生成的内容)。 generated_tokens = outputs[0][input_ids.shape[-1]:] response_text = tokenizer.decode(generated_tokens, skip_special_tokens=True) print(response_text) print(f"\n{'='*10} 统计信息 {'='*10}") # 粗略计算 token 生成速度 (tokens/sec) token_count = len(generated_tokens) duration = end_time - start_time print(f"耗时: {duration:.2f}秒") print(f"Token 数量: {token_count}") print(f"平均速度: {token_count / duration:.2f} tokens/s") print("✅ NPU 推理测试完成") # 释放显存 torch.npu.empty_cache() if __name__ == "__main__": run_inference() 2. 运行结果
脚本成功生成了一段完整的 curses 库代码雨脚本。

通过运行该代码查看代码效果以及质量:

效果还不错!模型在单卡 NPU 上运行稳定,推理速度满足实时交互需求。
四、实践阶段 2:构建对话智能体 (AscendBot)
在验证了模型的基础能力后,我们利用 Llama-3 的指令微调特性,构建一个具备“人设”的对话机器人。
相较于单次推理,Agent 的核心在于 “记忆保持”和 “多轮交互循环”。
1. 模组一:初始化智能体
这部分负责加载模型并设定“人设”(System Prompt)。
# ... (前置的 import 和 模型加载代码与上一节相同,此处省略以节省篇幅) ... # 初始化对话历史,这就是 Agent 的“短期记忆” # 这里设定了 AscendBot 的身份 history = [ { "role": "system", "content": "你是一个基于昇腾 NPU 算力运行的智能助手,名字叫 AscendBot。请用中文辅助用户解决问题。" }, ] print("🤖 AscendBot 已上线!(输入 'exit' 退出对话)") 2. 模组二:构建交互循环 (The Loop)
这是 Agent 的主驱逻辑。我们需要不断获取用户输入,更新历史记录,并请求模型生成新的回答。
while True: # --- 步骤 A: 获取输入 --- user_input = input("\n👤 User: ") if user_input.lower() in ["exit", "quit"]: print("AscendBot 下线。") break # --- 步骤 B: 更新记忆 --- # 将用户当前的问题追加到历史记录中 history.append({"role": "user", "content": user_input}) # --- 步骤 C: 格式化与推理 --- # 使用模板处理整个历史记录(包含 system, 之前的 user/assistant 对话, 和当前的 user 问题) input_ids = tokenizer.apply_chat_template( history, add_generation_prompt=True, return_tensors="pt" ).to(model.device) # 调用 NPU 进行生成 outputs = model.generate( input_ids, max_new_tokens=512, eos_token_id=terminators, do_sample=True, temperature=0.6, top_p=0.9, ) # --- 步骤 D: 解析与闭环 --- # 提取纯回复内容 response_tokens = outputs[0][input_ids.shape[-1]:] response_text = tokenizer.decode(response_tokens, skip_special_tokens=True) print(f"🤖 Agent: {response_text}") # 【关键】将模型的回答也追加到历史记录,形成闭环 # 这样下一次对话时,模型就能“记得”自己刚才说了什么 history.append({"role": "assistant", "content": response_text}) 完整代码:
import torch import torch_npu # 必须导入,让 PyTorch 识别 NPU from transformers import AutoTokenizer, AutoModelForCausalLM # 1. 配置路径和设备 model_path = "./models/LLM-Research/Meta-Llama-3-8B-Instruct" device = "npu" # 指定使用 NPU print(f"[*] 正在加载模型到 {device},请稍候...") # 2. 加载模型和分词器 tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype=torch.float16, # 昇腾通常推荐半精度 device_map=device, trust_remote_code=True ) # Llama-3 特有的结束符处理 terminators = [ tokenizer.eos_token_id, tokenizer.convert_tokens_to_ids("<|eot_id|>") ] print("[*] 模型加载完成!开始对话 (输入 'exit' 或 'quit' 退出)") print("-" * 50) # 3. 初始化对话历史 (这就是智能体的"短期记忆") # system prompt 设定了智能体的人设 messages = [ {"role": "system", "content": "你是一个基于昇腾算力运行的智能助手,你的名字叫 AscendBot。请用中文回答用户的问题。"}, ] while True: # 获取用户输入 user_input = input("\n👤 User: ") if user_input.lower() in ["exit", "quit"]: print("Bye!") break # 将用户输入加入历史 messages.append({"role": "user", "content": user_input}) # 应用 Chat Template (这是 Llama-3 能够理解对话结构的关键) input_ids = tokenizer.apply_chat_template( messages, add_generation_prompt=True, return_tensors="pt" ).to(model.device) # 生成回复 outputs = model.generate( input_ids, max_new_tokens=512, eos_token_id=terminators, do_sample=True, temperature=0.6, top_p=0.9, ) # 解码输出 (只取新生成的部分) response = outputs[0][input_ids.shape[-1]:] response_text = tokenizer.decode(response, skip_special_tokens=True) # 打印回复 print(f"🤖 Agent: {response_text}") # 将模型回复加入历史,形成闭环 messages.append({"role": "assistant", "content": response_text}) 2. 交互效果演示
在终端运行 python agent.py 后,我们与模型进行了如下对话:
👤**** User: 你是什么模型
🤖**** Agent: 我是 AscendBot,一个基于昇腾算力运行的智能助手。我是由 Huawei Ascend系列芯片平台搭建的 AI 模型…
👤**** User: 你了解昇腾多少
🤖**** Agent: 作为一个基于昇腾算力运行的智能助手,我当然了解昇腾的相关信息…包括高性能计算、低延迟计算…

模型成功遵循了 System Prompt 的指令(AscendBot人设设定),并且在多轮对话中保持了上下文逻辑,且中文回答流畅准确。
五、 问题总结与解决方法
在实操过程中,终端日志输出了一些 Warning 警告。
问题 1:torch_dtype 参数弃用警告
日志内容:
torch_dtype is deprecated! Use dtype instead! 原因分析:
新版本的 Transformers 库在 from_pretrained 方法中建议直接使用 dtype 参数,而不是旧版的 torch_dtype。
解决方法:
修改模型加载代码:
# 修改前 model = AutoModelForCausalLM.from_pretrained(..., torch_dtype=torch.float16) # 修改后 model = AutoModelForCausalLM.from_pretrained(..., dtype=torch.float16) 问题 2:Padding Token 未设置警告
日志内容:
The attention mask and the pad token id were not set. As a consequence, you may observe unexpected behavior. Setting pad_token_id to eos_token_id:128009 for open-end generation. 原因分析:
Llama-3 的 Tokenizer 默认没有指定 pad_token。虽然 Transformers 库自动将其设置为 eos_token 并继续运行,但这可能导致某些批处理或生成任务中的不稳定。
解决方法:
在加载 Tokenizer 后,显式手动指定 Pad Token:
tokenizer = AutoTokenizer.from_pretrained(model_path) # 显式将 pad_token 设置为 eos_token if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token tokenizer.pad_token_id = tokenizer.eos_token_id 六、 总结
通过本次实践,我们实现了 Llama-3-8B 模型在 Atlas 800T NPU 上的部署以及代码生成能力和系统提示词设定的人设对话能力验证。
1. 环境友好:配合 ModelScope 和 PyTorch 插件,昇腾环境的部署流程已非常接近 NVIDIA GPU 的体验。
2. 性能达标:在未做深度优化的前提下,单卡推理速度流畅,足以支撑 Chatbot 应用。
3. 开发便捷:使用 HuggingFace 原生接口(device_map=“npu”)即可无缝迁移代码。