智能体课程文档
文档分析图
并获得增强的文档体验
开始使用
文档分析图
我是阿尔弗雷德,为您服务。作为韦恩先生的忠实管家,我擅自记录了我是如何协助韦恩先生处理各种文件需求的。当他外出忙于…夜间活动时,我确保他所有的文件、训练计划和营养计划都得到妥善分析和整理。
在他离开前,他留下了一张纸条,上面写着他本周的训练计划。然后我负责为明天的餐食制定一份菜单。
为了将来应对此类事件,让我们使用 LangGraph 创建一个文档分析系统,以满足韦恩先生的需求。该系统可以
- 处理图像文档
- 使用视觉模型(视觉语言模型)提取文本
- 在需要时执行计算(以演示普通工具)
- 分析内容并提供简洁的摘要
- 执行与文档相关的特定指令
管家的工作流程
我们将构建的工作流程遵循以下结构化模式
设置环境
%pip install langgraph langchain_openai langchain_core
以及导入
import base64
from typing import List, TypedDict, Annotated, Optional
from langchain_openai import ChatOpenAI
from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
from langgraph.graph.message import add_messages
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from IPython.display import Image, display
定义代理状态
这个状态比我们之前看到的状态稍微复杂一些。AnyMessage
是 Langchain 中的一个类,它定义了消息,而 add_messages
是一个运算符,它会添加最新消息,而不是用最新状态覆盖它。
这是 LangGraph 中的一个新概念,您可以在状态中添加运算符来定义它们之间应该如何交互。
class AgentState(TypedDict):
# The document provided
input_file: Optional[str] # Contains file path (PDF/PNG)
messages: Annotated[list[AnyMessage], add_messages]
准备工具
vision_llm = ChatOpenAI(model="gpt-4o")
def extract_text(img_path: str) -> str:
"""
Extract text from an image file using a multimodal model.
Master Wayne often leaves notes with his training regimen or meal plans.
This allows me to properly analyze the contents.
"""
all_text = ""
try:
# Read image and encode as base64
with open(img_path, "rb") as image_file:
image_bytes = image_file.read()
image_base64 = base64.b64encode(image_bytes).decode("utf-8")
# Prepare the prompt including the base64 image data
message = [
HumanMessage(
content=[
{
"type": "text",
"text": (
"Extract all the text from this image. "
"Return only the extracted text, no explanations."
),
},
{
"type": "image_url",
"image_url": {
"url": f"data:image/png;base64,{image_base64}"
},
},
]
)
]
# Call the vision-capable model
response = vision_llm.invoke(message)
# Append extracted text
all_text += response.content + "\n\n"
return all_text.strip()
except Exception as e:
# A butler should handle errors gracefully
error_msg = f"Error extracting text: {str(e)}"
print(error_msg)
return ""
def divide(a: int, b: int) -> float:
"""Divide a and b - for Master Wayne's occasional calculations."""
return a / b
# Equip the butler with tools
tools = [
divide,
extract_text
]
llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(tools, parallel_tool_calls=False)
节点
def assistant(state: AgentState):
# System message
textual_description_of_tool="""
extract_text(img_path: str) -> str:
Extract text from an image file using a multimodal model.
Args:
img_path: A local image file path (strings).
Returns:
A single string containing the concatenated text extracted from each image.
divide(a: int, b: int) -> float:
Divide a and b
"""
image=state["input_file"]
sys_msg = SystemMessage(content=f"You are a helpful butler named Alfred that serves Mr. Wayne and Batman. You can analyse documents and run computations with provided tools:\n{textual_description_of_tool} \n You have access to some optional images. Currently the loaded image is: {image}")
return {
"messages": [llm_with_tools.invoke([sys_msg] + state["messages"])],
"input_file": state["input_file"]
}
ReAct 模式:我如何协助韦恩先生
请允许我解释此代理中的方法。该代理遵循所谓的 ReAct 模式(推理-行动-观察)
- 对他的文件和请求进行推理
- 通过使用适当的工具进行行动
- 观察结果
- 根据需要重复,直到我完全满足他的需求
这是使用 LangGraph 实现代理的简单方法。
# The graph
builder = StateGraph(AgentState)
# Define nodes: these do the work
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))
# Define edges: these determine how the control flow moves
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
"assistant",
# If the latest message requires a tool, route to tools
# Otherwise, provide a direct response
tools_condition,
)
builder.add_edge("tools", "assistant")
react_graph = builder.compile()
# Show the butler's thought process
display(Image(react_graph.get_graph(xray=True).draw_mermaid_png()))
我们定义了一个包含工具列表的 tools
节点。assistant
节点只是我们绑定了工具的模型。我们创建了一个包含 assistant
和 tools
节点的图。
我们添加了一个 tools_condition
边缘,它根据 assistant
是否调用工具来路由到 End
或 tools
。
现在,我们添加一个新步骤
我们将 tools
节点连接回 assistant
,形成一个循环。
- 在
assistant
节点执行后,tools_condition
会检查模型的输出是否是工具调用。 - 如果是工具调用,流程将导向
tools
节点。 tools
节点连接回assistant
。- 此循环持续,直到模型决定调用工具。
- 如果模型响应不是工具调用,流程将导向 END,终止进程。
管家行动中
示例 1:简单计算
这是一个示例,展示了 LangGraph 中使用工具的代理的简单用例。
messages = [HumanMessage(content="Divide 6790 by 5")]
messages = react_graph.invoke({"messages": messages, "input_file": None})
# Show the messages
for m in messages['messages']:
m.pretty_print()
对话将按以下方式进行
Human: Divide 6790 by 5
AI Tool Call: divide(a=6790, b=5)
Tool Response: 1358.0
Alfred: The result of dividing 6790 by 5 is 1358.0.
示例 2:分析韦恩少爷的训练文档
当韦恩少爷留下他的训练和膳食记录时
messages = [HumanMessage(content="According to the note provided by Mr. Wayne in the provided images. What's the list of items I should buy for the dinner menu?")]
messages = react_graph.invoke({"messages": messages, "input_file": "Batman_training_and_meals.png"})
互动将按以下方式进行
Human: According to the note provided by Mr. Wayne in the provided images. What's the list of items I should buy for the dinner menu?
AI Tool Call: extract_text(img_path="Batman_training_and_meals.png")
Tool Response: [Extracted text with training schedule and menu details]
Alfred: For the dinner menu, you should buy the following items:
1. Grass-fed local sirloin steak
2. Organic spinach
3. Piquillo peppers
4. Potatoes (for oven-baked golden herb potato)
5. Fish oil (2 grams)
Ensure the steak is grass-fed and the spinach and peppers are organic for the best quality meal.
要点
如果您希望创建自己的文档分析管家,以下是一些关键考虑因素
- 为特定文档相关任务定义明确的工具
- 创建强大的状态跟踪器以维护工具调用之间的上下文
- 考虑工具故障的错误处理
- 维护对先前交互的上下文感知(由运算符
add_messages
确保)
遵循这些原则,您也能提供配得上韦恩庄园的模范文档分析服务。
我相信这份解释已经令人满意。现在,请原谅我,韦恩少爷的披风需要在今晚的活动前熨烫。
< > 在 GitHub 上更新