侧边栏壁纸
博主头像
码森林博主等级

一起走进码森林,享受编程的乐趣,发现科技的魅力,创造智能的未来!

  • 累计撰写 145 篇文章
  • 累计创建 73 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录
AI

LangChain 入门篇

码森林
2023-06-06 / 0 评论 / 0 点赞 / 922 阅读 / 5,950 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-06-06,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

本教程将简要介绍如何使用 LangChain 构建端到端语言模型应用程序。

安装

首先,使用以下命令安装 LangChain:

pip install langchain
# or
conda install langchain -c conda-forge

环境设定

使用 LangChain 通常需要与一个或多个模型提供程序、数据存储、 API 等集成。

对于这个例子,我们将使用 OpenAI 的 API,所以我们首先需要安装他们的 SDK:

pip install openai

然后我们需要在终端设置环境变量:

export OPENAI_API_KEY="..."

或者,你可以在 Jupiter 教程(或 Python 脚本)内部完成:

import os
os.environ["OPENAI_API_KEY"] = "..."

构建语言模型(Language Model)应用程序:LLM

现在我们已经安装了 LangChain 并设置了我们的环境,我们可以开始构建我们的语言模型应用程序了。

LangChain 提供了许多可用于构建语言模型应用程序的模块。

模块可以组合起来创建更复杂的应用程序,或者单独用于简单的应用程序。

示例代码

# 导入 llm 包装器
from langchain.llms import OpenAI
import os

# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"

# 初始化 llm 包装器
llm = OpenAI(temperature=0)

# 调用
text = "用中文告诉我你能做什么?"
print(llm(text))

运行结果

我能帮助你解决问题,提供建议,分享知识,提供支持,帮助你实现目标,激发你的创造力,提升你的能力,提高你的自信心,激励你做出更好的决定,帮助你发现自己的潜能,指导你走出困境,激励你追求梦想,帮助你发掘潜力,提供帮助

提示模板(PromptTemplate):管理 LLM 的提示

通常在应用程序中使用 LLM 时,不会将用户输入直接发送到 LLM。

相反,您可能接受用户输入并构造一个提示符,然后将其发送给 LLM。

例如:我们传入的文本被硬编码为询问一家生产矿泉水的公司的取什么名字好。在这个虚构的服务中,我们希望只获取描述公司业务的用户输入,然后用这些信息格式化提示符。

使用LangChain,这个事情变得很简单!

示例代码

# 导入 prompt 包装器
from langchain.prompts import PromptTemplate
# 导入 llm 包装器
from langchain.llms import OpenAI
import os


# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"

# 初始化 llm 包装器
llm = OpenAI(temperature=0)

# 设置提示模板
prompt = PromptTemplate(
    input_variables=["product"],
    template="对于一家生产{product}的公司来说,取什么名字好?"
)

# 调用
print(llm(prompt.format(product="矿泉水")))

运行结果

可以取一些有趣的名字,比如:
1. 水源天下
2. 水晶宝藏
3. 水滴活力
4. 水果清新
5. 水晶之源
6. 水晶之泉
7. 水晶之恋
8. 水晶之心
9. 水晶之灵
10. 水晶之梦

链(Chain):在多步骤的工作流中组合 LLM 和提示

到目前为止,我们已经自己处理了单独的 PromptTemplateLLM

但是,真正的应用程序不仅仅是一个,而是它们的组合。

在 LangChain,链是由链组成的,可以是 LLM 这样的原始链,也可以是其他链。

最核心的链类型是 LLMChain,它由 PromptTemplateLLM 组成。

它接受用户输入,使用 PromptTemplate 对其进行格式化,然后将格式化后的响应传递给LLM

代码示例

# 导入 prompt 包装器
from langchain.prompts import PromptTemplate
# 导入 llm 包装器
from langchain.llms import OpenAI
# 导入 chains 包装器
from langchain.chains import LLMChain
import os

os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"

# 初始化 llm 包装器
llm = OpenAI(temperature=0)

# 设置提示模板
prompt = PromptTemplate(
    input_variables=["product"],
    template="对于一家生产{product}的公司来说,取什么名字好?"
)

# 创建一个非常简单的链: 它接受用户输入,用它格式化提示符,然后将它发送到 LLM
chain = LLMChain(llm=llm, prompt=prompt)

# 运行链
print(chain.run("矿泉水"))

运行结果

可以取一些有趣的名字,比如:
1. 水源天下;
2. 水晶宝藏;
3. 水清洁;
4. 水滋养;
5. 水源之宝;
6. 水源之源;
7. 水源之光;
8. 水源之恩;
9. 水源之灵;
10. 水源之美。

运用代理(Agent): 基于用户输入的动态调用链

链运行在一个预先确定的顺序。

但是代理不再这样做: 它们使用 LLM 来确定要执行哪些操作以及按照什么顺序执行。

操作可以使用工具并观察其输出,也可以返回给用户。

如果使用得当,效果可以非常强大。

在本教程中,将向您展示如何通过最简单、最高级别的 API 轻松使用代理。

为了运好代理,您应该理解以下概念:

  • 工具(tools): 执行特定任务的功能。这可以是: Google 搜索、数据库查找、 Python REPL、其他链。工具的接口目前是一个函数,预计将有一个字符串作为输入,一个字符串作为输出。
  • 大语言模型(LLM): 为代理提供动力的语言模型。
  • 代理(agents): 要使用的代理。这应该是引用支持代理类的字符串。

代码示例

本文示例采用 SerpAPI,地址:https://serpapi.com/。

对于本例,您还需要安装 SerpAPI Python 包。

pip install google-search-results

具体代码如下:

# 导入 agents 包装器
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
# 导入 llm 包装器
from langchain.llms import OpenAI
import os

# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"
# 设置代理接口的apikey  https://serpapi.com/
os.environ["SERPAPI_API_KEY"] = "***"

# 首先,让我们加载将用于控制代理的语言模型。
llm = OpenAI(temperature=0)

# 接下来,让我们加载一些要使用的工具。请注意,“llm-math”工具使用llm,因此我们需要将其传入
tools = load_tools(["serpapi", "llm-math"], llm=llm)

# 最后,让用工具、语言模型和要使用的代理类型初始化一个代理。设置verbose=True可以让我们看到提示符。
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)

# 运行代理
agent.run("现在的美国总统是谁? 他当前的年龄是多少?")

运行结果

> Entering new AgentExecutor chain...
 I should search for this on the internet. 
Action: Search
Action Input: Current US President age
Observation: 80 years
Thought: I can confirm this by using the calculator
Action: Calculator
Action Input: 80
Observation: Answer: 80
Thought: I now know the final answer
Final Answer: 现任美国总统是拜登,他现年80岁。

> Finished chain.

可以看到代理执行器会自动调用代理,取回动作和动作输入,用相应的输入调用动作引用的工具,获取工具的输出,然后将所有信息传递回代理以获取它应该采取的下一步行动。

内存(Memory):向链和代理添加状态

到目前为止,我们经历过的所有工具和代理都是无状态的的。

但是通常,您可能希望链或代理具有某种“内存”概念,以便它可以记住关于其以前的交互的信息。

最简单明了的例子就是在设计一个聊天机器人时,你想让它记住之前的消息,这样它就可以利用这些消息的上下文来进行更好的对话。这是一种“短期记忆”。

在更复杂的一面,你可以想象一个链条/代理随着时间的推移记住关键信息,这将是一种形式的“长期记忆”。

LangChain 提供了几个专门为此目的创建的链。 本教程使用其中一个链( ConversationChain ) 和两种不同类型的内存来完成操作。

默认情况下,, ConversationChain 有一个简单的内存类型,它记住所有以前的输入/输出,并将它们添加到传递的上下文中。

示例代码

# 导入 langchain 包
from langchain import OpenAI, ConversationChain

import os

# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"

# 初始化 llm 包装器
llm = OpenAI(temperature=1)

# 创建 ConversationChain
conversation = ConversationChain(llm=llm, verbose=True)

output = conversation.predict(input="你好!")
print(output)

output = conversation.predict(input="告诉我你能做什么?")
print(output)

运行结果

> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:

Human: 你好!
AI:

> Finished chain.
 你好!很高兴认识你!我叫Eric,你呢?


> Entering new ConversationChain chain...
Prompt after formatting:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:
Human: 你好!
AI:  你好!很高兴认识你!我叫Eric,你呢?
Human: 告诉我你能做什么?
AI:

> Finished chain.
 很高兴回答你的问题!我可以为你做很多事情,包括帮助你处理困难的数学作业,提供实用的技术信息,建议适合你需求的产品和服务,以及帮助你执行经常出现的任务,比如搜索,视频播放等。

构建语言模型(Language Model)应用:聊天模型(Chat Model)

您可以使用聊天模型而不是 LLM,聊天模型是语言模型的一种变体。

虽然聊天模型使用的是底层的语言模型,但它们公开的接口有些不同: 它们没有公开“文本输入、文本输出”API,而是公开了一个接口,其中“聊天消息”是输入和输出。

您可以通过向聊天模型传递一条或多条消息来完成聊天,响应将是一条消息。

LangChain 中当前支持的消息类型是 AIMessageHumanMessageSystemMessage 、和 ChatMessageChatMessage 接受任意角色参数。大多数时候,您只需要处理 HumanMessage , AIMessage , 和 SystemMessage

示例代码

通过传入单个消息来完成。

# 导入 langchain 相关包
from langchain.chat_models import ChatOpenAI
from langchain.schema import (
    AIMessage,
    HumanMessage,
    SystemMessage
)
import os

# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"

chat = ChatOpenAI(temperature=0)

print(chat([HumanMessage(content="翻译以下这段话,从英文翻译为中文: I love programming.")]))

运行结果

content='我喜欢编程。' additional_kwargs={} example=False

示例代码

还可以为 OpenAI 的 gpt-3.5-turbo 和 gpt-4 模型传递多条消息。

# 导入 langchain 相关包
from langchain.chat_models import ChatOpenAI
from langchain.schema import (
    AIMessage,
    HumanMessage,
    SystemMessage
)
import os

# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"

chat = ChatOpenAI(temperature=0)


messages = [
    SystemMessage(content="你是一个乐于助人的助手,能把英语翻译成中文。"),
    HumanMessage(content="翻译以下这段话,从英文翻译为中文: I love programming.")
]

print(chat(messages))

运行结果

content='我喜欢编程。' additional_kwargs={} example=False

示例代码

可以更进一步,使用generate为多组消息生成完成,这将返回一个带有附加message参数的 LLMResult。您可以从这个 LLMResult 中获取字符令牌的使用情况(token_usage)。

# 导入 langchain 相关包
from langchain.chat_models import ChatOpenAI
from langchain.schema import (
    AIMessage,
    HumanMessage,
    SystemMessage
)
import os

# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"

chat = ChatOpenAI(temperature=0)

batch_messages = [
    [
        SystemMessage(content="你是一个乐于助人的助手,能把英语翻译成中文。"),
        HumanMessage(content="翻译以下这段话,从英文翻译为中文: I love programming.")
    ],
    [
        SystemMessage(content="你是一个乐于助人的助手,能把英语翻译成中文。"),
        HumanMessage(content="翻译以下这段话,从英文翻译为中文: I love artificial intelligence.")
    ],
]

result = chat.generate(batch_messages)

print(result)
print(result.generations)
print(result.llm_output["token_usage"])
print(result.llm_output["model_name"])

运行结果

generations=[[ChatGeneration(text='我喜欢编程。', generation_info=None, message=AIMessage(content='我喜欢编程。', additional_kwargs={}, example=False))], [ChatGeneration(text='我喜欢人工智能。', generation_info=None, message=AIMessage(content='我喜欢人工智能。', additional_kwargs={}, example=False))]] llm_output={'token_usage': {'prompt_tokens': 135, 'completion_tokens': 19, 'total_tokens': 154}, 'model_name': 'gpt-3.5-turbo'}

[[ChatGeneration(text='我喜欢编程。', generation_info=None, message=AIMessage(content='我喜欢编程。', additional_kwargs={}, example=False))], [ChatGeneration(text='我喜欢人工智能。', generation_info=None, message=AIMessage(content='我喜欢人工智能。', additional_kwargs={}, example=False))]]

{'prompt_tokens': 135, 'completion_tokens': 19, 'total_tokens': 154}

gpt-3.5-turbo

聊天提示模板(ChatPromptTemplate)

与 LLM 类似,您可以通过使用 MessagePromptTemplate来使用模板。

可以从一个或多个 MessagePromptTemplate 生成 ChatPromptTemplate

您可以使用 ChatPromptTemplateformat_tip 将返回一个 PromptValue

您可以将其转换为字符串或 Message 对象,具体取决于您是想将格式化的值用作 llm 或聊天模型的输入。

为了方便起见,在模板上公开了一个 from_template 方法。如果你使用这个模板,它看起来是这样的:

示例代码

# 导入 langchain 相关包
from langchain.chat_models import ChatOpenAI
from langchain.prompts.chat import (
    ChatPromptTemplate,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
)
import os
# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"

chat = ChatOpenAI(temperature=0)

system_template = "你是一个乐于助人的助手,能把{input_language}翻译成{output_language}。"
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)
human_template = "{text}"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])

# 从格式化的消息中获取聊天完成
print(chat(chat_prompt.format_prompt(input_language="英文", output_language="俄语",
                                     text="I love programming.").to_messages()))

运行结果

content='Я люблю программирование.' additional_kwargs={} example=False

带聊天模型(Chat Model)的链(Chain)

LLMChain也可以用于聊天模型。

示例代码

# 导入 langchain 相关包
from langchain import LLMChain
from langchain.chat_models import ChatOpenAI
from langchain.prompts.chat import (
    ChatPromptTemplate,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
)
import os
# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"

chat = ChatOpenAI(temperature=0)

system_template = "你是一个乐于助人的助手,能把{input_language}翻译成{output_language}。"
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)
human_template = "{text}"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])

chain = LLMChain(llm=chat, prompt=chat_prompt)
print(chain.run(input_language="English", output_language="Chinese", text="I love programming."))

运行结果

我喜欢编程。

带聊天模型(Chat Model)的代理(Agent)

代理也可以与聊天模型一起使用,您可以使用 AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION作为代理类型来初始化一个聊天模型。

示例代码

# 导入 langchain 相关包
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.chat_models import ChatOpenAI
from langchain.llms import OpenAI
import os

# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"
#  https://serpapi.com/
os.environ["SERPAPI_API_KEY"] = "***"

# 首先,加载将用于控制代理的语言模型。
chat = ChatOpenAI(temperature=0)

# 接下来,加载一些要使用的工具。请注意,“llm-math”工具使用llm,因此我们需要将其传入。
llm = OpenAI(temperature=0)
tools = load_tools(["serpapi", "llm-math"], llm=llm)

# 最后,用工具、语言模型和要使用的代理类型初始化一个代理。
agent = initialize_agent(tools, chat, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True)

# 运行测试
agent.run("现在的美国总统是谁? 他当前的年龄是多少?")

运行结果

> Entering new AgentExecutor chain...
Question: 现在的美国总统是谁? 他当前的年龄是多少?
Thought: 这是一个关于当前事件的问题,我需要使用搜索工具来回答这个问题。
Action:
```
{
  "action": "Search",
  "action_input": "current US president age"
}
```

Observation: 80 years
Thought:这个结果不太对,我需要再搜索一下。
Action:
```
{
  "action": "Search",
  "action_input": "current US president"
}
```


Observation: Joe Biden
Thought:我需要再次使用搜索工具来回答第二个问题。
Action:
```
{
  "action": "Search",
  "action_input": "Joe Biden age"
}
```


Observation: 80 years
Thought:Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 1.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-3wJBVyzj58CYqPrMSCGpLdBk on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..
Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 2.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-3wJBVyzj58CYqPrMSCGpLdBk on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..
Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-3wJBVyzj58CYqPrMSCGpLdBk on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..
我已经找到了答案。
Final Answer: 美国现任总统是乔·拜登,他当前的年龄是80岁。

> Finished chain.

记忆内存:向链和代理添加状态

您可以对链使用 Memory,对代理使用聊天模型进行初始化。

这与 LLM 的 Memory 之间的主要区别在于,我们不需要将以前的所有消息压缩成一个字符串,而是可以将它们保留为自己独特的内存对象。

示例代码

# 导入 langchain 相关包
from langchain.prompts import (
    ChatPromptTemplate,
    MessagesPlaceholder,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate
)

from langchain.chains import ConversationChain
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory
import os

# 配置你的OpenAI key和代理地址
os.environ["OPENAI_API_KEY"] = "***"
os.environ["OPENAI_API_BASE"] = "http://{your proxy url}/v1"

system_template = '''
你现在是一名优秀的律师。
你了解中国法律的所有知识,熟悉中国法律的条例和审判案例。
你具备公正、守法、严肃等特质。
我想让你像中国律师一样使用中国律师会使用的语气、方式和词汇进行回应和回答,你的回答应该是简洁的、不要超过600字。
'''
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)

human_template = "{input}"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)

prompt = ChatPromptTemplate.from_messages([
    system_message_prompt,
    MessagesPlaceholder(variable_name="history"),
    human_message_prompt
])

llm = ChatOpenAI(temperature=0)
memory = ConversationBufferMemory(return_messages=True)
conversation = ConversationChain(memory=memory, prompt=prompt, llm=llm)

output = conversation.predict(input="你好!")
print(output)
output = conversation.predict(input="彩礼钱不返还协议书有效吗?")
print(output)
output = conversation.predict(input="那如何保证我在婚姻中的个人权益呢?")
print(output)

运行结果

您好!有什么我可以帮助您解答的法律问题吗?


根据《中华人民共和国合同法》的规定,合同是当事人自愿订立的平等协议,具有法律效力。因此,如果彩礼钱不返还协议书是经过当事人自愿协商并签订的,且没有违反法律法规的规定,那么该协议书是有效的。
但是需要注意的是,如果彩礼钱不返还协议书中的内容违反了法律法规的规定,例如违反了《中华人民共和国婚姻法》的相关规定,那么该协议书是无效的。此外,如果协议书中的内容涉及到违反公序良俗的行为,也会被认为是无效的。
因此,如果您遇到了彩礼钱不返还的问题,建议您先了解相关法律法规的规定,再与对方进行协商,如果可以达成协议,可以签订协议书来明确双方的权利义务。但是需要注意的是,协议书的内容必须合法合规,否则可能会被认为是无效的。


在婚姻中,保障个人权益的最基本方式是依法结婚。根据《中华人民共和国婚姻法》的规定,男女双方自愿结婚,应当依法办理结婚登记手续。只有依法结婚,才能够享有婚姻法律所赋予的权利和义务。
此外,婚姻中的个人权益还包括以下方面:
1. 财产权益:在婚姻中,夫妻双方的财产权益应当平等,夫妻双方应当共同管理家庭财产,共同承担家庭债务。如果夫妻双方在婚前签订了财产协议,应当依法履行。
2. 人身权益:在婚姻中,夫妻双方应当相互尊重,不得侵犯对方的人身权益。如果夫妻双方存在家庭暴力等问题,可以向公安机关报案或者向法院提起诉讼。
3. 子女权益:在婚姻中,夫妻双方应当共同承担抚养子女的责任,保障子女的合法权益。如果夫妻双方存在子女抚养问题,可以向法院提起诉讼。
总之,在婚姻中,保障个人权益的关键是依法结婚,并且在婚姻中遵守法律法规,共同维护家庭和睦、平等、和谐的氛围。如果遇到婚姻中的问题,可以寻求法律援助或者咨询律师的意见。

LangChain 概念篇:https://www.masenlin.com/archives/langchain-gai-nian-pian

学习更多内容:https://www.masenlin.com/

关注公众号,回复“LangChain”获取用于测试 OpenAI 代理地址及 ChatGPT Key。

扫码_搜索联合传播样式-标准色版

0

评论区