基于Base链的USDC Escrow解决AI Agent与请求者双向信任问题,利用~2秒最终确定性和低Gas成本,支持争议退款机制。
Frontend、Backend、共享包、服务边界与环境设计
第 101 章奠定了实现基础:仓库组织、工程工作流、环境、安全门控和开发原则。
第 102 章则深入一层。
目标是定义实际应用应如何拆分为组件,以及这些组件之间如何通信。
安全 AI 平台应避免成为一个单一的大型应用——将身份认证、数据库访问、AI 调用、文件处理、计费和管理功能混在一起。
相反,平台应建立清晰的边界。
基础架构如下:
User
↓
Frontend
↓
API Boundary
↓
Application Services
↓
Security / Policy
↓
Data / AI / Storage Services
↓
External Providers
每个边界应有明确的职责。
核心架构应提供:
架构还应允许各个组件独立演进,而无需重写整个系统。
完整的逻辑架构可表示为:
┌──────────────────────┐
│ User │
│ Browser / Mobile │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Web Frontend │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ API Gateway / Edge │
└──────────┬───────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌────────────┐ ┌────────────┐
│ Auth │ │ Policy │ │ API │
│ Service │ │ Engine │ │ Services │
└──────────┘ └────────────┘ └─────┬──────┘
│
┌────────────────────────────────────┼───────────────┐
▼ ▼ ▼
┌────────────┐ ┌──────────┐ ┌──────────┐
│ PostgreSQL │ │ Storage │ │ AI Layer │
└────────────┘ └──────────┘ └────┬─────┘
│
┌─────────────────┼─────────────┐
▼ ▼ ▼
Provider A Provider B Local Model
横切服务环绕整个系统:
Audit Logging
Monitoring
Security Detection
Secrets Management
Rate Limiting
Configuration
Backup
Policy Enforcement
前端是一个不可信的客户端。
即使应用控制了前端代码,恶意用户仍可在请求到达服务器之前修改请求。
Frontend ≠ Trusted Security Boundary
前端可以提供:
然而,服务器必须独立验证所有涉及安全的决策。
Frontend:
"User is allowed to edit project 123."
Backend:
"Let me verify that."
后端绝不应盲目信任客户端提供的授权声明。
后端代表主要的可信应用边界。
职责包括:
Authentication
Authorization
Input validation
Business logic
Policy enforcement
Database access
AI orchestration
Storage coordination
Job creation
Audit logging
Usage accounting
后端应暴露稳定的 API,而不是允许前端直接与内部基础设施通信。
API 网关或边缘层充当第一个受控入口点。
典型职责包括:
TLS termination
Request routing
Rate limiting
Request size limits
Basic filtering
Authentication forwarding
Request IDs
Security headers
Traffic control
网关不应成为应用授权的替代品。
Gateway:
"Request is authenticated."
Application:
"Is this user actually authorized to perform this action?"
这两个问题是不同的。
身份认证确认身份。
Credentials / Identity Provider
↓
Authentication
↓
User Identity
↓
Session / Token
身份认证应回答:
这个请求者是谁?
授权回答的是另一个问题:
这个请求者被允许做什么?
这一区别应贯穿整个实现。
授权应评估:
Subject
Resource
Action
Context
Policy
Subject:
User A
Resource:
Project B
Action:
edit
Context:
Tenant C
Policy:
User A owns or has permission on Project B
ALLOW
DENY
默认值应为拒绝。
策略引擎使安全决策变得一致。
系统可以通过集中式机制评估策略,而不是到处嵌入授权逻辑:
if user.role === "admin"
Request
↓
Identity
↓
Resource
↓
Action
↓
Policy Engine
↓
Decision
当平台引入管理权限时,这尤其有用。
业务操作应存在于应用服务中,而不是放在 HTTP 路由处理器内部。
HTTP Route
↓
Controller
↓
Application Service
↓
Repository / External Service
生成服务在概念上可能执行:
validate request
→ verify user
→ verify project access
→ evaluate content policy
→ verify usage limits
→ create generation job
→ enqueue task
→ audit event
HTTP 端点应主要负责协调请求,而不是包含整个业务过程。
数据层应将业务逻辑与数据库实现分离。
Application Service
↓
Repository
↓
ORM / Query Layer
↓
PostgreSQL
这提供了几个优势:
平台应避免复制关键逻辑。
有用的共享包包括:
packages/
├── auth/
├── ai/
├── database/
├── security/
├── storage/
├── validation/
├── observability/
├── config/
├── ui/
└── shared/
Contains reusable schemas and validation utilities.
Contains security-related controls and utilities.
Contains logging, tracing, metrics, and security-event interfaces.
Contains configuration schemas and environment validation.
Contains model interfaces and provider adapters.
应尽可能在应用边界上一致地使用 TypeScript。
共享类型可以表示 AI 生成请求:
GenerationRequest
而不是在前端和后端独立定义不同版本。
Frontend Type
↓
Shared Type
↓
API
↓
Backend Type
然而,编译时类型不能替代运行时验证。
恶意客户端可以发送任意 JSON。
TypeScript validation
+
Runtime validation
应结合使用。
每个外部输入都应被验证。
外部输入包括:
一般模式是:
Untrusted Data
↓
Parse
↓
Validate
↓
Normalize
↓
Use
不要仅仅因为前端通常发送正确的数据就假设外部数据符合预期模式。
AI 系统需要自己的安全边界。
应用不应允许代码库的任意部分进行任意模型调用。
Application
↓
AI Orchestrator
↓
Policy Checks
↓
Model Router
↓
Provider Adapter
↓
Model
编排器可以控制:
供应商特定代码应保持隔离。
packages/ai/
│
├── core/
├── router/
├── providers/
│ ├── provider-a/
│ ├── provider-b/
│ ├── provider-c/
│ └── local/
└── safety/
应用应通过稳定的内部接口通信。
generate(request)
callProviderAWithProviderSpecificParameters()
这使得迁移和降级更容易。
对象存储也应被隔离。
应用程序不应向用户暴露原始存储凭证。
用户
↓
API
↓
授权
↓
上传授权
↓
存储操作
对于大文件,可使用受控的上传机制,使文件不必经由应用服务器传递。
但仍需强制执行授权和文件限制。
媒体处理应与主应用程序隔离。
上传
↓
隔离区
↓
验证
↓
扫描
↓
处理 Worker
↓
输出验证
↓
可信存储
这降低了畸形或恶意媒体文件直接与主应用程序环境交互的可能性。
长时间运行的操作应成为作业。
API
↓
创建作业
↓
队列
↓
Worker
↓
结果
作业应有受控的状态:
queued(排队中)
processing(处理中)
completed(已完成)
failed(失败)
cancelled(已取消)
作业不应被无声丢失。
可重试的操作应设计为避免意外重复。
生成图片
如果客户端因网络超时而重试请求,系统应避免在只打算创建一次付费生成时意外创建两次付费生成。
概念性流程如下:
请求
↓
幂等性 Key
↓
检查现有操作
↓
存在?
├── 是 → 返回现有结果
└── 否 → 创建操作
这在以下场景尤为重要:
配置应因环境而异。
Development(开发)
Testing(测试)
Staging(预发布)
Production(生产)
源代码
↓
环境配置
↓
经验证的配置
↓
应用程序
如果必需的配置缺失或无效,应用程序应启动失败。
这优于使用不安全默认值静默运行。
配置可划分为:
应用程序 URL
功能开关
超时设置
模型名称
日志级别
数据库凭证
AI 提供商凭证
签名密钥
加密密钥
存储凭证
支付密钥
敏感配置应在运行时安全注入。
密钥应有独立的范围。
前端
→ 不含提供商密钥
API
→ API 所需的密钥
Worker
→ Worker 所需的密钥
数据库服务
→ 数据库凭证
部署系统
→ 部署凭证
只处理图片的 Worker 不应自动获得支付凭证。
这遵循最小权限原则。
内部服务不应自动互相信任。
安全的概念模型是:
服务 A
↓
身份
↓
认证
↓
授权
↓
服务 B
接收服务应验证:
调用方是真实的;
调用方被允许执行请求的操作;
请求是有效的;
请求在策略范围内。
对于多用户平台,每个相关操作都应有租户上下文(适用时)。
请求
↓
用户身份
↓
租户身份
↓
资源
↓
授权
数据库查询、存储路径、缓存、向量、作业和日志不应意外混合租户数据。
因此,租户隔离应一致地实现,而不仅仅在前端实现。
请求上下文可携带重要元数据穿越服务。
请求上下文
├── request_id(请求 ID)
├── trace_id(追踪 ID)
├── user_id(用户 ID)
├── tenant_id(租户 ID)
├── session_id(会话 ID)
├── security_context(安全上下文)
└── risk_context(风险上下文)
并非每个字段都需要暴露给每个服务。
敏感信息应最小化。
目的是使操作可追踪,同时不必要地传播个人数据。
安全事件应与普通调试日志区分开。
authentication_failed(认证失败)
authorization_denied(授权拒绝)
rate_limit_triggered(触发速率限制)
suspicious_upload(可疑上传)
policy_violation(策略违规)
privileged_action(特权操作)
credential_rotation(凭证轮换)
security_configuration_changed(安全配置已更改)
这些事件可供给监控和检测系统。
平台应暴露三类主要遥测数据:
Metrics(指标)
Logs(日志)
Traces(追踪)
安全事件构成另一个重要数据流。
请求
├── Metrics(指标)
├── Logs(日志)
├── Trace(追踪)
└── Security Events(安全事件)
这使工程团队能够理解系统健康状况和安全行为。
服务应提供受控的健康信息。
Liveness(存活探针)
Readiness(就绪探针)
Dependency health(依赖健康状况)
存活探针回答:
进程是否正在运行?
就绪探针回答:
此服务是否可以安全地接收流量?
健康端点应避免暴露敏感的内部信息。
分布式 AI 平台将经历故障。
AI 提供商不可用
数据库临时不可用
队列不可用
存储超时
网络故障
Worker 崩溃
系统应可预测地响应。
AI 提供商故障
↓
安全时重试
↓
策略允许时使用备用提供商
↓
排队等待稍后处理
↓
受控失败
重试应有上限。
无限重试可将小故障演变为重大停机。
每个外部操作都应有有界的超时。
数据库查询
API 请求
AI 推理
存储操作
队列操作
Webhook 调用
操作
↓
超时
↓
成功 / 受控失败
服务无限等待外部依赖会消耗线程、连接、内存,最终导致级联故障。
项目应定义哪些层可以依赖哪些其他层。
UI
↓
API
↓
应用服务
↓
领域 / 安全
↓
基础设施
基础设施不应意外地控制应用程序策略。
同样,UI 组件不应直接访问生产数据库。
这些依赖规则可通过代码审查和自动化工具在实际可行时强制执行。
考虑用户请求 AI 图片生成。
完整请求可能遵循:
1. 用户提交提示词
↓
2. 前端验证基本结构
↓
3. API 接收请求
↓
4. 认证验证身份
↓
5. 授权验证项目访问权限
↓
6. 运行时验证检查输入
↓
7. 内容策略评估请求
↓
8. 评估速率限制
↓
9. 检查使用配额
↓
10. 创建生成作业
↓
11. 作业进入队列
↓
12. Worker 接收作业
↓
13. AI 编排器选择模型
↓
14. 提供商适配器调用模型
↓
15. 验证输出
↓
16. 存储结果
↓
17. 记录审计事件
↓
18. 作业变为已完成
↓
19. 前端接收结果
此生命周期展示了安全性和功能性如何协同工作。
应避免几种设计。
反模式 1 — 前端中的 API 密钥
浏览器
↓
提供商 API
这会暴露提供商的凭证。
浏览器
↓
后端
↓
提供商
反模式 2 — 前端直接访问数据库
浏览器
↓
数据库
这绕过了重要的应用程序控制。
浏览器
↓
API
↓
授权
↓
数据库
反模式 3 — 一个巨无霸服务
单个代码库包含:
认证
AI
计费
媒体处理
数据库
管理后台
通知
没有有意义的边界,会变得难以安全和维护。
模块化单体可以在保留内部边界的前提下,作为一个优秀的起点,再引入多个独立部署的微服务。
反模式 4 — 信任客户端角色
role=admin
因为是客户端提供的。
后端必须从可信的服务器端信息派生和验证授权。
新平台在第一天不一定需要数十个微服务。
实用的起始架构是:
┌───────────────┐
│ Web 前端 │
└───────┬───────┘
│
▼
┌───────────────┐
│ API / 后端 │
│ 模块化 │
└───────┬───────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
PostgreSQL 存储 队列
│ │
│ ▼
│ Worker
│ │
└──────┬───────┘
▼
AI 编排器
│
┌────────┼────────┐
▼ ▼ ▼
提供商 提供商 本地
此架构提供了强边界,而不会过早地造成过多的运营复杂性。
平台可以逐步演进。
模块化单体
模块化后端
+
专用 Worker
独立的 AI 服务
+
专用处理服务
选择性微服务
+
服务网格
+
高级基础设施
架构应依据实际规模和运营需求演进,而非仅仅因为赶时髦就引入复杂性。
继续实现前,需验证以下事项:
[ ] 前端被视为不可信
[ ] 已定义 API 边界
[ ] 已定义身份认证边界
[ ] 已定义授权边界
[ ] 已定义策略引擎边界
[ ] 数据库访问已集中化
[ ] 存储访问已受控
[ ] AI 访问已集中化
[ ] 提供商适配器已隔离
[ ] 已定义 Worker 架构
[ ] 已定义队列架构
[ ] 已定义租户上下文
[ ] 已定义服务身份认证
[ ] 已定义运行时验证
[ ] 已定义配置验证
[ ] 密钥已隔离
[ ] 已定义日志架构
[ ] 已定义安全事件
[ ] 已定义健康检查
[ ] 已定义超时策略
[ ] 已定义重试策略
[ ] 已定义失败行为
第 102 章建立了连接第 101 章实现基础与实际平台组件的核心架构。
最重要的架构规则是:
不允许任何功能绕过安全边界。
AI 请求应经过 AI 控制层。
数据库操作应经过受控数据访问层。
文件处理应经过隔离和验证层。
管理操作应经过特权授权层。
外部提供商应通过受控适配器访问。
其结果是得到一个将功能与安全性整合为一体的架构,而非将二者作为独立系统分别开发。
下一章可以聚焦于实际的后端基础设施,包括应用初始化、模块组织、API 结构、配置加载、请求生命周期、错误处理、验证以及首个安全后端实现模式。
如需进一步行动,可考虑屏蔽此人或举报滥用行为