从网页截图到图文问答:基于 ColiVara 与 DeepSeek Janus-Pro 的多模态网站 RAG 实战
【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub
本文以 Colivara-deepseek-website-RAG 项目为蓝本,系统讲解如何将任意网页抓取为截图、切片成 PDF、交给 ColiVara 完成多模态索引与检索,最终由 DeepSeek 的多模态模型 Janus-Pro 结合图片上下文生成回答的完整链路。读完本文,你将掌握一套「网页级多模态 RAG」的工程实现:既能理解环境搭建、API 配置与一键启动方式,也能从 app.py 与 rag_code.py 的源码级视角,看懂 Firecrawl、ColiVara、Janus-Pro 三者之间真实的调用关系与关键参数。
项目概览:四个组件构成的网页级多模态 RAG
传统 RAG 只处理纯文本,而网页内容天然包含版式、图表、截图等视觉信息。本项目把「网页」这一非结构化输入完整地转换为图像证据,再走「检索 → 多模态生成」的路线,形成一套无需清洗 HTML 文本即可直接问答的方案。
项目共使用四类工具,各司其职:
| 组件 | 职责 | 在本项目中的角色 |
|---|---|---|
| DeepSeek Janus-Pro | 多模态大模型 | 理解检索到的网页截图,结合图片内容生成答案 |
| ColiVara | 文档理解与检索 | 对 PDF(网页截图切片)建索引,按语义召回最相关图片 |
| Firecrawl | 网页抓取 | 将目标网页渲染为整页截图(fullPage screenshot) |
| Streamlit | Web 界面 | 提供侧边栏配置、PDF 预览与流式对话聊天 UI |
项目还提供了一份演示视频,可以直接查看从输入 URL 到对话出结果的真实运行效果。
环境准备与安装
安装 DeepSeek Janus
Janus-Pro 是 DeepSeek 推出的多模态模型,需要先从官方仓库克隆并做本地可编辑安装(注意:请将下述命令中的仓库地址替换为你实际可访问的 DeepSeek 官方 Janus 仓库地址):
git clone https://github.com/deepseek-ai/Janus.git pip install -e ./Janus这一步至关重要:rag_code.py 顶部直接以包的形式导入了 Janus 仓库内的代码:
from Janus.janus.models import MultiModalityCausalLM, VLChatProcessor from Janus.janus.utils.io import load_pil_images其中VLChatProcessor负责把「对话消息 + 图片」编码为模型输入,load_pil_images负责从对话结构中取出 PIL 图片列表。如果跳过 Janus 的安装步骤,运行时导入会直接失败。
安装 Python 依赖
官方要求 Python 3.11 或更高版本,然后执行:
pip install streamlit-pdf-viewer colivara-py streamlit fastembed flash-attn transformers各依赖用途如下:
colivara-py:ColiVara 官方 Python SDK,用于创建集合、上传文档、执行检索;streamlit:Web 界面框架;streamlit-pdf-viewer:在 Streamlit 侧边栏内嵌渲染 PDF;transformers:加载 Hugging Face 上的 Janus-Pro 权重(AutoModelForCausalLM.from_pretrained);flash-attn:加速注意力计算,提升推理速度;fastembed:ColiVara 生态常用的嵌入组件依赖。
需要补充说明的是,从 app.py 的实际导入语句看,运行时还会用到以下几个未出现在上述 pip 列表中的包,请一并确保已安装:
from pdf2image import convert_from_path # 依赖系统级 poppler 工具 from fpdf import FPDF # fpdf2,用于切片生成 PDF from firecrawl import FirecrawlApp # Firecrawl 官方 SDK from dotenv import load_dotenv # 读取 .env from PIL import Image # 图片裁剪 import requests # 下载截图配置 API 密钥
本项目需要两个云服务密钥:ColiVara(文档理解与检索)和 Firecrawl(网页抓取)。在项目根目录创建.env文件并写入:
COLIVARA_API_KEY="<COLIVARA-API-KEY>" FIRECRAWL_API_KEY="<FIRECRAWL-API-KEY>"app.py 通过load_dotenv()加载该文件,随后用os.getenv("COLIVARA_API_KEY")与os.getenv("FIRECRAWL_API_KEY")读取并注入 FirecrawlApp 与 ColiVara 客户端。
启动项目
完成上述三步后,一行命令即可启动:
streamlit run app.py启动后浏览器会打开 Streamlit 界面,侧边栏输入网页 URL、点击Start RAG,系统便会自动完成「抓取 → 切 PDF → 建索引」三步预处理,之后即可在底部输入框开始图文问答,并支持Clear ↺按钮重置会话。
源码解读:一次「网页 → 问答」的完整旅程
下面沿着 app.py 与 rag_code.py 的实际代码,拆解整条数据流。
第一步:Firecrawl 全页截图
点击 Start RAG 后,app.py 会实例化 FirecrawlApp 并抓取目标网页:
app = FirecrawlApp(api_key=os.getenv("FIRECRAWL_API_KEY")) scrape_result = app.scrape_url(url_input, params={'formats': ['screenshot@fullPage'], 'waitFor': 10000})两个关键参数值得注意:
formats: ['screenshot@fullPage']:要求 Firecrawl 返回整页截图,而不是只截首屏,从而保证长网页的内容不被截断;waitFor: 10000:等待 10 秒再截图,给前端 JavaScript 渲染(尤其是懒加载内容)留出时间,避免截到空白页。
第二步:截图切片生成 PDF
ColiVara 的文档理解面向 PDF,因此需要把一张超长网页截图转成 PDF。create_pdf_from_screenshot 的实现思路是垂直均分为 10 片:
slice_height = math.ceil(height / 10) # 每片高度 = 总高 / 10 pdf = FPDF(unit='pt', format=[width, slice_height]) # 每页大小与切片一致 pdf.set_auto_page_break(auto=False) # 关闭自动分页 for i in range(10): top = i * slice_height bottom = min((i + 1) * slice_height, height) slice_img = image.crop((0, top, width, bottom)) # 逐片裁剪 pdf.add_page() pdf.image(temp_filename, x=0, y=0, w=width, h=bottom-top)这里的工程细节是:每片对应 PDF 的一页,页尺寸严格贴合切片的长宽比;同时关闭auto_page_break,避免内容被系统自动分页再次切割而失真。最后输出screenshot_slices.pdf供后续索引。
第三步:ColiVara 集合创建与文档索引
得到 PDF 后,app.py 初始化 ColiVara 客户端并完成两级写入:
rag_client = ColiVara(api_key=os.getenv("COLIVARA_API_KEY")) new_collection = rag_client.create_collection( name=st.session_state.collection_name, metadata={"description": "Webpage screenshots"} ) document = rag_client.upsert_document( collection_name=st.session_state.collection_name, name="webpage_document", document_path=st.session_state.pdf_path )create_collection:创建专属集合,集合名由uuid生成("webpage_collection" + str(session_id),见 app.py),避免多次会话互相污染;upsert_document:上传 PDF,ColiVara 在服务端完成版面分析、文本与视觉信息提取,建立多模态索引。
值得强调的是,本项目把会话级缓存也考虑了进去:st.session_state.file_cache以"{session_id}-webpage.pdf"为 key 缓存已建好的 query_engine,同一会话重复点击不会重复建索引(app.py)。
第四步:Retriever 检索与图片召回
检索逻辑封装在 rag_code.py 的Retriever类中:
class Retriever: def search(self, query): results = self.rag_client.search( query=query, collection_name=self.collection_name, top_k=3 ) related_image = results.results[0].img_base64 # 取最相关的一张 image_data = base64.b64decode(related_image) image = Image.open(io.BytesIO(image_data)) image.save("tempfile.jpeg") return "tempfile.jpeg"关键点:
top_k=3:召回 3 个候选,但最终只取results[0]即最相关的一张作为上下文图片;- 召回结果直接以
img_base64形式返回,无需再次下载,解码后落盘为tempfile.jpeg供多模态模型读取; - 这说明 ColiVara 检索返回的不只是「文本片段」,而是图片本身——这是本项目区别于文本 RAG 的核心差异。
第五步:Janus-Pro 多模态生成
生成侧由 rag_code.py 的RAG类完成。模型默认使用deepseek-ai/Janus-Pro-1B,初始化时加载权重并转为 bfloat16 精度:
class RAG: def __init__(self, retriever, llm_name="deepseek-ai/Janus-Pro-1B"): self._setup_llm() def _setup_llm(self): self.vl_chat_processor = VLChatProcessor.from_pretrained( self.llm_name, cache_dir="./Janus/hf_cache") self.tokenizer = self.vl_chat_processor.tokenizer self.vl_gpt = AutoModelForCausalLM.from_pretrained( self.llm_name, trust_remote_code=True, cache_dir="./Janus/hf_cache").to(torch.bfloat16).eval()query()方法把检索到的图片与用户问题拼装为 Janus 的对话格式:
qa_prompt_tmpl_str = f"""The user has asked the following question: Query: {query} Some images are available to you for this question. You have to understand these images thoroughly and extract all relevant information that will help you answer the query.""" conversation = [ {"role": "User", "content": f"<image_placeholder> \n {qa_prompt_tmpl_str}", "images": [image_context]}, {"role": "Assistant", "content": ""}, ]随后经过load_pil_images→vl_chat_processor(...)编码 →prepare_inputs_embeds组装图像与文本 embedding → 语言模型生成:
outputs = self.vl_gpt.language_model.generate( inputs_embeds=inputs_embeds, attention_mask=prepare_inputs.attention_mask, pad_token_id=self.tokenizer.eos_token_id, bos_token_id=self.tokenizer.bos_token_id, eos_token_id=self.tokenizer.eos_token_id, max_new_tokens=512, do_sample=False, use_cache=True, )这里隐含了几个可调参数:
max_new_tokens=512:答案最长生成 512 个 token;do_sample=False:使用贪心解码,输出确定性更强;use_cache=True:启用 KV Cache 加速;- prompt 模板明确要求模型先彻底理解图片再回答,将“读图”这一指令固化进系统提示。
最后,app.py 在 Streamlit 端以流式方式逐 chunk 渲染回答(带▌光标效果),形成完整的对话体验。
关键参数速查表
| 参数 | 位置 | 默认值 | 作用与调优建议 |
|---|---|---|---|
waitFor | app.py | 10000ms | 等待页面 JS 渲染;动态站点可调大,静态站可调小以提速 |
formats | app.py | screenshot@fullPage | 整页截图,保证长页内容完整 |
| 切片数量 | app.py | 10 片 | 切片越多单页越高清,但 PDF 体积与索引耗时同步上升 |
top_k | rag_code.py | 3 | 召回候选数;最终只取 top-1 图片,可修改为多图拼接上下文 |
llm_name | rag_code.py | deepseek-ai/Janus-Pro-1B | 可换成更大规格的 Janus-Pro 权重,按显存权衡 |
| 精度 | rag_code.py | bfloat16 | 兼顾显存与精度;显存充足时可试 fp16 或原始精度 |
max_new_tokens | rag_code.py | 512 | 需要长答案时可调大 |
cache_dir | rag_code.py | ./Janus/hf_cache | 模型权重缓存目录,注意预留磁盘空间 |
使用建议与限制
- 硬件要求:Janus-Pro-1B 需在本地以 bfloat16 加载并完成图片编码推理,建议具备支持 bf16 的 GPU;若仅 CPU 运行,生成速度会明显下降。
- 网络依赖:Firecrawl 抓取与 ColiVara 索引均为云端服务,需要网络连通与有效 API 密钥;本地模型推理则完全离线。
- 超长网页:截图切片固定为 10 片,超长网页单页内容密度更高,若视觉细节被压缩,可考虑增加切片数。
- 会话隔离:集合名绑定会话 UUID,并配合
file_cache缓存避免重复索引,多开会话互不干扰。 - 只读仓库提醒:本仓库为只读展示用途,文章所述安装、运行与配置均在本地环境完成,无需修改仓库内容。
延伸阅读
- 项目说明:Colivara-deepseek-website-RAG/README.md
- Web 界面与主流程:Colivara-deepseek-website-RAG/app.py
- 检索与多模态生成核心:Colivara-deepseek-website-RAG/rag_code.py
- 演示视频:Colivara-deepseek-website-RAG/video-demo.mp4
【免费下载链接】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),仅供参考