Phần I: Tư Duy Tổng Thể
Với văn bản dài vượt quá giới hạn token của mô hình ngôn ngữ lớn (LLM), cần thiết kế một pipeline **map-reduce** để chia nhỏ, tóm tắt từng phần và tổng hợp:
- Tải nội dung trang web.
- Chia thành các chunk có kích thước kiểm soát được.
- Tóm tắt ban đầu cho mỗi chunk (map).
- Tổng hợp tất cả các tóm tắt ban đầu (reduce).
- Nếu cần, lặp lại bước reduce cho đến khi đáp ứng giới hạn token.
- Xuất ra tóm tắt cuối cùng.
Bây giờ, chúng ta sẽ thực hiện bằng mã.
Phần II: Chuẩn Bị
1. Khởi Tạo LLM
Đầu tiên, sử dụng `initialize_model` để tải LLM:
<!-- langchain_env.py -->
from langchain_core.models import initialize_model
model = initialize_model("gpt-4s-mini", provider="openai")
Phần III: Chương Trình Chính main.py
1. Nhập Thư Viện & Khởi Tạo
import os
import sys
sys.path.append(os.getcwd())
from langchain_docs.loaders import WebPageLoader
from langchain.chains.combine_documents import create_merge_chain
from langchain.chains.llm import LanguageModelChain
from langchain.prompts import TemplatePrompt
from langchain.text_splitters import TokenTextSplitter
from typing import List, Dict
from langchain.chains.reduce import merge_docs, split_doc_list
from langchain.documents import Document
from langflow.constants import Execute
from langflow.graph import ENDPOINT, START_POINT, FlowGraph
from llm_setup import langchain_env
model = langchain_env.model
2. Tải Trang Web
loader = WebPageLoader("https://en.wikipedia.org/wiki/Machine_Learning")
documents = loader.load()
Dùng WebPageLoader để tải nội dung trang web vào danh sách `documents`.
3. Định Nghĩa Mẫu Prompt
- Map Phase Prompt
map_template = TemplatePrompt.from_messages(
[("system", "Tóm tắt ngắn gọn đoạn sau:\\n\\n{content}")]
)
- Reduce Phase Prompt
reduce_template = """
Dưới đây là một loạt các tóm tắt:
{docs}
Hãy tổng hợp và rút gọn thành một tóm tắt chính thống.
"""
reduce_prompt = TemplatePrompt([("user", reduce_template)])
4. Chia Đơn Vị Văn Bản Chunk
text_splitter = TokenTextSplitter(chunk_size=800, overlap=0)
split_docs = text_splitter.split_documents(documents)
print(f"Đã chia thành {len(split_docs)} chunks")
Chia nội dung trang web thành nhiều chunk, mỗi chunk có kích thước 800 tokens.
5. Định Nghĩa Hàm Tính Số Tokens
max_tokens = 800
def calc_token_length(docs: List[Document]) -> int:
return sum(model.get_num_tokens(d.page_content) for d in docs)
Tính tổng số token của tài liệu đầu vào để quyết định có cần tiếp tục gộp hay không.
6. Định Nghĩa Trạng Thái
Trạng thái chính:
class GlobalState(Dict):
contents: List[str]
summaries: List[str]
merged_summaries: List[Document]
final_summary: str
Trạng thái giai đoạn map:
class SummarizeState(Dict):
content: str
7. Tạo Tóm Tắt Ban Đầu (Giai Đoạn Map)
def create_initial_summary(state: SummarizeState):
prompt = map_template.invoke(state["content"])
response = model.invoke(prompt)
return {"summaries": [response.content]}
8. Logic Điều Phối Map
def map_tomato_states(state: GlobalState):
return [
Execute("create_initial_summary", {"content": content}) for content in state["contents"]
]
9. Thu Gom Tóm Tắt
def collect_summaries(state: GlobalState):
return {
"merged_summaries": [Document(summary) for summary in state["summaries"]]
}
10. Logic Reduce
- Hàm Reduce Nội Bộ
def _merge(input: dict) -> str:
prompt = reduce_prompt.invoke(input)
response = model.invoke(prompt)
return response.content
- Gộp Các Tóm Tắt
def merge_summaries(state: GlobalState):
doc_lists = split_doc_list(
state["merged_summaries"],
calc_token_length,
max_tokens,
)
results = []
for doc_list in doc_lists:
combined = merge_docs(doc_list, _merge)
results.append(combined)
return {"merged_summaries": results}
11. Kiểm Tra Tiếp Tục Gộp
def check_merge_needed(state: GlobalState):
num_tokens = calc_token_length(state["merged_summaries"])
if num_tokens > max_tokens:
return "merge_summaries"
else:
return "generate_final_summary"
12. Tạo Tóm Tắt Cuối Cùng
def generate_end_summary(state: GlobalState):
response = _merge(state["merged_summaries"])
return {"final_summary": response}
Phần IV: Xây Dựng Sơ Đồ Lưu Trình (FlowGraph)
graph = FlowGraph(GlobalState)
graph.add_node("create_initial_summary", create_initial_summary)
graph.add_node("collect_summaries", collect_summaries)
graph.add_node("merge_summaries", merge_summaries)
graph.add_node("generate_end_summary", generate_end_summary)
graph.add_conditional_edges(START_POINT, map_tomato_states, ["create_initial_summary"])
graph.add_edge("create_initial_summary", "collect_summaries")
graph.add_conditional_edges("collect_summaries", check_merge_needed)
graph.add_conditional_edges("merge_summaries", check_merge_needed)
graph.add_edge("generate_end_summary", ENDPOINT)
app = graph.compile()
Phần V: Thực Thi Quy Trình Tóm Tắt
for step in app.stream(
{"contents": [doc.page_content for doc in split_docs]},
{"recursion_limit": 10},
):
print(list(step.keys()))
Sử dụng `.stream()` để khởi động toàn bộ pipeline, truyền dữ liệu đã cắt, xuất kết quả theo dòng chảy cho đến khi hoàn thành.