做这行的人最近两年应该都有同一种体感:以前跟别人聊"我在做算法",对方会问是推荐还是风控;现在说"我在做大模型",对方十有八九会追问一句"是调 API 还是自己训?"。这个追问其实很精准,它把大模型开发这件事的核心分野一下子就戳出来了——调用别人封装好的接口,和自己把模型跑起来、改得动、接得上业务,中间隔着的不是一行代码,而是一整套工程认知。这份入门笔记就是写给那批想跨过这道坎的人:你可能是有几年后端或前端经验的工程师,可能是刚接触 AI 方向的学生,也可能是产品或者运维岗想搞清楚这套东西到底吃多少资源、链路有多长。我会从最实际的问题切入——显存怎么算、模型怎么挑、推理框架怎么选、微调到底改了什么、RAG 为什么比微调更适合大多数场景——把一条能真正跑通的学习路线摊开讲,尽量让每一步都有可验证的命令和参数,而不是停留在概念介绍。
1. 大模型开发到底在开发什么
很多人一上手就去啃 Transformer 论文,啃完发现还是不知道从哪下手。问题出在把"研究"和"开发"混成了一件事。做研究关心的是为什么这种结构能work,做开发关心的是给定一个已经开源的权重文件,我怎么把它变成能对外提供服务、能接自家数据、能控成本的东西。这两件事需要的能力重叠度其实不高,入门阶段优先解决后者,收益更大。
1.1 从"用模型"到"造应用"的三层能力模型
我把入门路径粗分成三层,一层叠一层,跳级学容易出现"每个词都认识但串不起来"的状况。
第一层是推理部署。核心目标只有一个:让模型在你自己的机器或者服务器上,稳定地把一段输入变成一段输出。这一层要搞明白的东西包括显存占用估算、量化格式的差异、推理引擎的选择、以及并发请求下吞吐和延迟的取舍。这一层做通了,你至少拥有了一个私有的、可控成本的对话或者生成服务。
第二层是能力改造,也就是围绕模型做上下文增强或者参数微调。前者是把外部知识在推理时塞进提示里,也就是 RAG;后者是拿自己的数据去调整模型的一部分参数,也就是微调。这两条路的适用边界非常清晰,选错了会浪费大量时间:知识更新频繁、要求可溯源,选 RAG;风格、格式、特定任务的表达习惯要固化,选微调。
第三层是应用工程。到了这一层,模型本身反而成了链路中的一环,你要处理的是提示词管理、多轮会话状态、工具调用、缓存、限流、失败重试、评测。真正落到业务里的项目,工作量的大头往往在这一层,而不是模型本身。
1.2 入门者最容易走偏的三个方向
第一个坑是把时间全花在预训练上。从零预训练一个可用的基座模型,需要的数据量和算力投入对个人和小团队来说是不现实的,而且即使训出来,效果大概率也不如现成的开源权重。入门阶段应该把"训练"限定在微调这个尺度上。
第二个坑是迷信"参数越大越好"。一个 70B 的模型在单张消费级卡上跑起来,要么量化到精度崩掉,要么速度慢到没法做交互。实际项目里,7B 到 14B 这个量级的模型配上好的提示工程和知识库,在很多垂直任务上的表现完全够用,而且迭代速度快几十倍。
第三个坑是跳过评测。调完提示词、微调完模型,感觉"好像变好了",但没有一套固定的测试集做对比,过两周你根本说不清哪个版本更靠谱。从第一天起就准备二十到五十条有代表性的输入输出样例,作为回归测试基准,这个习惯能省下大量返工。
2. 本地部署大模型,显存到底该按什么算
硬件是绕不过去的现实问题。网上关于配置的讨论经常是"我这卡能跑吗"这种问法,但更准确的问法应该是"我要在什么精度、多长上下文、多大并发下跑多大的模型"。这三个变量一变,结论完全不同。
2.1 参数量、精度、显存三者的换算逻辑
先记住一个粗糙但好用的公式:
推理显存 ≈ 权重占用 + KV Cache + 框架开销
权重占用的算法很直接:参数量乘以每参数字节数。FP16 每参数 2 字节,INT8 是 1 字节,4bit 量化大约 0.5 字节。所以一个 7B 模型,FP16 下光权重就要 14GB 左右,4bit 量化后降到 3.5GB 上下。这就解释了一个常见现象:同一张 8GB 显存的卡,跑 FP16 的 7B 模型必爆,跑 4bit 量化的就很宽裕。
KV Cache 是很多人忽略的部分,它随着上下文长度和并发数线性增长。计算公式是:
KV Cache 字节数 = 2 × 层数 × KV头数 × head_dim × 序列长度 × 并发数 × 精度字节数拿一个典型的 8B 级别模型举例:32 层、8 个 KV 头(用了 GQA 分组查询注意力)、head_dim 为 128、上下文 4096、并发 1、FP16。代入算一下,2×32×8×128×4096×2 ≈ 512MB。看起来不多,但如果把上下文拉到 32K 并发开到 8,这个数字会膨胀到 32GB 以上,直接超过权重本身。所以做长文本场景时,KV Cache 往往才是真正的瓶颈。
2.2 预算有限时的三条现实路线
预算在两三千块这个量级,基本只能走 CPU + 内存的路线,或者用二手显卡。CPU 推理的速度取决于内存带宽,经验值大概是几 token 每秒,做批处理任务可以,做实时对话会很煎熬。
预算在八千到两万,可以上 12GB 到 24GB 显存的卡。这个区间是我最推荐的入门配置:24GB 显存能跑 4bit 量化的 14B 模型加中等长度上下文,或者 7B 模型 FP16 加较长上下文,选择余地很大。
再往上就是多卡或者专业卡的路子。这时候考虑的重点就从"能不能跑"变成"每百万 token 的成本是多少",属于另一个话题了。
注意:显存估算只解决"装得下",不解决"跑得快"。同样的显存占用下,不同推理引擎的吞吐可能差三到五倍,这个差异在部署章节里细说。
3. 推理框架选型:Ollama、llama.cpp、vLLM 分别适合谁
选框架这件事,很多人是看哪篇文章火就用哪个,结果在错误场景里用错工具,然后得出"这东西不好用"的结论。实际上这几个主流方案的设计目标差异极大,先把场景想清楚再选,能少走很多弯路。
3.1 三款主流推理方案对照
| 方案 | 核心定位 | 硬件适配 | 并发能力 | 上手难度 | 典型场景 |
|---|---|---|---|---|---|
| Ollama | 开箱即用的本地运行时 | CPU / 各类 GPU | 弱 | 极低 | 个人实验、桌面助手、原型验证 |
| llama.cpp | 极致轻量的 C++ 推理 | CPU / Metal / CUDA | 弱到中 | 中 | 无 GPU 环境、边缘设备、嵌入式 |
| vLLM | 服务化高吞吐推理 | 以 NVIDIA GPU 为主 | 强 | 中高 | 线上服务、批量任务、多用户共享 |
Ollama 的价值在于把模型下载、格式转换、量化选择、服务启动这些步骤压缩成了一条命令,代价是它对并发和高负载场景基本没做优化。llama.cpp 的强项是量化格式丰富、内存占用控制得好,在没有独立显卡的机器上几乎是唯一解。vLLM 的核心卖点是 PagedAttention,它借鉴操作系统虚拟内存分页的思路来管理 KV Cache,把显存碎片降到很低,因此在多并发请求下吞吐量优势明显。
3.2 用 Ollama 跑通第一次本地推理
这是最快能看到结果的一条路,适合完全没接触过的同学建立信心。
# 安装完成后拉取模型并直接进入交互 ollama run qwen2.5:7b # 查看本地已有模型 ollama list # 以服务方式常驻,默认监听 11434 端口 ollama serve服务起来之后,可以直接用 HTTP 接口调用:
curl http://localhost:11434/api/generate -d '{ "model": "qwen2.5:7b", "prompt": "用三句话解释什么是注意力机制", "stream": false }'跑通之后建议做两件事。一是把OLLAMA_NUM_PARALLEL和OLLAMA_MAX_LOADED_MODELS这两个环境变量调一调,前者控制并发处理数,后者控制同时驻留的模型数量,理解了它们你就能解释"为什么第二个请求进来时慢了一大截"。二是打开任务管理器或者nvidia-smi盯着显存看一遍,把前面算出来的理论值和实测值对一下,这个对照过程比看十篇文章都管用。
3.3 vLLM 部署与"并发请求"到底指什么
很多人在查资料时会撞上"大模型并发请求"这个词,理解得比较模糊。简单说,并发请求就是同一时刻有多个请求都在等着模型输出。因为大模型是逐 token 生成的,一个请求往往要占用几百毫秒到几秒,如果串行处理,第二个用户就得排队等到天荒地老。所以服务化部署必须解决"多个请求交替推进"的问题。
不同框架的解法不一样。早期做法是静态批处理,攒够一批一起算,但这一批里最长的序列会拖住所有人。vLLM 用的是连续批处理,某个请求生成完了就立刻从批次里退出,新请求随时补进来,显存利用率高很多。这也是它在高并发下吞吐领先的原因。
部署命令大致长这样:
python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct \ --tensor-parallel-size 1 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --port 8000几个参数值得单独说。tensor-parallel-size是张量并行度,等于用几张卡来切分模型权重,单卡就填 1。max-model-len决定最长上下文,调大它会按前面说的公式吃掉 KV Cache。gpu-memory-utilization是显存使用上限比例,默认 0.9,留 10% 给框架和其他进程,如果遇到CUDA out of memory却又觉得算得没错,先降这个值试试。
起来之后接口是 OpenAI 兼容的,原来写好的调用代码改一下base_url就能切换,这一点对迁移特别友好。
提示:如果你的场景是单用户偶尔用一下,别上 vLLM,部署复杂度不划算;如果场景是多用户共享或者批量离线任务,别用 Ollama 扛,吞吐会成为瓶颈。选型标准是并发量,不是模型大小。
4. 大模型微调:LLaMA-Factory 能帮你省掉哪些活
微调是这个领域最容易被神化的环节。很多人以为微调是"把模型教聪明",实际上大多数情况下微调做的是"把模型教规矩"——让它稳定地按你要求的格式、话术、判断倾向来输出。想清楚这个定位,微调的期望值就正常了。
4.1 微调之前必须想清楚的三件事
第一,你的问题能不能用提示工程解决。如果换几版提示词效果就能达标,那就不需要微调。微调的隐性成本很高:要准备数据、要调参、要评测、还要维护模型版本。
第二,你的数据量够不够。LoRA 微调对数据量的要求比全参数微调低,但一般也要几百到几千条高质量样本才能看到稳定效果。几十条数据也能训,只是很容易过拟合,表现为在训练集上答得漂亮,换个说法就崩。
第三,你选全参数微调还是参数高效微调。全参数微调会更新所有权重,显存需求是推理的几倍,7B 模型基本要 80GB 以上显存才舒服。LoRA 只训练一小部分低秩矩阵,显存需求大幅下降,一张 24GB 的卡就能微调 7B 模型,这是个人开发者唯一现实的选择。
4.2 数据准备:格式比数量更重要
LoRA 微调常用的数据格式是指令-输出对,整理成 JSON 或者 JSONL:
{ "instruction": "把下面这句话改写成正式书面语", "input": "这事儿我觉得不太行", "output": "我认为此项方案存在一定可行性风险。" }数据质量上有几个经验性的判断标准。一是多样性比重复性重要,同一个意思换五种说法作为五条样本,比同一条样本复制五遍有用得多。二是输出风格要一致,如果你要求模型输出 JSON,那所有训练样本的输出都必须是合法 JSON,哪怕一条格式错了,模型也会学到坏习惯。三是别混入低质量样本,网上抓来的对话数据往往噪声很大,清洗的成本通常低于它带来的收益。
4.3 LoRA 关键参数的取值逻辑
LoRA 有几个绕不开的参数,理解了它们的作用,调参就不是瞎试了。
lora_rank(r)决定低秩矩阵的维度,也就是新增参数的多少。常用值 8、16、32、64。任务越复杂、和你原有数据分布差得越远,r 该越大。风格模仿类任务 r 取 8 到 16 往往就够,学一种全新的输出结构可能要 32 以上。
lora_alpha是缩放系数,通常设成 r 的两倍。它和 r 的比值决定 LoRA 分支输出的强度,比值太大会让训练不稳。
target_modules指定给哪些层加 LoRA。常见做法是覆盖注意力部分的 q、k、v、o 投影层,追求更好效果时再加上前馈层的 gate、up、down。模块加得越多,显存和训练时间越高。
learning_rate在 LoRA 场景下通常比全参数微调大一个量级,1e-4 到 2e-4 是常见区间。
用 LLaMA-Factory 的话,这些参数都在一个 YAML 配置文件里,改完直接跑命令:
llamafactory-cli train examples/train_lora/qwen_lora_sft.yaml一份精简的配置大致是这样:
model_name_or_path: Qwen/Qwen2.5-7B-Instruct stage: sft finetuning_type: lora lora_rank: 16 lora_alpha: 32 lora_target: q_proj,k_proj,v_proj,o_proj dataset: my_dataset template: qwen cutoff_len: 2048 per_device_train_batch_size: 2 gradient_accumulation_steps: 8 learning_rate: 1.5e-4 num_train_epochs: 3 bf16: true output_dir: saves/qwen_lora这份配置里有几个点是踩过坑才知道的。template必须和模型匹配,Qwen 系列用qwen,Llama 系列用llama3,用错了模型学不到正确的对话边界,效果会莫名其妙地差。cutoff_len是截断长度,设得比实际样本长度大很多会白白浪费显存。gradient_accumulation_steps是梯度累积,等效于把 batch size 放大 8 倍,这是在显存不够时提升有效批大小的标准手段。bf16在支持的新卡上开着,比 fp16 数值稳定性更好。
4.4 微调后的合并与验证
LoRA 训完得到的是一个适配器权重文件,体积通常只有几十到几百 MB。用的时候有两种方式:一是推理时同时加载基座和适配器,二是把适配器合并进基座得到一个完整的模型。合并的好处是部署简单,坏处是失去了灵活切换多个适配器的能力。
验证环节容易被跳过。我的做法是准备一份固定的测试集,微调前后各跑一遍,人工打分或者用规则校验格式合规率。光看训练 loss 下降没有意义,loss 降了但输出变得啰嗦、开始复读、拒绝回答,这些情况都真实存在。
5. RAG 应用开发:让模型接上你自己的知识
如果你的需求是"让模型知道我们公司的产品手册内容",那大概率不该选微调。微调是把知识压进参数里,成本高、更新慢、还容易记错;RAG 是在推理时把相关资料检索出来塞进上下文,更新只需要改数据库,而且能给出引用来源,可解释性好得多。这就是为什么大多数企业级的大模型应用开发项目,骨架都是 RAG。
5.1 RAG 链路的完整拆解
一条标准的 RAG 链路分两个阶段。离线阶段做文档处理:加载原始文档、切分成块、用嵌入模型转成向量、存进向量数据库。在线阶段做检索增强:把用户问题也转成向量、在库里找最相似的若干个块、把这些块拼进提示词、交给大模型生成答案。
每个环节都有坑。文档解析阶段,PDF 里的表格和双栏排版经常被解析成一团乱码,行业里有个说法叫"垃圾进垃圾出",这一步做不好后面全白搭。切分阶段,块太大检索不精准,块太小语义不完整。嵌入阶段,中文场景要选对嵌入模型,直接拿英文模型处理中文效果会明显下滑。检索阶段,纯向量检索对关键词类的精确匹配不敏感,通常要配合关键词检索做混合召回,再加一个重排序模型做精排。
5.2 切分与检索的关键参数
切分长度是第一个要定的事。常见区间是 256 到 512 个 token,配合 10% 到 20% 的重叠。重叠的意义在于避免一句话被拦腰截断后两边都读不通,属于低成本高收益的设置。
召回数量 k 值通常在 3 到 10 之间。k 太小可能漏掉关键信息,k 太大则会把噪声塞进上下文,反而干扰模型判断,而且上下文越长成本越高、速度越慢。我的一般做法是先召回 20 条,用重排序模型打分后再取前 3 到 5 条喂给模型。
关于"RAG 怎么读"这个搜索热词,顺手说一句。它不是三个字母拼读,而是作为一个整体单词读,类似"拉格"。这个小细节在实际交流中挺影响观感的,第一次开会听到别人念成 R-A-G 的时候,你大概就能判断对方是刚入门的。
5.3 一个最小可跑的 RAG 流程
用现成的编排工具能快速搭出原型。以本地模型加编排框架为例,核心环节无非是:配置本地模型的接入地址、加载文档、建索引、组装检索问答链。
from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.vectorstores import Chroma from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.llms import Ollama loader = TextLoader("handbook.txt", encoding="utf-8") docs = loader.load() splitter = RecursiveCharacterTextSplitter( chunk_size=400, chunk_overlap=60, separators=["\n\n", "\n", "。", "!", "?", " ", ""] ) chunks = splitter.split_documents(docs) embedding = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5") store = Chroma.from_documents(chunks, embedding, persist_directory="./db") llm = Ollama(model="qwen2.5:7b", temperature=0.2) question = "手册里对请假流程是怎么规定的?" hits = store.similarity_search(question, k=4) context = "\n\n".join([h.page_content for h in hits]) prompt = f"""仅根据下面的资料回答问题,资料中没有的内容直接回答"资料未提及"。 资料: {context} 问题:{question} """ print(llm.invoke(prompt))这段代码里有三个值得注意的设计。切分器的分隔符列表把中文标点放在了前面,这是为了在中文文档里尽量按句子边界切,比按固定字符数硬切效果好很多。temperature设成 0.2 是为了让回答稳定,知识问答场景不需要创意。提示词里那句"资料未提及就直接说未提及"是防幻觉的关键,没有这句话,模型在检索不到内容时会自己编一个看起来很合理的答案。
6. 常见问题排查速查表
前面几章偏重"怎么做",这一章集中说"做不出来怎么办"。下面这些问题几乎每个入门的人都会撞上至少两三个。
6.1 显存、速度、输出质量三类典型故障
报CUDA out of memory但显存估算明明够。大概率是 KV Cache 超了,尤其是你把上下文长度设得很大,或者并发数上去了。先把max-model-len砍一半试,再把显存使用比例降到 0.85,一般能定位到原因。另外注意nvidia-smi显示的显存会被上一个没退干净的进程占着,重启或者杀掉残留进程再看。
模型能跑但速度慢得离谱。先确认模型是不是真的跑在 GPU 上。常见情况是装的是 CPU 版本的推理库,程序明明没报错,实际全在 CPU 上算。再确认有没有用到量化,FP16 的 7B 模型在 8GB 卡上虽然勉强能加载,但会频繁在显存和内存之间换页,速度能慢十倍以上。
输出开始复读同一句话。这是解码策略的问题,检查 repetition penalty 是不是设得太低,或者干脆没设。也有可能是上下文里塞了太多相似内容,模型找不到新东西可讲。
微调后模型变笨了。先看学习率是不是太大,LoRA 场景下超过 3e-4 很容易把模型带偏。再看数据量,几百条以下的数据训三个 epoch 基本是在过拟合。还有一个隐蔽原因:模板配置和基座模型不匹配,导致模型在错误的对话边界上学习。
RAG 检索到了内容但回答还是错的。这时候要打印出来看实际召回了什么。很多时候是切分把一句话切断了,或者嵌入模型对中文语义的区分度不够。换一个中文优化过的嵌入模型,或者引入重排序环节,通常能改善。
并发一上来响应时间爆炸。这是推理引擎的选择问题,单实例的轻量方案在高并发下就是会排队。要么换成对并发做过优化的服务化框架,要么起多个实例在前面挂个负载均衡。
6.2 排查思路的通用套路
把上面这些散点归纳一下,排查大模型问题有个固定顺序:先确认硬件层(模型在哪跑、显存够不够),再看配置层(参数是不是配错了、模板对不对),然后看数据层(输入输出长什么样),最后才怀疑模型本身。绝大多数问题出在前三层,直接怀疑模型能力是最容易走偏的方向。
提示:调试时务必把完整的提示词和模型原始输出打印出来。很多"模型答得不对"的问题,看到原始提示词就明白了——要么是上下文被截断了,要么是模板拼接多了一个换行,要么是系统提示被后面的指令覆盖了。
6.3 一份可以照着走的学习路线
把学习顺序排一下,避免东一榔头西一棒子。
| 阶段 | 目标 | 关键动作 | 参考时长 |
|---|---|---|---|
| 第一阶段 | 建立直觉 | 读注意力机制的可视化讲解,理解 token、上下文、温度这些基础概念 | 1 到 2 周 |
| 第二阶段 | 跑通推理 | 用轻量工具在本地跑起一个 7B 模型,用接口调通,观察显存变化 | 1 周 |
| 第三阶段 | 服务化部署 | 用服务化框架部署,压测并发,理解吞吐与延迟的取舍 | 2 到 3 周 |
| 第四阶段 | 搭 RAG | 从文档切分到检索问答完整走一遍,调切分和召回参数 | 2 到 4 周 |
| 第五阶段 | 做微调 | 用参数高效方法微调一个模型,建立评测集做前后对比 | 3 到 4 周 |
| 第六阶段 | 应用工程 | 加缓存、限流、评测、多轮会话管理,做成可交付的东西 | 持续 |
这个路线里我会特别强调第三阶段不要省。很多人从本地跑通直接跳到搭 RAG,结果一到真实并发场景就发现整个方案推倒重来。先理解服务化的约束,再往上叠应用,返工率低很多。
7. 生态变化与几个容易被问到的方向
学到这里,基本能应付大多数入门级需求了。剩下的是一些方向性的判断,这些问题在面试和同行交流里被问到的频率很高,提前有个自己的看法会舒服很多。
7.1 多模态大模型给开发带来什么变化
多模态能力的普及,最直接的影响是输入端从"一段文本"变成了"文本加图片加音频"。对开发者的影响体现在三块。一是预处理链路变长了,图片要缩放、要切分、要控制分辨率,因为视觉 token 的消耗往往比文本高一个量级,一张高分辨率图片可能顶掉几千个 token 的上下文预算。二是检索方式变了,以前只需要文本嵌入,现在要处理图文混合的跨模态检索。三是评测更难了,文本任务还能用规则校验,图像理解的对错很多时候只能靠人工抽样。
从工程角度看,多模态并没有推翻原有的架构,RAG、微调、服务化这些套路都还在,只是每个环节处理的数据类型多了。所以入门阶段把文本这条线走扎实,扩展到多模态的时候迁移成本并不高。
7.2 关于"会不会挤压原有方向"的观察
经常有人问多模态和大模型的普及会不会把原来的一些技术方向挤没了。我自己的观察是,被压缩的往往不是方向本身,而是那些只停留在工具使用层面的岗位。举个具体的例子,图形化编程这类面向青少年的启蒙内容,核心价值在于培养逻辑思维和拆解问题的习惯,这个目标和模型能不能生成代码是两回事。模型擅长的是把想法快速变成代码,但"想法从哪来"这件事,恰恰是需要长期训练的。
真正在变化的是门槛的位置。以前会写一个 CRUD 接口就能找到工作,现在这部分被自动化得很彻底;但能判断一个方案在数据量翻十倍之后还能不能撑住、能在一堆看起来都对的输出里挑出真正靠谱的那个,这种判断力的价值反而在上升。
7.3 几个实践中的个人体会
说几条我在实际项目里踩出来的经验,都是文档里不太会写的。
第一条,别一上来就追求端到端。RAG 的效果不好,很多人第一反应是换更大的模型。实际拆开看,八成的问题出在文档解析和切分上,输入的东西本身就是残缺的,换什么模型都救不回来。把每一环单独跑一遍,看中间产物长什么样,定位效率高得多。
第二条,给模型留退路。让模型在不确定的时候说"我不确定",比让它硬编一个答案要好得多。做法上就是在提示词里明确允许它拒答,并且在评测时把"该拒答的时候拒答了"也算作正确。
第三条,成本要算 token 账。上下文越长、召回条数越多,单次请求的成本越高。很多人做原型时不看这个,上线后发现账单吓人。做设计的时候就把"平均每次请求消耗多少 token"当作一个指标盯着,会逼着你把提示词写得更精简。
第四条,版本管理要覆盖提示词。代码用 Git 管是常识,但提示词往往散在代码各处,改一版就找不回上一版了。把提示词单独抽成配置文件纳入版本管理,出了问题能快速回滚,做 A/B 对比也方便。
第五条,先做一个能用的,再做一个好的。刚入门的时候最容易陷入无止境的方案调研,比来比去两个月过去了还在选框架。挑一个能跑通的方案,先做出一个能演示的东西,拿到真实反馈之后再优化,这个顺序几乎在所有情况下都更有效率。