如何用AI高效总结整本书?破解上下文限制与成本难题

随着大型语言模型(LLMs)的崛起,文本处理从未如此高效。无论是翻译、摘要,还是聊天机器人,它们都能快速解决问题。

今天,我们将深入探讨如何利用LLMs来总结一本书,并且在过程中应对常见的上下文长度和成本限制。本文将教你如何在不牺牲效果的情况下,用最少的成本实现高效总结。

比如,在处理科幻小说《三体》时,我们可以利用这些技术提取出其中的关键情节,以便快速得到总结,而不必读取整本书。 Cover (2).jpg

让我们以40万字的英文名著《大卫·科波菲尔》为案例来演示。但是在你把这本书的PDF拖进去之前,请先摸摸钱包——因为这趟“阅读之旅”的账单可能高达十几美元!

是的,你没看错。大语言模型(LLM)虽然强大,但它有两个致命的缺点:昂贵,以及健忘

尽管Token的收费比以前已经降了不少,但对于大多数人来说还是太贵。

  1. 天价账单:LLM按Token计费,一本四十万字的书,Token量轻松突破50万。直接扔给GPT-4o,就像开着跑车去买菜,油费(API调用费)高得离谱。
  2. “金鱼记忆”:每个LLM都有一个“上下文窗口”(Context Window),就像它的大脑内存。比如GPT-4o,最多只能记住128k个Token。更要命的是,它还存在“首因效应”(容易记住开头)和“近因效应”(容易记住结尾),中间的大量关键情节可能被它忘得一干二净。

破局的关键,不在于花钱买更大的上下文窗口,而在于更聪明地“喂”数据。今天,我将向你展示一个绝妙的策略,它结合了语义分割和K-Means聚类算法,核心思路只有六个字:

先“浓缩”,再“消化”。

我们会像一位资深编辑,先用算法自动“划重点”,从几十万字的原著中提取出50个最具代表性的核心片段。然后,再请昂贵的GPT-4来精读这50个“精华”,最终串联成一份高质量、低成本的全书摘要。


第一步:环境准备与书籍加载

工欲善其事,必先利其器。我们首先需要安装几个核心的Python库。

pip install langchain langchain_openai openai tiktoken fpdf2 pandas faiss-cpu

(注意:这里我们加入了faiss-cpu用于后续的高效聚类)

接下来,我们加载这本公开版权的《大卫·科波菲尔》PDF,并提取出正文内容。

from langchain.document_loaders import PyPDFLoader

# 加载PDF
loader = PyPDFLoader("David-Copperfield.pdf")
# 我们跳过前言和目录,直接从正文开始
pages = loader.load_and_split()[6:1308] 

# 将所有页面内容合并为一个长文本
text = ' '.join([page.page_content.replace('\t', ' ') for page in pages])

print(f"书籍原文的前200个字符:\n{text[:200]}")

第二步:文本预处理与Token计算

原始文本中总有一些格式噪音,比如页眉页脚的版权信息、多余的换行和空格。我们写个简单的函数来给它清理一下。

import re

def clean_text(text):
   # 移除电子书网站的固定页脚
   cleaned_text = re.sub(r'\s*Free eBooks at Planet eBook\.com\s*', '', text, flags=re.DOTALL)
   # 移除多余的空格、换行符和控制字符
   cleaned_text = re.sub(r' +', ' ', cleaned_text)
   cleaned_text = re.sub(r'(David Copperfield )?[\x00-\x1F]', '', cleaned_text)
   cleaned_text = cleaned_text.replace('\n', ' ')
   return cleaned_text

clean_text = clean_text(text)

# 让我们看看这本书到底有多“贵”
from langchain_openai import OpenAI

llm = OpenAI(openai_api_key="your-openai-key-here") # 填入你的OpenAI API Key
token_count = llm.get_num_tokens(clean_text)
print(f"清洗后,这本书的总Token数为:{token_count}")

输出结果可能会在46万左右。按照GPT-4的输入价格(大约$10/1M tokens),光是输入就要4.6美元,输出的成本会更高,总计几十美元是跑不掉的。

第三步:智能切分(SemanticChunker)——让AI理解上下文

直接按固定字数切分文本太粗暴了,很容易把一个完整的场景或对话切得支离破碎。这里我们使用LangChain的SemanticChunker。它会先将文本转换为向量(表示语义的数字),再根据语义的连贯性来决定在哪里切分,确保每个切片都是一个意义完整的“思想单元”。

from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings

# 使用OpenAI的嵌入模型来理解文本语义
embeddings_model = OpenAIEmbeddings(openai_api_key="your-openai-key-here")

# 初始化语义切分器
text_splitter = SemanticChunker(embeddings_model, breakpoint_threshold_type="interquartile")

# 执行切分,得到一系列文档片段
docs = text_splitter.create_documents([clean_text])
print(f"全书被智能切分成了 {len(docs)} 个语义片段。")

这一步不仅是为了切分,更是为了后面聚类的基础。每个doc都是一个独立的、有意义的故事片段。

你可以采用其它便宜或免费的向量模型以节省成本。

第四步:K-Means聚类——找到故事的“主心骨”

这是我们整个策略的核心。想象一下,我们把几百个故事片段的“语义”投射到一个巨大的三维空间里,意思相近的片段会聚集在一起。K-Means算法就像一个“圈地”大师,它能在这个空间里自动找到N个“兴趣中心”(即质心),并把离中心最近的片段们归为一类。

我们的目标: 从每个“兴趣部落”里,只挑出最靠近中心的那个、最具代表性的片段。

import openai
import numpy as np
import pandas as pd
import faiss

# 1. 获取所有文档片段的嵌入向量
# 注意:text-embedding-3-small 是性价比极高的嵌入模型
response = openai.embeddings.create(
    model="text-embedding-3-small",
    input=[doc.page_content for doc in docs]
)
vectors = [item.embedding for item in response.data]
array = np.array(vectors).astype('float32')

# 2. 使用FAISS执行高效的K-Means聚类
num_clusters = 50  # 我们设定要找出50个核心主题,这个数值可以调整
dimension = array.shape[1]

kmeans = faiss.Kmeans(dimension, num_clusters, niter=20, verbose=True)
kmeans.train(array)
centroids = kmeans.centroids

# 3. 找到离每个聚类中心最近的那个文档
index = faiss.IndexFlatL2(dimension)
index.add(array)
# D是距离,I是索引
D, I = index.search(centroids, 1) 

# 4. 提取出这些最具代表性的文档
# 按原文顺序排序,保证摘要的连贯性
sorted_indices = np.sort(I, axis=0).flatten()
extracted_docs = [docs[i] for i in sorted_indices]

print(f"我们从 {len(docs)} 个片段中,精选出了 {len(extracted_docs)} 个核心片段。")

看,我们成功地将需要处理的文档数量从几百个锐减到了50个!

第五步:精华摘要与串联合成

现在,我们可以“奢侈”地请出GPT-4o了。我们为它精心设计一个Prompt,要求它在总结每个片段时,忘记客套,直接进入故事,保持叙事流畅,仿佛在续写上一段的摘要。

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from tqdm import tqdm

# 定义我们的摘要模型和Prompt
model = ChatOpenAI(temperature=0, model="gpt-4o", openai_api_key="your-openai-key-here")

prompt_template = ChatPromptTemplate.from_template(
    """请为一个长篇故事的其中一部分内容提供详细的摘要。
    你的摘要必须至少有两段,并且要充满细节。
    在总结时,请不要使用“在这段文字中...”或“本段讲述了...”这样的引导语。
    直接切入叙事,就像故事自然延续一样,无缝地呈现主要事件、人物和情感。

    原文片段如下:
    {text}

    摘要:
    """
)

# 构建我们的摘要链
chain = prompt_template | model | StrOutputParser()

# 循环调用,生成并拼接最终摘要
final_summary = ""
for doc in tqdm(extracted_docs, desc="正在生成摘要..."):
    summary_part = chain.invoke({"text": doc.page_content})
    final_summary += summary_part + "\n\n"

print("摘要完成!")

成果展示与成本核算

最激动人心的时刻到了!我们不仅得到了摘要,成本还很低。

  • 我们的方案
    1. 嵌入成本:466,000 tokens * $0.02 / 1M tokens ≈ $0.01 (几乎可以忽略不计)
    2. 摘要成本:我们只对50个精选片段进行摘要。假设每个片段平均500字,生成150字摘要,总处理Token量大约为 50 * (500*1.5)=37,500个输入Token,0.19美元,50*(150*1.5)=11,250个输出Token,0.17美元,一共0.36美元,这个数字远小于原始方案。

我们用极低的成本,换来了一份凝聚了全书精华的摘要。

最后一步:保存你的成果

让我们把这份智慧的结晶保存为PDF文件,方便阅读和分享。

from fpdf import FPDF

# ... (此处省略原文中的PDF生成代码,功能相同) ...
# 注意处理编码问题
final_summary_latin1 = final_summary.encode('latin-1', 'replace').decode('latin-1')

# 创建PDF实例并保存
# ...

print("摘要已成功保存为PDF!")

总结与思考

今天,我们通过一个实战案例,解决了使用LLM处理长文本时的两大核心痛点:成本和上下文限制。我们没有陷入“大力出奇迹”的思维定式,而是巧妙地运用了语义分块+K-Means聚类的策略,实现了对信息的“降维打击”。

这种“先浓缩,再消化”的思想,不仅适用于读书摘要,还可以广泛应用于: * 海量用户评论的情感分析和要点提取。 * 冗长法律文档或科研论文的快速解读。 * 长视频内容的章节自动生成和看点提炼。

对于个人项目或实验,我们今天的方法已经足够强大。但如果你正考虑构建企业级的AI应用,需要处理数百万甚至上亿的文档,那么对这些海量嵌入向量的高效存储、索引和检索就成了新的挑战。这时,像 MyScaleDB 这样专为AI应用设计的、支持SQL的向量数据库就派上用场了。它能帮你更专业、更高效地管理你的“AI记忆库”。

分享这篇文章

评论 (0)

还没有评论,来说两句吧!