利用 vLLM 手撸一个多模态RAG系统

# 利用 vLLM 实现多模态RAG 系统 本文将深入探讨如何使用 vLLM 构建多模态信息检索与生成(Multimodal RAG)系统,以实现对包含文本、图像和表格的文档的有效处理和智能问答。 如果您想了解更多关于自然语言处理或其他技术领域的信息,请关注我们的公众号 **柏企科技圈**。 ![](https://upload-images.jianshu.io/upload_images/17294212-917f4260e2848466.png) ## 一、多模态 RAG 概述 多模态 RAG 是一种先进的信息检索和生成方法,它整合了多种内容类型,主要是文本和图像。与传统仅依赖文本的 RAG 系统不同,多模态 RAG 充分发挥了文本和视觉信息的优势,为生成响应提供了更全面、更具上下文的基础。 许多文档,如研究论文、商业报告等,都包含文本、图像、图表和表格的混合。通过将视觉元素纳入检索和生成过程,多模态 RAG 系统能够: 1. 捕捉纯文本分析中丢失的细微差别; 2. 提供更准确、与上下文相关的响应; 3. 通过视觉辅助增强对复杂概念的理解; 4. 提高生成内容的整体质量和深度。 ## 二、多模态 RAG 的实现策略 实现多模态 RAG 管道有多种方法,各有其优势和考虑因素: 1. **联合嵌入和检索** - 利用 CLIP(对比语言 - 图像预训练)或 ALIGN(大规模图像和噪声文本嵌入)等模型为文本和图像创建统一的嵌入。 - 使用 FAISS 或 Annoy 等库实现近似最近邻搜索,以进行高效检索。 - 将检索到的多模态内容(原始图像和文本块)输入到多模态大语言模型(如 LLaVa、Pixtral 12B、GPT - 4V、Qwen - VL)中进行答案生成。 2. **图像到文本转换** - 使用 LLaVA 或 FUYU - 8b 等模型从图像生成摘要。 - 使用基于文本的嵌入模型(如 Sentence - BERT)为原始文本和图像标题创建嵌入。 - 将文本块传递给大语言模型进行最终答案合成。 3. **原始图像访问的混合检索** - 采用多模态大语言模型从图像生成文本摘要。 - 将这些摘要与原始图像的引用以及其他文本块一起嵌入和检索。这可以通过带有 Chroma、Milvus 等向量数据库的多向量检索器来实现,这些数据库用于存储原始文本、图像及其摘要以便检索。 - 对于最终答案生成,使用能够同时处理文本和原始图像输入的多模态模型,如 Pixtral 12B、LLaVa、GPT - 4V、Qwen - VL。 在本文中,我们将探索第三种方法,利用一系列强大的工具组合来创建一个高效的多模态 RAG 系统。 ## 三、系统实现步骤 1. **工具准备** - **Unstructured**:用于解析各种文档格式(包括 PDF)中的图像、文本和表格。 - **LLaVa via vLLM**:由 vLLM 服务引擎驱动,使用名为 LLaVA(llava - hf/llava - 1.5 - 7b - hf)的视觉语言模型来处理文本/表格摘要以及多模态任务,如图像摘要和从集成的文本和视觉输入生成答案。虽然不是最先进的模型,但 LLaVA 效率高且计算成本低。借助 vLLM,它可以无缝部署在 CPU 上,对于那些希望在性能和资源效率之间取得平衡的人来说是一种理想的、具有成本效益的解决方案。 - **Chroma DB**:作为我们的向量数据库,用于存储文本块、表格摘要、图像摘要以及它们的原始图像。结合其多向量检索器功能,它为我们的多模态系统提供了强大的存储和检索系统。 - **LangChain**:作为协调工具,将这些组件无缝集成在一起。 通过结合这些工具,我们将展示如何构建一个强大的多模态 RAG 系统,该系统能够处理不同类型的文档,生成高质量的摘要,并生成利用文本和视觉信息的全面答案。 2. **数据下载** 我们将使用这篇博客文章作为文档源,因为它包含以图像形式呈现的图表和表格中的有价值信息。 ```python import os import io import re import uuid import base64 import shutil import requests from tqdm import tqdm from PIL import Image import matplotlib.pyplot as plt from IPython.display import HTML, display from unstructured.partition.pdf import partition_pdf from langchain_core.documents import Document from langchain_text_splitters import CharacterTextSplitter from langchain.storage import InMemoryStore from langchain_chroma import Chroma from langchain.chains.llm import LLMChain, PromptTemplate from langchain_core.messages import HumanMessage, SystemMessage from langchain_core.prompts.chat import (ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate) from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnableLambda, RunnablePassthrough from langchain.retrievers.multi_vector import MultiVectorRetriever from openai import OpenAI as OpenAI_vLLM from langchain_community.llms.vllm import VLLMOpenAI from langchain.embeddings import HuggingFaceEmbeddings embeddings = HuggingFaceEmbeddings(model_name='BAAI/bge-large-en') os.mkdir("data") shutil.move("gtm_benchmarks_2024.pdf", "data") ``` 3. **从 PDF 文档中提取文本、表格和图像** 下载 PDF 后,我们将利用 unstructured.io 库处理文档并提取内容。 ```python def extract_pdf_elements(path, fname): """ 从 PDF 文件中提取图像、表格和文本块。 path: 文件路径,用于存储图像(.jpg) fname: 文件名 """ return partition_pdf( filename=path + fname, extract_images_in_pdf=True, infer_table_structure=True, chunking_strategy="by_title", max_characters=4000, new_after_n_chars=3800, combine_text_under_n_chars=2000, image_output_dir_path=path ) def categorize_elements(raw_pdf_elements): """ 将从 PDF 中提取的元素分类为表格和文本。 raw_pdf_elements: unstructured.documents.elements 列表 """ tables = [] texts = [] for element in raw_pdf_elements: if "unstructured.documents.elements.Table" in str(type(element)): tables.append(str(element)) elif "unstructured.documents.elements.CompositeElement" in str(type(element)): texts.append(str(element)) return texts, tables folder_path = "./data/" file_name = "gtm_benchmarks_2024.pdf" raw_pdf_elements = extract_pdf_elements(folder_path, file_name) texts, tables = categorize_elements(raw_pdf_elements) text_splitter = CharacterTextSplitter.from_tiktoken_encoder( chunk_size = 1000, chunk_overlap = 0 ) joined_texts = " ".join(texts) texts_token = text_splitter.split_text(joined_texts) print("文本块数量:", len(texts)) print("表格元素数量:", len(tables)) print("分词后的文本块数量:", len(texts_token)) ``` 4. **生成表格摘要** 我们将使用在 CPU 机器上运行的 vLLM 引擎来驱动 7B 参数的 LLaVA 模型(llava - hf/llava - 1.5 - 7b - hf)生成表格摘要。我们也可以像在任何 RAG 系统中通常那样使用基于文本的大语言模型,但在这里我们将使用能够处理文本和图像的 LLaVa 模型本身。 生成表格摘要是为了增强自然语言检索,这些摘要对于高效检索原始表格和文本块至关重要。 ```python llm_client = VLLMOpenAI( base_url = "http://localhost:8000/v1", api_key = "dummy", model_name = "llava-hf/llava-1.5-7b-hf", temperature = 1.0, max_tokens = 300 ) def generate_text_summaries(texts, tables, summarize_texts=False): """ 总结文本元素 texts: 字符串列表 tables: 字符串列表 summarize_texts: 是否总结文本 """ prompt_text = """你是一个负责总结表格以便检索的助手。 给出一个简洁的、针对检索进行优化的表格摘要。确保捕捉到所有细节。 输入: {element} """ prompt = ChatPromptTemplate.from_template(prompt_text) summarize_chain = {"element": lambda x: x} | prompt | llm_client | StrOutputParser() text_summaries = [] table_summaries = [] if texts and summarize_texts: text_summaries = summarize_chain.batch(texts, {"max_concurrency": 3}) elif texts: text_summaries = texts if tables: table_summaries = summarize_chain.batch(tables, {"max_concurrency": 3}) return text_summaries, table_summaries text_summaries, table_summaries = generate_text_summaries( texts_token, tables, summarize_texts=False ) print("文本摘要数量:", len(text_summaries)) print("表格摘要数量:", len(table_summaries)) ``` 5. **生成图像摘要** 现在,我们将使用视觉语言模型(VLM)生成图像摘要。 注意:图像可以通过两种主要方式提供给模型:传递图像链接或在请求中直接传递 base64 编码的图像。 ```python api_key = "dummy" base_url = "http://localhost:8000/v1" vlm_client = OpenAI_vLLM( api_key = api_key, base_url = base_url ) def encode_image(image_path): """获取 base64 字符串""" with open(image_path, "rb") as image_file: return base64.b64encode(image_file.read()).decode("utf-8") def image_summarize(img_base64, prompt): """生成图像摘要""" chat_response = vlm_client.chat.completions.create( model="llava-hf/llava-1.5-7b-hf", max_tokens=1024, messages=[{ "role": "user", "content": [ {"type": "text", "text": prompt}, "type": "image_url", "image_url": { "url": f"data:image/jpeg;base64,{img_base64}", }, }, ], stream=False ) return chat_response.choices[0].message.content.strip() def generate_img_summaries(path): """ 为图像生成摘要和 base64 编码字符串 path: Unstructured 提取的.jpg 文件路径列表 """ img_base64_list = [] image_summaries = [] prompt = """你是一个负责总结图像以便最佳检索的助手。 这些摘要将被嵌入并用于检索原始图像。 编写一个清晰简洁的摘要,捕捉所有重要信息,包括图像中的任何统计数据或关键点。""" for img_file in tqdm(sorted(os.listdir(path))): if img_file.endswith(".jpg"): img_path = os.path.join(path, img_file) base64_image = encode_image(img_path) img_base64_list.append(base64_image) generated_summary = image_summarize(base64_image, prompt) print(generated_summary) image_summaries.append(generated_summary) return img_base64_list, image_summaries img_base64_list, image_summaries = generate_img_summaries(folder_path) assert len(img_base64_list) == len(image_summaries) ``` 6. **存储和索引文档摘要** 为了配置多向量检索器,我们将原始文档(包括文本、表格和图像)存储在文档存储中,同时在向量存储中索引它们的摘要,以提高语义检索效率。 ```python def create_multi_vector_retriever( vectorstore, text_summaries, texts, table_summaries, tables, image_summaries, images ): """ 创建索引摘要但返回原始图像或文本的检索器 """ store = InMemoryStore() id_key = "doc_id" retriever = MultiVectorRetriever( vectorstore=vectorstore, docstore=store, id_key=id_key ) def add_documents(retriever, doc_summaries, doc_contents): doc_ids = [str(uuid.uuid4()) for _ in doc_contents] summary_docs = [ Document(page_content=s, metadata={id_key: doc_ids[i]}) for i, s in enumerate(doc_summaries) ] retriever.vectorstore.add_documents(summary_docs) retriever.docstore.mset(list(zip(doc_ids, doc_contents))) if text_summaries: add_documents(retriever, text_summaries, texts) if table_summaries: add_documents(retriever, table_summaries, tables) if image_summaries: add_documents(retriever, image_summaries, images) return retriever vectorstore = Chroma( collection_name="mm_rag_vectorstore", embedding_function=embeddings, persist_directory="./chroma_db" ) retriever_multi_vector_img = create_multi_vector_retriever( vectorstore, text_summaries, texts, table_summaries, tables, image_summaries, img_base64_list ) ``` 7. **多向量检索设置** 接下来,我们定义处理和处理文本数据和 base64 编码图像的函数和配置,包括调整图像大小和格式化模型提示。它设置了一个多模态检索和生成(RAG)上下文链,以集成和分析文本和图像数据来回答用户查询。 由于我们使用 vLLM 的 HTTP 服务器为视觉语言模型提供服务,该服务器与 OpenAI 视觉 API(聊天完成 API)兼容,因此为了设置模型的上下文,我们遵循特定的聊天模板。 ```python def plt_img_base64(img_base64): """显示 base64 编码字符串为图像""" image_html = f'' display(HTML(image_html)) def looks_like_base64(sb): """检查字符串是否看起来像 base64""" return re.match("^[A-Za-z0-9+/]+[=]{0,2}$", sb) is not None def is_image_data(b64data): """ 通过查看数据开头检查 base64 数据是否为图像 """ image_signatures = { b"\xff\xd8\xff": "jpg", b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a": "png", b"\x47\x49\x46\x38": "gif", b"\x52\x49\x46\x46": "webp" } try: header = base64.b64decode(b64data)[:8] for sig, format in image_signatures.items(): if header.startswith(sig): return True return False except Exception: return False def resize_base64_image(base64_string, size=(64, 64)): """ 调整 base64 编码图像的大小 """ img_data = base64.b64decode(base64_string) img = Image.open(io.BytesIO(img_data)) resized_img = img.resize(size, Image.LANCZOS) buffered = io.BytesIO() resized_img.save(buffered, format=img.format) return base64.b64encode(buffered.getvalue()).decode("utf-8") def split_image_text_types(docs): """ 拆分 base64 编码图像和文本 """ b64_images = [] texts = [] for doc in docs: if isinstance(doc, Document): doc = doc.page_content if looks_like_base64(doc) and is_image_data(doc): doc = resize_base64_image(doc, size=(64, 64)) b64_images.append(doc) else: texts.append(doc) return {"images": b64_images, "texts": texts} def img_prompt_func(data_dict): """ 将上下文合并为单个字符串 """ formatted_texts = "\n".join(data_dict["context"]["texts"]) messages = [] text_message = { "type": "text", "text": ( "你是一个在金融和商业指标方面有专长的助手。\n" "你将获得可能包括与业务绩效和行业趋势相关的文本、表格和图表的信息 ## 四、检索测试 我们提出问题:“从 2020 年到 2024 年,公共 SaaS 公司的年收入同比中位数增长率发生了怎样的变化?” ```python query = "How has the median YoY ARR growth rate for public SaaS companies changed from 2020 to 2024?" docs = retriever_multi_vector_img.invoke(query) plt_img_base64(docs[0]) ``` ## 五、运行 RAG 管道生成答案 由于我们当前的模型不支持非常长的上下文以及每个文本提示包含多个多模态项目,所以我们将修改检索到的上下文并测试最终答案合成部分。 ```python context = chain_multimodal_context.invoke(query)[0].content context = [ { 'type': 'text', 'text': "You are an AI assistant with expertise in finance and business metrics.\nYou will be given information that may include text, tables, and charts related to business performance and industry trends.\nYour task is to analyze this information and provide a clear, concise answer to the user's question.\nFocus on the most relevant data points and insights that directly address the user's query.\nUser's question: How has the median YoY ARR growth rate for public SaaS companies changed from 2020 to 2024?" }, { 'type': 'image_url', 'image_url': {'url': 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABAAEADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3wRoQOP1o8pPT9abFKX3Axuu1toJH3vce1SbvY0AN8pPT9axf7Zn+0zQDQr/MblRIQAjjdjcDnp0P0zW5u9jUVxCtxCY2MqAkHdG20j8RQBiW2uXM0sUcvh7UIfMkCbiAVQZI3Me3Az3re8pPT9aZbwi2iEatK4BJzI248+9S7vY0AN8pPT9arXcy20lqnlFvPl8rO7G35Sc+/TH41b3exqKWJJWV2D5UHGGIH5U1bqJ3toLHMj7gA3yNtOVI5x29arpq9hI1uqXSObgssW3kOV+8AfaoLxhLc2ssDXDyQSMhjiYKmSv/AC0J7fnyaZDY3SpEPNsLZUZjtt7fkA/3STgHpk45pDLFpq1tdRW7MHt5JywjhnXa7bevH61cWVHUMjBlPQryDWYbG5hlhZJ4bsRlvmu1/eLn+66jj8qr2TPZzWdnElvZRIJDLbM5cv3Bjbv6n69KAN3cPf8AI0bh7/karWWoW2oWkdzA+Y5ASu4bTwcdDVncv94UAG4e/wCRpCwIPXp6Uu5fUUhYEHkdKAESRGBCupKna2D0PoadkeopqKgBKquSctgd/enYHoKADI9RUU8ENygWVQcHKnOCp9QexqXA9BRgegoAxZLWeG6tp50N60CuqXKHa8efVBw31Hp0qXT724lhtPKkiv4WV/OuVYKVPVRt/TtWrgegqtPp1pczpPLCDMgIWRSVYA+457UAV4NTnn+yg6dcRmeN2bfj90R0DfXtSQXGpyS2wntYIY3iYzjzdzI+eAvqP8aeukQARhpryRUUrh7lyD06889Kkt9MsbIA21pDEyIUDKg3BeOM9ew/KgCxHDGm4qigudzEDqfWn7V9BVKS33TJJFcCNScyLyd/AHrx0pi2so8vdfBsZ3/Ljd+vFAGhtX0FG1fQVnrayjZuvw2M7vlxu/XihbWYeXuvw20Hf8uN3p34oA0Nq+go2r6Cs9bWYbN1+DgHd8v3vTvxQtrMNmb8HCkN8uNx7HrxjigDQ2r6CkKgA8DpVAWsw2ZvwcKQ3y9Tzg9eMcflTo7eVHjZr0OFB3rtxuPPPXjt+VAH/9k=' } ] chat_response = vlm_client.chat.completions.create( model="llava-hf/llava-1.5-7b-hf", messages=[{ "role": "user", "content": context }], stream=True ) for chunk in chat_response: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` 基于提供的用户问题以及检索到的文本片段和图像,模型现在将开始流式输出其响应。 ## 六、注意事项 为了演示图像检索,最初生成了较大(4k 令牌)的文本块,然后进行了总结。然而,情况并非总是如此,可能需要其他方法来确保准确和高效的分块和索引。 总结和答案质量似乎对图像大小和分辨率敏感。 当前使用的视觉语言模型仅支持单图像输入。 模型可能难以理解某些视觉元素,如图表或复杂的流程图。 在某些情况下,模型可能会生成错误的描述或标题。例如,在询问统计问题时可能会提供错误信息。 如果图像中的文本不清晰或模糊,模型将尽力解释,但结果可能不太准确。 ## 七、未来展望 使用具有更长上下文窗口且支持每条消息传递多个图像和/或传递多轮对话的视觉语言模型(如 Pixtral - 12B)测试准确性。 实现文本和图像模态之间更复杂的交互,使模型能够根据问题动态地优先考虑视觉或文本信息。 引入更精细的视觉内容总结技术,以生成更好的图像语义表示。 由于我们使用 vLLM 来提供模型服务,研究在 CPU 和 GPU 上使用不同优化运行相同模型时性能的变化将是很有趣的。 最后但同样重要的是,使用更好的分块和检索机制。 > 如果您对文中的技术细节或代码实现有任何疑问,欢迎随时交流探讨。同时,我们也将持续关注 RAG 技术的发展动态,为您带来更多的前沿资讯和深度分析。 > > 以上就是本文的全部内容,希望对您有所帮助!如果您想了解更多关于自然语言处理或其他技术领域的信息,请关注我们的公众号 **柏企科技圈**。 ## 参考文献 - https://blog.langchain.dev/semi-structured-multi-modal-rag/ - https://github.com/langchain-ai/langchain - https://python.langchain.com/docs/how_to/multi_vector/ - https://docs.vllm.ai/en/latest/models/vlm.html - https://platform.openai.com/docs/guides/vision - https://cs.stanford.edu/~myasu/blog/racm3/ # 推荐阅读 [1. 专家混合(MoE)大语言模型:免费的嵌入模型新宠](https://mp.weixin.qq.com/s/XwgigFEuyD-ED3IunlSItw?token=2113630118&lang=zh_CN) [2. LLM大模型架构专栏|| 从NLP基础谈起](https://mp.weixin.qq.com/s/MYx5V29WczQzxPybKBbT7Q?token=2113630118&lang=zh_CN) [3. AI Agent 架构新变革:构建自己的 Plan-and-Execute Agent](https://mp.weixin.qq.com/s/NBlp058THkckTKFscjPhiw?token=2113630118&lang=zh_CN) [4. 探索 AI 智能体工作流设计模式](https://mp.weixin.qq.com/s/gQuxYo7LiKuAWr04hzIjQg?token=2113630118&lang=zh_CN) [5. 探秘 GraphRAG:知识图谱赋能的RAG技术新突破](https://mp.weixin.qq.com/s/MtTju5IQ9alTwZP_xAK-bg?token=1679189915&lang=zh_CN) [6. 解锁 RAG 技术:企业数据与大模型的完美融合之道](https://mp.weixin.qq.com/s/9KdLmYj7WbbkMfljWMXvTg?token=1679189915&lang=zh_CN) 本文由[mdnice](https://mdnice.com/?platform=6)多平台发布
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容