DeepSeek从入门到精通:部署方式、提示词调优与参数配置实战指南

发布时间:2026/10/23 3:06:37
DeepSeek从入门到精通:部署方式、提示词调优与参数配置实战指南
简介《DeepSeek从入门到精通清华大学版》是一份面向AI初学者与开发者的系统入门指南由清华大学新闻与传播学院新媒体研究中心团队编写。内容围绕DeepSeek是什么、能做什么、如何用展开覆盖智能对话、文本生成、代码补全等应用场景并重点讲解推理模型如DeepSeek-R1与非推理大模型在逻辑推理、创意写作等任务上的差异以及“快思慢想”两类模型的选择思路。指南还梳理了从“下达指令”到“表达需求”的提示语策略包括指令驱动、需求导向、混合模式与启发式提问结合数学证明、创意写作、代码生成等示例帮助读者根据任务类型设计有效提示词、避开常见误区。资源为单个PDF文件共3.69MB内容紧凑且结构清晰适合快速通读与对照实践。已有2099人学习下载对想用好DeepSeek的普通用户和希望提升提示词技巧的开发者都有参考价值。1. DeepSeek 从入门到精通某高校版学习笔记到底能帮你省多少弯路DeepSeek 从入门到精通听上去像一句口号但某高校整理版学习笔记真正解决的问题是把“会用”和“用透”之间的巨大差距拆成了可操作的台阶。很多人只在网页对话框里点过几下遇到复杂任务就觉得模型不可控其实是提示词结构、采样参数和调用方式三件事没串起来。这套笔记的核心价值就是给出一条从接口跑通到应用落地的验证路径。如果你是大模型新手可以按它建立正确的手感如果你已经调过 API 但总在效果边缘反复横跳也能在这里找到参数边界和排错思路。下面我会按这条路径把每个阶段的命令、参数和踩坑记录整理成可直接复现的版本。整个过程不依赖特定环境只要你有一台能跑 Python 的电脑和一个可用的模型访问渠道。2. 把 DeepSeek 跑起来的三种方式官方对话、API 接入与本地部署在深入提示词之前先把运行方式选对。同一套提示词在三种方式下表现会有差异但更关键的是它们的成本和边界完全不同。我的建议是不要一上来就部署本地模型先用最轻的方式跑通再按需求迁移。2.1 先分清使用场景对话、自动化还是私有化你可以先问自己三个问题数据能不能出网调用频率是小时级还是秒级后续要不要接业务系统答案基本决定选型。我见过很多团队把大量时间花在本地部署上最后只是为了一个每天几十次的内部问答这在资源上并不划算。方式运行环境上手成本数据是否出网扩展性最适合的场景官方对话端浏览器最低出网无验证 Prompt、临时问答API 接入任意能发 HTTP 请求的环境低出网高自动化流程、批量处理、产品集成本地部署一台有 GPU 的服务器高不出网中数据合规、离线环境、二次开发官方对话端适合做探索和 Prompt 原型验证API 适合把它嵌入到现有系统里本地部署则是在数据不能出网或者需要深度定制时的选择。三者并不互斥我一般会在项目早期同时使用官方对话端和 API确认效果后再决定是否需要本地部署。选型时还要算一笔长期账API 按调用量计费本地部署一次性硬件投入高但边际成本低。如果每日请求量很小API 的隐性成本几乎可以忽略反而省掉了运维负担。请求量一旦上去本地部署的性价比才开始显现。2.2 官方对话端零命令起步的最小操作官方对话端的用法很简单打开官方页面登录账号选择一个对话模型在输入框里写清任务。但“跑通”和“会用”之间差着一个测试习惯。我建议第一次使用时就建立一个最小验证集包含三类问题需要推理的、需要格式化的、需要最新知识的。比如问题1一个笼子里有鸡和兔共35个头、94只脚问鸡和兔各多少只 问题2用三句话概括这段产品需求并输出为 markdown 列表。 问题3最近一周人工智能领域有哪些重要发布第一个问题测试模型推理能力第二个测试指令遵循和格式输出第三个测试它对实时信息的处理方式。官方对话端大概率会给出不错的回答但你要注意它是否追加了你不想要的解释。如果第三个问题它明确说无法获取实时信息那说明该对话模型本身不联网需要靠工具或知识库补全。这一步能帮你在后续选择 API 还是本地部署时心里有数。跑通完整流程后我还会做一次“清空会话”测试把刚才的对话完整复述一遍看模型是否还记得前面的约束。API 调用是无状态的官方对话端可能会保留上下文理解它们的状态差异能避免你高估模型记忆能力。2.3 API 接入用 Python 完成一次真实请求当你准备把 DeepSeek 接入业务流程最常用的方式是走兼容接口。下面这段代码是我在生产环境里的最小模板密钥通过环境变量注入不写在仓库里。import os import json import requests api_key os.environ.get(DEEPSEEK_API_KEY) base_url os.environ.get(DEEPSEEK_BASE_URL) # 从控制台获取形如 https://xxx/v1 if not api_key or not base_url: raise RuntimeError(请先设置 DEEPSEEK_API_KEY 和 DEEPSEEK_BASE_URL) payload { model: deepseek-chat, messages: [ {role: system, content: 你是一个只输出 JSON 的中文助手。}, {role: user, content: 把这句话转成带有意图和实体的 JSON帮我订一张明天从甲市到乙市的高铁票。} ], temperature: 0.2, max_tokens: 512, stream: False } headers { Authorization: fBearer {api_key}, Content-Type: application/json } resp requests.post(base_url /chat/completions, headersheaders, jsonpayload) data resp.json() print(data[choices][0][message][content])这段代码做了四件事读取环境变量、构造消息结构、发起 POST 请求、解析返回内容。temperature设成 0.2 是为了让输出更稳定因为这里要求结构化 JSONmax_tokens限制生成长度防止模型跑题后无限生成。stream设为 False 表示一次性返回适合调试如果你在做流式交互可以把stream改为 True并按行解析增量内容。生产环境里我还会给requests.post增加超时参数避免下游服务被慢请求拖死。一般设为timeout30如果业务需要更长的生成时间调到 60 也合理。这里需要注意不同服务商对base_url的路径要求不同有的已经包含/v1有的还需要你手动拼接。如果返回 404先检查是不是路径多了一段。另外请求失败时不要只打印状态码要把响应体里的error.message也打出来那才是真正有用的排错信息。我遇到过把 API 密钥放在代码里导致泄露的事故所以现在一律从环境变量读取并且在上线前用代码扫描工具检查密钥是否被误提交。2.4 本地部署当数据不能出网时的开源替代如果业务要求数据完全不出网本地部署是绕不开的选项。常见做法是先从官方渠道下载对应权重的模型文件放到一个本地目录里再用推理框架加载。下面这段代码基于 transformers 实现关键点在于不要直接写死模型版本而是用文件路径加载。from transformers import AutoModelForCausalLM, AutoTokenizer import torch model_dir ./local_model # 权重文件夹必须包含 config.json 和模型权重文件 tokenizer AutoTokenizer.from_pretrained(model_dir) model AutoModelForCausalLM.from_pretrained( model_dir, torch_dtypetorch.float16, device_mapauto ) messages [ {role: system, content: 你是本地知识库问答助手。}, {role: user, content: 请根据文档内容回答DeepSeek 是否支持语音输入} ] inputs tokenizer.apply_chat_template( messages, add_generation_promptTrue, return_tensorspt ).to(model.device) outputs model.generate( inputs, max_new_tokens512, do_sampleFalse ) response tokenizer.decode(outputs[0][inputs.shape[1]:], skip_special_tokensTrue) print(response)torch_dtypetorch.float16可以把显存占用减半device_mapauto让框架自动把层分配到可用设备。apply_chat_template会根据模型自带的模板格式化消息但如果你的 tokenizer 没有内置模板会直接报错。解决办法是先用tokenizer.chat_template检查一下没有的话就需要按照模型文档里的格式手动拼接提示词。本地部署最容易被忽略的是硬件边界。一个中等规模的开源对话模型加载权重通常需要十几 GB 显存如果只有一张 8GB 显卡就必须考虑量化。常见做法是先用 FP16 跑通推理确认效果达标后再做量化不要一开始就追求极致压缩。我自己的经验是本地部署的第一道门槛不是模型效果而是环境依赖版本。transformers 和 torch 版本不匹配会出很多莫名其妙的报错建议先跑通官方文档里的最小示例再加载目标模型。显存估算有个简单公式模型权重占用大约等于参数量的两倍FP16再加上激活值和缓存。你可以先用这个粗粒度判断当前硬件能不能跑而不是等 OOM 报错才回头。提示如果你只是做文本分类、信息抽取这类任务优先使用 API 而不是本地部署。本地真正不可替代的场景是数据合规和需要深度改造模型结构投入产出比要自己想清楚。3. 提示词与采样参数让 DeepSeek 从“会聊”变成“好用”模型跑通之后效果差距几乎全部来自提示词和参数。DeepSeek 本身能力足够但如果你只是把问题丢过去它会按照概率分布里最普通的路径回答。要让输出稳定、可控、符合业务预期需要把提示词结构化并把采样参数调到合适区间。3.1 角色、任务、输出格式三段式为什么它比直接提问稳定我在调 DeepSeek 时最常用的结构是“角色 任务 输出格式”。角色告诉模型以什么身份说话任务描述要解决的具体问题输出格式约束结果的形状。三段式不是模板套路而是在降低模型的自由发挥空间。直接问“这句话是什么意思”和说“你是客服质检员判断这句话是否包含威胁并输出 JSON”后者的输出边界清晰得多。你是客服质检员。下面是一段用户消息请判断它是否包含威胁性表达。 只需要输出 JSON {contains_threat: true/false, reason: 不超过20个字的原因} 用户消息你们再不发货我就去投诉。对比一下如果不给出输出格式模型可能会回答“用户表达了不满但没有明显威胁”然后你还要再写解析逻辑去处理这段自然语言。有了 JSON 约束解析成本几乎为零。三段式的核心不是字面顺序而是把关键信息按“身份、目标、边界”三个维度交代清楚。边界尤其重要它决定了模型不会额外输出解释、不会自己加戏。实际调试时我会把“角色”放在系统提示词里“任务”放用户消息开头“输出格式”放在用户消息最后。这样做的原因是模型对更靠近末尾的指令记忆更深刻角色在系统提示词里能持续影响整轮对话。很多人把输出格式放在系统提示词第一句结果模型在长文本生成到一半时丢掉了格式约束改成这种分布后稳定性明显提升。3.2 temperature、top_p、max_tokens、frequency_penalty 怎么配采样参数直接影响输出的确定性和多样性。DeepSeek 的接口兼容常见参数但它们不是随便填的。我一般会按任务类型来定初始值参数取值范围推荐初始值适用场景temperature0 - 20.3分类、抽取、结构化输出temperature0 - 20.8文案生成、头脑风暴、对话top_p0 - 10.9控制候选词范围通常与 temperature 配合max_tokens1 - 模型上限512防止生成过长预算有限frequency_penalty-2 - 20减少重复对话类可设 0.3presence_penalty-2 - 20鼓励/抑制新话题信息抽取通常设负值temperature控制的是概率分布的平滑程度值越低越倾向高频词越稳定但也越可能机械值越高越多样但容易偏题。top_p是另一种截断策略它只从累积概率达到阈值的最小 token 集合里采样。很多指南说两者不能同时调实际做法是先固定一个调另一个。我习惯于固定top_p0.9主要调temperature。max_tokens不是思维上限而是生成预算。如果任务需要长文总结设太短会被截断设太长又浪费延迟。还有一点容易被忽略max_tokens会把系统提示词、对话历史和生成的 token 统一计入上下文预算设得太大会触发模型上下文超限导致请求直接报错。所以我通常按“生成内容的最大长度 20% 余量”来设置。frequency_penalty和presence_penalty在 DeepSeek 上效果不像前面几个那么线性我一般不在第一轮调只有当输出出现明显重复或总是绕回同一个话题时才动它。frequency_penalty对已经出现过的 token 做惩罚值越高模型越不愿意重复presence_penalty对任何出现过的 token 一视同仁地惩罚更倾向于引入新话题。做信息抽取时我会把presence_penalty设成 -0.5让模型集中在给定文本上而不是发散到外部知识。3.3 用少样本示例约束返回结构三段式能解决大部分问题但在格式复杂、枚举值固定的场景下模型还是可能返回格式错误的 JSON。这时候少样本示例比任何参数都有效。少样本的意思是在消息里直接给出一个输入输出的例子让模型模仿这个映射关系。messages [ {role: system, content: 你是订单意图识别助手。只输出 JSON。}, {role: user, content: 输入我要把订单号 20240301 改成 20240302。输出{\intent\: \modify_order\, \order_id\: \20240301\}}, {role: assistant, content: {\intent\: \modify_order\, \order_id\: \20240301\}}, {role: user, content: 输入帮我查一下明天甲市到乙市的航班。输出{\intent\: \search_flight\, \from\: \甲市\, \to\: \乙市\, \date\: \明天\}} ]这里我把示例写成了消息轮次而不是放在一个大文本里。原因是模型在对话历史里能看到“用户输入 → 助手输出”的完整对应比在 system 里塞一段示例更容易被遵循。少样本的数量不是越多越好我通常先给 2 个如果场景化明显再增加到 5 个。超过 5 个后收益开始递减反而占用上下文窗口。使用少样本时有一个隐藏坑示例里的用词会污染提取结果。比如示例中把订单号写成 20240301真实数据里也是 8 位数字模型会倾向于把不符合这个形式的内容也强行对齐。所以示例要覆盖真实数据的边界情况至少要包含一个正常值和一个异常值。如果加了示例后模型仍然输出错误格式下一步不是继续加示例而是检查消息拼接顺序。有些时候是用户消息里的换行符把示例和真实输入粘在一起模型分不清边界。我习惯用类似### 示例开始 / ### 真实输入的标记来分隔效果通常比用空行好。这些标记本身是不固定的你可以在几种分隔符之间做 A/B 测试选稳定的一种。4. 基于 DeepSeek 做应用的四个关键环节上下文、函数调用、知识库和评测当 DeepSeek 被嵌入到真实系统后提示词只是最外层的问题。更复杂的是上下文怎么传、业务动作怎么触发、私有知识怎么塞进去、效果怎么验证。这四个环节决定项目能不能从 Demo 走到生产。4.1 多轮对话的上下文管理截断、摘要与结构化多轮对话时不能无脑把历史消息全部发给模型。模型能处理的上下文长度有限而且越长的历史越容易让模型迷失焦点。我维护消息列表时会按 token 数做预算超过阈值就丢弃最早的轮次或者把旧轮次压缩成摘要。MAX_CONTEXT_TOKENS 4000 def build_messages(system_prompt, history, new_user_msg): # 先用最新一轮构造消息逐步向前追加历史直到接近预算 messages [{role: system, content: system_prompt}, {role: user, content: new_user_msg}] used_tokens estimate_tokens(system_prompt) estimate_tokens(new_user_msg) for user_msg, assistant_msg in reversed(history[-10:]): role_user {role: user, content: user_msg} role_assistant {role: assistant, content: assistant_msg} cost estimate_tokens(user_msg) estimate_tokens(assistant_msg) if used_tokens cost MAX_CONTEXT_TOKENS: break messages.insert(1, role_assistant) messages.insert(1, role_user) used_tokens cost return messagesestimate_tokens是你要自己实现的近似函数常见做法是用中文字符数除以 1.5英文词数乘 0.8再留出 20% 余量。这段代码的逻辑是从后往前插入历史最早超过预算的轮次直接丢弃而不是无脑截断字符串。截断的替代方案是摘要把旧对话交给模型总结成一段话再作为 system 的一部分放回去。我的习惯是只有在历史轮次超过 20 轮时才启用摘要否则开销太大。摘要有一个副作用它会丢失细节比如用户之前提供的电话号码、地址等硬信息。所以在做摘要时我会单独把包含结构实体的消息保留而不是一视同仁地压缩。4.2 函数调用让模型按约束触发业务动作函数调用Function Calling的意义在于模型不直接返回一段话让你去正则匹配而是返回一个结构化的工具调用指令由你的代码去执行真实动作。DeepSeek 的接口支持在请求里声明可用函数模型会在需要时返回tool_calls。以查询天气为例你可以先定义一个函数描述{ type: function, function: { name: get_weather, description: 查询指定城市当前天气, parameters: { type: object, properties: { city: {type: string, description: 城市名}, date: {type: string, description: 日期格式YYYY-MM-DD} }, required: [city] } } }把这段 JSON 塞进请求体里的tools字段然后正常发消息。模型如果判断需要查天气返回内容里会包含tool_calls字段其中写明函数名和参数。你的代码要做的事情是识别到tool_calls后执行对应函数再把结果作为一条新的tool消息返回给模型让模型根据结果生成最终回复。这个环节最常见的错误是把函数名写得太宽泛比如process_data。模型不知道什么时候该调用它结果就是该调不调、不该调乱调。函数描述要精确到动词和触发条件例如“当用户询问天气时调用 get_weather”。参数定义也不要全用 string能枚举的字段用 enum模型填充会更准确。还要注意函数调用的循环保护。如果模型连续返回同一个tool_call你的代码需要设置最大调用次数否则会产生死循环。我一般限制单轮最多 3 次工具调用超过就返回错误信息让模型给出兜底回答。另外函数返回结果不能太长否则会挤占上下文。如果工具返回的数据量很大先做截断或摘要再送回模型。4.3 外挂知识库向量检索补齐最新信息模型的知识截止日期是固定的业务私有知识也不可能被预训练背下来。外挂知识库的标准流程是先把文档切成小段每段向量化存入向量库用户提问时把问题向量化召回最相近的 top-k 段落拼进提示词。这个过程不改变模型权重只改变输入。docs split_text(article, chunk_size500, overlap50) vectors [embedding_model.encode(doc) for doc in docs] # vectors 和 doc 一起写入向量库 question_vec embedding_model.encode(question) top_k_docs vector_db.search(question_vec, top_k3) context \n.join(top_k_docs) prompt f请只根据以下资料回答\n{context}\n\n问题{question}切片的chunk_size和overlap对召回质量影响很大。切太大语义可能分散到无关段落切太小同一句话被切断。我一般从 500 字和 50 字重叠开始再根据自己文档的类型调整。如果文档是问答对可以按“一问一答”作为一个 chunk而不是按字数切。召回数量 top_k 设置 3 到 5 个太少可能漏关键信息太多会让模型在长上下文里迷失。这里的关键是向量化模型和问答模型不是同一个向量化模型可以选轻量的通用模型不必要用 DeepSeek。拼提示词时一定要强调“只根据资料回答”否则模型会忍不住用自己的知识补充。而你也无法判断哪些来自资料、哪些来自幻觉。另一个常见问题是知识库更新后向量库里的旧向量没有清理导致同一段内容出现多个版本。我一般在写入向量时带上文档版本号和切片偏移量这样更新时能精确定位并替换旧向量。4.4 评测集用可重复的用例判断“变好”还是“变坏”没有评测集的调优都是主观感受。我在项目里会维护一组固定用例每次改动 Prompt 或参数后都跑一遍把输出存成文本对比。用例数量不用多覆盖每个业务分支的 10 到 30 条即可但必须固定。test_cases [ {name: 威胁识别-正常, messages: [...], expect: {contains_threat: False}}, {name: 威胁识别-明确威胁, messages: [...], expect: {contains_threat: True}}, {name: JSON格式合法, messages: [...], expect: json_loadable} ] def run_evaluation(model_func, cases): results [] for case in cases: output model_func(case[messages]) passed judge(output, case[expect]) results.append({name: case[name], passed: passed, output: output}) return resultsjudge可以是精确匹配、JSON 解析成功与否也可以是用另一个模型打分。重点不是评测方式多高级而是每次改动后有可对比的记录。我见过太多人调了一天参数最后问“到底变好没有”答不上来。固定评测集就是给这种争论一个停止按钮。评测集也不是一成不变线上出现新case时要及时补充。我每周会花一点时间把线上日志里的典型请求加入测试集防止模型被“调坏”后回归到老问题上。5. DeepSeek 落地常见问题排查六个现场与解决办法这一章从真实踩坑中挑出最常遇到的六个问题按照现象、原因、解决三段来写。你会发现大多数问题不是模型不够聪明而是调用方式和预期管理出了问题。5.1 现象一模型总是输出大段解释我要的 JSON 被包在里面很多人在设计提示词时已经写了“只输出 JSON”但模型还是回答“好的我会按照您的要求输出{...}”。原因是系统提示词和用户消息之间的边界模糊模型把“只输出 JSON”理解成了友好回复的指令。解决方法是把输出格式约束移到用户消息的最后一句并给出一个明确的起始标记。例如直接输出 JSON不要解释不要使用 markdown 代码块 {intent: ..., entities: {...}}如果还不行就在系统提示词里加上“你是一个 API只能返回机器可读文本”。少样本示例通常比单独一句“只输出 JSON”更有效因为它展示了完整输出形态。我记忆里最严重的一次模型在 JSON 外又包了 markdown 代码块下游解析直接失败。后来我在解析层加了预处理先把代码块标记去除再走 JSON 解析但根本解法还是改提示词让模型把{intent: ...}当作唯一输出。5.2 现象二同一个问题换一种说法就答错原因是模型对输入表达敏感你的 Prompt 只在固定句法上生效。解决办法不是继续调 Prompt而是扩充少样本示例覆盖同义不同形的表达。比如“明天甲市飞乙市”和“帮我看看甲市到乙市的航班”应映射到同一个结果。把这些变体加入评测集比在描述里写“支持口语化表达”可靠得多。另一个相关现象是模型对中英文混合输入表现不稳定比如“查一下 from 甲市 to 乙市的 flight”。如果业务里必然出现混合表达就要在示例里专门加几条这种样本而不是假设模型能自动理解。5.3 现象三多轮对话到第三轮模型忘记最初的指令上下文被新消息挤占或者你在每一轮没有重新注入系统提示词。API 调用是无状态的所有消息都会累积。解决方法是每一轮都把关键约束放到离问题最近的位置比如把 system 里的任务摘要复制到最后一轮用户消息前。不要假设模型会一直记住最早说的话。另一个常见做法是每轮自动摘要历史用摘要代替原始对话。还要注意如果用户消息里出现了和系统指令冲突的内容模型往往会服从用户消息。所以在用户输入进入 API 之前先做意图识别和敏感词拦截避免用户把“不要 JSON要自然语言”这样的指令带进对话。5.4 现象四调用 API 返回 401 或 429401 是密钥错误或权限不足429 是频率超过限额。现象看起来都是请求被拒但处理方式不同。401 先检查环境变量是否真的被读取可以在代码里临时打印api_key[:4]看前缀。429 则需要退避重试不建议用死循环而是指数退避每次等待时间按 1s、2s、4s 增长。这部分的坑在于很多人把 429 当网络故障反复重启程序实际上只是用量配额问题。我还会在代码里记录每次请求的耗时和状态码方便事后排查。如果 429 频繁出现不要只调大等待时间要看是不是业务代码里并发请求过猛必要时用信号量限制同一时刻的最大请求数。5.5 现象五本地部署后生成速度慢到不可用先确认是否没有用到 GPU。device_mapauto有时候会把部分层放 CPU导致推理明显变慢。可以打印model.hf_device_map查看分布。如果确实全部在 GPU还要检查是否使用的 FP32FP16 能省一半显存并加速。更彻底的加速手段是量化但量化后效果需要重新评测。最容易被忽略的是max_new_tokens设得太大生成 512 个 token 自然比 128 慢四倍先确认业务是否真的需要长回复。还有一个场景是并发请求全部排队单卡吞吐有限。此时可以在模型前面加一层请求合并将相同前缀的请求合并成一次推理或者用批处理把多个请求拼成一个 batch能显著提高吞吐。5.6 现象六测试集效果好线上效果一塌糊涂原因是测试集来自你手写的样本线上流量分布完全不同。解决方法是建立线上日志回流机制每天把真实请求保存下来按比例抽成新评测集持续加入。这不是一次性的工作而是常态化流程。另一个常见原因是线上 Prompt 被拼接了动态变量变量里含有特殊符号或超长文本导致模型行为偏移。给动态变量加长度限制和转义清洗能消除大部分线上怪问题。我经历过一次线上事故用户输入里含有一长串相同的感叹号把 Prompt 里的输出格式说明淹没模型直接崩溃。从那以后我在所有动态变量插入处都加了长度限制和重复字符压缩。6. 把“精通”落在验证方法上一个回归用例模板与我的使用习惯所有上一步的能力最终要靠一套验证方法固化成日常习惯。我自己的做法是维护一个regression/目录里面每个子任务一个 JSON 文件记录输入、期望输出、实际输出和参数快照。任何一次 Prompt 或模型版本变更都要先跑一遍这套用例再决定是否进入生产。import json from datetime import datetime def save_regression(records, task_name, config): snapshot { task: task_name, timestamp: datetime.now().isoformat(), config: config, results: records } with open(fregression/{task_name}_{snapshot[timestamp]}.json, w) as f: json.dump(snapshot, f, ensure_asciiFalse, indent2)这个模板不解决具体业务问题但它让所有调优过程可回溯。当你把 temperature 从 0.3 调到 0.5发现某个用例变好了另一个变差了你能明确知道变差的是谁而不是凭印象说“好像还行”。我会在每份记录里保存当时的 system prompt 和采样参数因为同样的输出在不同参数下复现不了。更进一步我会给每条用例打标签例如“中文口语”“长文本”“含特殊字符”这样当某个标签下的通过率明显下降时能立刻定位是哪一类输入受到了影响。我现在已经养成了一个习惯任何新功能上线前先写下 10 条它应该通过的测试用例再写代码调 Prompt。这个顺序让我的开发过程从“试到满意”变成“验到通过”。早期我总凭感觉调参数翻车次数多得记不清后来发现花在评测上的时间远小于花在猜测上的时间。希望这个习惯对你也有用。本文还有配套的精品资源点击获取