定制网站建设制作商,全网整合营销平台,wordpress建站原理,网站建设捌金手指花总十各位开发者#xff0c;下午好#xff01;
今天#xff0c;我们将深入探讨一个激动人心且充满工程挑战的领域#xff1a;如何在 Cloudflare Workers 这样的边缘计算平台上#xff0c;高效、可靠地运行轻量级 LangChain 逻辑。这不仅仅是将一个 Python 库移植到 JavaScript…各位开发者下午好今天我们将深入探讨一个激动人心且充满工程挑战的领域如何在 Cloudflare Workers 这样的边缘计算平台上高效、可靠地运行轻量级 LangChain 逻辑。这不仅仅是将一个 Python 库移植到 JavaScript 的问题它涉及到对边缘计算模型、资源限制、LangChain 架构以及现代Web开发范式的深刻理解和巧妙融合。1. 边缘部署与Cloudflare Workers构建未来应用的基础1.1 什么是边缘部署边缘部署Edge Deployment是指将应用程序的计算和数据存储尽可能地靠近用户或数据源。其核心目标是最小化延迟、提高响应速度、减少中心化服务器的负载并增强数据隐私与安全性。想象一下当用户在东京访问一个服务时其请求不是远赴美国东海岸的中心服务器而是在东京或附近的边缘节点得到处理。边缘部署的核心优势低延迟减少数据传输距离大幅降低往返时间RTT。高可用性分布式架构减少了单点故障的风险。可伸缩性能够根据流量需求在全球范围内弹性扩展。成本效益对于某些工作负载可以优化基础设施成本。数据本地化有助于满足数据主权和隐私法规。1.2 Cloudflare Workers无服务器边缘计算的实践者Cloudflare Workers 是 Cloudflare 提供的一种无服务器Serverless边缘计算平台。它允许开发者在 Cloudflare 庞大的全球网络边缘节点上运行 JavaScript、TypeScript 或 WebAssembly 代码。每个 Workers 脚本都可以在全球 300 多个城市的数据中心被执行这意味着用户的请求可以在离他们最近的物理位置得到响应。Cloudflare Workers 的核心特性全球分布式网络代码部署到 Cloudflare 的所有边缘节点。基于 V8 引擎使用 Google Chrome 背后的 V8 JavaScript 引擎提供高性能和快速启动时间。无服务器模型开发者无需管理服务器只需编写代码并部署。事件驱动主要响应 HTTP 请求但也可以响应定时任务、队列事件等。资源限制这是我们今天讨论的重点之一。Workers 实例有严格的 CPU 时间、内存和脚本大小限制以确保快速执行和资源公平分配。Cloudflare Workers 与传统 Serverless 平台如 AWS Lambda的对比特性Cloudflare WorkersAWS Lambda (或类似)执行位置全球边缘节点靠近用户特定区域数据中心通常离用户较远冷启动极快毫秒级V8 引擎优化较快数十到数百毫秒但通常慢于 Workers运行时JavaScript/TypeScript/WebAssembly (V8)多种运行时Node.js, Python, Java, Go, .NET 等资源限制严格的 CPU 时间 (50ms/30s)、内存 (128MB)、脚本大小相对宽松CPU时间、内存可配置到数GB更长执行时间持久化存储KV、Durable Objects、D1、R2S3、DynamoDB、RDS 等全套云服务开发模型专注于 Web 请求处理轻量级 API广泛的事件源集成支持复杂后端逻辑成本模型通常按请求和 CPU 时间计费非常经济按请求和内存/执行时间计费成本可能更高2. LangChain构建大语言模型应用的利器2.1 什么是 LangChainLangChain 是一个用于开发由大语言模型LLM驱动的应用程序的框架。它提供了一套模块化、可组合的工具极大地简化了与 LLM 交互、构建复杂链式操作和创建智能代理的过程。LangChain 旨在帮助开发者更轻松地构建以下类型的应用问答系统QA基于特定文档或知识库进行问答。聊天机器人维持上下文、执行多轮对话。数据提取和结构化从非结构化文本中提取信息。代理Agents让 LLM 能够自主决定使用哪些工具来完成任务。数据增强生成RAG – Retrieval Augmented Generation结合检索系统为 LLM 提供外部知识。2.2 LangChain 的核心组件LangChain 的强大之处在于其模块化的设计主要组件包括Models (模型):与各种 LLM 提供商如 OpenAI, Anthropic, Google 等的接口。LLMs:纯文本输入/输出模型。ChatModels:接受/返回聊天消息列表的模型更适合对话。EmbeddingModels:将文本转换为向量表示的模型。Prompts (提示):用于构造和管理发送给 LLM 的提示。PromptTemplates:动态生成提示的模板。ChatPromptTemplates:针对聊天模型的模板。Chains (链):将 LLM 与其他组件如提示模板、解析器、内存等组合起来的结构化调用序列。Retrievers (检索器):用于从外部数据源如向量数据库、文档存储检索相关信息通常用于 RAG。Memory (记忆):在多轮对话中存储和管理历史信息。Agents (代理):让 LLM 能够根据用户输入和可用工具自主决定执行一系列操作来完成复杂任务。Tools (工具):代理可以调用的外部功能如搜索引擎、计算器、API 等。LangChain 最初以 Python 库的形式发布并广受欢迎随后推出了 JavaScript/TypeScript 版本langchain.js这为我们在 Cloudflare Workers 上运行 LangChain 逻辑提供了可能。3. 在Cloudflare Workers上运行LangChain逻辑的工程挑战将 LangChain 逻辑移植到 Cloudflare Workers 并非简单的复制粘贴。我们需要应对边缘计算环境特有的诸多限制和编程范式差异。3.1 运行时环境不匹配Python 到 JavaScript/TypeScript 的范式转换挑战描述LangChain 的核心生态系统和大多数示例都基于 Python。Python 以其丰富的科学计算库和动态特性而闻名而 Cloudflare Workers 则运行在 V8 引擎上的 JavaScript/TypeScript 环境原生不支持 Python 代码。这意味着我们无法直接使用 Python 版的 LangChain。解决方案拥抱langchain.js。LangChain 团队推出了官方的 JavaScript/TypeScript 版本它旨在提供与 Python 版本相似的功能和 API。我们需要用 TypeScript推荐因为它提供类型安全和更好的开发体验来重写或实现我们的 LangChain 逻辑。代码示例基础 LLM 调用与提示模板首先确保你的 Cloudflare Workers 项目已经初始化并安装了langchain及其相关依赖。# 初始化 Wrangler 项目 npm create cloudflarelatest my-langchain-worker --typeweb cd my-langchain-worker # 安装 LangChain.js 和 OpenAI/Anthropic 等 LLM 库 npm install langchain langchain/openai # 或者 langchain/anthropic npm install -D typescript # 如果还没有安装worker.ts:import { Hono } from hono; import { OpenAI } from langchain/openai; // 或 Anthropic, GoogleGenerativeAI 等 import { PromptTemplate } from langchain/core/prompts; import { StringOutputParser } from langchain/core/output_parsers; // 初始化 Hono 应用程序用于处理 HTTP 请求 const app new Hono(); // 定义一个简单的 GET 请求路由 app.get(/ask, async (c) { // 从请求中获取用户输入 const query c.req.query(q); if (!query) { return c.json({ error: Please provide a query parameter q. }, 400); } // 1. 初始化 LLM // 注意OPENAI_API_KEY 应通过 Cloudflare Workers Secrets 管理 const model new OpenAI({ temperature: 0.7, openAIApiKey: c.env.OPENAI_API_KEY, // 从 Workers 环境变量获取 API Key // modelName: gpt-3.5-turbo // 默认模型可以显式指定 }); // 2. 定义提示模板 const promptTemplate PromptTemplate.fromTemplate( 你是一个专业的AI助手请根据以下问题给出简洁明了的回答。 问题: {question} ); // 3. 构建链 // 使用 LCEL (LangChain Expression Language) 构建链 const chain promptTemplate.pipe(model).pipe(new StringOutputParser()); try { // 4. 执行链 const result await chain.invoke({ question: query }); // 返回结果 return c.json({ query: query, answer: result }); } catch (error) { console.error(Error invoking LangChain:, error); return c.json({ error: Failed to process your request. }, 500); } }); // 导出 Workers 处理器 export default app;部署前配置wrangler.toml为了让 Workers 能够访问 API Key需要在wrangler.toml中定义环境变量。name my-langchain-worker main src/worker.ts compatibility_date 2024-01-01 # 绑定 Hono 应用 [vars] MY_VAR some_value # 可以定义其他变量 # 定义 Secrets。这些值不会被提交到版本控制需要在部署时设置 # 例如wrangler secret put OPENAI_API_KEY # 部署后通过 c.env.OPENAI_API_KEY 访问在部署时你需要通过wrangler secret put OPENAI_API_KEY命令来设置你的 OpenAI API Key。3.2 资源约束CPU 时间、内存和脚本大小的限制挑战描述Cloudflare Workers 为了保持极低的延迟和高效的资源利用对每个请求的执行资源有严格的限制。CPU 时间默认 50ms (免费计划)最长 30s (付费计划带waitUntil异步操作)。这对于通常需要几秒到几十秒才能完成的 LLM 推理来说是一个巨大的挑战。内存默认 128MB。大型模型、复杂的向量数据库索引或大量数据处理都可能超出此限制。脚本大小压缩后通常限制在 1MB 左右。这意味着我们需要极其精简的依赖和代码。解决方案3.2.1 CPU 时间管理异步操作与waitUntil对于不影响响应时间的后台任务如日志记录、缓存更新、部分数据预处理可以使用event.waitUntil()来延长 Workers 的生命周期允许这些任务在响应发送后继续执行但总时长仍受限制。Offloading (任务卸载)将计算密集型或长时间运行的任务卸载到专门的后端服务如 AWS Lambda, Google Cloud Functions, 或自建服务执行。Workers 仅负责协调和代理。流式传输Streaming对于 LLM 响应使用流式传输可以显著改善用户体验因为用户可以立即看到部分内容而不是等待整个响应生成。LangChain.js 和 LLM 提供商通常支持流式传输。代码示例利用waitUntil和流式传输import { Hono } from hono; import { OpenAI } from langchain/openai; import { PromptTemplate } from langchain/core/prompts; import { StringOutputParser } from langchain/core/output_parsers; import { StreamingTextResponse, LangChainStream } from ai; // 假设你使用 Vercel AI SDK 的流式接口 const app new Hono(); app.post(/chat-stream, async (c) { const { prompt } await c.req.json(); if (!prompt) { return c.json({ error: Prompt is required. }, 400); } const model new OpenAI({ temperature: 0.7, openAIApiKey: c.env.OPENAI_API_KEY, streaming: true, // 启用流式传输 }); const promptTemplate PromptTemplate.fromTemplate(请以友好的语气回答以下问题: {question}); const chain promptTemplate.pipe(model).pipe(new StringOutputParser()); // 使用 Vercel AI SDK 的 LangChainStream 来处理流式响应 // 这是一种在 Workers 上处理流的常见模式它封装了事件监听和响应构建 const { stream, handlers } LangChainStream(); // 异步执行链并将结果通过 handlers 传递给 stream // 注意这里 chain.stream() 返回一个 AsyncIterable chain.stream({ question: prompt }, { callbacks: [handlers] }).catch(console.error); // 立即返回一个 StreamingTextResponse // Cloudflare Workers 会自动处理这个 Response 对象的流式传输 return new StreamingTextResponse(stream); }); // 演示 waitUntil app.post(/log-async, async (c) { const { data } await c.req.json(); // 立即响应用户 c.json({ status: Processing in background }); // 使用 waitUntil 执行后台任务 c.executionCtx.waitUntil(async () { try { // 模拟一个需要时间但不需要阻塞主请求的任务 await new Promise(resolve setTimeout(resolve, 2000)); console.log(Background task completed for data:, data); // 这里可以写入 KV, D1, R2 或调用外部日志服务 } catch (error) { console.error(Background task failed:, error); } }); return c.json({ message: Request received, processing in background. }); }); export default app;注意StreamingTextResponse和LangChainStream通常是 Vercel AI SDK 的一部分。你可能需要安装ai包。在 Workers 环境中Response对象本身就支持BodyInit为ReadableStreamai库提供了一个方便的抽象。3.2.2 内存管理精简依赖仔细选择 LangChain.js 的模块。只导入你需要的特定组件避免导入整个langchain包。例如如果你只需要 OpenAI 模型只导入langchain/openai。数据流处理避免一次性加载大量数据到内存。对于大型文本或文件考虑分块处理或直接从 R2 (Cloudflare 的对象存储) 流式读取。外部存储将状态和大型数据集存储在 Cloudflare KV、D1 或 R2 中而不是 Worker 的内存。避免全局状态Workers 实例是短生命周期的且可能被复用。避免在全局作用域声明大型、可变的状态这不仅浪费内存也可能导致意外的行为。3.2.3 脚本大小管理Tree-shaking (摇树优化)现代打包工具如 Webpack, Rollup, esbuild – Wrangler 默认使用会自动移除未使用的代码。确保你的代码结构支持有效的 tree-shaking。选择轻量级库优先选择专为边缘环境设计的轻量级库。例如对于 HTTP 框架Hono 通常比 Express 更小。ES Modules (ESM)确保你的代码和依赖都使用 ESM 格式这有助于打包工具更好地进行 tree-shaking。Wrangler 配置wrangler.toml可以配置[build]部分来优化打包过程尽管默认配置通常已经很高效。代码示例精简依赖与wrangler.tomlpackage.json示例{ name: my-langchain-worker, version: 0.0.0, private: true, scripts: { deploy: wrangler deploy, start: wrangler dev }, dependencies: { hono: ^4.0.0, langchain/openai: ^0.0.28, // 只导入 OpenAI 模型 langchain/core: ^0.1.51, // LangChain 核心包含 PromptTemplate, OutputParser ai: ^3.0.0 // 用于流式传输的 Vercel AI SDK }, devDependencies: { cloudflare/workers-types: ^4.20240403.0, typescript: ^5.0.4, wrangler: ^3.47.0 } }通过只安装langchain/openai和langchain/core而不是langchain整个包可以显著减少最终的打包大小。3.3 依赖管理与打包Node.js生态系统与Workers的差异挑战描述虽然 Cloudflare Workers 运行 JavaScript但它并不是一个完整的 Node.js 环境。许多 Node.js 内置模块如fs,path,http等在 Workers 中不可用或者需要专门的 polyfill。LangChain.js 及其一些依赖可能在设计时考虑了 Node.js 环境这可能导致在 Workers 上运行时出现兼容性问题。解决方案Wrangler 的作用Cloudflare 的 CLI 工具wrangler负责项目的构建、打包和部署。它通常会使用esbuild进行打包能够很好地处理 TypeScript 和 ESM并进行 tree-shaking。Polyfills对于一些缺失的 Node.js 内置模块Cloudflare 提供了内置的 polyfills或者社区有解决方案。但应尽量避免依赖 Node.js 特有的 API选择 Web 标准 API如fetchAPI。选择 Workers 兼容的库在选择任何第三方库时优先考虑那些明确声明支持 Workers 或浏览器环境的库。自定义打包配置在wrangler.toml中你可以通过[build]部分来指定自定义打包器或配置。代码示例wrangler.toml打包配置name my-langchain-worker main src/worker.ts compatibility_date 2024-01-01 # build 配置 [build] command npm run build # 如果有自定义的构建脚本 # 如果你的入口文件在 src/worker.ts通常 main 字段就足够了 # wrangler 会自动处理 TypeScript 编译和打包。 # external [some-large-dependency] # 如果某个依赖你想从外部加载而不是打包进去对于 LangChain.js 自身其设计考虑了浏览器和 Workers 环境因此通常不会有太多的 Node.js 特有模块问题但仍需注意其依赖项。3.4 状态管理Workers 的无状态特性挑战描述Cloudflare Workers 默认是无状态的。这意味着每个请求都可能在一个全新的 Workers 实例上执行并且前一个请求的内存状态不会保留。这对于需要维护用户会话、对话历史或持久化数据的 LangChain 应用来说是一个核心挑战。解决方案Cloudflare 提供了一系列专门为边缘计算设计的持久化存储服务Cloudflare KV (Key-Value Store)适用于存储用户会话、配置、缓存数据等小型、非结构化的键值对数据。读写速度快但数据量不宜过大且有大小限制。Cloudflare D1 (Serverless Database)基于 SQLite 的无服务器关系型数据库。适用于结构化数据、需要 SQL 查询能力、以及事务性操作的场景。Cloudflare R2 (Object Storage)S3 兼容的对象存储服务适用于存储大型文件、媒体、文档等。适合用于存储检索增强生成 (RAG) 中的原始文档或索引文件。Durable Objects提供强大的有状态原语。每个 Durable Object 实例都有一个唯一的 ID并且可以在同一个 Workers 实例上长时间运行维持其内部状态。这对于构建有状态的聊天机器人、游戏服务器或其他需要持久化逻辑的应用程序非常有用。代码示例使用 Cloudflare KV 存储对话历史首先你需要在wrangler.toml中绑定一个 KV 命名空间。name my-langchain-worker main src/worker.ts compatibility_date 2024-01-01 [[kv_namespaces]] binding CHAT_HISTORY # 绑定名称将在 Worker 中通过 env.CHAT_HISTORY 访问 id YOUR_KV_NAMESPACE_ID # 你的 KV 命名空间 ID preview_id YOUR_PREVIEW_KV_NAMESPACE_ID # 预览环境的 KV 命名空间 ID (可选)创建 KV 命名空间并获取 IDwrangler kv namespace create CHAT_HISTORY。worker.ts:import { Hono } from hono; import { OpenAI } from langchain/openai; import { ChatPromptTemplate } from langchain/core/prompts; import { StringOutputParser } from langchain/core/output_parsers; import { RunnableSequence } from langchain/core/runnables; import { HumanMessage, AIMessage, BaseMessage } from langchain/core/messages; const app new Hono(); // 定义环境类型以便 TypeScript 能够识别 KV 绑定 type Bindings { CHAT_HISTORY: KVNamespace; OPENAI_API_KEY: string; }; // 辅助函数将 BaseMessage 数组序列化为 JSON 字符串 function serializeMessages(messages: BaseMessage[]): string { return JSON.stringify(messages.map(msg ({ type: msg._getType(), content: msg.content, name: msg.name, // 包含其他可能的字段如 tool_calls, function_call 等 }))); } // 辅助函数将 JSON 字符串反序列化为 BaseMessage 数组 function deserializeMessages(jsonString: string): BaseMessage[] { const rawMessages JSON.parse(jsonString); return rawMessages.map((rawMsg: any) { if (rawMsg.type human) return new HumanMessage(rawMsg.content); if (rawMsg.type ai) return new AIMessage(rawMsg.content); // 根据需要添加其他消息类型如 SystemMessage, ToolMessage, FunctionMessage return new BaseMessage(rawMsg.content, rawMsg.type); // Fallback }); } app.post(/chat, async (c) { const { sessionId, message } await c.req.json(); if (!sessionId || !message) { return c.json({ error: sessionId and message are required. }, 400); } const { CHAT_HISTORY, OPENAI_API_KEY } c.env as Bindings; // 1. 从 KV 加载历史消息 let history: BaseMessage[] []; const historyString await CHAT_HISTORY.get(sessionId); if (historyString) { history deserializeMessages(historyString); } // 2. 将当前用户消息添加到历史中 const currentMessages [...history, new HumanMessage(message)]; // 3. 定义聊天模型 const model new OpenAI({ temperature: 0.7, openAIApiKey: OPENAI_API_KEY, modelName: gpt-3.5-turbo, // 或者 gpt-4 }); // 4. 定义聊天提示模板 const chatPrompt ChatPromptTemplate.fromMessages([ [system, 你是一个友好的AI助手请根据对话历史回答问题。], ...currentMessages, // 插入历史消息 ]); // 5. 构建链 const chain RunnableSequence.from([ chatPrompt, model, new StringOutputParser(), ]); try { // 6. 执行链 const aiResponse await chain.invoke({}); // 注意这里因为 prompt 包含了所有消息所以 invoke 的参数为空 // 7. 将 AI 响应添加到历史中 const updatedHistory [...currentMessages, new AIMessage(aiResponse)]; // 8. 将更新后的历史保存到 KV await CHAT_HISTORY.put(sessionId, serializeMessages(updatedHistory)); // 返回结果 return c.json({ sessionId: sessionId, response: aiResponse }); } catch (error) { console.error(Error in chat:, error); return c.json({ error: Failed to process your chat request. }, 500); } }); export default app;3.5 延迟优化兼顾边缘优势与LLM推理时间挑战描述边缘部署的核心优势是低延迟。然而与 LLM 的 API 调用本身通常需要数百毫秒到数秒的时间这可能会抵消边缘计算带来的网络延迟优势。如何确保最终用户体验依然快速流畅是关键。解决方案LLM API 优化选择最近的 API 区域如果 LLM 提供商有多个区域确保 Workers 调用的是离 Workers 边缘节点最近的 API 端点。优化提示词简洁、清晰的提示词通常能更快地得到响应。减少不必要的上下文。流式传输如前所述流式传输让用户能即时看到部分响应极大改善感知延迟。缓存Cloudflare Cache API利用 Workers 内置的 Cache API 缓存 LLM 的常见问题响应。对于重复性高且结果稳定的查询非常有效。Cloudflare KV也可以将 LLM 响应缓存到 KV 中尤其适用于个性化缓存或需要更细粒度控制的场景。预取与并行对于可预测的用户交互可以预先获取一些数据或执行部分 LangChain 逻辑。如果任务可以分解并行执行多个 LLM 调用或检索操作然后合并结果。代码示例使用 Cloudflare Cache API 缓存 LLM 响应import { Hono } from hono; import { OpenAI } from langchain/openai; import { PromptTemplate } from langchain/core/prompts; import { StringOutputParser } from langchain/core/output_parsers; const app new Hono(); type Bindings { OPENAI_API_KEY: string; }; app.get(/cached-ask, async (c) { const query c.req.query(q); if (!query) { return c.json({ error: Please provide a query parameter q. }, 400); } const cacheKey new Request(c.req.url cachetrue); // 构建缓存键 const cache caches.default; // 获取默认缓存存储 // 尝试从缓存中获取响应 let response await cache.match(cacheKey); if (response) { console.log(Cache hit for query:, query); return response; // 直接返回缓存的响应 } console.log(Cache miss for query:, query); const { OPENAI_API_KEY } c.env as Bindings; const model new OpenAI({ temperature: 0.1, // 更低的温度有助于生成更一致的结果提高缓存命中率 openAIApiKey: OPENAI_API_KEY, }); const promptTemplate PromptTemplate.fromTemplate( 请回答以下问题答案应简洁且信息丰富: {question} ); const chain promptTemplate.pipe(model).pipe(new StringOutputParser()); try { const answer await chain.invoke({ question: query }); // 构建新的响应 response c.json({ query: query, answer: answer }); // 设置缓存策略缓存 1 小时 response.headers.set(Cache-Control, public, max-age3600); c.executionCtx.waitUntil(cache.put(cacheKey, response.clone())); // 将响应存入缓存 return response; } catch (error) { console.error(Error in cached-ask:, error); return c.json({ error: Failed to process your request. }, 500); } }); export default app;3.6 可观测性与监控挑战描述在分布式边缘环境中调试和监控应用程序变得更加复杂。传统的日志收集方式可能不适用需要专门的工具来追踪请求流、性能瓶颈和错误。解决方案Cloudflare LogsWorkers 的console.log()输出会被收集到 Cloudflare 的日志系统中。可以在 Cloudflare 控制台或通过日志推送服务如 Logpush 到 S3, Splunk 等查看。Cloudflare Trace Worker结合 OpenTelemetry允许开发者为 Workers 生成分布式追踪帮助理解请求在 Workers 内部和外部服务之间的流转。LangSmithLangChain 官方提供的可观测性平台可以追踪 LangChain 链的每一步执行、输入/输出、耗时和错误。自定义指标使用c.executionCtx.data.metrics(Hono 的c.executionCtx是ExecutionContext的实例) 或自定义 HTTP 头来报告关键性能指标。代码示例基本日志记录与 LangSmith 追踪import { Hono } from hono; import { OpenAI } from langchain/openai; import { PromptTemplate } from langchain/core/prompts; import { StringOutputParser } from langchain/core/output_parsers; import { CallbackManager } from langchain/core/callbacks/manager; // 用于 LangSmith const app new Hono(); type Bindings { OPENAI_API_KEY: string; LANGCHAIN_API_KEY?: string; // LangSmith API Key LANGCHAIN_TRACING_V2?: string; // true 启用 LangSmith V2 追踪 LANGCHAIN_PROJECT?: string; // LangSmith 项目名称 }; app.get(/traceable-ask, async (c) { const query c.req.query(q); if (!query) { return c.json({ error: Please provide a query parameter q. }, 400); } const { OPENAI_API_KEY, LANGCHAIN_API_KEY, LANGCHAIN_TRACING_V2, LANGCHAIN_PROJECT } c.env as Bindings; // 配置 LangSmith 回调管理器 let callbackManager: CallbackManager | undefined; if (LANGCHAIN_TRACING_V2 true LANGCHAIN_API_KEY LANGCHAIN_PROJECT) { // LangChain.js 会自动从环境变量中读取 LANGCHAIN_API_KEY, LANGCHAIN_TRACING_V2, LANGCHAIN_PROJECT // 如果需要更精细控制可以手动创建 AsyncLocalStorageCallbackManager 或 LangChainTracer // 但通常设置环境变量是最简单的方式。 console.log(LangSmith tracing enabled.); } else { console.log(LangSmith tracing not enabled. Check environment variables.); } const model new OpenAI({ temperature: 0.7, openAIApiKey: OPENAI_API_KEY, // callbackManager: callbackManager, // 如果手动创建 callbackManager }); const promptTemplate PromptTemplate.fromTemplate( 回答以下关于云计算的问题: {question} ); const chain promptTemplate.pipe(model).pipe(new StringOutputParser()); try { console.log(Processing query: ${query}); // 标准日志 const startTime Date.now(); const result await chain.invoke({ question: query }); const endTime Date.now(); console.log(Query ${query} processed in ${endTime - startTime} ms.); // 性能日志 return c.json({ query: query, answer: result }); } catch (error) { console.error(Error in traceable-ask:, error); // 错误日志 return c.json({ error: Failed to process your request. }, 500); } }); export default app;注意LangChain.js 会自动检测环境变量LANGCHAIN_API_KEY,LANGCHAIN_TRACING_V2,LANGCHAIN_PROJECT来启用 LangSmith 追踪。因此你通常只需要在wrangler.toml中设置这些secret而不需要在代码中手动创建callbackManager。3.7 安全性考虑挑战描述将 LLM 逻辑部署到边缘意味着你的 API 密钥、数据处理逻辑和用户输入都暴露在更广阔的网络中。安全性至关重要。解决方案API Key 管理绝不将 API 密钥硬编码到代码中。使用 Cloudflare Workers Secrets 来安全地存储和访问这些敏感信息。输入验证与消毒对所有用户输入进行严格的验证和消毒以防止注入攻击如 Prompt Injection。输出过滤对 LLM 的输出进行审查确保不包含敏感信息、恶意代码或不当内容。速率限制使用 Cloudflare 的内置速率限制功能保护你的 Workers 和上游 LLM API 免受滥用和 DDoS 攻击。最小权限原则如果 Workers 需要访问其他服务如 KV, D1只授予它完成任务所需的最小权限。HTTPSWorkers 自动使用 HTTPS确保数据传输加密。代码示例使用 Workers Secrets在wrangler.toml中定义 secrets# ... [[kv_namespaces]] binding CHAT_HISTORY id YOUR_KV_NAMESPACE_ID # ... # 部署时设置wrangler secret put OPENAI_API_KEY # 部署时设置wrangler secret put LANGCHAIN_API_KEY # 部署时设置wrangler secret put LANGCHAIN_PROJECT在 Worker 代码中通过c.env.YOUR_SECRET_NAME访问。// ... type Bindings { OPENAI_API_KEY: string; // TypeScript 类型定义确保类型安全 LANGCHAIN_API_KEY?: string; LANGCHAIN_TRACING_V2?: string; LANGCHAIN_PROJECT?: string; CHAT_HISTORY: KVNamespace; }; app.get(/secure-ask, async (c) { // ... const { OPENAI_API_KEY } c.env as Bindings; // 安全访问 API Key // ... }); // ...3.8 架构模式在Workers上构建LangChain应用的策略根据不同的需求和复杂性LangChain 在 Workers 上可以采用多种架构模式3.8.1 模式一简单的Prompt代理/LLM Wrapper描述Workers 作为 LLM API 的轻量级代理。它接收用户请求应用简单的提示模板调用 LLM然后返回响应。这种模式适用于简单的问答、内容生成等。优点最简单资源消耗最小延迟最低。缺点无法处理复杂逻辑、状态管理、外部数据检索。3.8.2 模式二边缘增强的RAG (Retrieval Augmented Generation)描述Workers 不仅调用 LLM还负责从边缘存储如 Cloudflare KV, D1, R2或外部检索服务中获取相关上下文然后将其与用户查询一起发送给 LLM。优点能够利用私有数据或实时数据增强 LLM 的知识。挑战检索过程的性能、数据同步、存储选择。代码示例简化的RAG (从KV检索)import { Hono } from hono; import { OpenAI } from langchain/openai; import { PromptTemplate } from langchain/core/prompts; import { StringOutputParser } from langchain/core/output_parsers; const app new Hono(); type Bindings { DOC_STORE: KVNamespace; // 用于存储文档片段的 KV 命名空间 OPENAI_API_KEY: string; }; // 预加载一些模拟文档片段到 KV。实际应用中这会通过后台进程或上传工具完成。 // 假设 KV 中有键值对 {doc-ai-def: 人工智能是..., doc-ml-def: 机器学习是...} app.get(/rag-ask, async (c) { const query c.req.query(q); if (!query) { return c.json({ error: Please provide a query parameter q. }, 400); } const { DOC_STORE, OPENAI_API_KEY } c.env as Bindings; // 1. 模拟检索根据查询关键词从 KV 检索相关文档片段 // 实际的检索会更复杂可能涉及嵌入向量搜索等 let context ; if (query.toLowerCase().includes(ai)) { context await DOC_STORE.get(doc-ai-def) || ; } if (query.toLowerCase().includes(机器学习)) { context await DOC_STORE.get(doc-ml-def) || ; } if (query.toLowerCase().includes(cloudflare)) { context await DOC_STORE.get(doc-cf-workers) || ; // 假设有这个文档 } // 2. 初始化 LLM const model new OpenAI({ temperature: 0.5, openAIApiKey: OPENAI_API_KEY, }); // 3. 定义提示模板包含检索到的上下文 const promptTemplate PromptTemplate.fromTemplate( 你是一个知识渊博的助手。请根据以下提供的背景信息回答问题。如果背景信息不足请礼貌地指出。 背景信息: {context} 问题: {question} ); // 4. 构建链 const chain promptTemplate.pipe(model).pipe(new StringOutputParser()); try { // 5. 执行链 const result await chain.invoke({ context: context || 没有找到相关的背景信息。, question: query }); return c.json({ query: query, context: context, answer: result }); } catch (error) { console.error(Error in RAG-ask:, error); return c.json({ error: Failed to process your request. }, 500); } }); export default app;3.8.3 模式三带工具调用的代理 (Agent with Tool Calling)描述Workers 接收用户请求LLM 根据请求决定调用哪个工具例如一个搜索 API、一个外部计算器 API、一个数据库查询工具。Workers 负责执行工具调用并将结果返回给 LLM 进行最终响应的生成。优点赋予 LLM 更强的能力使其能够与外部世界交互。挑战复杂性高需要仔细设计工具接口和错误处理。3.8.4 模式四有状态的对话机器人 (Stateful Conversational AI)描述结合 Durable Objects 或 KV/D1 来管理对话历史使得 LLM 能够在多轮对话中保持上下文。优点能够创建更自然、更连贯的对话体验。挑战状态同步、持久化存储的性能和成本。4. 实践中的考量与进阶话题4.1 Cloudflare Workers AI未来集成Cloudflare 推出了 Workers AI允许开发者在 Workers 边缘网络上直接运行开源的 AI 模型如 Llama 2、Stable Diffusion 等而无需调用外部 API。这对于轻量级推理任务如文本嵌入、文本生成、图像生成具有颠覆性的意义因为它将消除外部 LLM API 的网络延迟和成本。未来趋势将 LangChain 逻辑与 Workers AI 模型结合实现更低延迟、更私有的边缘 AI 应用。例如可以在 Workers AI 上生成嵌入向量然后用于 RAG 检索再将检索结果发送给外部 LLM 或 Workers AI 的文本生成模型。4.2 WebAssembly (WASM) 的潜力对于某些计算密集型但又需要严格资源控制的任务可以将核心逻辑编译成 WebAssembly 模块并在 Workers 中运行。例如如果有一个自定义的文本处理算法或轻量级向量操作用 Rust 或 C 实现并编译为 WASM可能比纯 JavaScript 更高效。4.3 构建强大的微服务架构Workers LangChain 可以作为更大微服务架构中的一个智能组件。例如一个 Workers 处理用户界面交互和 LLM 协调而其他的 Workers 或外部服务处理数据存储、身份验证、更复杂的业务逻辑。5. 总结与展望在 Cloudflare Workers 上运行轻量级 LangChain 逻辑是一项充满挑战但极具潜力的工程实践。通过深入理解边缘计算的限制、拥抱langchain.js、巧妙利用 Cloudflare 提供的持久化存储和优化工具我们能够构建出响应迅速、可伸缩、成本效益高的大语言模型应用。随着 Cloudflare Workers AI 等新技术的不断发展边缘智能的边界将持续拓宽为开发者带来更多创新的可能性。