docker compose up -d 为何无Trace

执行 docker compose up -d 后,Langfuse 页面能打开,Agent 请求却没有 Trace,Evaluation 也没有评分,此时不要反复重启容器。排查顺序只有三段:先确认 Compose 服务及依赖的健康状态,再确认 Agent 进程能否通过正确的 Host 和密钥完成 SDK 上报,最后确认 Evaluation 是否绑定到正确的 Trace 或 observation ID,以及异步任务是否确实执行。

docker compose up -d 中的 -d 只表示容器在后台运行,不代表数据库迁移、依赖服务和 Web 服务都已就绪;健康状态仍然要通过 docker compose ps 和日志确认。 因此,“页面打不开”“没有 Trace”“没有评分”应当作为三个不同故障定位。

容器都启动了,为什么 Langfuse 还是不能用?

先锁定 Langfuse 主版本,再使用对应版本的官方 Compose 文件。v2、v3、v4 的镜像标签、环境变量和存储依赖不能混用。现有 v3 部署资料包含 PostgreSQL、ClickHouse、Redis、MinIO 等依赖;v2 的 Compose 结构不同,v4 也不能直接沿用旧配置。 升级或回滚前还要先看对应版本的迁移说明。

以官方 v3 release 为例,命令会解析出当前仓库中的 v3 release tag,并记录实际 commit;正式环境应选择已经审核过的具体 tag,同时核对 Web、Worker 镜像是否来自同一版本:

git clone https://github.com/langfuse/langfuse.git "$HOME/langfuse-src"
cd "$HOME/langfuse-src"

LANGFUSE_REF="${LANGFUSE_REF:-$(git tag --list 'v3.*' --sort=-v:refname | head -n 1)}"
test -n "$LANGFUSE_REF" || { echo "未找到 v3 release tag"; exit 1; }

git checkout "$LANGFUSE_REF"
git rev-parse HEAD | tee LANGFUSE_REVISION

mkdir -p "$HOME/langfuse"
cp docker-compose.yml "$HOME/langfuse/docker-compose.yml"
cd "$HOME/langfuse"

接下来按配置检查、启动、状态和日志四步确认:

docker compose config --quiet
docker compose up -d
docker compose ps
docker compose config --services
docker compose logs --tail=200 langfuse-web langfuse-worker

docker compose config --quiet 只能发现 Compose 语法和环境变量插值问题,不能证明数据库可连接、密钥正确或服务已经健康。最后一条日志命令中的服务名以 docker compose config --services 的实际输出为准。

环境变量应从所选 release 的官方配置说明逐项核对。v3 常见配置包括 DATABASE_URL、CLICKHOUSE_URL、CLICKHOUSE_MIGRATION_URL、REDIS_AUTH、LANGFUSE_S3_EVENT_UPLOAD_*、LANGFUSE_S3_MEDIA_UPLOAD_*、NEXTAUTH_SECRET、ENCRYPTION_KEY 和 SALT,但具体变量集合以当前版本为准。 正式密钥可以这样生成,不要继续使用博客示例里的固定密码、全零加密密钥或默认 MinIO 凭据:

openssl rand -hex 32

如果容器反复重启,优先看 Web、Worker 和依赖服务日志,确认 PostgreSQL 连接、ClickHouse migration、Redis 以及 S3/MinIO 地址是否与 Compose 网络一致。浏览器地址和 SDK Host 也要分开判断:浏览器访问 http://localhost:3000 只说明 Web 端口可达;如果 Agent 也在 Docker 里,localhost 指向的是 Agent 容器自身,此时应使用同一 Compose 网络中的服务名。远程部署则要核对 DNS、反向代理和 HTTPS,SDK Host 应当指向 Langfuse Web API,而不是 PostgreSQL 或 ClickHouse 地址。

首次使用还要完成 Web 初始化,创建账号和项目,并取得该项目的 Host、公钥与私钥。Langfuse 登录并不依赖一组固定的默认管理员密码。项目密钥不匹配时,Web 页面仍然可以打开,但 SDK 无法向正确项目写入数据。

页面能打开,为什么 Agent 还是没有 Trace?

先确认数据有没有真正送到 Langfuse。网页可达只说明 Web 服务正在响应,不能证明 Agent 进程完成了 SDK 初始化。下面的代码以 Python SDK 3.x 为例,v2、v4 应改用各自版本文档中的对应接口。

安装并记录实际 SDK 版本:

python -m pip install "langfuse>=3,<4"
python -m pip show langfuse

在 Agent 实际运行的机器或容器中设置凭据。下面的 read 命令只在当前 shell 中读取密钥,不会把密钥写进仓库:

read -r -p "LANGFUSE_PUBLIC_KEY=" LANGFUSE_PUBLIC_KEY
read -r -s -p "LANGFUSE_SECRET_KEY=" LANGFUSE_SECRET_KEY; echo
export LANGFUSE_PUBLIC_KEY LANGFUSE_SECRET_KEY
export LANGFUSE_HOST='http://localhost:3000'

Agent 与 Langfuse 同处一个 Compose 网络时,LANGFUSE_HOST 应改为该网络内可达的 Langfuse Web 服务地址;跨服务器或反向代理后,则使用实际运行环境能够解析和访问的地址。

先单独验证认证:

python - <<'PY'
from langfuse import Langfuse

client = Langfuse()
client.auth_check()
PY

认证检查仍然失败时,优先排查 Host、项目密钥和容器网络,不需要先动 Evaluation。

认证通过后,再用下面的最小脚本验证 Trace 层级。示例中的 demo_tool 和 demo_model 只用于确认埋点链路;接入真实模型时,应替换成项目里的实际调用,或者使用对应模型 SDK、框架集成。

from uuid import uuid4

from langfuse import Langfuse

client = Langfuse()
client.auth_check()

marker = f"langfuse-check-{uuid4().hex}"


def demo_tool(value):
    return {"tool": "demo", "value": value}


def demo_model(value):
    return {"model": "demo", "text": f"handled:{value}"}


with client.start_as_current_span(
    name="agent-run",
    input={"marker": marker},
) as root:
    with client.start_as_current_span(
        name="tool-call",
        input={"marker": marker},
    ) as tool_span:
        tool_span.update(output=demo_tool(marker))

    with client.start_as_current_generation(
        name="model-call",
        model="demo/model",
        input={"marker": marker},
    ) as generation:
        generation.update(output=demo_model(marker))
        model_observation_id = generation.id

    root.update(output={"marker": marker})

print(f"marker={marker}")
print(f"trace_id={root.trace_id}")
print(f"root_observation_id={root.id}")
print(f"model_observation_id={model_observation_id}")

client.flush()

这里的外层 Span 会形成一条 Trace,模型调用和工具调用是它的子记录。短脚本退出前调用 flush(),避免进程先结束、数据还没送出。

如果完全没有 Trace,依次检查 SDK 是否初始化、Host 是否从 Agent 运行环境可达、密钥是否属于当前项目,以及是否真的执行到了带埋点的代码。如果只有外层 Trace,没有模型调用或工具调用,问题通常在埋点范围:Agent 的“决策—行动—观察”循环会产生多次推理和工具调用,只记录最终回复必然丢掉中间链路。 工具位于线程、队列或独立进程时,还要检查父级上下文是否随调用传递,必要时使用 SDK 返回的父子 ID 显式关联。

LangChain、LlamaIndex、LangGraph 和 Dify 应分别核对对应集成与版本,不能只复制通用初始化代码。Dify 1.16.1 的 Trace 缺失属于版本专项问题,应针对该版本的兼容性和数据上报进行排查,不宜直接套用通用结论。

Trace 已经出现,为什么 Evaluation 还是空的?

先把运行指标和质量评测分开。延迟、成本、Token 用量属于运行指标;即使这些数据已经出现在仪表盘里,也不能说明输出获得了质量评分。 同样,Trace 的存在也不会自动产生一个 Evaluation 结果。人工打分、代码规则、模型评审和数据集实验属于不同执行路径,每一种都要明确评测对象、规则以及运行范围。

评分首先要绑定到正确的记录层级。给整次 Trace 打分时使用 Trace ID;给某次模型调用或工具调用打分时,还要提供对应的 observation ID。不要拿 Trace 名称、用户 ID、请求标记代替记录 ID,否则很容易出现“Trace 存在,但分数不知道写到了哪里”的情况。

在上面的脚本中拿到 root.trace_id 和 model_observation_id 后,可以写入一条测试评分:

client.create_score(
    name="smoke-quality",
    value=1,
    trace_id=root.trace_id,
    observation_id=model_observation_id,
    comment="single-request smoke test",
)
client.flush()

如果评测对象是整条 Trace,省略 observation_id。也可以通过官方 Scores API 写入,但字段语义应与 SDK 一致。

自动评估没有结果时,先确认是否真的创建并触发了评测任务,而不是只配置了评测定义。再检查评测对象、规则或评审模型、数据集范围以及目标记录 ID。若当前版本把评估作为异步任务执行,还要检查 Worker:

docker compose ps langfuse-worker
docker compose logs --tail=200 langfuse-worker

所选版本没有这个服务名时,以 docker compose config --services 为准。Worker 不健康、任务未触发、密钥权限不足或评测输入缺失,都可能表现为 Trace 正常但 Evaluation 为空。

怎样确认整条链路真的通了?

先发送一条带唯一标记的 Agent 请求,把标记同时放进输入和最终输出。用返回的 Trace ID 找到对应记录,确认外层 Trace、模型 Generation 和工具 Span 都存在。随后用该 Trace ID 或指定的 observation ID 写入测试评分,再回到同一记录确认评分能够读取。只有 Trace、模型与工具子记录、单条评分三者都能按 ID 正确关联,才算真正打通。

确认链路后,再把已经定位过的失败案例沉淀为评测数据,在 Prompt 或模型变更后重复运行同一组用例,逐步形成“观测—定位—评测—回归”的闭环。CSDN《从玩具到生产:基于Langfuse构建LLM Agent的评估与观测闭环》强调的正是这条闭环,而不是部署成功后只确认页面能够打开。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容