核心概念 · 运行时与网关¶
编排完 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)
签名:
apps:{appId: 编译后的图};为None时用已注册到默认注册表的应用。host/port:监听地址(默认0.0.0.0:8080)。- 内部:注册表注册
appId→create_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 恢复执行——这就是「多轮交互式办理」的实现基础。
两种能力范围:
- 同进程内存断点(
wf.compile()默认):进程不退出时跨请求可恢复;演示够用。 - 持久化断点:接
AsyncSqliteSaver/AsyncPostgresSaver/MongoDBSaver/AIOMySQLSaver,进程重启也不丢。见指南 06。
序列化约定:断点内容含
_ctx(带stream_callback闭包),默认 msgpack 无法落库。持久化时必须注入框架的PersistentSerializer(或使用框架build_checkpointer),框架会自动剥离 / 重建_ctx。别绕过这套,否则恢复会炸。
5. 线程与隔离¶
chatId → thread_id落库;同一chatId的多轮请求共享同一断点链。- 不同
chatId完全隔离,可并行。 - 生产网关对同一
chatId有并发门控(同会话同一时刻只放行一个,冲突拒绝),避免重入。