尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

LangChain实战指南:从模型接入到RAG与Agent开发全解析

LangChain实战指南:从模型接入到RAG与Agent开发全解析 1. 先搞清楚 LangChain 到底能帮你解决什么实际问题如果你正在接触大模型应用开发或者想把大模型能力集成到自己的业务系统里那么 LangChain 是你绕不开的一个框架。它不是一个模型而是一个“胶水”和“脚手架”核心价值在于把大模型、外部数据、工具调用和复杂逻辑编排成一个可执行的、稳定的应用。很多人一上来就去看它的几十个模块结果越看越懵。其实从实际落地的角度看LangChain 主要解决三类问题模型接入与切换你的代码里可能今天用 OpenAI明天要换成本地部署的 Qwen后天又要测试 Claude。如果每次换模型都重写一遍请求、解析、错误处理的代码会非常痛苦。LangChain 提供了一个统一的接口LLM/ChatModel让你用几乎相同的代码调用不同的模型。Prompt 管理与复杂编排简单的问答可以直接写 Prompt。但如果你需要根据用户问题动态选择上下文、进行多步推理、或者把一个大任务拆成几个小任务依次执行手动拼接字符串会变成一场灾难。LangChain 提供了PromptTemplate、LCELLangChain 表达式语言等让你能像搭积木一样声明式地构建复杂的工作流。连接外部世界大模型本身的知识是静态的且可能过时。要让模型能“联网搜索”、“查询数据库”、“操作软件”就需要Agent智能体。而要让模型能“阅读”你的私有文档如公司手册、产品PDF就需要RAG检索增强生成。LangChain 为这两者提供了标准化的组件和设计模式。所以这个教程的目标不是让你背 API而是让你掌握如何用 LangChain 的思维把一个模糊的 AI 想法变成一个可运行、可调试、可扩展的工程项目。我会从环境配置开始带你走过单模型调用、Prompt 编排、Agent 开发最终完成一个 RAG 项目过程中会穿插大量我实际踩过的坑和调试经验。2. 环境配置别在第一步就卡住LangChain 是 Python 库所以一个干净的 Python 环境是基础。我强烈建议使用conda或venv创建独立的虚拟环境避免包冲突。2.1 基础环境搭建首先确保你的 Python 版本在 3.8 以上。然后创建并激活虚拟环境# 使用 conda conda create -n langchain-env python3.10 conda activate langchain-env # 或使用 venv python -m venv langchain-env source langchain-env/bin/activate # Linux/macOS langchain-env\Scripts\activate # Windows接下来安装 LangChain。注意LangChain 是一个庞大的项目包含了核心包和许多社区集成包。对于初学者先安装核心包和常用的集成。pip install langchain langchain-communitylangchain是核心langchain-community包含了大量第三方集成如各种数据库、工具等。2.2 模型提供商接入从 OpenAI 到本地模型模型是核心。我们从一个最简单的例子开始调用 OpenAI 的 GPT 模型。你需要一个 OpenAI 的 API Key。pip install openai然后在代码中设置环境变量并使用import os from langchain_openai import ChatOpenAI # 建议将 API Key 设置在环境变量中不要硬编码在代码里 os.environ[OPENAI_API_KEY] your-api-key-here llm ChatOpenAI(modelgpt-3.5-turbo) response llm.invoke(Hello, world!) print(response.content)这是最顺畅的路径。但如果你无法访问或希望使用本地模型比如 Qwen就需要换一种方式。这也是搜索热词里cursor接入本地模型、codex接入第三方模型背后大家关心的问题。接入本地/第三方模型的关键大多数开源模型都提供了兼容 OpenAI API 格式的本地服务如使用 Ollama 、 vLLM 或 OpenAI-Compatible 项目部署。一旦部署好你就可以像调用 OpenAI 一样调用它们。例如你用 Ollama 在本地运行了 Qwen2.5 模型ollama run qwen2.5:7b默认会在http://localhost:11434提供 API。在 LangChain 中你可以使用ChatOpenAI类但修改base_url和api_key如果不需要则随便填一个。from langchain_openai import ChatOpenAI # 指向本地 Ollama 服务 llm ChatOpenAI( base_urlhttp://localhost:11434/v1, api_keyollama, # Ollama 通常不需要鉴权但字段需要存在 modelqwen2.5:7b ) response llm.invoke(请用中文回答法国的首都是哪里) print(response.content)这里有个大坑不同本地部署方案对 API 的支持程度不同。如果报错第一件事是先用curl命令测试你的本地模型服务是否真的返回了 OpenAI 兼容的格式。curl http://localhost:11434/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen2.5:7b, messages: [{role: user, content: Hello}], stream: false }如果这个curl命令能返回正确的 JSON那么 LangChain 接入大概率没问题。如果失败问题出在模型服务端而不是 LangChain 配置。2.3 必备工具包安装根据你要做的任务可能还需要安装其他包# 用于 RAG文档加载、文本分割、向量数据库 pip install langchain-text-splitters langchain-chroma pypdf # 用于 Agent可能需要一些工具库如计算、搜索 pip install langchain-experimental # 包含一些实验性工具和Agent pip install wikipedia # 示例工具 # 用于更复杂的流程编排LangGraph pip install langgraph环境准备好后我们不要急着写复杂应用先从最核心的“对话”开始。3. 模型接入与 Prompt 编排从一次对话到可控流程很多教程把 Prompt 编排讲得很玄乎其实本质就是如何动态地、结构化地生成给模型的指令。3.1 基础 PromptTemplate告别字符串拼接假设你想做一个翻译机器人需要将用户输入的语言翻译成目标语言。错误做法字符串拼接user_input Hello target_lang 法语 prompt f请将以下英文翻译成{target_lang}{user_input} # 当变量多、结构复杂时这里会变得难以维护正确做法使用 PromptTemplatefrom langchain.prompts import ChatPromptTemplate template ChatPromptTemplate.from_messages([ (system, 你是一个专业的翻译助手。), (user, 请将以下 {source_lang} 文本翻译成 {target_lang}{text}) ]) prompt template.invoke({ source_lang: 英语, target_lang: 法语, text: Hello, world! }) print(prompt.to_string()) # 输出结构化的消息列表可以直接发给 ChatModel llm ChatOpenAI(modelgpt-3.5-turbo) response llm.invoke(prompt) print(response.content)ChatPromptTemplate的好处是它管理了消息角色system, user, assistant并且能安全地处理变量注入避免 Prompt 注入攻击。3.2 LCEL把工作流连起来LCELLangChain Expression Language是 LangChain V1 的核心。它让你能用|操作符把组件链起来像管道一样处理数据。上面“模板 - 模型 - 输出”的过程用 LCEL 可以写成一行链from langchain.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI llm ChatOpenAI(modelgpt-3.5-turbo) translation_prompt ChatPromptTemplate.from_template( 将以下 {source_lang} 文本翻译成 {target_lang}{text} ) # 定义链prompt | llm translation_chain translation_prompt | llm # 运行链 result translation_chain.invoke({ source_lang: 英语, target_lang: 日语, text: Good morning }) print(result.content)这看起来只是语法糖但当链变复杂时其价值就体现出来了。例如我们想在翻译后让模型对翻译结果进行简要评价from langchain.schema.output_parser import StrOutputParser from langchain.schema.runnable import RunnablePassthrough # 第一个链翻译 translation_chain translation_prompt | llm | StrOutputParser() # 第二个链评价。注意这里的输入是上一个链的输出翻译结果 evaluation_prompt ChatPromptTemplate.from_template( 你是一名翻译质检员。请对以下翻译结果进行简要评价1-2句话{translated_text} ) evaluation_chain evaluation_prompt | llm | StrOutputParser() # 组合链先翻译再将翻译结果传递给评价链 # RunnablePassthrough() 用于传递初始的输入变量这里我们用不上全部需要自定义 def combine_inputs(data): # 先运行翻译链得到翻译文本 translated translation_chain.invoke(data) # 返回一个字典包含评价链需要的 translated_text return {translated_text: translated} full_chain ( RunnablePassthrough() # 接收原始输入 | combine_inputs # 自定义函数生成中间结果 | evaluation_chain # 执行评价 ) # 现在 full_chain 的输入依然是 {source_lang, target_lang, text} final_result full_chain.invoke({ source_lang: 英语, target_lang: 日语, text: Good morning }) print(final_result)LCEL 的强大在于它的声明式和可组合性。你可以轻松地调试链中的任何一步也可以将链序列化保存。对于企业级项目这种可维护性和可观测性至关重要。3.3 更复杂的编排条件判断与分支现实任务很少是直线。例如根据用户问题类型决定是调用通用模型还是专业模型。这需要用到RunnableBranch。from langchain.schema.runnable import RunnableBranch # 假设我们有两个不同的 LLM可以是同一个模型的不同版本或不同厂商的模型 general_llm ChatOpenAI(modelgpt-3.5-turbo) expert_llm ChatOpenAI(modelgpt-4) # 或另一个本地专家模型 # 定义一个路由函数如果问题包含“法律”关键词走专家链否则走通用链 def route_question(input_dict): question input_dict[question].lower() if 法律 in question or 合同 in question: return legal else: return general # 定义两个处理分支 general_chain ( ChatPromptTemplate.from_template(你是一个助手请回答{question}) | general_llm | StrOutputParser() ) legal_chain ( ChatPromptTemplate.from_template(你是一名法律AI助手请严谨地回答以下法律相关问题{question}) | expert_llm | StrOutputParser() ) # 创建分支 branch RunnableBranch( (lambda x: route_question(x) legal, legal_chain), general_chain # 默认分支 ) # 运行 result branch.invoke({question: 签署劳动合同需要注意什么}) print(f法律问题结果{result}) result2 branch.invoke({question: 今天天气怎么样}) print(f通用问题结果{result2})通过 LCEL 和分支你就能构建出有一定逻辑判断能力的应用流程。这是通向 Agent 的基础。4. Agent 智能体开发让模型学会使用工具Agent 是 LangChain 中最激动人心也最容易让人困惑的部分。简单说Agent LLM 思考规划 工具使用。模型不再只是生成文本而是可以主动调用搜索引擎、计算器、数据库等外部工具来完成任务。4.1 Agent 的核心三要素工具ToolsAgent 可以调用的函数。比如搜索网络、查询数据库、执行代码、调用 API。LLM作为 Agent 的“大脑”负责理解目标、规划步骤、决定使用哪个工具、解析工具结果。代理Agent将 LLM 和工具组合在一起的执行框架。它定义了 LLM 与工具交互的流程ReAct、OpenAI Functions 等。4.2 创建一个最简单的 Agent我们创建一个能使用计算器和搜索维基百科的 Agent。首先安装必要库pip install langchain langchain-community wikipedia numexpr然后编写代码import os from langchain.agents import AgentExecutor, create_react_agent from langchain_openai import ChatOpenAI from langchain import hub from langchain_community.tools import WikipediaQueryRun, Tool from langchain_community.utilities import WikipediaAPIWrapper from langchain_experimental.tools import PythonREPLTool # 1. 定义工具 # 工具1Python REPL可以执行数学计算 python_repl PythonREPLTool() # 工具2维基百科搜索 api_wrapper WikipediaAPIWrapper(top_k_results2, doc_content_chars_max500) wikipedia WikipediaQueryRun(api_wrapperapi_wrapper) # 将工具包装成 LangChain Tool 对象 tools [ Tool( nameCalculator, funcpython_repl.run, description用于执行数学计算。输入应该是一个有效的 Python 数学表达式例如2 2 或 sqrt(25)。 ), Tool( nameWikipedia, funcwikipedia.run, description用于搜索维基百科获取关于人物、地点、公司、历史事件等的事实信息。输入应该是一个搜索查询词。 ) ] # 2. 初始化 LLM llm ChatOpenAI(modelgpt-3.5-turbo, temperature0) # 3. 获取一个预设的 Prompt来自 LangChain Hub # 这个 Prompt 包含了 ReAct 框架的指令思考Thought、行动Action、观察Observation prompt hub.pull(hwchase17/react-chat) # 4. 创建 Agent agent create_react_agent(llm, tools, prompt) # 5. 创建 Agent 执行器 agent_executor AgentExecutor( agentagent, toolstools, verboseTrue, # 开启详细日志可以看到 Agent 的思考过程 handle_parsing_errorsTrue, # 处理解析错误 max_iterations5 # 限制最大迭代次数防止死循环 ) # 6. 运行 Agent result agent_executor.invoke({ input: 请先计算 15 的平方根然后搜索一下爱因斯坦的生平。, chat_history: [] # 如果是多轮对话这里需要传入历史 }) print(result[output])运行这段代码你会看到控制台输出类似以下内容因为verboseTrue Entering new AgentExecutor chain... Thought: 用户问了两件事计算15的平方根然后搜索爱因斯坦的生平。我需要按顺序使用工具。 Action: Calculator Action Input: math.sqrt(15) Observation: 3.872983346207417 Thought: 我得到了计算结果。现在需要搜索爱因斯坦的生平。 Action: Wikipedia Action Input: Albert Einstein Observation: Page: Albert Einstein Summary: Albert Einstein was a German-born theoretical physicist... Thought: 我已经完成了用户要求的两个任务。 Final Answer: 15的平方根约为3.873。阿尔伯特·爱因斯坦是德裔理论物理学家他最著名的成就是提出了相对论并因此获得了1921年诺贝尔物理学奖。 Finished chain.这就是 Agent 的工作过程LLMThought决定下一步做什么选择工具Action并生成输入执行工具得到结果Observation然后根据结果进行下一步思考直到得出最终答案。4.3 Agent 开发中的关键坑点工具描述Description至关重要LLM 根据工具的描述来决定是否以及如何使用它。描述必须清晰、准确说明工具的用途、输入格式和限制。模糊的描述会导致 Agent 错误调用工具。控制迭代与超时必须设置max_iterations和可选的max_execution_time。否则一个陷入循环的 Agent 会无限消耗你的 Token。错误处理handle_parsing_errorsTrue是必要的因为 LLM 有时会生成不符合格式的文本导致解析失败。更好的做法是自定义一个错误处理回调。对本地模型不友好许多开源模型在遵循复杂的 ReAct 格式指令上表现不佳导致 Action 解析失败。如果使用本地模型可能需要更简单的 Agent 类型如ZERO_SHOT_REACT_DESCRIPTION或进行额外的 Prompt 调优。成本与延迟Agent 的每一步思考Thought和行动Action都是一次 LLM 调用对于按 Token 收费的 API成本会显著增加。需要权衡任务复杂度和成本。4.4 多智能体与 LangGraph当单个 Agent 不够用时就需要多个 Agent 协作这就是LangGraph的领域。LangGraph允许你定义由多个节点可以是 LLM、工具、函数或其他 Agent和边控制流组成的图。例如一个客服系统可能包含路由Agent判断问题类型-技术客服Agent处理技术问题-订单查询Agent处理订单问题-汇总Agent整理最终回复。LangGraph可以清晰地管理这种复杂的状态和流程。由于LangGraph概念更复杂建议在熟练掌握单 Agent 后再基于官方文档和示例进行学习。搜索热词中的langchain和langgraph的区别可以简单理解为LangChain 是构建单链或单 Agent 的框架而 LangGraph 是构建多 Agent 协作工作流的框架。5. RAG 项目实战构建你的私有知识库问答系统RAG检索增强生成是目前将大模型与私有数据结合最主流、最有效的方法。其核心流程是检索Retrieve相关文档片段 - 将它们与问题一起组合成 Prompt - 生成Generate答案。5.1 RAG 系统构建四步法我们以一个“公司内部知识库问答”为例使用 PDF 文档作为数据源。第一步文档加载与分割from langchain_community.document_loaders import PyPDFLoader from langchain_text_splitters import RecursiveCharacterTextSplitter # 1. 加载文档 loader PyPDFLoader(./path/to/your/company_handbook.pdf) documents loader.load() # 2. 分割文本 # 为什么分割因为大模型有上下文长度限制且整篇文档检索效率低。 text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每个片段的最大字符数 chunk_overlap50, # 片段之间的重叠字符避免上下文断裂 separators[\n\n, \n, 。, , , ] # 分割符优先级 ) chunks text_splitter.split_documents(documents) print(f原始文档页数{len(documents)}分割后片段数{len(chunks)})分割参数调优chunk_size和chunk_overlap是 RAG 效果的关键。太小会丢失上下文太大会引入噪声。对于通用文档500-1000 是个不错的起点。对于代码或技术文档可以按函数或章节分割。第二步向量化与存储我们需要将文本片段转换成向量嵌入并存入向量数据库以便后续相似度检索。from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma # 使用 OpenAI 的嵌入模型也可换为本地模型如 BGE embeddings OpenAIEmbeddings(modeltext-embedding-3-small) # 创建向量数据库这里使用 Chroma轻量且易用 # persist_directory 指定持久化目录否则数据只在内存中 vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db # 数据将保存到此目录 ) vectorstore.persist() # 显式保存关键选择嵌入模型选择与你的语言中/英和领域匹配的模型。OpenAI 的嵌入效果好但需付费。中文可考虑BAAI/bge-large-zh等开源模型通过HuggingFaceEmbeddings接入。向量数据库Chroma 适合轻量级和原型开发。生产环境可以考虑 Qdrant、Weaviate、Pinecone云服务等它们在规模、性能和功能上更强大。第三步检索构建一个检索器它能根据用户问题从向量库中找到最相关的文本片段。# 从已持久化的目录加载向量库 vectorstore Chroma( persist_directory./chroma_db, embedding_functionembeddings ) # 创建检索器。search_type 可选 similarity相似度或 mmr最大边际相关性兼顾相关性和多样性 retriever vectorstore.as_retriever( search_typesimilarity, search_kwargs{k: 4} # 返回最相关的 4 个片段 ) # 测试检索 query 公司年假政策是怎样的 relevant_docs retriever.invoke(query) print(f检索到 {len(relevant_docs)} 个相关片段) for i, doc in enumerate(relevant_docs): print(f\n--- 片段 {i1} ---\n{doc.page_content[:300]}...) # 打印前300字符第四步生成将检索到的上下文和原始问题组合发送给 LLM 生成答案。from langchain.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI from langchain.schema.runnable import RunnablePassthrough from langchain.schema.output_parser import StrOutputParser # 1. 定义 Prompt 模板 template 你是一个专业的公司知识库助手。请严格根据以下上下文信息回答问题。 如果你不知道答案就如实说不知道不要编造信息。 上下文信息 {context} 问题{question} 请根据上下文提供准确、简洁的答案 prompt ChatPromptTemplate.from_template(template) # 2. 定义 LLM llm ChatOpenAI(modelgpt-3.5-turbo, temperature0) # 3. 构建 RAG 链 # 链的流程传入问题 - 检索器获取上下文 - 格式化 Prompt - LLM 生成 - 解析输出 rag_chain ( {context: retriever, question: RunnablePassthrough()} | prompt | llm | StrOutputParser() ) # 4. 提问 question 公司年假政策是怎样的 answer rag_chain.invoke(question) print(f问题{question}) print(f答案{answer})至此一个最基本的 RAG 流水线就完成了。用户的问题会先检索知识库然后将相关片段作为上下文提供给 LLM从而生成基于你私有知识的答案。5.2 提升 RAG 效果的实战技巧基础的 RAG 很容易搭建但效果往往不尽如人意。以下是几个必须关注的优化点检索质量是天花板如果检索到的文档不相关LLM 再强也无力回天。优化方法优化分割策略尝试不同的chunk_size和分割符。对于结构化文档如 Markdown可以按标题分割。优化检索器尝试MMR搜索或在检索后增加一个LLM 重排序步骤用一个小模型对检索结果进行相关性排序。混合检索结合关键词检索如 BM25和向量检索取长补短。Prompt 工程给模型的指令要清晰。明确告诉它“基于上下文”、“不要编造”、“如果上下文没有就说不知道”。在上下文中用明显的标记如## 上下文 ##分隔。引用溯源对于企业应用知道答案来自哪份文档、哪一页非常重要。在分割文档时务必保留元数据如source,page。在生成答案后可以将引用的片段 ID 或源信息一并返回。处理“未命中”当检索器返回的片段相关性都很低时应该有一个降级策略比如直接让 LLM 回答“知识库中未找到相关信息”或者转而进行通用问答。评估与迭代建立评估集一组问题标准答案定期跑测试查看检索命中率、答案准确率。这是优化 RAG 系统的唯一科学方法。6. 从 Demo 到企业级项目必须考虑的工程问题把 Demo 跑通只是第一步。要用于实际生产你必须考虑以下问题6.1 配置管理与安全API Key 等敏感信息绝对不要硬编码在代码中。使用环境变量os.getenv或专业的密钥管理服务如 AWS Secrets Manager, HashiCorp Vault。配置分离将模型类型、API Base URL、温度参数、块大小等配置项放在配置文件如config.yaml或.env中便于不同环境开发、测试、生产切换。6.2 可观测性与日志记录每一次 LLM 调用包括输入的 Prompt、收到的响应、Token 使用量、耗时。这对于调试、成本分析和效果优化至关重要。LangChain 提供了回调机制Callbacks可以方便地集成日志系统。记录 Agent 的思考过程生产环境 Agent 出错时完整的 Thought-Action-Observation 链条是排查问题的唯一线索。6.3 性能与成本缓存对频繁出现的相似查询结果进行缓存可以大幅降低 LLM 调用成本和延迟。LangChain 内置了InMemoryCache、RedisCache等。异步处理对于批量任务或高并发场景使用 LangChain 的异步接口ainvoke,abatch可以显著提升吞吐量。模型降级非关键任务或简单任务使用更小、更便宜的模型如 GPT-3.5-turbo 而不是 GPT-4。限制与兜底为 Agent 设置严格的max_iterations和max_execution_time。为 RAG 检索设置超时和重试机制。6.4 部署与扩展封装为 API 服务使用 FastAPI 或 Flask 将你的 LangChain 应用封装成 REST API 或 WebSocket 服务。容器化使用 Docker 将应用及其依赖Python 环境、向量数据库打包确保环境一致性。向量数据库独立部署生产环境不要用 Chroma 的本地模式。将向量数据库如 Qdrant, Weaviate作为独立服务部署保证可扩展性和可靠性。监控与告警监控 API 的响应时间、错误率、Token 消耗。设置告警在服务异常或成本异常时及时通知。6.5 测试与评估单元测试测试单个组件如文档加载器、文本分割器、检索器。集成测试测试完整的 RAG 链或 Agent 流程使用固定的输入验证输出是否符合预期。端到端评估定期用一批真实用户问题测试整个系统评估答案的准确性、相关性和有用性。这需要人工或利用强模型如 GPT-4进行评判。7. 常见问题排查清单当你遇到问题时按以下顺序排查可以解决大部分情况模型调用失败检查 API Key 或本地模型服务地址是否正确。用curl或requests直接调用模型服务确认其本身是否正常。检查网络连接和防火墙设置。RAG 检索结果差检查文本分割是否合理。打印出前几个chunk看看内容是否连贯。检查嵌入模型是否适合你的文本语言。尝试换一个嵌入模型。检查检索数量k是否合适。太小可能遗漏太大可能引入噪声。检查向量数据库是否成功写入了数据。查看持久化目录的文件大小。Agent 陷入循环或调用错误工具检查工具描述是否清晰准确。降低 LLM 的temperature如设为 0减少随机性。在 Prompt 中给出更明确的步骤限制指令如“最多思考三步”。开启verboseTrue观察 Agent 的思考过程看是在哪一步逻辑出了问题。程序报错AttributeError或ImportError这通常是 LangChain 版本问题。V1 和 V0.x 版本 API 变化很大。确认你安装的是langchain0.1.0即 V1 系列并查阅对应版本的官方文档。检查是否安装了正确的集成包如langchain-openai,langchain-community。处理速度慢如果是本地模型检查 GPU 利用率。可能是模型太大或批处理设置不当。如果是 API 模型检查是否是网络延迟。考虑使用异步调用。对于 RAG检索步骤可能是瓶颈。考虑优化向量索引或使用更快的向量数据库。学习 LangChain 最好的方式不是记住所有类和方法而是理解其设计模式用标准化的组件Models, Prompts, Chains, Agents, Tools, RAG去编排和连接。从一个具体的项目需求出发选择需要的组件像搭积木一样构建你的应用并在过程中不断调试和优化。当你熟悉了这种模式就能快速地将任何 AI 想法转化为可运行的代码。
返回列表