1. 项目概述为什么我们需要一个“生产就绪”的AI框架如果你在过去一年里尝试过将任何AI功能集成到你的产品中我敢打赌你和我一样经历过一段“甜蜜的痛苦”。最初用几行代码调用OpenAI的API看着GPT吐出像模像样的文本那种兴奋感是真实的。但当你试图把这个“玩具”变成一个每天处理成千上万次请求、不能出错、还要能清晰计费和调试的生产级功能时痛苦就开始了。你会发现你的代码库里散落着各种promptTemplate字符串测试起来像在掷骰子账单突然飙升却不知道是哪段提示词惹的祸更别提处理那些恼人的速率限制和网络错误了。这正是GrowthX团队在为他们自己和数十家客户构建AI驱动产品时日复一日面对的困境。无论是内部的产品团队打造AI密集型功能还是前沿部署团队为Lovable、Airbyte这样的公司定制增长型AI智能体大家撞上的都是同一堵墙。于是他们决定不再重复造轮子而是把内部打磨了许久的解决方案——Output开源了出来。这不仅仅是一个库它是一个完整的TypeScript框架目标直指一个让你能像开发普通后端服务一样稳健、可观测、可维护地开发AI工作流和智能体。简单来说Output试图解决几个核心痛点提示词管理混乱、非确定性代码难以测试、成本黑洞、缺乏健壮的执行引擎。它把生产AI应用所需的一切——从提示词版本控制、基于评估的开发、全链路追踪与成本核算到具备自动重试和持久化的工作流引擎——都打包进了一个框架里。最吸引人的是它深度集成了对Claude Code的支持这意味着你的编码智能体能更好地理解你整个项目的上下文。下面我们就来拆解一下这个框架到底是怎么做的以及你该如何上手。2. 核心设计哲学从“胶水代码”到“一等公民”在深入细节之前理解Output的设计哲学至关重要。它不是在现有的LLM调用库比如LangChain或LlamaIndex之上又加了一层抽象。相反它从底层重新思考了“AI原生应用”的开发范式。传统的模式是你写业务逻辑然后在需要的地方“粘贴”一些LLM调用。Output则主张AI工作流本身就是业务逻辑的核心它应该享有与数据库查询、API调用同等级别的工程化支持。2.1 核心理念一提示词即代码这是Output最鲜明的特点之一。它彻底摒弃了在代码中拼接字符串、或在外部仪表板中管理提示词的做法。在Output项目中提示词是存储在.prompt文件中的。这些文件就在你的代码仓库里和你的TypeScript文件放在一起。为什么这么做版本控制你的提示词修改历史会清晰地记录在Git中。你可以看到某次产品迭代具体改了哪句提示词方便回滚和审计。代码审查在Pull Request中团队成员可以像审查业务逻辑一样审查提示词的改动讨论其措辞和潜在影响。消除上下文切换开发者不再需要离开IDE去某个网页控制台调整提示词。所有开发活动都在同一个环境中完成。Claude Code友好当你的编码助手如Claude Code分析项目时它能直接读取这些.prompt文件完全理解你希望AI执行的任务上下文从而生成更精准的配套代码。一个典型的.prompt文件可能长这样假设文件名为summarize.prompt// 这是一个系统指令定义了AI的角色和任务边界 system: You are a concise technical editor. Your task is to summarize complex API documentation into a clear, three-bullet-point list for developers. // 这是用户输入的变量插值位置 user: Summarize the following documentation: {documentation} // 你可以定义输出格式的约束 constraints: Output must be in valid markdown. Each bullet point must be under 15 words.在你的业务代码中你可以像导入模块一样导入并使用这个提示词import summarizePrompt from ./prompts/summarize.prompt; const result await ai.run(summarizePrompt, { variables: { documentation: someLongAPIText } });这种模式将提示词从“魔法字符串”提升为了可维护、可组合的代码资产。2.2 核心理念二评估驱动开发LLM的输出是非确定性的这给测试带来了巨大挑战。传统的单元测试断言输出完全等于某个字符串在这里基本失效。Output引入了“评估驱动开发”的概念核心是使用LLM-as-JudgeLLM作为裁判。具体如何运作你不再写expect(output).toBe(“expected string”)而是编写“评估器”。评估器本身也是一个小的AI工作流它的任务是评判你的主工作流的输出质量。例如你有一个客服邮件分类工作流你可以编写一个评估器来检查“分类结果是否准确”、“回复语气是否专业”。Output框架允许你针对一个提示词的修改批量运行数十个测试场景每个场景都会由对应的评估器给出一个“置信度分数”。这样你在合并代码前就能量化地知道新的提示词在“处理用户投诉”场景下得分是升了还是降了在“处理技术咨询”时是否依然稳健// 伪代码示例定义一个评估器 const isClassificationAccurate defineEvaluator({ name: “classification-accuracy”, prompt: Given the user query “{query}” and the AIs classification “{classification}”, is this classification correct? Respond only with YES or NO., judge: claude-3-5-sonnet, // 指定一个LLM作为裁判 score: (response) response “YES” ? 1.0 : 0.0 // 将裁判输出转化为0-1的分数 }); // 在测试套件中运行 const testSuite await output.testSuite({ workflow: myEmailClassifierWorkflow, evaluators: [isClassificationAccurate], testCases: [ { input: {query: “我的订单没收到”}, expectedClassification: “物流问题” }, { input: {query: “如何退款”}, expectedClassification: “售后问题” }, // ... 更多场景 ] }); const report await testSuite.run(); console.log(平均准确率${report.averageScore});这从根本上改变了AI功能的开发流程使其从“试了再说”变成“数据驱动决策”。3. 架构深度解析Output如何实现“生产就绪”一个框架宣称“生产就绪”很容易但Output通过其架构设计实实在在地解决了工程团队最头疼的几个问题。我们来逐一拆解它的核心模块。3.1 全链路追踪与成本核算告别“黑盒”与“账单惊吓”在分布式系统中我们有OpenTelemetry来追踪请求。在AI工作流中Output内置了类似的机制。每一次LLM调用、每一个工作流步骤、甚至每一次工具调用如数据库查询都会被自动记录并生成一条追踪链路。关键实现细节数据本地化追踪数据默认写入本地磁盘开发环境或你指定的S3桶生产环境。Output刻意避免了绑定任何第三方的可观测性平台。这意味着1) 你的数据完全在你掌控之下符合数据合规要求2) 没有额外的订阅费用3) 没有因供应商服务中断而导致你的调试能力瘫痪的风险。成本关联每一条追踪记录都清晰地关联了本次调用的模型、输入/输出token数以及根据官方定价计算出的成本。你可以在追踪视图中直接看到“这个用户请求在‘生成草稿’这一步调用了Claude-3-Sonnet花费了$0.0032”。当月底账单异常时你可以快速定位到是哪个工作流、甚至哪类提示词导致了成本激增。可视化与查询Output CLI工具提供了本地UI可以可视化这些追踪链路像看火焰图一样分析工作流的性能瓶颈和成本分布。实操心得在我们自己的项目中启用全链路追踪后我们第一时间发现了一个“隐藏”的成本漏洞一个用于“优化标题”的辅助步骤因为提示词不够严谨有时会生成过长的文本并送入下一个LLM步骤导致连锁的成本浪费。没有清晰的追踪这个问题就像海底的暗礁只有撞上收到账单时才会发现。3.2 持久化执行引擎基于Temporal的可靠性基石AI工作流往往是长时间运行、包含多个步骤、且可能失败的。网络抖动、LLM提供商限流、第三方API超时都是家常便饭。Output没有自己从头实现重试、状态持久化、超时控制等复杂逻辑而是选择集成Temporal.io作为其工作流引擎。Temporal带来了什么Temporal是一个成熟的开源工作流编排平台被Uber、Coinbase等公司用于处理关键业务流。它的核心能力是“持久化函数”保证你的工作流函数一定会执行完成即使进程崩溃、服务器重启。自动重试与回退你可以在Output中为任何步骤定义重试策略如指数退避。如果某次LLM调用失败Temporal会自动重试而你写的业务代码async/await风格完全不需要处理这些错误恢复逻辑。工作流历史与回放Temporal完整记录了工作流每一次状态变化。这意味着你可以随时“回放”任何一个已执行的工作流精确地看到它每一步的输入输出这对于调试非确定性的AI行为是无价之宝。异步与定时任务你可以轻松地创建延时任务“24小时后给用户发送跟进邮件”或周期性任务引擎会可靠地调度执行。在Output中你定义一个工作流就像写一个普通的异步函数import { workflow } from outputai/sdk; export const userOnboardingWorkflow workflow( async ({ userEmail, signupData }) { // 1. 调用LLM分析用户画像 const persona await ai.run(analyzePersonaPrompt, { variables: { data: signupData } }); // 2. 根据画像从数据库获取个性化内容推荐这是一个可能失败的操作 const recommendations await db.getPersonalizedContent(persona.traits); // 3. 生成个性化的欢迎邮件 const welcomeEmail await ai.run(generateWelcomeEmailPrompt, { variables: { persona, recommendations } }); // 4. 发送邮件另一个可能失败的外部调用 await emailService.send(userEmail, welcomeEmail); return { success: true, personaId: persona.id }; }, { // 配置工作流行为失败时最多重试3次每次间隔增加 retryPolicy: { maximumAttempts: 3, backoffCoefficient: 2.0 }, // 设置整个工作流的超时时间 executionTimeout: ‘5 minutes’, } );你写的只是业务逻辑async/awaitOutput和Temporal在背后处理了持久化、队列、分发、重试和监控所有脏活累活。3.3 加密凭据管理安全与便捷的平衡AI应用通常需要接入多个外部服务的API密钥OpenAI, Anthropic, 向量数据库等。管理这些密钥是个安全难题。Output提供了一个内置的、基于环境的加密凭据管理方案。AES-256加密所有凭据在静态存储时都是加密的。环境与工作流作用域你可以定义“开发”、“预发布”、“生产”等环境并为每个环境配置不同的密钥。更进一步你可以限制某个工作流只能访问特定的凭据比如一个只处理内部数据的工作流不需要发送邮件的SMTP密钥。无需外部密码库对于大多数应用来说引入一个像HashiCorp Vault这样的完整密码管理基础设施过于沉重。Output提供的方案在安全性和易用性之间取得了很好的平衡让你在项目早期就能实践安全的密钥管理而无需订阅额外的服务。在配置中你可以这样安全地引用凭据// 在你的 output.config.ts 中 export default defineConfig({ environments: { development: { credentials: { // 这些值来自加密的本地存储或环境变量不会硬编码在配置文件中 ‘OPENAI_API_KEY’: process.env.OUTPUT_CREDENTIAL_OPENAI_KEY, ‘ANTHROPIC_API_KEY’: process.env.OUTPUT_CREDENTIAL_ANTHROPIC_KEY, } } }, workflows: { myWorkflow: { // 声明此工作流需要的凭据 requiredCredentials: [‘OPENAI_API_KEY’] } } });4. 从零开始快速上手与核心工作流构建理论说了这么多现在让我们动手从初始化一个项目到构建第一个完整的AI工作流。4.1 环境初始化与项目结构首先确保你的系统已安装Node.js (18版本)。然后一行命令创建项目npx outputai/cli init my-first-output-project cd my-first-output-project这个命令会创建一个标准的Output项目骨架。让我们看看生成了什么my-first-output-project/ ├── .output/ # Output运行时数据追踪日志、缓存等 ├── workflows/ # 你的工作流定义文件 │ └── example.workflow.ts ├── prompts/ # 你的 .prompt 文件存放处 │ └── welcome.prompt ├── evaluators/ # 评估器定义 ├── tests/ # 测试文件包含评估场景 ├── output.config.ts # 项目主配置文件 ├── package.json └── tsconfig.jsonoutput.config.ts是你的控制中心。在这里你可以配置AI模型提供商、追踪存储后端、Temporal服务器地址等。4.2 构建一个真实的智能内容审核工作流假设我们要构建一个内容审核助手它需要1) 分析用户提交的文本2) 判断其是否违规及违规类别3) 如果可能违规生成一份详细的审核报告。第一步编写提示词在prompts/目录下创建analyze-content.promptsystem: You are a professional content moderation assistant. Analyze the given user-generated content for any policy violations. Be objective and focus on the actual text. user: Content to analyze: {content} constraints: - Respond with a valid JSON object. - The JSON must have the following structure: {isViolation: boolean, confidence: number between 0 and 1, categories: string[], reasoning: string} - “categories” can include: “hate_speech”, “harassment”, “spam”, “misinformation”, “explicit”, “none”. - “reasoning” should be a concise explanation in Chinese.再创建一个generate-report.prompt用于在发现违规时生成报告。第二步定义工作流在workflows/目录下创建content-moderation.workflow.tsimport { workflow, log } from ‘outputai/sdk’; import analyzePrompt from ‘../prompts/analyze-content.prompt’; import reportPrompt from ‘../prompts/generate-report.prompt’; // 定义一个类型安全的输入输出接口 interface ModerationInput { contentId: string; text: string; userId: string; } interface ModerationOutput { contentId: string; finalDecision: ‘approved’ | ‘rejected’ | ‘needs_review’; report?: string; analysis: { isViolation: boolean; confidence: number; categories: string[]; }; } export const contentModerationWorkflow workflow( async ({ contentId, text, userId }: ModerationInput): PromiseModerationOutput { // 步骤1调用LLM进行内容分析 log.info(Starting moderation for content ${contentId} from user ${userId}); const analysis await ai.run(analyzePrompt, { variables: { content: text }, model: ‘claude-3-haiku-20240307’, // 指定一个快速、便宜的分析模型 responseFormat: { type: ‘json_object’ } // 要求返回JSON }); // 步骤2基于分析结果做逻辑判断 const result: ModerationOutput[‘analysis’] JSON.parse(analysis); let finalDecision: ModerationOutput[‘finalDecision’] ‘approved’; let report: string | undefined; if (result.isViolation result.confidence 0.8) { // 高置信度违规直接拒绝并生成报告 finalDecision ‘rejected’; report await ai.run(reportPrompt, { variables: { content: text, analysis: result }, model: ‘claude-3-5-sonnet-20241022’ // 生成报告可以用更强一点的模型 }); } else if (result.isViolation result.confidence 0.4) { // 中等置信度标记为需要人工复核 finalDecision ‘needs_review’; report 低置信度违规请人工复核。AI分析${result.reasoning}; } // 否则置信度低或未违规直接通过 // 步骤3模拟将结果记录到数据库 log.info(Decision for ${contentId}: ${finalDecision}); // await db.auditLog.insert({ contentId, decision: finalDecision, ... }); return { contentId, finalDecision, report, analysis: result }; }, { // 配置这个工作流最多执行30秒失败重试2次 executionTimeout: ‘30 seconds’, retryPolicy: { maximumAttempts: 2 }, } );第三步本地测试与运行Output CLI提供了开发服务器和测试工具。启动开发环境npx output dev。这会启动本地Temporal工作流引擎和追踪UI。触发工作流你可以写一个简单的脚本或者使用Output提供的REST端点或CLI命令来触发工作流。npx output workflow run contentModerationWorkflow --data ‘{“contentId”: “123”, “text”: “这是一段测试文本”, “userId”: “user_456”}’查看追踪打开http://localhost:8233Temporal Web UI或Output提供的本地UI查看工作流的执行详情、每一步的输入输出、耗时和成本。4.3 为工作流编写评估测试现在我们需要确保这个审核工作流是可靠的。在tests/目录下创建moderation-evaluators.ts和测试场景。首先定义评估器// tests/moderation-evaluators.ts import { defineEvaluator } from ‘outputai/sdk’; // 评估器1判断决策是否合理 export const decisionIsAppropriate defineEvaluator({ name: ‘moderation-decision-appropriate’, prompt: 你是一个内容审核专家。给定以下信息 - 用户内容{content} - AI分析结果是否违规{isViolation}置信度{confidence}分类{categories} - 系统最终决策{finalDecision} (可能值approved通过, rejected拒绝, needs_review需复核) 问题基于AI分析结果这个最终决策是否合理只回答 YES 或 NO。 , judge: ‘gpt-4o-mini’, // 使用一个可靠的模型作为裁判 score: (response) response.trim().toUpperCase() ‘YES’ ? 1.0 : 0.0 }); // 评估器2检查报告是否包含必要信息如果生成了报告 export const reportIsDetailed defineEvaluator({ name: ‘moderation-report-detailed’, prompt: 如果系统生成了审核报告“{report}”请判断该报告是否包含了具体的违规原因引用和上下文分析只回答 YES 或 NO。如果未生成报告回答 YES。, judge: ‘gpt-4o-mini’, score: (response) response.trim().toUpperCase() ‘YES’ ? 1.0 : 0.0 });然后创建测试套件文件moderation.test.ts// tests/moderation.test.ts import { testSuite } from ‘outputai/sdk’; import { contentModerationWorkflow } from ‘../workflows/content-moderation.workflow’; import { decisionIsAppropriate, reportIsDetailed } from ‘./moderation-evaluators’; const suite testSuite({ workflow: contentModerationWorkflow, evaluators: [decisionIsAppropriate, reportIsDetailed], testCases: [ { name: ‘明显违规应被拒绝’, input: { contentId: ‘test-1’, text: ‘包含非常恶劣的辱骂言论…’, userId: ‘test-user’ }, // 我们“期望”AI分析出违规且决策是拒绝 expectedIntermediate: { analysis: { isViolation: true } }, expectedOutput: { finalDecision: ‘rejected’ } }, { name: ‘模糊内容需复核’, input: { contentId: ‘test-2’, text: ‘一些可能带有隐晦攻击性的政治隐喻…’, userId: ‘test-user’ }, expectedOutput: { finalDecision: ‘needs_review’ } }, { name: ‘无害内容应通过’, input: { contentId: ‘test-3’, text: ‘今天天气真好大家周末愉快’, userId: ‘test-user’ }, expectedOutput: { finalDecision: ‘approved’ } }, { name: ‘空内容或极短内容’, input: { contentId: ‘test-4’, text: ‘.’, userId: ‘test-user’ }, // 这里我们不预设具体决策但评估器会判断决策是否合理 } ] }); // 运行测试套件并打印报告 const report await suite.run(); console.log(‘测试报告’); for (const testCase of report.results) { console.log([${testCase.passed ? ‘✅’ : ‘❌’}] ${testCase.name}); console.log( 决策合理性得分${testCase.scores[‘moderation-decision-appropriate’]}); if (testCase.output.report) { console.log( 报告详细性得分${testCase.scores[‘moderation-report-detailed’]}); } } console.log(\n综合平均分${report.averageScore.toFixed(2)});运行测试npx output test ./tests/moderation.test.ts。你会得到一份详细的评估报告量化地告诉你工作流在不同场景下的表现。当你修改analyze-content.prompt中的措辞时重新运行测试就能看到分数如何变化从而科学地迭代你的提示词。5. 进阶实战与避坑指南当你熟悉了基础工作流后可能会遇到更复杂的需求。这里分享一些进阶模式和实践中踩过的坑。5.1 复杂工作流编排并行、竞争与人工干预现实中的AI工作流很少是简单的直线。Output结合Temporal的能力可以轻松编排复杂逻辑。模式一并行执行与聚合假设你需要从一段文本中同时提取“情感”、“实体”和“关键词”可以使用Promise.all实现并行调用提升性能。const [sentiment, entities, keywords] await Promise.all([ ai.run(sentimentAnalysisPrompt, { variables: { text } }), ai.run(entityExtractionPrompt, { variables: { text } }), ai.run(keywordExtractionPrompt, { variables: { text } }), ]); // 然后聚合结果…模式二竞争模式例如你有一个查询希望同时用Claude和GPT-4处理谁先返回就用谁的结果或取质量更高的。import { race } from ‘outputai/sdk’; // 假设Output提供了类似工具或使用Promise.race const claudeCall ai.run(queryPrompt, { model: ‘claude-3-sonnet’ }); const gptCall ai.run(queryPrompt, { model: ‘gpt-4o’ }); try { // 设置一个总超时并竞争第一个完成的结果 const winner await Promise.race([ claudeCall.then(result ({ provider: ‘claude’, result })), gptCall.then(result ({ provider: ‘gpt’, result })), new Promise((_, reject) setTimeout(() reject(new Error(‘Timeout’)), 5000)) ]); log.info(Used ${winner.provider}’s response); return winner.result; } catch (error) { // 处理超时或错误 }模式三等待人工输入有些工作流需要“人在回路”。例如上述内容审核中标记为needs_review的项目需要人工做出最终裁决后工作流才能继续。// 在Temporal/Output中你可以使用“信号”或“查询”来实现 import { workflow, defineSignal, defineQuery } from ‘outputai/sdk’; const humanDecisionSignal defineSignal‘approve’ | ‘reject’(‘human-decision’); const getStatusQuery defineQuery(‘get-status’); export const workflowWithHuman workflow(async ({ contentId }) { // … AI初步分析… if (needsHumanReview) { // 1. 发送通知给人工审核队列例如写入数据库触发通知 await notifyReviewQueue(contentId, aiAnalysis); // 2. 暂停工作流等待人工信号 const humanChoice await workflow.awaitSignal(humanDecisionSignal); // 或者工作流可以休眠但通过“查询”暴露当前状态供外部读取 // workflow.setHandler(getStatusQuery, () ({ status: ‘awaiting_human’ })); if (humanChoice ‘approve’) { // 继续执行批准后的逻辑 } else { // 执行拒绝后的逻辑 } } });外部系统如一个管理后台在人工审核后可以通过Temporal客户端向这个具体的工作流实例发送“信号”使其继续执行。5.2 成本控制与优化策略AI应用的成本可能失控。Output的追踪功能是监控的基础但你还需要主动策略。分级使用模型就像我们在内容审核工作流中做的初步分析用快速便宜的模型如Claude Haiku, GPT-4o-mini只有需要高质量生成或复杂推理时才调用昂贵模型如Claude Sonnet/Opus, GPT-4。设置用量预算与告警虽然Output本身不直接提供预算告警但你可以将它的成本追踪数据导出到你的监控系统如Prometheus Grafana或定期扫描追踪日志对特定工作流或用户的累计成本设置阈值告警。缓存LLM响应对于相对静态或重复的查询如将常见问题转化为标准回答可以考虑引入缓存层。Output的架构允许你在调用ai.run前后插入中间件逻辑实现简单的基于内容哈希的缓存。精细化追踪标签在运行工作流时可以附加自定义标签如userId、tenantId、workflowType。这样在分析成本时你可以轻松地按用户、租户或业务类型进行聚合快速定位“大客户”。5.3 常见陷阱与排查技巧提示词文件路径错误最常见的错误是.prompt文件导入路径不对。Output的提示词编译器在构建时处理这些文件。确保你的tsconfig.json包含了正确的路径映射或者使用相对路径导入。如果遇到“Cannot find module”错误首先检查文件是否存在以及导入语句是否正确。工作流函数必须是幂等的这是Temporal的核心要求。你的工作流函数可能会被重播在故障恢复时这意味着函数内的代码可能会被执行多次。因此任何副作用操作如发送邮件、插入数据库都必须放在“活动”中或者确保操作本身是幂等的。Output的ai.run在默认情况下是幂等的吗不一定因为LLM调用可能有非确定性。但Temporal的重试机制会保证整个工作流步骤的原子性。最佳实践是将非幂等的操作如发送邮件包装成一个单独的、可重试但最终会成功的“活动”。追踪数据量过大如果你运行高频工作流本地磁盘的追踪日志可能会快速增长。在生产环境务必在output.config.ts中配置S3或其他对象存储作为追踪后端并设置生命周期规则自动清理旧数据。Claude Code上下文理解不足虽然Output为Claude Code优化了项目结构但如果你的提示词逻辑非常复杂Claude可能仍无法完全理解。一个技巧是在关键的.prompt文件开头用注释清晰地说明这个提示词的目的、输入输出格式、以及它会在哪个工作流中被如何使用。给AI更多的上下文它能更好地辅助你。环境配置混淆在output.config.ts中定义了多个环境如development,staging,production但在运行或部署时忘记指定环境变量OUTPUT_ENV可能导致使用了错误的API密钥或连接到了错误的Temporal集群。在Docker或部署脚本中一定要显式设置这个环境变量。6. 部署与生产化考量将Output项目部署到生产环境涉及几个关键环节。基础设施依赖Temporal集群这是Output的大脑。对于生产环境你需要部署一个高可用的Temporal集群。可以选择Temporal Cloud托管服务或者使用官方Docker镜像在Kubernetes上自托管。存储后端追踪数据需要存储。开发时用本地磁盘生产环境务必切换到S3、GCS或兼容的对象存储。运行工作流的工作节点你需要运行Output工作流代码的服务器或容器。这些节点会从Temporal集群领取任务并执行。你可以根据负载轻松地横向扩展这些工作节点。部署流程构建运行npm run build或output build将你的TypeScript工作流代码编译成JavaScript。打包将构建产物dist/目录、package.json和必要的配置文件打包进Docker镜像。配置通过环境变量注入所有敏感信息API密钥、数据库连接串、Temporal服务地址等。Output的加密凭据功能可以帮你管理这些密钥。运行工作节点启动你的容器并确保它执行了工作流“worker”的注册代码。通常这在一个入口文件中// worker.ts import { Worker } from ‘outputai/sdk’; import * as workflows from ‘./workflows’; // 导入所有工作流 const worker await Worker.create({ workflows: Object.values(workflows), // 注册工作流 taskQueue: ‘my-app-task-queue’, // 任务队列名 // … 其他连接配置 }); await worker.run();触发工作流通过Output提供的HTTP服务器、CLI或直接使用Temporal客户端SDK从你的主应用服务器触发工作流执行。监控与运维健康检查为你的工作节点暴露健康检查端点。日志聚合确保工作节点和Temporal集群的日志被收集到像ELK或Datadog这样的中央日志系统。指标收集利用Temporal和Output暴露的指标如工作流执行次数、耗时、失败率在Grafana等仪表板上进行监控。错误处理除了工作流内的重试还需要有全局的告警机制监控工作流失败率。Temporal的Web UI是调查失败工作流的强大工具。Output框架通过将AI工作流的复杂性封装进一个熟悉的、工程化友好的范式里确实大幅降低了构建可靠AI应用的门槛。它可能不是所有场景下的银弹但对于那些厌倦了在脆弱的“胶水代码”和昂贵的第三方平台之间挣扎的团队来说它提供了一个极具吸引力的、自主可控的折中方案。开源和Apache 2.0协议也让团队可以无顾虑地将其引入自己的技术栈并根据需要进行定制。
Output框架:构建生产级AI应用的TypeScript全栈解决方案
1. 项目概述为什么我们需要一个“生产就绪”的AI框架如果你在过去一年里尝试过将任何AI功能集成到你的产品中我敢打赌你和我一样经历过一段“甜蜜的痛苦”。最初用几行代码调用OpenAI的API看着GPT吐出像模像样的文本那种兴奋感是真实的。但当你试图把这个“玩具”变成一个每天处理成千上万次请求、不能出错、还要能清晰计费和调试的生产级功能时痛苦就开始了。你会发现你的代码库里散落着各种promptTemplate字符串测试起来像在掷骰子账单突然飙升却不知道是哪段提示词惹的祸更别提处理那些恼人的速率限制和网络错误了。这正是GrowthX团队在为他们自己和数十家客户构建AI驱动产品时日复一日面对的困境。无论是内部的产品团队打造AI密集型功能还是前沿部署团队为Lovable、Airbyte这样的公司定制增长型AI智能体大家撞上的都是同一堵墙。于是他们决定不再重复造轮子而是把内部打磨了许久的解决方案——Output开源了出来。这不仅仅是一个库它是一个完整的TypeScript框架目标直指一个让你能像开发普通后端服务一样稳健、可观测、可维护地开发AI工作流和智能体。简单来说Output试图解决几个核心痛点提示词管理混乱、非确定性代码难以测试、成本黑洞、缺乏健壮的执行引擎。它把生产AI应用所需的一切——从提示词版本控制、基于评估的开发、全链路追踪与成本核算到具备自动重试和持久化的工作流引擎——都打包进了一个框架里。最吸引人的是它深度集成了对Claude Code的支持这意味着你的编码智能体能更好地理解你整个项目的上下文。下面我们就来拆解一下这个框架到底是怎么做的以及你该如何上手。2. 核心设计哲学从“胶水代码”到“一等公民”在深入细节之前理解Output的设计哲学至关重要。它不是在现有的LLM调用库比如LangChain或LlamaIndex之上又加了一层抽象。相反它从底层重新思考了“AI原生应用”的开发范式。传统的模式是你写业务逻辑然后在需要的地方“粘贴”一些LLM调用。Output则主张AI工作流本身就是业务逻辑的核心它应该享有与数据库查询、API调用同等级别的工程化支持。2.1 核心理念一提示词即代码这是Output最鲜明的特点之一。它彻底摒弃了在代码中拼接字符串、或在外部仪表板中管理提示词的做法。在Output项目中提示词是存储在.prompt文件中的。这些文件就在你的代码仓库里和你的TypeScript文件放在一起。为什么这么做版本控制你的提示词修改历史会清晰地记录在Git中。你可以看到某次产品迭代具体改了哪句提示词方便回滚和审计。代码审查在Pull Request中团队成员可以像审查业务逻辑一样审查提示词的改动讨论其措辞和潜在影响。消除上下文切换开发者不再需要离开IDE去某个网页控制台调整提示词。所有开发活动都在同一个环境中完成。Claude Code友好当你的编码助手如Claude Code分析项目时它能直接读取这些.prompt文件完全理解你希望AI执行的任务上下文从而生成更精准的配套代码。一个典型的.prompt文件可能长这样假设文件名为summarize.prompt// 这是一个系统指令定义了AI的角色和任务边界 system: You are a concise technical editor. Your task is to summarize complex API documentation into a clear, three-bullet-point list for developers. // 这是用户输入的变量插值位置 user: Summarize the following documentation: {documentation} // 你可以定义输出格式的约束 constraints: Output must be in valid markdown. Each bullet point must be under 15 words.在你的业务代码中你可以像导入模块一样导入并使用这个提示词import summarizePrompt from ./prompts/summarize.prompt; const result await ai.run(summarizePrompt, { variables: { documentation: someLongAPIText } });这种模式将提示词从“魔法字符串”提升为了可维护、可组合的代码资产。2.2 核心理念二评估驱动开发LLM的输出是非确定性的这给测试带来了巨大挑战。传统的单元测试断言输出完全等于某个字符串在这里基本失效。Output引入了“评估驱动开发”的概念核心是使用LLM-as-JudgeLLM作为裁判。具体如何运作你不再写expect(output).toBe(“expected string”)而是编写“评估器”。评估器本身也是一个小的AI工作流它的任务是评判你的主工作流的输出质量。例如你有一个客服邮件分类工作流你可以编写一个评估器来检查“分类结果是否准确”、“回复语气是否专业”。Output框架允许你针对一个提示词的修改批量运行数十个测试场景每个场景都会由对应的评估器给出一个“置信度分数”。这样你在合并代码前就能量化地知道新的提示词在“处理用户投诉”场景下得分是升了还是降了在“处理技术咨询”时是否依然稳健// 伪代码示例定义一个评估器 const isClassificationAccurate defineEvaluator({ name: “classification-accuracy”, prompt: Given the user query “{query}” and the AIs classification “{classification}”, is this classification correct? Respond only with YES or NO., judge: claude-3-5-sonnet, // 指定一个LLM作为裁判 score: (response) response “YES” ? 1.0 : 0.0 // 将裁判输出转化为0-1的分数 }); // 在测试套件中运行 const testSuite await output.testSuite({ workflow: myEmailClassifierWorkflow, evaluators: [isClassificationAccurate], testCases: [ { input: {query: “我的订单没收到”}, expectedClassification: “物流问题” }, { input: {query: “如何退款”}, expectedClassification: “售后问题” }, // ... 更多场景 ] }); const report await testSuite.run(); console.log(平均准确率${report.averageScore});这从根本上改变了AI功能的开发流程使其从“试了再说”变成“数据驱动决策”。3. 架构深度解析Output如何实现“生产就绪”一个框架宣称“生产就绪”很容易但Output通过其架构设计实实在在地解决了工程团队最头疼的几个问题。我们来逐一拆解它的核心模块。3.1 全链路追踪与成本核算告别“黑盒”与“账单惊吓”在分布式系统中我们有OpenTelemetry来追踪请求。在AI工作流中Output内置了类似的机制。每一次LLM调用、每一个工作流步骤、甚至每一次工具调用如数据库查询都会被自动记录并生成一条追踪链路。关键实现细节数据本地化追踪数据默认写入本地磁盘开发环境或你指定的S3桶生产环境。Output刻意避免了绑定任何第三方的可观测性平台。这意味着1) 你的数据完全在你掌控之下符合数据合规要求2) 没有额外的订阅费用3) 没有因供应商服务中断而导致你的调试能力瘫痪的风险。成本关联每一条追踪记录都清晰地关联了本次调用的模型、输入/输出token数以及根据官方定价计算出的成本。你可以在追踪视图中直接看到“这个用户请求在‘生成草稿’这一步调用了Claude-3-Sonnet花费了$0.0032”。当月底账单异常时你可以快速定位到是哪个工作流、甚至哪类提示词导致了成本激增。可视化与查询Output CLI工具提供了本地UI可以可视化这些追踪链路像看火焰图一样分析工作流的性能瓶颈和成本分布。实操心得在我们自己的项目中启用全链路追踪后我们第一时间发现了一个“隐藏”的成本漏洞一个用于“优化标题”的辅助步骤因为提示词不够严谨有时会生成过长的文本并送入下一个LLM步骤导致连锁的成本浪费。没有清晰的追踪这个问题就像海底的暗礁只有撞上收到账单时才会发现。3.2 持久化执行引擎基于Temporal的可靠性基石AI工作流往往是长时间运行、包含多个步骤、且可能失败的。网络抖动、LLM提供商限流、第三方API超时都是家常便饭。Output没有自己从头实现重试、状态持久化、超时控制等复杂逻辑而是选择集成Temporal.io作为其工作流引擎。Temporal带来了什么Temporal是一个成熟的开源工作流编排平台被Uber、Coinbase等公司用于处理关键业务流。它的核心能力是“持久化函数”保证你的工作流函数一定会执行完成即使进程崩溃、服务器重启。自动重试与回退你可以在Output中为任何步骤定义重试策略如指数退避。如果某次LLM调用失败Temporal会自动重试而你写的业务代码async/await风格完全不需要处理这些错误恢复逻辑。工作流历史与回放Temporal完整记录了工作流每一次状态变化。这意味着你可以随时“回放”任何一个已执行的工作流精确地看到它每一步的输入输出这对于调试非确定性的AI行为是无价之宝。异步与定时任务你可以轻松地创建延时任务“24小时后给用户发送跟进邮件”或周期性任务引擎会可靠地调度执行。在Output中你定义一个工作流就像写一个普通的异步函数import { workflow } from outputai/sdk; export const userOnboardingWorkflow workflow( async ({ userEmail, signupData }) { // 1. 调用LLM分析用户画像 const persona await ai.run(analyzePersonaPrompt, { variables: { data: signupData } }); // 2. 根据画像从数据库获取个性化内容推荐这是一个可能失败的操作 const recommendations await db.getPersonalizedContent(persona.traits); // 3. 生成个性化的欢迎邮件 const welcomeEmail await ai.run(generateWelcomeEmailPrompt, { variables: { persona, recommendations } }); // 4. 发送邮件另一个可能失败的外部调用 await emailService.send(userEmail, welcomeEmail); return { success: true, personaId: persona.id }; }, { // 配置工作流行为失败时最多重试3次每次间隔增加 retryPolicy: { maximumAttempts: 3, backoffCoefficient: 2.0 }, // 设置整个工作流的超时时间 executionTimeout: ‘5 minutes’, } );你写的只是业务逻辑async/awaitOutput和Temporal在背后处理了持久化、队列、分发、重试和监控所有脏活累活。3.3 加密凭据管理安全与便捷的平衡AI应用通常需要接入多个外部服务的API密钥OpenAI, Anthropic, 向量数据库等。管理这些密钥是个安全难题。Output提供了一个内置的、基于环境的加密凭据管理方案。AES-256加密所有凭据在静态存储时都是加密的。环境与工作流作用域你可以定义“开发”、“预发布”、“生产”等环境并为每个环境配置不同的密钥。更进一步你可以限制某个工作流只能访问特定的凭据比如一个只处理内部数据的工作流不需要发送邮件的SMTP密钥。无需外部密码库对于大多数应用来说引入一个像HashiCorp Vault这样的完整密码管理基础设施过于沉重。Output提供的方案在安全性和易用性之间取得了很好的平衡让你在项目早期就能实践安全的密钥管理而无需订阅额外的服务。在配置中你可以这样安全地引用凭据// 在你的 output.config.ts 中 export default defineConfig({ environments: { development: { credentials: { // 这些值来自加密的本地存储或环境变量不会硬编码在配置文件中 ‘OPENAI_API_KEY’: process.env.OUTPUT_CREDENTIAL_OPENAI_KEY, ‘ANTHROPIC_API_KEY’: process.env.OUTPUT_CREDENTIAL_ANTHROPIC_KEY, } } }, workflows: { myWorkflow: { // 声明此工作流需要的凭据 requiredCredentials: [‘OPENAI_API_KEY’] } } });4. 从零开始快速上手与核心工作流构建理论说了这么多现在让我们动手从初始化一个项目到构建第一个完整的AI工作流。4.1 环境初始化与项目结构首先确保你的系统已安装Node.js (18版本)。然后一行命令创建项目npx outputai/cli init my-first-output-project cd my-first-output-project这个命令会创建一个标准的Output项目骨架。让我们看看生成了什么my-first-output-project/ ├── .output/ # Output运行时数据追踪日志、缓存等 ├── workflows/ # 你的工作流定义文件 │ └── example.workflow.ts ├── prompts/ # 你的 .prompt 文件存放处 │ └── welcome.prompt ├── evaluators/ # 评估器定义 ├── tests/ # 测试文件包含评估场景 ├── output.config.ts # 项目主配置文件 ├── package.json └── tsconfig.jsonoutput.config.ts是你的控制中心。在这里你可以配置AI模型提供商、追踪存储后端、Temporal服务器地址等。4.2 构建一个真实的智能内容审核工作流假设我们要构建一个内容审核助手它需要1) 分析用户提交的文本2) 判断其是否违规及违规类别3) 如果可能违规生成一份详细的审核报告。第一步编写提示词在prompts/目录下创建analyze-content.promptsystem: You are a professional content moderation assistant. Analyze the given user-generated content for any policy violations. Be objective and focus on the actual text. user: Content to analyze: {content} constraints: - Respond with a valid JSON object. - The JSON must have the following structure: {isViolation: boolean, confidence: number between 0 and 1, categories: string[], reasoning: string} - “categories” can include: “hate_speech”, “harassment”, “spam”, “misinformation”, “explicit”, “none”. - “reasoning” should be a concise explanation in Chinese.再创建一个generate-report.prompt用于在发现违规时生成报告。第二步定义工作流在workflows/目录下创建content-moderation.workflow.tsimport { workflow, log } from ‘outputai/sdk’; import analyzePrompt from ‘../prompts/analyze-content.prompt’; import reportPrompt from ‘../prompts/generate-report.prompt’; // 定义一个类型安全的输入输出接口 interface ModerationInput { contentId: string; text: string; userId: string; } interface ModerationOutput { contentId: string; finalDecision: ‘approved’ | ‘rejected’ | ‘needs_review’; report?: string; analysis: { isViolation: boolean; confidence: number; categories: string[]; }; } export const contentModerationWorkflow workflow( async ({ contentId, text, userId }: ModerationInput): PromiseModerationOutput { // 步骤1调用LLM进行内容分析 log.info(Starting moderation for content ${contentId} from user ${userId}); const analysis await ai.run(analyzePrompt, { variables: { content: text }, model: ‘claude-3-haiku-20240307’, // 指定一个快速、便宜的分析模型 responseFormat: { type: ‘json_object’ } // 要求返回JSON }); // 步骤2基于分析结果做逻辑判断 const result: ModerationOutput[‘analysis’] JSON.parse(analysis); let finalDecision: ModerationOutput[‘finalDecision’] ‘approved’; let report: string | undefined; if (result.isViolation result.confidence 0.8) { // 高置信度违规直接拒绝并生成报告 finalDecision ‘rejected’; report await ai.run(reportPrompt, { variables: { content: text, analysis: result }, model: ‘claude-3-5-sonnet-20241022’ // 生成报告可以用更强一点的模型 }); } else if (result.isViolation result.confidence 0.4) { // 中等置信度标记为需要人工复核 finalDecision ‘needs_review’; report 低置信度违规请人工复核。AI分析${result.reasoning}; } // 否则置信度低或未违规直接通过 // 步骤3模拟将结果记录到数据库 log.info(Decision for ${contentId}: ${finalDecision}); // await db.auditLog.insert({ contentId, decision: finalDecision, ... }); return { contentId, finalDecision, report, analysis: result }; }, { // 配置这个工作流最多执行30秒失败重试2次 executionTimeout: ‘30 seconds’, retryPolicy: { maximumAttempts: 2 }, } );第三步本地测试与运行Output CLI提供了开发服务器和测试工具。启动开发环境npx output dev。这会启动本地Temporal工作流引擎和追踪UI。触发工作流你可以写一个简单的脚本或者使用Output提供的REST端点或CLI命令来触发工作流。npx output workflow run contentModerationWorkflow --data ‘{“contentId”: “123”, “text”: “这是一段测试文本”, “userId”: “user_456”}’查看追踪打开http://localhost:8233Temporal Web UI或Output提供的本地UI查看工作流的执行详情、每一步的输入输出、耗时和成本。4.3 为工作流编写评估测试现在我们需要确保这个审核工作流是可靠的。在tests/目录下创建moderation-evaluators.ts和测试场景。首先定义评估器// tests/moderation-evaluators.ts import { defineEvaluator } from ‘outputai/sdk’; // 评估器1判断决策是否合理 export const decisionIsAppropriate defineEvaluator({ name: ‘moderation-decision-appropriate’, prompt: 你是一个内容审核专家。给定以下信息 - 用户内容{content} - AI分析结果是否违规{isViolation}置信度{confidence}分类{categories} - 系统最终决策{finalDecision} (可能值approved通过, rejected拒绝, needs_review需复核) 问题基于AI分析结果这个最终决策是否合理只回答 YES 或 NO。 , judge: ‘gpt-4o-mini’, // 使用一个可靠的模型作为裁判 score: (response) response.trim().toUpperCase() ‘YES’ ? 1.0 : 0.0 }); // 评估器2检查报告是否包含必要信息如果生成了报告 export const reportIsDetailed defineEvaluator({ name: ‘moderation-report-detailed’, prompt: 如果系统生成了审核报告“{report}”请判断该报告是否包含了具体的违规原因引用和上下文分析只回答 YES 或 NO。如果未生成报告回答 YES。, judge: ‘gpt-4o-mini’, score: (response) response.trim().toUpperCase() ‘YES’ ? 1.0 : 0.0 });然后创建测试套件文件moderation.test.ts// tests/moderation.test.ts import { testSuite } from ‘outputai/sdk’; import { contentModerationWorkflow } from ‘../workflows/content-moderation.workflow’; import { decisionIsAppropriate, reportIsDetailed } from ‘./moderation-evaluators’; const suite testSuite({ workflow: contentModerationWorkflow, evaluators: [decisionIsAppropriate, reportIsDetailed], testCases: [ { name: ‘明显违规应被拒绝’, input: { contentId: ‘test-1’, text: ‘包含非常恶劣的辱骂言论…’, userId: ‘test-user’ }, // 我们“期望”AI分析出违规且决策是拒绝 expectedIntermediate: { analysis: { isViolation: true } }, expectedOutput: { finalDecision: ‘rejected’ } }, { name: ‘模糊内容需复核’, input: { contentId: ‘test-2’, text: ‘一些可能带有隐晦攻击性的政治隐喻…’, userId: ‘test-user’ }, expectedOutput: { finalDecision: ‘needs_review’ } }, { name: ‘无害内容应通过’, input: { contentId: ‘test-3’, text: ‘今天天气真好大家周末愉快’, userId: ‘test-user’ }, expectedOutput: { finalDecision: ‘approved’ } }, { name: ‘空内容或极短内容’, input: { contentId: ‘test-4’, text: ‘.’, userId: ‘test-user’ }, // 这里我们不预设具体决策但评估器会判断决策是否合理 } ] }); // 运行测试套件并打印报告 const report await suite.run(); console.log(‘测试报告’); for (const testCase of report.results) { console.log([${testCase.passed ? ‘✅’ : ‘❌’}] ${testCase.name}); console.log( 决策合理性得分${testCase.scores[‘moderation-decision-appropriate’]}); if (testCase.output.report) { console.log( 报告详细性得分${testCase.scores[‘moderation-report-detailed’]}); } } console.log(\n综合平均分${report.averageScore.toFixed(2)});运行测试npx output test ./tests/moderation.test.ts。你会得到一份详细的评估报告量化地告诉你工作流在不同场景下的表现。当你修改analyze-content.prompt中的措辞时重新运行测试就能看到分数如何变化从而科学地迭代你的提示词。5. 进阶实战与避坑指南当你熟悉了基础工作流后可能会遇到更复杂的需求。这里分享一些进阶模式和实践中踩过的坑。5.1 复杂工作流编排并行、竞争与人工干预现实中的AI工作流很少是简单的直线。Output结合Temporal的能力可以轻松编排复杂逻辑。模式一并行执行与聚合假设你需要从一段文本中同时提取“情感”、“实体”和“关键词”可以使用Promise.all实现并行调用提升性能。const [sentiment, entities, keywords] await Promise.all([ ai.run(sentimentAnalysisPrompt, { variables: { text } }), ai.run(entityExtractionPrompt, { variables: { text } }), ai.run(keywordExtractionPrompt, { variables: { text } }), ]); // 然后聚合结果…模式二竞争模式例如你有一个查询希望同时用Claude和GPT-4处理谁先返回就用谁的结果或取质量更高的。import { race } from ‘outputai/sdk’; // 假设Output提供了类似工具或使用Promise.race const claudeCall ai.run(queryPrompt, { model: ‘claude-3-sonnet’ }); const gptCall ai.run(queryPrompt, { model: ‘gpt-4o’ }); try { // 设置一个总超时并竞争第一个完成的结果 const winner await Promise.race([ claudeCall.then(result ({ provider: ‘claude’, result })), gptCall.then(result ({ provider: ‘gpt’, result })), new Promise((_, reject) setTimeout(() reject(new Error(‘Timeout’)), 5000)) ]); log.info(Used ${winner.provider}’s response); return winner.result; } catch (error) { // 处理超时或错误 }模式三等待人工输入有些工作流需要“人在回路”。例如上述内容审核中标记为needs_review的项目需要人工做出最终裁决后工作流才能继续。// 在Temporal/Output中你可以使用“信号”或“查询”来实现 import { workflow, defineSignal, defineQuery } from ‘outputai/sdk’; const humanDecisionSignal defineSignal‘approve’ | ‘reject’(‘human-decision’); const getStatusQuery defineQuery(‘get-status’); export const workflowWithHuman workflow(async ({ contentId }) { // … AI初步分析… if (needsHumanReview) { // 1. 发送通知给人工审核队列例如写入数据库触发通知 await notifyReviewQueue(contentId, aiAnalysis); // 2. 暂停工作流等待人工信号 const humanChoice await workflow.awaitSignal(humanDecisionSignal); // 或者工作流可以休眠但通过“查询”暴露当前状态供外部读取 // workflow.setHandler(getStatusQuery, () ({ status: ‘awaiting_human’ })); if (humanChoice ‘approve’) { // 继续执行批准后的逻辑 } else { // 执行拒绝后的逻辑 } } });外部系统如一个管理后台在人工审核后可以通过Temporal客户端向这个具体的工作流实例发送“信号”使其继续执行。5.2 成本控制与优化策略AI应用的成本可能失控。Output的追踪功能是监控的基础但你还需要主动策略。分级使用模型就像我们在内容审核工作流中做的初步分析用快速便宜的模型如Claude Haiku, GPT-4o-mini只有需要高质量生成或复杂推理时才调用昂贵模型如Claude Sonnet/Opus, GPT-4。设置用量预算与告警虽然Output本身不直接提供预算告警但你可以将它的成本追踪数据导出到你的监控系统如Prometheus Grafana或定期扫描追踪日志对特定工作流或用户的累计成本设置阈值告警。缓存LLM响应对于相对静态或重复的查询如将常见问题转化为标准回答可以考虑引入缓存层。Output的架构允许你在调用ai.run前后插入中间件逻辑实现简单的基于内容哈希的缓存。精细化追踪标签在运行工作流时可以附加自定义标签如userId、tenantId、workflowType。这样在分析成本时你可以轻松地按用户、租户或业务类型进行聚合快速定位“大客户”。5.3 常见陷阱与排查技巧提示词文件路径错误最常见的错误是.prompt文件导入路径不对。Output的提示词编译器在构建时处理这些文件。确保你的tsconfig.json包含了正确的路径映射或者使用相对路径导入。如果遇到“Cannot find module”错误首先检查文件是否存在以及导入语句是否正确。工作流函数必须是幂等的这是Temporal的核心要求。你的工作流函数可能会被重播在故障恢复时这意味着函数内的代码可能会被执行多次。因此任何副作用操作如发送邮件、插入数据库都必须放在“活动”中或者确保操作本身是幂等的。Output的ai.run在默认情况下是幂等的吗不一定因为LLM调用可能有非确定性。但Temporal的重试机制会保证整个工作流步骤的原子性。最佳实践是将非幂等的操作如发送邮件包装成一个单独的、可重试但最终会成功的“活动”。追踪数据量过大如果你运行高频工作流本地磁盘的追踪日志可能会快速增长。在生产环境务必在output.config.ts中配置S3或其他对象存储作为追踪后端并设置生命周期规则自动清理旧数据。Claude Code上下文理解不足虽然Output为Claude Code优化了项目结构但如果你的提示词逻辑非常复杂Claude可能仍无法完全理解。一个技巧是在关键的.prompt文件开头用注释清晰地说明这个提示词的目的、输入输出格式、以及它会在哪个工作流中被如何使用。给AI更多的上下文它能更好地辅助你。环境配置混淆在output.config.ts中定义了多个环境如development,staging,production但在运行或部署时忘记指定环境变量OUTPUT_ENV可能导致使用了错误的API密钥或连接到了错误的Temporal集群。在Docker或部署脚本中一定要显式设置这个环境变量。6. 部署与生产化考量将Output项目部署到生产环境涉及几个关键环节。基础设施依赖Temporal集群这是Output的大脑。对于生产环境你需要部署一个高可用的Temporal集群。可以选择Temporal Cloud托管服务或者使用官方Docker镜像在Kubernetes上自托管。存储后端追踪数据需要存储。开发时用本地磁盘生产环境务必切换到S3、GCS或兼容的对象存储。运行工作流的工作节点你需要运行Output工作流代码的服务器或容器。这些节点会从Temporal集群领取任务并执行。你可以根据负载轻松地横向扩展这些工作节点。部署流程构建运行npm run build或output build将你的TypeScript工作流代码编译成JavaScript。打包将构建产物dist/目录、package.json和必要的配置文件打包进Docker镜像。配置通过环境变量注入所有敏感信息API密钥、数据库连接串、Temporal服务地址等。Output的加密凭据功能可以帮你管理这些密钥。运行工作节点启动你的容器并确保它执行了工作流“worker”的注册代码。通常这在一个入口文件中// worker.ts import { Worker } from ‘outputai/sdk’; import * as workflows from ‘./workflows’; // 导入所有工作流 const worker await Worker.create({ workflows: Object.values(workflows), // 注册工作流 taskQueue: ‘my-app-task-queue’, // 任务队列名 // … 其他连接配置 }); await worker.run();触发工作流通过Output提供的HTTP服务器、CLI或直接使用Temporal客户端SDK从你的主应用服务器触发工作流执行。监控与运维健康检查为你的工作节点暴露健康检查端点。日志聚合确保工作节点和Temporal集群的日志被收集到像ELK或Datadog这样的中央日志系统。指标收集利用Temporal和Output暴露的指标如工作流执行次数、耗时、失败率在Grafana等仪表板上进行监控。错误处理除了工作流内的重试还需要有全局的告警机制监控工作流失败率。Temporal的Web UI是调查失败工作流的强大工具。Output框架通过将AI工作流的复杂性封装进一个熟悉的、工程化友好的范式里确实大幅降低了构建可靠AI应用的门槛。它可能不是所有场景下的银弹但对于那些厌倦了在脆弱的“胶水代码”和昂贵的第三方平台之间挣扎的团队来说它提供了一个极具吸引力的、自主可控的折中方案。开源和Apache 2.0协议也让团队可以无顾虑地将其引入自己的技术栈并根据需要进行定制。