当前位置:   article > 正文

探索 LangChain、Hugging Face、LM Studio 等 AI 应用工具_dify lm studio

dify lm studio

1. LangChain v0.2

LangChain Logo

简介

LangChain 是一个旨在帮助开发人员构建由大型语言模型(LLMs)驱动的应用程序的框架。其主要特点如下:

  1. 模块化组件:LangChain 提供了多个独立或组合使用的模块。
    • 提示管理 Prompt:用于优化和处理提示的工具。
    • 记忆管理 Memory:管理跨不同交互的状态。
    • 链式操作 Chain:将多个 LLM 调用链接在一起,以创建更复杂的应用程序。
    • 代理 Agent:基于用户输入动态决策的系统。
    • 回调 Calback:用于日志记录和事件跟踪。
  2. 集成能力:LangChain 支持与多个 LLM 提供商的集成,如 OpenAI、Cohere 和 Hugging Face,适用于各种应用场景。
  3. 应用场景:常见应用包括聊天机器人、文档分析工具和自动推理系统。
  4. 社区和开源:作为一个开源项目,LangChain 拥有不断增长的社区,提供广泛的支持和贡献。

安装

安装 LangChain 到 Python 环境
LangChain作为一个框架由许多包组成

pip install langchain					// 0.2.10
pip install langcahin_community			// 0.2.9
pip install langchain_openai			// 0.1.17
  • 1
  • 2
  • 3

可以使用pip show package_name验证和查看版本

概念指南

LangChain 0.2

  • Architecture - 架构
    • langchain-core - LangChain 核心
    • Partner packages - 合作伙伴包 // 从 LangChain 社区分离的流行的包
    • langchain - LangChain
    • langchain-community - LangChain 社区
    • langgraph - LangGraph
    • langserve - LangServe
    • LangSmith - LangSmith
  • LangChain Expression Language (LCEL) - LangChain 表达式语言
    • Runnable interface - 可运行接口
  • Components - 组件
    • Chat models - 聊天模型
    • LLMs - 大型语言模型
    • Messages - 消息
    • Prompt templates - 提示模板
    • Example selectors - 示例选择器
    • Output parsers - 输出解析器
    • Chat history - 聊天历史
    • Documents - 文档
    • Document loaders - 文档加载器
    • Text splitters - 文本分割器
    • Embedding models - 嵌入模型
    • Vector stores - 向量存储
    • Retrievers - 检索器
    • Tools - 工具
    • Toolkits - 工具包
    • Agents - 代理
    • Callbacks - 回调
  • Techniques - 技术
    • Streaming - 流式处理
    • Structured output - 结构化输出
    • Retrieval - 检索
    • Text splitting - 文本分割
    • Evaluation - 评估
    • Tracing - 跟踪

Runnable interface
LangChain 引入了一个标准接口 “Runnable” 协议,旨在简化自定义链的创建和调用过程。该协议已在多个 LangChain 组件中实现,包括聊天模型、LLM、输出解析器、检索器和提示模板等。通过统一的接口,开发者可以更轻松地定义和调用自定义链。

Runnable 协议包含以下标准接口方法:

  • stream:以流的形式返回响应的块。
  • invoke:对单个输入调用链。
  • batch:对输入列表调用链。
组件输入类型输出类型
PromptDictionaryPromptValue
ChatModel单字符串、聊天消息列表或 PromptValueChatMessage
LLM单字符串、聊天消息列表或 PromptValueString
OutputParserLLM 或 ChatModel 的输出取决于解析器
Retriever单字符串文档列表
Tool单字符串或字典(取决于工具)取决于工具

简单试用

(1) 模型选择

使用本地模型,可以通过 LM Studio、Ollama 等方式下载

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio", max_tokens=256, temperature=0.0, top_p=0.9)

llm.invoke("你好")
  • 1
  • 2
  • 3
  • 4
  • 5
AIMessage(content='你好!很高兴能为你提供帮助。有什么问题或者需要我做的事情吗?', response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 24, 'total_tokens': 40}, 'model_name': 'Qwen/Qwen1.5-7B-Chat-GGUF/qwen1_5-7b-chat-q5_k_m.gguf', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-16d51193-ab6e-4462-bb18-9818b88962ec-0', usage_metadata={'input_tokens': 24, 'output_tokens': 16, 'total_tokens': 40})
  • 1

AIMessage.content 即为返回内容
————————————————————
使用 OpenAI API 访问云端模型,例如通过 OpenAI阿里云DashScope模型服务灵积 申请

from langchain_openai import ChatOpenAI

qwen = ChatOpenAI(model="qwen-turbo", api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", max_tokens=256, temperature=0.0, top_p=0.9)

qwen.invoke("你好")
  • 1
  • 2
  • 3
  • 4
  • 5
AIMessage(content='你好!有什么我可以帮助你的吗?', response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 9, 'total_tokens': 17}, 'model_name': 'qwen-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-d9ef21c6-2b64-4982-964a-d55b651865a2-0', usage_metadata={'input_tokens': 9, 'output_tokens': 8, 'total_tokens': 17})
  • 1

————————————————————
也可以直接调用 HuggingFace 上的模型

from langchain_community.llms.huggingface_hub import HuggingFaceHub

model = HuggingFaceHub(repo_id="openai-community/gpt2", task="text-generation", huggingfacehub_api_token="hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")

response = model.generate(["Hello, how are you?"])
print(response)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
generations=[[Generation(text="Hello, how are you?\n\nI'm a little bit of a nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd. I'm a big nerd")]] llm_output=None run=[RunInfo(run_id=UUID('0e4e40a7-d8ea-4a4b-8194-58c45bda66a5'))]
  • 1

==================================

(2) 基础操作

使用提示词模板 PromptTemplate

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
        ("system", "You are a helpful assistant that translates {input_language} to {output_language}."),
        ("user", "{input}")
])

chain = prompt | llm # 简单组合链,串联可运行对象

chain.invoke({"input": "I love programming.", "input_language": "English", "output_language": "French"})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
AIMessage(content="J'aime programmer.", response_metadata={'token_usage': {'completion_tokens': 5, 'prompt_tokens': 28, 'total_tokens': 33}, 'model_name': 'Qwen/Qwen1.5-7B-Chat-GGUF/qwen1_5-7b-chat-q5_k_m.gguf', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-6a97f040-3bfe-4658-a797-a3c3a4d175a1-0', usage_metadata={'input_tokens': 28, 'output_tokens': 5, 'total_tokens': 33})
  • 1

使用输出解析器 OutputParser

from langchain_core.output_parsers import StrOutputParser

str_output_parser = StrOutputParser() # 使用字符串解析器

chain = prompt | llm | str_output_parser

chain.invoke({"input": "I love programming.", "input_language": "English", "output_language": "French"})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
"J'aime programmer."
  • 1

使用链 Chain(结构化序列,相较于管道运算符 |,为创建复杂工作流和管理不同元素之间的交互提供了标准接口)

from langchain.chains.llm import LLMChain

chain = LLMChain(llm=llm, prompt=prompt) # 过时的,简单链直接使用 pipe 运算符 "|"

chain.invoke({"input": "I love programming.", "input_language": "English", "output_language": "Chinese"})
  • 1
  • 2
  • 3
  • 4
  • 5
{'input': 'I love programming.',
 'input_language': 'English',
 'output_language': 'Chinese',
 'text': '我喜欢编程。'}
  • 1
  • 2
  • 3
  • 4
(3) 更多操作
Runnable 调用
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

llm = ChatOpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio", max_tokens=256, temperature=0.0, top_p=0.9)

prompt = ChatPromptTemplate.from_messages([
        ("system", "You are a helpful assistant that translates {input_language} to {output_language}."),
        ("user", "{input}")
])
str_parser = StrOutputParser()

chain = prompt | llm | str_parser

# 使用 stream 进行流式输出
for s in chain.stream({"input": "Most LLM applications have a conversational interface......", "input_language": "English", "output_language": "Chinese"}):
    print(s, end="", flush=True)
# 大多数LLM(法学硕士)申请都有对话界面......

# 使用 batch 批量处理
chain.batch([{"input": "Hi", "input_language": "English", "output_language": "Chinese"}, {"input": "你好", "input_language": "Chinese", "output_language": "Japanese"}])
# ['你好!有什么我能帮助你的吗?', 'こんにちは!お元気ですか?']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
链的连接
next = ChatPromptTemplate.from_template("扩写这句话:{text}")

nextchain = {"text": chain} | next | llm | str_parser

nextchain.invoke({"input": "Hi", "input_language": "English", "output_language": "Chinese"})
# '您好!非常高兴能为您提供帮助......'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

也可以使用 lambda 临时函数

lambdachain = chain | (lambda input: {"text": input}) | next | llm | str_parser

lambdachain.invoke({"input": "Hi", "input_language": "English", "output_language": "Chinese"})
# '您好!非常高兴能为您提供帮助......'
  • 1
  • 2
  • 3
  • 4
Runnable 并行
from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import embeddings
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio")
embedding = embeddings.OpenAIEmbeddings(base_url="http://localhost:1234/v1", api_key="lm-studio")

texts = ["Ben在上海工作", "Alex喜欢吃苹果"]
vectorstore = FAISS.from_texts(texts, embedding)
retriever = vectorstore.as_retriever() # 检索器

template ="""仅根据以下上下文回答问题
{context}
问题: {question}
"""
prompt = ChatPromptTemplate.from_template(template)

retrieval_chain = (
    {"context": retriever, "question": RunnablePassthrough()} 
    # RunnablePassthrough() 不做修改,直接往后传递
    | RunnablePassthrough()
    | prompt
    | llm
    | StrOutputParser()
)

retrieval_chain.invoke("Ben在哪工作?")
# 'Ben在上海工作。'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

[ERROR] 'input' field must be a string or an array of strings
出现报错,对参数进行修改
embeddings.OpenAIEmbeddings(check_embedding_ctx_length=False,

使用 itemgetter

from operator import itemgetter

template ="""仅根据以下上下文回答问题
{context}
问题: {question}
使用{language}回答
"""
prompt = ChatPromptTemplate.from_template(template)

retrieval_chain = (
    {
        "context": itemgetter("question") | retriever,
        "question": itemgetter("question"),
        "language": itemgetter("language")
    }
    | prompt
    | llm
    | StrOutputParser()
)

retrieval_chain.invoke({"question": "Ben在哪工作?", "language": "英语"})
# 'Ben works at Huawei.'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

使用 RunnableParallel

from langchain_core.runnables import RunnableParallel

joke_chain = ChatPromptTemplate.from_template("讲一个笑话,主题是{topic}") | llm | StrOutputParser()
poem_chain = ChatPromptTemplate.from_template("写两行短诗,主题是{topic}") | llm | StrOutputParser()

map_chain = RunnableParallel(joke=joke_chain, poem=poem_chain)
map_chain.invoke({"topic": "熊"})
# {'joke': '当然可以!这是一个以熊为主题的笑话:\n\n为什么熊不喜欢在网上购物?\n\n因为他们总是会“熊”不住地把东西全部加入购物车,然后又忘记结账!',
# 'poem': '熊掌威猛赤岩铸,白鼻悠然林海游。\n冬眠长梦待春醒,山岭间荡起狩猎歌。'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
自定义函数 RunnableLambda
from langchain_core.runnables import RunnableLambda

def length_function(text):
    return len(text)
def _multiple_length_function(text1, text2):
    return len(text1) * len(text2)
def multiple_length_function(_dict):
    return _multiple_length_function(_dict["text1"], _dict["text2"])

prompt = ChatPromptTemplate.from_template("{a} + {b} 得多少")

chain = (
    {
        "a": itemgetter("x1") | RunnableLambda(length_function),
        "b": {"text1": itemgetter("x1"), "text2": itemgetter("x2")} | RunnableLambda(multiple_length_function)
    }
    | prompt
    | llm
    | StrOutputParser()
)

chain.invoke({"x1": "hello", "x2": "world"})
# '5 + 25 = 30'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
额外 assign 参数
# 链中增加额外参数 RunnablePassthrough.assign(y = ?)

from langchain_core.runnables import RunnableParallel, RunnablePassthrough

runnable = RunnableParallel(
    extra = RunnablePassthrough.assign(mult = lambda x: x["num"] * 3),
    modified = lambda x: x["num"] + 1
)
runnable.invoke({"num": 2})
# {'extra': {'num': 2, 'mult': 6}, 'modified': 3}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
(4) langchain.js

在 TypeScript 中使用 LangChain 0.2

安装

npm install langchain
npm i @langchain/openai
npm i @langchain/community
  • 1
  • 2
  • 3

试用

import { ChatOpenAI } from "@langchain/openai"

const llm = new ChatOpenAI({
    model: "Qwen/Qwen1.5-7B-Chat-GGUF",
    temperature: 0.9,
    configuration: {
        baseURL: "http://localhost:1234/v1",
    },
    apiKey: "lm-studio",
    maxTokens: 256,
    topP: 0.9
});

async function getAnswer(){
    const response = await llm.invoke("hi");
    console.log(response);
}

getAnswer();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
AIMessage {
  "id": "chatcmpl-zav1wc9nlvvuxaod5kjc",
  "content": "Hello! How can I help you today? If you have any questions or need assistance, feel free to ask.",
  "additional_kwargs": {},
  "response_metadata": {
    "tokenUsage": {
      "completionTokens": 23,
      "promptTokens": 24,
      "totalTokens": 47
    },
    "finish_reason": "stop"
  },
  "tool_calls": [],
  "invalid_tool_calls": [],
  "usage_metadata": {
    "input_tokens": 24,
    "output_tokens": 23,
    "total_tokens": 47
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2. Hugging Face

请添加图片描述

简介

Hugging Face 是一家专注于自然语言处理(NLP)和人工智能的技术公司,成立于2016年。它以提供先进的机器学习模型和工具而闻名,特别是在 NLP 领域。

  1. Transformers库
    Hugging Face最著名的项目是其开源的Transformers库。这是一个用于自然语言处理的强大工具包,支持多种预训练的变换器模型(如BERT、GPT、RoBERTa等),这些模型可以用于多种NLP任务,包括文本生成、文本分类、问答系统、翻译等。

  2. 数据集库
    除了Transformers库,Hugging Face还提供了Datasets库,这个库包含了大量标准化和预处理的数据集,方便用户快速获取和使用各种公开的数据集进行训练和测试。

  3. 模型中心
    Hugging Face的Model Hub是一个在线平台,用户可以在上面分享和下载预训练模型。这个平台极大地方便了研究人员和开发者,因为他们可以直接使用他人分享的模型或将自己的模型上传供其他人使用。

  4. Inference API
    Hugging Face提供的Inference API允许用户通过简单的API调用来使用预训练模型进行推理。这对于那些不想自己部署模型的用户来说非常方便。

  5. 社区与文档
    Hugging Face有一个活跃的社区,用户可以在论坛上讨论问题、分享经验。此外,该公司还提供了详尽的文档和教程,帮助用户快速上手其工具和库。

  6. 开源与企业服务
    Hugging Face的许多工具和库都是开源的,任何人都可以免费使用和贡献。此外,他们还为企业用户提供高级服务,如定制模型、技术支持等,以满足商业应用的需求。

如何调用API

登陆账号后,在设置-访问令牌中创建 new token
浏览模型平台,或者直接查找想要使用的模型
openai-community/gpt2
可以使用 Deploy-Inference API (serverless) 中的格式,也可以使用 LangChain 访问模型
Inference API

3. LM Studio

简介

LM Studio 是一款桌面应用程序,旨在让用户在本地计算机上运行本地大语言模型(LLMs)。通过这款工具,用户可以方便地使用强大的语言模型进行各种任务,而无需依赖云服务。

  • LM Studio 服务器
    LM Studio 提供了类似 OpenAI API 的服务器接口,支持以下端点:
    /v1/chat/completions:用于生成聊天对话的补全。
    /v1/completions:用于生成文本补全。
    /v1/embeddings:用于生成文本嵌入。
    这些接口支持各种本地 LLMs,包括 Llama 3 和 Phi-3 等,使得用户可以在本地进行高效的模型推理和应用开发。

  • 文本嵌入(Text Embeddings)
    LM Studio 的嵌入服务器能够本地生成文本嵌入,非常适用于基于检索的生成(RAG)应用。通过这种方式,用户可以高效地生成文本的向量表示,用于信息检索、相似性计算、推荐系统等场景。

  • 从 JS/TS/Node 程序化地使用 LLMs
    LM Studio 允许用户在 JavaScript、TypeScript 和 Node.js 代码中程序化地加载和使用大语言模型。这样,开发者可以轻松地将强大的语言模型功能集成到自己的应用程序中,实现自动化文本处理、智能回复生成、文本分析等功能。

LM Studio 服务器

(1) 浏览或查找模型并下载
在这里插入图片描述
(2) 点击左侧的<->,打开本地服务器界面
在这里插入图片描述
(3) 在顶部选择使用的模型,点击 Start Server 按钮启动本地服务器
在这里插入图片描述
(4) 此时,LM Studio 已经准备好接受 API 请求,可以最小化窗口
(5) 支持的请求负载参数(可以参考 OpenAI API 端点的参数)

参数说明
model指定使用的语言模型的名称,例如 “Llama 3” 或 “Phi-3”。
top_p核采样概率阈值,范围 0 到 1,较低值限制候选词汇选择。
top_k最高 K 采样的 K 值,0 表示不使用最高 K 采样。
messages聊天生成的消息列表,包含 role(用户、系统或助手)和 content(消息内容)。
temperature控制生成文本随机性,范围 0 到 1,较高值生成更多样化输出。
max_tokens生成文本的最大标记数,控制生成内容的长度。
stream是否启用流式输出,true 表示逐步返回生成文本。
stop指定一个或多个停止序列,生成文本包含这些序列时停止生成。
presence_penalty控制词汇新颖性,范围 -2.0 到 2.0,较高值鼓励使用更多新词。
frequency_penalty控制词汇重复性,范围 -2.0 到 2.0,较高值减少词汇重复出现。
logit_bias调整某些词汇的出现概率,指定词汇的 logit 偏差。
repeat_penalty控制整体文本重复性,较高值减少文本重复。
seed指定随机数生成器的种子,实现生成结果的可重复性。

JavaScript/TypeScript SDK

lmstudio.js

import { LMStudioClient } from "@lmstudio/sdk";

async function main() {
  const client = new LMStudioClient();
  // 加载模型
  const llama3 = await client.llm.load("lmstudio-community/Meta-Llama-3-8B-Instruct-GGUF", {
    config: { gpuOffload: "max" },
  });
  // 创建文本补全预测
  const prediction = llama3.complete("The meaning of life is");
  // 输出
  for await (const text of prediction) {
    process.stdout.write(text);
  }
}
main();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4. Dify.AI

请添加图片描述

简介

Dify.AI 是一个 LLMOps 平台,旨在简化 AI 本地应用程序的开发和运营。

  • 可视化提示编排:通过可视化界面创建和调试提示,快速完成 AI 应用程序的开发。
  • 长上下文集成:自动使用数据作为上下文预处理文本,简化大规模输入管理。
  • 基于 API 的开发:提供后端即服务功能,允许直接访问 Web 应用程序或 API 集成,无需复杂的后端设置。
  • 数据标注和改进:提供工具用于审查 AI 日志、标注数据并持续改进 AI 性能。
  • 工具集成:支持与 Google 搜索、DALL·E 和 Wolfram Alpha 等多种工具集成,增强 AI 代理的功能。
  • 社区和开源:由经验丰富的开发者团队构建,Dify.AI 致力于开源开发,推动 AI 创新的协作生态系统。
功能Dify.AILangChainFlowiseOpenAI Assistants API
编程方法API + 面向应用Python 代码面向应用基于 API
支持的 LLMs丰富的种类丰富的种类丰富的种类仅支持 OpenAI
RAG 引擎
代理
工作流
可观测性
企业功能(SSO/访问控制)
本地部署

安装

首先,安装 Docker
GitHub 下载文件
打开终端,在 dify 目录下,输入指令,通过 docker 部署应用

cd docker				# 进入 docker 文件夹
cp .env.example .env	# 拷贝环境文件
docker compose up -d	# 启动 docker-compose.yml 中的服务,运行容器
  • 1
  • 2
  • 3

使用

容器运行成功后,通过浏览器打开应用,默认localhost:80
在这里插入图片描述
在设置中可以添加模型,可以使用 OpenAI API、Hugging Face 等,也可以使用本地AI
在这里插入图片描述
创建应用,可以创建空白应用也可以打开模板
在这里插入图片描述

附录

LangChain 官方网站
Hugging Face 官方网站
LM Studio 官方网站
Dify.AI 官方网站
Dify GitHub
Docker 官方网站

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/938885
推荐阅读
相关标签
  

闽ICP备14008679号