最近带着团队把三个核心业务都接上了大模型。从最初的“调通就行”,到后来面对生产环境的吞吐量、稳定性和成本要求,中间踩的坑,比我前两年写的Bug还多。
尤其是上周,一个看似简单的对话接口,差点让我们的SLA告警全盘飘红。不是代码错了,而是我们对API的理解,还停留在“Hello World”的层面。
大模型API,表面上就是发个POST请求,收个JSON。可真要把它用稳、用好、用便宜,里头全是细节。限流429只是开胃小菜,token的隐性浪费、超时重试的“惊群效应”、返回格式的“薛定谔稳定”……每一个都可能成为压垮线上服务的最后一根稻草。
今天,我把过去几个月用真金白银和线上故障换来的8个实战技巧,毫无保留地分享给你。每一个技巧都附带了可直接粘贴到项目里的代码片段,不绕弯子,只说干货。
大模型API接口,表面看起来简单粗暴——一个POST请求,就能让AI为你打工。但当你把它丢进真实的生产环境,429限流、超时重试、天价Token账单、返回格式解析崩溃……这些“坑”就会一个接一个地蹦出来,让你深夜加班,血压飙升。
技术方案没有银弹,但有最佳实践。今天,我结合自己踩过的坑和多个上线项目的经验,为你总结出8个经过实战检验的核心技巧。每一条都附上可直接复制粘贴的代码,让你从“能用”进阶到“稳定、高效、省钱”。
🎯 技巧一:指数退避重试 - 别让一次网络抖动搞崩你的定时任务
场景还原:你写了一个自动生成日报的脚本,每天早上8点准时运行。有一天,API服务端因为负载高,瞬间返回了503。如果你的代码只是简单地 sleep(2) 然后重试,会发生什么?
大量和你一样的脚本,都会在2秒后准时发起第二波请求,这相当于把流量洪峰原封不动地往后平移了2秒,再次冲击服务器,很可能引发更长时间的瘫痪。这就叫“惊群效应”。
解决方案:指数退避 + 随机抖动。这是分布式系统的经典容错模式。
import timeimport randomimport requestsfrom functools import wrapsdef retry_with_backoff(max_retries=3, base_delay=1, max_delay=60): """ 一个聪明的重试装饰器:指数级增加等待时间,并加入随机扰动。 :param max_retries: 最大重试次数 :param base_delay: 基础等待时间(秒) :param max_delay: 最大等待时间(秒),防止等待时间过长 """ def decorator(func): @wraps(func) def wrapper(*args, **kwargs): last_exception = None # 记录最后一次异常 for attempt in range(max_retries + 1): # 尝试次数 = 重试次数 + 初始调用 try: return func(*args, **kwargs) # 尝试执行原函数 except requests.exceptions.RequestException as e: last_exception = e if attempt == max_retries: # 如果已达最大重试次数,则放弃 break if hasattr(e, 'response') and e.response is not None and e.response.status_code == 429: retry_after = int(e.response.headers.get("Retry-After", base_delay * (2 ** attempt))) delay = retry_after else: delay = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay) print(f"⚠️ 请求失败: {e}, {delay:.1f}秒后进行第{attempt+1}次重试") time.sleep(delay) # 等待 raise last_exception return wrapper return decorator@retry_with_backoff(max_retries=3)def call_llm_api(messages, api_key, base_url="https://api.openai.com/v1"): """封装一个基础的GPT API调用函数""" resp = requests.post( f"{base_url}/chat/completions", headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}, json={"model": "gpt-4o", "messages": messages, "temperature": 0.7}, timeout=30 ) resp.raise_for_status() # 如果状态码不是2xx,抛出HTTPError return resp.json()
关键点解读:
- 指数退避等待时间按
base_delay * (2^attempt) 增长,第一次等1秒,第二次等2秒,第三次等4秒...给服务端足够的恢复时间。 - 随机抖动在计算出的等待时间上加一个
0-1秒的随机数。这能完美打破不同客户端重试的时间同步,避免“惊群效应”。 - 区别对待429对于限流错误,优先读取响应头中的
Retry-After(服务器告诉你具体等多久),这比客户端自己猜更精准。
加上这十几行代码,你的脚本就从“脆弱”变得“健壮”。
⚡ 技巧二:异步并发处理 - 让100个请求在3秒内完成,而不是30秒
痛点:你要处理50条用户评论的情感分析,串行调用API,每次等待0.5秒,总共就要25秒。用户体验就是“卡顿”。
解决方案:asyncio + aiohttp + Semaphore(信号量)。这是Python处理高并发I/O的黄金组合。
import asyncioimport aiohttpimport randomasync def call_one(session, sem, prompt, api_key): """单个异步API调用,内置简单重试""" async with sem: # 信号量:控制同时“飞行”中的请求数量 for attempt in range(3): # 内置重试逻辑 try: async with session.post( "https://api.openai.com/v1/chat/completions", headers={"Authorization": f"Bearer {api_key}"}, json={"model": "gpt-4o-mini", "messages": [{"role": "user", "content": prompt}]}, timeout=aiohttp.ClientTimeout(total=60) ) as resp: if resp.status == 429: retry_after = int(resp.headers.get("Retry-After", 2 ** attempt)) await asyncio.sleep(retry_after) continue # 继续重试循环 resp.raise_for_status() # 检查其他HTTP错误 data = await resp.json() return data["choices"][0]["message"]["content"] except Exception as e: if attempt == 2: # 最后一次重试也失败 return f"错误: {e}" await asyncio.sleep(2 ** attempt + random.uniform(0, 1))async def batch_call(prompts, api_key, max_concurrent=10): """批量异步调用入口函数""" sem = asyncio.Semaphore(max_concurrent) async with aiohttp.ClientSession() as session: # 复用连接池,提升性能 tasks = [call_one(session, sem, p, api_key) for p in prompts] return await asyncio.gather(*tasks)api_key = "your-api-key"prompts = [f"概括这段文本的第{i}段" for i in range(50)]results = asyncio.run(batch_call(prompts, api_key, max_concurrent=10))print(f"处理完成,共{len(results)}条结果")
- 串行版本
- 异步版本(10并发)50个请求 / 10并发 ≈ 5个批次,每批最慢的请求耗时约0.6秒,总耗时 ≈ 3秒
安全提醒:max_concurrent 不宜设置过大。主流云API的默认限流在每分钟数百次请求(RPM),设置10-20是安全且高效的范围。
🔍 技巧三:Token成本预判 - 在按下“发送”键前,锁死你的API预算
灵魂一问:收到月度账单时,你是不是也曾目瞪口呆——明明感觉没怎么用,费用却高得离谱?问题往往出在Token的“暗流”消耗。一段看似平常的对话,背后可能已吞噬了成千上万的Token。
破局之匙:告别len(text) / 4 这类不靠谱的“民间土法”,拥抱官方工具 tiktoken,实现精准预测。
import tiktokendef count_tokens(messages, model="gpt-4o"): """ 精确计算一组消息(messages列表)的Token数量。 计算规则与OpenAI API后台计费系统完全一致。 """ try: encoding = tiktoken.encoding_for_model(model) except KeyError: encoding = tiktoken.get_encoding("cl100k_base") num_tokens = 0 for msg in messages: num_tokens += 4 # 每条消息固定的格式开销(role, content等) for key, value in msg.items(): num_tokens += len(encoding.encode(str(value))) if key == "name": # 若消息包含‘name’字段,额外+1 token num_tokens += 1 num_tokens += 2 # 每次对话结尾的priming tokens return num_tokensdef truncate_messages(messages, max_tokens, model="gpt-4o"): """ 智能截断超长历史对话。 核心策略:保留所有系统指令(system messages),优先丢弃最早的用户/助手对话轮次。 """ system_msgs = [m for m in messages if m["role"] == "system"] other_msgs = [m for m in messages if m["role"] != "system"] while count_tokens(system_msgs + other_msgs, model) > max_tokens and len(other_msgs) > 1: other_msgs.pop(0) # 移除列表中的第一条历史消息 return system_msgs + other_msgs # 合并返回long_history = [ {"role": "system", "content": "你是专业的金融分析助手。"}, {"role": "user", "content": "分析一下今天A股大盘的走势。"}, {"role": "assistant", "content": "今天大盘呈现高开低走的态势..."},]trimmed_history = truncate_messages(long_history, max_tokens=4000)print(f"截断后的历史记录Token数: {count_tokens(trimmed_history)}")
为什么非得用tiktoken不可?不同模型(如GPT-4、Claude、通义千问)内置的分词器(Tokenizer)天差地别。一个英文单词、一个中文汉字、一行代码、甚至一个表情符号,在不同模型下的“切割”方式完全不同。tiktoken是OpenAI官方开源的分词库,能确保你的本地计算与云端计费结果毫厘不差。
高手进阶:流式响应中的实时用量捕捉如果你开启了流式传输(stream=True),又想实时获知精确的Token消耗,可以这样配置请求参数:
resp = requests.post( f"{base_url}/chat/completions", headers={"Authorization": f"Bearer {api_key}"}, json={ "model": "gpt-4o", "messages": messages, "stream": True, # 开启流式 "stream_options": {"include_usage": True} # 关键参数!流式结束后会返回usage字段 })
💸 技巧四:成本监控与预警 - 给你的每一分API开销装上“北斗”
残酷现实:一次模型调用手滑,可能让你白白浪费几十甚至上百元。gpt-4o和gpt-4o-mini的输入输出价格相差十几倍!选错模型,就是和钱过不去。
解决方案:为每一次API调用自动贴上“价签”,并构建项目级的成本聚合看板,让费用支出透明可控。
首先,建立一个实时价格表(以2026年6月市场价为参考,已按汇率转换为人民币):
PRICING = { "gpt-4o": {"input": 18.0, "output": 72.0}, # 国外模型,价格偏高 "gpt-4o-mini": {"input": 1.1, "output": 4.3}, # 国外入门款,谨慎使用 "deepseek-chat": {"input": 1.9, "output": 7.9}, # 国内首选,性价比极高 "qwen-max": {"input": 2.5, "output": 10.0}, # 通义千问旗舰版 "yi-large": {"input": 1.5, "output": 6.0}, # 零一万物,性能强劲 "glm-4-plus": {"input": 2.0, "output": 8.0}, # 智谱AI,综合表现均衡 "ERNIE-4.0": {"input": 4.0, "output": 16.0}, # 文心一言,中文理解出色}def calculate_cost(model, prompt_tokens, completion_tokens): """计算单次调用的成本(元)""" if model not in PRICING: print(f"警告:未找到模型 {model} 的价格信息") return None p = PRICING[model] cost = (prompt_tokens / 1_000_000) * p["input"] + (completion_tokens / 1_000_000) * p["output"] return costusage = resp["usage"] # 假设resp是API返回的JSONcost = calculate_cost("deepseek-chat", usage["prompt_tokens"], usage["completion_tokens"])print(f"📊 本次消耗: {usage['total_tokens']} tokens, 费用约 ¥{cost:.6f}")
单次计算还不够,我们需要一个“账本”来跟踪整个项目或服务的总开销:
from dataclasses import dataclass, fieldfrom datetime import datetimeimport json@dataclassclass UsageTracker: """一个轻量级的使用量追踪器""" total_prompt: int = 0 # 累计输入Token total_completion: int = 0 # 累计输出Token total_cost: float = 0.0 # 累计费用(元) calls: int = 0 # 累计调用次数 log: list = field(default_factory=list) # 详细调用日志 def record(self, model, prompt_tokens, completion_tokens, metadata=""): """记录一次调用""" cost = calculate_cost(model, prompt_tokens, completion_tokens) or 0.0 self.total_prompt += prompt_tokens self.total_completion += completion_tokens self.total_cost += cost self.calls += 1 self.log.append({ "time": datetime.now().isoformat(), "model": model, "prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens, "cost": round(cost, 6), "metadata": metadata # 可添加业务标签,如 "用户注册邮件生成" }) def summary(self): """输出统计摘要""" return { "总调用次数": self.calls, "总Token消耗": self.total_prompt + self.total_completion, "输入Token": self.total_prompt, "输出Token": self.total_completion, "估算总费用": f"¥{self.total_cost:.4f}" }project_tracker = UsageTracker()project_tracker.record("deepseek-chat", usage["prompt_tokens"], usage["completion_tokens"], metadata="日报生成任务")print(json.dumps(project_tracker.summary(), indent=2, ensure_ascii=False))
关于国内模型选择的深度分析
策略一:日常任务,认准“性价比之王”对于绝大部分的对话、总结、邮件草拟等任务,DeepSeek Chat 是当之无愧的首选。它的价格几乎是GPT-4o的十分之一,但在中文理解和生成上丝毫不落下风,堪称“省钱省心”的模范生。
策略二:复杂逻辑与代码,启用“国产旗舰”当任务涉及复杂的逻辑推理、长文档分析或代码生成时,可以切换到 Qwen-Max 或 GLM-4-Plus。它们在多步推理和指令遵循上更有优势,虽然价格稍高,但能显著提升输出质量,避免因模型能力不足导致的重复调用和无效消耗。
策略三:高敏感中文任务,交给“本地专家”对于需要深度理解中文语境、文化背景或专业术语的任务(如法律文书、市场报告、古诗词创作),ERNIE-4.0 或 Yi-Large 是更好的选择。它们在中文语义上的细腻把握,是国外模型难以比拟的。
核心提醒:
- 警惕网络波动直接使用境外API,延迟和失败率是隐形成本。国内模型在稳定性和速度上优势巨大。
- 关注合规性企业级应用务必确保数据不出境,使用国内已备案或提供私有化部署的模型服务,是规避法律风险的底线。
- 善用免费额度许多国内平台(如阿里云百炼、百度千帆)为新用户提供可观的免费Token包,是项目冷启动阶段控制成本的绝佳工具。
把账算清楚,钱花在刀刃上,这才是技术人的精打细算。
有了这个追踪器,你就能清晰回答:“我们这个月的AI成本主要花在哪个功能上了?”
🧱 技巧五:强制结构化输出 - 告别“正则表达式地狱”
解析之痛:你让模型提取用户信息,它返回了JSON,但偶尔外面会包一层 Markdown 代码块 json ... ,或者末尾多了一个逗号。你的 json.loads() 直接崩溃,生产环境告警响了。
最佳实践:双保险策略。
一:使用API原生支持(最推荐)
OpenAI和大部分兼容API都支持 response_format 参数,能极大提高输出JSON的稳定性。
resp = requests.post( f"{base_url}/chat/completions", headers={"Authorization": f"Bearer {api_key}"}, json={ "model": "gpt-4o", "messages": [{ "role": "user", "content": "从以下简历文本中,提取姓名、工作年限(数字)和技能列表(数组)。" }], "response_format": {"type": "json_object"} # 强制要求返回纯JSON对象 })data = resp.json()result = json.loads(data["choices"][0]["message"]["content"])
二:编写一个“金刚不坏”的解析函数即使有原生支持,为了兼容其他模型或处理极端情况,一个健壮的解析器仍是必备的。
import reimport jsondef safe_json_parse(text): """ 容错性极强的JSON解析函数。 能处理:包裹在markdown代码块、含有尾随逗号、带有行注释等常见非标情况。 """ original_text = text json_match = re.search(r'```(?:json)?\s*\n?(.*?)\n?```', text, re.DOTALL) if json_match: text = json_match.group(1).strip() text = re.sub(r',\s*}', '}', text) text = re.sub(r',\s*]', ']', text) text = re.sub(r'//[^\n]*', '', text) try: return json.loads(text) except json.JSONDecodeError as e: start = text.find("{") end = text.rfind("}") if start >= 0 and end > start: try: return json.loads(text[start:end+1]) except json.JSONDecodeError: pass raise ValueError(f"无法解析为JSON,原始文本:{original_text[:200]}") from ellm_output = "```json\n{\n "name": "张三",\n "skills": ["Python", "SQL"],\n}\n```"parsed = safe_json_parse(llm_output)print(parsed["name"]) # 输出:张三
双保险之下,你的数据解析流程将稳如泰山。
🚦 技巧六:自适应速率限制 - 动态调整,不浪费每一毫秒
常见误区:很多教程教你用 time.sleep(1) 来应对限流。但在低峰期,这会让你的程序无谓等待;在高峰期,又可能因为等待不足而再次触发限流。
智能方案:实现一个基于滑动窗口的自适应限速器。它会根据最近的请求历史,动态计算下一次请求的最佳发送时间。
import timefrom collections import dequeclass AdaptiveRateLimiter: """ 自适应速率限制器。 原理:维护一个时间窗口(如60秒),窗口内记录所有请求的时间戳。 当请求数接近限制时,自动计算需要等待的时间。 """ def __init__(self, rpm_limit=500, window_size_seconds=60): """ :param rpm_limit: 每分钟最大请求数 :param window_size_seconds: 时间窗口大小,通常等于60秒 """ self.rpm_limit = rpm_limit self.window_size = window_size_seconds self.window = deque() # 双端队列,存储时间戳 def acquire(self): """ 申请执行一个请求的许可。 如果当前速率已达上限,则阻塞直到有可用的配额。 """ now = time.time() while self.window and self.window[0] < now - self.window_size: self.window.popleft() current_rate = len(self.window) if current_rate >= self.rpm_limit: wait_time = self.window[0] + self.window_size - now + 0.01 # 加一点缓冲 if wait_time > 0: time.sleep(wait_time) return self.acquire() self.window.append(time.time()) return Truelimiter = AdaptiveRateLimiter(rpm_limit=500) # 假设你的API Key每分钟限500次for prompt in prompts_list: limiter.acquire() # 这里可能会阻塞 result = call_llm_api_simple(prompt) # 你的调用函数
它的聪明之处:
- 请求稀疏时
- 请求密集时自动将请求速率平滑到
rpm_limit 以下,避免触发429错误。 - 比固定延迟高效
对于异步程序,你可以实现一个 AsyncAdaptiveRateLimiter,将 time.sleep 替换为 asyncio.sleep。
📚 技巧七:长文本分块处理 - 128K上下文也不是万能的
新手的幻想:“现在GPT-4都有128K上下文了,我把整本产品手册扔进去不就完了?” 现实是:
- 成本爆炸
- 质量下降模型在处理超长上下文时,对中间部分内容的关注度和准确性会下降,俗称“中间失忆”。
- 回复截断风险
成熟策略:分而治之,递归摘要。
def chunk_text_by_paragraph(text, max_chunk_tokens=6000, model="gpt-4o"): """ 按段落分块文本,确保不在句子中间切断,保留语义完整性。 :param max_chunk_tokens: 每个块的最大Token数(需预留生成空间) """ encoding = tiktoken.encoding_for_model(model) paragraphs = text.split("\n\n") chunks = [] current_chunk = "" current_tokens = 0 for para in paragraphs: para_tokens = len(encoding.encode(para)) if current_tokens + para_tokens > max_chunk_tokens and current_chunk: chunks.append(current_chunk.strip()) current_chunk = para current_tokens = para_tokens else: if current_chunk: current_chunk += "\n\n" + para current_tokens += para_tokens else: current_chunk = para current_tokens = para_tokens if current_chunk.strip(): chunks.append(current_chunk.strip()) return chunksdef summarize_long_document(text, api_key): """ 对超长文档进行递归摘要。 步骤:1. 分块。2. 每块独立摘要。3. 合并摘要再摘要。 """ chunks = chunk_text_by_paragraph(text, max_chunk_tokens=6000) print(f"📖 文档被切分为 {len(chunks)} 个语义块。") if len(chunks) == 0: return "" chunk_summaries = [] for i, chunk in enumerate(chunks): print(f" 正在处理第 {i+1}/{len(chunks)} 块...") resp = call_llm_api([ {"role": "system", "content": "你是一个摘要专家。请用3到5句话,准确概括以下文本的核心内容。"}, {"role": "user", "content": chunk} ], api_key) chunk_summaries.append(resp["choices"][0]["message"]["content"]) if len(chunk_summaries) == 1: return chunk_summaries[0] combined_summaries = "\n\n--- 分段摘要 ---\n\n".join( [f"块{i+1}: {summary}" for i, summary in enumerate(chunk_summaries)] ) final_resp = call_llm_api([ {"role": "system", "content": "你是一名编辑。请将以下多段摘要,整合、润色为一篇连贯、流畅、完整的全文摘要。确保逻辑通顺,消除重复。"}, {"role": "user", "content": combined_summaries} ], api_key) return final_resp["choices"][0]["message"]["content"]
这种方法的优势:
- 成本可控
- 质量更高
- 结果更连贯第二轮“摘要的摘要”环节,能有效整合各块信息,消除分割感。
🛡️ 技巧八:分层错误处理 - 给每种异常贴上明确的“病因”标签
最差的代码:一个庞大的 try...except 块,把所有异常混在一起处理,日志里只有一句“调用失败”,让你根本无从排查。
清晰的架构:将错误分为三层,每层责任明确。
def call_llm_robust(messages, api_key, model="gpt-4o-mini", timeout=30): """ 一个健壮的API调用函数,包含清晰的分层错误处理。 """ try: resp = requests.post( "https://api.openai.com/v1/chat/completions", headers={"Authorization": f"Bearer {api_key}"}, json={"model": model, "messages": messages}, timeout=timeout ) if resp.status_code == 429: retry_after = resp.headers.get("Retry-After", "5") raise RuntimeError(f"[HTTP 429] 触发速率限制。建议 {retry_after} 秒后重试,或检查用量配额。") elif resp.status_code == 401: raise RuntimeError("[HTTP 401] API密钥无效、过期或无权限。请检查密钥。") elif resp.status_code == 503: raise RuntimeError("[HTTP 503] 服务暂时不可用(可能正在维护或过载)。请稍后重试。") resp.raise_for_status() data = resp.json() choice = data["choices"][0] finish_reason = choice.get("finish_reason") if finish_reason == "length": raise RuntimeError("[API LENGTH] 回复因达到max_tokens参数而被截断。请增加max_tokens或简化问题。") elif finish_reason == "content_filter": raise RuntimeError("[API FILTER] 回复内容被安全过滤器拦截。请调整提示词或内容。") elif finish_reason == "tool_calls": pass elif finish_reason not in ["stop", "tool_calls"]: raise RuntimeError(f"[API UNKNOWN] 未知的结束原因: {finish_reason}") return choice["message"]["content"] except requests.exceptions.Timeout: raise RuntimeError(f"[NETWORK TIMEOUT] 请求在 {timeout} 秒后超时。请检查网络或增加超时时间。") except requests.exceptions.ConnectionError: raise RuntimeError("[NETWORK CONNECTION] 无法连接到API服务器。请检查网络连接或代理设置。") except json.JSONDecodeError: raise RuntimeError("[DATA FORMAT] API返回了非JSON数据,可能是代理拦截或服务器错误。响应文本开头: " + (resp.text[:100] if 'resp' in locals() else "N/A")) except Exception as e: raise RuntimeError(f"[UNEXPECTED] 未预期的错误: {type(e).__name__}: {e}")
分层处理的价值:
- 快速定位看到错误信息前缀
[HTTP 429]、[API LENGTH] 或 [NETWORK TIMEOUT],你立刻知道问题出在哪个环节。 - 精准决策HTTP 429 可以触发指数退避重试;API LENGTH 需要调整参数而非重试;NETWORK TIMEOUT 可能需要检查防火墙。
- 友好日志监控系统可以根据错误类型设置不同的告警等级和处理流程。
🎬 总结:从“能跑”到“跑得好”
这8个技巧,没有一个是“黑科技”,但它们都是工程实践中提炼出的黄金法则。
- 指数退避重试
- 异步并发和自适应限速,让你的程序在合规前提下跑出极限速度。
- Token计数和成本追踪,给你的每一分预算都装上仪表盘和警报器。
- 结构化输出和长文本分块,让你从模型那里获得高可用、高质量的结果。
技术进阶之路,往往不是去寻找更炫酷的框架,而是把这些基础但至关重要的事情做扎实、做优雅。下次当你写下 requests.post(...) 时,不妨对照一下这张清单:你的代码,离“生产就绪”还差几步?
回看这八大「基建级」技巧,价值究竟在哪里?它不是教你写更花哨的代码,而是帮你建立一道可预测性防线。让每一次API调用从「可能出问题」变成「出问题也能从容应对」。重试、token计数、结构化解析、分层错误处理……本质都是为了让系统在线上稳定运行,让你半夜睡得着觉。
工具链推荐:
- 重试与限流Python里直接用
tenacity + backoff,策略成熟可靠。 - Token与成本
tiktoken 精确计数是基本功,langchain 的 callback 模块可以帮你做成本日志和告警。 - 结构化解析Pydantic的
model_validate 是处理API返回的利器,结合 try-except 能让脏数据无所遁形。 - 监控与调试接入
OpenTelemetry 做分布式链路追踪,关键错误日志一定要带上 request_id。
技巧是死的,思维是活的。真正的工程能力,是把不确定性关进笼子里。