1. 从“提示词拼接”到“提示即代码”GenAIScript 的范式革新如果你和我一样在过去两年里深度使用过大语言模型那你一定经历过这个阶段打开一个文本编辑器或者某个在线提示词工具开始小心翼翼地拼接一段又一段的文本。你可能会用 Markdown 语法来划分章节用 XML 标签来包裹关键信息或者用一些特殊的占位符来标记需要动态替换的内容。整个过程充满了不确定性你很难复用之前的逻辑调试更是无从谈起——如果模型输出不符合预期你只能凭感觉去猜测是提示词的哪个部分出了问题然后手动调整、重新发送周而复始。这种工作方式我称之为“提示词手工艺时代”它低效、脆弱且难以规模化。直到我遇到了 GenAIScript。它的核心理念“Prompting is Coding”像一道闪电瞬间照亮了这条混沌的道路。它不是在教你如何写一个更好的提示词句子而是从根本上提供了一套编程工具让你能用 JavaScript 或 TypeScript 来“编写”你的提示词工作流。这不仅仅是语法的改变更是思维的升级。当你开始用变量、函数、循环和条件判断来构建提示词时你实际上是在构建一个可测试、可调试、可复用的“智能程序”。GenAIScript 将提示工程从一个充满玄学的“艺术”转变为一门严谨的“工程”。它让你能够像管理软件项目一样用版本控制、模块化、自动化测试等成熟的工程实践来管理你的 AI 应用逻辑。对我来说这彻底改变了与 LLM 协作的方式从一次性的、脆弱的对话变成了可迭代、可维护的自动化流程。2. 核心架构解析如何用代码“组装”智能GenAIScript 的设计哲学非常清晰它不是一个全新的语言或复杂的框架而是一个构建在 JavaScript/TypeScript 之上的“工具箱”。它通过提供一系列精心设计的 API 和运行时环境将 LLM 调用、上下文管理、文件处理、工具调用等能力无缝集成到你的代码逻辑中。理解它的几个核心抽象是高效使用它的关键。2.1 核心抽象脚本、上下文与提示一个 GenAIScript 脚本通常以.genai.js或.genai.ts为后缀的本质是一个定义了如何与 LLM 交互的程序。其核心流程可以概括为准备上下文Context - 组装提示Prompt - 执行并解析结果。脚本Script是执行的入口。script()函数用于配置脚本的全局行为比如指定使用的模型、系统提示词、安全策略等。这相当于为你的 AI 任务设置好了“舞台”和“规则”。// 脚本配置示例使用 OpenAI 的 GPT-4o 模型并启用安全系统提示 script({ model: “openai:gpt-4o” // 模型配置 system: [“system.safety_harmful_content”] // 内置的安全系统提示 temperature: 0.2 // 控制输出的随机性 })上下文Context是 LLM 的“知识”来源。在传统提示中你需要手动将文件内容、数据库查询结果等粘贴进去。在 GenAIScript 中你可以使用def()函数来动态地、智能化地注入上下文。// 读取当前目录下所有 .md 文件并作为上下文注入命名为“DOCS” const markdownFiles await workspace.find(“**/*.md”) def(“DOCS” markdownFiles) // 更智能的注入只注入文件的前100行并自动优化以适应模型的上下文窗口 def(“CONFIG” await workspace.readText(“config.yaml”) { sliceHead: 100 })这里的def()函数非常强大。它不仅仅是简单的字符串替换。GenAIScript 的运行时会在后台评估注入内容的大小和结构必要时对其进行智能分块、总结或提取关键信息以确保其能最有效地被目标 LLM 所利用同时不超出上下文长度限制。这解决了手动处理长文档时的核心痛点。提示Prompt的组装则通过$这个模板标签函数来完成。这是 GenAIScript 最标志性的语法。它允许你将 JavaScript 表达式变量、函数调用结果等直接嵌入到提示词字符串中形成最终的、动态生成的提示。const topic “量子计算” const style “用比喻的方式解释” $请${style}以下概念${topic}。请参考 DOCS 中的相关定义。这段代码生成的提示词会是“请用比喻的方式解释以下概念量子计算。请参考 DOCS 中的相关定义。” 而DOCS的内容已经被def()函数智能地注入到了上下文中。这种写法让提示词的生成逻辑一目了然并且易于重构。2.2 结构化输出与数据模式告别 JSON 解析噩梦让 LLM 返回结构化数据如 JSON是现代 AI 应用的基础需求但随之而来的是繁琐的解析和验证。GenAIScript 通过defSchema()函数和内置的 Zod 集成优雅地解决了这个问题。你不再需要在提示词里写“请返回一个 JSON 对象包含 name 和 age 字段”然后手动用JSON.parse()去解析可能格式不完整的响应。你可以直接定义一个数据模式Schemaconst personSchema defSchema(“PERSON” { type: “object” properties: { name: { type: “string” description: “人物姓名” } age: { type: “number” minimum: 0 } hobbies: { type: “array” items: { type: “string” } } } required: [“name” “age”] }) // 在提示词中直接引用这个模式 $从以下文本中提取人物信息并严格按照 ${personSchema} 定义的格式输出 JSON。文本${rawText}当 LLM 完成响应后GenAIScript 会自动从响应文本中提取 JSON 部分并根据你定义的模式进行验证和修复。如果 LLM 返回的 JSON 缺少了必填字段age或者hobbies不是数组GenAIScript 的运行时可以根据配置自动发起一个修复请求让 LLM 修正输出直到得到一个符合模式的有效数据对象。这个功能极大地提升了开发效率和结果的可靠性。2.3 工具与代理让 LLM 拥有“手脚”单纯的文本生成能力是有限的。真正的智能应用需要 LLM 能够与现实世界交互查询数据库、调用 API、执行计算。GenAIScript 的工具Tools和代理Agents系统正是为此而生。工具允许你将任意的 JavaScript 函数暴露给 LLM 调用。你只需要用defTool()进行声明描述其功能和参数GenAIScript 就会自动将其格式化为模型能理解的工具定义如 OpenAI 的 function calling。// 定义一个查询天气的工具 defTool( “getWeather” “获取指定城市的当前天气” { city: “string” } // 参数模式 async ({ city }) { // 这里是实际的业务逻辑可以调用任何 API const response await fetch(https://api.weather.com/v1/current?city${city}) const data await response.json() return 城市 ${city} 的天气是${data.condition}温度 ${data.temp}°C。 } ) // 在脚本中启用这个工具 script({ tools: [“getWeather”] }) // LLM 现在可以根据用户问题自动决定是否以及如何调用这个工具 $用户问“北京今天天气怎么样”代理是更高层次的抽象。它将一组工具、特定的系统提示词和模型配置打包成一个可复用的“智能体”。你可以把它看作一个具备特定技能和目标的 AI 助手。GenAIScript 内置了一些实用的代理比如 Git 代理它封装了 Git 命令允许 LLM 分析代码仓库。// 使用内置的 Git 代理 script({ tools: [“agent_git”] }) $分析本仓库最近一周的提交记录总结主要的开发活动类型。执行这段脚本LLM 会通过 Git 代理工具获取提交历史然后进行分析和总结。你不需要自己写 Git 命令的解析逻辑代理已经帮你处理好了工具调用和结果整合的复杂性。3. 实战工作流从零构建一个文档智能分析助手理论说得再多不如动手实践。让我们构建一个实用的工具一个本地文档分析助手。它的功能是扫描一个目录下的所有 Markdown 和 PDF 文档自动生成一份摘要报告并提取出其中的待办事项TODO和项目风险点。3.1 环境准备与项目初始化首先你需要一个 Node.js 环境建议 v18 以上。GenAIScript 提供了两种使用方式VS Code 扩展和命令行工具CLI。对于日常开发我强烈推荐使用VS Code 扩展因为它提供了无与伦比的开发体验语法高亮、智能提示、一键运行、实时调试。安装 VS Code 扩展在 VS Code 扩展商店中搜索 “GenAIScript” 并安装。创建项目目录mkdir doc-analyzer cd doc-analyzer初始化配置文件GenAIScript 需要一个genaiscript.json文件来管理项目配置和模型设置。创建一个最简单的配置{ “$schema”: “https://microsoft.github.io/genaiscript/schemas/genaiscript-schema.json” “name”: “doc-analyzer” “model”: “openai:gpt-4o-mini” // 这里以 OpenAI 为例你需要有自己的 API Key }设置 API 密钥在项目根目录创建一个.env文件确保它在.gitignore中填入你的模型供应商 API 密钥例如OPENAI_API_KEYsk-your-key-hereVS Code 扩展会自动读取这个文件。你也可以在配置文件中直接指定“env”字段。3.2 编写核心分析脚本接下来我们创建主脚本文件analyze-docs.genai.mjs使用.mjs以支持 ES 模块。// 脚本配置使用较新的模型低 temperature 以保证分析稳定性 script({ model: “openai:gpt-4o-mini” temperature: 0.1 // 启用安全扫描防止意外泄露密钥 secretScanning: true }) // 1. 动态发现文档 // 使用 workspace 模块查找所有 Markdown 和 PDF 文件 const docs await workspace.find(“**/*.{mdpdf}”) console.log(找到 ${docs.length} 个文档文件。) if (docs.length 0) { throw new Error(“未找到任何 .md 或 .pdf 文件。”) } // 2. 智能注入文档内容 // 我们将每个文档的内容作为独立上下文注入并标记来源 for (const doc of docs) { const content await workspace.read(doc) // read 方法能自动处理文本和二进制文件 // 为每个文档创建一个唯一的上下文标识符避免冲突 def(DOC_${doc.name}, content, { // 对于大文件只取前 2000 个字符进行分析平衡效果与成本 sliceHead: 2000, // 添加来源信息便于 LLM 引用 description: 来源文件${doc.path} }) } // 3. 定义输出数据结构模式 const reportSchema defSchema(“ANALYSIS_REPORT” { type: “object” properties: { summary: { type: “string” description: “对所有文档内容的整体性摘要不超过300字。” } keyThemes: { type: “array” items: { type: “string” } description: “从文档中提炼出的3-5个核心主题或关键词。” } todos: { type: “array” items: { type: “object” properties: { task: { type: “string” } sourceDoc: { type: “string” } priority: { type: “string” enum: [“high” “medium” “low”] } } required: [“task” “sourceDoc”] } description: “从所有文档中提取出的待办事项列表。” } risks: { type: “array” items: { type: “object” properties: { description: { type: “string” } sourceDoc: { type: “string” } severity: { type: “string” enum: [“critical” “high” “medium” “low”] } } required: [“description” “sourceDoc”] } description: “从所有文档中识别出的潜在风险或问题点。” } } required: [“summary” “keyThemes” “todos” “risks”] }) // 4. 构建并执行分析提示 $ 你是一个专业的文档分析助理。我已经为你提供了多个文档的内容每个文档的上下文标识符为 DOC_文件名。 请完成以下任务 1. **整体摘要**基于所有文档内容撰写一份简洁的综合摘要。 2. **提炼主题**归纳出 3-5 个贯穿这些文档的核心主题。 3. **提取待办事项**仔细扫描所有文档找出所有明确或隐含的待办事项TODO、行动计划、未决问题。为每个事项标注其来源文档和优先级。 4. **识别风险**找出文档中提到的任何风险、假设、依赖项缺失或可能存在矛盾的地方。评估其严重性。 请将分析结果严格按照 ${reportSchema} 定义的 JSON 格式输出。 // 5. 结果后处理与输出 // GenAIScript 会自动将符合模式的 JSON 解析为 JavaScript 对象 const analysisResult env.vars.ANALYSIS_REPORT // 将结果保存为 JSON 文件 await workspace.writeText( “./analysis_report.json” JSON.stringify(analysisResult null 2) // 美化输出 ) console.log(“分析完成结果已保存至 analysis_report.json”) // 同时在控制台输出关键信息 console.log(“\n 核心主题 ”) analysisResult.keyThemes.forEach(theme console.log(- ${theme})) console.log(“\n 高优先级待办事项 ”) analysisResult.todos .filter(todo todo.priority “high”) .forEach(todo console.log(- [${todo.sourceDoc}] ${todo.task}))3.3 运行与调试在 VS Code 中打开这个脚本文件你会看到编辑器顶部有一个 “Run” 按钮。点击它GenAIScript 扩展就会执行这个脚本。执行过程扩展会首先在你的项目目录中扫描文档然后将文档内容、你定义的 Schema 和提示词模板组合起来发送给配置的 LLM本例中是 GPT-4o-mini。查看结果执行完成后你可以在 VS Code 的 “GenAIScript” 输出面板看到完整的运行日志包括模型请求和响应的原始信息可在设置中控制详细程度。生成的analysis_report.json文件会自动在编辑器中打开。调试技巧检查上下文如果结果不理想首先检查def()注入的内容是否正确。你可以在def()前后用console.log输出内容片段。调整提示词$模板中的指令是影响结果的关键。尝试让指令更具体、更结构化。例如将“提取待办事项”改为“找出所有以‘TODO’、‘[ ]’或‘下一步’开头的句子”。使用sliceHead/sliceTail对于超长文档盲目注入全部内容会消耗大量 Token 且可能稀释关键信息。使用{ sliceHead: 1000 }只注入开头部分或结合workspace.read()的maxTokens选项进行智能截断。迭代开发GenAIScript 的优势在于快速迭代。你可以先写一个简单的脚本分析单个文件验证流程和输出格式再逐步扩展为处理多文件、复杂模式。这个脚本展示了一个完整的 GenAIScript 应用生命周期从文件系统交互、上下文准备、结构化输出定义到最终的提示组装、执行和结果处理。你可以在此基础上轻松扩展例如增加对 Word 文档、Excel 表格的支持或者将分析结果自动提交到项目管理工具如 Jira中。4. 高级特性与生态集成释放全部潜力掌握了基础工作流后GenAIScript 的一些高级特性可以帮你构建更强大、更可靠的生产级应用。4.1 内置检索增强生成对于知识库问答这类场景你需要将用户问题与海量文档进行语义匹配。GenAIScript 内置了向量搜索RAG功能无需依赖外部服务。// 1. 为文档创建向量索引 const index await retrieval.createIndex(“my-docs-index”) for (const file of await workspace.find(“docs/**/*.md”)) { const text await workspace.readText(file) // 将文档分块并添加到索引 await index.add({ text path: file.path }) } await index.save() // 索引可以持久化避免每次重建 // 2. 查询时进行语义检索 const query “如何在 GenAIScript 中定义工具” const { chunks } await retrieval.vectorSearch(query “my-docs-index” { topK: 3 }) // 3. 将检索到的相关片段作为上下文注入 def(“RELEVANT_DOCS” chunks.map(c c.text).join(“\n\n”)) // 4. 基于检索到的上下文进行回答 $基于 RELEVANT_DOCS 中的内容回答以下问题${query}。如果资料中没有明确答案请说明。这个流程将外部知识库与 LLM 的生成能力紧密结合确保了回答的准确性和依据性。4.2 测试与评估构建可靠的提示工作流提示工程的脆弱性在于微小的改动可能导致输出质量的巨大波动。GenAIScript 集成了promptfoo来进行自动化测试和评估Evals。你可以创建一个测试套件针对你的脚本提供不同的输入用例和预期的评估标准。// 在脚本配置中定义测试 script({ model: “openai:gpt-4o-mini” tests: { // 提供测试用例文件 files: “test_cases.csv” // 定义评估准则Rubric rubric: “回答必须准确引用提供的上下文且语气专业友好。” // 提供每个测试用例的“事实”或预期要点 facts: (vars) 本次查询是关于 ${vars.input} 的。正确答案应包含 X 和 Y 要素。 } })运行npx genaiscript test your-script.genai.mjs它会自动运行所有测试用例并生成一份评估报告告诉你脚本在不同场景下的表现如何从而让你有信心进行重构和优化。4.3 与开发运维流程集成GenAIScript 的 CLI 工具让它能轻松融入自动化流程。代码审查助手在 CI/CD 流水线如 GitHub Actions中集成自动对 Pull Request 进行代码审查、生成变更摘要或检查提交信息规范。# 在 GitHub Actions 的 steps 中 - name: AI Code Review run: | npx genaiscript run code-reviewer “${{ github.event.pull_request.changed_files }}” \ --pull-request-reviews \ --github-token “${{ secrets.GITHUB_TOKEN }}”批量处理使用 CLI 批量处理大量文件。npx genaiscript run summarize-doc “./reports/*.pdf” --output-dir “./summaries”作为 API 使用你也可以将 GenAIScript 脚本作为一个模块导入到你的 Node.js 后端服务中。import { run } from “genaiscript/api” const result await run(“./scripts/my-agent.genai.mjs” { input: “用户查询” })4.4 模型与供应商的灵活性GenAIScript 的model配置项提供了极大的灵活性让你可以根据成本、性能和需求切换不同的后端。“openai:gpt-4o”使用 OpenAI 的最新模型。“azure:my-deployment-name”使用 Azure OpenAI 服务。“anthropic:claude-3-5-sonnet”切换到 Anthropic Claude。“github:gpt-4o”使用 GitHub Copilot Chat 的模型如果你有订阅。“ollama:llama3.2”使用本地运行的 Ollama完全离线数据隐私性最高。这种抽象让你只需修改一行配置就能让整个脚本在不同的模型生态中运行便于进行成本对比和性能测试。5. 避坑指南与最佳实践经过一段时间的密集使用我总结了一些关键的经验教训能帮你绕过不少弯路。1. 上下文管理的艺术少即是多LLM 的上下文窗口是宝贵资源。盲目注入整个文件内容是最常见的错误。务必使用def()的选项如sliceHeadmaxTokens或先通过workspace.grep()、retrieval.vectorSearch()进行预处理只注入最相关的信息。对于超长文档考虑先使用一个简单的提示词让 LLM 自己总结出关键段落再将总结作为上下文注入主任务。2. 模式验证是安全网不是万能药defSchema()能极大提高输出数据的可靠性但它依赖于 LLM 的理解和配合。对于极其关键的数据建议提供示例在模式描述或系统提示中给出一个清晰的输出示例。使用更严格的模型在生成结构化数据的任务上GPT-4 通常比 GPT-3.5 可靠得多。设置重试机制利用 GenAIScript 的脚本逻辑如果解析失败可以捕获错误并尝试用更详细的指令重新生成。3. 工具调用的可靠性当 LLM 决定调用一个工具时它生成的参数可能不符合函数预期。务必在你的工具函数内部添加严格的参数验证和错误处理。返回给 LLM 的错误信息应清晰、可操作例如“调用 getWeather 失败参数 ‘city’ 不能为空字符串”而不是一个晦涩的异常堆栈。4. 成本与延迟监控在开发阶段频繁运行脚本可能会产生意想不到的 API 调用成本。建议开发时使用小模型或本地模型如“openai:gpt-4o-mini”或“ollama:qwen2.5-7b”。利用缓存GenAIScript 支持对 LLM 响应进行缓存通过配置cache选项对于重复的提示可以显著节省成本和时间。查看详细日志运行脚本时关注输出中的 Token 使用量优化提示和上下文以减少不必要的消耗。5. 脚本的模块化与复用不要写一个巨无霸脚本。将通用的功能拆分成小的、可复用的脚本文件。例如你可以有一个summarize.genai.mjs专门负责摘要一个extract-todos.genai.mjs专门提取待办事项。然后在主脚本中通过runPrompt()函数或 CLI 调用来组合它们。这符合软件工程的高内聚、低耦合原则也让测试和维护变得更简单。GenAIScript 将提示工程从一种“黑魔法”转变为一种可编程、可测试、可集成的软件开发实践。它可能不是解决所有 AI 应用问题的银弹但它无疑为开发者提供了一套强大而优雅的工具让我们能够以更工程化的方式去探索和实现 LLM 的潜力。当你开始用代码的思维去构建提示时你会发现与机器协作的边界正在被极大地拓宽。