Langfuse Monorepo 实战Turborepo Task 配置参考与缓存优化指南【免费下载链接】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/langfuseTurborepo 是 Langfuse 单仓库monorepo的构建编排核心它依据任务依赖图并行执行任务并对产物做本地/远程缓存。本文以 Turborepo 官方任务配置参考为基础结合 Langfuse 仓库 turbo.json、根目录 package.json、web/package.json 与 worker/package.json 中的真实配置系统讲解tasks下每个配置项的含义、语法与实战取舍。读完本文你将能够读懂并独立设计一套「并行、可缓存、缓存键精确」的 Turborepo 任务管线并能在 Langfuse 这类同时包含 Next.js 前端、Node worker 与共享包的仓库中复现其构建、测试、类型检查全流程。Langfuse 的工作区由 pnpm-workspace.yaml 定义包含webNext.js 前端、worker后台任务进程、ee以及packages/**下的共享包langfuse/shared、repo/eslint-plugin、repo/in-app-agent-sandbox-runtime等根目录 package.json 的devDependencies声明了turbo: 2.10.5所有根级脚本都通过turbo run task委托给 Turborepo 编排。因此任务配置的每一个细节都会直接影响本地开发体验与 CI 构建时长。dependsOn控制任务执行顺序dependsOn声明任务之间的先后关系是 Turborepo 构建调度图task graph的基础。它支持三种语法语法含义^task先运行所有依赖包dependencies中的同名tasktask先运行同一包内的taskpkg#task先运行指定包的task其中^前缀至关重要——没有它你引用的就是同一个包内的任务{ tasks: { build: { dependsOn: [ ^build, // 先构建所有依赖包 codegen, // 再运行本包的 codegen shared#build // 最后或并行依赖关系内构建指定包 shared ] } } }Langfuse 根 turbo.json 中大量使用了这一机制build: { dependsOn: [db:generate, ^build] }—— 先为所有依赖包执行db:generatePrisma 客户端生成再按依赖图自底向上构建test: { dependsOn: [^test, db:generate] }—— 依赖包的测试先跑同时本包测试前先完成 Prisma 客户端生成typecheck: { dependsOn: [db:generate, ^build] }—— 类型检查依赖构建产物这与 TypeScript 的references/项目引用场景相吻合。值得注意的是^build只会在声明为依赖的包中触发构建。Langfuse 的 web/package.json 通过langfuse/shared: workspace:*声明了对共享包的工作区依赖build: tsc的worker也依赖langfuse/shared正是这些声明让^build能够自动推导出正确的构建顺序——无需任何手写prebuild脚本。并行任务的 Transit 节点模式对于lint、check-types这类可以并行执行、但又需要依赖包源码变化时缓存失效的任务直接使用dependsOn: [^lint]会强制串行执行拖慢整条流水线而dependsOn: []虽然能并行却会破坏缓存失效逻辑——依赖包源码改了本任务的缓存键却不变。解决之道是引入一个不匹配任何真实脚本的transit任务{ tasks: { transit: { dependsOn: [^transit] }, lint: { dependsOn: [transit] }, check-types: { dependsOn: [transit] } } }transit任务本身没有对应脚本不会真正执行任何命令但它建立了依赖包源码 → 本包的哈希关联使得lint与check-types既能并行、又能在依赖源码变化时正确失效缓存。Langfuse 的 turbo.json 在类似场景中采用了一种更显式的写法lint: { dependsOn: [repo/eslint-plugin#build, ^build] }即先构建共享的 ESLint 插件包再依赖依赖包的构建产物然后并行执行各包 lint。两种思路殊途同归要么用 transit 节点建立纯缓存依赖要么用真实的pkg#task依赖明确指定所需的前置产物切忌用空dependsOn破坏缓存语义。outputs声明可缓存产物outputs声明任务的产物 glob 模式。如果省略该字段任务产物将完全不参与缓存注意Langfuse 中cache仍为 true但没有 outputs 时缓存只回放日志、不恢复产物等于形同虚设{ tasks: { build: { outputs: [dist/**, build/**] } } }各框架的典型写法// Next.js outputs: [.next/**, !.next/cache/**] // Vite outputs: [dist/**] // TypeScript (tsc) outputs: [dist/**, *.tsbuildinfo] // 无文件产物lint、typecheck outputs: []用!前缀排除不需要缓存的子路径例如.next/cache/**属于 Next.js 内部缓存回放它会污染后续构建。Langfuse 的实践完全对应这些模板turbo.json 中build/build:test使用outputs: [dist/**, .next/**, !.next/cache/**]同时缓存 tsc 产物与 Next.js 构建产物lint与typecheck使用outputs: []——它们只向 stdout 输出、不产生文件缓存仅用于命中后跳过执行特例repo/in-app-agent-sandbox-runtime#build:docker-image使用outputs: [.local-image-built]用单个哨兵文件标记 Docker 镜像构建完成这是一种副作用型任务的产物标记技巧build:check通过NEXT_DIST_DIR.next-check把产物写到独立目录outputs相应配置为[.next-check/**, !.next-check/cache/**]避免与正式构建产物互相污染。inputs精确控制哈希输入inputs声明计算任务哈希cache key时纳入考量的文件。默认情况下包内所有被 git 跟踪的文件都会进入哈希声明inputs后则只对列出的文件做哈希{ tasks: { test: { inputs: [src/**, tests/**, vitest.config.ts] } } }特殊值值含义$TURBO_DEFAULT$先包含默认输入包内全部被跟踪文件再叠加后续的增/删规则$TURBO_ROOT$/path从仓库根目录引用文件{ tasks: { build: { inputs: [ $TURBO_DEFAULT$, !README.md, $TURBO_ROOT$/tsconfig.base.json ] } } }Langfuse 在 turbo.json 中把inputs用在最需要精确哈希的地方db:generate: { inputs: [packages/shared/prisma/schema.prisma], cache: false, dependsOn: [^db:generate] }Prisma 客户端生成只依赖schema.prisma这一个文件因此inputs精确指向它任何其他文件变动都不会触发无谓的重算。同理repo/in-app-agent-sandbox-runtime#build:docker-image的inputs限定为[Dockerfile, package.json, tsconfig.json]——只有这些文件会影响镜像内容。与global.inputs的交互globalConfiguration 未来特性当启用futureFlags.globalConfiguration后global.inputs中列出的文件会被前置合并到每个任务的inputs随后统一参与任务哈希计算。这与旧行为globalDependencies文件进入全局哈希任何任务都无法退出有本质区别旧行为globalDependencies文件计入全局哈希任务的inputs只能控制哪些包内文件被哈希任务无法用取反 glob 退出某个全局文件。新行为global.inputs文件合并进每个任务的inputs任务级inputs与全局inputs合并后统一哈希进任务哈希任务可以通过取反 glob 排除特定的全局文件。{ futureFlags: { globalConfiguration: true }, global: { inputs: [tsconfig.json, .env] }, tasks: { build: {}, lint: { inputs: [$TURBO_DEFAULT$, !$TURBO_ROOT$/.env] } } }上例中build哈希所有包内文件 tsconfig.json.envlint哈希所有包内文件 tsconfig.json但通过取反 glob!$TURBO_ROOT$/.env排除了.env。没有显式inputs的任务仍然哈希全部包内文件默认行为并叠加全局 inputs。Langfuse 当前 turbo.json 尚未启用该 future flag使用的是顶层globalDependencies: [.env]旧行为.env的任何变化都会使所有任务缓存全局失效。若未来迁移到globalConfiguration即可让lint等任务通过!$TURBO_ROOT$/.env退出这一粗粒度输入获得更细的缓存命中率。env环境变量参与哈希env声明会影响任务哈希的环境变量——这些变量的值发生变化会直接使缓存失效{ tasks: { build: { env: [ API_URL, NEXT_PUBLIC_*, // 通配符匹配 !DEBUG // 从哈希中排除 ] } } }Langfuse 在 turbo.json 中有两处非常有代表性的用法build: { dependsOn: [db:generate, ^build], env: [NEXT_IGNORE_BUILD_ERRORS], outputs: [dist/**, .next/**, !.next/cache/**] }配置注释解释了原因NEXT_IGNORE_BUILD_ERRORS会切换 Next.js 的类型检查开关带开关与不带开关的构建绝不能共享缓存条目——否则一次未做类型检查的构建会被缓存回放成已做类型检查的结果。这是环境变量决定产物正确性、必须入哈希的教科书级案例。任务级env之外Langfuse 还通过顶层globalEnv声明了所有任务共享的哈希环境变量globalEnv: [ NEXT_PUBLIC_LANGFUSE_BLOB_EXPORT_CUTOFF, NEXT_PUBLIC_LANGFUSE_BLOB_EXPORTER_CUTOFF, NEXT_PUBLIC_LANGFUSE_ANALYTICS_EXPORTER_CUTOFF, CLICKHOUSE_BIN ]这些变量的变化会令全部任务缓存失效。同时注意envMode: loose——与默认的strict只放行env/globalEnv中声明的变量不同loose 模式允许所有环境变量透传进任务运行时。Langfuse 的 web/worker 脚本大量使用dotenv -e ../.env --加载仓库根目录的.env文件见 web/package.json 的build、typecheck、dev等脚本loose 模式保证了这些运行时变量不会被 Turborepo 的严格过滤拦截。cache开关缓存cache启用或禁用任务的缓存默认true{ tasks: { dev: { cache: false }, deploy: { cache: false } } }应禁用缓存的场景开发服务器、部署命令、带副作用的任务。Langfuse turbo.json 中所有带副作用的任务都显式关闭了缓存dev、dev:worker、dev:web长驻开发服务器缓存无意义db:migrate、db:deploy、db:reset、db:push、db:seed、db:seed:examples直接改数据库绝不能回放db:generate配置注释明确说明Prisma generate 把客户端类型写进 node_modulesTurbo 缓存命中只回放日志、不会在全新 CI runner 上恢复这些副作用因此cache: falseworker#dev与repo/in-app-agent-sandbox-runtime#test:e2e同样关闭缓存。与之相对build、build:test、build:check、lint、typecheck、test均为cache: true最大化 CI 复用。persistent标记长驻任务persistent: true标记不会退出的长驻任务如 dev server默认false{ tasks: { dev: { cache: false, persistent: true } } }dev server 必须标记 persistent——否则依赖它的任务会永远等待其退出。Langfuse 的 turbo.json 为dev、dev:worker、dev:web、worker#dev均配置了persistent: true与cache: false的组合。注意根目录 package.json 中dev:worker与dev:web通过--filter只启动单个包而根dev则同时拉起 web 与 worker 的持久进程。interactive允许 stdin 输入interactive: true允许任务接收标准输入默认false适用于需要交互式输入的命令如login{ tasks: { login: { cache: false, interactive: true } } }outputLogs控制日志回放策略outputLogs决定任务日志的展示时机可选值full完整输出、hash-only仅哈希、new-only仅缓存未命中时输出、errors-only仅错误、none{ tasks: { build: { outputLogs: new-only // 缓存命中时不刷屏 } } }Langfuse 将outputLogs: errors-only用于绝大多数高频任务——build、build:test、build:check、lint、typecheck、db:generate、repo/eslint-plugin#build等。对于拥有数百个任务的 monorepo这一策略能显著降低 CI 日志噪音命中缓存的包只回放哈希与结论出错时才暴露完整日志。可配合futureFlags.errorsOnlyShowHash在启动/完成时同时显示任务哈希便于排查缓存命中情况。with并发运行伴生任务with让任务与指定任务并发运行适合需要运行时依赖的长驻任务{ tasks: { dev: { with: [api#dev], persistent: true, cache: false } } }与dependsOn串行等待不同with是同时启动dev server 需要其他服务先跑起来时用它。Langfuse 中 web 的dev脚本依赖langfuse/shared构建产物与数据库就绪其dependsOn: [db:generate]承担了前置生成职责而with适合的场景是例如同时拉起 worker 与 web 的持久进程。interruptible允许 watch 模式重启interruptible: true允许turbo watch在文件变化时重启任务默认false{ tasks: { dev: { persistent: true, interruptible: true, cache: false } } }适用于不能自动检测依赖变化的 dev server。interruptible与persistent配合watch 模式下持久任务在源文件变化时被中断并重启而普通持久任务只会保持运行。description任务的人类可读说明description仅作文档用途不影响执行与缓存{ tasks: { build: { description: Compiles the application for production deployment } } }passThroughEnv运行时可见但不参与哈希passThroughEnv声明的变量在任务运行时可用但不进入缓存哈希{ tasks: { build: { passThroughEnv: [AWS_SECRET_KEY, GITHUB_TOKEN] } } }警告这些变量的变化不会导致缓存失效。若某个变量会影响产物内容应使用env让它进入哈希只有纯运行时凭据密钥、token才适合放进passThroughEnv避免把敏感值写进缓存键的同时保持缓存稳定。Langfuse 根 package.json 中release脚本通过dotenv -e ../.env -- release-it加载发布凭据这类场景正是passThroughEnv或顶层globalPassThroughEnv的适用对象。extendsPackage Configuration 的任务继承extends只在包级turbo.jsonPackage Configuration中生效控制任务配置的继承方式// packages/ui/turbo.json { extends: [//], tasks: { lint: { extends: false // 从本包排除该任务 } } }值行为true默认从根 turbo.json 继承false从包中排除该任务或完全不继承地重新定义Langfuse 目前只有一个根级 turbo.json尚未使用包级 Package Configuration但根配置中的repo/eslint-plugin#build、langfuse/shared#db:generate、worker#dev等pkg#task覆盖表明当某个包的配置与根级差异明显时更整洁的做法是把这些覆盖下沉到各包的turbo.json中配extends: [//]并通过$TURBO_EXTENDS$在继承的数组dependsOn、env、inputs、outputs、passThroughEnv、with上追加而非替换根配置。Langfuse 之所以仍在根配置中使用pkg#task是因为其每个包的特殊配置点都较少且集中适合统一在一个文件里审阅。完整实战Langfuse 根任务配置全景综合以上所有配置项Langfuse turbo.json 的完整任务表如下节选核心任务任务dependsOn关键配置设计意图builddb:generate,^buildenv: [NEXT_IGNORE_BUILD_ERRORS]outputs: [dist/**, .next/**, !.next/cache/**]outputLogs: errors-only先出 Prisma 客户端再自底向上构建缓存键区分类型检查开关build:checkdb:generate,^build产物输出到.next-check/**独立目录避免与正式构建产物互踩dev/dev:worker/dev:webdb:generatecache: falsepersistent: true长驻开发服务器db:generate^db:generatecache: falseinputs: [packages/shared/prisma/schema.prisma]只对 schema 变化敏感副作用写 node_modules 不缓存db:migrate等数据库命令—cache: false改库操作禁止回放lintrepo/eslint-plugin#build,^buildoutputs: []outputLogs: errors-only先构建共享 ESLint 插件再并行 lint无文件产物typecheckdb:generate,^buildoutputs: []outputLogs: errors-only依赖构建产物做全量类型检查test^test,db:generatecache: true依赖包测试先行 Prisma 客户端前置worker#dev—cache: falsepersistent: trueworker 专用长驻任务配套地根 package.json 的脚本保持纯粹的委托语义build: turbo run build、typecheck: turbo run typecheck、lint: turbo run lint、test: turbo run test、dev:worker: turbo run dev --filterworker任务逻辑全部下沉到各包 package.jsonweb用next build/next devworker用tsc/vitest run这正是 Turborepo 官方推荐的包内脚本 根配置注册 根脚本仅委托三层结构。常见误区速查^前缀混淆^build是依赖包的 build无前缀build是本包的 buildLangfuse 的test同时使用^test与db:generate演示了二者的区别。漏配outputs任务产生了文件却不声明outputs缓存命中后产物不会恢复CI 上表现为看似缓存了却仍然全量构建。dependsOn: []破坏缓存失效并行任务若需要依赖感知的缓存应使用 transit 节点或显式pkg#task依赖而非清空依赖。环境变量不进哈希影响产物正确性的变量如 Langfuse 的NEXT_IGNORE_BUILD_ERRORS必须进env否则会回放错误产物。.env不进inputsTurbo 本身不加载.env但它需要感知变化——Langfuse 用顶层globalDependencies: [.env]让所有任务对.env变化失效未来可迁移到global.inputs实现按任务排除。根脚本绕过 Turbo根 package.json 必须委托turbo run不要用cd apps/web next build ...手写串行链否则会失去并行与缓存能力。turbo build简写在代码中的误用写入 package.json 与 CI 时应使用turbo run buildturbo task简写仅用于人工终端的一次性操作。延伸阅读任务配置的配套全局项globalEnv、globalDependencies、envMode、cacheDir、futureFlags.globalConfiguration等.agents/skills/turborepo/references/configuration/global-options.mdturbo.json 整体结构与 Package Configuration 说明.agents/skills/turborepo/references/configuration/RULE.md环境变量在 strict/loose 模式下的行为与框架自动推断.agents/skills/turborepo/references/environment/RULE.mdLangfuse 根任务配置全文turbo.json根脚本委托与工作区脚本定义package.json、web/package.json、worker/package.json工作区包划分pnpm-workspace.yaml【免费下载链接】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),仅供参考