写给自己的 LangChain 开发教程(一):Hello world & 历史记录

这个教程是一边学习一边写的,中间可能会出现一些疏漏或者错误,如果您看到该篇文章并且发现了问题,还请多多指教!

0. 前置工作

a. 本地部署大模型

使用 Lang chain 开发大模型相关的应用,首先要拥有调用大模型的能力。我们可以在各个大模型的官网购买调用额度,获取到对应的API KEY ,比如 OPEN AI ;我们还可以选择在本地部署开源的大模型,这样我们只需要拥有一个较大内存的机器,就可以免费的无限调用大模型能力。

几乎所有的开源大模型都在官网有一套自己的部署教程,但是各个模型的部署步骤可能略有差异。ollama 是一个流行的开源的大模型管理工具,我们可以使用它来部署市面上开源的大模型,而不需要自己去一个个查阅部署文档。我们打开 ollama官网来下载对应系统的版本。下载并启动之后我们在命令行输入 ollama -v 来测试是否运行,确认运行之后我们可以访问本地的11434端口(http://localhost:11434/)来查看。

借助 ollama ,我们可以便捷的部署想要的模型,我们在官网的 models tab 下查看支持的模型,在命令行运行 ollama pull 模型名称 来拉取大模型。我们根据自己的设备来选择合适的大模型,这里我们选择 qwen3:32b (运行所需的内存和模型本身的大小差不多,16g 内存的话选择 14b 及以下),运行 ollama run qwen3:32b 命令,它会自动帮我们部署并运行。

当我们在命令行运行 ollama run 命令之后,命令行会变成大模型的交互界面, 我们可以输入一些内容来测试。输入 /bye 可以退出。

同时 ollama 会在11434端口提供一套 restful 的 api 用来调用大模型的功能,具体的接口定义我们可以参考官方文档。到这一步我们已经拥有了部署大模型和通过接口调用大模型的能力,已经可以做一些对大模型的应用了,我们可以通过自己的后端服务来高度定制化的开发自己想要的功能,比如智能客服,知识库等等。

b. 初识 LangChain

LangChain 是一个为开发大模型应用设计的框架,它提供了部分封装好的组件,并且编排流程非常容易,和名字一样,它可以让每一个抽象模块(例如)链接接起来。它提供开发的 Python SDK 和 JS/TS SDK。

1. Hello world

a. 和大模型对话

使用 pnpm add @langchain/core @langchain/community langchain @langchain/ollama 来下载开发所需的包之后,我们来尝试初始化我们的脚本。

js 复制代码
// test.js
import { ChatOllama } from "@langchain/ollama";
​
const llm = new ChatOllama({
  model: 'qwen3:32b'
})
​
const result = await llm.invoke('你是谁')
​
console.log(result)
swift 复制代码
AIMessage {
  "content": "<think>\n嗯,用户问"你是谁",我需要先确认他们想知道的是什么。可能他们想了解我的身份、功能或者开发背景。首先,我应该按照要求用中文回答,保持口语化,避免使用专业术语或格式。用户可能希望得到简洁明了的回答,同时包含关键信息,比如我是通义千问,由通义实验室研发,基于大量数据训练,能够回答问题、创作文字等。还要提到我的中文名和英文名,以及多语言支持。需要确保回答自然,不使用Markdown,分段落但不用标题。另外,用户可能有后续问题,比如具体功能或使用场景,所以回答要留有余地,方便进一步交流。检查是否有遗漏的重要信息,比如我的训练数据截止时间,但用户没问到,可能暂时不需要提。最后,保持友好和专业的语气,让用户感觉亲切可信。\n</think>\n\n你好!我是通义千问,由通义实验室研发的超大规模语言模型。我的中文名是通义千问,英文名是Qwen,能够回答问题、创作文字,比如写故事、写公文、写邮件、写剧本、逻辑推理、编程等等,还能表达观点,玩游戏等。我支持多种语言,包括但不限于中文、英文、德语、法语、西班牙语等。有什么我可以帮你的吗?",
  "additional_kwargs": {},
  "response_metadata": {
    "model": "qwen3:32b",
    "created_at": "2025-08-22T02:11:11.461663Z",
    "done_reason": "stop",
    "done": true,
    "total_duration": 68058281459,
    "load_duration": 21474202125,
    "prompt_eval_count": 10,
    "prompt_eval_duration": 19185655791,
    "eval_count": 284,
    "eval_duration": 27385923000
  },
  "tool_calls": [],
  "invalid_tool_calls": [],
  "usage_metadata": {
    "input_tokens": 10,
    "output_tokens": 284,
    "total_tokens": 294
  }
}

运行这个脚本,在等待大模型调用输出之后我们可以看到控制台打印了整个的输出结构,不过我们在使用的时候通常会采用流式输出的方式来获取结果。

js 复制代码
// test.js
// const result = await llm.invoke('你是谁')
const stream = await llm.stream('你是谁')
​
// console.log(result)
for await (const chunk of stream) {
  console.log(chunk.content)
}

运行脚本后我们可以看到终端里的响应很快,并且是一块一块打印出结果的。

大模型的调用中可以接收不同格式的提示词参数,我们直接传入字符串相当于给大模型传递了类似于 ['user', '你是谁'] 这样一条信息。如果我们想传递多条信息,就可以用数组的方式来组织,例如这样。

js 复制代码
// test.js
import { AIMessage, HumanMessage } from "@langchain/core/messages";
import { ChatOllama } from "@langchain/ollama";
​
const llm = new ChatOllama({
  model: 'qwen3:32b'
})
​
const messages = [
  new HumanMessage('你是谁'),
  new AIMessage('我是Qwen3'),
  new HumanMessage('你可以做什么')
]
​
// 等同于如下代码
// const messages = [
//   ['user', '你是谁'],
//   ['ai', '我是Qwen3'],
//   ['user', '你可以做什么']
// ]
​
const result = await llm.invoke(messages)
​
console.log(result)
swift 复制代码
​
AIMessage {
  "content": "<think>\n好的,我现在需要处理用户的问题:"你可以做什么"。首先,我需要回顾之前的对话历史,用户之前问过"你是谁",我回答了自己是Qwen3。现在用户进一步询问我的功能,我需要详细但简洁地列出我的能力。\n\n首先,用户可能想知道我的主要功能,比如回答问题、创作文字、编程、逻辑推理等。我需要确保覆盖这些方面,同时提到多语言支持和代码写作,因为这对国际用户来说很重要。\n\n接下来,用户可能有更深层次的需求,比如他们可能想用我来完成具体的任务,比如写故事、写邮件,或者解决编程问题。我需要举例说明这些应用场景,让用户更容易理解我的能力。\n\n另外,用户可能关心我的多模态能力,比如处理图片、语音等,但根据之前的回答,Qwen3主要专注于文本处理,所以可能不需要提到这些,除非有相关更新。需要确认当前版本的功能,避免提供错误信息。\n\n还要注意用户可能的潜在需求,比如他们可能希望我能够进行复杂的逻辑推理或数据分析,所以需要提到这些方面。同时,保持回答友好和鼓励用户提问,促进进一步的互动。\n\n最后,确保回答结构清晰,分点列出,使用简洁的标题和项目符号,让用户一目了然。避免使用过于技术化的术语,保持口语化,让不同背景的用户都能理解。\n</think>\n\n嘿!很高兴你问我这个问题~ 🌟 作为Qwen3,我可以帮你做很多事情呢!\n\n**我的主要能力包括:**\n1. **知识问答** - 无论是科学、文化、历史还是日常生活问题,我都可以提供详细的解答(当然需要基于可靠的知识库哦)\n2. **内容创作** - 故事/公文/邮件/剧本/诗歌/代码...只要你想写的类型,我都能尝试!\n3. **逻辑推理** - 复杂的数学题、编程问题或者谜题,我都能一步步帮你分析\n4. **多语言支持** - 中英日韩法西语等100+语言无障碍交流\n5. **创意激发** - 如果你在创作/工作/学习中遇到瓶颈,我们可以一起头脑风暴\n6. **代码写作** - Python/Java/JavaScript等20+编程语言,从基础语法到复杂算法都可以协助\n7. **对话理解** - 可以记住对话历史,进行多轮深入交流\n8. **情感陪伴** - 虽然是AI,但我会认真倾听你的想法和困扰\n\n**举个🌰:**\n- 你可以说:"帮我写篇关于气候变化的科普文章"\n- 或者:"解释下区块链技术的工作原理"\n- 甚至:"给我讲个睡前故事,要有魔法元素"\n\n我最擅长的是通过对话理解你的具体需求,然后给出最合适的帮助。有什么想法随时告诉我哦~ (对了,如果你不确定该问什么,我也可以给你一些有趣的建议!)",
  "additional_kwargs": {},
  "response_metadata": {
    "model": "qwen3:32b",
    "created_at": "2025-08-22T02:32:15.176159Z",
    "done_reason": "stop",
    "done": true,
    "total_duration": 74426570791,
    "load_duration": 5870500708,
    "prompt_eval_count": 26,
    "prompt_eval_duration": 5639857166,
    "eval_count": 610,
    "eval_duration": 62867086250
  },
  "tool_calls": [],
  "invalid_tool_calls": [],
  "usage_metadata": {
    "input_tokens": 26,
    "output_tokens": 610,
    "total_tokens": 636
  }
}

b. 创建提示词模板

在使用大模型处理任务的时候我们可能会输入很多类似的提示词,这些提示词的框架相同,但是部分内容是变化的。LangChain 提供了创建提示词模板的能力来应对这种场景。

js 复制代码
import { ChatPromptTemplate } from '@langchain/core/prompts'
import { ChatOllama } from "@langchain/ollama";
​
const llm = new ChatOllama({
  model: 'qwen3:32b'
})
​
const promptTemplate = ChatPromptTemplate.fromTemplate("只用三句话,给我讲一个主题为{topic}的笑话, 使用{language}作为回答的语言")
​
const input = await promptTemplate.invoke({
  topic: '前端开发',
  language: '中文'
})
​
const input2 = await promptTemplate.invoke({
  topic: '前端开发',
  language: 'English'
})
​
const result = await llm.invoke(input.content)
​
console.log(result)

上面的示例代码中,我们使用了一个字符串作为我们的提示词模版,ChatPromptTemplate 会解析字符串中的{placeholder} 占位符来作为使用该模版创建提示词时可以接受的入参数。我们分别向大模型中传入不同的提示词来查看结果

swift 复制代码
// input
"<think>\n好的,用户让我用三句话讲一个关于前端开发的笑话,用中文回答。首先,我需要确定前端开发的常见元素,比如HTML、CSS、JavaScript,还有常见的痛点,比如兼容性、bug、调试等。\n\n接下来,考虑笑话的结构。三句话的话,通常需要一个设定,一个转折,然后笑点。比如经典的"A说...B说...然后..."结构。要确保笑点明确,同时贴近前端开发的实际问题。\n\n然后,具体想几个例子。比如,前端开发者在调试时遇到的常见问题,比如浏览器兼容性,或者代码出错。比如,用"display: none;"来隐藏元素,但可能因为拼写错误或者层级问题导致无效,这时候可以说"我用了display: none; 但是没用",然后另一个开发者指出"你少写了分号",或者"你在Chrome调试,但用户用IE"。不过要确保三句话内完成。\n\n或者,可以结合框架的问题,比如使用Vue或React时,组件不更新,然后发现是因为忘记用key,或者状态管理的问题。例如:"前端开发者说:'我的组件不更新了!' 另一个开发者问:'你用了react吗?' 第一个开发者回答:'不,我用的是resact。'" 这里"resact"是拼写错误,制造笑点。\n\n再检查是否符合三句话的要求,是否有明确的笑点,并且主题明确。可能需要调整用词让笑话更自然。比如,关于浏览器兼容性的:"前端开发者对老板说:'我们网站在所有浏览器都正常了!' 老板问:'包括IE吗?' 开发者回答:'老板,我们把它从兼容模式改成了怀旧模式。'" 这样三句话,利用IE的兼容性问题,用"怀旧模式"代替,比较幽默。\n\n或者,关于代码调试的:"前端开发者调试了三小时,发现是拼写错误。 另一个开发者问:'你怎么找到的?' 他回答:'我注释掉所有代码,只剩console.log('Hello, world!'); 还是报错,才发现是log拼错了。'" 但可能超过三句话,需要压缩。\n\n再精简一下,比如:"前端开发者说:'我的代码没问题!' 浏览器却说:'你少了个括号。' 开发者反驳:'你又懂什么!' 然后代码突然运行了。" 但可能需要两句话,或者调整结构。\n\n最终决定用浏览器兼容性的梗,比较经典,容易理解。确保三句话,有对话形式,结尾有笑点。比如:"前端开发者对老板说:'我们网站在所有浏览器都正常了!' 老板问:'包括IE吗?' 他回答:'老板,我们把它从兼容模式改成了怀旧模式。'" 这样符合要求,三句话,主题明确,笑点在于IE的兼容性问题,用"怀旧模式"来幽默处理。\n</think>\n\n前端开发者对老板说:"我们网站在所有浏览器都正常了!"  \n老板问:"包括IE吗?"  \n他回答:"老板,我们把它从兼容模式改成了怀旧模式。""
​
// input2
"<think>\n好的,用户让我用三句话讲一个前端开发的笑话,而且要用英文回答。首先,我需要确定用户的需求。他们可能是在寻找一个简洁又相关的笑话,适合前端开发者或者对前端有兴趣的人。\n\n接下来,我要考虑前端开发中的常见元素,比如HTML、CSS、JavaScript,或者常见的挑战,比如兼容性问题、调试、框架更新等等。笑话需要简短,所以必须抓住一个核心点。\n\n然后,我需要确保三句话结构清晰,有设定、转折和 punchline。比如,可以围绕开发者遇到的典型问题,比如浏览器兼容性,或者代码调试的困难。例如,用"<div>"和"</div>"这样的标签来构造笑话,因为它们是前端的基础元素。\n\n可能的方向:开发者试图解决一个问题,但因为标签没闭合或者语法错误导致问题,最后发现是简单的错误。或者用幽默的方式表达前端框架的快速变化,比如"为什么前端开发者不喜欢高处?因为它们害怕落下(fall)到旧版本中。"不过这个可能不够好。\n\n另一个思路:使用HTML标签作为双关语。比如,一个开发者一直用"<div>"来解决问题,但最后发现应该用"<span>",但这样可能不够有趣。或者,为什么前端开发者总是迷路?因为他们总是跟着链接(links)走。\n\n再想想,可能用开发者调试代码的场景。比如,为什么前端开发者总是带着伞?以防遇到雨(bug)天。不过不够贴切。\n\n或者,关于CSS的笑话,比如开发者试图让盒子居中,但失败了,最后发现忘记写分号。或者,"为什么CSS这么敏感?因为它总是容易被格式(format)化。"\n\n回到用户要求的三句话结构,可能需要一个设定,一个转折,然后 punchline。例如:\n\n1. A front-end developer was struggling to center a div on their webpage.\n2. After hours of debugging, they realized they had forgotten to close the <div> tag.\n3. They sighed, "Why is everything in front-end development so... framed?"\n\n这里第三句的"framed"双关,既指框架(frame)的问题,也暗示"div"标签的问题。不过可能不够自然。\n\n另一个例子:\n\n1. A front-end developer tried to make their website responsive.\n2. They spent all day adjusting CSS, but the layout still broke on mobile.\n3. Finally, they gave up and wrote "display: magic;" --- it worked... in production.\n\n这里用了"display: magic;"作为幽默的CSS属性,但可能不够准确。\n\n或者,关于浏览器兼容性:\n\n1. A front-end developer promised their boss a feature would work in all browsers.\n2. The day before launch, they discovered it only worked in Firefox.\n3. In a panic, they renamed Firefox to "Chrome, Edge, and Safari" and crossed their fingers.\n\n这个可能有点复杂,但符合三句话结构。\n\n不过用户可能想要更简洁的,比如基于标签的笑话:\n\n1. A front-end developer was lonely until they met a <div>.\n2. They fell in love at first sight, but the <div> had no closing tag.\n3. Now they're stuck in an infinite loop of unresolved feelings.\n\n这里用了HTML标签的闭合问题来比喻感情问题,可能比较有趣,同时符合三句话的要求。需要检查是否符合英文表达习惯,以及是否有双关或幽默效果。可能用户会喜欢这种结合技术术语和日常情境的笑话。\n</think>\n\nA front-end developer opened three browser tabs: HTML, CSS, and JavaScript.  \nThey spent hours debugging why the page looked broken...  \nOnly to realize they'd forgotten to close a `<div>` and their life was now `</div>`."

(AI 的笑话有点冷的🥶)

当我们想要像大模型输入一系列信息时,我们可以使用 MessagePlaceholder 来创建占位符

js 复制代码
import { AIMessage, HumanMessage } from "@langchain/core/messages";
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'
import { ChatOllama } from "@langchain/ollama";
​
const llm = new ChatOllama({
  model: 'qwen3:32b'
})
​
const messages = [
  new HumanMessage('你是谁'),
  new MessagesPlaceholder('msgs'),
]
​
// 等同于如下代码
// const messages = [
//   ['user', '你是谁'],
//   ['placeholder', '{msgs}'],
// ]
​
const promptTemplate = ChatPromptTemplate.fromMessages(messages)
​
const input = await promptTemplate.invoke({
  msgs: [
    new AIMessage('我是Qwen3'),
    new HumanMessage('你可以做什么')
  ]
})
​
const result = await llm.invoke(input)
​
console.log(result)

3. 带历史记录的对话应用

a. 手动管理

在与大模型对话过程中,我们会需要大模型根据我们以往的输入来综合回答,现在我们的代码并不支持历史记录,每一次调用都是与大模型的第一次对话。我们创建一个示例来测试一下

js 复制代码
import { ChatOllama } from "@langchain/ollama";
​
const llm = new ChatOllama({
  model: 'qwen3:32b'
})
​
await llm.invoke('我的名字是 tocka')
​
const result = await llm.invoke('我的名字是什么')
​
console.log(result.content)
​
// terminal
"<think>\n好的,用户问"我的名字是什么"。我需要先看看对话历史里有没有提到名字。用户刚注册时可能没提供名字,所以系统可能不知道。这时候应该礼貌地询问用户的名字,同时说明需要名字才能继续对话。要确保回复友好,不显得机械。比如可以说:"你好!目前我还不知道你的名字呢。你可以告诉我你的名字吗?这样我们接下来的对话会更愉快哦~"这样既明确又友好,用户也会觉得被重视。另外,要注意不要假设用户已经提供了信息,保持中立和开放的态度。\n</think>\n\n你好!目前我还不知道你的名字呢。你可以告诉我你的名字吗?这样我们接下来的对话会更愉快哦~"

在示例中我们发现我们在提问前告知了大模型答案,但是没有起作用。按照我们前一小节使用到的内容,我们会想到的使用数组的方式来向大模型传入对话的历史

js 复制代码
// test.js
import { ChatOllama } from "@langchain/ollama";
​
const llm = new ChatOllama({
  model: 'qwen3:32b'
})
​
const history = []
​
const result1 = await llm.invoke('我的名字是 tocka')
history.push(result1)
​
const result = await llm.invoke([
  ...history,
  ['user', '我的名字是什么']
])
​
console.log(result.content)
​
// terminal
"<think>\n好的,用户问"我的名字是什么",我需要先看看之前的对话历史。用户之前自我介绍是"你好,Tocka!很高兴认识你。有什么我可以帮助你的吗?无论是问题、讨论还是其他需求,随时告诉我哦!😊",所以用户可能以为自己的名字是Tocka。但用户现在问的是"我的名字是什么",这可能是因为他们不确定或者想确认。\n\n首先,我需要确认用户是否真的忘记了自己的名字,或者是在测试我是否记得。根据对话上下文,用户之前提到的名字是Tocka,所以应该回复Tocka。但需要确保没有混淆,比如用户可能在之前的对话中有其他名字,但这里没有显示。所以根据现有信息,用户的名字是Tocka。\n\n另外,用户可能是在测试我的记忆能力,或者想确认我的回答是否一致。这时候需要友好地确认名字,并保持亲切的语气。同时,要避免假设用户有其他名字,除非有明确的信息。所以正确的回应应该是确认用户的名字是Tocka,并表达帮助的意愿。\n</think>\n\n你好呀,Tocka!你的名字是用户在对话开始时主动提到的哦~不过我有点好奇,你是想确认名字的正确性,还是突然忘记自己给自己取的昵称啦?(笑) 如果有其他想聊的,我随时都在!"

我们发现现在大模型可以通过读取对话历史来获取到我们的名字,但是这样手动管理历史记录很复杂,而且如果我们要复用部分流程的话,需要定义不同的工厂函数用来生产上下文。LangChain 集成了 LangGraph 来处理复杂的应用流程和编排,LangGraph 中提供了不同的类,我们先忽略无关的部分,专注于处理我们的历史记录功能。

b. 获取历史记录

通过之前的代码我们知道了处理历史记录实际上就是保存之前发过的消息然后再输入给大模型,那么有没有什么办法可以获取到我们希望的"同一个对话记录"中的记录呢?我们可以使用 LangGraph 中提供的 MemorySaver 来将历史记录保存在我们的内存中

js 复制代码
// test.js
import { MemorySaver, MessagesAnnotation, StateGraph } from "@langchain/langgraph";
import { ChatOllama } from "@langchain/ollama";
import { v4 as uuid } from "uuid";
​
const llm = new ChatOllama({
  model: 'qwen3:32b'
})
​
const callModel = async (state) => {
  const result = await llm.invoke(state.messages)
  return {
    messages: result
  }
}
​
// 定义数据保存器
const memory = new MemorySaver()
​
// 编排流程 input => callModel => output
const app = new StateGraph(MessagesAnnotation)
  .addNode('model', callModel)
  .addEdge('__start__', 'model')
  .addEdge('model', '__end__')
  // 将流程编译成应用,checkpointer 可以理解为游戏中的存档点
  .compile({
    checkpointer: memory
  })
​
const config = {
  configurable: {
    thread_id: uuid()
  }
}
​
await app.invoke({
  messages: '我的名字是tocka'
}, config)
​
const result = await app.invoke({
  messages: '我的名字是什么'
}, config)
​
console.log(result)

我们运行示例代码,发现控制台打印出了我们输入过的信息和大模型的回答结果,功能上我们暂时完成了,但是示例代码中我们还有很多不认识的函数,我们一一介绍一下。

  • StateGraph : StateGraph 是定义工作流的构造函数,它可以接受一个 Annotation 作为参数来定义我们工作流的输出的状态结构

  • Annotation: Annotation 是用来定义我们状态流转中的结构,例如我们可以定义 InputAnnotation 作为我们的输入结构,在流程中我们定义的 Annotation 一定是作为传入 StateGraphAnnotation的子集,否则未在其中定义的字段接收到的值会被至为 undefined

    js 复制代码
    const StateAnnotation = Annotation.Root({
      question: Annotation,
      answer: Annotation
    })
    ​
    const InputStateAnnotation = Annotation.Root({
      question: Annotation
    })
    ​
    const search = async (state: typeof InputStateAnnotation.State) => {
      const answer = await llm.invoke(state.question)
      return {
        answer
      }
    }
    ​
    const workflow = new StateGraph(StateAnnotation)
      .addNode('search', search)
      .addEdge('__start__', 'search')
      .addEdge('search', '__end__')
      .compile()
    ​
    const result = await workflow.invoke({ question: '我是谁' })
    // result: { question, answer }
  • checkpointer : 本质上是一个状态管理接口,它定义了如何保存、读取和管理工作流的状态,而具体存储在哪里(内存、数据库、文件)取决于具体的实现。我们可以理解为游戏中的存档点,在上面的示例代码中我们是直接使用了内置的工具来将信息持久化在内存中,实际开发中我们会需要链接数据库存储数据

4. 小结

上面的示例中我们了解了如何调用大模型以及实现带有历史记录的对话功能,初步使用了 LangGraph 的编排功能。初步感受下来其实大模型应用的开发本质是一个工程化的问题,我们利用提示词管理,历史记录等等流程来实现我们想要达到的功能。下一步我们学习一下如何使用 LangChain 开发 RAG 的相关功能。

参考:LangChain 官方文档

相关推荐
神码小Z2 分钟前
NewsNow搭建喂饭级教程
人工智能·业界资讯
青山Coding5 分钟前
Cesium应用(三):全球气压可视化与气象时序图实现方案
前端·gis·cesium
Dante但丁9 分钟前
手扒Github项目文档级知识图谱构建框架RAKG(保姆级)Day5
人工智能
肖书婷10 分钟前
人工智能 -- 循环神经网络day1 -- 自然语言基础、NLP基础概率、NLP基本流程、NLP特征工程、NLP特征输入
人工智能
Struart_R11 分钟前
LLaVA-3D,Video-3D LLM,VG-LLM,SPAR论文解读
人工智能·深度学习·计算机视觉·3d·大语言模型·多模态
老虎062712 分钟前
JavaWeb前端03(Ajax概念及在前端开发时应用)
前端·javascript·ajax
人工智能训练师14 分钟前
openEuler系统中如何将docker安装在指定目录
linux·运维·服务器·人工智能·ubuntu
格林威18 分钟前
Baumer高防护相机如何通过YoloV8深度学习模型实现网球运动员和网球速度的检测分析(C#代码UI界面版)
人工智能·深度学习·数码相机·yolo·ui·c#·视觉检测
Aphasia31126 分钟前
性能优化之重绘和重排
前端·面试
Python私教28 分钟前
yggjs_react使用教程 v0.1.1
前端·react.js·前端框架