Langfuse 仓库 Agent 协作指南与工程工作流解析:从 CLAUDE.md 看开源 LLM 平台的开发规范
【免费下载链接】langfuse🪢 Open source AI engineering platform: LLM evals, observability, metrics, prompt management, playground, datasets. Integrates with OpenTelemetry, LangChain, OpenAI SDK, LiteLLM, and more. 🍊YC W23项目地址: https://gitcode.com/GitHub_Trending/la/langfuse
Langfuse 是开源的 LLM 工程平台,用于开发、监控、评估和调试 AI 应用。本文以仓库根目录的 CLAUDE.md(与 .agents/AGENTS.md 内容一致,属于兼容性符号链接)为核心骨架,系统拆解 Langfuse 仓库的架构布局、核心开发命令、本地数据检查手段、种子数据 CLI、质量验证体系以及生成文件管理规范。读完本文,你将掌握如何在该仓库中高效地安装依赖、启动开发环境、构造可复现的测试数据、跑通全套质量检查,并理解这套"面向 Agent 与人类工程师双服务对象"的协作约定为何如此设计。
一、文档定位:CLAUDE.md 为何存在
根目录 CLAUDE.md 不是一份普通的 README,而是仓库的Agent 指南(Agent Guidelines),其开头即自述:Langfuse 是一个用于开发、监控、评估和调试 AI 应用的开源 LLM 工程平台。
值得特别注意的是它的文件组织机制(见 Shared Agent Setup 章节):
.agents/AGENTS.md是唯一的权威根指南(canonical root guide);- 根目录的
AGENTS.md是指向.agents/AGENTS.md的符号链接; - 根目录的
CLAUDE.md则是指向AGENTS.md的兼容性符号链接。
这一机制由 scripts/agents/sync-agent-shims.mjs 实现:该脚本会扫描仓库中所有包含AGENTS.md的目录,为每个目录生成同级的CLAUDE.md符号链接,让 Claude 在读取该目录内文件时能自动加载包级本地指南(package-local guidance)。同时脚本还会根据 .agents/config.json 生成.claude/settings.json、.mcp.json、.cursor/mcp.json、.vscode/mcp.json等各类工具链的配置文件。修改 skills 或 AGENTS.md 之后,必须运行:
pnpm run agents:sync # 同步符号链接与生成配置 pnpm run agents:check # 校验同步结果(含路径失效检查)文档明确规定:agent 指南只写进AGENTS.md,绝不写进CLAUDE.md(后者是被生成的符号链接),并且指南应放在"最窄的、拥有该主题的 AGENTS.md"中,以便只有在需要时才加载进上下文。这也是为什么 packages/shared/scripts/seeder/AGENTS.md、.agents/ARCHITECTURE_PRINCIPLES.md 等文件分散存在于树中的原因——它们各自只负责自己的领域。
二、先识别服务对象:外部贡献者与维护者
CLAUDE.md 的第一个章节 "Who You Are Working For" 强调一个核心理念:这个仓库服务两类人,他们拿到的是仓库的两半,工作前必须先判断清楚,且"绝不静默猜测"(never guess silently)。
判断依据不是面试,而是配置:读取~/.config/langfuse/me.md;如果该文件不存在,则由langfuse-onboardingskill(位于 .agents/skills/langfuse-onboarding/)第 1 步指明。在 Cursor Cloud 环境中,判断依据是运行所有者(cursor-cloudrun-info)加团队名册,而非gh api …permissions——因为 Cloud 的 GitHub token 是只读集成,即使维护者也会报告push: false;桌面端则仍使用gh api user再读取.permissions.push。
两类对象的差异直接决定协作方式:
- 外部贡献者(outside contributor):拿到的是代码与 CONTRIBUTING.md——如何构建、检查要求是什么、如何发起 Pull Request。不涉及内部追踪器、手册或工作节奏,因为他们无法打开这些内容。
- 维护者(maintainer):除了上述内容,还会得到一个承载组织上下文的助手。文档期望这个助手能:
- 回答"我今天该做什么"——依据来自追踪器而非记忆(
linear-work-rhythmskill); - 了解团队其他成员的工作动态,在设计某个界面之前提醒"同事上周刚重构过该流程";
- 拿到一个链接(追踪器工单、PR、Slack 链接、截图)就能接手推进,而不是反问该用哪个 skill;
- 主动、简短地提示组织层面逾期的事项(未写的更新、挂在
Merged却无文档决策的 issue、已悄然过期的项目目标日期); - 主动提出实现方案,而不是被动等待指令。
- 回答"我今天该做什么"——依据来自追踪器而非记忆(
此外,文档给出了一条重要的沟通准则:保持简短(Keep it short)——维护者通常正在处理任务,一段需要跳读的段落还不如两句能被读完的话。
三、仓库架构与依赖方向
CLAUDE.md 用一棵目录树概括了仓库的顶层结构,这是理解整个代码库的起点:
langfuse/ |- web/ # Next.js app (UI + tRPC + public REST) |- worker/ # Queue consumers and background processing |- packages/shared/ # Shared domain, DB, queue contracts, repositories |- ee/ # Enterprise package consumed by web |- generated/ # Generated API clients (do not hand-edit) |- fern/ # API definition sources `- scripts/ # Repo scripts其中generated/目录在当前仓库中未包含(属于生成物,见后文),其余目录均为实际存在。各包之间的依赖方向是单向且严格受控的:
web→@langfuse/shared、@langfuse/eeworker→@langfuse/shared@langfuse/ee→@langfuse/shared@langfuse/shared→ 不导入web、worker或ee中的任何内容
这意味着packages/shared是整个依赖图的最底层,所有跨包复用逻辑都必须下沉到它内部。文档还标注了几个"高信号"的共享入口点:
- 队列契约:队列 payload 的 schema 与队列名契约由 packages/shared/src/server/queues.ts 统一持有;
- 领域模型:
packages/shared/src/domain/下的observations.ts、traces.ts、scores.ts; - Postgres schema:packages/shared/prisma/schema.prisma;
- ClickHouse 迁移:模板位于 packages/shared/clickhouse/migrations/(含 90+ 个 SQL 迁移文件)。
更深层的设计意图记录在 .agents/ARCHITECTURE_PRINCIPLES.md("Underlying Architecture Principles"),它明确 Langfuse 的架构应面向大规模、探索式的可观测性,基于宽结构化事件数据:
- 将 observation 作为首要分析单元,trace 只是关联 observation 的相关句柄;
- 偏好"宽事件"(wide, richly attributed events),而不是碎片化的 metrics/logs/traces;
- 保留高基数上下文,让用户能按任意维度切片、分组、过滤和调试未知问题;
- 倾向不可变/追加式事件记录,避免读时去重带来的隐藏查询成本;
- 围绕列式访问模式设计存储与查询路径(窄字段选择、时间受限扫描、有效排序键、数据剪枝);
- API 契约需要具备规模感知:必要时强制时间窗口、暴露字段选择、使用游标分页,避免默认扫描全量历史。
这些原则为实际编写代码时的取舍(是否新增 join、是否新增指标、是否读取大字段)提供了决策框架。
四、核心开发命令:安装、开发与测试
CLAUDE.md 的 "Core Commands" 章节给出了一套以 pnpm + turbo 为底座的标准工作流,实测命令均与根目录 package.json 中的 scripts 一致:
| 用途 | 命令 |
|---|---|
| 安装依赖 | pnpm install |
| 全量开发(所有包) | pnpm run dev |
| 仅开发 web | pnpm run dev:web |
| 仅开发 worker | pnpm run dev:worker |
| 全量 Lint | pnpm run lint |
| 全量类型检查 | pnpm run typecheck/pnpm tc |
| 构建检查 | pnpm run build:check |
| 完整构建 | pnpm run build |
| 安装 Playwright Chromium | pnpm run playwright:install |
运行单个测试文件是日常开发中最常用的操作。vitest 按文件名参数过滤,且不同包有各自的过滤命令:
- web 服务端测试:
pnpm --filter web run test <file> - web 客户端测试:
pnpm --filter web run test-client <file> - worker 测试:
pnpm --filter worker run test <file> - shared 测试:
pnpm --filter @langfuse/shared run test <file>
这与仓库的测试命名约定呼应:web 下既有*.servertest.ts(如web/src/__e2e__/api.servertest.ts)也有*.clienttest.ts/*.clienttest.tsx(如web/src/utils/numbers.clienttest.ts),而 worker 使用*.test.ts(如worker/src/__tests__/evalService.test.ts)。
另外两条工具链约定值得注意:
- 不要通过
./node_modules/.bin/*调用 Node 安装的二进制,一律经由pnpm运行; - 在 shell 命令中始终为文件路径加引号,或对路径密集的命令使用
noglob,以避免 zsh 通配符展开在处理动态 Next.js 路由时出问题; - 不要新增或扩大 ESLint disable 注释或配置覆盖,除非用户明确批准具体规则与范围。
五、本地开发环境与数据检查
开发基础设施由 docker-compose.dev.yml 定义。它实际编排了五个服务,每个都带健康检查与可覆盖的端口/凭据变量:
| 服务 | 镜像 | 默认端口(绑定 HOST_IP,默认 127.0.0.1) | 默认凭据 |
|---|---|---|---|
| ClickHouse | clickhouse/clickhouse-server:25.12 | HTTP 8123 / Native 9000 | 用户clickhouse/ 密码clickhouse |
| Postgres | postgres:17 | 5432 | 用户/密码/库均为postgres |
| Redis | redis:7.2.4 | 6379 | 密码myredissecret(--requirepass,maxmemory-policy noeviction) |
| MinIO | chainguard/minio | API 9090 / 控制台 9091 | minio/miniosecret |
| floci(worker-tests profile) | floci/floci:1.5.17-compat | 4566 | — |
其中 ClickHouse 25.12 是 Langfuse v4 的最低版本(events 表依赖 >=25.x 的全文索引/enable_full_text_index),所有开发与 CI 部署模式都锁定同一版本。启动/停止命令在根 package.json 中也有对应脚本:pnpm run infra:dev:up(docker compose -f ./docker-compose.dev.yml up -d --wait)与pnpm run infra:dev:down。
CLAUDE.md 的 "Local Data Inspection" 章节给出了直接连接本地数据库做只读检查的三个命令(支持${VAR:-default}形式的变量覆盖),用于理解既有测试数据:
# Postgres PGPASSWORD="${POSTGRES_PASSWORD:-postgres}" psql -h "${HOST_IP:-127.0.0.1}" -p "${POSTGRES_HOST_PORT:-5432}" -U "${POSTGRES_USER:-postgres}" -d "${POSTGRES_DB:-postgres}" # ClickHouse clickhouse client --host "${HOST_IP:-127.0.0.1}" --port "${CLICKHOUSE_NATIVE_PORT:-9000}" --user "${CLICKHOUSE_USER:-clickhouse}" --password "${CLICKHOUSE_PASSWORD:-clickhouse}" --database default # Redis REDISCLI_AUTH="${REDIS_AUTH:-myredissecret}" redis-cli -h "${HOST_IP:-127.0.0.1}" -p "${REDIS_HOST_PORT:-6379}"如果连接失败,应检查 docker-compose.dev.yml 中的本地覆盖变量并确认服务在运行。文档同时强调:优先只读查询;需要构造前端测试状态时,继续使用种子 CLI 而非临时插入。
六、种子数据 CLI:用pnpm run seed构造可复现测试数据
这是仓库最有特色的工程实践之一。CLAUDE.md 规定:用种子 CLI 预填本地测试数据(pnpm run seed -- list列出场景,运行结果会打印 UI 深链接),绝不使用临时脚本或裸 ClickHouse 插入。
packages/shared/scripts/seeder/AGENTS.md 对这套 CLI 做了完整展开:入口是 cli.ts(对应 shared 包的seed:scenario脚本),配套 doctor.ts(栈健康检查并打印修复命令)、seed-postgres.ts、seed-clickhouse.ts 以及scenarios/目录(每个场景一个文件,共享rng.ts、payload.ts、event-mirror.ts、verify.ts)。
常用场景命令示例(均带--v4走 v4 events 路径):
pnpm run seed -- doctor # 检查开发栈,逐条打印失败项的修复命令 pnpm run seed -- list # 列出所有场景与参数(--json 输出机器可读结果) pnpm run seed -- trace-tree --observations 5000 --breadth 500 --v4 pnpm run seed -- deep-chain --v4 # 单链 1401 个顺序 generation(布局压力测试) pnpm run seed -- agent-graph --v4 # 图密集 trace:350 个 observation 产生约 1350 条节点连接 pnpm run seed -- timeline-shapes --v4 # 一打小 trace,每种时间线形态一个(重试退避、人工等待、扇出、慢工具、in-flight…) pnpm run seed -- many-traces --count 100000 --days 14 pnpm run seed -- outlier-traffic --days 90 # 带成本/延迟/token 离群点的昼夜流量 pnpm run seed -- session-shapes --shape media # 携带 @@@langfuseMedia:...@@@ 引用的消息(需要 MinIO)每次运行的最后一行 stdout 是一个 JSON 摘要,包含traceIds、sessionIds、counts、verified(ClickHouse 回读校验)和links(UI 深链接);--dry-run可只预测数量不写数据,--json抑制进度输出。
该指南还规定了种子场景的工程约束(属于可验证的实现事实):
- 场景名、flag 名、JSON 摘要键是公开契约,只能增量演进,不得重命名或删除;
- 场景必须确定性:随机性取自
Rng(由--seed播种),id 由--id-prefix派生,绝不调用Math.random; - 落入 ClickHouse ORDER BY 键的值(时间戳、v3 的
type、events 的start_time)不能来自顺序随机流或墙钟,须用utcDayStartMs()锚定时间、用无状态的jitter(seed, index, max)做逐行变化,否则无关 flag 改变随机流消耗位置会导致重跑时静默重复行; - 每个场景用 ClickHouse 回读验证写入并大声报错;
- 不允许客户数据、机密或需要模型提供商密钥的 fixture。
如果某个 bug 依赖特定数据形态,文档的建议是:先问"pnpm run seed能否在本地预填该形态",不能则考虑扩展种子场景,并说明为何 seed 无法表达。
七、质量验证体系:证据优先,检查必须真实执行
CLAUDE.md 的 "Verification" 章节确立了一套按风险分层、以证据收尾的验证文化:每项检查都要引用其摘要行(如 turbo 的Tasks: 8 successful, 8 total或 vitest 的Tests 12 passed (12)),说明跳过了哪些检查及原因,"绝不把未经验证的工作报告为完成,也绝不以未完成状态收尾"。
按变更范围的检查矩阵:
web/**:pnpm run lint+ 定向 web 测试;worker/**:pnpm run lint+ 定向 worker 测试;packages/shared/**(非 schema):pnpm run lint+ 一个定向 web 检查 + 一个定向 worker 检查;packages/shared/prisma/**或packages/shared/clickhouse/**:pnpm run lint+pnpm run db:generate+ 定向 web/worker 回归;- 公共 API 契约(web/src/pages/api/public/、web/src/features/public-api/types/ 或 fern/apis/):
pnpm run lint+ 定向服务端 API 测试 + Fern 更新/重新生成 +pnpm run openapi:check; - 跨包重构:
pnpm run lint+pnpm run typecheck+ 受影响包的定向测试。
几个关键的"陷阱提醒"(这些是其他项目很少写明的实操经验):
- 通过不代表执行过:
lint和typecheck是 turbo 缓存任务,且 worktree 共享同一份缓存,通过结果可能只是另一分支的重放。必须同时引用Cached:行;要强制真实执行,用pnpm exec turbo run lint --force(--no-cache只停止写入,不强制执行)。 - warning 即失败:
web、worker、packages/shared、ee四个包都以--max-warnings 0运行 eslint,一个 warning 就会让分支失败。 @langfuse/shared解析的是构建后的dist:根pnpm run typecheck会通过 turbo 的typecheck.dependsOn含^build自动先构建 shared;但pnpm --filter=web run typecheck不会。在 worktree 间切换分支后,必须执行pnpm --filter=shared run db:generate && pnpm --filter=shared run build,否则 typecheck 会基于上一个分支的源码报告。- knip 是必需检查:
pnpm exec knip是 CI 的pipeline.yml中必需的一步,但它没有对应的 package.json script,极易只在本地漏跑;web/**、packages/shared/**、worker/**下未使用的文件和导出都会导致其失败。 - 没有检查会真正加载页面:渲染变更只有在有人真正看过之后才算验证。只有当你确实不确定时才驱动浏览器(可能回流布局、携带状态的流程、无法预判的交互);小改动且信心高时,给出 URL 让开发者自己扫一眼更快。关键原则是"提供不等于推诿,静默跳过才是"——要明确说出你没检查什么。
- 客户端 bundle 健全性:CI 对每个生产 web 构建运行
pnpm run scan:client-bundle(见 scripts/scan-client-bundle.mjs),扫描被压缩器删除的绑定(SWC dropped-binding 类,会以运行时ReferenceError形态出现,dev 构建和类型检查都发现不了)以及泄漏进浏览器 chunk 的 Node 全局(裸require、process、Buffer等)。失败时,脚本头部的注释会说明规范修复方式。
测试策略:测试的价值在于钉住"没人注意就可能回归"的行为;如果唯一可断言的只是复述 diff 本身(间距值就是那个值、标签就是那个文本),这个测试就没有意义,应当跳过并一句话说明原因。bug 修复需要测试时,先写最小的失败测试并确认它在修复前的行为上确实失败,再改生产代码;新的测试只在覆盖不同 adapter、契约或执行路径时才添加,并优先扩展最接近的既有测试套件,而不是新建孤立测试。
八、生成文件管理与 API 契约
CLAUDE.md 的 "Generated Files" 章节划定了一条红线:不得手工编辑生成物或构建产物,包括:
generated/*web/.next/*web/.next-check/**/dist/*packages/shared/prisma/generated/*
公共 API 契约的变更必须更新 fern/apis/ 中的 Fern 源并重新生成输出,绝不手工编辑generated/**。这也解释了 fern/ 目录的定位——它是 API 定义的事实来源(source of truth),包含client、organizations、server三套 API 定义,其中server下就有 39 个 YAML 定义文件;生成物则落在web/public/generated/api/、web/public/generated/api-client/、web/public/generated/organizations-api/等处。相关校验命令是pnpm run openapi:check(导出后再执行 scripts/openapi/assert-generated-current.sh 断言生成结果与当前仓库一致)。
九、Cursor Cloud 与 PR 工作流
对于在 Cursor Cloud 中运行 agent 的场景,CLAUDE.md 提供了专门的说明("Cursor Cloud specific instructions"):
- 身份识别:以
cursor-cloudrun-info的owningUserName/owningUserEmail加名册为准;仓库 postinstall 与 Cloud 启动通常会先用LINEAR_API_KEY恢复~/.config/langfuse/me.md。忽略git config(cursoragent@cursor.com)与 Cloudgh的.permissions.push; - Linear 访问:优先用已授权的 MCP;否则用
LINEAR_API_KEY(或LINEAR_TOKEN/LINEAR_API_TOKEN)做真实读取。Cloud 中交互式mcp_auth不可用;都不行时,让用户把LINEAR_API_KEY作为 Cursor Cloud secret 添加后重新运行(本次运行看不到之后添加的 secret); - 启动整套源码构建栈:必须通过
bash scripts/agents/start-cursor-cloud.sh,不要在 3000/3030 端口再启动第二个 web 或 worker 进程;修改 web/worker 生产代码后,浏览器验收前需重跑该脚本。之所以不用 Compose 直接启动,是因为工作区.env含面向宿主机的localhost服务 URL,不能用于插值容器服务配置; - 预览部署:每个 PR 通过 GitHub Actions 自动构建一个一次性全栈预览
pr-<N>.preview.langfuse.com(无需自行拉起),用langfuse-previewsskill 读取/调试(如用kubectl读预览的 web/worker 错误日志);预览通常周一至周五 08:00–24:00(Europe/Berlin)运行。本地验证通过后应开"可评审"的 PR(非 draft),用合成数据测试预览部署,并给 PR 打上cursor标签; - 分支命名:使用 Linear 的 git 分支名(
lfe-XXXX-short-title),绝不创建cursor/分支; - 评审留言:PR 打开后在最后一个评论里写"评审者应质疑什么"(那些可疑的部分),而不是 changelog;用户可见的改动要把修复证据放进评论与 PR 正文。
上下文交接(Context Handover)也是文档强调的两个"易跳过但代价高昂"的时刻:改动既有功能前,先沿 commit、承载它们的 PR、head 分支名(工作项 id 所在处)回溯到工作项与先前的 agent 上下文(命令见.agents/skills/pr-stack-workflow/skill);请求评审或合并前,先把决策、反复、人的引导与陷阱留存在工作项上——"合并后就没有后来"。对应工具是linear-context-handover与linear-planningskills 以及规定 agent 可向追踪器写什么、如何标记的linear-agent-writes。
十、工程纪律小结
纵观整个 CLAUDE.md,可以提炼出 Langfuse 仓库几条贯穿始终的工程纪律:
- 一切以 AGENTS.md 为源,CLAUDE.md 只是生成的兼容层,指南按领域下沉到最窄的目录;
- 依赖方向单向受控,
packages/shared是唯一可被 web/worker/ee 共同依赖的底座; - 测试数据一律走 seed CLI,确定性、可回读校验、输出 UI 深链接;
- 验证以证据收尾,警惕 turbo 缓存导致的"假通过",warning 即失败;
- 生成物一律不手改,API 契约以 Fern 源为准;
- 区分服务对象,外部贡献者与维护者拿到的是同一仓库的两半,协作方式完全不同。
对于希望在 Langfuse 仓库中做开发或贡献的工程师与 Agent 开发者,这份文档既是操作手册(命令、端口、凭据、检查矩阵),也是协作协议(交接、评审、预览、分支命名)。按本文梳理的路径逐步操作,即可在本地跑起完整栈、造出可复现数据并交付符合仓库质量门槛的变更。
【免费下载链接】langfuse🪢 Open source AI engineering platform: LLM evals, observability, metrics, prompt management, playground, datasets. Integrates with OpenTelemetry, LangChain, OpenAI SDK, LiteLLM, and more. 🍊YC W23项目地址: https://gitcode.com/GitHub_Trending/la/langfuse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考