写点什么

手把手教你在 JavaScript 中使用 LangChain,解锁 AI 应用能力

作者 | Matt Nikonorov

  • 2023-11-29
    北京
  • 本文字数:9102 字

    阅读完需:约 30 分钟

手把手教你在JavaScript中使用LangChain,解锁AI应用能力

JS 版的 LangChain,是一个功能丰富的 JavaScript 框架。不管你是开发者还是研究人员都可以利用该框架通过创建语言分析模型和 Agents 来开展各项实验。该框架还提供了十分丰富的功能设置,基于这些功能设置,NLP 爱好者可以通过构建自定义模型来提高文本数据的处理效率。与此同时,作为一个 JS 框架,开发人员可以轻松的将他们的 AI 应用集成到自己的 Web 应用中。


环境准备


安装下面的步骤,我们创建一个新目录并且安装 LangChain 的 npm 包:


1.执行如下命令,安装 LangChain 的 npm 包


npm install -S langchain
复制代码


2.在目录下面创建一个以.mjs 为后缀的文件(例如:test1.mjs)


Agents(智体)


在 LangChain 中,一个 Agent 代表的是一个具备理解和生成文本能力的实例。通过给这些 Agent 设置特定行为和数据源,就可以训练他们执行各种与语言相关的任务,从而使他们具备为更多的应用提供服务的能力。


创建 LangChain 的 Agent


利用 LangChain 框架创建的 Agent 在数据获取和响应优化上都支持“工具”的配置。请看下面的示例代码。该例中,Agent 体使用 Serp API(一个网络搜索 API)在互联网上搜索与输入内容相关的信息,然后根据搜索得到的内容完成响应数据的生成,与此同时,它还使用 llm-math 工具来执行诸如 转换单位、百分比对比等 数学运算任务。


// langchain 智能体引入import { initializeAgentExecutorWithOptions } from "langchain/agents";// 引入语言模型:OpenAiimport { ChatOpenAI } from "langchain/chat_models/openai";// 引入网络搜索工具import { SerpAPI } from "langchain/tools";// 引入计算函数 工具import { Calculator } from "langchain/tools/calculator";// OpenAI 的 API 访问的密钥process.env["OPENAI_API_KEY"] = "YOUR_OPENAI_KEY"// SerpAPI 访问密钥process.env["SERPAPI_API_KEY"] = "YOUR_SERPAPI_KEY"// 创建工具链const tools = [new Calculator(), new SerpAPI()];// 模型配置,这里用的是 OpenAI gpt-3.5-turboconst model = new ChatOpenAI({ modelName: "gpt-3.5-turbo", temperature: 0 });// 智能体初始化const executor = await initializeAgentExecutorWithOptions(tools, model, {  agentType: "openai-functions",  verbose: false,});// 执行,这里给出的问题是:"通过搜索互联网,找出自 2010 年以来 Boldy James 发行了多少张专辑,以及 Nas 自 2010 年以来发行了多少张专辑?找出谁发行了更多的专辑,并显示百分比的差异。"const result = await executor.run("By searching the Internet, find how many albums has Boldy James dropped since 2010 and how many albums has Nas dropped since 2010? Find who dropped more albums and show the difference in percent.");console.log(result);
复制代码


上述代码,在模型创建之后,通过 initializeAgentExecutorWithOptions 函数将模型和工具(SerpAPI 和 Calculator)进行合并,生成了一个 executor(执行者)。在输入端,我们要求 LLM(大语言模型) 通过搜索 Internet(使用 SerpAPI),找出自 2010 年以来,Nas 和 Boldy James 这两位艺术家中谁发行了更多专辑,并技术差值百分比(使用计算器)。


在该例子中,我通过明确地告诉 LLM“通过搜索互联网…”,以使它通过互联网获取最新数据,而不使用 OpenAI 的的默认数据(该数据截止 2021 年),从而得出正确答案。


译者注:OpenAI 于 2023 年 11 月 2 日发布会上,表示其模型数据已经更新到了 2023 年 4 月。


下面是上面代码的输出:


> node test1.mjs从 2010 年至今,Boldy James 发了 4 张专辑,Nas 发了 17 张因此,Nas 比 Boldy James 发行的专辑要多,两者发行专辑的差值是 13我们将使用如下公式:(差值 / 总值)*100,来计算差值百分比在这里,差值是 13,总值是 17因此差值百分比是:(13/17)*100 = 76.47%所以,从 2010 年至今,Nas 发布的专辑比 Boldy James 多了 76。47%
复制代码


模型(Models)


LangChain 中支持三种类型的模型使用方式:


  1. LLM(大语言模型)

  2. Chat Model(对话模型)

  3. Embeddings(Embeddings 技术是一种将高纬数据转为低维数据的技术)


下面通过示例,我们一起来了解这三种模型的使用。


语言模型


LangChain 为 JavaScript 提供了使用语言模型能力,通过该能力 JS 可以根据文本输出生成文本输出。它不像聊天模型那么复杂,最适合处理简单的输入 - 输出的语言任务。下面是一个基于 OpenAI 模型的代码示例:


import { OpenAI } from "langchain/llms/openai";const llm = new OpenAI({  openAIApiKey: "你自己的 OpenAI 的密钥",  model: "gpt-3.5-turbo",  temperature: 0});const res = await llm.call("List all red berries");console.log(res);
复制代码


如你所见,该例是要求 OpenAI 的 gpt-3.5-turbo 模型罗列所有的红色浆果。其中,我将 temperature 设为 0,其目的是为了确保 LLM 输出结果的准确性。下面是输出的结果:


1. Strawberries2. Cranberries3. Raspberries4. Redcurrants5. Red Gooseberries6. Red Elderberries7. Red Huckleberries8. Red Mulberries
复制代码


对话模型


如果你需要更复杂的答案和对话,则需要使用对话模型。对话模型在技术上与语言模型有何不同?好吧,用 LangChain 官方文档 的话来说:


对话模型是语言模型的一个变体。虽然对话模型在底层使用的依然是大语言模型,但是他们在接口上略有不同。对话模型没有使用“文本输入、文本输出”格式的 API,而是使用了一个基于“聊天消息”来实现输入输出的接口。


下面是一个简单的 JavaScript 对话模型脚本(该示例相当无用但很有趣)。


import { ChatOpenAI } from "langchain/chat_models/openai";import { PromptTemplate } from "langchain/prompts";// 创建对话,配置密钥、模型版本、和 temperatureconst chat = new ChatOpenAI({  openAIApiKey: "YOUR_OPENAI_KEY",  model: "gpt-3.5-turbo",  temperature: 0});// 通过提示词模版,创建提示词const prompt = PromptTemplate.fromTemplate(`你现在扮演一个诗人的角色,在回答时请保持语言的韵律: {question}`);const runnable = prompt.pipe(chat);// 对话执行const response = await runnable.invoke({ question: "Djokovic, Federer 和 Nadal,三人中谁是最好的网球运动员?" });console.log(response);
复制代码


如你所见,上面的代码首先发送了一条系统消息给对话机器人,告诉它,当前扮演的是一个诗人角色,且在回答的时候要始终使用押韵的方式。然后再向对话机器人发送一条用户消息,让它给出 Djokovic、Federer 和 Nadal 这三人中,谁是最好的网球运动员。如果你运行这个脚本,将会看到如下内容:


// AI 消息体AIMessage.content:'In the realm of tennis, they all shine bright,\n' +'Djokovic, Federer, and Nadal, a glorious sight.\n' +'Each with their unique style and skill,\n' +'Choosing the best is a difficult thrill.\n' +'\n' +'Djokovic, the Serb, a master of precision,\n' +'With agility and focus, he plays with decision.\n' +'His powerful strokes and relentless drive,\n' +"Make him a force that's hard to survive.\n" +'\n' +'Federer, the Swiss maestro, a true artist,\n' +'Graceful and elegant, his game is the smartest.\n' +'His smooth technique and magical touch,\n' +'Leave spectators in awe, oh so much.\n' +'\n' +'Nadal, the Spaniard, a warrior on clay,\n' +'His fierce determination keeps opponents at bay.\n' +'With his relentless power and never-ending fight,\n' +'He conquers the court, with all his might.\n' +'\n' +"So, who is better? It's a question of taste,\n" +"Each player's greatness cannot be erased.\n" +"In the end, it's the love for the game we share,\n" +'That makes them all champions, beyond compare.'
复制代码


译注:这是一首诗,实在翻译不来,就不翻译了哈。


Embeddings


Embeddings 支持将文本数据转换为向量数据,以便于和其他相关的内容进行关联。这可能听起来有点抽象,让我们直接来看一个例子:


import { OpenAIEmbeddings } from "langchain/embeddings/openai";
process.env["OPENAI_API_KEY"] = "YOUR_OPENAI_KEY"
const embeddings = new OpenAIEmbeddings();
const res = await embeddings.embedQuery("谁是万维网之父?");console.log(res)
复制代码


这里是数据返回,是一大串的浮点数据:


[  0.02274114,  -0.012759142,   0.004794503,  -0.009431809,    0.01085313,  0.0019698727,  -0.013649924,   0.014933698, -0.0038185727,  -0.025400387,  0.010794181,   0.018680222,   0.020042595,   0.004303263,   0.019937797,  0.011226473,   0.009268062,   0.016125774,  0.0116391145, -0.0061765253,  -0.0073358514, 0.00021696436,   0.004896026,  0.0034026562,  -0.018365828,  ... 1501 more items]
复制代码


这就是 Embeddings 的形态。仅仅是为了六个单词,就用了那么多浮点数!利用 Embeddings 技术,可以将输入文本与潜在答案、相关文本、名称等进行关联。


下面让我们来看一个 Embeddings 模型的一个使用案例


在下面的脚本中,我们向模型提问:“世界上最重的动物是什么?”。然后我们借助 Embeddings 技术让模型能从我们提供的参考答案中找出最佳答案。


import { OpenAIEmbeddings } from "langchain/embeddings/openai";
process.env["OPENAI_API_KEY"] = "YOUR_OPENAI_KEY"
const embeddings = new OpenAIEmbeddings();// 余弦相似度函数function cosinesim(A, B) { var dotproduct = 0; var mA = 0; var mB = 0;
for(var i = 0; i < A.length; i++) { dotproduct += A[i] * B[i]; mA += A[i] * A[i]; mB += B[i] * B[i]; }
mA = Math.sqrt(mA); mB = Math.sqrt(mB); var similarity = dotproduct / (mA * mB);
return similarity;}// 嵌入 1:蓝鲸是世界上最重的动物const res1 = await embeddings.embedQuery("The Blue Whale is the heaviest animal in the world");// 嵌入 2:乔治·奥威尔写了《一九八四》这本书const res2 = await embeddings.embedQuery("George Orwell wrote 1984");// 嵌入 3:随机内容const res3 = await embeddings.embedQuery("Random stuff");// 源内容数组const text_arr = ["The Blue Whale is the heaviest animal in the world", "George Orwell wrote 1984", "Random stuff"]// 利用 embeddings 转换之后的数据数组const res_arr = [res1, res2, res3]// 问题:世界上最重的动物是什么?const question = await embeddings.embedQuery("What is the heaviest animal?");// 相似度数组const sims = []for (var i=0;i<res_arr.length;i++){ // 这里利用 cosinesim 函数,计算问题和每个答案的相识度 sims.push(cosinesim(question, res_arr[i]))}// 给数组挂载求最大值的函数 (数组本身不具备, 通过原型赋予)Array.prototype.max = function() { return Math.max.apply(null, this);};// 输出相识度最大的 结果console.log(text_arr[sims.indexOf(sims.max())])
复制代码


在上面的代码中,先定义了一个计算相识度的函数:cosinesim(A, B),其次利用 embeddings 技术将每个答案转换为了向量数据,接着使用 cosinesim 函数计算出了每个答案和输入问题的相识度值,最高拿到相识度最高的答案,完成输出。下面是输出的结果:


The Blue Whale is the heaviest animal in the world// 蓝鲸是世界上最重的动物
复制代码


Chunks(数据块)


由于 LangChain 模型在生产响应的时候不支持大文本的输入。所以需要用到诸如文本分割等数据分块的技术将大文本数据分割成多个 Chunk。下面我向你演示 LangChain 中两种简单的文本数据分割方法,以实现大文本输入。


方法一、CharacterTextSplitter


为了避免分割之后,Chunk 中内容中断,可以使用换行符来进行文本拆分,该方法是在每次出现换行符时执行分割,可以通过 CharacterTextSplitter 来实现,示例代码如下:


import { Document } from "langchain/document";import { CharacterTextSplitter } from "langchain/text_splitter";// 创建一个分割器,使用换行符进行分割,每个区块的大小是 7,区块的重叠度是 3const splitter = new CharacterTextSplitter({  separator: "\n",  chunkSize: 7,  chunkOverlap: 3,});const output = await splitter.createDocuments([your_text]);
复制代码


这是拆分文本的一种有用的方法,同时,你可以使用任何字符作为 Chunk 的分隔符,而不仅仅是换行符(\n)


方法二、RecursiveCharacterTextSplitter


如果要严格按一定长度的字符拆分文本,可以使用 RecursiveCharacterTextSplitter 来实现,示例代码如下:


import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";const splitter = new RecursiveCharacterTextSplitter({  // chunk 的大小  chunkSize: 100,  // chunk 的重叠度  chunkOverlap: 15,});const output = await splitter.createDocuments([your_text]);
复制代码


在此示例中,会将文本按照每 100 个字符进行一次拆分,每个 Chunk 的重叠度为 15 个字符。


Chunk 的大小和重叠度


通过上面的示例,想必你已经迫不及待的想知道 Chunk 的大小和重叠度这两个参数确切的含义以及它们对性能的影响了吧。下面我简单从两方面解释下:


  • chunkSize 决定了每个 Chunk 中的字符数量。chunkSize 的值越大,那么 Chunk 中的字符数就越多,LangChain 处理该 Chunk 和产生对应输出所需的时间就越长,反之亦然。

  • chunkOverlap 是用于设置了每个 Chunk 之间共享上下文的大小。chunkOverlap 的值越高,Chunk 的冗余度就越高 ;chunkOverlap 的值越低,Chunk 之间共享的上下文就越少。通常将 chunkOverlap 设置在 Chunk 大小的 10% 到 20% 之间会比较理想,当然,真正理想 chunkOverlap 值还是要根据不同的文本类型和使用场景来确定。


Chains(模型链)


通过单个 LLM 的输入输出是无法完成一些更为复杂的任务,因此需要利用 Chains,通过将多个 LLM 的功能链接一起来完成。下面是一个很有意思的例子:


import { ChatPromptTemplate } from "langchain/prompts";import { LLMChain } from "langchain/chains";import { ChatOpenAI } from "langchain/chat_models/openai";
process.env["OPENAI_API_KEY"] = "YOUR_OPENAI_KEY"
// 这是一段知识库const wiki_text = `Alexander Stanislavovich 'Sasha' Bublik (Александр Станиславович Бублик; born 17 June 1997) is a Kazakhstani professional tennis player. He has been ranked as high as world No. 25 in singles by the Association of Tennis Professionals (ATP), which he achieved in July 2023, and is the current Kazakhstani No. 1 player...
Alexander Stanislavovich Bublik was born on 17 June 1997 in Gatchina, Russia and began playing tennis at the age of four. He was coached by his father, Stanislav. On the junior tour, Bublik reached a career-high ranking of No. 19 and won eleven titles (six singles and five doubles) on the International Tennis Federation (ITF) junior circuit.[4][5]...`const chat = new ChatOpenAI({ temperature: 0 });const chatPrompt = ChatPromptTemplate.fromMessages([ [ "system", "You are a helpful assistant that {action} the provided text", ], ["human", "{text}"],]);// 这里将 2 个模型进行了链接const chainB = new LLMChain({ prompt: chatPrompt, llm: chat,});
const resB = await chainB.call({ action: "lists all important numbers from", text: wiki_text,});console.log({ resB });
复制代码


在上面的代码中,我在提示词中设置了一个变量,同时通过将 LLM 的 temperature 设置为 0,以要求 LLM 给出一个基于事实的回答。该例中,我要求 LLM 基于给定的简短知识库,输出我最喜欢网球运动员的关键数据。以下是 LLM 给出的回答:


{  resB: {    text: 'Important numbers from the provided text:\n' +      '\n' +      "- Alexander Stanislavovich 'Sasha' Bublik's date of birth: 17 June 1997\n" +      "- Bublik's highest singles ranking: world No. 25\n" +      "- Bublik's highest doubles ranking: world No. 47\n" +      "- Bublik's career ATP Tour singles titles: 3\n" +      "- Bublik's career ATP Tour singles runner-up finishes: 6\n" +      "- Bublik's height: 1.96 m (6 ft 5 in)\n" +      "- Bublik's number of aces served in the 2021 ATP Tour season: unknown\n" +      "- Bublik's junior tour ranking: No. 19\n" +      "- Bublik's junior tour titles: 11 (6 singles and 5 doubles)\n" +      "- Bublik's previous citizenship: Russia\n" +      "- Bublik's current citizenship: Kazakhstan\n" +      "- Bublik's role in the Levitov Chess Wizards team: reserve member"  }}
复制代码


很酷,但这还没有真正展示 Chains 的全部能力。再看一个更实际的例子:


import { z } from "zod";import { zodToJsonSchema } from "zod-to-json-schema";import { ChatOpenAI } from "langchain/chat_models/openai";import {  ChatPromptTemplate,  SystemMessagePromptTemplate,  HumanMessagePromptTemplate,} from "langchain/prompts";import { JsonOutputFunctionsParser } from "langchain/output_parsers";process.env["OPENAI_API_KEY"] = "YOUR_OPENAI_KEY"const zodSchema = z.object({  albums: z    .array(      z.object({        name: z.string().describe("The name of the album"),        artist: z.string().describe("The artist(s) that made the album"),        length: z.number().describe("The length of the album in minutes"),        genre: z.string().optional().describe("The genre of the album"),      })    )    .describe("An array of music albums mentioned in the text"),});const prompt = new ChatPromptTemplate({  promptMessages: [    SystemMessagePromptTemplate.fromTemplate(      "List all music albums mentioned in the following text."    ),    HumanMessagePromptTemplate.fromTemplate("{inputText}"),  ],  inputVariables: ["inputText"],});const llm = new ChatOpenAI({ modelName: "gpt-3.5-turbo", temperature: 0 });const functionCallingModel = llm.bind({  functions: [    {      name: "output_formatter",      description: "Should always be used to properly format output",      parameters: zodToJsonSchema(zodSchema),    },  ],  function_call: { name: "output_formatter" },});const outputParser = new JsonOutputFunctionsParser();const chain = prompt.pipe(functionCallingModel).pipe(outputParser);const response = await chain.invoke({  inputText: "My favorite albums are: 2001, To Pimp a Butterfly and Led Zeppelin IV",});console.log(JSON.stringify(response, null, 2));
复制代码


此脚本通过读取输入的文本信息,识别所有提到的音乐专辑以及将每张专辑的名称、艺术家、长度和流派,最后将所有数据转换为 JSON 格式进行输出。以下是输入“我最喜欢的专辑是:2001 年、To Pimp a Butterfly 和 Led Zeppelin IV”的输出:


{  "albums": [    {      "name": "2001",      "artist": "Dr. Dre",      "length": 68,      "genre": "Hip Hop"    },    {      "name": "To Pimp a Butterfly",      "artist": "Kendrick Lamar",      "length": 79,      "genre": "Hip Hop"    },    {      "name": "Led Zeppelin IV",      "artist": "Led Zeppelin",      "length": 42,      "genre": "Rock"    }  ]}
复制代码


虽然这只是一个有趣的例子,但通过该技术可以将非结构化的文本数据转为结构化的数据,从而使用在其他应用系统中。


不止 OpenAI


尽管在演示 LangChain 不同功能的示例中,我一直都是使用 OpenAI 模型。但其实 LangChain 并不局限于 OpenAI 模型。你可以将 LangChain 与许多其他 LLM 和 AI 服务一起使用。在 LangChain 的官方文档中可以找到 LangChain 的 JS 版本所支持集成的完整 LLM 列表。


例如,你可以将 Cohere 与 LangChain 一起使用。再使用 npm install cohere-ai 安装 Cohere 之后,你就可以像下面示例代码一样,使用 LangChain 和 Cohere 编写一个简单的问答脚本:


import { Cohere } from "langchain/llms/cohere";const model = new Cohere({  maxTokens: 50,  apiKey: "YOUR_COHERE_KEY", // In Node.js defaults to process.env.COHERE_API_KEY});const res = await model.call(  "Come up with a name for a new Nas album" // 给 Nas 的新专辑起个名字);console.log({ res });
复制代码


输出的结果如下:


{  res: ' Here are a few possible names for a new Nas album:\n' +    '\n' +    "- King's Landing\n" +    "- God's Son: The Sequel\n" +    "- Street's Disciple\n" +    '- Izzy Free\n' +    '- Nas and the Illmatic Flow\n' +    '\n' +    'Do any'}
复制代码


总结


读完本篇文章,相信你已经对 JS 版的 LangChain 各方面能力都有所了解了。现在你可以通过 LangChain 用 JS 开发各种基于 AI 的应用和体验 LLM 了。当然,也请你必参考 LangChainJS 的官方文档,以了解更多有关特定功能的详细信息。


最后,预祝你在 JavaScript 中愉快的使用 LangChain 进行编码和体验!如果你喜欢这篇文章,你可能还想阅读如何在 Python 中使用 LangChain 这篇文章:


https://www.sitepoint.com/langchain-python-complete-guide/


原文链接:


https://www.sitepoint.com/langchain-javascript-complete-guide/


相关阅读:

LangChain 的问题所在

OpenAI 用 45 分钟重塑游戏规则!干掉 MJ、LangChain,创造“不会编程的应用开发者”新职业

LangChain:2023 年最潮大语言模型 Web 开发框架

理论 + 实践详解最热的 LLM 应用框架 LangChain

2023-11-29 13:585677

评论

发布
暂无评论
发现更多内容

神经网络极简入门

EquatorCoco

人工智能 神经网络 网络协议

AI 001 号员工通义灵码入职阿里云丨阿里云云原生 4 月产品月报

阿里巴巴云原生

阿里云 AI 云原生

破茧成蝶,展翅翱翔——行云乔迁,再启新程!

行云管家

行云管家 行云 乔迁

怎样选择IT外包公司?需要注意什么?

Ogcloud

IT IT外包 IT外包公司 IT外包服务 IT外包服务商

企业怎样进行IT外包以及IT外包服务内容

Ogcloud

IT IT外包 IT外包公司 IT外包服务 IT外包服务商

SQL事前巡检插件

京东零售技术

sql 企业号 5 月 PK 榜 #SQL

GLM国产大模型训练加速:高效性能与成本优化的实践

百度开发者中心

人工智能 深度学习 大模型

生成式AI的「七宗罪」!

白洞计划

AI

阿里巴巴中国站关键字搜索API返回值全攻略:精准定位所需商品

技术冰糖葫芦

API 编排 API boy API】 pinduoduo API

观测云 VS ELK:谁是日志监控的王者?

观测云

ELK 日志分析

AI绘图新选择:Fooocus工具发布,小显存助力大模型运行

百度开发者中心

人工智能 Ai绘图

Sermant在异地多活场景下的实践

华为云开源

开源 微服务 云原生 服务治理 sermant

大营销抽奖系统,DDD开发要如何建模?

不在线第一只蜗牛

财务世界中数据叙事对于企业决策的影响力

智达方通

企业管理 全面预算 财务管理 数据叙事

Jira Server 不维护了,如何将 Jira 平滑迁移到阿里云云效

阿里巴巴云原生

阿里云 云原生 云效

Jira Server 不维护了,如何将 Jira 平滑迁移到阿里云云效

阿里云云效

阿里云 云原生 云效

京东JD商品详情API返回值揭秘:精准掌握商品信息

技术冰糖葫芦

API 编排 API boy API 策略 pinduoduo API

营销权益平台春晚技术探究| 京东云技术团队

京东科技开发者

博睿数据将出席ClickHouse Hangzhou User Group第1届 Meetup

博睿数据

开发体商融合:提升体育赛事直播平台发展新路径

软件开发-梦幻运营部

企业IT架构治理之道| 京东云技术团队

京东科技开发者

通义灵码实战系列:一个新项目如何快速启动,如何维护遗留系统代码库?

阿里云云效

阿里云 云原生 通义灵码

CCE云原生混部场景下的测试案例

不在线第一只蜗牛

云原生

生成式AI的「七宗罪」!

脑极体

AI

软件测试学习笔记丨后端接口开发 - MyBatis 增删改查

测试人

软件测试

Redis Cluster on K8s 大揭密

小猿姐

数据库 redis k8s

通义灵码实战系列:一个新项目如何快速启动,如何维护遗留系统代码库?

阿里巴巴云原生

阿里云 云原生 通义灵码

面试官:核心线程数为0时,线程池如何执行?

王磊

Java 面试

教你用Perl实现Smgp协议

快乐非自愿限量之名

Linux 电信

Databend 开源周报第 143 期

Databend

手把手教你在JavaScript中使用LangChain,解锁AI应用能力_生成式 AI_InfoQ精选文章