RAG,llamindex

4 阅读14分钟

结合你现有的国际渔业法规 RAG 项目(已实现自定义分块、多路召回),以及 LlamaIndex 的核心能力、RAG 通用优化思路和渔业法规的专业属性(多语言、层级化、条款化) ,从LlamaIndex 原生能力落地、检索增强、效果评估、专业适配、工程化优化5 个维度,给你梳理可直接落地的优化点,包含你提到的 query 分析、向量库复用、RAGAS 适配,也补充多路由 / 查询扩写的 LlamaIndex 实现、渔业专属优化等,所有优化都贴合你 “新手用 LlamaIndex” 的背景,给出具体实现思路 + 代码参考,不用额外装复杂库:

一、基础必做:LlamaIndex 原生能力落地(你提到的 + 核心工程化)

这部分是你已经想到的,直接给渔业项目的适配代码 / 步骤,基于你现有的rag_core.py和中医项目的 LlamaIndex 用法,无缝衔接:

1. 向量库持久化 / 读取(避免重复 chunk + 转向量,节省时间)

你现有代码是实时切分、建索引,每次运行都要重新处理文档,直接复用中医项目的StorageContext做向量持久化,适配你的IATTCResolutionSplitter自定义分块器:

python

运行

# 【新增】在你建完index后,添加向量持久化(执行1次即可)
from llama_index.core import StorageContext
# 假设你现有代码构建了index = VectorStoreIndex.from_documents(..., transformations=[IATTCResolutionSplitter()])
index.storage_context.persist(persist_dir="./fishery_emb") # 渔业向量库存储目录

# 【后续运行】直接读取向量库,跳过chunk/转向量(核心优化:只chunk一轮)
storage_context = StorageContext.from_defaults(persist_dir="./fishery_emb")
index = load_index_from_storage(storage_context)

2. Query 全流程追踪(检索片段溯源 + LLM Prompt 查看)

基于中医项目的retrieve方法 +LlamaDebugHandler,适配你的多路召回检索器,既能看最终传给大模型的片段,也能看 Prompt 拼接细节,直接加在你现有查询逻辑后

python

运行

# 1. 检索片段溯源(看多路召回最终选了哪些渔业条款片段)
from llama_index.core import QueryBundle
from llama_index.core.schema import MetadataMode

# 假设你构建了multi_route_retriever = MultiRouteRetriever(index, nodes, llm)
contexts = multi_route_retriever.retrieve(QueryBundle("你的渔业问题,如IATTC禁渔期豁免条件?"))
# 打印溯源片段(带渔业专属的chunk_type/clause_id/tags)
print("===== 检索溯源片段 =====")
for i, ctx in enumerate(contexts):
    node = ctx.node
    print(f"【片段{i+1}】类型:{node.metadata.get('chunk_type')} | 条款ID:{node.metadata.get('clause_id', '无')} | 标签:{node.metadata.get('tags')}")
    print(f"内容:{node.get_content(metadata_mode=MetadataMode.LLM)[:200]}...\n")

# 2. LLM Prompt全流程追踪(看最终拼接的Prompt,调试渔业Prompt模板)
from llama_index.core.callbacks import LlamaDebugHandler, CallbackManager
import llama_index.core.settings as Settings

# 初始化调试器(放在项目开头)
llama_debug = LlamaDebugHandler(print_trace_on_end=True)
Settings.callback_manager = CallbackManager([llama_debug])

# 执行查询后,打印传给大模型的完整Prompt
response = query_engine.query("IATTC禁渔期豁免条件?")
event_pairs = llama_debug.get_llm_inputs_outputs()
print("===== 传给LLM的完整Prompt =====")
print(event_pairs[0][1].payload["formatted_prompt"])

3. 日志精细化(调试多路召回 / 关键词过滤,适配渔业项目)

你现有代码有简单打印,基于 LlamaIndex 的 logging 升级,精准看每一步的召回 / 过滤结果,方便调试渔业法规的关键词提取(如force majeure/fishing closure):

python

运行

import logging
import sys

# 配置DEBUG级别日志,打印多路召回/过滤细节
logging.basicConfig(
    stream=sys.stdout,
    level=logging.DEBUG,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))

运行后会打印BM25 召回数量、向量召回数量、关键词过滤剔除的节点,精准定位渔业检索的问题(比如某条款未被召回是因为关键词不匹配)。

二、核心优化:查询扩写 / 多路由检索(LlamaIndex 原生实现,不用额外装库)

你提到的查询扩写(查询路由) ,其实就是 LlamaIndex 的Multi-Query Retriever/Query Transformation,和你现有MultiRouteRetriever的子查询生成思路契合,直接基于 LlamaIndex 原生组件优化,替代手动写子查询,更简洁且适配 LlamaIndex 生态,分 2 种实现(新手选轻量版,进阶选自定义版):

轻量版:LlamaIndex 原生MultiQueryRetriever(查询扩写,一键实现)

基于大模型自动将原始查询扩写为多个同义查询,提升向量检索的覆盖率,适配渔业法规的专业术语(如 “禁渔期”=“休渔期”=“fishing closure”) ,直接集成到你的多路召回中:

python

运行

from llama_index.core.retrievers import MultiQueryRetriever

# 基于你现有的llm和index,创建查询扩写检索器
multi_query_retriever = MultiQueryRetriever(
    retriever=index.as_retriever(similarity_top_k=10),
    llm=llm,
    num_queries=4 # 扩写4个同义查询,可根据渔业问题调整
)
# 替换你现有代码中的向量召回逻辑,用扩写后的查询做检索
sub_query_nodes = multi_query_retriever.retrieve(QueryBundle("IATTC围网渔船禁渔期豁免?"))

进阶版:LlamaIndexQueryTransformation(自定义渔业查询扩写规则)

如果你想适配渔业的多语言 / 专业层级(如中文问题→扩写英文查询 + 条款号查询),用原生的QueryTransformation自定义扩写逻辑,比你手动写_generate_sub_queries更贴合 LlamaIndex

python

运行

from llama_index.core.query_engine import TransformQueryEngine
from llama_index.core.transformations import QueryTransformation

# 自定义渔业查询扩写器(中文→英文+条款关键词+多视角)
class FisheryQueryTransform(QueryTransformation):
    def __call__(self, query_bundle: QueryBundle) -> QueryBundle:
        original_query = query_bundle.query_str
        # 渔业专属扩写:中文→英文+视角扩写(如法规规定/申请流程/适用对象)
        expanded_queries = [
            original_query,
            f"英文:{self._zh2en_fishery(original_query)}", # 渔业术语中英互转
            f"{original_query} 对应的IATTC条款号是什么?",
            f"{original_query} 的申请流程和所需材料?"
        ]
        # 拼接为新的查询包
        return QueryBundle(query_str=" || ".join(expanded_queries))
    
    def _zh2en_fishery(self, zh_query):
        # 渔业核心术语中英映射(你可补充更多,如禁渔期=fishing closure)
        fishery_map = {"禁渔期":"fishing closure", "豁免":"exemption", "围网渔船":"purse-seine vessel", "不可抗力":"force majeure"}
        for zh, en in fishery_map.items():
            zh_query = zh_query.replace(zh, en)
        return zh_query

# 集成到查询引擎
transform = FisheryQueryTransform()
base_query_engine = index.as_query_engine(similarity_top_k=10)
transformed_query_engine = TransformQueryEngine(base_query_engine, query_transform=transform)

# 执行查询,自动扩写后检索
response = transformed_query_engine.query("IATTC围网渔船因不可抗力错过禁渔期能否豁免?")

三、专业适配:渔业法规 RAGAS 评估优化(贴合多语言 / 层级化特点)

你提到的 RAGAS 评估适配,结合渔业法规的核心属性(英文原版 + 中文译本、公约 + 细则、条款化) ,给你可落地的适配方案,包含标签化改造、Prompt 定制、多语言校验,且基于 RAGAS 原生装饰器实现,不用重写核心逻辑:

1. 前置:给渔业文档加RAGAS 评估专属标签(你现有代码已做,稍补充)

你现有IATTCResolutionSplitter已经给节点加了chunk_type/clause_id/tags补充渔业 RAGAS 评估需要的 4 个核心标签,让上下文精度判定更精准,直接改你现有代码的metadata

python

运行

# 在你创建TextNode时,补充渔业专属标签(以条款节点为例)
node = TextNode(
    text=full_clause,
    metadata={
        "chunk_type": "Clause",
        "clause_id": clause_num,
        "tags": ", ".join(tags),
        "法规层级": "IATTC公约" or "实施细则", # 新增:公约/细则/修正案
        "适用区域": "东太平洋", # 新增:渔业专属,如东太平洋/大西洋
        "鱼种": "金枪鱼" or "鲭鱼", # 新增:适配渔业鱼种
        "作业方式": "围网" or "延绳钓" # 新增:渔业作业方式,精准匹配问题
    }
)

2. RAGAS 忠实度 Prompt 定制(法条引用标注要求)

修改 RAGAS 的faithfulness评估 Prompt,明确要求 LLM 校验时检查 “公约名称、条款号、生效时间” ,适配渔业法规的条款化特点,用 RAGAS 的自定义 Prompt 装饰器实现:

python

运行

from ragas import evaluate
from ragas.metrics import faithfulness
from ragas.metrics.base import Metric
from functools import partial

# 自定义渔业专属的忠实度Prompt
FISHERY_FAITH_PROMPT = """
你是渔业法规评估专家,需要校验LLM的回答是否完全基于给定的上下文,且**所有法条引用必须标注公约名称、条款号、生效时间**。
上下文:{context}
LLM回答:{answer}
请判断回答是否忠实于上下文,且法条引用符合要求,仅输出0-1的分数(0=不忠实/未标注,1=完全忠实/标注完整)。
"""

# 替换RAGAS原生faithfulness的Prompt
fishery_faithfulness = faithfulness.clone()
fishery_faithfulness.prompt.template = FISHERY_FAITH_PROMPT

# 用自定义指标评估渔业RAG
result = evaluate(
    dataset=your_fishery_dataset, # 你的渔业测试数据集
    metrics=[fishery_faithfulness, ...], # 其他指标(如context_precision)
    llm=llm,
    embed_model=embed_model
)

3. 多语言校验:新增 “译文与原文一致性” 指标(RAGAS 装饰器实现)

针对渔业法规英文原版 + 中文译本的特点,基于 RAGAS 的Metric基类自定义指标,校验 LLM 回答中引用的中译文是否与英文原文一致,直接集成到 RAGAS 评估中:

python

运行

from ragas.metrics import Metric
from ragas.metrics.criteria import CriteriaScore

# 自定义多语言一致性指标(渔业英文原文+中文译本)
fishery_zh_en_consistency = CriteriaScore(
    name="fishery_zh_en_consistency",
    criteria="LLM回答中引用的中文法条译文,必须与英文原文的语义完全一致,无增删/曲解,专业术语翻译准确(如fishing closure=禁渔期,非休渔期)",
)

# 评估时加入该指标
result = evaluate(
    dataset=your_fishery_dataset,
    metrics=[fishery_faithfulness, fishery_zh_en_consistency, context_precision],
    llm=llm,
    embed_model=embed_model
)

四、检索增强:基于渔业特点优化 LlamaIndex 检索策略

你现有代码已实现BM25 + 向量 + 多查询三路召回,结合渔业法规条款化、长文档、专业术语密集的特点,补充 3 个 LlamaIndex 原生的检索优化点,提升召回准确率:

1. 混合检索重排序(LlamaIndexRerank,解决多路召回结果融合)

你现有代码只是简单合并去重,用 LlamaIndex 原生的重排序器,基于大模型 / 交叉编码器对多路召回的结果按渔业问题相关性重排序,避免低相关的条款排在前面:

python

运行

from llama_index.core.postprocessor import SentenceTransformerRerank

# 初始化重排序器(适配渔业专业术语,用通用交叉编码器即可)
reranker = SentenceTransformerRerank(
    model="BAAI/bge-reranker-large", # 中文/英文都适配,渔业术语友好
    top_n=10 # 最终保留10个最相关的渔业条款
)

# 集成到查询引擎(你的多路召回后执行重排序)
query_engine = index.as_query_engine(
    similarity_top_k=20,
    node_postprocessors=[reranker], # 重排序作为后处理步骤
    retriever=multi_route_retriever # 你的自定义多路召回器
)

2. 元数据过滤检索(LlamaIndexMetadataFilters,精准匹配渔业属性)

基于你给节点加的法规层级 / 适用区域 / 鱼种 / 作业方式元数据,用 LlamaIndex 的元数据过滤器,实现精准检索(比如只查 “IATTC 公约 + 东太平洋 + 金枪鱼 + 围网” 的条款),避免无关法规被召回:

python

运行

from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.indices.postprocessor import MetadataFilters, FilterCondition

# 定义渔业专属元数据过滤器(可根据用户问题动态生成)
filters = MetadataFilters(
    filters=[
        {"key": "法规层级", "value": "IATTC公约"},
        {"key": "适用区域", "value": "东太平洋"},
        {"key": "作业方式", "value": "围网"},
    ],
    condition=FilterCondition.AND # 同时满足所有条件
)

# 给检索器加过滤器
filtered_retriever = index.as_retriever(
    similarity_top_k=20,
    filters=filters
)

# 构建过滤后的查询引擎
query_engine = RetrieverQueryEngine.from_args(filtered_retriever)

3. 术语增强 Embedding(适配渔业专业术语,提升向量检索精度)

渔业法规有大量专业术语 / 英文缩写(如 IATTC、purse-seine、fishing closure),在 Embedding 前对文本做术语增强,结合 LlamaIndex 的TextTransformation实现,让向量更贴合渔业场景:

python

运行

from llama_index.core.transformations import TextTransformation

# 渔业术语增强器(英文缩写补全+专业术语标准化)
class FisheryTermEnhance(TextTransformation):
    def __call__(self, nodes, **kwargs):
        term_map = {
            "IATTC": "Inter-American Tropical Tuna Commission(美洲间热带金枪鱼委员会)",
            "purse-seine": "围网作业(purse-seine vessel)",
            "fishing closure": "禁渔期(渔业管理措施)"
        }
        for node in nodes:
            text = node.text
            for term, full in term_map.items():
                text = text.replace(term, full)
            node.text = text
        return nodes

# 集成到索引构建/文档加载阶段
enhance_transform = FisheryTermEnhance()
# 建索引时加入术语增强,和你的自定义分块器一起用
index = VectorStoreIndex.from_documents(
    documents,
    transformations=[FisheryTermEnhance(), IATTCResolutionSplitter()]
)

五、工程化优化:LlamaIndex 项目落地必备(新手友好,提升稳定性)

作为 LlamaIndex 新手,在渔业项目中加入这些优化,让项目从 “实验版” 变成 “可落地版” ,无复杂操作,都是原生配置:

1. 自定义 Prompt 模板(中文 + 渔业专业,替代 LlamaIndex 英文默认模板)

参考中医项目的自定义 Prompt,写渔业专属的 QA/Refine Prompt,明确要求 LLM标注条款号、公约名称、生效时间,提升回答的专业性和严谨性:

python

运行

from llama_index.core import PromptTemplate

# 渔业专属QA Prompt(要求标注条款号、公约名称)
FISHERY_QA_PROMPT = PromptTemplate("""
上下文是国际渔业法规(IATTC)的条款内容,包含英文原版和中文译本:
{context_str}
请根据上下文回答以下渔业问题,**所有回答必须标注对应的公约名称、条款号,若有生效时间需一并标注**,回答需严谨、贴合渔业法规专业要求,不用无关内容:
问题:{query_str}
回答:
""")

# 渔业专属Refine Prompt(补充回答时仍需标注条款)
FISHERY_REFINE_PROMPT = PromptTemplate("""
原始问题:{query_str}
现有回答:{existing_answer}
新增上下文:{context_msg}
请根据新增上下文完善现有回答,**完善后仍需标注公约名称、条款号**,若上下文无相关信息,直接返回原有回答,不做修改:
完善后回答:
""")

# 更新查询引擎的Prompt
query_engine.update_prompts({
    "response_synthesizer:text_qa_template": FISHERY_QA_PROMPT,
    "response_synthesizer:refine_template": FISHERY_REFINE_PROMPT
})

2. 流式输出(LlamaIndexstreaming=True,提升渔业问答体验)

参考之前讲的streaming参数,给查询引擎开启流式输出,适合渔业法规的长回答(如条款解读、申请流程) ,让用户不用干等,直接加参数即可:

python

运行

# 构建流式查询引擎
query_engine = index.as_query_engine(
    similarity_top_k=5,
    streaming=True, # 开启流式
    retriever=multi_route_retriever
)

# 流式打印回答
response = query_engine.query("IATTC围网渔船禁渔期豁免的申请流程?")
response.print_response_stream()

3. 批量查询 / 缓存(LlamaIndexCache,提升渔业批量问题处理效率)

如果需要处理批量渔业问题(如批量校验条款、批量回答咨询),用 LlamaIndex 的原生缓存,避免重复检索 / LLM 调用,提升效率:

python

运行

from llama_index.core.cache import SimpleCache
import llama_index.core.settings as Settings

# 初始化缓存(内存缓存,也可改用Redis缓存)
Settings.cache = SimpleCache()

# 第一次查询:正常检索/调用LLM
response1 = query_engine.query("IATTC禁渔期多久?")
# 第二次查询相同问题:直接从缓存获取,无需重复检索
response2 = query_engine.query("IATTC禁渔期多久?")

六、优化优先级建议(新手落地,从易到难)

结合你 “第一次用 LlamaIndex” 的背景,按「基础→核心→专业→工程化」的顺序落地,不用一次性全做,保证每一步都有效果:

  1. 第一优先级(1-2 天落地) :向量库持久化(只 chunk 一轮)+ Query 检索溯源 + 自定义渔业 Prompt 模板(核心工程化,解决重复计算和回答不专业问题);
  2. 第二优先级(3-5 天落地) :LlamaIndex 原生 MultiQueryRetriever(查询扩写)+ 元数据过滤检索(精准匹配渔业属性)+ 流式输出(提升体验);
  3. 第三优先级(1 周左右) :RAGAS 渔业适配(标签化 + 自定义 Prompt + 多语言指标)+ 混合检索重排序(提升召回准确率);
  4. 第四优先级(进阶) :渔业术语增强 Embedding + 批量查询缓存 + 多语言查询扩写(适配英文原版文档)。

最后:LlamaIndex 替代 RAGAS 的原生评估组件

你提到 LlamaIndex 有替代 RAGAS 的组件,就是LlamaIndex Evaluation,基于 LlamaIndex 原生能力实现 RAG 效果评估,不用额外装 RAGAS 库,适合新手,核心支持检索评估(上下文精度 / 召回率)、生成评估(忠实度 / 流畅度) ,且能直接复用你的渔业 LLM/Embedding 模型,简单实现:

python

运行

from llama_index.core.evaluation import (
    FaithfulnessEvaluator,
    ContextPrecisionEvaluator,
    BatchEvalRunner
)

# 初始化LlamaIndex原生评估器(复用你的渔业LLM/Embedding)
faithfulness_eval = FaithfulnessEvaluator(llm=llm)
context_precision_eval = ContextPrecisionEvaluator(llm=llm, embed_model=embed_model)

# 批量评估渔业测试用例
eval_questions = [
    "IATTC围网渔船因不可抗力错过禁渔期能否豁免?",
    "IATTC金枪鱼禁渔期的生效时间是什么?"
]
batch_evaluator = BatchEvalRunner(
    {"faithfulness": faithfulness_eval, "context_precision": context_precision_eval},
    llm=llm
)
# 执行评估
eval_results = batch_evaluator.evaluate_queries(
    query_engine=query_engine,
    queries=eval_questions
)
# 打印评估结果
print(eval_results)

该组件的优势是和 LlamaIndex 生态无缝衔接,不用额外适配模型 / 向量库,缺点是专业度不如 RAGAS,适合你前期快速评估,后期再用 RAGAS 做精细化的渔业专业评估。

以上所有优化点都基于你现有的rag_core.py代码和中医项目的 LlamaIndex 用法,无额外复杂依赖,新手可直接落地,且全程贴合国际渔业法规的专业特点,解决多语言、条款化、层级化的核心问题。