📎 配套脚本附件正文未内嵌 · 共 5 个 · 点开可在线查看
📂 查看全部 761 个脚本附件 →
⚠️ 脚本内含明文凭据(与本页正文一致),请勿在公网环境直接运行。
Langfuse · Self Host

Langfuse 自托管部署教程

与 Dify 共存,官方 docker-compose,不占用 80/443。步骤未改,代码块已换成完整脚本。

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 APIhttp://192.168.110.207:9090
同机已有服务Dify(80/443)、Xinference(9997)

前置要求:


二、部署思路

Langfuse 官方自托管不需要从源码编译。仓库根目录的 docker-compose.yml 会一次性拉起 6 个容器:

服务镜像作用端口
langfuse-weblangfuse/langfuse:4Web 控制台0.0.0.0:3000
langfuse-workerlangfuse/langfuse-worker:4后台任务127.0.0.1:3030
postgrespostgres:17元数据127.0.0.1:5432
redisredis:7队列127.0.0.1:6379
clickhouseclickhouse/clickhouse-server:25.12观测数据127.0.0.1:8123、9000
miniocgr.dev/chainguard/minio对象存储0.0.0.0:9090

要点:

  1. 只用官方 compose,不要用 docker-compose.dev.yml。 后者是开发模式。
  2. 必须自备 .env。 仓库只有 .env.prod.example,没有现成 .env。
  3. Langfuse 使用自己的 Postgres / Redis,不要复用 Dify 的库。 两边容器名不同,互不影响。
  4. 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 timeoutDocker Hub 不可达下文「镜像加速」;换 daoCloud 等 mirror
web 容器 unhealthy.env 缺 NEXTAUTH_SECRET 等对照 .env.prod.example 逐项补
3000 能开但登录跳 localhostNEXTAUTH_URL 写错改为 http://192.168.110.207:3000
与 Dify 抢 5432误改 compose 复用 Dify PG禁止复用;Langfuse 自有 postgres 容器
health OK 但 UI 502worker 未起或 Redis 连不上docker compose logs langfuse-worker

三、和 Dify 共存时的端口约定

本次 207 上 Dify 已经占用:

Langfuse 本次使用:

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

bash
#!/usr/bin/env bash
set -euo pipefail

ls -la /Langfuse/langfuse
cd /Langfuse/langfuse
git remote -v
ls docker-compose.yml .env.prod.example

正常应能看到:

如果目录还不存在,再克隆:

完整脚本:scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_02.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Langfuse_部署教程/Langfuse部署_内联_02.sh

bash
#!/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

bash
#!/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

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

必须遵守的规则:

  1. ENCRYPTION_KEY 必须是 64 位 hex,用 openssl rand -hex 32 得到。
  2. LANGFUSE_INIT_USER_EMAIL 必须是合法邮箱。admin@local 会让 Web 直接报 Invalid environment variables,页面 500。
  3. POSTGRES_PASSWORD、DATABASE_URL 里的密码、以及所有 MinIO 的 SECRET_ACCESS_KEY 必须一致。
  4. NEXTAUTH_URL 必须是别人浏览器实际访问的地址。

本次 207 环境已经写好的登录信息:


六、第 3 步:拉镜像(国内环境重点)

官方 compose 会拉这些镜像:

text
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 会报:

text
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

bash
#!/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.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

bash
#!/usr/bin/env bash
set -euo pipefail

cd /Langfuse/langfuse
docker compose -p langfuse up -d --remove-orphans

-p langfuse 会让容器名变成:

这样不会和 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

bash
#!/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

bash
#!/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

bash
#!/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

bash
#!/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

期望:

text
{"status":"OK","version":"4.12.0"}
200

浏览器打开:

text
http://192.168.110.207:3000

登录:

如果登录提示没有这个用户,用同一组邮箱密码走 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 日志:

text
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

bash
#!/usr/bin/env bash
set -euo pipefail

cd /Langfuse/langfuse
docker compose -p langfuse up -d langfuse-web

9.4 空的 LANGFUSE_INIT_ORG_ID 会跳过自动建账号

现象:Web 已经 200,但日志写:

text
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

bash
#!/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

bash
#!/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

bash
#!/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

bash
#!/usr/bin/env bash
set -euo pipefail

cd /Langfuse/langfuse
docker compose -p langfuse up -d --force-recreate langfuse-web

数据卷在 Docker volume 里,名字大致为:

不要随意 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 未受影响

访问入口:

text
http://192.168.110.207:3000

登录:

text
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

python
#!/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

python
#!/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

python
#!/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

python
#!/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

python
#!/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())