RAG效果优化

如果您在使用百炼应用的 RAG 功能时遇到知识召回不完整或内容不准确的问题,可以参考本文的建议和示例以提升 RAG 效果。

1. RAG 工作流程简介

RAG(Retrieval Augmented Generation,检索增强生成)是一种结合了信息检索和文本生成的技术,能够在大模型生成答案时利用外部知识库中的相关信息。

其工作流程包含解析与切片、向量存储、检索召回、生成答案等几个关键阶段。

image.jpeg

接下来,本文将从 RAG 的解析与切片、检索召回、生成答案阶段入手,帮助您优化 RAG 的效果。

2. RAG 效果优化

2.1 准备工作

首先,请确保导入百炼知识库的文档符合以下要求:

  • 包含相关知识:如果知识库中没有相关知识,那么大模型大概率无法回答相关问题。解决方案就是更新知识库补充相关知识。

  • 使用Markdown格式(推荐):PDF内容格式复杂,解析效果可能不佳,建议先转成文本文档(Markdown/DOC/DOCX等)。例如,您可以使用百炼的DashScopeParse先将PDF转成Markdown格式,然后再使用大模型整理格式,同样可以取得不错的效果。更多说明请参见阿里云大模型ACP课程的 RAG 章节。

    为了让百炼应用在返回结果中能够正常展示文档中的插图,您需要对文档中的插图进行链接化处理
    知识库当前暂不支持文档中视频和音频内容的解析。
  • 内容表达清晰、结构合理、无特殊样式文档的内容排版也会显著影响 RAG 的效果,具体请参见文档应如何格式化排版有利于RAG

  • 与提示词语言一致:如果用户的提示词更多使用外语(例如英语),建议您的文档内容也使用相应语言。如有必要(例如文档中的专业术语),可进行双语言或多语言处理。

  • 消除实体歧义:对文档中相同实体的表述进行统一。例如,“ML”、“Machine Learning”和“机器学习”可以统一规范为“机器学习”。

    您可以尝试将文档输入到大模型中,让大模型来帮助您统一规范(如果文档较长,可先将其拆为多个部分,再逐一输入)。

上述五项准备工作是保证接下来 RAG 优化效果的基础。如果您已经做好准备,就可以着手深入了解并优化 RAG 应用的各个环节了。

2.2 解析与切片阶段

本段落仅介绍在 RAG 的切片阶段,百炼支持优化的配置项。

首先,您导入知识库的文档会被解析和切片。切片的主要目的是在后续向量化过程中尽量减少干扰信息影响,同时保持语义的完整性。因此,您在创建知识库所选择的文档切分chunk策略会对 RAG 的效果产生很大影响。如果切片方式不当,可能会导致下述问题:

文本切片过短

文本切片过长

明显的语义截断

image

image

image

文本切片过短会出现语义缺失,导致检索时无法匹配。

文本切片过长会包含不相关主题,导致召回时返回无关信息。

文本切片出现了强制性的语义截断,导致召回时缺失内容。

因此,在实际应用中,您应当尽量让文本切片包含的信息完整,同时避免包含过多的干扰信息。百炼建议您:

  1. 在创建知识库时,文档切分chunk选择智能切分

  2. 将文档成功导入知识库后,对文本切片的内容进行人工检查和修正

2.2.1 智能切分

选择适合您知识库的文本切片长度并非易事,因为这必须考虑多种因素:

  • 文档的类型:例如,对于专业类文献,增加长度通常有助于保留更多上下文信息;而对于社交类帖子,缩短长度则能更准确地捕捉语义。

  • 提示词的复杂度:一般来说,如果用户的提示词较复杂且具体,则可能需要增加长度;反之,缩短长度会更为合适。

    ...

当然,上述结论并不一定适用于所有情况,您需要选择合适的工具反复试验(例如LlamaIndex提供对不同切片方法的评估功能),才能找到合适的文本切片长度,但显然这并非易事。

如果您希望在短时间内获得良好的效果,建议您在创建知识库时,文档切分chunk选择智能切分,这是百炼在经过大量评估后总结出的推荐策略。

image

应用这一策略时,知识库会:

  1. 首先利用系统内置的分句标识符将文档划分为若干段落。

  2. 基于划分的段落,根据语义相关性自适应地选择切片点进行切分(语义切分),而非根据固定长度进行切分。

在上述过程中,知识库将努力确保文档各部分的语义完整性,尽量避免不必要的划分和切分。这一策略将应用于该知识库中的所有文档(包括后续导入的文档)。

2.2.2 修正文本切片内容

当然,在实际切片的过程中,仍然存在一定几率会出现意外的切分,或者出现其它问题(例如文本中的空格有时切片后会被解析为%20)。

image

因此,百炼建议您在成功将文档导入知识库后,进行一次人工检查,以确认文本切片内容的语义完整性和正确性。如果发现意外的切分或其他解析错误,您可以直接编辑文本切片进行修正(保存后,文本切片原有的内容将失效,新的内容将用于知识库检索)。

请注意,此处您只是修改了知识库中的文本切片,并未修改您在数据管理(临时存储)中的原文档或数据表。因此,后续再次导入知识库时,仍需您进行人工检查和修正。

2.3 检索召回阶段

本段落仅介绍在检索召回阶段,百炼支持优化的配置项。
借助应用观测功能,您可以端到端地查看和分析RAG的完整检索召回过程。

检索召回阶段面临主要问题是:难以从知识库众多的文本切片中,找出与提示词最相关且包含正确答案相关信息的文本切片。在此基础上进一步细化:

问题类型

改进策略

在多轮会话场景中,用户输入的提示词有时可能不够完整,或者存在歧义。

开启多轮会话改写,知识库会自动将用户的提示词改写为更完整的形式,从而更好地匹配知识。

知识库中包含多个类别的文档。希望在A类文档中检索时,召回结果中却包含其他类别(比如B类)的文本切片。

建议为文档添加标签。知识库检索时,会先根据标签筛选相关文档。

仅非结构化知识库支持为文档添加标签。

知识库中有多篇结构内容相似的文档,例如都包含“功能概述”。希望在A文档的“功能概述”中检索,召回结果中却包含其他相似文档中的信息。

提取元数据。知识库会在向量检索前使用元数据进行结构化搜索,从而精准找到目标文档并提取相关信息。

仅非结构化知识库支持建立文档元数据。

知识库的召回结果不完整,没有包含全部相关的文本切片。

建议降低相似度阈值,提高召回片段数,以召回一些原本应被检索到的信息。

知识库的召回结果中包含大量无关的文本切片。

建议提高相似度阈值,以排除与用户提示词相似度低的信息。

2.3.1 多轮会话改写

想象一下,在多轮会话中,您的用户某次使用了类似“百炼手机X1”这样提示词进行提问。看似简短,却可能导致 RAG 系统在检索时缺乏必要的上下文信息,原因是:

  • 一款手机产品通常会有多个代际版本同时在售。

  • 对于同一代产品,厂家通常会提供多种存储容量选项,比如128GB、256GB等。

    ...

而这些关键信息可能用户在之前的会话中已经给出,如果能够有效利用,将能够帮助 RAG 更准确地进行检索。

针对这种情况,您可以使用百炼的多轮会话改写功能。系统会根据之前的会话,自动将用户的提示词改写为更完整的形式。

比如用户提问:

百炼手机X1。

在启用多轮会话改写功能的情况下,系统会根据与该用户此前的历史会话,在检索前对该用户的提示词进行改写(仅示例):

请提供产品库中所有在售版本的百炼手机X1及其具体参数信息。

显然,这样改写后的提示词可以帮助 RAG 更好地理解用户的意图,让回答更加准确。

下图为您展示了如何开启多轮会话改写功能(选择图中的推荐配置时,同样也会开启本功能)。

image

请注意,多轮会话改写功能是与知识库绑定的,开启后仅对当前知识库相关的查询生效。且如果您在创建知识库时未启用该配置,则后续无法再为该知识库开启,除非重新创建知识库。

2.3.2 标签过滤

本段落内容仅适用于非结构化知识库。

想象一下,您在使用一些音乐APP时,可能有时会通过歌手名称来筛选歌曲,从而快速找到同一类(该歌手唱的)歌曲。

类似地,为您的非结构化文档添加标签可以引入额外的结构化信息。这样应用在检索知识库时,会先根据标签筛选文档,从而提升检索的准确性和效率。

目前百炼支持以下两种方式设置标签:

  • 在上传文档时设置标签:控制台操作步骤请参见导入数据,相关APIAddFile

  • 在数据管理页面编辑标签:对于已上传的文档,可单击文档右侧的标签进行编辑,相关APIUpdateFileTag

    image

目前百炼支持以下两种方式使用标签:

  • 通过API调用百炼应用时,可以在请求参数tags中指定标签。

  • 在控制台编辑应用时设置标签(但本方式仅适用于智能体应用)。

    请注意,此处的设置将应用于该智能体应用后续的所有用户问答。

    image

2.3.3 提取元数据

本段落内容仅适用于非结构化知识库。

将元数据嵌入文本切片,可以有效增强每个切片的上下文信息。在特定场景下,这种方法能够显著提升非结构化知识库的 RAG 效果。

假设以下场景:

某知识库中有大量手机产品说明文档,文档名称为手机的型号(比如百炼X1、百炼Zephyr Z9等),且所有文档都包含「功能概述」章节。

当该知识库未启用元数据时,用户输入以下提示词进行检索:

百炼手机X1的功能概述。

通过命中测试,您可以查看检索实际召回了哪些切片(如下图所示)。由于所有文档都包含“功能概述”,因此知识库会召回一些与查询实体(百炼手机X1)无关但和提示词相似的文本切片(如下图中的切片1、切片2),它们的排名甚至高于我们实际需要的文本切片。这显然会影响到 RAG 的效果。

命中测试的召回结果只保证排名,相似值的绝对值大小仅供参考。当绝对值的差别不大时(5%以内),可基本认为召回概率一样。

image

接下来,我们将手机名称,按照metadata抽取中提到的步骤,设置为元数据(相当于让每篇文档相关的文本切片带上各自对应的手机名称信息),然后再进行一次相同的测试作为对比。

image

此时,知识库会在向量检索前增加一层结构化搜索,完整过程如下:

  1. 从提示词中提取元数据 {"key": "name", "value": "百炼手机X1"}。

  2. 根据提取的元数据,找到所有包含“百炼手机X1”元数据的文本切片。

  3. 再进行向量(语义)检索,找到最相关的文本切片。

启用元数据后的命中测试实际召回效果如下图所示。可以看到,知识库此时已经可以精准找到与“百炼手机X1”相关且包含“功能概述”的文本切片。

image

除此以外,元数据的一个常见应用是在文本切片中嵌入日期信息,以便过滤最近的内容。更多元数据用法,请参见metadata抽取

2.3.4 调整相似度阈值

当知识库找到和用户提示词相关的文本切片后,会先将它们发送给Rank模型(在创建知识库的自定义参数设置中)进行重排序,而相似度阈值则用于筛选经过Rank模型重排序后返回的文本切片,只有相似度分数超过此阈值的文本切片才有可能被提供给大模型。

image

调低此阈值,预期可能会召回更多文本切片,但也可能导致召回一些相关度较低的文本切片;提高此阈值,可能会减少召回的文本切片。

若设置得过高(如下图所示),则可能导致知识库丢弃所有相关的文本切片,这将限制大模型获取足够背景信息生成回答。

image

没有最好的阈值,只有最适合您场景的阈值。您需要通过命中测试反复试验不同的相似度阈值,观察召回结果,找到最适合您需求的方案。

命中测试建议步骤

  1. 设计能够覆盖客户常见问题的测试用例;

  2. 根据知识库的具体应用场景和前期导入文档的质量,选择一个合适的相似度阈值;

  3. 执行命中测试,查看知识库召回结果;

  4. 基于召回结果,重新调整您的知识库的相似度阈值,具体操作请参见编辑知识库

image

2.3.5 提高召回片段数

召回片段数即多路召回策略中的K值。经过前面的相似度阈值筛选后,如果文本切片数量超过K,系统最终会选取相似度分数最高的 K 个文本切片提供给大模型。也正因为此,不合适的K值也可能会导致 RAG 漏掉正确的文本切片,从而影响大模型生成完整的回答。

比如下面的这个例子,用户通过以下提示词进行检索:

百炼X1手机有什么优势?

从下方示意图可以看到,目标知识库中实际与用户提示词相关,需要返回的文本切片总共有7个(下图左侧,已用绿色标出),但由于已经超出了当前设定的最大召回片段数K,因此包含优势5(超长待机)和优势6(拍照清晰)的文本切片被舍弃,没有提供给大模型。

image

由于 RAG 本身无法判断需要多少个文本切片才能给出“完整”的答案,因此即使最终提供的文本切片有遗漏,随后大模型仍然会基于缺失的文本切片生成不完整的回答。

大量实验结果表明:在诸如“列举...”、“总结...”,“比较一下X、Y...”等场景中,提供更多高质量的文本切片(例如K=20)给大模型,比仅提供前10个或前5个文本切片效果更好。虽然这样做可能会引入噪声信息,但如果文本切片质量较高的话,大模型通常能够有效抵消噪声信息的影响。

您可以在编辑百炼应用时调整召回片段数,具体操作请参考引用知识库中的检索配置(可选)

image

请注意,召回片段数也并非越大越好。因为有时召回的文本切片在拼装后,其总长度会超出大模型的输入长度限制,导致部分文本切片被截断,反而影响了 RAG 的效果。

因此,推荐您选择智能拼装。这一策略会在不超过大模型最大输入长度的前提下,尽可能多地为您召回相关的文本切片。

2.4 生成答案阶段

本段落仅介绍在生成答案阶段,百炼支持优化的配置项。
借助应用观测功能,您可以端到端地查看和分析RAG的完整生成答案过程。

至此,大模型已经可以根据用户的提示词以及从知识库检索召回的内容,生成最终的回答。然而,返回结果有可能还是不及您的预期。

问题类型

改进策略

大模型并未理解知识和用户提示词之间的关系,感觉答案是生硬拼凑的。

建议选择合适的大模型,从而有效地理解知识和用户的提示词之间的关系。

返回的结果没有按照要求,或者不够全面。

建议优化提示词模板

返回的结果不够准确,其中包含了大模型自身的通用知识,并未完全基于知识库。

建议开启拒识,仅限于根据知识库检索到的知识回答。

相似的提示词,希望每次返回的结果相同或不同。

建议调整大模型参数

2.4.1 选择合适的大模型

由于不同大模型在指令遵循、语言支持、长文本、知识理解等方面存在能力差异,可能导致:

模型A未能有效理解检索到的知识与提示词之间的关系,从而生成的答案无法准确回应用户的提示词。在更换为参数更多或者专业能力更强的模型B后,该问题可能就能得到解决。

比如用户提问:

我是中华人民共和国公民,我想收养一名未成年人,我必须具备哪些条件?

先后使用通义千问2-开源版-7B通义法睿-Plus大模型,结合包含相关资料的知识库进行测试:

通义千问2-开源版-7B

通义法睿-Plus

image

image

可以看出,在这个例子中,此模型没能很好地理解用户提示词中的限制以及检索到的知识。

更换大模型后,该问题得到了解决。

您可以在编辑百炼应用时,根据实际需求选择模型

image

建议选择通义千问的商业模型,例如通义千问-Max、通义千问-Plus等大模型。这些商业大模型相比开源版本,具备最新的能力和改进。

  • 如果只是简单的信息查询总结,小参数量的大模型足以满足需求,例如 通义千问-Turbo

  • 如果您希望RAG能完成较为复杂的逻辑推理,建议选择参数量更大、推理能力更强的大模型,例如通义千问-Plus或者是模型列表

  • 如果您的问题需要查阅大量的文档片段,建议选择上下文长度更大的大模型,例如Qwen-Long通义千问-Turbo通义千问-Plus

  • 如果您构建的 RAG 应用面向一些非通用领域,例如法律领域,建议使用面向特定领域训练的大模型,例如通义法睿

2.4.2 优化提示词模板

我们知道,大模型本身是基于给定的文本来预测下一个Token的。这意味着您可以通过调整提示词来影响大模型的行为(例如怎样利用检索到的知识等),间接提升 RAG 的效果。

以下为您介绍三种比较常见的优化方法:

方法一:对输出的内容进行限定

您可以在提示词模板中提供上下文信息、指令以及预期的输出形式,用于指示大模型完成任务。例如,您可以加入以下输出指示要求大模型:

如果所提供的信息不足以回答问题,请明确告知“根据现有信息,我无法回答这个问题”。切勿编造答案。

来减少大模型出现幻觉的几率。

方法二:添加示例

使用少样本提示(Few-Shot Prompting)的方法,将希望大模型模仿的问答示例添加到提示词中,引导大模型正确利用检索到的知识(下方示例使用的是通义千问-Plus)。

提示词模板

用户提示词和百炼应用返回的结果

# 要求
请从下方文本中提取技术规格,并以 JSON 格式展示。
${documents}

image

# 要求
请从下方文本中提取技术规格,并以 JSON 格式展示,请严格按照示例给定的字段展示。
${documents}

# 示例
## 输入:星尘S9Pro, 突破性6.9英寸1440 x 3088像素屏下摄像头设计,带来无界视觉享受。512GB存储与16GB RAM的顶级配置,配合6000mAh电池与100W快充技术,让性能与续航并驾齐驱,引领科技潮流。参考售价:5999 - 6499。
## 输出:{ "product":"星尘S9 Pro", "screen_size":"6.9inch", "ram_size": "16GB", "battery":"6000mAh" }

image

方法三:添加内容分隔标记

检索召回的文本切片如果随意混杂在提示词模板中,不利于大模型理解整个提示词的结构。因此,建议您将提示词和变量${documents}明确分开。

此外,为了保证效果,请确保您的提示词模板中变量${documents}只出现一次(请参考下方左边的正确示例)。

正确示例

错误示例

# 角色
你是一名客服人员,专注于分析和解决用户的问题,并通过检索知识库提供准确的解决方案。

# 要求
**直接返回结果**:请根据用户输入的提示词以及知识库中的内容给出直接返回结果,无需
进行推理。
**返回的结果中不要包含具体联系方式**:返回的结果中仅需包含对用户提示词的总结、相
关在职人员的姓名及其负责的内容。
**默认联系人**:如果找不到相关的在职人员,请直接返回“今日值班人:百炼客服01”。

# 知识库
请记住以下材料,它们可能对回答问题有帮助。
${documents}
# 角色
你是一名客服人员,专注于分析和解决用户的问题,并通过检索知识库提供准确的解决方案。
请利用${documents}中的信息来辅助解答。

# 要求
**直接返回结果**:请根据用户输入的提示词以及知识库中的内容给出直接返回结果,无需
进行推理。
**返回的结果中不要包含具体联系方式**:返回的结果中仅需包含对用户提示词的总结、相
关在职人员的姓名及其负责的内容。
**默认联系人**:如果找不到相关的在职人员,请直接返回“今日值班人:百炼客服01”。

# 知识库
请记住以下材料,它们可能对回答问题有帮助。
${documents}

了解更多提示词优化方法,请参阅Prompt工程

2.4.3 开启拒识

如果您希望百炼应用返回的结果严格基于从知识库中检索到的知识,排除大模型自身通用知识的影响,您可以在编辑百炼应用时设置回答范围为仅知识库范围

对于在知识库中找不到相关知识的情况,您还可以设置固定回复(自动)。

回答范围:知识库 + 大模型知识

回答范围:仅知识库范围

image

image

百炼应用返回的结果将综合从知识库中检索到的知识和大模型自身的通用知识。

百炼应用返回的结果将严格基于从知识库中检索到的知识。

关于知识范围判定,建议您选择搜索阈值+大模型判断的方式。这一策略会先通过相似度阈值筛选潜在文本切片,再由一个大模型裁判,通过您设置的判断Prompt对关联度进行深入分析,从而进一步提高了判定的准确性。

image

以下是一个判断Prompt的示例,供您参考。此外,当在知识库中未找到相关知识时,设置一个固定回复:抱歉,未找到相关的手机型号。

# 判断规则:
- 问题和文档匹配的前提是问题中涉及的实体与文档描述的实体完全相同。
- 问题在文档中完全没有提到。

用户提示词和百炼应用返回的结果(命中知识时)

用户提示词和百炼应用返回的结果(未命中知识时)

image

image

关于拒识的更多说明,请参考引用知识库中的检索配置(可选)

2.4.4 调整大模型参数

相似的提示词,如果您希望每次返回的结果相同或不同,您可以在编辑百炼应用时,修改参数配置来调整大模型参数。

image

上图中的温度系数可以控制大模型生成内容的随机性:温度越高,生成的文本越多样,反之,生成的文本越确定。

  • 具有多样性的文本,适用于创意写作(如小说、广告文案)、头脑风暴、聊天应用等场景。

  • 具有确定性的文本,适用于有明确答案(如问题分析、选择题、事实查询)或要求用词准确(如技术文档、法律文本、新闻报道、学术论文)的场景。

其他两个参数:

最长回复长度:此参数用于控制大模型生成的最多Token个数。如果您希望生成详细的描述可以将该值调高;如果希望生成简短的回答可以将该值调低。

携带上下文轮数:此参数用于控制大模型参考历史对话的轮数,设为1时表示模型在回答时不会参考历史对话信息。

2.4.5 模型调优

最后,如果您已经充分尝试了上述方法,还希望进一步提升效果,您可以考虑针对您的具体场景,选择一个大模型进行模型调优

3. 常见问题

如何让百炼应用在回答中正常展示文档中的插图?

非结构化知识库

为了让百炼应用在回答中能够正常展示文档中的插图,您可以通过在文档中插入图片可公网访问的URL链接来实现。请注意,如果直接在文档中插入图片,该图片将不会在回答中展示

请确保您图片的URL链接公网可访问且指向一个有效的图片文件,否则百炼应用将无法展示该图片。
如果您已按说明操作,但图片仍无法正常显示,请您检查文本切片中的图片URL链接,并与正确的URL进行对比,确认是否有出现多余的空格或特殊字符(可能是被系统误解析导致)。您可以直接编辑文本切片中的URL进行修正。

文档正确引用图片示例

提示词模板示例

用户提示词和百炼应用返回的结果

image

# 知识库
请记住以下材料,他们可能对回答问题有帮助。
${documents}

# 要求
如果有图片,请展示图片。

image

文档错误引用图片示例

提示词模板示例

用户提示词和百炼应用返回的结果

image

# 知识库
请记住以下材料,他们可能对回答问题有帮助。
${documents}

# 要求
如果有图片,请展示图片。

image

解释:由于在这个示例中,图片是直接插入文档中的,这种方式百炼不支持在回答中展示图片。

结构化知识库

为了让百炼应用在回答中能够正常展示文档中的插图,您可以在数据表中使用linkstring类型的字段来存储图片可公网访问的URL链接(如果一条数据记录需要关联多张图片,请为每张图片分别建立一个字段),请参考下方的正确引用图片示例

请确保图片的URL链接公网可访问且指向一个有效的图片文件,否则百炼应用将无法展示该图片。

结构化数据表正确引用图片示例

提示词模板示例

用户提示词和百炼应用返回的结果

image

# 知识库
请记住以下材料,他们可能对回答问题有帮助。
${documents}

# 要求
如果有图片,请展示图片。

image

文档应如何格式化排版有利于RAG?

文档内容排版建议

  • 文档的各级标题层次分明,各标题下的内容表达清晰。

  • 文档中尽量不要有水印。

  • 列表中间的某一条之下尽量不要再分级。

  • 文档中尽量不要有表格和图片(复杂表格会影响整体文档解析结果)。

文档标题层级不够清晰-示例

原文档

一级标题为“四、奖品使用规则:”,内容有“奖品1:...”和“奖品2:...”。

image.png

处理后会出现的问题

将“奖品2:...”解析为“奖品1:...”的下一级标题。 建议将文档中的“奖品1:...”以及“奖品2:...”设置为带序号的二级标题。

文档中有水印-示例

原文档

文档带有水印,总体内容有三条。

image.png

处理后会出现的问题

第三条会被分到一个chunk,但是由于水印部分被识别成文字,导致“(五)十一等耕地12万元/亩”后会多出“政府公报”几个字,并且由于“政府公报”的水印位置比较靠前,会导致(一)(二)(三)(四)(五)的顺序被打乱,变成(一)(五)(三)(四)(二)。

列表中间的某一条之下再分级-示例

原文档

一级标题“活动规则”下是一个有序列表,其中的第3条“活动介绍”之下又是一个列表(分为ab)。

image.png

处理后会出现的问题

一级标题“活动规则”下是一个有序列表,其中的第3条“活动介绍”之下又是一个列表。这会导致“活动介绍”被当成二级标题,其之后的所有内容被误当成“活动介绍”二级标题之下的内容。 建议不要在列表之下再分级,如果需要尽量把需要分级的点放置在列表的最后一条。

一个比较好的示例

  • 各标题下内容相对独立且清晰。

  • 无水印。

  • 标题之下是列表,但列表之下不再分级。

  • 无表格、无图片。

image.png