@@ -42,13 +42,16 @@ Guide 见过太多这样的事故。真正难的并非”怎么发一个 HTTP
4242
4343``` mermaid
4444flowchart LR
45- User["用户请求"] --> App["业务服务"]
46- App --> Prompt["Prompt 与上下文组装"]
47- Prompt --> Gateway["模型网关"]
48- Gateway --> Provider["供应商 API"]
49- Provider --> Stream["流式事件"]
50- Stream --> Parser["增量解析"]
51- Parser --> Sink["前端展示、落库与观测"]
45+ User["用户请求"]:::client
46+ App["业务服务"]:::business
47+ Prompt["Prompt 组装"]:::business
48+ Gateway["模型网关"]:::gateway
49+ Provider["供应商 API"]:::external
50+ Stream["流式事件"]:::infra
51+ Parser["增量解析"]:::infra
52+ Sink["前端/落库/观测"]:::success
53+
54+ User --> App --> Prompt --> Gateway --> Provider --> Stream --> Parser --> Sink
5255
5356 classDef client fill:#00838F,color:#FFFFFF,stroke:none,rx:10,ry:10
5457 classDef business fill:#E99151,color:#FFFFFF,stroke:none,rx:10,ry:10
@@ -57,12 +60,6 @@ flowchart LR
5760 classDef infra fill:#9B59B6,color:#FFFFFF,stroke:none,rx:10,ry:10
5861 classDef success fill:#4CA497,color:#FFFFFF,stroke:none,rx:10,ry:10
5962
60- class User client
61- class App,Prompt business
62- class Gateway gateway
63- class Provider external
64- class Stream,Parser infra
65- class Sink success
6663 linkStyle default stroke-width:2px,stroke:#333333,opacity:0.8
6764```
6865
@@ -109,7 +106,7 @@ TTFT(Time To First Token)指从请求发出到收到第一个可展示 Token
109106
110107Guide 的经验:面向用户展示的长文本默认用流式,后台批处理和强结构化任务默认用同步。
111108
112- ## SSE、WebSocket 和 HTTP chunked 这三种流式协议怎么选
109+ ## ⭐️ SSE、WebSocket 和 HTTP chunked 这三种流式协议怎么选
113110
114111流式输出有几种常见承载方式,别把它们混成一个东西。
115112
@@ -327,7 +324,7 @@ tenantId:userId:conversationId:messageId:attemptGroup
327324
328325落库时,只允许一个 attempt 成为 ` final ` 。其他 attempt 保留为诊断记录,不参与用户上下文。这样既能排查问题,又不会污染下一轮 Prompt。
329326
330- ## 为什么要限流?如何限流?
327+ ## ⭐️ 为什么要限流?如何限流?
331328
332329很多团队的限流是从收到 429 开始的。
333330
@@ -344,6 +341,49 @@ AI 应用的限流应该在自己的系统里先完成。供应商的 429 是最
344341| 模型级 | 某个模型或模型族 | 避免热门模型被打满 | 模型维度令牌桶、降级到备用模型 |
345342| 供应商级 | OpenAI、Anthropic、Gemini 等 | 保护外部依赖和 API Key | 全局 RPM、TPM、并发、熔断 |
346343
344+ ``` mermaid
345+ flowchart TB
346+ subgraph User["用户层"]
347+ U1["单用户/账号"]:::client
348+ U2["每分钟请求数"]:::info
349+ U3["每日 Token 上限"]:::info
350+ end
351+
352+ subgraph Tenant["租户层"]
353+ T1["企业/团队/项目"]:::business
354+ T2["月度配额"]:::info
355+ T3["并发上限"]:::info
356+ end
357+
358+ subgraph Model["模型层"]
359+ M1["指定模型/模型族"]:::gateway
360+ M2["令牌桶"]:::info
361+ M3["降级备用模型"]:::info
362+ end
363+
364+ subgraph Provider["供应商层"]
365+ P1["OpenAI/Anthropic\n/Gemini"]:::external
366+ P2["全局 RPM/TPM"]:::info
367+ P3["熔断器"]:::info
368+ end
369+
370+ User --> Tenant --> Model --> Provider
371+
372+ classDef client fill:#00838F,color:#FFFFFF,stroke:none,rx:10,ry:10
373+ classDef business fill:#E99151,color:#FFFFFF,stroke:none,rx:10,ry:10
374+ classDef gateway fill:#7B68EE,color:#FFFFFF,stroke:none,rx:10,ry:10
375+ classDef external fill:#607D8B,color:#FFFFFF,stroke:none,rx:10,ry:10
376+ classDef success fill:#4CA497,color:#FFFFFF,stroke:none,rx:10,ry:10
377+ classDef info fill:#95A5A6,color:#FFFFFF,stroke:none,rx:10,ry:10
378+
379+ style User fill:#F5F7FA,stroke:#005D7B,stroke-width:2px,rx:10,ry:10
380+ style Tenant fill:#F5F7FA,stroke:#005D7B,stroke-width:2px,rx:10,ry:10
381+ style Model fill:#F5F7FA,stroke:#005D7B,stroke-width:2px,rx:10,ry:10
382+ style Provider fill:#F5F7FA,stroke:#005D7B,stroke-width:2px,rx:10,ry:10
383+
384+ linkStyle default stroke-width:2px,stroke:#333333,opacity:0.8
385+ ```
386+
347387Gemini 官方限流文档把限流维度拆成 RPM、输入 TPM、RPD,并说明限制按项目而不是单个 API Key 应用;OpenAI 官方文档也展示了请求数、Token 数、剩余额度等 rate limit header。具体数值和模型关系变化很快,生产系统不要把文档里的静态数字写死,要从控制台、响应头或配置中心动态管理。
348388
349389### 为什么 Token 预算比请求数更重要
@@ -485,6 +525,48 @@ OpenAI 官方 Structured Outputs 文档强调可以让输出遵循开发者提
4855253 . ** 降级 Schema** :复杂对象拆成多个小对象,或先分类再抽取字段。
4865264 . ** 人工或规则兜底** :高价值订单、金融、医疗、法务场景不要完全依赖自动修复。
487527
528+ ``` mermaid
529+ flowchart TB
530+ Start([结构化输出失败]):::client
531+ L1["第一级:本地校验"]:::business
532+ L1A["JSON Schema\nJackson\nBean Validation"]:::info
533+
534+ L2["第二级:轻量修复"]:::business
535+ L2A["只修格式\n不重新生成业务内容"]:::info
536+
537+ L3["第三级:降级 Schema"]:::business
538+ L3A["拆成多个小对象\n先分类再抽取字段"]:::info
539+
540+ L4["第四级:人工兜底"]:::danger
541+ L4A["高价值订单\n金融/医疗/法务"]:::info
542+
543+ Success([完成]):::success
544+ Fail([标记异常\n人工处理]):::danger
545+
546+ Start --> L1
547+ L1 --> L1A
548+ L1A -->|校验通过| Success
549+ L1A -->|校验失败| L2
550+ L2 --> L2A
551+ L2A -->|修复成功| Success
552+ L2A -->|修复失败| L3
553+ L3 --> L3A
554+ L3A -->|降级成功| Success
555+ L3A -->|降级失败| L4
556+ L4 --> L4A --> Fail
557+
558+ classDef client fill:#00838F,color:#FFFFFF,stroke:none,rx:10,ry:10
559+ classDef business fill:#E99151,color:#FFFFFF,stroke:none,rx:10,ry:10
560+ classDef success fill:#4CA497,color:#FFFFFF,stroke:none,rx:10,ry:10
561+ classDef danger fill:#C44545,color:#FFFFFF,stroke:none,rx:10,ry:10
562+ classDef warning fill:#F39C12,color:#FFFFFF,stroke:none,rx:10,ry:10
563+ classDef info fill:#95A5A6,color:#FFFFFF,stroke:none,rx:10,ry:10
564+
565+ linkStyle default stroke-width:2px,stroke:#333333,opacity:0.8
566+ linkStyle 2,4,6,8 stroke:#4CA497,stroke-width:2px
567+ linkStyle 9 stroke:#C44545,stroke-width:2px,stroke-dasharray:5 5
568+ ```
569+
488570一个实用原则:结构化返回失败时,不要把原始自然语言硬塞给下游系统。能展示给用户,不代表能被程序执行。
489571
490572## Java 后端怎么落地 LLM 调用?
0 commit comments