第 13 章:Gateway、鉴权与渠道 —— 同一个 Agent,不同的信任入口
Web、内部服务、IM 与 webhook 最终都能触发 Agent,但它们绝不能拥有完全相同的身份来源与能力。
Gateway lifespan 做什么
FastAPI lifespan() 是系统装配根。启动阶段大致完成:
- 加载并验证 AppConfig、日志与 tracing;
- 初始化数据库 engine、迁移与各 repository;
- 创建 checkpointer、store、RunManager、StreamBridge;
- 恢复孤儿运行,启动 heartbeat;
- 初始化/刷新 MCP、Skills 与 memory backend;
- 创建 scheduler 与渠道管理器;
- 把依赖放入
app.state供 router 使用。
关闭阶段反向停止后台任务、等待 run 收尾并释放连接。把资源生命周期放在 app 根部,避免每个 router 自己创建连接池或后台线程。
Middleware 顺序
HTTP 请求依次经过 Trace、CORS、CSRF、Auth 等中间件(Starlette 实际包裹顺序要结合注册顺序理解)。它们解决不同问题:
- Auth:请求是谁;
- CSRF:浏览器携带 cookie 的写请求是否来自合法页面;
- CORS:哪些 origin 能读取响应;
- Trace:一次请求/运行的关联 ID。
只做 Auth 不能防 CSRF;只限制 CORS 也不能阻止浏览器发送某些跨站请求。
用户身份的可信来源
Agent runtime 不能相信客户端 body 里的 user_id。Web 请求先由 AuthMiddleware 解析 session/JWT/OIDC,写入 request state;Gateway 创建 run 时再把 authenticated user、role 和 attributes 注入可信 context。
内部调用使用专门 header/secret,并可带 owner user id。resolve_config_user_id() 明确让 LangGraph Server 保留 auth 字段优先于普通 configurable/context,防止客户端伪造覆盖。
认证与授权分层
认证回答“你是谁”,授权回答“你能操作什么”。AuthorizationProvider 接收:
Principal(user_id, role, oauth_provider, channel_user_id, attributes...)
Resource(type, target, metadata...)
Action2
3
内置 RBAC 把 (role, resource_type) 编译成 allow/deny policy。未知角色或错误配置失败关闭;某 role/resource 没配置策略则按 provider 定义保持不限制,避免把“无规则”误当成“规则拒绝”。
工具授权有两层:组装时 filter_resources 缩小 schema;执行时 guardrail adapter 再按具体调用检查。子代理继承相同 Principal,不会因为换事件循环而退回匿名用户。
每用户数据隔离
身份贯穿:
- thread/run 查询必须验证 owner;
- sandbox key 使用
(user_id, thread_id); - custom agents、skills、memory 有用户命名空间;
- upload/artifact 路径由可信 user id 解析;
- IM 群聊还保留
channel_user_id,区分共享 thread 中的发言者。
任何只使用 thread_id 作为文件路径或缓存 key 的新代码,都可能形成跨用户碰撞。
IM Channels 如何复用主运行时
Feishu、Slack、Telegram、Discord、DingTalk 等 channel adapter 负责:
- 接收平台事件并验证平台身份;
- 去重、解析文本/附件和会话映射;
- 将 sender/channel 元数据转换成 DeerFlow run context;
- 调用同一 Gateway/LangGraph 运行路径;
- 把流式/最终结果转换成平台消息格式。
它们不是另一套 Agent。这样 Web 与 IM 的模型、工具、记忆和持久化语义相同,差异集中在输入输出适配。
Webhook 为什么更严格
GitHub webhook 等外部评论触发器的输入信任级别低于登录后的操作台。Lead Agent 在 webhook channel 中移除 update_agent,避免任意评论者永久修改 Agent 配置。相同思路也应应用于高影响工具:入口信任级别应成为授权上下文,而不是只看“是否有 token”。
人机澄清协议
ask_clarification 返回带 artifact.human_input 的 ToolMessage。前端验证版本和字段类型,渲染自由文本、单选或 v2 form。回复仍作为普通 HumanMessage 启动下一 run,但可通过 additional_kwargs 隐藏 UI 并携带结构化响应。
这个协议允许刷新后恢复未回答卡片,也允许旧前端把未知 v2 请求降级为普通文本。它说明“等待用户”不是暂停 Python coroutine,而是持久化一个业务状态,下一次运行再继续。
源码锚点
backend/app/gateway/app.pybackend/app/gateway/auth_middleware.pybackend/packages/harness/deerflow/authzbackend/app/channels
最后一章把这些认识转成部署、排障和扩展方法。