从需求到稳定运行:工程全景
前面的笔记按主题拆开解释概念。这一篇换一种方式:只用一个案例,把分散的知识重新放回完整链路。目标不是再列一遍技术名词,而是让你看到一个功能从需求、代码和数据,走到浏览器、服务器和线上运行环境时,每一层分别在回答什么问题。
案例故意做得很小。它不是完整产品,但已经包含保存、查询、权限、失败处理、部署和可选模型能力。把这条链路走通以后,换成支付、聊天、内容平台或 Agent 任务,变化的只是业务规则和处理成本,排查问题的位置并没有变。
1. 先建立一张能复述的全景图
跳转到“1. 先建立一张能复述的全景图”一个在线系统可以先用下面的顺序理解:
用户目标 ↓浏览器交互 ↓DNS / TLS / CDN / WAF ↓负载均衡 / API ↓应用服务 ↓数据库 / 缓存 / 队列 / 对象存储 / 模型服务 ↓部署、日志、指标、trace、备份与回滚请求向下走,反馈向上走。用户看到的结果只是最后一层;真正决定系统能否稳定运行的,是每一层怎样处理输入、保存状态、返回输出,以及失败时怎样收场。
这张图不是组件清单。图上每增加一个框,就多一套部署、监控、权限和故障模式。小系统不需要一开始就有缓存、消息队列、微服务和向量数据库。更可靠的做法是先画出最小主链路,再根据证据增加组件。
还有几项能力横穿所有层:
- 身份与权限:谁能读、写、删除什么。
- 安全:输入、文件、网络请求、密钥和模型输出是否可信。
- 测试:正常路径、异常路径和边界条件怎样验证。
- 版本与交付:代码、配置、数据库迁移能否一起发布和回滚。
- 可观测性:线上是否真的按照设计在工作。
- 成本:请求、存储、模型调用和运维人力是否可持续。
2. 案例:一个学习资料收藏夹
跳转到“2. 案例:一个学习资料收藏夹”假设要做一个只给自己和小团队使用的收藏夹,用来保存学习资料、写几句笔记并打标签。
第一版只做这些事:
- 保存一条资料:URL、标题、笔记、标签。
- 按时间浏览,按关键词和标签筛选。
- 修改、归档和删除自己的资料。
- 所有资料默认私有,不能看到别人的内容。
明确不做:多人实时编辑、公开分享页、推荐流、跨设备离线同步、自动摘要。它们不是永远不做,而是暂时不进入第一版。
还要写下数量假设。没有数字,架构讨论很容易变成“越高并发越好”:
| 维度 | 第一版假设 |
|---|---|
| 用户 | 1,000 个注册用户,100 个同时在线 |
| 数据 | 10 万条收藏,每条连同索引约 2 KB |
| 流量 | 峰值约 20 QPS,读取明显多于写入 |
| 延迟 | 保存 p95 小于 500 ms,列表 p95 小于 300 ms |
| 可用性 | 月度 99.5%,允许短暂只读或功能降级 |
| 备份 | RPO 24 小时,RTO 2 小时 |
按这些数字估算:
并发请求 ≈ 20 QPS × 0.1 秒 ≈ 2 个原始数据 ≈ 100,000 × 2 KB ≈ 200 MB第一版用一个应用服务和一个 PostgreSQL 就足够。这个结论不是“永远不扩容”,而是让当前方案与当前约束匹配。升级应该由指标触发,例如数据库连接池长期接近上限、查询 p95 持续超出预算、搜索扫描行数过大、模型请求阻塞主接口,而不是由“听说大公司都这么做”触发。
3. 从源码到可运行进程
跳转到“3. 从源码到可运行进程”浏览器发出的一个 HTTP 请求,最终会变成网卡上的字节;服务代码由操作系统调度成进程;数据经过内存、文件系统和磁盘;跨机器通信还要经过 DNS、路由和 TLS。平时不需要记住每个硬件细节,但必须能在故障时判断问题发生在哪一层。
例如:
connection refused更像进程没监听、端口写错或被防火墙拒绝。- TLS 握手失败更像证书、域名、时间或代理配置问题。
- 请求已经进入服务但很慢,要继续看应用、数据库、缓存或下游依赖。
- 服务被系统杀死,要检查内存、资源限制和进程日志,而不只是改业务代码。
更完整的层次关系见计算机系统一页通。
3.1 让环境可复现
跳转到“3.1 让环境可复现”“在我机器上能跑”通常意味着运行条件没有被记录。至少要固定:
操作系统或容器镜像语言运行时版本依赖清单与锁文件数据库版本启动命令和端口配置项名称数据库迁移与初始化数据配置和密钥分开。仓库里可以放变量名和没有敏感信息的示例:
APP_ENV=developmentAPP_PORT=3000DATABASE_URL=postgres://user:password@localhost:5432/bookmarksSESSION_SECRET=replace-meMODEL_API_KEY=optionalMODEL_API_KEY 只能存在于服务端,不能进入浏览器代码、日志或 Git 历史。真实值通过环境变量或密钥管理服务注入。
推荐把启动、迁移、测试和健康检查写成明确命令,而不是依赖某个人记得一串步骤。容器可以帮助固定运行环境,但它不是备份,也不会自动解决配置、数据迁移或网络问题。相关笔记:可复现环境、Linux 与部署。
3.2 用版本库保存“为什么”
跳转到“3.2 用版本库保存“为什么””Git 记录的不只是文件差异。一个好的提交应该说明这次改动解决了什么问题,必要时把接口、迁移、测试和文档放在同一个变更里。数据库结构变化尤其不能只改线上,而不留下迁移脚本。
不要提交真实 .env、数据库文件、用户数据或密钥。提交前先看 git status 和 git diff,确认没有把调试输出、临时文件和凭据带进去。
调试时按下面的顺序缩小范围,比同时重装依赖、清缓存和改代码有效得多:
- 写下预期结果和实际结果。
- 找到能稳定复现的最小步骤。
- 判断请求停在哪一层:浏览器、网络、服务、数据库还是外部依赖。
- 每次只改变一个变量,记录结果。
- 修复后用测试和真实请求验证,而不是只看日志里不再报错。
4. 浏览器怎样把一次操作变成请求
跳转到“4. 浏览器怎样把一次操作变成请求”用户打开页面时,浏览器获取 HTML、CSS 和 JavaScript,解析成 DOM 和 CSSOM,经过布局、绘制与合成后显示出来。前端框架可能还涉及组件渲染、状态更新和水合。点击“保存”以后,JavaScript 处理事件、组织请求、等待响应,再更新界面。
这条路径决定了前端必须处理的不只是“成功”:
空闲 → 保存中 → 已保存 ↘ 失败 → 重试 / 修改输入保存期间要禁用重复提交,显示明确状态;失败时保留用户输入,给出可操作的错误;没有数据时显示空状态,而不是一片空白。不要在网络请求还没成功时假装已经保存。
4.1 先定接口,再写两端代码
跳转到“4.1 先定接口,再写两端代码”前端和服务器之间需要一个明确契约。保存收藏可以用下面的请求:
POST /api/bookmarksContent-Type: application/jsonCookie: session=<opaque-token>Idempotency-Key: 7c3e4b1d-8b7a-4f6d-9e63-2f9a30c7c711
{ "url": "https://example.com/http-cache", "title": "HTTP 缓存笔记", "note": "整理缓存命中与失效", "tags": ["http", "cache"]}成功时返回 201 Created:
{ "id": "b_01J8Z6Q4R2", "status": "active", "createdAt": "2026-10-31T09:20:00Z"}错误也属于契约。不要只返回一段自然语言:
{ "error": { "code": "INVALID_URL", "message": "URL 必须是 http 或 https 地址", "requestId": "req_01J8Z6R7K9" }}接口要写清:
- 认证方式和允许的角色。
- 字段长度、必填项和规范化规则。
- 分页方式,例如游标分页而不是无限增长的 offset。
- 幂等键是否必需,重复请求返回什么。
- 超时、限流、错误码和重试建议。
- 删除是立即生效、软删除还是延迟清理。
浏览器端校验改善体验,服务器端校验保证安全。前端不能相信任何来自地址栏、表单、剪贴板或第三方接口的数据。
5. 服务端怎样处理一次保存
跳转到“5. 服务端怎样处理一次保存”请求到达服务后,可以按下面的顺序理解:
接收入口 → 解析 HTTP 与路由 → 认证用户 → 限流与基础防护 → 校验并规范化输入 → 检查对象级权限 → 事务写入数据库 → 提交后发送异步任务 → 返回结果和 requestId认证回答“你是谁”,授权回答“你能不能操作这条数据”。用户已登录,不代表他能读取另一个用户的收藏。查询时要把资源 ID 和当前用户范围一起写进条件,而不是先查出来再在代码里比较。
保存逻辑可以先用伪代码表达:
handleSave(request): user = authenticate(request) input = validate(request.body) return db.transaction: existing = findBookmark(owner=user.id, url=input.canonicalUrl) if existing: return existing
bookmark = insertBookmark(owner=user.id, input) outbox.add("bookmark.created", bookmark.id) return bookmark这段伪代码有一个重要漏洞:两个并发请求可能同时查不到记录,然后都尝试插入。最终仍必须依靠数据库唯一约束兜底,冲突后返回已有记录。只在应用层“先查再写”不够。
Idempotency-Key 解决的是另一个问题:浏览器超时后重试,或者网关重发同一个请求。服务端把“用户 + 幂等键 + 请求摘要”保存起来,重试时返回第一次的结果,避免重复创建或重复触发任务。
响应返回前,不要把摘要生成、图片下载或模型调用塞进主链路。它们可以进入队列,由后台 worker 执行。主请求只负责确认数据已经可靠保存。相关笔记:请求与 API、并发与缓存、语言与结构。
6. 数据模型决定系统能回答什么
跳转到“6. 数据模型决定系统能回答什么”这个应用至少需要用户和收藏两类数据。先关注收藏表:
CREATE TABLE bookmarks ( id UUID PRIMARY KEY, owner_id UUID NOT NULL REFERENCES users(id), canonical_url TEXT NOT NULL, title TEXT NOT NULL, note TEXT, tags TEXT[] NOT NULL DEFAULT '{}', status TEXT NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ);
CREATE UNIQUE INDEX bookmarks_owner_url_active ON bookmarks (owner_id, canonical_url) WHERE deleted_at IS NULL;
CREATE INDEX bookmarks_owner_created ON bookmarks (owner_id, created_at DESC);
CREATE INDEX bookmarks_tags_gin ON bookmarks USING GIN (tags);这里每个决定都有含义:
owner_id把对象权限放进数据模型,查询不能漏掉它。canonical_url是规范化后的 URL,避免同一地址因大小写或尾斜杠产生重复记录。- 部分唯一索引只约束未删除记录,删除后可以重新收藏同一地址。
created_at支持“我的收藏按时间倒序”。tags的 GIN 索引支持标签包含查询。deleted_at表示软删除,便于恢复和审计,但真正删除仍需单独流程。
URL 规范化不能随意丢掉查询参数,因为参数可能决定页面内容。规则应由测试固定,而不是靠字符串截断。
6.1 事务、异步和一致性
跳转到“6.1 事务、异步和一致性”保存收藏和写入 outbox 事件应该在一个事务里。这样不会出现“数据库里有收藏,但后台永远不会生成摘要”的半成功状态。worker 采用至少一次投递,因此事件处理必须按事件 ID 幂等。
不是所有读取都需要强一致:
- 刚保存完马上刷新列表,应该能看见自己的数据。可以读主库,或在会话中等待复制追上。
- 其他人看到的公开统计可以慢几秒。
- 搜索索引可以短暂落后,但界面要说明“结果可能延迟”。
一致性是逐个操作定义的要求,不是给整个系统贴一个标签。
6.2 搜索与数据生命周期
跳转到“6.2 搜索与数据生命周期”小数据量下,简单的关键词查询可能够用;数据增长后,LIKE '%词%' 往往会扫描大量行。可以根据需求选择全文索引、trigram 索引或独立搜索服务,但都要限制返回数量,并继续带用户范围。
还要提前回答:
- 删除后多久从备份和搜索索引中消失?
- 是否允许导出自己的全部数据?
- 备份是否做过恢复演练,而不只是显示“成功”?
- 分析事件是否包含笔记正文、URL 中的敏感参数或用户身份?
用户数据、分析数据和对账数据的事实来源不同。分析表可以冗余,但不能反过来决定用户看到什么。相关笔记:数据库与存储、指标与治理。
7. 系统变大以后,先改哪里
跳转到“7. 系统变大以后,先改哪里”第一版可以是一个部署单元:
浏览器 → 应用服务 → PostgreSQL └→ outbox → worker → 可选摘要任务这不是落伍的“单体”,而是一个模块边界清楚的模块化单体。它的优点是事务简单、部署少、排查路径短。微服务把代码耦合换成网络调用、数据一致性和独立运维成本,只有在团队边界、独立扩缩容或故障隔离有明确需求时才值得。
扩容次序应该由证据决定:
| 现象 | 先验证 | 再考虑 | 不要先做 |
|---|---|---|---|
| 列表变慢 | 查询计划、索引、返回字段 | 缓存、只读副本 | 直接拆微服务 |
| 写入冲突 | 事务范围、唯一约束、热点行 | 批处理、分区 | 直接换分布式数据库 |
| 长任务占满请求 | 超时、阻塞点、并发数 | 队列、worker 池、背压 | 无限加线程 |
| 读取集中 | 命中率、热点键、权限粒度 | 进程内缓存、共享缓存 | 无失效策略地全量缓存 |
| 多地域访问 | 真实延迟与合规要求 | CDN、区域副本 | 未经设计就做双主 |
7.1 可靠性是失败时仍能解释的行为
跳转到“7.1 可靠性是失败时仍能解释的行为”可靠性不只等于“多部署几个实例”。至少要处理:
- 超时:每个外部调用都有上限,不能让一个慢依赖拖住整条请求。
- 重试:只重试可安全重试的操作,设置次数、退避和抖动,避免重试风暴。
- 熔断与隔离:下游持续失败时暂停调用,保护线程、连接和内存。
- 降级:摘要失败时仍能保存和搜索;模型不可用时退回关键词结果。
- 背压:队列积压时限制新任务,而不是无限接收直到内存耗尽。
- 优雅停止:发布或缩容时停止接新请求,等待在途请求结束或安全取消。
- 备份与恢复:定期验证能否在目标 RTO 内恢复,而不只是备份文件存在。
常见故障可以这样推演:
| 故障 | 正确行为 |
|---|---|
| 数据库不可用 | 返回明确的 503,不要假装保存成功;保留可重试提示 |
| 缓存全部失效 | 限流回源,保护数据库,必要时只提供降级读取 |
| 队列积压 | 主保存继续,摘要和通知延后,界面显示待处理状态 |
| 模型超时 | 返回关键词搜索结果或“稍后重试”,不让主接口一起挂掉 |
| 请求重复到达 | 幂等键和唯一约束返回同一结果,任务不重复执行 |
| 新版本代码回滚 | 数据迁移保持向后兼容,旧版本仍能读取新结构中的必填部分 |
8. 上线不是把代码复制到服务器
跳转到“8. 上线不是把代码复制到服务器”一次可回滚的发布大致包括:
提交代码 → 静态检查与测试 → 构建不可变产物 → 检查数据库迁移 → 小流量部署 → 健康检查与指标观察 → 扩大流量或回滚数据库迁移建议采用“扩展—迁移—收缩”:先增加兼容字段或表,双写或回填,切换读取,确认旧版本不再依赖后再删除旧结构。不要在一个发布里同时删除字段、修改代码和回滚机制。
Linux、容器和网络边界仍然适用:进程需要监听正确端口,容器需要资源限制和健康检查,数据不能只放在临时容器层,服务跨网络访问要经过 DNS、TLS、防火墙和安全组。容器解决的是运行环境封装,不自动解决持久化、备份和权限。
8.1 权限与攻击面
跳转到“8.1 权限与攻击面”对这个应用,最现实的威胁不是“黑客电影式入侵”,而是普通错误:
| 风险 | 具体问题 | 控制 |
|---|---|---|
| 越权读取 | 只按收藏 ID 查询,没带当前用户 | 所有对象查询带 owner_id,并测试他人 ID |
| XSS | 把笔记或标题当 HTML 渲染 | 默认按文本渲染;必须富文本时做白名单净化 |
| CSRF | Cookie 会话下被第三方站点代为提交 | SameSite、CSRF token、检查请求来源 |
| SSRF | 服务器自动抓取用户填写的 URL | 限制协议、解析后阻断内网地址、限制重定向和响应大小 |
| 密钥泄露 | 把模型或其他服务密钥打进前端 | 密钥只放服务端,日志和错误信息脱敏 |
| 滥用 | 批量保存、无限搜索、模型调用刷量 | 认证、限流、配额、审计和异常检测 |
权限检查要覆盖读取、修改、删除和导出,不只是保存接口。相关笔记:安全与交付。
8.2 观测一次请求
跳转到“8.2 观测一次请求”线上至少需要三类信号:
- 日志:时间、请求 ID、用户范围、接口、状态码、错误码、耗时;不记录密钥和完整敏感正文。
- 指标:请求速率、错误率、延迟分位、数据库连接池、缓存命中率、队列积压、模型调用耗时与费用。
- trace:一次请求在接入层、应用、数据库和下游中的时间分布。
健康检查分存活和就绪:进程活着不代表能接流量。数据库连接没准备好、迁移没完成或依赖不可用时,就绪检查应该阻止新流量进入。
下面用一个故障把前端、数据库、部署和观测串起来:
- 新版本加入了关键词搜索,发布后接口 p95 从 300 ms 升到约 2 秒,错误率超过 3%。
- 仪表盘显示浏览器和 CDN 正常,延迟集中在 API;trace 显示大部分时间花在数据库查询。
- 日志显示搜索使用了
LIKE '%关键词%',没有可用索引,而且分页没有上限;并发稍高就占满连接池。 - 先关闭搜索功能开关,恢复主路径,再回滚不兼容部分;不要用无限加连接数掩盖慢查询。
- 修复方式是增加合适的全文或 trigram 索引,限制返回条数和查询超时,按真实数据量做压测,再通过灰度重新发布。
- 事后补上 p95、连接池和慢查询告警,并把“查询在 10 万行数据上的计划”加入发布检查。
这个例子里,缓存也许能暂时减少请求,但根因是查询模式和索引。监控的价值是帮你定位,而不是让告警看起来更专业。相关笔记:可观测性。
9. 需要时再加模型能力
跳转到“9. 需要时再加模型能力”模型不是每个应用的必要层。收藏、列表和权限用确定性代码更便宜、更稳定。只有当任务本身模糊、需要自然语言理解或生成时才加入,例如:
- 为长笔记生成摘要。
- 根据一段描述找“意思相近”的资料。
- 对多篇资料做有来源的回答。
以“根据收藏回答问题”为例,完整流程仍然是普通系统流程,只是中间多了模型:
用户问题 → 认证与用户范围 → 关键词召回 + 向量召回 → 合并、去重 → 权限过滤 → 重排候选 → 组织上下文与提示词 → 模型生成 → 结构校验、引用检查 → 返回答案或降级结果请求可以是:
{ "question": "HTTP 缓存什么时候会失效?"}响应要能说明依据和降级状态:
{ "answer": "资料中提到了过期、主动失效和缓存键变化三种情况。", "sources": ["b_01J8Z6Q4R2", "b_01J8Z8T1M6"], "degraded": false}相关笔记:大模型与多模态、RAG 与 Agent。
9.1 提示词、检索和 Agent 各自负责什么
跳转到“9.1 提示词、检索和 Agent 各自负责什么”- 检索决定把哪些资料放进上下文。文本要先切分、建索引,查询时召回候选并重排。权限过滤必须在候选进入模型前完成。
- 提示词负责任务说明、输出约束和少量示例。把已收藏网页的内容当“资料”,不要把它当“系统指令”;网页里可能藏着提示注入。
- Agent适合需要多步工具调用的任务,例如先搜索、再打开资料、再汇总。它需要最大步数、超时、工具权限、失败处理,重要操作还要人工确认。
- 结构化输出便于程序校验,但模型返回的 JSON 仍可能字段缺失或不符合业务规则,必须验证,不能直接写数据库。
一个简化的提示约束可以写成:
只根据 <sources> 中的内容回答。如果资料不足,回答“现有资料无法确认”,不要补充外部事实。每条结论附上来源 id。输出 JSON:{"answer": string, "sourceIds": string[]}提示词不能替代数据库权限,也不能解决过期索引。它只是模型层的一部分。
9.2 模型层的失败和评测
跳转到“9.2 模型层的失败和评测”| 失败 | 处理 |
|---|---|
| 模型超时或限流 | 返回关键词搜索结果,标记 degraded,允许重试 |
| 输出格式错误 | 做一次结构修复或重试,仍失败则返回纯检索结果 |
| 资料不足或答案无来源 | 明确说无法确认,不编造引用 |
| 提示注入 | 网页内容只作为数据,工具权限最小化,危险动作需确认 |
| 跨用户泄漏 | 检索前按用户过滤,缓存键包含权限范围 |
| 成本失控 | 限制上下文长度、并发和配额,异步任务单独计量 |
上线前准备一组固定问题、期望来源和不可接受回答,记录召回率、引用正确性、延迟和费用。模型或提示词变化后先离线评测,再小流量比较;上线后收集人工反馈和失败样本。相关笔记:模型生命周期。
模型层没有改变前面的结论:它仍然通过接口接收输入,仍然需要数据库、队列、权限、超时、降级、日志、测试和回滚。它只是把一部分不确定性从代码转移到了模型和检索。
10. 用测试和交付把改动锁住
跳转到“10. 用测试和交付把改动锁住”这个案例的测试不应该只覆盖“保存成功”:
| 层级 | 例子 | 主要发现 |
|---|---|---|
| 单元测试 | URL 规范化、标签清洗、权限判断 | 边界规则错误 |
| 集成测试 | API + 数据库 + 事务 + 幂等键 | 接口与真实存储不一致 |
| 端到端测试 | 保存、刷新、搜索、删除 | 浏览器与后端链路断开 |
| 契约测试 | 请求字段、错误码、分页格式 | 前后端版本不兼容 |
| 压力测试 | 2 倍峰值、慢数据库、队列积压 | 过载和资源耗尽 |
| 安全测试 | 他人资源 ID、脚本注入、内网 URL | 越权和 SSRF |
| 模型评测 | 召回、引用、拒答、超时降级 | 演示之外的真实质量 |
一次完整变更可以按下面的顺序推进:
- 写清用户结果、约束和非目标。
- 写 API 契约、数据迁移和验收条件。
- 准备环境、种子数据和可重复的测试。
- 先实现一条最小垂直链路,再补边界。
- 覆盖错误、重复、越权和依赖失败。
- 检查迁移是否向后兼容、是否可回滚。
- 灰度发布,观察请求、错误、延迟和资源。
- 记录取舍,清理临时开关和旧代码。
代码质量和设计模式服务于可理解、可修改,而不是为了让目录看起来复杂。只有重复出现、边界明确的问题才值得抽象。第三方库、模型和数据源还要检查许可证、维护状态和数据使用边界。相关笔记:代码与测试、安全与交付。
11. 把整条链路收成一套框架
跳转到“11. 把整条链路收成一套框架”看懂陌生系统时,可以固定问六个问题:
- 用户要完成什么目标?哪些结果不能接受?当前的非目标是什么?
- 哪份数据是事实来源?谁拥有、保留多久、怎样删除和恢复?
- 一次请求经过哪些边界?每层的输入、输出和超时是什么?
- 重复、并发、超时、过载和依赖故障时会发生什么?
- 谁可以读、写、删除和导出?最坏的越权后果是什么?
- 怎样验证、部署、观测、降级、回滚和继续演进?
可以把这套框架记成:
系统 = 用户目标 + 数据与状态 + 请求链路 + 失败处理 + 权限边界 + 运行反馈 + 演进成本九个章节也可以放回这条链路中:
| 章节 | 在案例里回答什么 | 对应笔记 |
|---|---|---|
| 计算机系统 | 程序在哪里运行,内存、I/O 和网络为什么会影响性能 | 计算机系统一页通 |
| 开发环境与工具 | 怎样复现环境、管理版本和定位故障 | 可复现环境、Git 与调试 |
| 浏览器与前端 | 用户操作怎样变成请求,页面怎样处理等待和错误 | 浏览器运行模型、前端工程与应用设计 |
| 服务器与后端 | 请求怎样认证、校验、写入并安全返回 | 请求与 API、并发与缓存、语言与结构 |
| 数据 | 数据存在哪里,谁能查,怎样保证索引、事务和生命周期 | 数据库与存储、指标与治理 |
| 架构与系统设计 | 何时扩容、拆服务和降级,怎样记录取舍 | 分布式与高可用、系统设计方法与案例 |
| 基础设施与运维 | 怎样部署、联网、保护边界和观察线上 | Linux 与部署、网络与云安全、可观测性 |
| 模型系统 | 何时需要模型,检索、提示词、Agent 和评测怎样接入 | 大模型与多模态、RAG 与 Agent、模型生命周期 |
| 工程素养 | 怎样用代码、测试、安全和交付把改动变成可靠结果 | 代码与测试、安全与交付 |
如果只能记住一句话:先确定用户结果和事实来源,再沿着一次真实请求穿过所有边界,最后检查失败、权限、观测和回滚。技术栈会变化,这套提问顺序不会。
三个练习
跳转到“三个练习”- 给收藏夹增加“公开分享链接”。写出新的数据字段、权限规则、撤销方式、缓存和搜索引擎风险。
- 假设流量增长 100 倍。先列出需要测量的指标,再只选择两项最小的架构变化,并说明为什么其他变化暂时不做。
- 给案例增加“根据收藏回答问题”。画出召回、权限过滤、模型调用和降级的路径,再列出三种不能把模型输出直接写入数据库的情况。
能独立完成这三题,基本就能把分散的概念串成一条可以用于设计、排查和交付的完整链路。