Langfuse 自托管部署教程(192.168.110.207 实战版)
本文记录在
192.168.110.207上,基于仓库/Langfuse/langfuse官方docker-compose.yml把 Langfuse 4 拉起来的完整步骤。与本机已有的 Dify 共存,不改 Dify 代码、不占用 80/443。
本次实际结果:Langfuse
4.12.0已可访问,健康检查返回{"status":"OK","version":"4.12.0"}。
运行环境:业务脚本均在 207 固定目录
/opt/workbuddy执行,Python 为/usr/bin/python3。 Windows 开发机仅保留_probe_*调试脚本;同步命令:python scripts_cursor/完整代码约定/00_部署_workbuddy到207.py。
一、适用环境
| 项 | 值 |
|---|---|
| 服务器 | 192.168.110.207 |
| 系统 | Linux + Docker + Docker Compose v2 |
| 部署目录 | /Langfuse/langfuse |
| 源码仓库 | https://github.com/langfuse/langfuse.git |
| Compose 项目名 | langfuse |
| 访问地址 | http://192.168.110.207:3000 |
| 健康检查 | http://192.168.110.207:3000/api/public/health |
| MinIO S3 API | http://192.168.110.207:9090 |
| 同机已有服务 | Dify(80/443)、Xinference(9997) |
前置要求:
- Docker Compose ≥ v2
- 磁盘建议剩余 10GB 以上(本次镜像合计大约 4GB+)
- 能访问镜像仓库。国内直连 Docker Hub 很容易超时,需要镜像加速。
二、部署思路
Langfuse 官方自托管不需要从源码编译。仓库根目录的 docker-compose.yml 会一次性拉起 6 个容器:
| 服务 | 镜像 | 作用 | 端口 |
|---|---|---|---|
langfuse-web | langfuse/langfuse:4 | Web 控制台 | 0.0.0.0:3000 |
langfuse-worker | langfuse/langfuse-worker:4 | 后台任务 | 127.0.0.1:3030 |
postgres | postgres:17 | 元数据 | 127.0.0.1:5432 |
redis | redis:7 | 队列 | 127.0.0.1:6379 |
clickhouse | clickhouse/clickhouse-server:25.12 | 观测数据 | 127.0.0.1:8123、9000 |
minio | cgr.dev/chainguard/minio | 对象存储 | 0.0.0.0:9090 |
要点:
- 只用官方 compose,不要用
docker-compose.dev.yml。 后者是开发模式。 - 必须自备
.env。 仓库只有.env.prod.example,没有现成.env。 - Langfuse 使用自己的 Postgres / Redis,不要复用 Dify 的库。 两边容器名不同,互不影响。
- Web 必须绑定局域网地址。
NEXTAUTH_URL写成http://192.168.110.207:3000,不要写localhost。
2.1 部署阶段与验收(通读后再动手)
| 阶段 | 对应下文章节 | 做完应看到什么 |
|---|---|---|
| 准备 | 一、三 | 端口不冲突、磁盘够、镜像能拉 |
拉代码与 .env | 四~五 | /Langfuse/langfuse/.env 已按本文填好 |
| 起栈 | 六 | 6 容器 running;:3000/api/public/health → OK |
| 初始化 | 七~八 | 能登录 Web;pk/sk 已写入 Dify 用 |
| 与 Dify 对接 | (另文) | 见 Langfuse_连接Dify专题 |
| 常见现象 | 可能原因 | 处理 |
|---|---|---|
| pull 超时 / i/o timeout | Docker Hub 不可达 | 下文「镜像加速」;换 daoCloud 等 mirror |
| web 容器 unhealthy | .env 缺 NEXTAUTH_SECRET 等 | 对照 .env.prod.example 逐项补 |
| 3000 能开但登录跳 localhost | NEXTAUTH_URL 写错 | 改为 http://192.168.110.207:3000 |
| 与 Dify 抢 5432 | 误改 compose 复用 Dify PG | 禁止复用;Langfuse 自有 postgres 容器 |
| health OK 但 UI 502 | worker 未起或 Redis 连不上 | docker compose logs langfuse-worker |
三、和 Dify 共存时的端口约定
本次 207 上 Dify 已经占用:
80/443:Dify Nginx5003:plugin_daemon9997:Xinference8088:weaviate 中继
Langfuse 本次使用:
3000:控制台(对外)9090:MinIO(对外)5432/6379/8123/9000/3030:只绑127.0.0.1
Dify 的 Postgres / Redis 没有映射到宿主机,所以 Langfuse 把 127.0.0.1:5432 和 127.0.0.1:6379 给自己用是安全的。不要改成 0.0.0.0:5432,否则可能和别的本机数据库冲突。
四、第 1 步:确认源码目录
SSH 登录 207 后:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_01.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_01.sh
#!/usr/bin/env bash
set -euo pipefail
ls -la /Langfuse/langfuse
cd /Langfuse/langfuse
git remote -v
ls docker-compose.yml .env.prod.example正常应能看到:
docker-compose.yml.env.prod.exampleweb/、worker/、packages/
如果目录还不存在,再克隆:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_02.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_02.sh
#!/usr/bin/env bash
set -euo pipefail
mkdir -p /Langfuse
cd /Langfuse
git clone https://github.com/langfuse/langfuse.git
cd langfuse五、第 2 步:生成密钥并写 .env
在服务器上生成密钥:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_03.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_03.sh
#!/usr/bin/env bash
set -euo pipefail
openssl rand -hex 32 # NEXTAUTH_SECRET
openssl rand -hex 16 # SALT
openssl rand -hex 32 # ENCRYPTION_KEY,必须是 64 位十六进制
openssl rand -hex 16 # POSTGRES_PASSWORD
openssl rand -hex 12 # CLICKHOUSE_PASSWORD
openssl rand -hex 16 # REDIS_AUTH
openssl rand -hex 16 # MINIO_ROOT_PASSWORD在 /Langfuse/langfuse/.env 写入下面内容。把占位符换成上一步生成的值。
完整 .env 片段:patches_cursor/Langfuse_部署教程/Langfuse部署_内联_01.env
运行/引用:patches_cursor/Langfuse_部署教程/Langfuse部署_内联_01.env
NEXTAUTH_URL=http://192.168.110.207:3000
NEXTAUTH_SECRET=请替换
SALT=请替换
ENCRYPTION_KEY=请替换
POSTGRES_USER=postgres
POSTGRES_PASSWORD=请替换
POSTGRES_DB=postgres
DATABASE_URL=postgresql://postgres:请替换@postgres:5432/postgres
CLICKHOUSE_USER=clickhouse
CLICKHOUSE_PASSWORD=请替换
REDIS_AUTH=请替换
MINIO_ROOT_USER=minio
MINIO_ROOT_PASSWORD=请替换
LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID=minio
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY=请替换
LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID=minio
LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY=请替换
LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID=minio
LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY=请替换
TELEMETRY_ENABLED=false
LANGFUSE_INIT_ORG_ID=dckj
LANGFUSE_INIT_ORG_NAME=DCKJ
LANGFUSE_INIT_PROJECT_ID=default
LANGFUSE_INIT_PROJECT_NAME=default
LANGFUSE_INIT_USER_EMAIL=admin@example.com
LANGFUSE_INIT_USER_NAME=admin
LANGFUSE_INIT_USER_PASSWORD=Langfuse123456必须遵守的规则:
ENCRYPTION_KEY必须是 64 位 hex,用openssl rand -hex 32得到。LANGFUSE_INIT_USER_EMAIL必须是合法邮箱。admin@local会让 Web 直接报Invalid environment variables,页面 500。POSTGRES_PASSWORD、DATABASE_URL里的密码、以及所有 MinIO 的SECRET_ACCESS_KEY必须一致。NEXTAUTH_URL必须是别人浏览器实际访问的地址。
本次 207 环境已经写好的登录信息:
- 邮箱:
admin@example.com - 密码:
Langfuse123456
六、第 3 步:拉镜像(国内环境重点)
官方 compose 会拉这些镜像:
docker.io/langfuse/langfuse:4
docker.io/langfuse/langfuse-worker:4
docker.io/postgres:17
docker.io/redis:7
docker.io/clickhouse/clickhouse-server:25.12
cgr.dev/chainguard/minio本次实战中,直连 registry-1.docker.io 会报:
Get "https://registry-1.docker.io/v2/": dial tcp ...:443: i/o timeout所以不要死等官方源。推荐用国内镜像源拉完再打回官方标签:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_04.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_04.sh
#!/usr/bin/env bash
set -euo pipefail
# Redis
docker pull docker.m.daocloud.io/library/redis:7
docker tag docker.m.daocloud.io/library/redis:7 redis:7
docker tag redis:7 docker.io/redis:7
# Langfuse Worker
docker pull docker.m.daocloud.io/langfuse/langfuse-worker:4
docker tag docker.m.daocloud.io/langfuse/langfuse-worker:4 langfuse/langfuse-worker:4
docker tag langfuse/langfuse-worker:4 docker.io/langfuse/langfuse-worker:4
# ClickHouse
docker pull docker.m.daocloud.io/clickhouse/clickhouse-server:25.12
docker tag docker.m.daocloud.io/clickhouse/clickhouse-server:25.12 clickhouse/clickhouse-server:25.12
docker tag clickhouse/clickhouse-server:25.12 docker.io/clickhouse/clickhouse-server:25.12
# Web / Postgres 若还没有,同样处理
docker pull docker.m.daocloud.io/langfuse/langfuse:4
docker tag docker.m.daocloud.io/langfuse/langfuse:4 langfuse/langfuse:4
docker tag langfuse/langfuse:4 docker.io/langfuse/langfuse:4
docker pull docker.m.daocloud.io/library/postgres:17
docker tag docker.m.daocloud.io/library/postgres:17 postgres:17
docker tag postgres:17 docker.io/postgres:17备选镜像源:
docker.xuanyuan.medocker.1ms.run
注意:docker.1ms.run 本次拉 Redis 时出现过长时间卡住。某个源超过 3 分钟没进度,就换下一个,不要一直等。
cgr.dev/chainguard/minio 不走 Docker Hub,本次可以直接拉下来。如果以后失败,再改 compose 用 minio/minio。
七、第 4 步:启动服务
在 /Langfuse/langfuse 执行:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_05.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_05.sh
#!/usr/bin/env bash
set -euo pipefail
cd /Langfuse/langfuse
docker compose -p langfuse up -d --remove-orphans-p langfuse 会让容器名变成:
langfuse-langfuse-web-1langfuse-langfuse-worker-1langfuse-postgres-1langfuse-redis-1langfuse-clickhouse-1langfuse-minio-1
这样不会和 Dify 的 docker-db_postgres-1、docker-redis-1 重名。
如果是通过 SSH 远程执行,不要让 docker compose up 挂在 SSH 会话上。本次用的是:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_06.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_06.sh
#!/usr/bin/env bash
set -euo pipefail
systemd-run --unit=langfuse-compose-up --collect \
/bin/bash -c 'cd /Langfuse/langfuse && docker compose -p langfuse up -d --remove-orphans'查看进度:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_07.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_07.sh
#!/usr/bin/env bash
set -euo pipefail
systemctl status langfuse-compose-up --no-pager
journalctl -u langfuse-compose-up -f八、第 5 步:验证
容器状态:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_08.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_08.sh
#!/usr/bin/env bash
set -euo pipefail
cd /Langfuse/langfuse
docker compose -p langfuse ps期望 6 个服务都是 Up,其中 postgres / redis / clickhouse / minio 为 healthy。
健康检查:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_09.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_09.sh
#!/usr/bin/env bash
set -euo pipefail
curl -sS http://127.0.0.1:3000/api/public/health
curl -sS -o /dev/null -w "%{http_code}\n" http://192.168.110.207:3000期望:
{"status":"OK","version":"4.12.0"}
200浏览器打开:
http://192.168.110.207:3000登录:
- 邮箱:
admin@example.com - 密码:
Langfuse123456
如果登录提示没有这个用户,用同一组邮箱密码走 Sign up 注册一次即可。原因见下一节。
九、本次踩过的坑
9.1 Docker Hub 超时
现象:compose up 拉到一半报 registry-1.docker.io 连接超时。
处理:已下载的层会留在本机,换国内镜像源继续拉,再 docker tag 成 compose 期望的名字。
9.2 SSH 会话把 compose 掐掉
现象:在 Windows 上用远程命令跑 docker compose up -d,镜像还没拉完就 Timeout,进程被杀掉,容器一个都没起来。
处理:用 systemd-run 或 setsid 在服务器本机后台跑,不要让 SSH 会话当父进程。
9.3 非法初始邮箱导致页面 500
现象:容器都 Up,但访问 http://192.168.110.207:3000 返回 500。Web 日志:
Invalid environment variables原因:.env 里写了 LANGFUSE_INIT_USER_EMAIL=admin@local。Langfuse Web 会做邮箱格式校验,@local 不通过。
处理:改成 admin@example.com,然后:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_10.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_10.sh
#!/usr/bin/env bash
set -euo pipefail
cd /Langfuse/langfuse
docker compose -p langfuse up -d langfuse-web9.4 空的 LANGFUSE_INIT_ORG_ID 会跳过自动建账号
现象:Web 已经 200,但日志写:
LANGFUSE_INIT_ORG_ID is not set but other LANGFUSE_INIT_* variables are configured.
The following variables will be ignored: ... LANGFUSE_INIT_USER_EMAIL ...处理:LANGFUSE_INIT_ORG_ID 和 LANGFUSE_INIT_PROJECT_ID 都要有值。本次用的是 dckj / default。如果自动建用户没生效,直接在页面 Sign up。
9.5 不要把 max_tokens 这类 Dify 问题混进来
Langfuse 这次部署不改 Dify、不改插件、不改数据库里的 Agent 配置。
十、日常运维
启动:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_11.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_11.sh
#!/usr/bin/env bash
set -euo pipefail
cd /Langfuse/langfuse
docker compose -p langfuse up -d停止:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_12.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_12.sh
#!/usr/bin/env bash
set -euo pipefail
cd /Langfuse/langfuse
docker compose -p langfuse stop看日志:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_13.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_13.sh
#!/usr/bin/env bash
set -euo pipefail
docker logs -f langfuse-langfuse-web-1
docker logs -f langfuse-langfuse-worker-1重启 Web:
完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_14.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_14.sh
#!/usr/bin/env bash
set -euo pipefail
cd /Langfuse/langfuse
docker compose -p langfuse up -d --force-recreate langfuse-web数据卷在 Docker volume 里,名字大致为:
langfuse_langfuse_postgres_datalangfuse_langfuse_redis_datalangfuse_langfuse_clickhouse_datalangfuse_langfuse_minio_data
不要随意 docker compose down -v,那会删数据。
十一、本次环境已验证的结果
| 检查项 | 结果 |
|---|---|
| 目录 | /Langfuse/langfuse |
| Compose 项目 | langfuse |
| Web 版本 | 4.12.0 |
| 健康检查 | {"status":"OK","version":"4.12.0"} |
| 登录页 | HTTP 200 |
| 局域网访问 | HTTP 200 |
| Dify | 仍在运行,80/443 未受影响 |
访问入口:
http://192.168.110.207:3000登录:
admin@example.com
Langfuse123456.env 和内部密钥保存在服务器 /Langfuse/langfuse/.env,本地副本见 langfuse_deploy_creds.txt。
本专题 Python 程序一览
| 文件 | 用途 | |---|---|| Langfuse_00_确认容器与健康.py | Langfuse 专题 · 步骤 0:确认 Langfuse / Dify 容器与健康检查。 | | Langfuse_01_打langfuse_trace补丁_修改后.py | Langfuse 专题 · 步骤 5 · 修改后:给 langfuse_trace.py 补 Observation UUID,重启 worker。 | | Langfuse_01_确认langfuse_trace_修改前.py | Langfuse 专题 · 步骤 5 · 修改前:确认 langfuse_trace.py 是否已打 Observation ID 补丁。 | | Langfuse_02_配置Dify_Tracing.py | Langfuse 专题 · 步骤 3:POST trace-config + /trace 打开应用 Langfuse Tracing。 | | Langfuse_03_验收Trace与对话.py | Langfuse 专题 · 步骤 6:发验收对话 + 查 Celery ops_trace + Langfuse traces API。 |
Langfuse_00_确认容器与健康.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Langfuse_连接Dify专题/Langfuse_00_确认容器与健康.py
完整补丁:patches_cursor/Langfuse_部署教程/Langfuse部署_内联_01.py
运行/引用:见 patches_cursor/Langfuse_部署教程/Langfuse部署_内联_01.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Langfuse 专题 · 步骤 0:确认 Langfuse / Dify 容器与健康检查。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
print("=== 容器 ===")
out, err = d.sh(
"docker ps --format 'table {{.Names}}\t{{.Status}}' | grep -E 'langfuse|docker-api-1|docker-worker-1'"
)
print(out or err)
print("\n=== Langfuse health ===")
out, err = d.sh("curl -s %s" % C.LANGFUSE_HEALTH)
print(out or err)
for ctr in (C.DIFY_API_CTR, C.DIFY_WORKER):
print("\n=== %s -> Langfuse ===" % ctr)
out, err = d.sh(
"docker exec %s curl -s -m 8 %s" % (ctr, C.LANGFUSE_HEALTH)
)
print(out or err)
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())Langfuse_01_打langfuse_trace补丁_修改后.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Langfuse_连接Dify专题/Langfuse_01_打langfuse_trace补丁_修改后.py
完整补丁:patches_cursor/Langfuse_部署教程/Langfuse部署_内联_02.py
运行/引用:见 patches_cursor/Langfuse_部署教程/Langfuse部署_内联_02.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Langfuse 专题 · 步骤 5 · 修改后:给 langfuse_trace.py 补 Observation UUID,重启 worker。"""
from __future__ import annotations
import sys
import time
import _bootstrap # noqa: F401
import _config as C
import _plugin_util as PU
from dify_api import Dify
LF_TRACE_PATH = (
"/app/api/.venv/lib/python3.12/site-packages/dify_trace_langfuse/langfuse_trace.py"
)
def patch_container(d: Dify, container: str) -> None:
content = PU.container_read(d, container, LF_TRACE_PATH)
if PU.LF_TRACE_MARKER in content:
print(container, "already patched")
return
new_content = PU.apply_langfuse_trace_patch(content)
PU.container_write(d, container, LF_TRACE_PATH, new_content)
out, err = d.sh(
"docker exec %s python -m py_compile %s" % (container, LF_TRACE_PATH)
)
print(container, "py_compile:", out or err or "ok")
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
for ctr in (C.DIFY_WORKER, C.DIFY_API_CTR):
patch_container(d, ctr)
print("\n=== restart docker-worker-1 ===")
out, err = d.sh("docker restart %s" % C.DIFY_WORKER, t=120)
print(out or err)
for i in range(15):
out, _ = d.sh("docker logs --since 30s %s 2>&1 | tail -5" % C.DIFY_WORKER)
if "ready" in (out or "").lower():
print("worker ready")
break
time.sleep(2)
else:
print("[WARN] 未在日志中看到 celery ready,请手动 docker logs -f", C.DIFY_WORKER)
for ctr in (C.DIFY_WORKER, C.DIFY_API_CTR):
print(PU.grep_in_container(d, ctr, LF_TRACE_PATH, "auto-assign if missing"))
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())Langfuse_01_确认langfuse_trace_修改前.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Langfuse_连接Dify专题/Langfuse_01_确认langfuse_trace_修改前.py
完整补丁:patches_cursor/Langfuse_部署教程/Langfuse部署_内联_03.py
运行/引用:见 patches_cursor/Langfuse_部署教程/Langfuse部署_内联_03.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Langfuse 专题 · 步骤 5 · 修改前:确认 langfuse_trace.py 是否已打 Observation ID 补丁。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
import _plugin_util as PU
from dify_api import Dify
LF_TRACE_PATH = (
"/app/api/.venv/lib/python3.12/site-packages/dify_trace_langfuse/langfuse_trace.py"
)
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
for ctr in (C.DIFY_WORKER, C.DIFY_API_CTR):
print("\n=== %s ===" % ctr)
out, err = d.sh("docker exec %s ls -l %s" % (ctr, LF_TRACE_PATH))
print(out or err)
grep = PU.grep_in_container(d, ctr, LF_TRACE_PATH, "auto-assign if missing")
if grep.strip():
print("[OK] 已打补丁:\n", grep)
else:
print("[WARN] 未打补丁 — 运行 Langfuse_01_打langfuse_trace补丁_修改后.py")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())Langfuse_02_配置Dify_Tracing.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Langfuse_连接Dify专题/Langfuse_02_配置Dify_Tracing.py
完整补丁:patches_cursor/Langfuse_部署教程/Langfuse部署_内联_04.py
运行/引用:见 patches_cursor/Langfuse_部署教程/Langfuse部署_内联_04.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Langfuse 专题 · 步骤 3:POST trace-config + /trace 打开应用 Langfuse Tracing。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
ok, _, _ = d.login()
print("login", ok)
if not ok:
return 1
pp("current trace", d.get("/apps/%s/trace" % C.APP_ID))
cfg = d.get("/apps/%s/trace-config?tracing_provider=langfuse" % C.APP_ID)
pp("current config", cfg)
body = {
"tracing_provider": "langfuse",
"tracing_config": {
"public_key": C.LANGFUSE_PK,
"secret_key": C.LANGFUSE_SK,
"host": C.LANGFUSE_URL,
},
}
if (cfg.get("json") or {}).get("has_not_configured"):
r = d.post("/apps/%s/trace-config" % C.APP_ID, body=body)
pp("create trace-config", r)
else:
r = d.req("PATCH", "/apps/%s/trace-config" % C.APP_ID, body=body)
pp("update trace-config", r)
if r.get("code") not in ("200", "201"):
r = d.post("/apps/%s/trace-config" % C.APP_ID, body=body)
pp("create after failed patch", r)
r = d.post(
"/apps/%s/trace" % C.APP_ID,
body={"enabled": True, "tracing_provider": "langfuse"},
)
pp("enable tracing", r)
pp("trace after", d.get("/apps/%s/trace" % C.APP_ID))
pp("config after", d.get("/apps/%s/trace-config?tracing_provider=langfuse" % C.APP_ID))
out, err = d.sh(
"docker exec %s psql -U postgres -d dify -c "
"\"SELECT id, name, tracing FROM apps WHERE id='%s';\""
% (C.PG_CONTAINER, C.APP_ID)
)
print(out or err)
return 0 if r.get("code") in ("200", "201") else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())Langfuse_03_验收Trace与对话.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Langfuse_连接Dify专题/Langfuse_03_验收Trace与对话.py
完整补丁:patches_cursor/Langfuse_部署教程/Langfuse部署_内联_05.py
运行/引用:见 patches_cursor/Langfuse_部署教程/Langfuse部署_内联_05.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Langfuse 专题 · 步骤 6:发验收对话 + 查 Celery ops_trace + Langfuse traces API。"""
from __future__ import annotations
import sys
import time
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
print("=== 1/3 发验收对话 ===")
payload = {
"inputs": {},
"query": C.PROBE_QUERY_LANGFUSE,
"response_mode": "streaming",
"files": [],
"conversation_id": "",
}
r = d.post(
"/agent/%s/chat-messages" % C.AGENT_ID,
body=payload,
extra="-m 420 --max-time 420",
t=450,
)
print("HTTP", r.get("code"))
print((r.get("text") or "")[-800:])
print("\n=== 2/3 worker ops_trace 日志 ===")
time.sleep(5)
out, err = d.sh(
"docker logs --since 5m %s 2>&1 | grep ops_trace | tail -10" % C.DIFY_WORKER
)
print(out or err or "(无 ops_trace 行)")
print("\n=== 3/3 Langfuse GET /api/public/traces ===")
out2, err2 = d.sh(
"curl -s -u '%s:%s' '%s/api/public/traces?limit=5'"
% (C.LANGFUSE_PK, C.LANGFUSE_SK, C.LANGFUSE_URL)
)
print(out2 or err2)
return 0 if r.get("code") == "200" else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())