Skip to content

Q. Chunk是怎么切的?固定?语义?还是自适应?

Chunk的切分策略直接影响到后续Embedding的质量和检索召回的效果,不存在一种“万能”的策略,而是需要根据数据源的特性、业务场景的需求以及成本效益来综合选择和组合。

在实践中,主要会考虑和应用以下几种切分方式,它们分别对应了固定、语义和自适应的思想:

1. 固定长度切分 (Fixed-size Chunking)

这是最简单直接的方式,但也是最粗暴的。

  • 实现方式:
    • 按字符数(Character Splitting):例如,每1000个字符切成一个块。
    • 按Token数(Token Splitting):更精确的方式,使用与Embedding模型相同的Tokenizer,例如,按每512个Token切块。这能更好地控制输入到模型中的内容长度。
  • 关键参数:
    • chunk_size: 每个块的大小。
    • chunk_overlap: 块之间的重叠部分。设置重叠(e.g., 100个Token)至关重要,它可以有效防止语义信息在块的边界处被切断,保证了信息的连续性。比如一个完整的句子被切分到了两个块的交界处,重叠区可以确保这个句子在两个块中都以较完整的形式存在。
  • 优点:
    • 实现简单,计算开销小,处理速度快。
  • 缺点:
    • 忽略语义完整性:很容易粗暴地将一个完整的句子、段落或代码块从中间切开,破坏了原始的语义结构,导致单个Chunk的语义不完整,影响Embedding质量。
  • 适用场景:
    • 当源数据结构化程度很低,没有明显的段落、句子等分隔符时,作为一种保底策略。
    • 对处理速度要求极高,且对检索精度容忍度稍高的场景。

2. 语义切分 (Semantic Chunking)

这种方式更智能,力求保持每个Chunk的语义完整性。

  • 实现方式:
    • 基于规则/分隔符 (Rule-based/Separator-based):
      • 递归字符切分 (Recursive Character Splitting): 这是LangChain等框架中非常常用且推荐的一种方式。它会尝试按一个预设的、有优先级的分隔符列表(例如:["\n\n", "\n", " ", ""])进行切分。它首先尝试用“双换行符”(段落)切分,如果切分后的块仍然大于chunk_size,再在这些块内部用“单换行符”(句子)切分,以此类推。这是一种在固定长度和语义之间做的非常好的折衷。
      • 特定文档格式切分: 针对特定格式,如Markdown、LaTex、HTML等,我们可以利用其本身的结构标记(如#标题,<li>列表项)来进行切分。例如,将每个一级标题下的所有内容作为一个Chunk。
    • 基于自然语言处理 (NLP-based):
      • 句子切分: 使用NLTK、spaCy等NLP库,先将文本分割成句子,再将若干个完整的句子组合成一个不超限的Chunk。这是保证语义基本单元完整的有效方法。
      • 高级语义切分: 这是一个更前沿的方向。比如,LlamaIndex中的SemanticSplitter,它通过计算句子之间的Embedding相似度来决定切分点。如果相邻句子间的语义差异(Embedding距离)超过一个阈值,就在此处进行切分。这能更好地捕捉到话题的转变。
  • 优点:
    • 最大程度保留语义完整性,生成的Chunk质量高,有利于模型理解和生成高质量的Embedding。
    • 检索到的上下文相关性更强,因为返回的是一个或多个语义完整的片段。
  • 缺点:
    • 计算开销相对较大,特别是高级语义切分。
    • 可能导致Chunk大小非常不均匀,有些Chunk可能很小(如一个短句),有些则接近chunk_size上限。

3. 自适应切分 (Adaptive Chunking)

“自适应”是上述策略的智能化组合与应用,其核心思想是具体问题具体分析。在企业级知识库中,数据源是多种多样的,不可能用一种策略包打天下。

  • 实现策略:
    1. 分层与组合 (Hierarchical & Combined Approach):

      • 我们可以设计一个多层次的切分策略。例如,先将一篇长文档按章节(语义切分)切成大的Parent Chunk。然后,在每个Parent Chunk内部,再使用递归字符切分或句子切分切成更小的、用于检索的Child Chunk
      • 在检索时,我们先召回Child Chunk,然后根据需要,可以向上追溯,将它所属的整个Parent Chunk或前后相邻的Child Chunk一起提供给LLM作为上下文。这种小块召回,大块推理 (Small-to-Big Retrieval) 的模式,既保证了检索的精确性,又提供了充足的上下文。
    2. 内容感知切分 (Content-aware Splitting):

      • 识别数据类型: 在数据预处理流水线中,首先识别文档类型。如果是代码文件(.py, .java),就调用基于AST或特定分隔符(如函数、类定义)的代码切分器。如果是PDF格式的财报,就优先识别表格和段落,进行结构化提取后再切分。如果是Markdown,就使用Markdown切分器。
      • 元数据驱动: 我们可以将元数据(如文档标题、章节、作者、日期)与Chunk内容一同存储。切分时可以利用这些元数据,比如确保一个Chunk不会跨越不同的章节。

4. 总结与选择

在构建一个企业级知识库后端时,策略会是这样的:

  1. 默认策略: 选择 递归字符切分 (Recursive Character Splitting) 作为基准和默认策略。因为它在实现复杂度、性能和效果之间取得了很好的平衡,是大部分场景下的“甜点”。
  2. 混合策略: 建立一个可配置的、基于内容类型的切分策略引擎。允许我们为不同数据源(如Confluence页面、代码库、PDF合同、客户聊天记录)配置最适合的切分方法。例如:
    • 文档/文章: 递归字符切分 或 句子切分组合。
    • 代码: 基于语法结构的切分器。
    • 表格: 将表格整体或按行提取为结构化数据,而不是暴力切分。
  3. 持续评估与优化: Chunking策略不是一成不变的。建立一套评估机制,通过分析检索召回的Hit Rate、上下文的有效性(Context Precision/Recall)以及最终答案的质量,来反向迭代和优化我们的切分策略。比如,如果我们发现很多问题因为上下文被切断而回答错误,我们可能需要增大chunk_overlap或者采用更保守的语义切分方法。
  4. 探索前沿: 同时,持续关注和实验像高级语义切分Small-to-Big这样的高级策略,在关键业务场景中进行小范围试点,以追求极致的检索效果。

总而言之,Chunk切分是一个系统工程,需要从单一策略的深度优化多策略的智能调度两个维度去构建,最终目标是为RAG流程的下游提供语义完整、大小适中、与查询意图高度相关的上下文信息。

Q. Embedding模型选型和维度怎么来的?

1. 模型选型

模型选型是一个典型的系统设计权衡(Trade-off)过程,主要在效果、成本、安全和维护性之间做取舍。我们从两大类模型进行分析:

  1. 商业闭源模型 (通过API调用)

    • 代表: OpenAI (text-embedding-3-large/small/ada-002), Cohere (embed-v3), Google (text-embedding-gecko)等。
    • 优点:
      • 性能卓越: 通常在各大公开评测榜单(如MTEB - Massive Text Embedding Benchmark)上名列前茅,具有非常强的通用语义表征能力。
      • 开箱即用: 无需关心模型部署、硬件(GPU)和维护,通过API调用即可,极大降低了初期搭建和运维的复杂度。
      • 持续迭代: 服务商会不断更新模型,我们可以无感享受到模型升级带来的效果提升。
    • 缺点:
      • 数据隐私与安全: 对于企业级知识库,将内部敏感数据发送给第三方API是一个巨大的合规风险和安全隐患。这是在企业应用中被否决的首要原因。
      • 成本考量: 按Token计费,当知识库规模巨大(亿级文档)或调用频繁时,长期成本非常高昂。
      • 网络依赖与延迟: 依赖外部网络,可能会有不稳定的延迟,影响实时性要求高的场景。
      • 厂商锁定: 深度绑定特定厂商,迁移成本高。
  2. 开源模型 (私有化部署)

    • 代表: BAAI/BGE系列 (如 bge-m3, bge-large-zh-v1.5),GTE系列,Jina系列, E5系列等,这些都可以在Hugging Face上找到。
    • 优点:
      • 数据安全可控: 模型部署在企业内网,数据不出域,完全满足企业级的安全合规要求。这是企业级应用的核心优势。
      • 成本可控: 初期需要投入硬件(GPU服务器)和人力进行部署维护,但长期来看,对于大规模应用,边际成本极低,总体成本远低于API。
      • 可定制化强: 可以在企业自有数据上进行微调 (Fine-tuning),让模型更懂本领域的“黑话”和专有名词,从而显著提升在特定领域的检索效果。
      • 性能优异: 像BGE、GTE等系列的头部开源模型,在MTEB上的表现已经非常接近甚至在某些任务上超过了商业模型。特别是针对中文领域,有很多优秀的专门优化过的模型。
    • 缺点:
      • 技术门槛与运维成本: 需要团队具备模型部署、监控、扩缩容的能力。
      • 硬件投入: 需要采购或租赁GPU资源。

在企业级知识库场景下,选型优先级会是:开源模型私有化部署 > 商业API

  1. 基准测试先行: 选取一个有代表性的数据集(包含公司内部的文档、问答对等),选择几款在MTEB榜单上表现优异的开源模型(例如,如果多语言需求强,选bge-m3;如果以中文为主,选bge-large-zh-v1.5)和一款商业API模型(如text-embedding-3-small)进行离线评测。评测指标包括检索任务的Recall@K, MRR等。
  2. 综合评估: 结合基准测试的结果和我们的业务限制(安全、预算、团队技术栈),做出最终决定。绝大多数情况下,为了数据安全和长期成本,我们会选择表现最好的那款开源模型进行私有化部署。
  3. 微调作为后手: 如果通用开源模型在特定领域的专业术语上表现不佳,下一步的优化方向就是利用内部积累的“(问题, 相关文档片段)”数据对,对选定的开源模型进行微调。

2. 维度选择

维度的选择并非随心所欲,它主要由以下几个因素决定:

  1. 模型本身决定 (主要因素)

    Embedding的维度是模型在设计和训练时就确定好的固有属性。例如:

    • bge-large-zh-v1.5 的维度是 1024
    • OpenAI text-embedding-ada-002 的维度是 1536
    • 经典的 BERT-base 模型输出维度是 768

    当我们选择了一个模型,基本上也就选择了它的原生维度。这是因为模型最后一层的神经元数量就决定了输出向量的长度,这个向量被训练来在那个特定维度的空间里最好地表示语义。

  2. 新技术带来的灵活性:可变维度

    近年来出现了一项非常重要的技术——Matryoshka Representation Learning (MRL, 套娃表示学习)

    • 代表模型: OpenAI的text-embedding-3-small/large,Jina AI的jina-embeddings-v2系列。
    • 原理: 这类模型在训练时就经过特殊设计,使得其输出的高维Embedding向量(如text-embedding-3-large的3072维)的前N个维度本身就是一个高质量的低维Embedding。就像俄罗斯套娃一样,一个大娃娃里面包含了所有小娃娃。
    • 优势: 这赋予了我们在使用时选择维度的能力。我们可以根据业务场景在性能和成本之间动态权衡:
      • 需要极致精度: 使用模型的完整维度(如1536, 3072)。
      • 对存储和检索速度要求高: 可以只截取向量的前面部分,比如256或512维。这样可以大幅降低向量数据库的存储成本和内存占用,并加快检索速度。
    • 如何选择: 如果我们选用了这类模型,就要通过实验来确定一个最佳平衡点。例如,分别测试使用256、512、1024维在评测集上的表现。如果发现从512维提升到1024维,检索准确率仅提升了0.5%,但存储和计算成本增加了1倍,那么512维可能就是当前性价比最高的选择。
  3. 降维 (次要/传统方法)

    在MRL技术出现之前,如果想使用更低的维度,我们也可以采用传统的降维技术,如PCA (主成分分析)。可以先用模型生成高维向量,然后通过PCA等算法将其映射到低维空间。但这通常会带来信息损失,效果不如MRL这类原生支持可变维度的技术。在当今的模型选型中,这更多是一种备选方案。

3. 总结完整决策流程:

  1. 确定场景需求: 明确我们知识库的语言(中/英/多语言)、数据敏感性、预算和性能要求。
  2. 初选模型: 根据需求,从Hugging Face和商业API中挑选2-3个候选模型。例如,选择bge-m3(可私有化,多语言,固定1024维)和OpenAI text-embedding-3-small(API,支持可变维度)。
  3. 确定维度策略:
    • 对于bge-m3,维度固定为1024。
    • 对于text-embedding-3-small,设计实验,测试其在256, 512, 1536维下的性能和资源消耗。
  4. 进行PoC (Proof of Concept): 在自有的“黄金标准”数据集上运行评测,对比各模型及各维度设置下的检索效果和成本。
  5. 最终决策: 基于PoC的数据,做出最优选择。例如,我们可能发现bge-m3在1024维的效果已经足够好,且满足安全和成本要求,就最终采纳它。或者,如果我们对成本极度敏感且能接受API,可能会选择text-embedding-3-small的512维版本。

这个决策过程是一个数据驱动、系统化的过程,确保我们选择的Embedding模型和维度组合最适合当前的企业级知识库业务。

Q. Rerank用没用?怎么融合BM25和Dense检索?

1. 为什么需要Rerank?

单纯依赖向量检索(Dense Retrieval)或关键词检索(BM25)都有其固有的局限性:

  • 向量检索 (Dense Retrieval):

    • 优点: 能深刻理解语义相似性,可以召回字面不匹配但意思相关的文档(例如,提问“公司年假有多少天”,能召回包含“年度休假政策”的文档)。
    • 缺点:
      1. 对关键词不敏感: 对于一些特定的、低频的术语、ID或人名(如“JIRA-12345”,“张三的报告”),Embedding模型可能无法精确捕捉,导致召回失败。
      2. 语义鸿沟问题: 训练数据与实际业务数据存在领域差异时,模型可能产生错误的语义关联。
      3. 计算成本高: 对全库进行向量检索,尤其是在大规模数据上,成本和延迟都较高。
  • 关键词检索 (BM25 - Sparse Retrieval):

    • 优点:
      1. 快且高效: 基于倒排索引,技术成熟,速度极快。
      2. 关键词精确匹配: 对专有名词、ID等关键词的匹配效果非常好。
    • 缺点:
      1. 语义理解能力差: 无法处理同义词、近义词或更复杂的语义关系,容易因用词不同而漏掉相关文档。

Rerank的作用:Rerank(重排)模型是一个更强大、更复杂的模型,它不用于初次召回,而是对初步召回的候选集进行精细化的二次排序。它的核心价值在于:

  1. 提升排序精度: Rerank模型(通常是Cross-Encoder)可以同时看到querycandidate document的完整内容,进行深度的交互式语义相关性判断,其精度远高于仅能独立编码的向量检索模型(Bi-Encoder)。
  2. 融合多种召回源: 它是融合BM25和向量检索结果的天然“裁判”。
  3. 降低误报: 有效过滤掉那些在初筛阶段因为某些“伪相关”而被召回的文档。

2. 如何融合BM25和Dense检索?

融合两种召回方式,我们采用的是**混合检索(Hybrid Search)**的策略,其核心是“先召回,后融合排序”。具体实现有以下几种主流方式:

  1. 两阶段检索(Two-Stage Retrieval)

    这是我们最常用的架构:

    • 第一阶段:并行召回 (Recall)

      1. BM25召回: 用户query同时发往一个基于关键词的检索引擎(如Elasticsearch, OpenSearch)。召回Top-K个(比如K=50)与关键词最相关的文档。
      2. 向量检索召回: 用户的query经过Embedding模型转换成向量,然后去向量数据库(如Milvus, Faiss, Weaviate)中进行相似度搜索,也召回Top-N个(比如N=50)语义最相关的文档。
    • 第二阶段:融合与重排 (Fusion & Rerank)

      1. 结果去重与合并: 将BM25和向量检索召回的K+N个结果合并成一个候选列表,并去除重复的文档。此时我们得到了一个比如80个左右的候选集。
      2. Rerank精排:
        • 将用户的query和这80个候选文档的每一个,组成[query, candidate document]对。
        • 将这些候选文档对输入到Rerank模型(例如 bge-reranker-large, Cohere Rerank 或自训练的Cross-Encoder模型)中。
        • Rerank模型会为每一个候选文档对输出一个相关性得分(通常是0到1之间)。
        • 根据这个得分,对候选集进行降序排列
      3. 最终选择: 取出经过Rerank后得分最高的Top-M个(例如M=3或5)文档,作为最终的上下文信息,喂给LLM进行答案生成。
  2. 分数融合 (Score Fusion)

    这是一种更轻量级的融合方式,不一定需要独立的Rerank模型,但效果通常不如前者。

    1. 同样是并行进行BM25召回和向量检索召回。
    2. 对两种检索方法返回的结果,将其原始得分进行归一化(Normalization),使其处于相同的尺度(如0-1)。
    3. 使用一个加权融合算法,如Reciprocal Rank Fusion (RRF),来计算每个文档的最终得分。RRF对文档的排名进行加权,而不是分数,这使得它对不同检索系统的分数分布不敏感,鲁棒性更好。
      • RRF公式: RRF_Score(d) = Σ (1 / (k + rank_i(d))),其中rank_i(d)是文档d在第i个召回系统中的排名,k是一个常数(如60)。
    4. 根据融合后的最终得分进行排序,选出Top-M。

3. 选择与实践

  • 首选两阶段检索 + Rerank模型。这是效果驱动下的最佳实践。它能最大化地结合BM25的精确匹配能力和向量检索的语义理解能力,并通过强大的Rerank模型进行最终裁决,输出的上下文质量最高。

  • Rerank模型的选型:

    • 开源模型: BAAI的bge-reranker-large在公开评测上表现非常出色,是私有化部署的首选。
    • 商业API: Cohere的rerank API效果公认顶尖,如果可以接受API调用,它是一个非常好的选择。
    • 我们同样会建立评估集,来测试不同Rerank模型在自己业务数据上的表现。
  • 为什么Rerank更优: Rerank模型(Cross-Encoder)和Embedding模型(Bi-Encoder)的核心区别在于:

    • Bi-Encoder(用于向量检索):querydocument独立编码成向量的,检索时只计算向量间的距离。它不知道要和谁比较,所以只能生成一个“通用”的语义表示。
    • Cross-Encoder(用于Rerank):querydocument拼接在一起(例如用[SEP]分隔)后,同时输入模型进行计算的。模型可以充分地进行内部的Attention交互,捕捉两者之间细微的关联,因此判断更准。但这也意味着它计算量大,不适合用于全库召回,只能用于小范围的精排。

在企业级知识库后端,融合BM25和Dense检索,并引入Rerank模型,是一个多路召回,一票终审的精密架构。

  1. 我们一定会用Rerank,因为它是保证最终上下文质量的关键一环。
  2. 我们会采用并行召回 + Rerank精排的架构,因为它效果最好。
  3. 我们会并行发起BM25和向量检索召回请求,以获取一个高召回率、低精确率的候选集,兼顾关键词和语义。
  4. 然后利用Rerank模型对这个候选集进行“精加工”,得到一个高精确率的最终上下文列表,再交给LLM。

这个架构虽然比单一检索路径更复杂,但它带来的效果提升是显著的,能够更好地应对企业知识库中多样化、复杂化的查询需求。

Q. Prompt是如何写的?如何评估Hit Rate和Hallucination

1. Prompt是如何写的?

Prompt的构建包含以下几个层面:

  1. 基础模板设计

    这个模板不是一成不变的,而是包含了多个可填充的“槽位”。一个典型的RAG Prompt模板可能长这样:

    你是一个专业的[角色],例如“XX公司技术支持专家”。
    请基于以下提供的上下文信息,用简洁、专业的语言回答用户的问题。
    
    [上下文信息]
    ---
    {retrieved_chunks}
    ---
    
    [历史对话] (可选,用于多轮对话)
    ---
    {chat_history}
    ---
    
    [用户问题]
    {user_query}
    
    回答要求:
    1. 只能使用提供的上下文信息进行回答,如果上下文中没有足够信息,请明确指出“根据我所掌握的资料,无法回答这个问题”。
    2. 回答应保持[风格要求],例如“客观中立”、“热情友好”。
    3. [其他指令],例如“如果答案包含步骤,请使用列表形式呈现”。
    
    请给出你的回答:
  2. 业务专家协同设计

    对于Prompt中的角色定义 ([角色])领域特定指令 ([其他指令]),需要与业务专家或产品经理紧密合作。例如:

    • 如果知识库是面向HR的,我们会和HR专家一起确定,LLM应该扮演一个“资深HRBP”的角色,回答风格应该是“严谨、准确且富有同理心”。
    • 如果知识库是面向开发者的API文档,我们会和技术负责人一起确定,回答中应优先包含代码示例。
  3. 基于用户反馈和评测结果的迭代 (持续优化)

    Prompt不是一劳永逸的。我们会持续收集用户反馈和进行离线评测。

    • 用户反馈: 如果用户频繁反馈“答案太啰嗦”,我们就在Prompt中加入“请回答核心要点,保持简洁”的指令。
    • 评测发现: 如果评测中发现模型经常“过度自信”地回答“我不知道”,即使上下文中有一点点线索,我们可能会调整指令,鼓励它进行合理的推断,并明确标出“这部分是基于上下文的推断”。

2. 如何评估Hit Rate和Hallucination?

评估是RAG系统中非常重要但又充满挑战的一环。我们通常会建立一个多维度、自动与人工相结合的评测体系。

  1. 评估检索环节的 Hit Rate (召回命中率)

    Hit Rate衡量的是我们的**检索模块(Retriever)**是否成功地将包含答案的“黄金”文档片段(Golden Chunks)召回到了Top-K的候选集中。

    • 前提: 需要构建一个评测数据集。这个数据集由一系列的(问题, 黄金文档ID/内容)对组成。这通常需要人工标注。
    • 评估流程:
      1. 遍历评测集中的每一个“问题”。
      2. 用我们的RAG系统(BM25+向量检索+Rerank)对该问题进行检索,获得Top-K个召回结果(chunks)。
      3. 检查这K个结果中,是否包含了预先标注的那个“黄金文档”
      4. Hit Rate = (命中了黄金文档的问题数量) / (总问题数量)
    • 作用: 这个指标直接反映了RAG系统中“R”(Retrieval)部分的好坏。如果Hit Rate低,说明我们的Chunk切分、Embedding模型或Rerank策略有问题,LLM就算再聪明也“巧妇难为无米之炊”。
  2. 评估生成环节的 Hallucination (幻觉)

    评估幻觉更加复杂,因为它涉及到对生成内容语义层面的判断。我们主要采用以下几种方法:

    • 方法一:基于上下文的忠实度评估 (Faithfulness / Groundedness)

      这个指标衡量生成的答案是否完全基于所提供的上下文。这是检测“捏造事实”型幻觉的核心。

      • 自动化评估:

        1. 使用LLM进行裁判 (LLM-as-a-Judge): 这是一个非常流行且有效的方法。我们会设计一个专门的评估Prompt,将生成的答案原始的上下文一起提供给一个强大的LLM(如GPT-4),然后问它:“请判断以下‘答案’中的每一句话,是否都能在给定的‘上下文’中找到依据。请给出是/否的判断,并说明理由。”
        2. 自动化工具: 像Ragas, TruLens, DeepEval这样的开源框架,已经内置了这类评估指标。它们会自动执行上述流程,并给出一个0到1之间的忠实度分数。
      • 人工评估: 对于一些关键的或自动化评估分数较低的Case,我们会进行人工抽样审查,进行最终裁定。

    • 方法二:答案相关性评估 (Answer Relevance)

      这个指标衡量生成的答案是否与用户原始问题相关。这可以检测到那些“答非所问”型的幻觉。

      • 自动化评估:
        1. 同样可以使用LLM-as-a-Judge。将用户问题生成的答案提供给LLM,问它:“这个答案在多大程度上回应了用户的问题?请从1-5分打分。”
        2. 也可以计算“问题”和“答案”的Embedding向量相似度,作为一个辅助参考指标,但效果不如LLM裁判。
    • 方法三:“毒性”与负面内容检测 (Negative Content Detection)

      除了事实性幻觉,我们还要防止模型生成不当、有偏见或“胡说八道”的内容。

      • 自动化评估:
        1. 使用内容审查API(如OpenAI Moderation API)。
        2. 维护一个“负面关键词”列表进行匹配。
        3. 设计特定的“红队测试 (Red Teaming)”问题,主动诱导模型产生不当言论,来测试系统的“护栏”是否坚固。

3. 评估闭环

我们会将上述评估流程集成到CI/CD流水线中。每当我们的RAG系统有任何重大变更(比如更换Embedding模型、调整Chunk策略、修改Prompt模板),我们都会自动运行这套评测集,生成一份详细的评估报告,包含Hit Rate, Faithfulness, Answer Relevance等核心指标。只有当新版本的指标不低于旧版本,或者在可接受的范围内,我们才会将其部署到生产环境。

通过这个闭环,我们确保了对RAG系统质量的持续监控和改进。