项目开源地址:https://github.com/GuoBug/PatchCat
在线体验:https://guobug.github.io/PatchCat/
往期回顾:
📖 《从 0 到 1 打造 AI 提示流编排器:双引擎架构与纯前端 DAG 实践(开源系列 01)》
📖 《从 0 到 1 打造 AI 提示流编排器:Kahn 拓扑排序、安全变量引擎与异步运行时实战(开源系列 02)》
📖 《从 0 到 1 打造 AI 提示流编排器:多模型生态适配、流式思维链与三级日志实战(开源系列 03)》
📖 《从 0 到 1 打造 AI 提示流编排器:告别单画布重绘瓶颈,状态机分层解耦与双模存储实战(开源系列 04)》
📖 《从 0 到 1 打造 AI 提示流编排器:从 Dify 学习知识库架构,实现私有文档检索与 RAG 画布节点(开源系列 05)》
📖 《从 0 到 1 打造 AI 提示流编排器:条件分支路由、Kahn 图剪枝与 HTTP 节点实战(开源系列 06)》
一、 引言:编排器不能只留在浏览器里当“自嗨玩具”
在上一篇(系列 06)中,我们完成了核心调度引擎的重大蜕变:引入了 9 大规则算子的 IF/ELSE 条件路由、打通了 Kahn 拓扑排序中的动态分支剪枝与聚合器合流,并用弹性 HTTP 节点连接了真实的外部网络。此时的 PatchCat,在计算表达能力上已经具备了构建工业级复杂 Agent 工作流的全部底座。
然而,当我和身边做技术与产品的朋友一起试用编排好的工作流时,两个极其尖锐的现实痛点浮出水面:
-
“黑盒盲测”与割裂的调试体验: 在画布上配置好一个包含意图分类、分支检索、大模型生成的完整链条后,如果想要验证不同用户输入的表现,以往只能一遍遍去修改
InputNode的表单,点击顶部的“运行工作流”,然后蹲在底部展开的日志抽屉里肉眼一条条检索报错。 真实的人机协作是对话式的。 开发者迫切需要一个像 Dify / Coze 那样,能够在不切屏的情况下随手唤出、支持连续多轮、能像真实用户一样输入提问并实时看到 Token 消耗与每个节点耗时下钻的即时交互式调试抽屉。 -
无法落地的“空中楼阁”危机: 即便我们在浏览器画布里把工作流调得天衣无缝,如果它无法对外交付任何服务契约,那它就只是一张漂亮的本地可视化图表。它必须能够被一键发布成标准的生产 REST API,让企业内部的前端项目、移动端 App、微信公众号后台或 Python 自动化脚本能够通过标准 HTTP / SSE 请求无缝集成。
如何让编排器从“玩具”真正跨越到“可交付的生产基础设施”?
这正是 PatchCat 在 v0.3.0 中完成的“最后一公里”攻坚。本篇将继续公开坦诚地分享我和 AI 搭档(Google Gemini)在架构推演、协议设计与极限测试中的双向共创过程。
二、 关键决策与双向共创:人机启发推演矩阵 (Milestone Co-Discovery)
在设计“对话调试抽屉”与“API 一键发布”两大模块时,我和 AI 搭档再次形成了高频互动的方案碰撞:
关键点 1:调试抽屉的沉浸感与多维可观测性
- 我提出的产品体验痛点: 在画布中央弹出一个巨大的 Modal 会完全遮挡工作流拓扑,无法在看对话的同时观察画布节点的亮灯变色;同时,调试不仅要看最终回复,更要能一眼看出“到底是 RAG 召回花了 3 秒,还是哪个大模型响应卡住了”。
- AI 搭档提出的工程规范:
建议将调试面板设计为右侧无模态滑动抽屉(Slide-over Drawer),并支持全局快捷键
Ctrl+Shift+D;为了精准定位性能瓶颈,不能仅把日志平铺打印,而要在底层收集并聚合每个节点的开始时间戳与耗时,在聊天气泡下方生成节点级耗时瀑布流(Per-node Trace Breakdown),同时支持将整场会话的 Trace 一键导出为 Markdown 诊断报告。
关键点 2:工作流发布为 API 的契约与安全隔离
- 我提出的业务诉求: 用户在界面上点一下“发布 API”,就能立即拿到一个固定的 HTTP 端点,并且能在页面里直接复制跑得通的 cURL、Python 和 JavaScript 代码,粘贴到终端里就能直接用。
- AI 搭档指出的生产架构隐患:
AI 搭档从生产微服务安全与网络规范角度给出了关键约束:
- 双模响应契约:调用者有的需要一次性拿到 JSON 结果做后台处理,有的需要前端打字机流式呈现,后端必须同时支持普通同步响应与
text/event-streamSSE 流式输出; - API Key 生命周期隔离:必须实现基于 Workflow 维度的独立 API Key(形如
pk_live_...),而不是让调用者使用管理后台的全局 Token,且必须支持一键撤销、重新生成与随时启停访问; - 参数动态注入契约:接口输入必须与画布上的首个
InputNode字段自动做反射映射,不能要求外部调用方理解内部复杂的节点 ID。
- 双模响应契约:调用者有的需要一次性拿到 JSON 结果做后台处理,有的需要前端打字机流式呈现,后端必须同时支持普通同步响应与
三、 核心技术剖析一:交互式对话调试抽屉(Chat Debug Panel)
在前端实现中,我们创建了 src/components/panels/ChatDebugPanel.tsx,并将其无缝集成在画布右侧:

1. 全局快捷键与状态解耦
我们通过注册全局键盘监听事件,实现了 Ctrl+Shift+D(Mac 下为 Cmd+Shift+D)的极速唤起,使得开发者双手无需离开键盘即可在“画布调参”与“对话问答”之间无缝切换:
// src/components/panels/ChatDebugPanel.tsx (键盘事件监听)
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key.toLowerCase() === 'd') {
e.preventDefault();
onToggle();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [onToggle]);
2. SSE 流式打字机逐字渲染与 Trace 归集
当用户在对话框中敲击回车时,前端引擎 BrowserWorkflowEngine 启动执行,并通过 AsyncGenerator 实时推流:
// src/components/panels/ChatDebugPanel.tsx (执行与流式捕获核心)
const nodeTraces: Record<string, { label: string; durationMs: number; status: string }> = {};
let assistantContent = '';
const startTime = Date.now();
try {
const eventStream = engine.executeWorkflow({
nodes,
edges,
inputs: { query: userText, input: userText }, // 自动注入首节点变量
});
for await (const event of eventStream) {
if (event.type === 'LLM_CHUNK') {
// 捕获大模型逐字吐出的 Token,触发打字机动画
assistantContent += event.payload.chunk;
updateCurrentMessage(assistantContent);
} else if (event.type === 'NODE_COMPLETE') {
// 捕获每个节点的完成事件,记录执行耗时
const { nodeId, durationMs } = event.payload;
const node = nodes.find(n => n.id === nodeId);
nodeTraces[nodeId] = {
label: node?.data.label || nodeId,
durationMs,
status: 'success',
};
} else if (event.type === 'NODE_SKIPPED') {
const { nodeId } = event.payload;
nodeTraces[nodeId] = {
label: nodeId,
durationMs: 0,
status: 'skipped',
};
}
}
} finally {
finalizeMessage({
content: assistantContent,
traces: Object.values(nodeTraces),
totalDurationMs: Date.now() - startTime,
});
}
每个回答气泡下方都附带一个可折叠的 “执行耗时明细”,点击即可下钻查看每一个节点的处理毫秒数;右上角更贴心配备了 “导出诊断报告” 按钮,一键生成 Markdown 格式的完整排查记录,极大地提升了研发排障效率。
四、 核心技术剖析二:一键发布为生产 REST API
为了让工作流能够被企业系统调用,我们在 FastAPI 后端新增了专用的执行路由:POST /api/v1/workflows/{workflow_id}/run。

1. 后端双模执行引擎(FastAPI 源码剖析)
在 server/app/api/v1/endpoints/workflows.py 中,我们为接口设计了双重响应机制——调用方只需通过请求参数 ?stream=true 即可自由切换:
# server/app/api/v1/endpoints/workflows.py (精简核心)
@router.post("/{workflow_id}/run")
async def run_published_workflow(
workflow_id: str,
request: WorkflowRunRequest,
stream: bool = Query(False, description="是否启用 SSE 流式传输"),
api_key: str = Header(..., alias="X-API-Key"),
db: AsyncSession = Depends(get_db)
):
# 1. 安全鉴权:校验工作流维度的独立 API Key 与发布状态
workflow = await workflow_service.get_workflow(db, workflow_id)
if not workflow or not workflow.api_published:
raise HTTPException(status_code=403, detail="Workflow is not published or does not exist.")
if workflow.api_key != api_key:
raise HTTPException(status_code=401, detail="Invalid Workflow API Key.")
# 2. 模式 A:流式 SSE 输出 (用于前端聊天打字机)
if stream:
async def event_generator():
async for event in workflow_service.execute_stream(workflow, request.inputs):
# 以标准的 text/event-stream 协议向外持续泵出事件
yield f"data: {json.dumps(event.dict())}\n\n"
yield "data: [DONE]\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
# 3. 模式 B:同步 JSON 输出 (用于后台任务与自动化系统)
result = await workflow_service.execute_sync(workflow, request.inputs)
return {
"status": "success",
"workflow_id": workflow_id,
"outputs": result.outputs,
"latency_ms": result.duration_ms,
"tokens_used": result.tokens_used
}
2. 开发者友好的发布弹窗(PublishApiModal)
在前端画布顶部点击 “发布为 API”,会弹出优雅的发布控制台 PublishApiModal.tsx:
- 开关即生效:一键开启或关闭对外服务;
- 密钥重置:支持随时轮换泄漏的 API Key;
- 自适应代码示例:弹窗内自动填充当前的
workflow_id与生成的API Key,提供可直接在终端粘贴运行的 cURL 命令、具备优雅流式解析的 Python 脚本,以及现代浏览器原生的 JavaScript Fetch 代码。
# 复制即可直接测试的生产请求示例
curl -X POST "http://localhost:8000/api/v1/workflows/wf_8f3d12/run" \
-H "Content-Type: application/json" \
-H "X-API-Key: pk_live_9a7b4c6e1f028" \
-d '{"inputs": {"query": "如何申请技术支持退款?"}}'
五、 工业级场景验证:两大官方内置预设
伴随 v0.3.0 的发布,我们在仓库中内置了两个极具代表性的端到端实战预设(支持中英双语开箱即用):
预设 1:智能客服意图条件路由(Conditional Customer Routing)
- 拓扑链路:
用户输入➔LLM 意图分类器➔IF/ELSE 条件分支➔并行专业话术处理➔变量聚合器➔最终输出; - 业务价值:完整跑通了系列 06 中的动态分支剪枝与合流,展示了复杂工单流在对话抽屉里的丝滑响应。
预设 2:实时天气 API 集成(Weather API Integration)
- 拓扑链路:
输入城市名称➔HttpNode 发送 GET 请求至 Open-Meteo API➔LLM 进行人性化出行建议润色➔输出; - 业务价值:验证了 HTTP 节点在真实公网环境下的超时控制与数据结构解包能力。
六、 极端测试实证:115 个测试全绿的工程底气
为了让系统真正达到生产可用的水位,我们进一步将自动化测试用例扩充至 115 个(前端 94 个单元测试 + 后端 21 个 pytest 集成测试),在持续集成中保持 100% 全绿通过:
- API Key 越权与禁用防御测试: 测试当工作流未勾选“发布”时外部调用的 403 拦截;测试使用错误 API Key 调用的 401 拦截;测试重置 Key 后旧 Key 的立即失效。
- SSE 长连接异常中断与资源释放: 在 pytest 中模拟客户端接收一半数据后强行断开连接,断言 FastAPI 后端任务能感知并在上下文管理器中优雅释放数据库会话与模型资源。
- Chat 调试抽屉的会话隔离与内存防泄漏: 测试在连续进行 50 轮问答并频繁唤起/隐藏抽屉时,React 状态树的内存占用平稳,Trace 节点数据无重复累积。
七、 总结与开源演进展望
从最初系列 01 的几百行纯前端原型,到如今系列 07 的全功能提示流编排平台:
- 我们攻克了 Kahn 拓扑调度、多模型思维链、状态机解耦、Dify 三层 RAG 知识库;
- 我们落地了 条件分支动态剪枝、多路汇聚聚合器、弹性 HTTP 网络触角;
- 我们补齐了 即时对话调试抽屉与一键发布生产 REST API 的最后一公里交付。
这一路走来,正是“真实坦诚的人机结对编程”与“在实战中边写边学”的最佳佐证。承认经验有限,在 AI 搭档的严谨推演与规约守护下,一步一个脚印地把每一行代码、每一个测试用例夯实。
未来演进展望
PatchCat 的核心框架已经基本成型,但距离真正的行业顶尖系统依然有探索空间:
- 循环节点(Loop / While Node):支持基于条件迭代的自纠错与自我反思;
- 人机协同断点(Human-in-the-loop):敏感操作时暂停工作流等待人工审批;
- 子图嵌套(Sub-workflow):允许将整个工作流作为一个独立节点打包复用。
开源是一场长跑,求指教、求交流!欢迎各位开发者 访问 GitHub 仓库 给个 Star,或在 Issue 区留下您的宝贵建议!