ai-engineering-hub 实战:基于 Cleanlab TLM 与 LlamaParse 构建可信 RAG(Trustworthy RAG)系统
2026/9/10 16:04:59 网站建设 项目流程

ai-engineering-hub 实战:基于 Cleanlab TLM 与 LlamaParse 构建可信 RAG(Trustworthy RAG)系统

【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub

面向复杂文档的 RAG 问答,最怕的不是"答不出来",而是"答得自信但错了"。本教程以 trustworthy-rag 项目为核心,讲解如何借助 Cleanlab 的可信语言模型(TLM)为每个 RAG 回答附带可信度评分(trustworthiness score)推理依据(explanation),并用 LlamaParse 将复杂 PDF 解析为 LLM 友好的 Markdown。读完本文,你将掌握一套"答案与置信度同时产出"的可信 RAG 完整工程方案,并能在本地一键运行可交互的 Streamlit 演示。

为什么需要 Trustworthy RAG:从"生成答案"到"评估答案"

传统 RAG 流水线通常只负责把检索到的上下文拼进提示词,然后让 LLM 生成答案,至于这个答案是否可靠、是否有幻觉,往往缺乏量化信号。Trustworthy RAG 的思路是在生成环节引入可信语言模型(Trustworthy Language Model,TLM):TLM 不仅生成文本,还会同步输出一个置信度分数和一条自然语言推理说明,让系统与使用者都能判断"这句话到底可不可信"。

在 trustworthy-rag 项目中,这一思路被拆成两个核心组件:

  1. Cleanlab TLM:提供置信度分数(trustworthiness_score)与推理(explanation)的可信语言模型,作为 RAG 的 LLM 后端;
  2. LlamaParse:由 LlamaCloud 提供的文档解析服务,将表格、图表、多栏排版等复杂 PDF 解析成干净的 Markdown,保证进入向量库的是"LLM 就绪"的文本。

因此整个项目的技术骨架是:LlamaParse(复杂文档解析)→ 向量索引(嵌入 + 检索)→ Cleanlab TLM(生成 + 可信度评分),再通过 LlamaIndex 的事件机制把评分与推理从底层模型中"捞"出来,呈现给用户。

环境准备与依赖安装

1. 准备 API Key 并配置 .env

项目运行时需要两个外部服务的密钥,对应 .env.example 中的两个变量:

变量用途获取方式
LLAMA_CLOUD_API_KEY调用 LlamaParse 解析复杂 PDF在 LlamaCloud 控制台生成
CLEANLAB_API_KEY调用 Cleanlab TLM 生成可信回答在 TLM 服务页面申请

操作方式:将 .env.example 复制为.env,填入真实密钥。仓库 .gitignore 已忽略.env,密钥不会进入版本库。

2. 安装依赖

README 明确要求Python 3.11 或更高版本,然后执行:

pip install llama-index-llms-cleanlab llama-index llama-index-embeddings-huggingface

三个包各自承担明确职责:

  • llama-index-llms-cleanlab:Cleanlab TLM 的 LlamaIndex 集成,负责把 TLM 包装成标准的BaseLLM,供Settings.llmVectorStoreIndex使用;
  • llama-index:核心框架,提供索引、查询引擎、提示词模板与 instrumentation(事件)系统;
  • llama-index-embeddings-huggingface:本地 Hugging Face 嵌入模型接入,负责将文档与查询向量化。

另外,app.py 中还用到了streamlitllama-parsepython-dotenv等依赖,运行应用前需要一并安装(可参考 notebook 首行注释中的安装命令)。

3. 运行应用

streamlit run app.py

启动后浏览器会打开 Streamlit 界面:左侧侧边栏可上传 PDF 并预览、建索引,主区域是与文档对话的聊天窗口,每条回答下方都会展示Trustworthiness ScoreReasoning

核心链路一:用 Cleanlab TLM 作为可信 LLM

TLM 的接入方式在 app.py 的load_llm()中一目了然:

@st.cache_resource def load_llm(): options = { "model": "gpt-4o", "max_tokens": 256, "log": ["explanation"], } llm = CleanlabTLM(api_key=os.environ["CLEANLAB_API_KEY"], options=options) return llm

三个关键配置项:

  • model:底层托管的模型标识(当前仓库固定为gpt-4o)。TLM 会在该模型之上进行可信度校准,因此换模型即意味着换一套置信度分布,调参时建议保持稳定;
  • max_tokens:单次生成的最大 token 数,仓库取 256。对 RAG 问答场景这是一个较克制的上限,防止回答失控超长;若你的文档问题需要长答案,可适度调大;
  • log:声明需要 TLM 额外返回的信息,仓库传入["explanation"],即要求模型在生成回答的同时给出"为什么可信/为何怀疑"的推理文本。

注意api_key直接读取环境变量CLEANLAB_API_KEY,因此.env必须在启动进程前被加载(notebook 中通过load_dotenv()完成,见 main.ipynb)。

TLM 返回的额外字段

直接调用 TLM 时,置信度与推理会出现在响应的additional_kwargs中。main.ipynb 给出了一个真实运行样例:

response = llm.complete("What is NVIDIA's ticker symbol?") print(response) # NVIDIA's ticker symbol is NVDA. response.additional_kwargs

输出:

{'trustworthiness_score': 0.9885545474223644, 'explanation': 'Did not find a reason to doubt trustworthiness.'}

这里trustworthiness_score是一个 0~1 的浮点数(样例为 0.989),explanation是一句人类可读的推理。这份证据说明:可信度信息由 TLM 在生成时同步产出,不需要额外做二次打分模型,这正是本方案"零额外开销地获得可信信号"的关键。

核心链路二:用 LlamaParse 解析复杂文档

复杂 PDF(含表格、双栏、扫描排版、数学公式)直接喂给切分器,往往会把版式语义打碎。项目使用 LlamaParse 先把 PDF 转成结构化 Markdown,再进行索引。相关代码在 app.py:

from llama_parse import LlamaParse parser = LlamaParse(result_type="markdown") docs = parser.load_data(file_path)
  • result_type="markdown":指定解析输出格式为 Markdown,这是后续文档切分与检索最友好的形态;
  • load_data(file_path):上传的文件先被写入临时目录(tempfile.TemporaryDirectory),再交给 LlamaParse 异步解析;notebook 的运行日志显示解析会生成一个job_id(如3e4a97ad-7b24-45e2-af2f-45c549e10e88),即 LlamaParse 云端任务编号;
  • 解析失败时,app.py 会捕获异常并通过st.error提示"Error parsing PDF",避免应用崩溃。

仓库 docs 目录内置了一份示例文档dspy.pdf(DSPy 框架论文/白皮书),notebook 正是用它完成了端到端演示,你可以直接复用该文件验证链路。

核心链路三:向量索引、查询引擎与提示词定制

嵌入模型

仓库在嵌入上使用了 BGE 系列本地模型:

  • app.py:HuggingFaceEmbedding(model_name="BAAI/bge-large-en-v1.5", trust_remote_code=True)
  • main.ipynb:HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

同一项目内 notebook 用small版(更轻量、便于快速实验),Streamlit 应用用large版(精度更高)。两者都是英文为主的向量模型,这也提示了适用前提:本文方案面向英文文档效果最佳,中文文档需要替换为合适的中文嵌入模型。

构建索引与查询引擎

app.py 展示了从文档到查询引擎的最小闭环:

Settings.embed_model = embed_model index = VectorStoreIndex.from_documents(docs, show_progress=True) Settings.llm = load_llm() query_engine = index.as_query_engine()

这里通过全局Settings分别注入嵌入模型与 TLM,VectorStoreIndex.from_documents完成切分、向量化与入库,as_query_engine()得到可回答问题的查询引擎。注意顺序:先设置嵌入模型再建索引,先设置 LLM 再生成查询引擎。

定制 QA 提示词模板

项目没有沿用默认提示词,而是定制了一个要求模型逐步思考、不确定就明说的模板(app.py):

qa_prompt_tmpl_str = ( "Context information is below.\n" "---------------------\n" "{context_str}\n" "---------------------\n" "Given the context information above I want you to think step by step to answer the query in a crisp manner, incase case you don't know the answer say 'I don't know!'.\n" "Query: {query_str}\n" "Answer: " ) qa_prompt_tmpl = PromptTemplate(qa_prompt_tmpl_str) query_engine.update_prompts( {"response_synthesizer:text_qa_template": qa_prompt_tmpl} )

两处设计值得注意:

  • 模板同时注入{context_str}(检索到的上下文)与{query_str}(用户问题),并在指令中显式要求"step by step"思考、"不知道就回答 I don't know!"——这既提升了推理质量,也让 TLM 的置信度判断建立在更稳的生成过程之上;
  • update_prompts的键名response_synthesizer:text_qa_template是 LlamaIndex 内部响应合成器的提示词注册名,直接改模板而无需改检索逻辑,体现了 LlamaIndex 的可插拔设计。

核心链路四:用 Instrumentation 事件机制捕获可信度评分

答案文本来自query_engine.query(),但可信度分数与推理存放在底层 TLM 响应的additional_kwargs,普通调用拿不到。项目巧妙利用了 LlamaIndex 的 instrumentation(事件分发)机制来解决,核心实现在 utils.py:

from llama_index.core.instrumentation import get_dispatcher from llama_index.core.instrumentation.events.llm import LLMCompletionEndEvent from llama_index.core.instrumentation.event_handlers import BaseEventHandler class GetTrustworthinessScoreAndReasoning(BaseEventHandler): events: ClassVar[List[BaseEvent]] = [] trustworthiness_score: float = 0.0 reasoning: str = "" @classmethod def class_name(cls) -> str: return "GetTrustworthinessScoreAndReasoning" def handle(self, event: BaseEvent) -> Dict: if isinstance(event, LLMCompletionEndEvent): self.trustworthiness_score = event.response.additional_kwargs[ "trustworthiness_score" ] self.reasoning = event.response.additional_kwargs["explanation"] self.events.append(event)

其工作机理是:

  1. 事件监听:LlamaIndex 的根调度器get_dispatcher()会在 LLM 完成生成时派发LLMCompletionEndEvent
  2. 按类型过滤:自定义BaseEventHandlerhandle()只处理LLMCompletionEndEvent,从中取出response.additional_kwargs里的trustworthiness_scoreexplanation
  3. 注册挂载setup_trustworthiness_handler()把 handler 注册到根 dispatcher,之后所有 LLM 调用的事件都会被它捕获:
def setup_trustworthiness_handler(): root_dispatcher = get_dispatcher() event_handler = GetTrustworthinessScoreAndReasoning() root_dispatcher.add_event_handler(event_handler) return event_handler

因此,RAG 问答后只需从同一个 handler 实例读取两个属性即可拿到可信信号(utils.py 的outputs_with_trustworthiness/display_response封装了这一步)。这是本项目区别于普通 RAG 的最核心模式:不改动检索与生成主流程,仅通过事件订阅就为系统叠加了"可信度观测"能力,可迁移到任何 LlamaIndex 应用。

端到端验证:Notebook 演示的真实输出

main.ipynb 把上述链路串联成可逐步复现的演示,流程为:

  1. load_dotenv()加载.env,配置CleanlabTLMgpt-4o/max_tokens=256/log=["explanation"]);
  2. 直接调用llm.complete(...)观察additional_kwargs中的trustworthiness_scoreexplanation(见上文 NVIDIA 示例);
  3. 设置Settings.llmSettings.embed_modelBAAI/bge-small-en-v1.5);
  4. LlamaParse(result_type="markdown").load_data("docs/dspy.pdf")解析示例文档,构建VectorStoreIndex与查询引擎;
  5. 注册事件 handler,查询 "What is DSPy?" 并打印完整结果。

notebook 保存的真实运行输出如下:

Response: DSPy is a framework that abstracts the process of prompting and fine-tuning language models by using natural language signatures. ... Trustworthiness score: 0.96 Reasoning: Did not find a reason to doubt trustworthiness.

这个例子非常有信息量:模型基于文档给出了完整、流畅的 DSPy 定义回答,同时给出0.96的高可信度与"未发现可疑点"的推理。它证明了整套链路(解析 → 检索 → 生成 → 可信度捕获)可以真实跑通,且置信度与回答质量一致。若检索到的上下文不足或存在矛盾,TLM 的分数通常会下降——这正是将该分数作为 RAG 质量监控信号的基础。

交互层:Streamlit 应用的用户体验设计

app.py 在工程细节上也值得学习:

  • 会话隔离:用uuid.uuid4()生成session_id,上传文件以{session_id}-{filename}为键存入st.session_state.file_cache,避免多用户/多次上传互相污染;
  • 缓存复用@st.cache_resource缓存 TLM 实例;已建索引的文件直接复用query_engine,不重复解析(app.py);
  • PDF 内联预览:通过 base64 嵌入<iframe>在侧边栏展示原文(app.py),方便用户对照答案与原文;
  • 流式输出 + 可视化评分:回答按单词逐字渲染(time.sleep(0.05)模拟打字机效果),随后用带呼吸灯动画的浮层展示Trustworthiness Score(保留两位小数),再单独展示Reasoning文本(app.py);
  • 清理入口Clear ↺按钮调用reset_chat()清空消息并触发gc.collect()释放内存。

适用前提与使用建议

  • 英文优先:示例嵌入模型(BGE en 系列)与示例文档(docs/dspy.pdf)均面向英文,中文场景需替换嵌入模型并评估 TLM 对中文的置信度表现;
  • 需要两个外部 API Key:整个方案依赖 LlamaCloud(LlamaParse)与 Cleanlab TLM 两个云端服务,离线环境无法直接运行;
  • max_tokens=256是当前配置:回答较长的复杂问题时可能被截断,可按需调大;同时model字段决定底层模型与置信度分布,生产环境更换前建议先小规模验证;
  • 可信度分数是辅助信号而非绝对真理:它来自 TLM 对自身生成过程的内部评估,实践中应将其与检索质量、人工抽检结合使用,例如对低分回答自动触发"重新检索"或"拒绝回答"策略;
  • 事件捕获依赖单次查询的最后一次 LLM 调用:从实现看,handler 记录的是最近一次LLMCompletionEndEvent的字段,若查询链路中存在多次 LLM 调用(如多轮代理),需要根据事件顺序进一步区分,这是将该模式扩展到 Agent 场景时要注意的边界。

总结

本文围绕 trustworthy-rag 完整拆解了"可信 RAG"的落地方案:LlamaParse 负责把复杂 PDF 变成干净的 Markdown,BGE 嵌入模型负责向量化,Cleanlab TLM 负责生成并同步产出置信度评分与推理,LlamaIndex instrumentation 事件机制负责把可信信号无侵入地取出并展示。你可以在本仓库中按 README 的三步(配.envpip installstreamlit run app.py)立刻复现,也可以直接阅读 app.py、utils.py 与 main.ipynb 三份源码,将"事件驱动捕获可信度"这一模式移植到自己的 RAG 系统中——让每一次回答都自带可审计的置信度证据。

【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询