当前位置:   article > 正文

LangChain开发LLM应用【入门指南】_langchain 开发社区

langchain 开发社区

LangChain是什么?

LangChain 是一个开源的自然语言处理框架,旨在帮助开发者更容易地构建和部署基于LLM的应用(构建和运行AI工作流

LangChain主要解决什么问题?

LLM 的局限性:

在实际的场景中,LLM 擅长在常规上下文下对提示做出响应,但在未接受过训练的特定领域却会遇到挑战。 人们用Prompts来引导 LLM 生成回复,为了让 LLM 在特定领域发挥更佳效果,机器学习工程师需要将其与组织内部数据来源整合,并应用提示工程技术。

LangChain 的出现简化了开发数据响应式应用程序的中间步骤,并提高了 Prompt Engineering 的效率。同时,提供了一套易用、直观的工具和界面,使开发人员能够轻松地将 LLM 与数据源和提示工程技术进行整合。

LangChain可以做什么?

  1. 知识库

  2. 聊天机器人

  3. 智能助理

  4. 文本摘要

  5. 。。。 。。。

LangChain的优势

  1. 简化LLM的集成:LangChain 支持广泛的 LLM(OpenAI 的 ChatGPT、Google 的 BERT等),并抽象了与特定模型或提供商交互的细节,使您可以轻松地在不同的LLM之间切换,而无需大量修改应用程序代码

  2. 模块化组件:LangChain的核心思想是我们可以将不同的组件 “ 链 ” 在一起,以创建更高级的 LLMs 用例

  3. 可扩展性:开发者可以无缝创建自定义组件或将它们与其他库和工具集成

  4. 不断发展的社区和丰富的生态系统:LangChain拥有一个蓬勃发展的开发者社区,包含大量由社区贡献的工具和模块。

 LangChain的安装

  1. pip install langchain
  2. pip install langchain-community

LangChain的核心组件

LangChain 的独特之处之一便是其灵活性和模块化。通过将自然语言处理管道分解为单独的组件,开发人员可以轻松混合和匹配这些构建块,以创建满足其特定需求的自定义工作流程,从而使得 LangChain 成为一个高度适应性的框架,可用于为广泛的用例和行业构建对话式人工智能应用程序。

  1. Chains:“链”是指调用序列(无论是LLM、工具还是数据预处理步骤)。主要支持的方法是使用 LCEL

  2. Model I/O:与任何语言模型进行接口交互的基本组件

  3. Retrieval (Data Connection):用于检索外部数据,然后在执行生成步骤时将其传递给 LLM

  4. Agents:使用语言模型来选择要采取的一系列操作。在链中,一系列操作被硬编码(在代码中)。在代理中,语言模型被用作推理引擎来确定要采取哪些操作以及按什么顺序。

  5. Memory:提供存储过去交互信息的能力

  6. Callbacks:回调系统,允许您连接到 LLM 申请的各个阶段。这对于日志记录、监控、流传输和其他任务非常有用

组件:Chains

链( Chains )是一个非常通用的概念,它指的是将一系列模块化组件(或其他链)以特定方式组合起来,以实现共同的用例。

“链”是指调用序列(无论是LLM、工具还是数据预处理步骤)。主要支持的方法是使用 LCEL

LangChain表达式(LCEL)

LCEL的特点:

  1. 简洁:通过| 符号将不同的组件链接在一起,将一个组件的输出作为下一个组件的输入

    chain = prompt | model | output_parser

  2. 流式支持

  3. 异步支持

  4. 重试和回退

  5. 访问中间结果

基本示例:

  1. from langchain_core.output_parsers import StrOutputParser
  2. from langchain_core.prompts import ChatPromptTemplate
  3. from langchain_openai import ChatOpenAI
  4. # 创建提示语
  5. prompt = ChatPromptTemplate.from_template("给我讲一个关于{topic}的笑话")
  6. # 创建模型
  7. model = ChatOpenAI(model="gpt-4")
  8. # 创建输出解析器
  9. output_parser = StrOutputParser()
  10. # 定义流程(链)
  11. chain = prompt | model | output_parser
  12. # 执行链
  13. chain.invoke({"topic": "小狗"})

流式示例:

  1. from langchain_core.output_parsers import StrOutputParser
  2. from langchain_core.prompts import ChatPromptTemplate
  3. from langchain_openai import ChatOpenAI
  4. # 创建提示语
  5. prompt = ChatPromptTemplate.from_template("给我讲一个关于{topic}的笑话")
  6. # 创建模型
  7. model = ChatOpenAI(model="gpt-4")
  8. # 定义流程(链)
  9. chain = prompt | model
  10. # 执行链
  11. for s in chain.stream({"topic": "比特币"}):
  12. print(s.content, end="|", flush=True)组件:Agents

组件:Model I/O

model_io 图示

针对模型的输入/输出,LangChain 提供了与任何语言模型进行接口交互的基本组件。

  • 提示 prompts : 将模型输入模板化、动态选择和管理

  • 语言模型 models : 通过标准接口调用语言模型

  • 输出解析器 output_parsers : 从模型输出中提取信息

提示 prompts

一个简单的提示模版创建过程:

  1. from langchain import PromptTemplate
  2. # 创建一个提示模版
  3. template = """/
  4. 你是一个数据开发的专家,负责为我解答相关的问题,言简意赅即可,我的问题是: {question}?
  5. """
  6. prompt = PromptTemplate.from_template(template)
  7. prompt.format(question="周期事实表与事务事实表有什么区别")
语言模型 models

LangChain提供了两种类型模型的接口和集成:

  • LLMs: 输入一个文本字符串并返回一个文本字符串的模型

  • 聊天模型: 由语言模型支持的模型,接受一个聊天消息列表作为输入并返回一个聊天消息**(封装)**

LLMs和聊天模型在细微但重要的方面有所不同。LangChain中的LLMs是指纯文本完成模型调用。它们包装的API接受一个字符串提示作为输入,并输出一个字符串完成。 聊天模型不是接受单个字符串作为输入,而是接受一个聊天消息列表作为输入。通常,这些消息带有发言者标签(通常是“系统”,“AI”和“人类”之一)。它们返回一个AI聊天消息作为输出。

LangChain的聊天模型,目前支持的消息类型有“AIMessage”,“HumanMessage”,“SystemMessage”和“ChatMessage” (“ChatMessage”接受一个任意角色参数)。

大多数时候,您只需处理“HumanMessage”,“AIMessage”和“SystemMessage”。

聊天模型的简单示例:

  1. from langchain_openai import ChatOpenAI
  2. from langchain.schema import (
  3. AIMessage,
  4. HumanMessage,
  5. SystemMessage
  6. )
  7. # 创建模型
  8. chat = ChatOpenAI()
  9. # 创建消息
  10. messages = [
  11. SystemMessage(content="您是中文翻译成西班牙语的得力助手。"),
  12. HumanMessage(content="上班使我快乐")
  13. ]
  14. # 模型调用消息
  15. chat.invoke(messages)
输出解析器 output_parsers

语言模型输出文本。但很多时候,您可能希望获得比仅文本更结构化的信息。这就是输出解析器的作用。

输出解析器的简单示例:

  1. from langchain.output_parsers import PydanticOutputParser
  2. from langchain_core.prompts import PromptTemplate
  3. from langchain_core.pydantic_v1 import BaseModel, Field
  4. from langchain_openai import ChatOpenAI
  5. # 创建模型
  6. model = ChatOpenAI()
  7. # 定义所需的数据结构
  8. class Joke(BaseModel):
  9. setup: str = Field(description="笑话的问题")
  10. punchline: str = Field(description="产生笑点的回答")
  11. # 设置解析器
  12. parser = PydanticOutputParser(pydantic_object=Joke)
  13. # 将解析器说明也注入到提示模板中
  14. prompt = PromptTemplate(
  15. template="用一个笑话回答用户的询问,其中笑话的问题以问号结尾.\n{format_instructions}\n{query}\n",
  16. input_variables=["query"],
  17. partial_variables={"format_instructions": parser.get_format_instructions()},
  18. )
  19. # 创建“链”
  20. chain = prompt | model | parser
  21. # 调用链并处理输出
  22. response = chain.invoke({"query": "给我讲个笑话"})
  23. print(response)

组件:Retrieval

许多LLM应用程序需要用户特定数据,这些数据不是模型的训练集的一部分. 完成这一任务的主要方法是通过检索增强生成(RAG). 在此过程中,检索外部数据,然后在生成步骤中将其传递给LLM。

LangChain为RAG应用程序提供了所有的构建模块(从简单到复杂)。

data_connection_diagram

主要有如下五个模块:

  • 文档加载器:

    从许多不同来源加载文档. LangChain提供了100多种不同的文档加载器,并与主要提供商(如AirByte和Unstructured)集成.

    LangChain提供了加载各种类型文档(HTML、PDF、代码)的集成,从各种位置(私人S3存储桶、公共网站)加载.

  • 文本转换器:

    检索的一个关键部分是仅获取文档的相关部分. 为了最好地准备文档以进行检索,这涉及几个转换步骤. 其中一个主要步骤是将大型文档分割(或分块)为较小的块.

    LangChain提供了几种不同的算法来完成此操作,以及针对特定文档类型(代码、markdown等)进行优化的逻辑.

  • 文本嵌入模型:

    检索的另一个关键部分是为文档创建嵌入. 嵌入捕捉文本的语义含义,使您能够快速高效地查找其他相似的文本.

    LangChain与25多个不同的嵌入提供商和方法进行集成, 从开源到专有API, 使开发者能够选择最适合您需求的一种.

    LangChain提供了标准接口,使您可以轻松切换模型

  • 向量存储:

    随着嵌入的兴起,出现了对支持这些嵌入的数据库的需求. LangChain与50多个不同的向量存储进行集成,从开源本地存储到云托管专有存储, 使开发者能够选择最适合您需求的一种.

    LangChain公开了标准接口,使您可以轻松切换向量存储.

  • 检索器:

    一旦数据在数据库中,您仍然需要检索它. LangChain支持许多不同的检索算法. LangChain支持易于入门的基本方法-即简单的语义搜索. 但是,LangChain还添加了一系列算法以提高性能.

    这些算法包括:

    1. 父文档检索器: 允许您为每个父文档创建多个嵌入,允许您查找较小的块但返回较大的上下文.

    2. 自查询检索器: 用户的问题通常包含对不仅仅是语义的东西的引用,而是表达一些最好用元数据过滤器表示的逻辑.自查询允许您从查询中解析出语义部分和查询中存在的其他元数据过滤器.

    3. 集合检索器: 有时您可能希望从多个不同的来源或使用多个不同的算法检索文档.集合检索器使您可以轻松实现此目的.

    RAG应用的简单示例:

  1. ###### 1.文档读取器
  2. from langchain_community.document_loaders import UnstructuredWordDocumentLoader
  3. file_path = '/home/logsget/langchain_demo/files/天津市突发事件总体应急预案.docx'
  4. print("读取文件路径:\n"+file_path+"\n")
  5. # 创建读取器
  6. loader = UnstructuredWordDocumentLoader(
  7. file_path, mode="single", strategy="fast",
  8. )
  9. # 读取文档
  10. docs = loader.load()
  11. # 文件内容预览
  12. print(docs[0].page_content[:500])
  13. ###### 2.文本切分器
  14. from langchain_text_splitters import RecursiveCharacterTextSplitter
  15. text_splitter = RecursiveCharacterTextSplitter(
  16. chunk_size=3000, chunk_overlap=200, add_start_index=True
  17. )
  18. all_splits = text_splitter.split_documents(docs)
  19. # 查看分片信息
  20. len(all_splits)
  21. # 查看分片内容长度
  22. len(all_splits[0].page_content)
  23. # 查看分片的元数据信息
  24. all_splits[5].metadata
  25. ###### 3.文本嵌入模型
  26. from langchain_openai import OpenAIEmbeddings
  27. embedding=OpenAIEmbeddings()
  28. ###### 4.向量存储
  29. from langchain_chroma import Chroma
  30. vectorstore = Chroma.from_documents(documents=all_splits, embedding=OpenAIEmbeddings())
  31. ###### 5.检索器
  32. retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 3})
  33. # 测试检索器
  34. retrieved_docs = retriever.invoke("突发事件分类分级")
  35. # 查看召回块数
  36. len(retrieved_docs)
  37. print(retrieved_docs[0].page_content)
  38. ###### 6.完整的RAG示例
  39. from langchain_core.prompts import PromptTemplate
  40. # 自定义的中文 RAG prompt 模板
  41. chinese_rag_prompt = PromptTemplate(
  42. template="你是问答任务的助手。使用以下检索到的上下文来回答问题。如果你不知道答案,就说你不知道。最多使用三个句子并保持答案简洁。:\n\n上下文信息:\n{context}\n\n问题:{question}\n",
  43. input_variables=["context", "question"]
  44. )
  45. chinese_rag_prompt
  46. from langchain_core.output_parsers import StrOutputParser
  47. from langchain_core.runnables import RunnablePassthrough
  48. from langchain_openai import ChatOpenAI
  49. # 重新定义检索器,避免上下文超限
  50. retriever_new = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 1})
  51. # 整合召回的文本块
  52. def format_docs(docs):
  53. return "\n\n".join(doc.page_content for doc in docs)
  54. # 创建chat模型
  55. chat_model = ChatOpenAI(model="gpt-4")
  56. # 创建“链”
  57. rag_chain = (
  58. {"context": retriever_new | format_docs, "question": RunnablePassthrough()}
  59. | chinese_rag_prompt
  60. | chat_model
  61. | StrOutputParser()
  62. )
  63. for chunk in rag_chain.stream("天津市应急预案如何对突发事件分类分级?"):
  64. print(chunk, end="", flush=True)

组件:Agents

使用语言模型来选择要采取的一系列操作。在链中,一系列操作被硬编码(在代码中)。在代理中,语言模型被用作推理引擎来确定要采取哪些操作以及按什么顺序。

有些应用程序不仅需要预定的LLM/其他工具的调用链,还可能需要根据用户的输入构建未知的链条。在这些类型的链条中,有一个"代理"(agent)可以访问一套工具。根据用户的输入,代理可以决定是否调用这些工具中的任何一个。

chains可以创建预定义的工具使用顺序:

agents可以控制工作流循环调用,并控制循环次数

使用代理 (Agents) ,LLM 可以编写和执行 Python 代码。它可以搜索信息,甚至查询 SQL 数据库。

以下是LangChain中不同Agent类型:

代理类型

适用模型类型

支持聊天记录

支持多输入工具

支持并行函数调用

需要的模型参数

应用场景

Tool Calling

Chat

tools

使用tool-calling模型时

OpenAI Tools

Chat

tools

使用新版的OpenAI模型(从1106版开始)时

OpenAI Functions

Chat

functions

使用OpenAI模型,或调整用于函数调用的开源模型时

XML

LLM

使用擅长处理XML的模型,如Anthropic模型时

Structured Chat

Chat

需要支持多输入工具时

JSON Chat

Chat

使用擅长处理JSON的模型时

ReAct

LLM

使用简单模型时

Self Ask With Search

LLM

使用简单模型且只有一个搜索工具时

这些代理类型根据不同的模型类型、工具支持以及功能需求,适用于不同的应用场景。更多详细信息可以访问LangChain的官方文档

要使用代理 (Agents) ,我们需要三样东西:

  • 一个基本的 LLM,

  • 我们将要进行交互的工具 Tools,

  • 一个控制交互的代理 (Agents) 。

使用数据库的Agent示例:

  1. from sqlalchemy import create_engine
  2. from langchain_community.utilities import SQLDatabase
  3. # 数据库连接信息
  4. username = 'root'
  5. password = 'MyNewPass1!'
  6. host = 'desk04v.mlprod.bjpdc.qihoo.net'
  7. port = '3306'
  8. database = 'test'
  9. engine = create_engine(f'mysql+mysqlconnector://{username}:{password}@{host}:{port}/{database}')
  10. db = SQLDatabase(engine)
  11. result = db.run("select * FROM courses LIMIT 5;")
  12. print(result)
  13. from langchain_community.agent_toolkits import create_sql_agent
  14. from langchain_openai import ChatOpenAI
  15. llm = ChatOpenAI(model="gpt-4o", temperature=0)
  16. agent_executor = create_sql_agent(llm, db=db, agent_type="openai-tools", verbose=True)
  17. agent_executor.invoke(
  18. "找到学分最高的课程"
  19. )

组件:Memory

提供存储过去交互信息的能力

Memory是LangChain中用于存储和更新上下文的组件,它可以让语言模型记住之前的信息和状态。你可以把它想象成语言模型的大脑,它可以存储短期记忆和长期记忆。根据不同的使用场景,LangChain内部定义的Memory有以下类型:

内存系统需要支持两个基本操作:读和写。每个链都定义了一些需要某些输入的核心执行逻辑。其中一些输入直接来自用户,但其中一些输入可以来自内存。在给定的运行中,一条链将与其内存系统交互两次。

Diagram illustrating the READ and WRITE operations of a memory system in a conversational interface.

相关记忆组件从不同角度解决机器对话固有的遗忘问题:

  • ConversationBufferMemory:基于对话历史缓存实现简单的全记忆

  • ConversationBufferWindowMemory:利用时间窗口机制控制记忆容量

  • ConversationSummaryMemory:通过提取语义摘要,记住关键信息并舍弃Noise

  • ConversationKGMemory:将对话实体和事件图谱化,实现知识级记忆

  • ConversationEntityMemory:连接外部实体知识,辅助机器人记忆和联想

ChatGPT的网页也应用了Memory功能:

应用:聊天机器人

对话记忆,它使得对话连贯,并且如果没有它,每个查询都将被视为完全独立的输入,而不考虑过去的交互。

具有和不具有对话记忆的情况

简单聊天机器人示例:

  1. from langchain_openai import ChatOpenAI
  2. from langchain_core.messages import AIMessage, HumanMessage
  3. from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
  4. from langchain.memory import ChatMessageHistory
  5. # 创建一个简单的“链”,带提示模版
  6. prompt = ChatPromptTemplate.from_messages(
  7. [
  8. (
  9. "system",
  10. "你是一个有用的助手。尽你所能回答所有问题。",
  11. ),
  12. MessagesPlaceholder(variable_name="chat_history"),
  13. ("human", "{input}"),
  14. ]
  15. )
  16. chain = prompt | chat
  17. from langchain_core.runnables.history import RunnableWithMessageHistory
  18. # 创建一个历史信息容器
  19. demo_ephemeral_chat_history_for_chain = ChatMessageHistory()
  20. # 把chain和历史记录容器,封装在一起
  21. chain_with_message_history = RunnableWithMessageHistory(
  22. chain,
  23. lambda session_id: demo_ephemeral_chat_history_for_chain,
  24. input_messages_key="input",
  25. history_messages_key="chat_history",
  26. )
  27. # 第一次调用,session_id 保证唯一即可
  28. chain_with_message_history.invoke(
  29. {"input": "什么是快乐星球?"},
  30. {"configurable": {"session_id": "xyr123"}},
  31. )
  32. # 第二次调用,同样的session_id
  33. chain_with_message_history.invoke(
  34. {"input": "我忘记了,我刚刚问了你啥?"}, {"configurable": {"session_id": "xyr123"}}
  35. )

组件:Callbacks

LangChain 提供了一个回调系统,允许用户在 LLM 应用的各个阶段埋钩(hook)。

这些回调函数允许用户在链中不同的阶段插入自己的逻辑,从而实现更灵活和可控的流程。这对于日志记录、监控、中断和控制流非常有用。

简单的回调示例:

  1. import asyncio
  2. from typing import Any, Dict, List
  3. from langchain.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler
  4. from langchain_core.messages import HumanMessage
  5. from langchain_core.outputs import LLMResult
  6. from langchain_openai import ChatOpenAI
  7. # 创建一个Callback
  8. class MyCustomSyncHandler(BaseCallbackHandler):
  9. # 在接收到新token时回调
  10. def on_llm_new_token(self, token: str, **kwargs) -> None:
  11. print(f"接受到新的token: {token}")
  12. # 创建一个流式的ChatOpenAI
  13. chat = ChatOpenAI(
  14. streaming=True,
  15. callbacks=[MyCustomSyncHandler()],
  16. )
  17. # 执行
  18. await chat.agenerate([[HumanMessage(content="给我讲个冷笑话")]])

LangChain的架构

LangChain在三个层面简化了LLM应用的构建生命周期:

  1. 开发:

    • langchain-core:(基础支持层)基础抽象和LangChain表达式语言(定义最底层的基础功能与协议)

    • langchain-community:(扩展层)第三方集成,例如openAI的模型调用、文件加载器、Agent工具包等

    • langchain:(核心层)包含主要功能组件,定义和执行链条(Chains)、代理(Agents)以及检索策略(Retrieval Strategies)

    • templates:提供一系列易于部署的参考架构

  2. 迭代:

    • LangSmith:一个开发者调试平台,用于调试、测试、评估和监控LLM应用程序,有效帮助开发者迭代AI应用程序

  3. 发布:

    • LangServer:将 LangChain 链部署为 REST API

生态:LangServe

LangServe 是 LangChain 提供的一个服务框架,旨在简化和加速大规模语言模型应用的部署和管理。它为开发者提供了一套工具和API,使他们能够轻松地将语言模型集成到各种应用中。

LangServe 的核心功能包括:

  1. 简化部署:提供易于使用的部署工具,支持多种后端环境。

  2. 高扩展性:能够处理大规模并发请求,确保应用稳定运行。

  3. 监控和日志:实时监控模型性能,提供详细的日志记录,便于故障诊断和优化。

  4. API 集成:提供丰富的API,方便与现有系统集成。

  5. 安全性:提供多层次的安全机制,保障数据和模型的安全。

通过 LangServe,开发者可以更专注于应用逻辑,而无需担心底层的部署和管理问题,从而加速智能应用的开发和上线。

client 和 server

LangServe安装

  1. # 同时安装客户端和服务端
  2. pip install "langserve[all]"

发布一个简单应用

1)启动server

  1. #!/home/logsget/.conda/envs/langchain_env/bin/python
  2. from fastapi import FastAPI
  3. from langchain.prompts import ChatPromptTemplate
  4. from langchain.chat_models import ChatAnthropic, ChatOpenAI
  5. from langserve import add_routes
  6. app = FastAPI(
  7. title="LangChain Server",
  8. version="1.0",
  9. description="A simple api server using Langchain's Runnable interfaces",
  10. )
  11. model = ChatOpenAI(model="gpt-4o")
  12. prompt = ChatPromptTemplate.from_template("请用中文回复我的问题,问题:{question}")
  13. add_routes(
  14. app,
  15. prompt | model,
  16. path="/openai",
  17. )
  18. if __name__ == "__main__":
  19. import uvicorn
  20. uvicorn.run(app, host="desk02v.mlprod.bjpdc.qihoo.net", port=8000)

2)url发送http请求

  1. curl --location --request POST 'http://desk02v.mlprod.bjpdc.qihoo.net:8000/openai/invoke' \
  2. --header 'Content-Type: application/json' \
  3. --data '{
  4. "input": {
  5. "question": "什么是快乐星球?"
  6. }
  7. }'
{"output":{"content":"“快乐星球”是中国大陆一部受欢迎的儿童科幻电视剧,首播于2004年。它讲述了一群小朋友在一个叫做“快乐星球”的虚拟世界中,经历各种冒险、解决问题并学习成长的故事。剧中的小朋友们通过使用高科技设备,进入这个虚拟世界,在那里他们遇到了各种挑战,并通过自己的智慧和团队合作来克服困难。该剧不仅充满了奇幻色彩和冒险元素,还融入了教育意义,教导孩子们勇敢、友爱和团队精神。","additional_kwargs":{},"response_metadata":{"token_usage":{"completion_tokens":128,"prompt_tokens":22,"total_tokens":150},"model_name":"gpt-4o","system_fingerprint":null,"finish_reason":"stop","logprobs":null},"type":"ai","name":null,"id":"run-c0f7e522-bacf-40cb-8e57-7c328e5302b5-0","example":false,"tool_calls":[],"invalid_tool_calls":[]},"metadata":{"run_id":"975fa851-345e-49a0-9fe3-1ed3804c204c","feedback_tokens":[]}}

3)playground 调试

http://desk02v.mlprod.bjpdc.qihoo.net:8000/openai/playground

生态:LangSmith

一个开发者调试平台,用于调试、测试、评估和监控LLM应用程序,有效帮助开发者迭代AI应用程序

有什么用:

  • 快速调试新的链、代理或一组工具

  • 创建和管理用于微调、少样本提示和评估的数据集

  • 运行回归测试,以便自信地进行开发

  • 捕获生产分析,以获取产品见解和持续改进

官网:LangSmith

文档:https://www.wpsshop.cn/w/小舞很执着/article/detail/1020030

推荐阅读
相关标签
  

闽ICP备14008679号