跳转至

核心概念 · 运行时与网关

编排完 DSL 只是第一步。本文讲编译后的图怎么执行、断点怎么恢复,以及怎么把它暴露成一个对外的流式对话服务。

1. 两种执行方式:invoke vs serve

纯库方式 网关方式
入口 app.invoke / app.astream serve(apps) / create_app(reg)
面向 程序内直接跑一个图 对外提供 HTTP 对话协议
需要 只要编译后的图 需要 FastGPT 兼容协议(SSE / detail / interactive / 变量回传)
结论 简单场景 / 测试用 绝大多数生产场景

判断标准很简单:如果你需要「像 ChatGPT 一样发给客户端、按 SSE 流式输出、带断点续跑」,就走网关;如果你只是在另一个服务里调用图拿最终结果,invoke 就够了。

2. 网关:serve()create_app()

compile() 返回的是原生 LangGraph 图,它自己不含 HTTP。有两种方式把它变成服务:

方式一:serve() — 一键启动(推荐)

from agentframework import Workflow, serve
from agentframework.nodes import Answer

wf = Workflow(name="hello")
wf.add_node(Answer(name="a", text="ok"))
app = wf.compile()

serve({"hello": app}, host="0.0.0.0", port=8080)

签名:

serve(apps=None, *, host="0.0.0.0", port=8080, registry=None)
  • apps{appId: 编译后的图};为 None 时用已注册到默认注册表的应用。
  • host/port:监听地址(默认 0.0.0.0:8080)。
  • 内部:注册表注册 appIdcreate_app(reg)uvicorn.run

方式二:create_app() — 自行接管(要鉴权 / 自定义中间件时)

create_app(registry) 返回一个组装好的 FastAPI 实例,你可以继续挂鉴权中间件,再自己 uvicorn.run。完整 API Key 鉴权示例见指南 08

from agentframework.gateway.app import create_app
from agentframework.gateway.registry import AppRegistry
import uvicorn

reg = AppRegistry()
reg.register("hello", app)  # 可注册多个应用(多 appId)
uvicorn.run(create_app(reg), host="0.0.0.0", port=8080)

网关本身是 FastAPI,自动提供 /docs(Swagger UI)、/redoc/openapi.json 接口描述;协议语义权威见仓库 doc/Agent接口契约.md

3. 对外接口形态(FastGPT 兼容)

唯一核心端点是 POST /api/v2/chat/completions(另有调试用 GET /api/v2/apps)。请求体(JSON)典型字段:

{
  "appId": "hello",
  "chatId": "user-001",
  "messages": [{"role": "user", "content": "你好"}],
  "stream": true
}
  • appId:选哪个已注册工作流。
  • chatId线程 / 断点映射键thread_id)。同一 chatId 的多次请求在同一会话上续跑。
  • messages:对话历史(OpenAI 风格 role/content),由框架转成内部消息。
  • stream:是否 SSE 流式。

网关收到请求后的内部处理链(create_app 职责):

flowchart LR
    A[解析请求] --> B[按 appId 查图]
    B --> C[消息转 BaseMessage]
    C --> D[注入运行上下文 ctx]
    D --> E[图 astream_events]
    E --> F[answer/事件]
    F --> G[detail→flowResponses]
    G --> H[[DONE]]

一次请求会产生哪些事件

事件 时机 内容
answer 输出节点流式增量 回复文本增量(如 ChatLLM / Answer
flowNodeStatus 节点开始/结束 节点执行状态(供前端展示流程)
flowResponses detail=true 各节点详情快照
updateVariables 请求结束 VariableOutput 显式声明的变量回传
interactive 交互节点暂停 让用户选择/输入的载荷
[DONE] 结束 流式结束标记

事件名、data 结构与错误结构 jsonRes {code,statusText,message,data} 均以 doc/Agent接口契约.md 为准,这里只给概念。

4. 断点与恢复(checkpointer)

interactive 节点(UserSelect / UserInput)执行到一半会暂停,把现场(包括执行到哪个节点、待用户填的值)存进 checkpointer,然后向客户端发 interactive 事件。用户提交后,网关用同一个 chatId 恢复执行——这就是「多轮交互式办理」的实现基础。

# interactive 必须带 checkpointer,否则跨请求无法恢复
app = wf.compile(store=...)  # 默认内存 / 官方 saver / 自定义 store

两种能力范围:

  • 同进程内存断点wf.compile() 默认):进程不退出时跨请求可恢复;演示够用。
  • 持久化断点:接 AsyncSqliteSaver / AsyncPostgresSaver / MongoDBSaver / AIOMySQLSaver进程重启也不丢。见指南 06

序列化约定:断点内容含 _ctx(带 stream_callback 闭包),默认 msgpack 无法落库。持久化时必须注入框架的 PersistentSerializer(或使用框架 build_checkpointer),框架会自动剥离 / 重建 _ctx。别绕过这套,否则恢复会炸。

5. 线程与隔离

  • chatId → thread_id 落库;同一 chatId 的多轮请求共享同一断点链。
  • 不同 chatId 完全隔离,可并行。
  • 生产网关对同一 chatId 有并发门控(同会话同一时刻只放行一个,冲突拒绝),避免重入。

下一步

  • 想用网关对外的 Swagger / 契约细节 → 仓库 doc/Agent接口契约.md
  • 断点落库实战 → 指南 06
  • 鉴权与部署 → 指南 08