很多人对“7天学会 AI 大模型”这句话的第一反应是怀疑:Transformer 的论文都读不完,预训练要几千张卡,7 天能学会什么?但如果你把目标从“训练一个大模型”调整为“能独立完成一个大模型应用的选型、开发和上线”,7 天其实是一个相对合理的周期。这两件事的难度差了一个数量级,而市面上大量教程把它们混在一起讲,导致初学者既看不到终点,也找不到起点。
真正卡住大多数人的不是数学,而是地图缺失。你不知道 Transformer、预训练、微调、提示工程、RAG、Agent 这六个词分别处在大模型技术栈的哪一层,不知道哪些是必须亲手做的,哪些是理解原理即可,于是学着学着就陷进了某个局部细节里。比如花三天啃注意力公式的推导,却始终没跑通过一次模型推理;或者一上来就搭 RAG,结果连“为什么检索到的内容模型不用”都判断不了。
这篇文章要解决的就是这个问题。我会按“先建立全景地图,再逐层下钻”的顺序,把这条学习路线拆成九个部分:技术栈分层、环境准备、Transformer 核心机制、预训练与微调、提示工程、RAG、Agent,最后给出可执行的 7 天排期和排错清单。每一层都配可运行的代码,代码不追求覆盖全部细节,但保证你复制下来能跑通,跑通之后能看懂。
读完之后你应该能拿到三个东西:一张能指导后续学习的技术地图、一套能跑起来的最小代码基线、一份知道该在哪里停下来的判断标准。对于准备转方向的后端开发、要做 AI 功能的产品团队,以及被“大模型”这个词反复轰炸但一直没动手的工程师,这些比再多论文解读都有用。
1. 7天学会大模型,这句话到底该怎么理解
先把预期摆正。大模型领域的岗位至少分三类:预训练算法工程师、微调与对齐工程师、应用开发工程师。前两类岗位的门槛在算力和数据,不是个人在七天内能跨过去的;第三类岗位的门槛在工程能力和对模型行为的理解,这才是绝大多数人真正要走的路。
所以“7 天学会”的合理定义是:你能独立完成一个基于开源模型或 API 的 AI 应用,包括模型选型、提示设计、检索增强、工具调用、效果评测和基本的问题排查。至于从零预训练一个 7B 模型,那是团队级任务,个人学习阶段只需要理解它的流程和代价,不需要亲手复现。
这个定位带来一个很重要的取舍:不要在数学推导上过度停留,但必须理解每一步在做什么。你不需要手推 softmax 的梯度,但必须知道为什么注意力要做缩放、为什么长上下文会变慢、为什么微调会遗忘。这些认知直接决定你在工程中怎么选参数、怎么排查效果问题。
另一个常见误区是把“会用 API”当成“会大模型”。调用一次 chat completions 接口确实五分钟就能学会,但当模型输出不稳定、检索结果不相关、工具调用参数错乱时,能定位问题的人靠的是对底层机制的理解,而不是对 SDK 的熟悉程度。这也是本文后面每个章节都要落到“为什么”的原因。
明确了这一点,接下来的学习才有节奏可言。下面这张技术栈地图,是整个学习路线的骨架。
2. 大模型技术栈全景:五层结构先建立地图
大模型相关的概念之所以让人混乱,是因为它们分布在完全不同的抽象层级上,却被并列着讲。用一张分层表把它们摆开,理解成本会立刻下降。
| 层级 | 核心内容 | 谁在负责 | 学习者该做到什么程度 |
|---|---|---|---|
| 第一层:模型结构 | Transformer、注意力、位置编码、归一化 | 模型架构研究者 | 理解原理,能手写最小注意力 |
| 第二层:预训练 | 语料、分词、下一词预测、分布式训练 | 大厂与头部团队 | 理解流程与代价,不亲手做 |
| 第三层:微调与对齐 | SFT、LoRA、偏好对齐、量化 | 算法工程师 | 能跑通 LoRA 微调并评估效果 |
| 第四层:提示工程 | 指令设计、结构化输出、少样本示例 | 所有应用开发者 | 必须熟练,成本最低收益最高 |
| 第五层:应用架构 | RAG、Agent、工具调用、评测与监控 | 应用开发工程师 | 主战场,需完整落地一遍 |
从这张表能看出一个关键判断:越往下,门槛越高但可学内容越少;越往上,门槛越低但工程量越大。大部分人的时间应该花在第四层和第五层,第三层做到“能跑通、能评估”,第一层和第二层做到“能讲清楚”。
这里有个容易被忽略的细节:这五层不是严格的流水线,而是相互影响的。比如你在第五层做 RAG 时发现模型总是忽略检索内容,问题可能出在第四层的提示结构,也可能出在第三层微调时把模型的指令遵循能力训坏了。能在这几层之间来回定位,才是真正“学会”的标志。
再补充一个判断标准,用来检验自己是否建立了地图:随便给你一个新闻标题,比如“某公司发布支持 100 万上下文的新模型”,你能不能立刻说出它影响的是哪一层、对应用开发者意味着什么、有什么代价。如果说不出来,说明地图还没建好,先别急着往下写代码。
3. 环境准备与前置条件
这一节的目的不是让你装一堆东西,而是装到“刚好够用”。大模型生态的依赖冲突非常常见,尤其是 transformers、torch、CUDA 三者的版本匹配,一开始装复杂了,后面排错成本会翻倍。
先说硬件。纯学习阶段,一台带独立显卡(显存 8GB 以上)的笔记本就够跑通 0.5B 到 1.5B 级别的模型推理和 LoRA 微调;如果没有独立显卡,用 CPU 跑推理也可以,只是慢,微调建议直接用云上按量计费的实例,跑完就释放。做 RAG 和 Agent 的部分对本地算力要求很低,主要靠 API 或小型向量模型。
软件侧的基础环境如下。具体版本请以你实际项目的依赖为准,本文重点演示通用思路,不建议直接照抄版本号到生产项目里。
# 文件路径:环境准备命令 # 建议使用 Python 3.10 或 3.11,新建独立虚拟环境 python -m venv llm-env source llm-env/bin/activate # Windows 用 llm-env\Scripts\activate # 基础依赖:先装 torch,再装上层库,顺序很重要 pip install --upgrade pip pip install torch --index-url https://download.pytorch.org/whl/cu121 # 模型与微调 pip install transformers accelerate peft datasets # 检索与向量 pip install sentence-transformers numpy # 应用层 pip install openai这里有个真实的坑:很多人先pip install transformers再装 torch,结果 pip 自动拉了一个和本机 CUDA 不匹配的 CPU 版 torch,之后加载模型报Torch not compiled with CUDA enabled。正确顺序是先确定 torch 版本,再装其他库,因为 transformers 本身对 torch 的版本要求是区间,不会强行覆盖已装的版本。
环境验证用下面这几行,跑通说明基础链路没问题:
# 文件路径:check_env.py import torch import transformers print("torch 版本:", torch.__version__) print("CUDA 可用:", torch.cuda.is_available()) if torch.cuda.is_available(): print("显卡型号:", torch.cuda.get_device_name(0)) print("显存(GB):", round(torch.cuda.get_device_properties(0).total_memory / 1024**3, 1)) print("transformers 版本:", transformers.__version__)如果CUDA 可用是 False,先别急着往下走,回到上一步确认 torch 的安装源。这一步没解决,后面所有模型加载都会退化成 CPU 推理,慢到你会误以为是代码写错了。
还有一个前置条件是账号与密钥。后面 Agent 部分需要调用模型 API,提前准备好 API Key,并通过环境变量注入,不要硬编码在代码里。这是安全底线,不是可选优化。
# 文件路径:设置环境变量(Linux / macOS) export OPENAI_API_KEY="你的密钥" export OPENAI_BASE_URL="你的接口地址" # Windows PowerShell # $env:OPENAI_API_KEY="你的密钥"4. Transformer:注意力机制到底在算什么
Transformer 被讲得玄乎,其实核心问题只有一个:在把一句话喂给模型时,每个词应该“看”哪些其他词。传统 RNN 是从左到右一个个传,长距离依赖会衰减;卷积是固定窗口,看不到远距离。自注意力换了个思路,让每个词直接和句子里所有词算一遍相关性,然后按相关性加权求和。
“算相关性”的方式就是 Q、K、V。Query 是“我在找什么”,Key 是“我能提供什么”,两者点积得到匹配分数,再把分数归一化成权重,最后对 Value 加权求和。这套机制的好处是可以并行计算,坏处是复杂度随序列长度平方增长——这也是长上下文推理贵、慢的根本原因,不是工程没优化好。
多头注意力是把这一套并行做多份,每份关注不同的模式。有的头可能关注语法搭配,有的关注指代关系。理解到这一层就够了,具体每个头学到了什么,目前没有统一解释。
下面是一个可直接运行的最小实现,去掉工程优化,只保留计算逻辑:
# 文件路径:attention_demo.py import math import torch import torch.nn as nn class SelfAttention(nn.Module): """最小可运行的多头自注意力实现""" def __init__(self, d_model: int, n_heads: int): super().__init__() assert d_model % n_heads == 0, "d_model 必须能被 n_heads 整除" self.d_model = d_model self.n_heads = n_heads self.head_dim = d_model // n_heads # 一次性生成 Q、K、V,比三个独立线性层更省显存 self.qkv = nn.Linear(d_model, d_model * 3, bias=False) self.out = nn.Linear(d_model, d_model, bias=False) def forward(self, x, mask=None): b, t, _ = x.shape qkv = self.qkv(x) # [b, t, 3*d_model] q, k, v = qkv.chunk(3, dim=-1) # 各自 [b, t, d_model] # 拆头:[b, t, n_heads, head_dim] -> [b, n_heads, t, head_dim] q = q.view(b, t, self.n_heads, self.head_dim).transpose(1, 2) k = k.view(b, t, self.n_heads, self.head_dim).transpose(1, 2) v = v.view(b, t, self.n_heads, self.head_dim).transpose(1, 2) # 缩放点积:除以 sqrt(head_dim) 防止点积随维度增大而爆炸 scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim) if mask is not None: scores = scores.masked_fill(mask == 0, float("-inf")) attn = torch.softmax(scores, dim=-1) out = torch.matmul(attn, v) # [b, n_heads, t, head_dim] out = out.transpose(1, 2).contiguous().view(b, t, self.d_model) return self.out(out), attn if __name__ == "__main__": torch.manual_seed(0) x = torch.randn(2, 5, 32) # batch=2, 序列长度=5, 维度=32 layer = SelfAttention(d_model=32, n_heads=4) y, weights = layer(x) print("输出形状:", y.shape) # 期望 [2, 5, 32] print("注意力权重形状:", weights.shape) # 期望 [2, 4, 5, 5] print("权重每行求和(应接近 1):", weights.sum(-1)[0, 0])运行python attention_demo.py,如果输出形状是[2, 5, 32]、权重每行之和接近 1,说明注意力计算是对的。这里最值得关注的是缩放那一步,很多人复现时漏掉/ math.sqrt(head_dim),小模型上看起来还能训,维度一上去梯度就崩。
至于完整的 Transformer 块,还要加上残差连接、层归一化和前馈网络。残差是为了让梯度能跨层传回去,层归一化是为了稳定数值分布,前馈网络提供非线性变换能力。现在的开源模型普遍用 Pre-Norm 结构加 RMSNorm,比原始论文的 Post-Norm 更好训,这个改动看起来小,但在大模型训练稳定性上是关键。
理解完这一节,你再看模型配置文件里的num_hidden_layers、num_attention_heads、hidden_size,就能对应上算力和显存的消耗来源了。
5. 预训练与微调:模型能力的两次塑造
预训练做的事情用一句话概括:给模型海量文本,让它反复做“猜下一个词”的任务。这个任务听起来太简单,但为了猜得准,模型被迫学会了语法、事实、推理模式,甚至是代码结构。这就是所谓的“压缩即理解”。
预训练的成本极高,一次完整的训练动辄数千张加速卡、数月时间。所以个人学习阶段不需要复现,只需要理解三个关键点:分词器决定了模型怎么切文本,直接影响力;语料质量比数量更重要;训练目标决定了模型的能力边界。这三条在做应用时都用得上,比如你发现模型对中文数字处理很差,很可能是分词器把它切碎了。
微调才是个人和小团队真正会动手的部分。全参数微调需要保存所有参数的梯度和优化器状态,7B 模型在单卡上基本不可行;LoRA 的做法是冻结原模型,只在旁边挂两个小矩阵做低秩分解,训练完成后把增量合并回原权重或单独保存。这样可训练参数能降到原来的千分之一级别,单张消费级显卡就能跑。
下面这段代码演示如何给一个开源小模型挂上 LoRA,并打印可训练参数比例:
# 文件路径:lora_setup.py from transformers import AutoModelForCausalLM, AutoTokenizer from peft import LoraConfig, TaskType, get_peft_model # 模型名请替换为你本地已下载或可访问的模型 model_name = "Qwen/Qwen2.5-0.5B-Instruct" tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype="auto", device_map="auto", trust_remote_code=True, ) lora_config = LoraConfig( task_type=TaskType.CAUSAL_LM, r=8, # 低秩维度,越大容量越强、显存越高 lora_alpha=16, # 缩放系数,常取 r 的两倍 lora_dropout=0.05, target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], bias="none", ) model = get_peft_model(model, lora_config) model.print_trainable_parameters()print_trainable_parameters()会输出类似“trainable params: 1.5M / all params: 500M”的结果。如果可训练比例在 0.1% 到 1% 之间,说明配置正常。这里最关键的参数是target_modules,它决定给哪些层挂 LoRA。只挂q_proj、v_proj是最省显存的经典做法,挂满四个投影层效果更好但显存翻倍。新手最容易踩的坑是照抄别人博客里的模块名,结果模型结构不同,直接报找不到模块,正确的做法是先打印模型结构确认层名。
微调数据的格式也很容易出错。对话模型通常需要把样本拼成带角色标记的字符串,并且只对回答部分计算损失。如果整段都算损失,模型会连用户提问也学着生成,效果会明显变差。这部分建议直接用datasets配合DataCollatorForLanguageModeling或对话模板工具处理,不要手工拼字符串。
最后一个必须建立的认知:微调不是万能的,它擅长改变风格和格式,不擅长注入新知识。想让模型回答你公司内部文档里的问题,正确路径是 RAG,不是微调。这个判断能帮你省下大量无效算力。
6. 提示工程:成本最低的效果杠杆
提示工程常被低估,因为它不需要训练、不消耗算力,看起来“不够技术”。但在实际项目里,同一个模型,提示结构改一改,效果差距能到 30% 以上。这不是玄学,原因是模型的输出分布高度依赖输入的上下文结构。
一个能稳定工作的提示通常包含四个部分:角色与边界、任务描述、输入数据、输出格式约束。缺任何一块都会带来不确定性。比如只写“总结这段文字”,模型会自由决定长度和风格;加上“用三句话总结,只保留事实,不要评价”,输出立刻可控。
结构化输出是工程里最实用的技巧。不要指望模型“自然地”返回 JSON,要在提示里给出明确的 schema,并且在代码侧做校验和重试。
# 文件路径:structured_prompt.py from openai import OpenAI import json import os client = OpenAI( api_key=os.environ["OPENAI_API_KEY"], base_url=os.environ.get("OPENAI_BASE_URL"), ) SYSTEM_PROMPT = """你是一个工单分类助手。请严格按以下 JSON 结构输出,不要输出任何额外文字: { "category": "从[账号, 支付, 功能异常, 其他]中选择一个", "priority": "从[高, 中, 低]中选择一个", "summary": "不超过 30 字的问题摘要" }""" def classify_ticket(text: str) -> dict: resp = client.chat.completions.create( model="你的模型名称", temperature=0, # 分类任务用 0,降低随机性 messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": text}, ], ) raw = resp.choices[0].message.content.strip() # 防御性处理:模型可能包了 markdown 代码块 if raw.startswith("```"): raw = raw.strip("`") raw = raw.split("\n", 1)[-1] if "\n" in raw else raw return json.loads(raw) if __name__ == "__main__": print(classify_ticket("我付款成功但是订单一直是待支付状态,已经两个小时了"))这段代码里有两个工程细节值得注意。第一,temperature=0不是完全确定性,只是降低随机性,所以下游必须有校验。第二,解析 JSON 前要处理 markdown 代码块包裹的情况,这是实际项目里最高频的解析失败原因。更稳的做法是使用模型厂商提供的结构化输出或函数调用能力,让约束在解码层生效。
少样本示例(few-shot)是另一个高性价比手段。给两三个输入输出示例,比写一大段规则描述更有效。但示例要覆盖边界情况,如果只给标准案例,模型遇到异常输入就会乱猜。经验做法是:一个典型正例、一个易混淆的负例、一个边界例。
提示工程真正难的地方不是写,而是版本管理和评测。改了提示之后效果是变好还是变坏,必须有固定的测试集来验证,否则就是凭感觉调参。这一点会在最后一节的工程建议里展开。
7. RAG:把企业知识接进模型
RAG 要解决的问题很具体:模型的知识停留在训练数据的时间点,而且不知道你公司的内部文档。微调解决不了这个问题,因为微调是在调整行为模式,不是往参数里塞一份可检索的文档库。
RAG 的流程分两段。离线阶段把文档切块、向量化、存进向量库;在线阶段把用户问题向量化,检索最相似的几个块,拼进提示词一起送给模型。听起来简单,但每一段都有大量细节决定成败。
最典型的失败场景是“检索到了但模型没用好”。原因往往出在切块策略上:按固定字数切会把一句话切断,语义不完整;切得太大又会引入无关内容,稀释关键信息。比较稳的做法是按语义边界切(段落、标题),并保留一定重叠,让跨块的上下文不断裂。
下面是一个不依赖向量数据库、用 numpy 做余弦相似度的最小 RAG 实现,方便你先把流程跑通,再替换成生产组件:
# 文件路径:mini_rag.py import numpy as np from sentence_transformers import SentenceTransformer # 中文小模型,首次运行会自动下载 encoder = SentenceTransformer("BAAI/bge-small-zh-v1.5") # 模拟知识库:实际项目里从文件、数据库或接口加载 DOCUMENTS = [ "退款申请需在订单完成后 7 天内提交,超过时限需联系人工客服处理。", "发票开具支持电子普票和专用发票,专用发票需提供纳税人识别号和开户行信息。", "会员等级分为普通、白银、黄金三档,黄金会员享受优先客服通道。", "系统维护时间为每周三凌晨 2 点到 4 点,期间下单功能不可用。", ] doc_vectors = encoder.encode(DOCUMENTS, normalize_embeddings=True) def retrieve(query: str, top_k: int = 2): q_vec = encoder.encode([query], normalize_embeddings=True)[0] # 向量已归一化,点积即余弦相似度 scores = doc_vectors @ q_vec idx = np.argsort(-scores)[:top_k] return [(DOCUMENTS[i], float(scores[i])) for i in idx] def build_prompt(query: str, contexts): context_text = "\n".join(f"[{i+1}] {c}" for i, (c, _) in enumerate(contexts)) return f"""请仅根据下面的资料回答问题。如果资料中没有答案,直接回答“资料中未提及”,不要自行推测。 资料: {context_text} 问题:{query} """ if __name__ == "__main__": question = "我订单完成十天了还能退款吗?" ctx = retrieve(question, top_k=2) for text, score in ctx: print(f"相似度 {score:.4f} -> {text}") print("-" * 40) print(build_prompt(question, ctx))运行之后你会看到检索结果和拼装好的提示词。这里有几个设计点值得展开。第一,normalize_embeddings=True让向量长度为 1,点积直接等于余弦相似度,省掉除法。第二,提示里明确写了“资料中没有答案就直说”,这是抑制幻觉最有效的一句话。第三,用编号标记资料块,方便模型引用,也方便你在日志里追溯是哪一条起了作用。
生产环境还需要补三件事:一是混合检索,向量检索擅长语义,关键词检索擅长专有名词,两者结合效果更稳;二是重排序,先粗召回 20 条再精排取前 3 条,能显著提升准确率;三是引用溯源,让模型输出时带上资料编号,前端可以展示来源,用户信任度会高很多。
还有一个常被忽略的点:RAG 的效果上限由检索决定,不是由模型决定。检索不到正确内容,换再强的模型也没用。所以调优顺序应该是先看召回率,再看生成质量。
8. Agent:从回答问题到完成任务
RAG 让模型“知道得更多”,Agent 让模型“能做事”。区别在于 Agent 会自主决定调用哪个工具、传什么参数、根据返回结果决定下一步。这个能力的技术基础是函数调用(function calling / tool calling):你把可用工具的 schema 告诉模型,模型输出结构化的调用请求,你的代码执行后把结果回传,模型继续推理。
理解 Agent 的关键是理解它的循环结构:思考、行动、观察、再思考。这个循环必须设上限,否则模型可能陷入死循环,反复调用同一个工具。生产环境还要加上超时、重试和人工兜底。
下面是一个完整的工具调用示例,包含工具定义和执行循环:
# 文件路径:mini_agent.py import json import os from openai import OpenAI client = OpenAI( api_key=os.environ["OPENAI_API_KEY"], base_url=os.environ.get("OPENAI_BASE_URL"), ) # 工具的真实实现 def get_order_status(order_id: str) -> str: fake_db = { "A1001": "已发货,预计明天送达", "A1002": "待支付,剩余支付时间 30 分钟", } return fake_db.get(order_id, "未找到该订单") def apply_refund(order_id: str, reason: str) -> str: # 真实场景必须做权限校验和幂等控制 return f"订单 {order_id} 的退款申请已提交,原因:{reason}" TOOL_IMPL = { "get_order_status": get_order_status, "apply_refund": apply_refund, } # 给模型看的工具描述,字段名和参数类型必须准确 TOOLS = [ { "type": "function", "function": { "name": "get_order_status", "description": "根据订单号查询订单当前状态", "parameters": { "type": "object", "properties": { "order_id": {"type": "string", "description": "订单号,例如 A1001"} }, "required": ["order_id"], }, }, }, { "type": "function", "function": { "name": "apply_refund", "description": "为指定订单提交退款申请", "parameters": { "type": "object", "properties": { "order_id": {"type": "string", "description": "订单号"}, "reason": {"type": "string", "description": "退款原因"}, }, "required": ["order_id", "reason"], }, }, }, ] def run_agent(user_input: str, max_steps: int = 5): messages = [ {"role": "system", "content": "你是电商客服助手,可以查询订单和提交退款。操作前先确认订单状态。"}, {"role": "user", "content": user_input}, ] for step in range(max_steps): resp = client.chat.completions.create( model="你的模型名称", messages=messages, tools=TOOLS, temperature=0, ) msg = resp.choices[0].message messages.append(msg) if not msg.tool_calls: return msg.content for call in msg.tool_calls: name = call.function.name args = json.loads(call.function.arguments) print(f"[步骤 {step+1}] 调用 {name} 参数 {args}") try: result = TOOL_IMPL[name](**args) except Exception as e: result = f"工具执行失败:{e}" messages.append({ "role": "tool", "tool_call_id": call.id, "content": str(result), }) return "已达到最大步数限制,请人工介入处理" if __name__ == "__main__": print(run_agent("帮我查一下订单 A1001 到哪了,如果还没签收就申请退款,原因是买错了"))这段代码跑起来会看到完整的“查询、判断、调用”过程。几个工程要点必须强调:max_steps不能省,这是防止成本失控的第一道闸;工具执行必须 try-except,任何异常都要转成文本回传给模型,否则整个循环会中断;写操作的幂等性必须自己做,不要指望模型不重复调用。
还有一个安全边界问题:凡是涉及资金、删除、权限变更的工具,都不应该让模型直接调用,正确做法是让模型生成待确认的操作,由用户或审批流程确认后执行。这是底线,不是性能优化。
Agent 和 RAG 经常组合使用:检索负责提供事实,工具负责执行动作,模型负责编排。组合之后系统的复杂度会明显上升,所以建议先把 RAG 单独跑稳,再叠加 Agent。
9. 7天学习路线、常见问题与最佳实践
把上面的内容排进七天,节奏大概是这样的。第一天建立地图,把技术栈五层和关键术语过一遍,跑通环境检查脚本;第二天手写注意力并跑通最小推理,理解 tokenizer 的作用;第三天理解预训练流程和代价,不做复现;第四天跑通 LoRA 配置并打印可训练参数,理解微调适用边界;第五天集中练提示工程,把结构化输出和少样本示例各写一遍;第六天完整跑通最小 RAG,并尝试调整切块和 top_k 观察变化;第七天跑通 Agent 工具调用,然后做一次整体串讲。
这个排期的核心原则是每天必须有一次“跑通”,而不是每天读一堆材料。跑通带来的确定性,是抵抗焦虑最有效的东西。
下面是这段时间最容易遇到的问题和排查方向:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 模型加载报 CUDA 不可用 | 装到了 CPU 版 torch | 执行环境检查脚本看is_available() | 卸载后按 CUDA 版本重装 torch |
| 加载模型时显存不足 OOM | 模型太大或未量化 | 打印模型参数量和显存占用 | 换更小模型,或加载时使用半精度、4bit 量化 |
| LoRA 报找不到 target_modules | 模型结构层名与配置不匹配 | 打印model结构查看线性层命名 | 按实际层名修改配置 |
| 微调后模型输出变乱 | 损失计算覆盖了用户输入部分 | 检查数据拼接与 labels 构造 | 只对回答部分计算损失 |
| 提示返回的 JSON 解析失败 | 输出被 markdown 包裹或有多余文字 | 打印原始返回内容 | 增加清洗逻辑,或改用结构化输出能力 |
| RAG 答非所问 | 检索结果不相关或提示未约束 | 打印 top_k 内容和相似度分数 | 调整切块粒度、换向量模型、加关键词混合检索 |
| 模型忽略检索内容 | 上下文过长导致关键信息被稀释 | 检查拼进提示的资料长度 | 减少 top_k,加“仅根据资料回答”约束 |
| Agent 反复调用同一工具 | 缺少停止条件或工具返回信息不足 | 打印每步调用参数和返回 | 加最大步数限制,让工具返回明确的成功/失败状态 |
| 工具调用参数类型错误 | schema 描述不清或必填项缺失 | 对比模型输出与实际 schema | 补全 description 和 required 字段 |
| 接口调用超时或限流 | 并发过高或未做重试 | 查看返回状态码与错误信息 | 加指数退避重试,控制并发 |
除了排错,还有几条值得在项目早期就落实的工程习惯。提示词要进版本管理,不要散落在代码字符串里,改一次就要能追溯。建立小规模评测集,哪怕只有 50 条真实样本,也比凭感觉判断强得多,每次改提示或换模型都跑一遍。日志要记录完整链路,包括原始问题、检索到的内容、模型输入、模型输出和耗时,出问题时这些是唯一能还原现场的东西。
成本控制也要提前想。上下文长度是最主要的成本来源,检索 top_k 从 5 降到 3 可能效果不变但成本降四成。缓存高频问题的结果、对简单问题走小模型、对复杂问题才走大模型,这些分层策略在真实业务里收益很明显。
最后是安全边界。所有涉及用户数据的内容,在送进模型前要做脱敏;所有模型输出的内容,在展示给用户前要做校验;所有写操作,都要有权限控制和操作审计。模型不是可信执行环境,把它当成一个能力很强但需要监督的协作方,这个心态能帮你避开绝大多数事故。
回到最初那个问题:7 天能学会大模型吗。如果你的目标是独立做出一个能用的 AI 应用,答案是能,前提是把精力放在第四层和第五层,把第一层和第二层理解到能讲清楚为止。真正的分水岭不在你读了多少论文,而在你有没有跑通过一条完整链路,并且在它出问题时知道从哪一层开始查。这条链路跑通一次之后,后面所有的模型更新、框架变化,都只是在同一张地图上换零件。