[轻松迁移:从RefineDocumentsChain到LangGraph的转变]

85 阅读3分钟

引言

在处理长文本时,RefineDocumentsChain提供了一种有效的分析策略。它通过将文本分割为较小的文档,依次处理并逐渐完善结果。然而,随着技术的进步,LangGraph实现能够提供更多优势,如监控执行步骤、支持逐步执行结果流等。本文将详细探讨如何从RefineDocumentsChain迁移到LangGraph,并用实际代码示例说明这一转变。

主要内容

RefineDocumentsChain解析

RefineDocumentsChain的策略是将长文本分成小块,每个小块经过处理后逐步更新总结。典型应用场景是总结文本内容,这在大文本相对于给定语言模型的上下文窗口很大时特别有用。

LangGraph的优势

LangGraph不仅能逐步监控执行过程,还允许流式传输执行步骤和单个tokens。此外,由于LangGraph是由模块化组件构成的,因此它易于扩展和修改,如整合工具调用等。

模型加载

我们可以选择不同的聊天模型,如OpenAI、Anthropic等。请注意,由于网络限制,开发者在使用某些API时可能需要考虑使用API代理服务。

# 使用API代理服务提高访问稳定性
import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass()
from langchain_openai import ChatOpenAI

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

示例:总结文档序列

让我们通过生成一些简单的文档来进行示例演示。

from langchain_core.documents import Document

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

代码示例

使用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}"
)
summarize_prompt = ChatPromptTemplate(
    [("human", "Write a concise summary of the following: {context}")])
initial_llm_chain = LLMChain(llm=llm, prompt=summarize_prompt)

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
)

# 执行链
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.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]:
    return "refine_summary" if state["index"] < len(state["contents"]) else END

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)

常见问题和解决方案

  • 如何监控和调试LangGraph的执行过程?
    LangGraph允许流式传输执行步骤和单个tokens,通过详细的日志记录和可视化工具可以实现有效的调试。

  • 如何处理API访问时的网络限制?
    在某些地区,开发者可能需要使用API代理服务来提高访问稳定性。

总结和进一步学习资源

从RefineDocumentsChain迁移到LangGraph提供了更大的灵活性和扩展能力。在使用LangGraph进行复杂文本分析时,可以更好地控制执行过程和即时响应。

进一步学习资源

参考资料

  1. LangChain Documentation: www.langchain.com/docs
  2. LangGraph API Reference: www.langgraph.com/api

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

---END---