本地部署InternLM2大模型与Lagent智能体:从环境搭建到工具调用的实践指南
1. 从“玩具”到“工具”为什么我们要动手跑大模型Demo如果你最近关注AI肯定被各种“大模型”刷屏了。从ChatGPT到文心一言再到层出不穷的开源模型感觉不聊两句大模型都快跟不上时代了。但说实话对于大多数开发者或者技术爱好者来说这些大模型就像远在天边的“神仙”——知道它很厉害但总觉得和自己隔着一层玻璃看得见摸不着。参数动辄百亿千亿部署起来好像需要一堆昂贵的GPU光是想想就觉得门槛太高。这正是“书生·浦语”这类开源大模型和配套趣味Demo的价值所在。它把那个看似遥不可及的“神仙”请到了我们每个人的笔记本电脑上。今天要聊的就是基于InternLM2-Chat-1.8B这个轻量级模型配合Lagent智能体框架跑起来的一系列趣味Demo。这不仅仅是跟着教程点几下鼠标而是亲手把大模型从云端“请下来”让它在你本地的环境里运行、推理、甚至犯点小错误。这个过程远比看十篇技术综述更有价值。为什么我这么强调“动手”因为大模型领域理论和实践之间存在巨大的鸿沟。你可以在论文里读到“注意力机制”、“Transformer架构”但只有当你亲手调一个temperature参数看到模型从一本正经变成天马行空时你才能真正理解“生成多样性”意味着什么。你可以在博客里看到“智能体Agent”是未来但只有当你用Lagent让模型自己去调用一个计算器工具完成“123的456次方是多少”这种问题你才会惊叹于“工具使用”能力如何让模型突破自身的知识局限。所以这篇笔记的目的不是复述官方文档而是记录一个一线开发者在本地环境从头搭建、运行、调试这些Demo的全过程。我会重点分享那些文档里不会写但实际操作中一定会遇到的“坑”以及填坑的思路。比如为什么我的模型加载特别慢显存明明够为什么还是报OOM内存溢出max_tokens设多少才合适Lagent的Web Demo怎么突然打不开了这些才是从“知道”到“会用”的关键。2. 环境搭建避开依赖冲突的“隐形雷区”跑Demo的第一步永远是环境准备。这一步看似简单照着requirements.txt安装就行但恰恰是新手最容易折戟沉沙的地方。大模型相关的库PyTorch, Transformers等对版本极其敏感一个不匹配就可能导致后续各种诡异错误。2.1 核心三件套PyTorch、CUDA与Transformers我们的主角是InternLM2-Chat-1.8B一个1.8B参数的对话模型。虽然比动辄7B、13B的模型小很多但它依然需要GPU加速才能有可用的响应速度。因此环境核心是PyTorch与CUDA的匹配。首先确定你的CUDA版本。在命令行输入nvidia-smi右上角显示的CUDA Version是你驱动支持的最高版本但实际安装的CUDA工具包版本可能不同。更稳妥的是用nvcc --version查看。假设我们系统是CUDA 11.8。接下来去PyTorch官网pytorch.org获取安装命令。这里有个关键点不要盲目使用pip install torch这通常会安装最新的CPU版本。对于CUDA 11.8命令应该是pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118这一步很多人会出错因为网络问题导致从默认源下载慢或失败。建议先配置国内镜像源或者直接下载whl文件离线安装。安装完成后务必验证import torch print(torch.__version__) # 应显示2.x.x print(torch.cuda.is_available()) # 必须为True print(torch.cuda.get_device_name(0)) # 显示你的GPU型号如果cuda.is_available()返回False说明PyTorch装成了CPU版本或者CUDA版本不匹配需要彻底卸载重装。然后是Hugging Face的transformers库。理论上直接pip install transformers即可但为了兼容性我强烈建议指定一个稍旧但稳定的版本比如pip install transformers4.36.2。因为最新版的transformers可能引入了某些API变动而模型代码可能还未适配。2.2 虚拟环境必不可少的“隔离舱”我见过太多人因为依赖冲突而崩溃。你的电脑上可能跑着多个Python项目每个项目都有自己的一套依赖。直接在全域安装很容易导致版本冲突。使用Conda或Python内置的venv创建虚拟环境是最佳实践。# 使用conda推荐尤其方便管理不同CUDA版本 conda create -n internlm_demo python3.10 conda activate internlm_demo # 或者使用venv python -m venv internlm_demo_venv # Linux/Mac source internlm_demo_venv/bin/activate # Windows internlm_demo_venv\Scripts\activate在激活的虚拟环境中再执行上述PyTorch等包的安装。这样所有依赖都被限制在这个“隔离舱”内不会影响其他项目。做完实验直接删除这个环境即可系统干干净净。2.3 模型下载速度与完整性的博弈InternLM2-Chat-1.8B的模型文件存储在Hugging Face Model Hub上。我们可以用snapshot_download来自动下载。from modelscope import snapshot_download model_dir snapshot_download(Shanghai_AI_Laboratory/internlm2-chat-1_8b, cache_dir./local_model)这里第一个坑是网络。直接从Hugging Face下载几个G的模型文件速度可能很慢甚至中断。有两个解决方案使用国内镜像在运行代码前设置环境变量HF_ENDPOINThttps://hf-mirror.com。这样snapshot_download会通过国内镜像站下载速度有质的飞跃。手动下载如果代码下载总是失败可以直接在镜像站网页找到模型文件用下载工具如wget或迅雷下载整个仓库的git lfs文件然后放到cache_dir指定的目录中对应的子文件夹里。第二个坑是完整性。模型文件很大下载中断可能导致文件损坏。下载完成后运行Demo时如果出现“无法加载权重”等错误很可能是这个原因。解决办法是删除不完整的缓存重新下载。你可以通过比较下载文件夹的大小和Hugging Face页面上显示的总大小来初步判断。3. 初探InternLM2-Chat-1.8B与一个“小模型”对话环境准备好模型下载完毕最激动人心的时刻来了——让我们启动一个最简单的对话脚本看看这个1.8B的“小模型”能做什么。3.1 编写一个极简的对话循环下面是一个最基础的交互脚本它揭示了与大模型交互的核心流程import torch from transformers import AutoTokenizer, AutoModelForCausalLM # 1. 加载模型和分词器 model_name_or_path ./local_model/Shanghai_AI_Laboratory/internlm2-chat-1_8b tokenizer AutoTokenizer.from_pretrained(model_name_or_path, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained(model_name_or_path, torch_dtypetorch.float16, device_mapauto) # torch_dtypetorch.float16 使用半精度显著减少显存占用 # device_mapauto 让Transformers自动分配模型层到GPU和CPU # 2. 构建对话历史InternLM2使用特定的对话模板 history [] while True: query input(\n用户: ) if query.lower() in [exit, quit]: break # 将用户输入加入历史并生成模型输入 history.append({role: user, content: query}) # 使用模型的chat模板生成符合格式的prompt prompt tokenizer.apply_chat_template(history, tokenizeFalse, add_generation_promptTrue) # 3. 生成参数设置 inputs tokenizer(prompt, return_tensorspt).to(model.device) with torch.no_grad(): # 推理时不计算梯度节省内存 outputs model.generate( **inputs, max_new_tokens512, # 最多生成512个新token temperature0.7, # 温度参数控制随机性 do_sampleTrue, # 启用采样而非贪婪解码 top_p0.8, # 核采样参数保留概率质量前80%的token repetition_penalty1.1, # 重复惩罚避免重复循环 ) # 4. 解码并提取本轮回复 response tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokensTrue) print(f助手: {response}) # 5. 将助手回复加入历史以便多轮对话 history.append({role: assistant, content: response})运行这个脚本你就可以在命令行里和模型对话了。第一次运行会加载模型根据你的GPU性能可能需要几十秒到几分钟。加载完成后每次推理就很快了。3.2 关键参数解析如何“调教”模型的输出上面代码中的generate函数里的几个参数是你控制模型行为的“旋钮”。理解它们你才算真正开始使用大模型。max_new_tokens这是单次生成的最大token数。Token是模型处理文本的基本单位一个中文词可能被分成多个token。这个值设得太小回答可能被截断设得太大如果模型“废话连篇”会浪费计算资源。对于1.8B的模型日常对话设256-512足够写长文可以设1024。你需要根据任务类型和你的耐心程度来调整。temperature这是控制输出随机性最重要的参数。它的值在0到1之间可以大于1但通常不推荐。temperature 0模型总是选择概率最高的下一个词贪婪解码。输出确定性最强但容易重复、枯燥。temperature 0.7常用值引入适度随机性输出更有创意、更自然像人类的语言。temperature 1随机性增强输出可能变得荒谬、不合逻辑。 你可以把它想象成“想象力开关”。做严谨的数学推理时调低如0.2写诗歌、故事时调高如0.9。top_p(核采样)这是另一种控制多样性的方法。它设定一个概率阈值如0.8然后从累积概率超过该阈值的最小候选词集合中采样。和temperature配合使用能更好地过滤掉低概率的“胡言乱语”。通常temperature和top_p只调节一个即可top_p0.9或0.95是常见设置。repetition_penalty如果模型陷入“好的好的好的…”或者不断重复同一句话的循环把这个参数调到1.05到1.2之间可以有效抑制重复。注意这些参数没有绝对的最优值。最好的学习方式就是对比实验。对同一个问题固定其他参数只改变temperature观察输出有何不同。你会立刻感受到参数的力量。3.3 实测体验1.8B模型的能力边界跑起来之后你可以问它各种问题。我的实测感受是优点对于简单的常识问答、中文对话、文本摘要它的表现相当不错响应速度快语言流畅。比如问“解释一下牛顿第一定律”它能给出基本正确的回答。缺点毕竟是1.8B的小模型复杂逻辑推理、数学计算、事实性知识尤其是最新的是它的弱项。比如问“请计算2357乘以4891”它大概率会瞎编一个数字。问“2023年世界杯冠军是谁”它可能答不上来或给出错误答案。幻觉Hallucination小模型更容易“一本正经地胡说八道”。它会自信地编造不存在的书籍、人物或事件。这是目前所有大模型的通病小模型更甚。认识到它的边界很重要。这引出了下一个主题如何让这个不擅长计算的模型准确地进行数学运算答案是给它配上“外挂”——工具。4. 引入Lagent为模型装上“手脚”的智能体框架如果大模型是一个聪明但“手无缚鸡之力”的大脑那么智能体Agent就是为这个大脑配备的手、脚和工具库。Lagent是一个轻量级的开源智能体框架它的核心思想是让大模型学习调用外部工具如计算器、搜索引擎、代码解释器来完成它自身不擅长的任务。4.1 Lagent框架的核心工作流Lagent的工作流程可以简化为一个循环规划模型根据用户请求思考需要用什么工具、按什么步骤执行。执行模型生成工具调用的动作Action框架执行该动作如调用Python解释器执行一段代码。观察框架将工具执行的结果Observation返回给模型。反思模型根据结果决定是继续调用下一个工具还是整合所有信息生成最终回复给用户。这个“思考-行动-观察”的循环使得模型能够解决远超其原生能力的问题。4.2 部署Lagent Web Demo官方提供了基于Gradio的Web Demo让我们能直观地看到这个过程。部署步骤通常如下# 1. 克隆Lagent仓库 git clone https://gitee.com/internlm/lagent.git cd lagent # 2. 安装依赖注意在之前创建的虚拟环境中 pip install -e . # 以可编辑模式安装方便修改代码 # 3. 运行示例Demo例如基于InternLM2的Web Demo cd examples python internlm2_agent_web_demo.py运行后命令行会输出一个本地URL如http://127.0.0.1:7860用浏览器打开它。4.3 Web Demo实战与排坑打开Web界面你会看到一个聊天框旁边可能有一个工具选择区比如Python解释器、计算器、搜索引擎等。这里我分享几个实战中的关键点和常见问题1. 工具调用不成功检查模型提示词PromptLagent的核心是教会模型何时以及如何调用工具。这依赖于给模型的“系统提示词”System Prompt。这个提示词被预先写在了internlm2_agent_web_demo.py或其配置文件中。如果模型总是忽略工具直接回答或者调用格式错误很可能是提示词没写好或者模型没理解。对于开源模型你需要仔细设计提示词明确告诉模型“你有一个可用的工具列表当遇到需要计算、搜索等任务时你应该以{‘action’: ‘工具名’, ‘action_input’: ‘输入参数’}的格式来调用工具。”2. Web页面打不开或报错端口冲突默认端口7860可能被其他程序占用。可以在启动命令中指定其他端口python internlm2_agent_web_demo.py --server_port 8080。Gradio版本问题Gradio更新较快可能导致接口不兼容。尝试固定版本pip install gradio3.x.x查看原项目要求的版本。网络问题确保你的浏览器能访问本地回环地址127.0.0.1。3. 实际体验当模型学会使用计算器这是最令人兴奋的部分。在Demo中选择启用“计算器”工具然后问“请问123的456次方等于多少” 在没有工具的情况下模型会胡乱生成一个数字。但在Lagent中你会看到类似以下的日志或界面反馈用户 请问123的456次方等于多少 助手思考 这是一个复杂的指数运算超出了我的直接计算能力。我需要使用计算器工具。 助手行动 {‘action’: ‘calculator’, ‘action_input’: ‘123**456’} 系统观察 工具返回了一个极其巨大的数字这里是一长串数字。 助手 根据计算器的结果123的456次方等于 [一个精确的巨大数字]。这个过程清晰展示了智能体的价值模型自知其短规划使用工具解析工具结果并组织成自然语言回复。这不仅仅是计算对于需要实时信息的查询调用搜索API、需要执行复杂操作的任务调用代码解释器都是同样的逻辑。5. 深入拆解一个完整工具调用案例的代码级分析只看Web界面不够过瘾我们深入到代码层面看看Lagent是如何实现一次工具调用的。这能帮助我们未来定制自己的工具。假设我们有一个最简单的工具get_current_time功能是返回当前时间字符串。我们想教InternLM2模型在用户问时间时调用它。5.1 定义工具首先我们需要按照Lagent的格式定义一个工具类from lagent.actions import BaseAction import datetime class GetCurrentTimeAction(BaseAction): 一个获取当前时间的工具。 def __init__(self): super().__init__() # 工具的描述对于让模型理解何时使用此工具至关重要 self.desc 当用户询问当前时间、今天日期或类似关于时间的问题时使用此工具。输入应为空字符串或now。 def run(self, query: str): 运行工具。 Args: query: 工具的输入这里我们忽略它或用于触发。 Returns: str: 当前时间的字符串。 # 简单返回当前时间 current_time datetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S) return f当前时间是{current_time}5.2 构建智能体并设计系统提示词接下来我们需要初始化模型并构建一个包含此工具的智能体。最关键的一步是设计系统提示词System Promptfrom transformers import AutoTokenizer, AutoModelForCausalLM from lagent import Agent, ReAct, GPTAPI, ActionExecutor import torch # 加载模型和分词器 model AutoModelForCausalLM.from_pretrained(model_dir, torch_dtypetorch.float16, device_mapauto) tokenizer AutoTokenizer.from_pretrained(model_dir, trust_remote_codeTrue) # 创建工具执行器并注册我们的工具 actions ActionExecutor() actions.append(GetCurrentTimeAction()) # 将工具添加到执行器 # 核心系统提示词 system_prompt 你是一个乐于助人的AI助手并且可以使用工具。 你拥有以下工具 - get_current_time: 当用户询问当前时间、今天日期或类似关于时间的问题时使用此工具。输入应为空字符串或now。 请严格按照以下格式思考和回应 思考你需要分析用户请求决定是否需要使用工具以及使用哪个工具。 行动如果你决定使用工具你必须严格按照以下JSON格式输出且只输出这个JSON对象 {action: 工具名称, action_input: 工具的输入参数} 观察工具返回的结果会提供给你。 最终答案根据观察和你的思考给出最终回复给用户。 如果不需要工具请直接给出答案。 现在开始与用户对话。 # 注意这里的“get_current_time”必须与工具类名GetCurrentTimeAction的命名规则匹配 # Lagent通常会进行一些转换如驼峰转下划线具体要看框架实现。5.3 实现推理循环最后我们实现一个简化的推理循环模拟Lagent的ReAct推理-行动过程def simple_agent_chat(query, history[]): # 将系统提示词和对话历史结合 messages [{role: system, content: system_prompt}] messages.extend(history) messages.append({role: user, content: query}) # 将消息列表转换为模型输入的文本 prompt tokenizer.apply_chat_template(messages, tokenizeFalse, add_generation_promptTrue) # 第一次生成让模型思考并可能输出行动指令 inputs tokenizer(prompt, return_tensorspt).to(model.device) with torch.no_grad(): outputs model.generate(**inputs, max_new_tokens512, temperature0.1) # 温度调低让输出更确定 full_response tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokensTrue) # 解析模型的输出看是否包含行动指令 # 这里需要编写一个简单的解析器来提取可能的JSON import json import re # 尝试从响应中查找JSON块 json_match re.search(r\{.*action.*action_input.*\}, full_response, re.DOTALL) if json_match: try: action_cmd json.loads(json_match.group()) action_name action_cmd.get(action) action_input action_cmd.get(action_input, ) # 执行工具 if action_name in actions: tool_result actions[action_name].run(action_input) # 将工具结果作为“观察”加入对话历史让模型进行第二轮生成以给出最终答案 new_messages messages.copy() new_messages.append({role: assistant, content: full_response}) # 模型的第一轮输出含行动 new_messages.append({role: user, content: f观察{tool_result}}) # 模拟系统返回观察 # 生成最终答案 final_prompt tokenizer.apply_chat_template(new_messages, tokenizeFalse, add_generation_promptTrue) final_inputs tokenizer(final_prompt, return_tensorspt).to(model.device) with torch.no_grad(): final_outputs model.generate(**final_inputs, max_new_tokens256) final_response tokenizer.decode(final_outputs[0][final_inputs.input_ids.shape[1]:], skip_special_tokensTrue) return final_response.strip() else: return f错误未知的工具 {action_name}。 except json.JSONDecodeError: # 如果解析JSON失败说明模型没有正确调用工具直接返回它的原始回复 return full_response else: # 没有检测到工具调用直接返回模型回复 return full_response # 测试 print(simple_agent_chat(现在几点了))这个简化示例揭示了智能体框架的核心提示词工程和输出解析。你需要通过精心设计的提示词让模型学会在特定场景下生成结构化的工具调用指令然后你需要一个鲁棒的解析器从模型的自由文本输出中准确提取出这些指令。6. 性能优化与实用技巧让Demo跑得更快更稳在本地跑模型尤其是资源有限的情况下性能是个大问题。下面分享几个让体验更流畅的技巧。6.1 量化在精度和速度间取得平衡模型权重默认是FP32单精度浮点数或FP16半精度。量化Quantization是将高精度权重转换为低精度如INT8, INT4的过程能大幅减少模型内存占用和提升推理速度但会轻微损失精度。对于InternLM2-Chat-1.8B我们可以使用bitsandbytes库进行8位或4位量化加载from transformers import BitsAndBytesConfig import torch # 配置4位量化 bnb_config BitsAndBytesConfig( load_in_4bitTrue, # 使用4位量化加载 bnb_4bit_compute_dtypetorch.float16, # 计算时使用FP16 bnb_4bit_use_double_quantTrue, # 使用双重量化进一步压缩 bnb_4bit_quant_typenf4, # 使用NF4量化类型效果较好 ) model AutoModelForCausalLM.from_pretrained( model_dir, quantization_configbnb_config, # 传入量化配置 device_mapauto, trust_remote_codeTrue )使用4位量化后1.8B模型可能只需要2-3GB的显存这让很多消费级显卡如RTX 3060 12GB也能轻松运行。代价是输出质量可能有细微下降但对于Demo和许多应用来说完全可接受。注意量化加载可能需要额外安装bitsandbytes库且对CUDA版本有要求。如果安装失败可以尝试寻找对应你CUDA版本的预编译轮子文件。6.2 利用CPU和磁盘进行混合加载如果你的GPU显存实在太小连量化后的模型都放不下可以使用device_map参数进行更精细的控制将部分模型层卸载到CPU甚至磁盘。model AutoModelForCausalLM.from_pretrained( model_dir, torch_dtypetorch.float16, device_map{ : cuda:0, # 默认设备是GPU 0 model.layers.0: cpu, # 把第0层放到CPU model.layers.1: cpu, # ... 可以指定更多层到CPU lm_head: cuda:0 # 输出头放回GPU }, offload_folder./offload, # 如果使用offload_state_dict指定临时文件夹 trust_remote_codeTrue )这种方法会显著降低推理速度因为数据需要在CPU和GPU之间传输。这通常作为最后的手段。6.3 推理速度优化技巧使用torch.compilePyTorch 2.0如果模型支持使用model torch.compile(model)可以加速推理。首次运行会有编译开销后续运行会变快。测试前请先确认你的PyTorch版本和CUDA环境支持。调整max_new_tokens这是影响生成时间最直接的参数。在满足需求的前提下尽量设小。启用KV缓存model.generate默认会启用键值缓存Key-Value Cache这能避免在生成每个新token时重新计算之前所有token的注意力大幅提升生成效率。确保你没有错误地禁用它。批量推理Batch Inference如果你需要处理多个相似的查询可以将它们组成一个batch一次性输入模型这比循环处理每个查询效率高得多。但要注意这会增加显存占用。7. 从Demo到应用可能的延伸方向与思考跑通Demo只是起点。在这个过程中我们实际上已经摸到了大模型应用开发的几个关键环节环境配置、模型加载、提示词编写、参数调优、工具集成。基于此你可以尝试更多微调Fine-tuning如果你有特定领域的数据如客服对话、法律条文、医疗报告可以用LoRA等高效微调方法让InternLM2-1.8B在你专属的任务上表现更好。llamafactory、xtuner等工具可以降低微调门槛。构建复杂智能体除了计算器、时间可以为模型集成更多工具如网络搜索让模型能获取实时信息。数据库查询让模型能操作内部业务数据。API调用让模型能操作其他软件系统。 设计一个能协调多个工具完成复杂任务的智能体是当前应用的热点。探索不同模型InternLM2系列还有7B、20B等更大尺寸的模型能力更强。也可以在Hugging Face上尝试其他优秀的开源模型如Qwen、ChatGLM、Yi等感受不同模型的特点。部署为API服务使用FastAPI或vLLM等推理引擎将模型封装成HTTP API供其他应用程序调用。vLLM特别擅长高并发下的推理优化。回过头看这节“趣味Demo”之旅远不止于“趣味”。它是一把钥匙打开了本地部署和把玩大模型的大门。你亲手解决了环境问题、理解了模型加载、调试了参数、集成了工具甚至窥探了智能体的内部机制。这些经验比任何纸上谈兵的理论都来得扎实。下次当有人再谈论大模型如何神奇时你可以淡定地说“哦我在自己电脑上跑过一个还教它用了计算器。” 这份亲手实践得来的底气正是技术人最宝贵的财富。