课程LubanAgent 实战课:8 课从零到部署你的业务 Agent / 第三章 · 质量与上线:评测、图编排与生产部署 / 第 8 课 部署上生产:Docker 全栈、Langfuse 观测与升级演练
— 12 min read

第 8 课 部署上生产:Docker 全栈、Langfuse 观测与升级演练

用 Docker 起五服务全栈并接 Langfuse 双端回放 Trace,演练跟上游升级而不丢 app/ 领地改动(领地契约),最后用评测回归证明升级没改坏,完成毕业。

第 8 课:部署上生产——全栈、观测、以及升级不丢改动

学完本课你能:用 Docker 起五服务全栈并在 Langfuse 回放自己的 Trace;演练一次「跟上游升级不丢改动」的合并;用评测回归证明升级没改坏东西 | 预计耗时 60 分钟 | 前置:第 7 课 | 难度 ★★★

最后一课。前七课你一直在自己的笔记本上折腾——dev.sh 起停、SQLite、终端里看日志。这一课把小图带出笔记本:Docker 全栈部署、独立观测系统、还有这个框架最独特的一个承诺的实操验证——你在这个仓库里写了两课的 app/ 领地,框架升级时会不会被冲掉?

先回答一个你可能憋了很久的问题:为什么部署要五个服务?因为一个真实的 Agent 系统不止"跑模型的 API":应用本体(app)、数据库(MySQL)、观测(Langfuse + 它自己的库)、还有客服示例依赖的假业务系统(mock-biz)。开发期它们被 SQLite 和 noop 观测简化掉了,生产形态得把它们各就各位。

1. 学习目标

  • 用 docker compose 起五服务全栈,验证每个服务的角色
  • 接上 Langfuse,双端(Langfuse / 工作台 Traces)回放同一条 Trace
  • 完成一次模拟的「上游升级合并」,确认 app/ 领地完好
  • 用第 6 课的评测集做升级后的终检

2. 概念讲解:镜像、观测分层、领地契约

单镜像单端口。 app 的镜像里装了三样东西:Python 运行时、后端、构建好的工作台前端(构建期用 node 打包,运行期由 FastAPI 在 8000 端口一并托管)。所以生产上没有"前端服务器"这个东西——一个镜像一个端口,反向代理都省了。

观测的三个层次。 这个框架的观测设计值得看清楚再配:

  1. 内部 DB 永远是 source of truth——每个 Run/Trace 先落库,这一层不需要任何配置;
  2. Langfuse 是旁路——Trace 同步外发到自部署的 Langfuse(数据在你自己的服务器上,这是"数据主权"),用于漂亮的 UI、筛选、对比;外发失败不阻断业务(失败记录留痕、自动重试);
  3. 可切换可关闭——OBSERVABILITY_BACKEND=none/langfuse/langsmith,不宣传"完全等价切换"。

为什么"双端回放同一条 Trace"是这一课的检查点?因为它一次验证了前两层:内部库里有(工作台能放),外发也活着(Langfuse 能放)。

领地契约(本课的主角)。 回想第 1 课的"你住哪里,框架住哪里":app/ 是你的领地,lubanagent/ 是框架。这个分界有三件配套设施保证它不只是口号:

  • .lubanagent-upstream——一个清单文件,把仓库里每个文件归类(framework / shared / scaffold-seed / user-owned),合并冲突时按类处理;
  • CHANGELOG 按领地打标签——[framework] 的变更你直接合并;[scaffold-seed](种子文件,如客服示例)的变更默认保留你的版本;[migration] 附带数据库升级命令;
  • UPGRADE.md——每个版本的合并指引。

上游升级的操作形态:git fetch upstream && git merge upstream/master,冲突按上面的规则处理。这一课你会演练一遍(用本地分支模拟上游)。

3. 动手:主线步骤

步骤 1:起全栈

docker compose up -d --build

首次构建约 3-5 分钟(含前端构建)。起来后五个服务:

服务 端口 角色
app 8000 后端 API + 工作台(同端口)
mysql 3306(内部) 生产数据库(app 容器启动时自动跑 luban migrate && luban init,幂等——见 app 行)
langfuse 3000 观测 UI
langfuse-db —(内部) Langfuse 自己的 Postgres
mock-biz 9100 客服示例的假业务系统

✅ 你应该看到:docker compose ps 五个容器全部 running;其中 app/mysql/langfuse-db 配了 healthcheck、过一会显示 running (healthy),langfuse 和 mock-biz 只显示 running(它们没配 healthcheck,不是故障)。浏览器开 http://localhost:8000 ——工作台出来了,这次不是 Vite 开发服务器,是镜像里构建好的前端。注意端口 8000 上没有你本地开发的数据(这是独立的 MySQL 容器库)——luban init 已在容器启动时自动跑过,内置客服在,但你的 my-lib-assistant 不在(它在你的开发库里)。这正常:生产部署的是你决定发布的东西。

步骤 2:在生产栈上跑一次对话

curl -N -X POST http://localhost:8000/v1/agents/customer-service-bot/runs \
  -H "Content-Type: application/json" \
  -d '{"input": "帮我查一下订单 ORD-1001"}'

(生产栈连着 mock-biz 容器,查单能走通;需要真实模型体验则给 compose 的 app 服务配上你的 API Key 环境变量后 docker compose up -d app 重建。)

✅ 你应该看到:完整的 SSE 事件流,和第 1 课那条 curl 结构相同——生产栈和开发栈跑的是同一套代码。

步骤 3:接 Langfuse,双端回放

编辑 .env(或 compose 的 environment),设置:

OBSERVABILITY_BACKEND=langfuse
LANGFUSE_HOST=http://localhost:3000
LANGFUSE_PUBLIC_KEY=pk-lf-...      # Langfuse 首页 Settings 里创建
LANGFUSE_SECRET_KEY=sk-lf-...

docker compose up -d app 重建 app 使配置生效,然后再跑一次步骤 2 的对话。

✅ 你应该看到:Langfusehttp://localhost:3000,首次访问注册管理员账号)的 Traces 列表里出现这次 Run,点开能看到完整的调用树;工作台 Traces 页也能回放同一条——双端同源,一处不留另一处也查得到。配置前的那些 Run 只在工作台里(内部 DB 一直都在写)——这正是"内部库 source of truth、Langfuse 旁路"的直观体现。

步骤 4:演练「跟上游升级不丢改动」

这是毕业前最重要的一课。用本地分支模拟上游发新版(在你的开发仓库、不是 Docker 里):

# 1. 制造"上游新版本":从当前点拉一个分支,改一处框架文件(模拟上游修 bug)
git checkout -b simulated-upstream
echo "# upstream patch" >> lubanagent/runtime/context_builder.py
git commit -am "simulated upstream change"
git checkout master

# 2. 回到 master,模拟你已经有了自己的改动(你的 app/ 领地产物已在工作区)

# 3. 合并"上游"
git merge simulated-upstream

✅ 你应该看到:合并干净完成(无冲突或冲突只在框架文件)——app/ 下你的全部产出(agents/tools/datasets/graphs/multiagents)原封不动,框架文件更新了。检查:

ls app/agents/          # my_lib_assistant.yaml 还在
git diff simulated-upstream -- app/   # 你的领地与上游无交集
git branch -d simulated-upstream      # 清理演练分支(框架文件的模拟改动也还原)

真实场景里把 simulated-upstream 换成 upstream/master,配合 CHANGELOG 的 [migration] 标签跑 luban migrate[scaffold-seed] 的保留指令处理种子文件——流程完全同构。这套"能长期跟上游"的能力,是你选"基座"而不是"一次性模板"的根本理由。

步骤 5:升级终检——评测回归

模拟升级后,跑第 6 课的评测集做终检(在开发环境):

.venv/bin/luban eval run my-lib-assistant --dataset app/datasets/my_lib_eval.yaml --compare my-baseline.json

✅ 你应该看到:regressions: []升级(或任何大动作)之后跑一次评测回归,这是这套教程希望你带走的工作习惯——它把"应该没改坏"变成"没改坏,有证据"。

步骤 6:收工(可选)

docker compose down        # 停栈(数据卷保留)
docker compose down -v     # 连数据卷一起清(彻底重置)

4. 完成自检清单

  • 五服务 healthy,8000 单端口出工作台
  • 生产栈上完成一次对话(事件流与开发栈同构)
  • Langfuse 与工作台双端能放同一条 Trace
  • 模拟升级合并完成,app/ 领地零改动
  • 升级终检 regressions: []

5. 常见坑

docker compose up 构建慢/失败。 前端构建吃网络(npm 源),参考第 1 课坑 5 换镜像。构建失败看 docker compose logs app

Langfuse 配了但 Trace 不出现。 检查三点:Key 是否从 Langfuse Settings 里创建(不是自己编的)、app 容器是否重建过(环境变量在容器创建时注入)、LANGFUSE_HOST 从 app 容器的视角是否可达(compose 网络内是 http://langfuse:3000,宿主机访问才是 localhost:3000——compose 文件里已配好,自己改时别改错视角)。

合并冲突比预想多。 看冲突文件的领地归属:framework 的优先上游(你本来就不该改它),scaffold-seed 的优先自己(git merge -X ours),你的 user-owned 文件上游根本不会动。拿不准时对照 .lubanagent-upstream 清单。

生产栈里找不到我的 Agent。 正常——生产库是独立的 MySQL,你开发库里的东西不会自动出现。发布 = 把你的 YAML/代码带到生产仓库再跑 luban init(这正是"定义是数据、可导出可导入"设计的用武之地)。

6. 练习

练习 A(模仿):给 compose 的 app 服务加上你的 DeepSeek Key(环境变量),重建后在工作台给生产库也绑上模型和你的 my-lib-assistant(luban export 导出 YAML → 放进 app/agents/ → 容器内 init,或直接经 API 创建)。体验一次完整的"发布"。

练习 B(变式):把 OBSERVABILITY_BACKEND 切成 none,重建,跑一次对话——工作台 Traces 依然全量可放(内部 DB 不依赖观测后端)。再切回 langfuse。理解"可关闭"不是"关了就瞎"。

练习 C(综合,毕业验收):写一份你自己的「毕业验收单」:列 8-10 条你认为这个项目必须成立的验收项(至少覆盖:三种执行形态各一条、守门一条、评测回归一条、升级合并一条),逐条跑通并留证据(命令输出/截图)。这份单子就是你以后带自己的 Agent 项目上线的 checklist 初稿。

7. 毕业

走到这里,你拥有的不是一个"跟练 demo",是一个可以写进简历、可以拿出来演示的完整项目:

  • 一个你自己命名的 Agent 产品(my-lib-assistant 或你的场景):人设、真实模型、自写的低/高风险工具(含审批流)、校准过阈值的知识库、8 用例评测集与基线、一条含人工节点的图流程、一个路由正确的团队——全部住在你的 app/ 领地;
  • 可量化的证据:评测报告、Trace 与费用归因;
  • 可运行的部署:五服务 Docker 栈 + Langfuse 观测;
  • 一次真实演练过的升级路径:领地契约不是纸面承诺。

毕业设计建议(六选一)

方向 难度 依赖课程 验收标准
课程答疑助手(RAG 重) ★★ 2-5 30+ 条评测集全绿,含 5 条对抗性拒答用例
审批型办事助手(流程重) ★★★ 4、7 高风险操作 100% 走审批;图含 human 节点且 interrupt/resume 可演示
技术支持分流台(多 Agent 重) ★★★ 7 supervisor 路由准确率有评测数字;max_delegations 守住
评测驱动的 Prompt 迭代报告(方法重) ★★ 6 ≥3 轮「改-评-对比」记录,--compare 出回归结论
SOP Skill 化助手(知识工程重) ★★ 4、5 + 手册 §5 Skill 自建 SKILL.md + scripts,zip 上传可用
全栈部署运维报告(部署重) ★★ 8 五服务部署 + 成本分析 + 一次升级合并实录

每个方向都请附一段2 分钟演示脚本(讲什么、演示什么、晒哪张截图)——学生晒作品,就是这个项目最好的传播。

欢迎把你的毕业作品、Trace 截图、验收单发到社区(Issue/讨论区)。遇到坑也欢迎提——教程的"常见坑"一节就是这样长出来的。


延伸阅读:UPGRADE.md(升级合并指引)、部署安全须知 | 涉及文件:docker-compose.ymldocker/Dockerfile.lubanagent-upstreamCHANGELOG.md

目录