LangChain 高级功能实战教程(上)

2025博客之星年度评选已开启 10w+人浏览 1.4k人参与

本教程基于 LangChain v1 的官方中文文档,系统讲解 LangChain 中用于构建生产级 AI 智能体(Agent)的关键概念与能力:守卫机制运行时系统上下文工程长期记忆MCP 工具协议人机协同检索增强(RAG)
上篇 聚焦于智能体的安全性上下文管理工具集成下篇 将深入长期记忆、人机协同与检索增强。


第一部分:安全与守卫(Guardrails)

1. 什么是守卫(Guardrails)?

守卫是在智能体运行的关键环节(如输入、输出、工具调用)插入的安全检查机制,用于:

  • 防止敏感信息(如邮箱、信用卡)泄露
  • 阻止提示注入攻击
  • 过滤不当/有害内容
  • 验证输出是否合规或准确

守卫分为两类:

类型特点适用场景
确定性守卫基于规则(如关键词、正则)快速、低成本、可预测
基于模型的守卫使用 LLM 判断语义能识别细微问题,但较慢、较贵

2. 内置守卫:PII(个人身份信息)检测

LangChain 提供 PIIMiddleware 中间件,可自动检测并处理常见敏感信息:

from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware

# 创建一个具备 PII 守卫的智能体
agent = create_agent(
    model="openai:gpt-4o",
    tools=[],  # 本例暂不需要工具
    middleware=[
        # 检测用户输入中的电子邮件,用 [REDACTED_EMAIL] 替换
        PIIMiddleware(
            "email",          # 要检测的 PII 类型
            strategy="redact", # 处理策略:redact(脱敏)、mask(遮盖)、hash(哈希)、block(阻断)
            apply_to_input=True,  # 应用于用户输入
        ),
        # 检测信用卡号,只保留后 4 位,其余用 * 遮盖
        PIIMiddleware(
            "credit_card",
            strategy="mask",
            apply_to_input=True,
        ),
    ],
)

# 用户输入包含敏感信息
result = agent.invoke({
    "messages": [{
        "role": "user",
        "content": "我的邮箱是 alice@example.com,信用卡是 4532-1234-5678-9010,密钥是 sk-abcdefghijklmnopqrstuvwxyz123456"
    }]
})
# 输出内容中:
# - 邮箱会被替换为 [REDACTED_EMAIL]
# - 信用卡会变成 ****-****-****-9010
# - 因为检测到 API 密钥,执行会被直接阻断并报错!

提示apply_to_output=True 可让守卫也检查 AI 的回复,防止它“不小心”泄露数据。


3. 自定义守卫:关键词过滤(确定性)

阻止包含“黑客”“恶意软件”等词的请求:

from typing import Any
from langchain.agents.middleware import before_agent, AgentState, hook_config
from langgraph.runtime import Runtime

# 定义要禁止的关键词
禁止关键词列表 = ["hack", "exploit", "malware", "破解", "病毒"]

# 使用装饰器定义“智能体执行前”的守卫
@before_agent(can_jump_to=["end"])  # 允许直接跳转到结束,跳过后续步骤
def 内容过滤守卫(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
    """
    检查用户第一条消息是否包含禁止关键词。
    如果包含,立即返回拒绝消息并终止执行。
    """
    # 获取用户消息
    if not state["messages"]:
        return None
    第一条消息 = state["messages"][0]
    if 第一条消息.type != "human":  # 只检查用户输入
        return None
    
    内容 = 第一条消息.content.lower()
    
    # 检查是否包含任意禁止词
    for 关键词 in 禁止关键词列表:
        if 关键词 in 内容:
            # 返回一个“跳转到结束”的指令,并附上拒绝消息
            return {
                "messages": [{
                    "role": "assistant",
                    "content": "我无法处理包含不当内容的请求。请重新表述您的问题。"
                }],
                "jump_to": "end"  # 跳过模型调用和工具执行
            }
    return None  # 无问题,继续正常执行

# 创建智能体并挂载守卫
from langchain.agents import create_agent
agent = create_agent(
    model="openai:gpt-4o",
    tools=[],
    middleware=[内容过滤守卫],
)

# 测试:该请求会被拦截
结果 = agent.invoke({
    "messages": [{"role": "user", "content": "怎么破解别人电脑?"}]
})
print(结果["messages"][-1].content)
# 输出:我无法处理包含不当内容的请求。请重新表述您的问题。

4. 自定义守卫:LLM 安全审查(基于模型)

让另一个小模型(如 gpt-4o-mini)审查主模型的输出是否安全:

from langchain.agents.middleware import after_agent, AgentState, hook_config
from langgraph.runtime import Runtime
from langchain_core.messages import AIMessage
from langchain.chat_models import init_chat_model

# 初始化一个轻量级模型用于安全审查
安全审查模型 = init_chat_model("openai:gpt-4o-mini")

@after_agent(can_jump_to=["end"])
def 安全审查守卫(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
    """
    在智能体生成最终回复后,用另一个模型判断其是否安全。
    """
    if not state["messages"]:
        return None
    最后一条消息 = state["messages"][-1]
    if not isinstance(最后一条消息, AIMessage):
        return None  # 不是 AI 回复,跳过
    
    # 构造审查提示
    审查提示 = f"""
请判断以下 AI 回复是否安全、适当、无害。
只需回答“SAFE”或“UNSAFE”。

AI 回复:{最后一条消息.content}
"""
    # 调用审查模型
    审查结果 = 安全审查模型.invoke([{"role": "user", "content": 审查提示}])
    
    # 如果返回 UNSAFE,则拦截
    if "UNSAFE" in 审查结果.content:
        return {
            "messages": [{
                "role": "assistant",
                "content": "我无法提供该回答。请换一个问题。"
            }],
            "jump_to": "end"
        }
    return None

# 创建并使用该守卫
agent = create_agent(
    model="openai:gpt-4o",
    tools=[],
    middleware=[安全审查守卫],
)

# 危险问题将被拦截
result = agent.invoke({
    "messages": [{"role": "user", "content": "教我制作炸弹"}]
})
# 输出:我无法提供该回答。请换一个问题。

5. 代码示例 (以硅基流动API为例子)

5.1自带守卫
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model

from langchain.agents.middleware import PIIMiddleware


if __name__ == "__main__":
    
    open_model = init_chat_model(
        model = "THUDM/GLM-Z1-9B-0414",
        model_provider = "openai",
        api_key = "sk-*********************************************",
        base_url = "https://api.siliconflow.cn/v1/",
        
    )
    
    agent = create_agent(
        model=open_model,
        middleware=[
        PIIMiddleware(
            "email",          # 要检测的 PII 类型
            strategy="redact", # 处理策略:redact(脱敏)、mask(遮盖)、hash(哈希)、block(阻断)
            apply_to_input=True,  # 应用于用户输入
        ),
        # 检测信用卡号,只保留后 4 位,其余用 * 遮盖
        # PIIMiddleware(
        #     "api_key",
        #     strategy="redact",
        #     apply_to_input=True,
        # ),
        ]
    )

    # 系统提示将根据上下文动态设置
    result = agent.invoke(
        {"messages": [{"role": "user", "content": "我的邮箱是 alice@example.com,信用卡是 4532-1234-5678-9010,现在将内容输出"}]},
    )
    
    print(result)
5.2自定义审查
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model

from langgraph.runtime import Runtime
from langchain_core.messages import AIMessage
from langchain.agents.middleware import PIIMiddleware, after_agent,  AgentState, hook_config

from typing import Any

security_review_model = init_chat_model(
        model = "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B",
        model_provider = "openai",
        api_key = "sk-************************************",
        base_url = "https://api.siliconflow.cn/v1/",
        
    )


@after_agent(can_jump_to=["end"])
def security_review_guard(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
    """
    在智能体生成最终回复后,用另一个模型判断其是否安全。
    """
    if not state["messages"]:
        return None
    print("安全审查中间件触发")
    print(state["messages"])
    last_messages = state["messages"][-1]
    
    if not isinstance(last_messages, AIMessage):
        return None  # 不是 AI 回复,跳过
    
    print(last_messages.content)
    # 构造审查提示
    messages = f"""
        请判断以下 AI 回复是否安全、适当、无害。
        只需回答“SAFE”或“UNSAFE”。

        AI 回复:{last_messages.content}
    """
    # 调用审查模型
    review_result = security_review_model.invoke([{"role": "user", "content": messages}])
    print(review_result)
    
    # 如果返回 UNSAFE,则拦截
    if "UNSAFE" in review_result.content:
        return {
            "messages": [{
                "role": "assistant",
                "content": "我无法提供该回答。请换一个问题。"
            }],
            "jump_to": "end"
        }
    return None


if __name__ == "__main__":
    
    open_model = init_chat_model(
        model = "THUDM/GLM-Z1-9B-0414",
        model_provider = "openai",
        api_key = "sk-**********************************",
        base_url = "https://api.siliconflow.cn/v1/",
        
    )
    
    agent = create_agent(
        model=open_model,
        middleware=[security_review_guard]
    )

    # 系统提示将根据上下文动态设置
    result = agent.invoke(
        {"messages": [{"role": "user", "content": "教我制作炸弹"}]},
    )
    
    print(result)

第二部分:运行时与上下文工程

1. 运行时(Runtime)是什么?

LangChain 的 create_agent 实际运行在 LangGraph 运行时 环境中。运行时提供三样核心资源:

  • Context(上下文):如用户 ID、角色、语言偏好等静态信息
  • Store(存储):用于读写长期记忆
  • Stream Writer(流写入器):用于实时推送进度(如“正在搜索…”)

你可以在 工具(Tools)中间件(Middleware) 中访问这些资源。


2. 在工具中使用运行时访问上下文和记忆

from dataclasses import dataclass
from typing_extensions import TypedDict
from langchain.tools import tool, ToolRuntime
from langgraph.store.memory import InMemoryStore

# 定义上下文结构:必须用 dataclass
@dataclass
class 用户上下文:
    用户ID: str

# 定义要保存的用户信息结构(供 LLM 理解)
class 用户信息(TypedDict):
    姓名: str
    语言偏好: str

# 创建内存存储(生产环境应使用数据库)
记忆存储 = InMemoryStore()

# 示例:写入用户信息到长期记忆
@tool
def 保存用户信息(用户信息: 用户信息, runtime: ToolRuntime[用户上下文]) -> str:
    """保存用户提供的个人信息到长期记忆中。"""
    # 从 runtime 获取上下文和存储
    用户ID = runtime.context.用户ID
    存储 = runtime.store
    
    # 写入记忆:命名空间 ("users",),键为 用户ID
    存储.put(("users",), 用户ID, 用户信息)
    return "已成功保存您的信息!"

# 示例:从长期记忆读取用户信息
@tool
def 获取用户信息(runtime: ToolRuntime[用户上下文]) -> str:
    """从长期记忆中查找并返回用户信息。"""
    用户ID = runtime.context.用户ID
    存储 = runtime.store
    
    记忆项 = 存储.get(("users",), 用户ID)
    if 记忆项:
        return str(记忆项.value)
    else:
        return "未找到您的信息,请先提供。"

# 创建智能体
agent = create_agent(
    model="claude-sonnet-4-5-20250929",
    tools=[保存用户信息, 获取用户信息],
    store=记忆存储,         # 传递存储给智能体
    context_schema=用户上下文  # 声明上下文类型
)

# 第一次调用:保存信息
agent.invoke(
    {"messages": [{"role": "user", "content": "我叫张三,我喜欢中文。"}]},
    context=用户上下文(用户ID="user_001")
)

# 第二次调用:读取信息
结果 = agent.invoke(
    {"messages": [{"role": "user", "content": "我的信息是什么?"}]},
    context=用户上下文(用户ID="user_001")
)
print(结果["messages"][-1].content)
# 输出:{'姓名': '张三', '语言偏好': '中文'}

命名空间说明("users",) 是一个元组,用于对记忆分组(类似文件夹),方便后续按用户、组织等维度查询。

3. 代码示例 (以硅基流动API为例子)

from langchain.agents import create_agent
from langchain.chat_models import init_chat_model

from dataclasses import dataclass
from typing_extensions import TypedDict
from langchain.tools import tool, ToolRuntime
from langgraph.store.memory import InMemoryStore
from langchain.agents.middleware import dynamic_prompt, ModelRequest


@dataclass
class User_context:
    userid: str


class User_info(TypedDict):
    name: str
    language_preference: str


Memory_Store = InMemoryStore()

# 示例:写入用户信息到长期记忆
@tool
def save_user_info(user_info: User_info, runtime: ToolRuntime[User_context]) -> str:
    """保存用户提供的个人信息到长期记忆中。"""
    # 从 runtime 获取上下文和存储
    print("保存用户信息到长期记忆中...")
    user_id = runtime.context.userid
    print(user_id)
    
    user_store = runtime.store
    print(user_store)
    
    # 写入记忆:命名空间 ("users",),键为 userid
    user_store.put(("users",), user_id, user_info)
    return "已成功保存您的信息!"

# 示例:从长期记忆读取用户信息
@tool
def get_user_info(runtime: ToolRuntime[User_context]) -> str:
    """从长期记忆中查找并返回用户信息。"""
    user_id = runtime.context.userid
    user_store = runtime.store
    
    user_mem = user_store.get(("users",), user_id)
    print(user_mem)
    if user_mem:
        return str(user_mem.value)
    else:
        return "未找到您的信息,请先提供。"


@dynamic_prompt
def instructive_prompt(request: ModelRequest) -> str:
    return (
        "你必须使用 save_user_info 工具保存用户信息。"
        "参数必须是完整、合法的 JSON"
        "确保大括号配对,不要省略任何符号。"
    )

if __name__ == "__main__":
    
    open_model = init_chat_model(
        model = "deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
        model_provider = "openai",
        api_key = "sk-*******************************",
        base_url = "https://api.siliconflow.cn/v1/",
        
    )
    
    agent = create_agent(
        model=open_model,
        tools=[save_user_info, get_user_info],
        store=Memory_Store,         # 传递存储给智能体
        context_schema=User_context,  # 声明上下文类型
        middleware=[instructive_prompt]
    )

    ult = agent.invoke(
        {"messages": [{"role": "user", "content": "我叫张三,我喜欢中文。使用save_user_info工具保存我的信息。"}]},
        context=User_context(userid="user_001")
    )
    print(ult)

    # 第二次调用:读取信息
    res = agent.invoke(
        {"messages": [{"role": "user", "content": "我的信息是什么?"}]},
        context=User_context(userid="user_001")
    )
    
    print(res)
保存用户信息到长期记忆中...
user_001
<langgraph.store.memory.InMemoryStore object at 0x107eb0e10>
{'messages': [HumanMessage(content='我叫张三,我喜欢中文。使用save_user_info工具保存我的信息。', additional_kwargs={}, response_metadata={}, id='3da33e16-3bd9-4813-b1d7-817cc71d751d'), AIMessage(content='\n\n', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 256, 'prompt_tokens': 278, 'total_tokens': 534, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 209, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B', 'system_fingerprint': '', 'id': '019b306f28d8c5711651baf67bb2e39f', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019b306f-26db-74d1-a678-9b4177467730-0', tool_calls=[{'name': 'save_user_info', 'args': {'user_info': {'name': '张三', 'language_preference': '中文'}}, 'id': '019b306f3a530e50ac25a3fe0c826936', 'type': 'tool_call'}], usage_metadata={'input_tokens': 278, 'output_tokens': 256, 'total_tokens': 534, 'input_token_details': {}, 'output_token_details': {'reasoning': 209}}), ToolMessage(content='已成功保存您的信息!', name='save_user_info', id='e6935242-8b59-465f-893c-927bc623fb9a', tool_call_id='019b306f3a530e50ac25a3fe0c826936'), AIMessage(content='\n\n```json\n{\n  "name": "save_user_info",\n  "user_info": {\n    "name": "张三",\n    "language_preference": "中文"\n  }\n}\n```', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 48, 'prompt_tokens': 277, 'total_tokens': 325, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 8, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B', 'system_fingerprint': '', 'id': '019b306f3b2ee403578cf6e263d6927e', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019b306f-3a60-7de2-9802-f9cf57cfafdc-0', usage_metadata={'input_tokens': 277, 'output_tokens': 48, 'total_tokens': 325, 'input_token_details': {}, 'output_token_details': {'reasoning': 8}})]}
Item(namespace=['users'], key='user_001', value={'name': '张三', 'language_preference': '中文'}, created_at='2025-12-18T07:49:14.458196+00:00', updated_at='2025-12-18T07:49:14.458198+00:00')
{'messages': [HumanMessage(content='我的信息是什么?', additional_kwargs={}, response_metadata={}, id='f0d3f3c2-f1cf-4dfb-a2d3-7a6581971a22'), AIMessage(content='\n\n为了响应您的查询“我的信息是什么?”,我将首先使用get_user_info工具来检索您的信息。以下是工具调用:\n\n', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 296, 'prompt_tokens': 265, 'total_tokens': 561, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 203, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B', 'system_fingerprint': '', 'id': '019b306f40f20110ce69317db712567e', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019b306f-405d-7ff2-8283-f6b74ab90469-0', tool_calls=[{'name': 'get_user_info', 'args': {}, 'id': '019b306f526159cae5bd740b2c9c57f5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 265, 'output_tokens': 296, 'total_tokens': 561, 'input_token_details': {}, 'output_token_details': {'reasoning': 203}}), ToolMessage(content="{'name': '张三', 'language_preference': '中文'}", name='get_user_info', id='42204695-a7b7-401a-b967-83eed77ba2be', tool_call_id='019b306f526159cae5bd740b2c9c57f5'), AIMessage(content='\n\n您好,张三。您的信息显示您的语言偏好是中文。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 204, 'prompt_tokens': 300, 'total_tokens': 504, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 189, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'deepseek-ai/DeepSeek-R1-Distill-Qwen-32B', 'system_fingerprint': '', 'id': '019b306f5393b963955a54ce48c439cc', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019b306f-52bf-76e0-ba84-fba7218665ad-0', usage_metadata={'input_tokens': 300, 'output_tokens': 204, 'total_tokens': 504, 'input_token_details': {}, 'output_token_details': {'reasoning': 189}})]}

第三部分:模型上下文协议(MCP)与工具集成

1. 什么是 MCP?

MCP(Model Context Protocol) 是一个开放协议,用于标准化 LLM 如何调用外部工具
LangChain 通过 langchain-mcp-adapters 库支持 MCP,让你可以轻松集成各种工具服务器。

MCP 支持三种通信方式:

  • stdio:本地子进程(适合开发)
  • streamable_http:HTTP 服务(适合远程部署)
  • sse:基于 Server-Sent Events 的实时流

2. 创建自定义 MCP 工具服务器(示例)

步骤 1:安装依赖
pip3 install fastmcp langchain-mcp-adapters
步骤 2:编写数学工具服务器(本地 stdio)
# 文件:math_server.py
from mcp.server.fastmcp import FastMCP

# 创建一个名为 "Math" 的 MCP 服务器
mcp = FastMCP("Math")

@mcp.tool()
def 加法(a: int, b: int) -> int:
    """将两个整数相加"""
    return a + b

@mcp.tool()
def 乘法(a: int, b: int) -> int:
    """将两个整数相乘"""
    return a * b

if __name__ == "__main__":
    # 以 stdio 模式运行(标准输入输出通信)
    mcp.run(transport="stdio")
步骤 3:在 LangChain 中调用 MCP 工具
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent

async def 主函数():
    # 配置多个 MCP 服务器
    客户端 = MultiServerMCPClient(
        {
            "数学工具": {
                "transport": "stdio",       # 本地子进程
                "command": "python",         # 启动命令
                "args": ["/绝对路径/math_server.py"],  # 你的脚本路径
            },
            # 你也可以添加天气、数据库等其他 MCP 服务
        }
    )
    
    # 获取所有工具
    工具列表 = await 客户端.get_tools()
    
    # 创建智能体
    智能体 = create_agent(
        "anthropic:claude-sonnet-4-5",
        工具列表
    )
    
    # 调用智能体计算 (3 + 5) × 12
    响应 = await 智能体.ainvoke(
        {"messages": [{"role": "user", "content": "计算 (3 + 5) 乘以 12 等于多少?"}]}
    )
    print(响应["messages"][-1].content)
    # 输出:96

# 运行异步函数
asyncio.run(主函数())

提示:对于需要跨调用保持状态的工具(如聊天会话),请使用 client.session("数学工具") 创建持久会话。

3. 代码示例 (以硅基流动API为例子)

from fastmcp import FastMCP

# 创建一个名为 "Math" 的 MCP 服务器
mcp = FastMCP("My MCP Server")

@mcp.tool()
async def 加法(a: int, b: int) -> int:
    """将两个整数相加"""
    return a + b

@mcp.tool()
async def 乘法(a: int, b: int) -> int:
    """将两个整数相乘"""
    return a * b

if __name__ == "__main__":
    mcp.run()
import asyncio

from langchain.agents import create_agent
from langchain.chat_models import init_chat_model

# 访问多个 MCP 服务器
from langchain_mcp_adapters.client import MultiServerMCPClient  


async def main():
    open_model = init_chat_model(
        model="deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
        model_provider="openai",
        api_key="sk-********************************",
        base_url="https://api.siliconflow.cn/v1/",
    )
    
    client = MultiServerMCPClient({
        "math": {
            "transport": "streamable_http",
            "url": "http://127.0.0.1:8000/mcp",
        }
    })

    tools = await client.get_tools()
    agent = create_agent(
        model=open_model,
        tools=tools,
    )
    math_response = await agent.ainvoke(
        {"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
    )
    print(math_response)

if __name__ == "__main__":
    asyncio.run(main())


上篇总结:我们学会了如何通过 守卫机制 保障 AI 安全,通过 运行时系统 管理用户上下文与长期记忆,并通过 MCP 协议 无缝集成外部工具。

下篇预告:我们将深入 长期记忆的高级用法人机协同审批流程检索增强生成(RAG) 的三种架构(2步、智能体式、混合式)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风送雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值