当前位置: 首页 > news >正文

LangChain入门指南:从零构建大语言模型应用与RAG系统

1. 从零开始:为什么你需要LangChain?

如果你最近在折腾大语言模型,不管是OpenAI的GPT系列,还是开源的Llama、Qwen,大概率会碰到一个头疼的问题:这些模型本身很强大,但怎么把它们真正用起来,做成一个能解决实际问题的应用?比如,你想让模型帮你读公司内部的PDF文档并回答问题,或者做一个能自动分析用户反馈并生成周报的智能助手。这时候,你发现光靠调用模型的API远远不够,你需要处理文档加载、文本分割、向量化存储、对话历史管理、复杂逻辑编排等一系列“脏活累活”。

LangChain就是为解决这些“脏活累活”而生的。它不是另一个大模型,而是一个框架,一个专门用来构建基于大语言模型的应用的“脚手架”。你可以把它想象成乐高积木的基础底板,而各种大模型、工具、数据源就是上面的积木块。LangChain提供了一套标准化的接口和组件,让你能像搭积木一样,快速、灵活地将这些模块组合成一个功能完整的AI应用。

我最初接触LangChain时,也被它繁杂的概念搞得有点晕——Chain、Agent、Tool、Memory、Retrieval……但真正用起来才发现,它的设计思想非常清晰:标准化组合化。它把LLM应用开发中那些通用的、重复性的工作抽象成了组件,你不需要每次都从零开始写文件解析、写缓存逻辑、写提示词模板。更重要的是,它让你能专注于业务逻辑本身,而不是陷在工程细节里。

所以,这篇快速入门的目标很明确:不搞长篇大论的理论,直接带你上手,用最短的时间理解LangChain的核心概念,并亲手搭建两个最实用的应用场景——一个简单的问答链和一个带检索增强的文档问答系统。你会发现,原来让AI“干活”可以这么简单。

2. 环境准备与核心概念扫盲

2.1 快速搭建你的开发环境

工欲善其事,必先利其器。开始之前,我们需要一个干净的Python环境。我强烈建议使用condavenv来创建独立的虚拟环境,避免包版本冲突。

# 使用conda创建环境(如果你安装了Anaconda或Miniconda) conda create -n langchain-demo python=3.10 conda activate langchain-demo # 或者使用venv python -m venv langchain-demo source langchain-demo/bin/activate # Linux/Mac # 或 .\langchain-demo\Scripts\activate # Windows

接下来,安装LangChain。这里有个小细节:LangChain社区非常活跃,核心库拆分为多个包以保持轻量。对于入门,我们主要安装langchain-core(核心接口)、langchain(标准集成组件)和langchain-community(第三方工具集成)。

pip install langchain langchain-core langchain-community

为了连接大模型,我们还需要安装对应模型的SDK。本文将以OpenAI的API为例,因为它最通用、最稳定。你需要在 OpenAI平台 获取一个API密钥。

pip install openai

设置环境变量,将你的API密钥安全地配置进去。不要在代码里硬编码密钥。

# Linux/Mac export OPENAI_API_KEY='你的-api-key-here' # Windows (PowerShell) $env:OPENAI_API_KEY='你的-api-key-here'

注意:对于生产环境,请使用.env文件配合python-dotenv库,或使用密钥管理服务,绝对不要将密钥提交到代码仓库。

2.2 五分钟理解六个核心概念

LangChain的概念不少,但入门只需抓住最关键的六个。我用最直白的话解释一下:

  1. 模型 I/O (Model I/O):这是和LLM对话的“出入口”。主要包括:

    • Prompt Templates(提示词模板):可复用的提示词框架,比如“请用{style}的风格总结以下内容:{text}”。它把变量和固定结构分开,方便管理和迭代。
    • Language Models(语言模型):就是LLM本身,比如GPT-4、Claude等。LangChain提供了统一的调用接口。
    • Output Parsers(输出解析器):LLM的输出是文本,但程序需要结构化的数据(如JSON、列表)。输出解析器帮你把非结构化的文本“翻译”成程序能懂的结构。
  2. 检索 (Retrieval):让模型能访问“外部知识”的核心。当模型自身的知识不够或已过时,你需要从你自己的文档、数据库里找信息喂给它。这通常涉及:

    • 文档加载器:从PDF、网页、数据库等地方把文档读进来。
    • 文本分割器:把长文档切成模型能“消化”的小块。
    • 向量存储:将文本块转换成向量(一组数字),并存储起来,便于后续快速查找相似内容。
    • 检索器:根据用户问题,从向量存储里找出最相关的文本块。
  3. 链 (Chains):这是LangChain的灵魂。一个链就是把多个组件(模型、提示词、工具等)按特定顺序组合起来,完成一个更复杂的任务。比如“加载文档 -> 分割 -> 检索 -> 生成答案”就是一个链。最简单的链是LLMChain,就是一个“提示词 + 模型”的组合。

  4. 代理 (Agents):如果说“链”是预设好的流水线,那“代理”就是有自主决策能力的智能体。代理可以访问“工具”(比如搜索、计算、查数据库),并根据用户的目标,自己决定先做什么、后做什么,甚至调用哪个工具。它让AI应用从“自动化”走向了“智能化”。

  5. 记忆 (Memory):让对话有“上下文”。普通的API调用是无状态的,你问“我叫什么名字?”,它根本不知道你是谁。记忆组件负责保存和管理对话历史,让模型能记住之前说过的话,实现多轮对话。

  6. 回调 (Callbacks):相当于应用的“日志和监控系统”。你可以通过回调在链执行的各个阶段(开始、结束、出错)插入自定义逻辑,用于日志记录、流式输出、性能监控等,对调试和优化非常有用。

理解了这些,我们就可以开始动手了。接下来的部分,我们会把这些概念变成具体的代码。

3. 第一个LangChain应用:构建智能问答链

让我们从一个最简单的“链”开始,直观感受LangChain的工作方式。我们将创建一个根据公司名称生成宣传口号的链。

3.1 初始化模型与提示词

首先,我们需要导入必要的模块,并初始化一个LLM对象。这里我们使用OpenAI的gpt-3.5-turbo模型,它性价比高,响应速度快。

from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate # 初始化聊天模型。temperature控制创造性,越高答案越随机。 llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)

接下来,创建一个提示词模板。注意,我们使用的是ChatPromptTemplate,它是为聊天模型设计的,可以处理系统消息、用户消息等多种角色。

# 定义一个提示词模板 prompt_template = ChatPromptTemplate.from_messages([ ("system", "你是一位资深的品牌营销专家。"), ("user", "请为一家名为{company_name}的{industry}公司,构思一句朗朗上口、突出{core_value}核心价值的宣传口号。") ])

这个模板里包含了三个变量:company_name(公司名)、industry(行业)和core_value(核心价值)。我们可以用不同的值填充它,生成不同的提示词。

3.2 组装并运行你的第一个链

现在,我们把提示词模板和模型“链”起来。在LangChain中,最简单的链就是LLMChain

from langchain.chains import LLMChain # 创建链 slogan_chain = LLMChain(llm=llm, prompt=prompt_template) # 运行链,传入变量 result = slogan_chain.invoke({ "company_name": "绿源科技", "industry": "新能源", "core_value": "可持续创新" }) print(result["text"]) # 输出模型生成的口号

运行这段代码,你可能会得到类似“绿动未来,源创无限——绿源科技,驱动可持续新纪元”这样的输出。看,我们只用了几行代码,就完成了一个可配置、可复用的AI小应用。

实操心得temperature参数很重要。对于需要确定性答案的任务(如代码生成、数据提取),设为0或0.1;对于需要创造性的任务(如起名、写文案),可以设为0.7-0.9。一开始不妨多试试不同值,观察输出效果。

3.3 深入解析:链的调用与流式输出

上面我们用了invoke方法,这是同步调用。LangChain还支持异步调用ainvoke,这在Web服务中非常有用。此外,如果你想看到模型一个字一个字生成答案的过程(即流式输出),可以使用stream方法。

# 异步调用 (适用于FastAPI等异步框架) # async def generate_slogan(): # result = await slogan_chain.ainvoke(...) # return result # 流式输出 for chunk in slogan_chain.stream({"company_name": "快达物流", "industry": "物流", "core_value": "极速可靠"}): if "text" in chunk: print(chunk["text"], end="", flush=True) # 逐词打印

流式输出能极大提升用户体验,让用户感觉响应更快。在Web应用中,你可以通过Server-Sent Events (SSE) 将每个chunk实时推送给前端。

4. 进阶实战:打造你的专属知识库问答机器人

简单的问答链只能利用模型的内置知识。但在实际工作中,我们往往需要让模型回答关于特定、私有文档的问题,比如公司制度、产品手册、技术文档等。这就是检索增强生成(RAG)的用武之地,也是LangChain最强大的场景之一。

4.1 RAG流程全景图

一个典型的RAG应用分为“索引”和“查询”两个阶段:

  1. 索引阶段:将你的文档库处理成模型可查询的格式。
    • 加载文档 -> 分割文本 -> 文本向量化 -> 存入向量数据库。
  2. 查询阶段:回答用户问题。
    • 将用户问题向量化 -> 从向量库检索相关文本片段 -> 将片段和问题组合成提示词 -> 发送给LLM生成答案。

下面我们一步步实现它。

4.2 文档加载与处理:从原始文件到文本块

假设我们有一个product_manual.pdf的产品手册。首先,我们需要安装处理PDF的库。

pip install pypdf # 一个轻量级的PDF解析库

然后,使用LangChain的文档加载器来读取它。

from langchain_community.document_loaders import PyPDFLoader # 加载PDF文档 loader = PyPDFLoader("./product_manual.pdf") documents = loader.load() print(f"加载了 {len(documents)} 页文档。") # 每页是一个Document对象,包含`page_content`(文本)和`metadata`(如页码)。

加载后的文档可能很长,直接塞给LLM会超出上下文限制,且包含大量无关信息。因此,我们需要进行文本分割。这里使用RecursiveCharacterTextSplitter,它会尝试按字符(如换行、句号、空格)递归地分割,尽量保持语义段落完整。

from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 每个文本块的最大字符数 chunk_overlap=50, # 块之间的重叠字符数,避免上下文断裂 separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""] # 分割优先级 ) split_docs = text_splitter.split_documents(documents) print(f"文档被分割成 {len(split_docs)} 个文本块。")

注意事项chunk_size没有黄金标准。太小会丢失上下文,太大会降低检索精度并增加LLM成本。对于通用文档,500-1000是个不错的起点。chunk_overlap设置重叠可以防止一个完整的句子被拦腰截断,通常设为chunk_size的10%-20%。

4.3 向量化与存储:构建文档的“记忆”

文本分割后,我们需要将它们转换成向量(即嵌入),并存储到向量数据库中。这里我们使用Chroma,一个轻量级、易上手的开源向量数据库。同时,我们需要一个嵌入模型来生成向量。OpenAI的text-embedding-ada-002是业界标杆,我们用它来演示。

pip install chromadb langchain-openai
from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma # 初始化嵌入模型 embeddings = OpenAIEmbeddings(model="text-embedding-ada-002") # 将分割后的文档转换为向量,并持久化存储到本地目录 `./chroma_db` vectorstore = Chroma.from_documents( documents=split_docs, embedding=embeddings, persist_directory="./chroma_db" # 指定持久化目录 ) # 如果需要后续加载,可以使用 Chroma(persist_directory="./chroma_db", embedding_function=embeddings)

这个过程可能会消耗一些时间,取决于文档数量和大小。Chroma会将向量数据存储在本地,下次启动应用时可以直接加载,无需重新计算。

4.4 组装检索链:从提问到答案

存储完成后,我们就可以构建检索链了。LangChain提供了一个高级抽象RetrievalQA,它把检索、提示、生成打包成了一个链。

from langchain.chains import RetrievalQA # 从已存在的向量库创建检索器 retriever = vectorstore.as_retriever( search_type="similarity", # 相似度搜索 search_kwargs={"k": 3} # 返回最相关的3个文本块 ) # 创建RetrievalQA链 qa_chain = RetrievalQA.from_chain_type( llm=llm, # 使用之前定义的LLM chain_type="stuff", # 最常用的类型,将所有检索到的文档“塞”进提示词 retriever=retriever, return_source_documents=True, # 返回源文档,便于追溯答案来源 verbose=True # 打印详细日志,调试时非常有用 ) # 现在,开始提问吧! question = "这款产品的主要安全特性是什么?" result = qa_chain.invoke({"query": question}) print("答案:", result["result"]) print("\n--- 来源文档 ---") for i, doc in enumerate(result["source_documents"]): print(f"\n片段 {i+1}: {doc.page_content[:200]}...") # 打印前200字符

当你运行这段代码时,RetrievalQA链会执行以下操作:

  1. 用嵌入模型将你的问题转换成向量。
  2. 在向量数据库中搜索与问题向量最相似的3个文本块。
  3. 将这些文本块和你的问题一起,填充到一个预设的提示词模板中。
  4. 将完整的提示词发送给LLM,生成最终答案。
  5. 返回答案和来源文档。

chain_type="stuff"是最直接的方式,但如果检索到的文档总长度超过LLM的上下文窗口,就会出错。对于超长文档,可以考虑"map_reduce""refine"等更复杂的链类型,它们会对文档进行分步处理。

5. 避坑指南与效能优化

在实际使用中,你肯定会遇到各种问题。下面是我踩过的一些坑和总结的优化技巧。

5.1 检索效果不佳?可能是这里出了问题

你可能会发现,有时候机器人给出的答案不准确,或者根本没用到你文档里的信息。别急着怪模型,大概率是检索环节出了问题。

  • 问题1:检索不到相关内容

    • 检查点search_kwargs={“k”: n}中的n是否太小?可以尝试增加到5或10。
    • 检查点:文本分割的chunk_size是否合适?如果块太大,一个块里包含多个不相关主题,会稀释关键信息的向量表示。尝试减小chunk_size到300-400。
    • 检查点:嵌入模型是否匹配?不同模型生成的向量空间不同。确保索引和查询时使用同一个嵌入模型。
  • 问题2:答案胡编乱造(幻觉)

    • 检查点:提示词是否明确要求模型“基于给定上下文回答”?可以在RetrievalQA中自定义prompt,加入强约束,例如:“请严格仅根据以下上下文信息回答问题。如果上下文没有提供足够信息,请直接说‘根据已知信息无法回答该问题’。上下文:{context} 问题:{question}”
    • 检查点:开启return_source_documents=True,并仔细核对模型生成的答案是否真的来源于你提供的片段。很多时候幻觉是因为模型自行补充了知识。
  • 问题3:回答冗长或跑题

    • 调整LLM参数:降低temperature(如设为0.1),让答案更确定、更简洁。
    • 优化提示词:在系统消息中明确要求“回答应简洁、精准,不超过三句话”。

5.2 成本与性能优化技巧

使用商业API,成本是需要考虑的因素。以下是一些省钱的技巧:

  1. 缓存嵌入结果:文档的嵌入向量一旦生成就不会变,可以永久缓存。使用CacheBackedEmbeddings可以避免重复计算相同内容的嵌入,大幅节省成本和时间。
  2. 选择合适的模型:不是所有任务都需要GPT-4。对于简单的信息提取、总结,gpt-3.5-turbo完全够用且便宜一个数量级。对于嵌入,text-embedding-3-smallada-002更便宜且性能更好。
  3. 限制上下文长度:严格控制chunk_size和检索返回的块数(k)。送入LLM的令牌数越少,费用越低,速度也越快。
  4. 异步处理:对于批量处理文档或同时服务多个用户请求,使用异步调用(ainvoke,astream)可以显著提高吞吐量。

5.3 调试利器:LangSmith

当链变得复杂时,调试会变得困难。LangChain官方推出了一个强大的平台——LangSmith。它可以可视化地追踪每一次链的调用,查看每一步的输入输出,精确找到问题出在哪个环节(是检索没找到?还是提示词没写好?还是模型理解错了)。

虽然它是商业服务,但提供了免费额度。对于学习和调试复杂应用,强烈建议注册使用。只需设置几个环境变量,你的所有链调用就会自动记录到LangSmith仪表盘。

export LANGCHAIN_TRACING_V2=true export LANGCHAIN_API_KEY="你的-langsmith-api-key" export LANGCHAIN_PROJECT="你的项目名"

6. 从链到智能体:探索更自主的AI

掌握了链和RAG,你已经能解决80%的问题。但有时候,用户的需求不是一次问答就能解决的,可能需要多步推理、调用外部工具(如搜索、计算器、数据库)。这时候,就需要请出智能体(Agent)

智能体的核心思想是“思考-行动-观察”循环。它有一个目标,一套可用的工具(Tools),和一个决定使用哪个工具的“大脑”(通常是LLM)。

6.1 创建一个简单的数学计算代理

让我们创建一个能使用Python解释器进行复杂数学计算的代理。

from langchain.agents import create_react_agent, AgentExecutor from langchain import hub from langchain_community.tools import Tool from langchain_experimental.tools import PythonREPLTool # 1. 定义工具 python_repl_tool = PythonREPLTool() # 一个安全的Python代码执行工具 # 可以将多个工具包装在一起 tools = [ Tool( name="Python_REPL", func=python_repl_tool.run, description="用于执行数学计算或数据操作的Python代码解释器。输入必须是有效的Python代码。" ) ] # 2. 从LangChain Hub拉取一个高效的提示词模板(ReAct格式) prompt = hub.pull("hwchase17/react") # 3. 创建智能体 agent = create_react_agent(llm, tools, prompt) # 4. 创建代理执行器 agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True) # 5. 运行代理 result = agent_executor.invoke({ "input": "请计算圆周率π的平方根,并求出其结果与自然常数e的差值。" }) print(result["output"])

运行这段代码,你会看到verbose=True模式下,代理详细的思考过程:

  1. Thought: 我需要计算π的平方根和e的值,然后求差。这需要数学计算,我应该使用Python_REPL工具。
  2. Action: 调用Python_REPL工具,输入代码import math; sqrt_pi = math.sqrt(math.pi); diff = sqrt_pi - math.e; print(diff)
  3. Observation: 工具执行后返回的结果(一个数字)。
  4. Thought: 我得到了计算结果,现在可以给出最终答案了。
  5. Final Answer: 输出最终答案。

6.2 代理与链的核心区别

通过这个例子,你可以清晰感受到代理的区别:

  • :是确定性的流程。你预先定义好步骤(检索->生成),它就会按部就班执行。适合流程固定、目标明确的任务。
  • 代理:是目标导向的决策者。你只告诉它目标(“解这个数学题”),并提供工具。它自己规划步骤、选择工具、执行并迭代,直到达成目标。适合需要多步推理、条件判断或工具调用的复杂任务。

选择使用链还是代理,取决于你的应用场景是否需要这种自主规划和工具使用的能力。对于大多数文档问答、内容生成、数据提取任务,链已经足够。当你需要AI与外部世界动态交互时,代理才是更好的选择。

7. 常见问题与解决方案速查

在学习和项目开发中,以下是我被问得最多的一些问题:

Q1: LangChain和LangGraph、LlamaIndex有什么区别?

  • LangChain:是一个全面的应用构建框架,提供了模型I/O、检索、链、代理、记忆等全套组件,强调灵活组合。
  • LangGraph:是LangChain团队推出的一个库,用于构建有状态、多参与者的复杂工作流。它用图(Graph)的概念来编排多个链或代理,特别适合需要循环、分支、并行等复杂控制流的场景。你可以理解为,LangChain是乐高积木,LangGraph是拼装复杂乐高模型的说明书。
  • LlamaIndex:更专注于数据索引和检索这一环节,在RAG的数据连接、索引结构、高级检索策略上做得非常深入。它常与LangChain结合使用,用LlamaIndex做强大的数据层,用LangChain做应用编排层。

Q2: 我的提示词怎么写效果都不好,怎么办?提示词工程是门实践科学。除了遵循清晰、具体、提供示例等基本原则外,一个实用的技巧是使用Few-Shot示例。在提示词模板中,提供一两个输入输出的例子,能极大地引导模型理解你的格式和意图要求。LangChain的FewShotPromptTemplate可以方便地实现这一点。

Q3: 向量检索速度慢,怎么优化?首先,确保你的向量数据库支持索引(如Chroma的HNSW)。其次,可以考虑:

  • 量化:使用更短的向量(如从1536维降到768维),牺牲极少精度换取速度和存储空间。
  • 预过滤:在向量检索前,先用元数据(如文档类型、日期)过滤掉大量不相关文档,缩小搜索范围。
  • 升级硬件:向量检索是计算密集型,CPU优化或使用GPU能大幅提升速度。

Q4: 如何将LangChain应用部署为API服务?最常用的方式是结合FastAPI。将你的链或代理封装成FastAPI的端点。注意处理好异步调用、请求超时、错误处理和API密钥管理。对于生产环境,还需要考虑限流、监控和日志。

from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() # ... 初始化你的qa_chain ... class QueryRequest(BaseModel): question: str @app.post("/ask") async def ask_question(request: QueryRequest): result = await qa_chain.ainvoke({"query": request.question}) return {"answer": result["result"]}

Q5: 代码里出现LangChainDeprecationWarning怎么办?LangChain更新很快,API常有变动。警告信息通常会告诉你应该改用哪个新方法。务必查看警告指向的文档链接。保持依赖包更新到较新版本,并定期检查官方迁移指南,是避免兼容性问题的最好方法。

走到这里,你已经掌握了LangChain最核心的部件和思想。从定义一个简单的提示词链,到构建一个能理解私有知识的RAG系统,再到创建一个能自主使用工具的智能体,这套工具链极大地降低了LLM应用开发的门槛。我个人的体会是,初期不要追求大而全,从一个明确的小需求切入,用最简单的链实现它,看到效果后再逐步迭代、增加复杂度。遇到问题多查官方文档(现在中文社区资料也越来越丰富),多在实践中调试。记住,LangChain是帮你提效的脚手架,而不是束缚你的枷锁,当你熟悉了它的模式后,完全可以按自己的业务需求灵活定制和扩展。

http://www.jsqmd.com/news/1388183/

相关文章:

  • 从指令到交付:Kimi K3作为AI Agent的自主规划与执行能力实测
  • 2026怎么判断一个AI搜索优化工具服务有保障?深度指南
  • Python全栈项目CI/CD实战:从Docker化到自动化部署
  • Java面向对象 02根源篇:为什么面向对象会成为主流
  • 2026深圳搬家拆装清运一站式服务:红木家具榫卯结构无损拆装与多层防护打包、挂机/柜机/中央空调专业移机、旧家具随车清运——收费标准与正规公司筛选标准 - 禧燕搬家
  • 临沂网站建设铭镇:深耕本土数字生态,以匠心铸就企业品牌核心竞争力
  • 绩效考核靠领导印象打分怎么破?北京华恒智信成功案例
  • 计算机毕业设计之基于Spark+Echarts的健康医疗数据分析系统
  • 赣州深科网站建设:从入门到精通,揭秘本地企业数字化升级的避坑指南与实战策略
  • SAP UI5 里有没有 RxJS fromEvent,从 UI5 事件体系到 Observable 的完整对应关系
  • 不局限蓝牙AOA!室内定位项目踩坑无数?4份资料避开90%不靠谱
  • 借鉴MTK光源色温氛围色保留逻辑实现加权平均简化自动白平衡氛围色保留方法介绍
  • Spring Boot多模块项目配置加载难题:从原理到实战解决方案
  • 数据安全审计系统架构设计与AI实践
  • 关机长时间转圈卡顿?【图文讲解】3 类后台提速10秒关机完整教程
  • 2026年储能电子洁净车间厂家甄选:高等级净化工程与微尘控制技术实力深度解析 - 卓企推荐
  • 东风地区网站建设怎么做才能既好看又实用?老站长掏心窝子分享避坑指南与实战策略
  • 构建统一AI编程助手网关:智能路由与多后端集成实践
  • 破解物理AI技术困局(7):TVA实现开放词汇感知
  • OpenClaw框架解析:从提示词工程到上下文工程的AI智能体系统化设计
  • Ubuntu系统引导失败与网络故障的完整修复指南
  • Nacos生产环境高可用部署与配置管理实战指南
  • SSH终端的剪切粘贴和移动的区别
  • Tiago Dual机器人四视角联合仿真:Isaac Sim+ROS 2+RViz 2环境搭建全攻略
  • 模拟火车TS2024高清插件|专属独立驾驶室|非基础版|以实物截图为准
  • Socket丢包粘包的处理方案
  • JavaScript 中的定时器与动画基础
  • TVA智能体:物理AI的“虚实连接器”
  • podman管理redis集群
  • 基于el-input实现数字输入框:从原理到实战的完整指南