news 2026/10/28 7:25:50

重试三次,订单发出去三份:幂等键该放在哪一层

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
重试三次,订单发出去三份:幂等键该放在哪一层

版权与内容来源声明
本文为原创整理。文中涉及官方文档、开源仓库、论文与公开报道的内容,均在附表 A 中标注来源;引用官方原文保持原样,不作改写。文中命令、版本号与界面截图以本文成文时的实测/核验结果为准,标注「待验证」的部分请以你本地环境实际输出为判断依据。本文不推荐任何不合规的软件获取方式,也不对任何收益结果作承诺。转载请注明出处。

先看现场:同一笔单三条记录,时间戳只差 1 秒多

三条记录不是三次误操作,是同一次操作被重放了三次。三条重复记录的形态是这样:09:14:02.417 | 20261027001 | 199.00 | 待发货 | 小程序;往下隔 1.485 秒,一模一样的一行;再隔 1.486 秒,第三行。三个时间戳挤在 2.971 秒里,业务字段逐字相同。

三条记录除了创建时间,其余字段完全一致

要看的是「哪里相同、哪里不同」:订单号相同、金额相同、收货信息相同、下单渠道相同,只有创建时间和网关侧的请求流水号不同。用户手抖点两次,收货信息、优惠券、备注里至少会有一处不一样,而且不会恰好等间距。字段全同、间隔等距,这不是人的行为,是定时器的行为。

幂等(idempotent)的意思是:同一个操作执行一次和执行多次,对服务端产生的效果相同。这里显然不成立——一次下单的意图,落了三条待发货记录,仓库要发三次货。这就是「副作用重复」,它和「数据不一致」不是一回事:数据本身没有错,每一条都是合法订单,错的是条数。

1.48 秒的等间距,是重试循环的呼吸节奏

第一次到第二次 1.485 秒,第二次到第三次 1.486 秒,差 1 毫秒。人工点击的间隔分布通常在几百毫秒到几秒之间,方差很大;两次都落在 1.48 秒,只能来自「失败 → 等一个固定间隔 → 再打一次」这种循环。1.5 秒减去一次往返的耗时,正好是配置里那个重试间隔。

所以排查方向从一开始就不该是「谁重复提交了」,而应该是「哪一层的重试越过了业务边界」。这也是我写这篇的起点:我在本地把这条链路复现了一遍,改配置、加日志、数调用次数,把「三层重试叠起来会发生什么」跑成了可重复的结果。下面出现的「我」,指的都是我复现和模拟的动作,不是某家公司的现场——我没有亲历那家公司的故障,也没有他们的日志。

故障是一层层叠出来的,不是「重试次数配多了」

重复副作用的成因不是重试次数,是幂等键的作用域比重试的边界小。每一层重试单独看都有充分理由,问题出在它们之间没有共享同一个「这是同一次业务操作」的标识。

三层各自的重试,都能给出正当理由

客户端那层重试,理由是网络抖动:连接被重置、回执在半路丢了,不重试就等于把一次成功当成失败。这一层配 3 次,是多数团队的标准值。

网关或 SDK 那层重试,理由是上游偶发 5xx 和读超时。这一层最容易被忽略,因为它是「别人替你打开的」:以 Stripe 的官方 SDK 为例,截至 2026-10-27 其文档写明 SDK 可以自动生成幂等键并重试请求——听起来是好事,但自动生成的键是随机串,跟你的业务号毫无关系。

队列那层重投,理由是至少一次投递(at-least-once delivery,消息保证被处理至少一次,不保证只处理一次)。消费者如果在可见性超时(visibility timeout,消息被取走后在这段时间内不会投给别人)之前没有确认,消息会重回队列。这一层配 2 次,也不是谁乱配的。

三层都合理,叠起来就是 12 次。这个乘法必须算清,因为它是容量规划和保留期计算的输入。

把乘法算清:客户端 3 × 网关 2 × 队列 2 = 12

重试所在层次数上限什么条件下会再打一次这一层会不会重新生成幂等键
客户端(Agent 调用工具的那个循环)3没收到回执,或收到可重试的失败会——键如果写在循环体里,每次尝试都是一个新键
网关 / SDK 自动重试2上游 5xx、连接被断开、读超时会——SDK 自动生成的是随机串,跟业务号无关
队列(至少一次投递)2消费者未在可见性超时内确认,消息重投不会生成,但也未必透传——消息体里可能压根没有键

最坏情况 3 × 2 × 2 = 12 次真实执行。注意表里第四列的两种「会」性质不同:客户端那层是你的代码在循环体里生成键,网关那层是别人的 SDK在替你生成键。后者更难发现,因为它不在你的代码里,你 review 的时候看不到。

还有一个反直觉的算术:把重试次数从 3 降到 1,最坏情况从 12 降到 4,仍然是 4 份。**降次数只能把重复的规模改小,改不了「会重复」这个性质。**这就是为什么「重试次数配少一点」不构成修复。

HTTP 语义规范早就写明:非幂等方法不该被自动重试

IETF 的 HTTP 语义规范 RFC 9110 在 9.2.2 节给的定义是:「A request method is considered “idempotent” if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent.」——按这份规范的枚举,POST 不在幂等之列。紧接着的一句更直接:

A client SHOULD NOT automatically retry a request with a non-idempotent method unless it has some means to know that the request semantics are actually idempotent, regardless of the method, or some means to detect that the original request was never applied.

用中文说:客户端不应该自动重试非幂等方法,除非它有办法知道这次请求的语义实际上是幂等的,或者有办法判断原始请求根本没有被执行。规范把责任划得很清楚——要自动重试,你得先有办法证明它是幂等的。三层重试都开了、却没有任何一层能证明幂等,这句话就是对着这种配置写的。

那怎么「证明」?Stripe 的做法是让客户端带一个幂等键,服务端拿这个键做记录。截至 2026-10-27,其文档里写明:v1 接口会保存第一次请求的状态码与响应体,用同一个键重试会返回保存的那份响应,连 500 错误也一样保存。这就是「键 → 结果」而不是「键 → 已处理标记」,区别在第 3 章展开。

一个完整案例的复盘:键生成在最内层,等于没有幂等

键的位置决定一切:键在工具层生成,等于每次执行都是新操作;键在最外层按业务语义生成,12 次执行也只是 1 次操作。

实测一:键放在内层,12 次执行就落 12 张单

我把这条链路写成了一段可跑的标准库脚本:最内层是真正写库的create_order,外面套队列投递、网关尝试、客户端尝试三层循环,失败序列固定为「前 N 次物理执行已生效但回执丢失,第 N+1 次回执正常」,不用随机数,也不计时。三种键的口径各跑两遍——只开客户端重试、以及三层全叠。

🧪 实测环境:Python 3.13.12 / macOS / 仅标准库 —— 脚本work/exp1027/e04_retry.py,下方围栏即该文件全文,输出为其真实 stdout

"""e04 实测一:三层重试叠加时,幂等键放在哪一层决定副作用条数。 只依赖标准库。失败序列是固定的:第 1 次到第 N-1 次物理执行都「已生效但回执丢失」, 第 N 次回执正常返回,流程收敛。不用随机数、不计时,输出逐字可复现。 """ORDER_NO="20261027001"CLIENT_ATTEMPTS=3GATEWAY_ATTEMPTS=2QUEUE_DELIVERIES=2STORE={}# 服务端幂等表:键 -> 记录号RECORDS=[]# 真正落库的副作用CALLS=0# 物理执行次数LOST_UNTIL=0# 前多少次物理执行的回执会丢defcreate_order(key):"""最内层:真正写库。返回本次是否拿到回执。"""globalCALLS CALLS+=1ifkeyisnotNoneandkeyinSTORE:pass# 命中幂等表:不写库,直接回放上次结果else:RECORDS.append(len(RECORDS)+1)ifkeyisnotNone:STORE[key]=len(RECORDS)returnCALLS>LOST_UNTILdefrun(strategy,lost_until,gateway_attempts,queue_deliveries):"""strategy: none / inner / outer,决定键在哪一层生成。"""globalSTORE,RECORDS,CALLS,LOST_UNTIL STORE,RECORDS,CALLS,LOST_UNTIL={},[],0,lost_until business_key="order:%s:create"%ORDER_NO# 最外层入口生成一次,此后只透传seq=[0]defkey_for_call():ifstrategy=="none":returnNoneifstrategy=="inner":seq[0]+=1return"auto-%03d"%seq[0]# 每次进入工具层现生成一个returnbusiness_key# 全程同一个,逐层透传for_inrange(CLIENT_ATTEMPTS):done=Falsefor_inrange(gateway_attempts):for_inrange(queue_deliveries):ifcreate_order(key_for_call()):done=Truebreakifdone:breakifdone:breakreturnCALLS,len(STORE),len(RECORDS)defwidth(s):returnsum(2if0x3000<=ord(c)<=0x9FFFelse1forcins)defshow(head,rows):widths=[max(width(r[i])forrin[head]+rows)+2foriinrange(len(head))]forrin[head]+rows:line=""fori,cellinenumerate(r):line+=cell+" "*(widths[i]-width(cell))print(line.rstrip())LABELS={"none":"无键","inner":"内层现生成","outer":"外层业务键"}rows=[]# 场景一:只有客户端在重试,内层各只打一次;回执丢 2 次,第 3 次成功forsin("none","inner","outer"):calls,keys,n=run(s,2,1,1)rows.append(("仅客户端 %d 次"%CLIENT_ATTEMPTS,LABELS[s],str(calls),str(keys),str(n)))# 场景二:客户端 3 x 网关 2 x 队列 2 全叠上;回执丢 11 次,第 12 次成功forsin("none","inner","outer"):calls,keys,n=run(s,11,GATEWAY_ATTEMPTS,QUEUE_DELIVERIES)rows.append(("%d x %d x %d 三层叠加"%(CLIENT_ATTEMPTS,GATEWAY_ATTEMPTS,QUEUE_DELIVERIES),LABELS[s],str(calls),str(keys),str(n)))show(("重试层级","键的口径","物理执行","不同键数","副作用条数"),rows)print("\n最坏情况实际执行次数 = %d x %d x %d = %d"%(CLIENT_ATTEMPTS,GATEWAY_ATTEMPTS,QUEUE_DELIVERIES,CLIENT_ATTEMPTS*GATEWAY_ATTEMPTS*QUEUE_DELIVERIES))
重试层级 键的口径 物理执行 不同键数 副作用条数 仅客户端 3 次 无键 3 0 3 仅客户端 3 次 内层现生成 3 3 3 仅客户端 3 次 外层业务键 3 1 1 3 x 2 x 2 三层叠加 无键 12 0 12 3 x 2 x 2 三层叠加 内层现生成 12 12 12 3 x 2 x 2 三层叠加 外层业务键 12 1 1 最坏情况实际执行次数 = 3 x 2 x 2 = 12

这张表要分三组读。

第一组三行复刻的就是现场那条形态:只有客户端在重试,内层各打一次,3 次物理执行落了 3 张单。注意「内层现生成」那一行也是 3,键数是 3、副作用也是 3:**每次执行都生成一个新键,跟没有键的结果一模一样。**这就是「键在最内层等于没有幂等」的量化版本。

第二组三行是把网关的自动重试和队列重投打开之后:物理执行从 3 涨到 12,无键和内层生成都变成 12 条副作用。同一个 bug,只是把两个开关打开,最坏损失从 3 份变成 12 份。这两行数字之间没有别的原因,就是那笔乘法。

第三组看最后一行:12 次物理执行、1 个键、1 条副作用。键在正确的位置时,重试次数的增长不改变副作用条数——这才是「重试可以放心开」的前提。表里「不同键数」这一列是我特意加的,它比「副作用条数」更早暴露问题:只要这个数跟着物理执行次数一起涨,键就没起到作用。

实测二:同一个键按什么口径算,结果差 5 倍

键放在外层只是位置对了,口径还可能错。我拿同一笔业务、五次重试尝试,试了五种生成口径。五次请求体只差两个字段:client_ts和trace_id——这两个字段在真实链路里每次重试都会变,因为重试通常走一个新的请求上下文。

🧪 实测环境:Python 3.13.12 / macOS / 仅标准库 —— 脚本work/exp1027/e04_key.py,下方围栏即该文件全文,输出为其真实 stdout

"""e04 实测二:同一笔业务、五种键口径,服务端能把几次尝试认成同一次。 五次尝试的请求体只差两个字段:client_ts 与 trace_id(重试时必然变化)。 不用随机数、不计时,键样本由 sha256 截断得到,输出逐字可复现。 """importhashlibimportjson ORDER_NO="20261027001"TS=[0,0,1,1,2]# 五次尝试的秒偏移,固定TRACE=["t-a1","t-b2","t-c3","t-d4","t-e5"]UUIDS=["9f3a2c1b-7e04-4a11-8c35-0b7d21e6a901","2c8d5e40-1b93-4f67-a2d8-6e04c17b3f52","7a01f9d3-5c26-4e88-b713-8d2f40a6c9e4","4e62b8c7-0d15-4932-9af6-1c83e5b07d29","b58c07e2-6a41-4f39-8d20-3e97a1c46bf8"]defbody(i):return{"order_no":ORDER_NO,"action":"create","amount":199,"client_ts":1761527700+TS[i],"trace_id":TRACE[i]}defh(obj):raw=json.dumps(obj,sort_keys=True,ensure_ascii=False).encode()returnhashlib.sha256(raw).hexdigest()[:12]defkey_of(name,i):b=body(i)ifname=="uuid":returnUUIDS[i]ifname=="ts":return"%d-%s"%(b["client_ts"],ORDER_NO)ifname=="body_all":returnh(b)ifname=="body_biz":returnh({k:vfork,vinb.items()ifknotin("client_ts","trace_id")})return"order:%s:create"%ORDER_NOdefwidth(s):returnsum(2if0x3000<=ord(c)<=0x9FFFelse1forcins)defshow(head,rows):widths=[max(width(r[i])forrin[head]+rows)+2foriinrange(len(head))]forrin[head]+rows:line=""fori,cellinenumerate(r):line+=cell+" "*(widths[i]-width(cell))print(line.rstrip())CASES=[("uuid","每次调用新生成 UUID4"),("ts","业务号 + 秒级时间戳"),("body_all","整个请求体哈希"),("body_biz","请求体哈希(剔除时间戳/链路号)"),("order","业务号 + 动作"),]rows=[]forname,labelinCASES:keys=[key_of(name,i)foriinrange(len(TS))]distinct=len(set(keys))rows.append((label,keys[0][:24],str(distinct),str(len(TS)-distinct),str(distinct)))show(("键的生成口径","第 1 次的键","不同键数","认成同一次","副作用条数"),rows)print("\n共 %d 次尝试;「认成同一次」= 尝试次数 - 不同键数"%len(TS))
键的生成口径 第 1 次的键 不同键数 认成同一次 副作用条数 每次调用新生成 UUID4 9f3a2c1b-7e04-4a11-8c35- 5 0 5 业务号 + 秒级时间戳 1761527700-20261027001 3 2 3 整个请求体哈希 81390fa2bd70 5 0 5 请求体哈希(剔除时间戳/链路号) 4591891d64c1 1 4 1 业务号 + 动作 order:20261027001:create 1 4 1 共 5 次尝试;「认成同一次」= 尝试次数 - 不同键数

五种口径,同五次尝试,副作用从 5 条到 1 条。

第三行和第四行最值得对照:两行都是哈希,一行 5 条、一行 1 条。差别只在于哈希的输入里有没有client_ts和trace_id。**只要键的输入包含任何「每次请求都会变」的字段,它就不再是一个键,而是一个流水号。**时间戳、随机数、请求 id、纳秒级时间,都属于这一类。

第二行最能骗人:业务号 + 秒级时间戳,看起来「有业务号了,应该没问题」。但那五次尝试落在三个不同的秒里,于是变成 3 个键、3 条副作用。秒级时间戳能不能合并,取决于重试间隔是否小于 1 秒——这是一个纯粹的运气问题,不是设计。同理,把时间戳换成日期,重试跨过零点照样裂开。

第四行和第五行都是 1 条,但两者有个重要区别:第四行的键是算出来的,它的稳定性依赖于「业务字段集合」这个约定;以后有人在请求体里加一个nonce或timestamp,这个键会静默地裂开,而且没有任何报错。第五行的键是写出来的,业务号 + 动作是一个显式的约定,改了就得改代码,改代码就会被 review 看见。我选第五种,理由是它的失效方式是可发现的。

服务端要存「键 → 结果」,不是「键 → 已处理」

键传到了、口径也对,还差最后一步:服务端拿这个键做什么。

只存一个「已处理」标记的写法很常见,也很危险:重试打进来,服务端发现键已存在,直接返回一个「重复请求」的空响应。调用方拿到这个响应,既不知道原来那次的订单号,也不知道金额,只能自己去查——而「查」这个动作本身又可能失败。于是有的客户端把「重复请求」当成失败,继续重试;有的把它当成成功,但拿不到结果,只好走补偿逻辑。

Stripe 的文档在这一步给的是另一个答案,原文的意思是:v1 接口会保存第一次请求的状态码和响应体,同一个键的重试返回保存的那份响应,包括 500 错误在内。这就把「重试」变成了「读缓存」:第一次的结果是什么,后面每一次拿到的就是什么。幂等不只是「不重复执行」,还包括「重复请求拿到同一份结果」。

我改了什么:四条硬规矩

四条规矩都是「位置 + 内容 + 时限 + 冲突」四个维度上的硬约束,任何一条松掉,其余三条都会退化成形式。

第一,幂等键在最外层入口生成,全链路透传,工具层禁止生成。入口指的是「用户意图被翻译成一次业务操作」的那一层,通常是 API 的 handler 或者编排层。键一旦生成就写进请求上下文,随调用链一路带下去;工具层(真正调用外部服务的那个函数)只允许读取,不允许创建。落地时加一条静态检查就够:工具层代码里出现生成键的调用,CI 直接失败。这条能拦住实测一里「内层现生成」那三行。

第二,服务端存「键 → 结果」,不存「键 → 已处理」。存的必须是能直接返回给调用方的那份结果:业务单号、状态、以及必要的响应字段。存标记的写法省不了多少空间,却把重试方逼进了「重复但拿不到结果」的灰色地带。

第三,键的保留期 ≥ 最长重试链路的总时长 × 安全系数。这笔账要按实测一的最大值算:12 次执行,若每次退避上限 30 秒,最坏等 360 秒;队列可见性超时 60 秒、重投 2 次,再加 120 秒;合计约 8 分钟。这里的「退避上限」不是随口取的——退避必须封顶、并且要加抖动,这是云厂商架构博客里反复讲的工程约束(重试间隔若一直翻倍会等到不可接受的时长,若所有客户端同时按同一节奏重试又会形成同步的尖峰)。安全系数取 10,得 80 分钟。但这个数只覆盖「机器还在重试」的场景,覆盖不了「人第二天对账才发现」。所以取 24 小时更稳——截至 2026-10-27,Stripe 文档写的是键至少保留 24 小时、之后可以移除,键被移除后重放同一个键可能执行一次新请求。保留期短于重试窗口,等于自己制造重复。

第四,同键不同体必须拒绝,不能覆盖。同一个键、不同的参数,说明要么键的口径错了(比如键里少了区分维度),要么是两次不同的业务撞了键。两种情况都不该静默接受。Stripe 的处理是返回一个表示「与原始请求不匹配」的错误;自己实现时,至少要做到「不比就拒」:先比对请求体的业务字段,不一致直接拒,不要用新请求体覆盖旧结果。覆盖的后果最难查——第一条记录的金额会被第二条悄悄改掉。

这份资料的用法:文里这两段脚本可以直接跑,配套的重试链路配置模板、幂等表建表语句和键口径检查清单也整理进了资料包,扫码即可获取:

比技术更该先想清楚的事:谁定义键、保留多久、冲突怎么回

幂等键的难点不在代码,在三个归属问题:谁有权定义「什么算同一次操作」、谁来决定保留多久、撞键时谁说了算。这三个问题不定,四条规矩落不下去。

业务方定义键的语义。「订单号 + 动作」里的「动作」是什么粒度,只有业务能回答:改地址算不算新操作?部分退款和全额退款是不是同一个动作?重发一次通知算不算?这些问题写成代码就是键的字符串拼接,写成文档就是一张动作清单。技术侧能提供的是约束和检查,提供不了答案。这一条最容易被跳过,因为它是唯一一条不写代码的规矩。

框架和 SDK 保证透传。键要能从入口一路带到最内层,中间任何一次序列化、任何一层网关、任何一个异步任务的入参裁剪,都可能把它弄丢。这里有个具体的坑:把键放在请求体的可选字段里,比放在请求头或调用上下文里危险得多——请求体是最容易被改写、裁剪、脱敏的地方。透传的验收方式很简单:在最内层的日志里打一行键,跑一次全链路,看这行和入口那行是否逐字相同。不相同就是没透传。

SRE 定保留期并对齐重试窗口。保留期是个跨团队的契约:它必须大于重试链路的总时长,也必须大于「人能发现并介入」的时长。这两个数分别由框架配置和值班节奏决定,只有 SRE 同时看得到。落地时把它做成配置项,并且跟重试次数联动——有人把网关重试从 2 改到 5,保留期就该同步重算,而不是靠记得。

三条并起来,四条规矩各有一个人名可以追:

硬规矩归谁验收方式(能当场做的)
键在最外层生成、全链路透传业务方定语义,框架 / SDK 保证透传入口和最内层各打一行键,跑一次全链路,两行逐字相同
服务端存「键 → 结果」服务端实现方拿同一个键重放,比两次响应体是否字节相同
保留期 ≥ 重试总时长 × 安全系数SRE保留期配置与重试次数写在同一个配置文件里,改一个必须改另一个
同键不同体拒绝业务方定「体」的字段范围,服务端执行构造一次改金额的同键请求,看是否被拒

没有任何一条能只靠「注意一下幂等性」落地——它会落到某个人、某个配置项、某次 code review 上,否则就是一句写在文档里的话。

什么时候该停手:当你在讨论「要不要把重试次数降下来」时,停下来,先回答键在哪一层生成。次数是可以调的参数,键是结构;调参数解决不了结构问题。反过来,如果键的位置和口径都对,重试次数反而可以放心往上加——实测一最后一行那 12 次执行只落 1 条副作用,就是这个意思。

这份资料的用法:键的动作清单模板、透传验收脚本、保留期与重试次数的联动配置样例都在资料包里,扫码即可获取:

附表 A:本文引用事实与出处对照表

事实出处(文档名 + 发布方 + 链接)本文位置
原文:A request method is considered “idempotent” if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent.RFC 9110《HTTP Semantics》9.2.2 节,IETF,https://www.rfc-editor.org/rfc/rfc9110.html第 2 章
原文:A client SHOULD NOT automatically retry a request with a non-idempotent method unless it has some means to know that the request semantics are actually idempotent, regardless of the method, or some means to detect that the original request was never applied.同上第 2 章
原文:For API v1, Stripe saves the status code and body of the first request made with a given idempotency key after endpoint execution begins. Retries with the same key return the saved response, including 500 errors.《Authentication》Idempotent requests 节,Stripe,https://docs.stripe.com/api/authentication第 2、3 章
原文:Stripe retains keys for at least 24 hours and can remove them after that. Reusing a key after it’s removed can execute a new request. / Stripe SDKs can generate idempotency keys and retry requests automatically.《Advanced error handling》POST requests 节、Idempotency 节,Stripe,https://docs.stripe.com/error-low-level第 2、4 章
原文:Sending the same idempotency key with different parameters produces an error indicating that the request didn’t match the original.同上(Network errors 节)第 4 章
原文:Capped exponential backoff means that clients multiply their backoff by a constant after each attempt, up to some maximum value. / It’s to add jitter.《Exponential Backoff And Jitter》,AWS Architecture Blog,https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/第 4 章(保留期算式里的退避上限)
三种键口径 × 两种重试层级下的物理执行次数、不同键数与副作用条数本文实测一(Python 3.13.12,仅标准库,work/exp1027/e04_retry.py)第 3 章
五种键口径下五次重试尝试的不同键数与副作用条数本文实测二(Python 3.13.12,仅标准库,work/exp1027/e04_key.py)第 3 章

写在最后:这篇用到的资料

写这篇文章时,把相关的官方文档和源码又翻了一遍,顺手也整理了几份配套的东西:

  • 大模型学习路线图:从零基础到能自己动手做 Agent,按阶段说明每一步该学什么、哪些可以先跳过
  • 《LangChain + LangGraph + MCP 智能体开发实战》视频课:7 个模块,从私有化部署、Embedding+RAG 到 MCP+Agent 全流程
  • AI 大模型知识库(在线可查):Agent Skills 从入门到落地、Claude Skills 完全指南等专题,按目录浏览即可
  • 640 套 AI 大模型行业报告 + 经典 PDF 书籍:看行业落地案例和别人怎么做的时候用得上
  • 大模型零基础到精通教学视频:跟着敲一遍,比只读文档快得多

资料是我自己整理的,放在下面这个码上,扫码即可获取:






添加时备注「AI」,优先通过。

资料按「先路线、再动手、最后查漏」的顺序整理好了,建议先看学习路线那一份,照着它挑一条适合自己当前基础的路径再往下看。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 7:25:37

慢SQL优化全流程:从监控到治理的实战指南

1. 慢SQL优化实战&#xff1a;从定位到解决的完整指南SQL查询性能问题就像高速公路上的堵车点&#xff0c;会拖累整个系统的运行效率。作为从业十年的数据库老兵&#xff0c;我处理过上千个慢SQL案例&#xff0c;发现80%的性能问题都源于少数几个典型模式。今天我们就来拆解慢S…

作者头像 李华
网站建设 2026/10/28 7:25:27

HexView V1.12.05 汽车电子固件处理实战:地址、段与校验

简介&#xff1a;HexView&#xff08;Vector&#xff09;V1.12.05 是一款由 Vector 出品的十六进制查看与编辑工具&#xff0c;主要面向汽车电子、嵌入式开发及数据格式转换方向的个人学习者。它可查看 Intel-HEX、Motorola S-record 及部分车厂专用格式文件&#xff0c;并支持…

作者头像 李华
网站建设 2026/10/28 7:25:22

自研轻量神经网络:普通显卡也能跑完训练的开源实践

说实话&#xff0c;写这个项目之前&#xff0c;我自己也被“深度学习必须靠大显卡、高算力”的说法劝退过很久。直到我把目标调转过来——不是去适配某个花哨的模型&#xff0c;而是设计一个网络结构&#xff0c;让一张普通的游戏显卡在几个小时内跑完一轮完整训练。这个自研的…

作者头像 李华
网站建设 2026/10/28 7:24:07

大模型全景指南:从原理、选型到落地实践

这两年问大模型的人明显变多了&#xff0c;而且问法一直在变。刚开始是"GPT是什么、能干嘛"&#xff0c;后来变成"开源模型选哪个、怎么部署"&#xff0c;现在更多是"我们公司想接入大模型&#xff0c;到底该走API还是私有化、微调和RAG怎么选、Agent…

作者头像 李华
网站建设 2026/10/28 7:23:54

VITS2端到端TTS模型原理与ONNX CPU部署实践指南

1. 为什么我会写下这份 VITS2 读书笔记1.1 从 VITS 到 VITS2&#xff1a;端到端 TTS 的演进做 TTS 的朋友&#xff0c;大概率都经历过“先提特征、再丢给声码器”的两阶段时代。那时候最头疼的不是模型本身不够强&#xff0c;而是上游发音不准、下游音质差&#xff0c;两套系统…

作者头像 李华
网站建设 2026/10/28 7:22:31

单文件AI编码代理:融合GUI自动化与MCP协议的开源实践

写这个项目的时候我其实没指望它能火。当时手头堆了好几个重复性工作&#xff1a;改前端样式、调接口、整理 GUI 自动化测试脚本&#xff0c;每个都得开着 IDE 一遍遍手动点。后来我把常用的编码辅助能力封装成一个只有单个文件的 AI 编码代理&#xff0c;它能直接操控图形界面…

作者头像 李华