RAG 进阶:Agentic RAG 与多智能体协作(系列二·第3篇):多模态 Agent

让 Agent 能看图、能画图、能处理 PDF/Excel

前面两篇解决了"工具怎么安全"和"上下文怎么不爆",这篇把 Agent 的感知能力从纯文本扩展到图片、文档、表格。核心思路不变:多模态模型负责"看懂",LangGraph 负责"串联",工具层负责"解析"。


一、架构总览

markdown 复制代码
用户输入(文字 + 图片/文件)
        ↓
   路由节点(判断模态)
        ↓
  ┌─────────────────────────────┐
  │ 图片 → 视觉分析节点           │
  │ PDF  → 文档解析节点           │
  │ Excel→ 数据分析节点           │
  │ 画图 → 图像生成节点           │
  └─────────────────────────────┘
        ↓
   整合节点(汇总结果输出)

关键点:在线问答和离线入库要分开。OCR 一个 200 页的扫描件可能要几分钟,不能让用户等。


二、图片理解:让 Agent "看图说话"

用支持视觉的多模态模型(GPT-4o / Claude 3.5 / Qwen-VL),把图片转成 Base64 直接喂给模型。

python 复制代码
import base64
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage

llm_vision = ChatOpenAI(model="gpt-4o", temperature=0)

def encode_image(path: str) -> str:
    with open(path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")

def analyze_image(image_path: str, question: str) -> str:
    """让模型看图并回答问题"""
    b64 = encode_image(image_path)
    message = HumanMessage(content=[
        {"type": "text", "text": question},
        {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}}
    ])
    return llm_vision.invoke([message]).content

# 示例
result = analyze_image("factory_equipment.jpg", "这台设备有没有潜在故障?")

典型场景:设备巡检拍照分析、截图问题诊断、设计稿评审。


三、PDF 解析:最复杂的格式

PDF 分三类,处理策略完全不同:

类型 特征 处理方式
原生文本 PDF 可直接复制文字 PyPDF2 / pdfplumber 直接提取
扫描版 PDF 本质是图片 OCR(PaddleOCR / Tesseract)
混合 PDF 文字+图表混排 分层提取:文字走文本提取,图表走 OCR
python 复制代码
import pdfplumber
from PIL import Image
import pytesseract

def parse_pdf(pdf_path: str) -> str:
    """解析 PDF,自动判断是否需要 OCR"""
    full_text = []
    
    with pdfplumber.open(pdf_path) as pdf:
        for i, page in enumerate(pdf.pages):
            # 1. 尝试提取文本
            text = page.extract_text() or ""
            
            # 2. 如果文本太少,可能是扫描件,走 OCR
            if len(text.strip()) < 50:
                image = page.to_image(resolution=200)
                ocr_text = pytesseract.image_to_string(
                    image.original, lang="chi_sim+eng"
                )
                text = ocr_text
            
            # 3. 提取表格(转成 Markdown)
            tables = page.extract_tables()
            table_text = ""
            for table in tables:
                if table:
                    # 转 Markdown 表格
                    header = "| " + " | ".join(table[0]) + " |"
                    sep = "| " + " | ".join(["---"] * len(table[0])) + " |"
                    rows = "\n".join(
                        "| " + " | ".join(row) + " |" for row in table[1:]
                    )
                    table_text += f"\n{header}\n{sep}\n{rows}\n"
            
            full_text.append(f"--- 第 {i+1} 页 ---\n{text}\n{table_text}")
    
    return "\n".join(full_text)

避坑:

  • 多栏排版的 PDF 提取后文字顺序可能错乱,需要按坐标排序。
  • 表格提取是难点,pdfplumber 和 camelot 可以互补使用。
  • 大文件先分页截断,不要一次性塞给模型。

四、Excel/CSV 分析:让 Agent 会"算账"

Excel 处理的关键是先转成结构化数据,再交给代码解释器或模型分析。

python 复制代码
import pandas as pd

def analyze_excel(file_path: str, question: str) -> str:
    """分析 Excel/CSV 文件"""
    # 1. 读取文件
    if file_path.endswith(".csv"):
        df = pd.read_csv(file_path)
    else:
        df = pd.read_excel(file_path)
    
    # 2. 生成数据摘要(让模型了解数据结构)
    summary = {
        "shape": df.shape,
        "columns": list(df.columns),
        "dtypes": df.dtypes.astype(str).to_dict(),
        "sample": df.head(3).to_dict(orient="records"),
        "describe": df.describe().to_dict(),
    }
    
    # 3. 让模型基于摘要决定下一步操作
    prompt = f"""你是数据分析师。基于以下数据摘要,回答用户问题。
如果需要进行计算或绘图,请输出 Python 代码。

数据摘要:{summary}
用户问题:{question}

回答或输出可执行的 Python 代码。"""
    
    response = llm.invoke(prompt).content
    # 如果返回代码,交给代码解释器执行(见系列二第1篇)
    return response

进阶:配合系列一第1篇的代码解释器,Agent 可以自动画折线图、柱状图,甚至生成数据透视表。


五、图像生成:让 Agent 会"画图"

用 DALL·E 3 或 Stable Diffusion 作为工具,让 Agent 根据描述生成图片。

python 复制代码
from openai import OpenAI

client = OpenAI()

def generate_image(prompt: str, size: str = "1024x1024") -> str:
    """生成图片并返回 URL"""
    response = client.images.generate(
        model="dall-e-3",
        prompt=prompt,
        size=size,
        quality="standard",
        n=1,
    )
    return response.data[0].url

# 作为 Agent 工具注册
image_gen_tool = {
    "name": "generate_image",
    "description": "根据文字描述生成图片",
    "parameters": {
        "type": "object",
        "properties": {
            "prompt": {"type": "string", "description": "图片描述"}
        },
        "required": ["prompt"]
    }
}

典型场景:写诗配图、产品概念图、营销海报草图。


六、完整多模态工作流(LangGraph)

python 复制代码
from typing import TypedDict, Optional
from langgraph.graph import StateGraph, END

class MultiModalState(TypedDict):
    query: str
    image_path: Optional[str]
    file_path: Optional[str]
    parsed_content: str
    analysis_result: str
    final_output: str

def router_node(state: MultiModalState) -> str:
    """根据输入类型路由到不同处理节点"""
    if state.get("image_path"):
        return "vision"
    elif state.get("file_path"):
        ext = state["file_path"].split(".")[-1].lower()
        if ext in ("pdf",):
            return "document"
        elif ext in ("xlsx", "xls", "csv"):
            return "spreadsheet"
    return "text_only"

def vision_node(state: MultiModalState) -> MultiModalState:
    result = analyze_image(state["image_path"], state["query"])
    return {"analysis_result": result}

def document_node(state: MultiModalState) -> MultiModalState:
    content = parse_pdf(state["file_path"])
    # 截断后交给模型总结
    prompt = f"请总结以下文档内容,回答用户问题:{state['query']}\n\n{content[:15000]}"
    result = llm.invoke(prompt).content
    return {"parsed_content": content[:5000], "analysis_result": result}

def spreadsheet_node(state: MultiModalState) -> MultiModalState:
    result = analyze_excel(state["file_path"], state["query"])
    return {"analysis_result": result}

def text_only_node(state: MultiModalState) -> MultiModalState:
    result = llm.invoke(state["query"]).content
    return {"analysis_result": result}

def integrate_node(state: MultiModalState) -> MultiModalState:
    return {"final_output": state["analysis_result"]}

# 构建图
workflow = StateGraph(MultiModalState)
workflow.add_node("router", router_node)
workflow.add_node("vision", vision_node)
workflow.add_node("document", document_node)
workflow.add_node("spreadsheet", spreadsheet_node)
workflow.add_node("text_only", text_only_node)
workflow.add_node("integrate", integrate_node)

workflow.set_entry_point("router")
workflow.add_conditional_edges("router", router_node, {
    "vision": "vision",
    "document": "document",
    "spreadsheet": "spreadsheet",
    "text_only": "text_only",
})
for node in ["vision", "document", "spreadsheet", "text_only"]:
    workflow.add_edge(node, "integrate")
workflow.add_edge("integrate", END)

app = workflow.compile()

# 使用示例
result = app.invoke({
    "query": "这份财报的核心数据是什么?",
    "file_path": "financial_report.pdf",
})
print(result["final_output"])

七、生产级注意事项

  1. 离线/在线分离:文档入库走异步图(OCR → 校验 → 分块 → 向量化),在线问答只消费已索引的结果。
  2. 成本优化:视觉模型 Token 贵,先做预处理(裁剪、压缩、OCR 转文本)再调用。
  3. 质量门控:OCR 结果加一道"人工审核"节点,低置信度的结果不直接入库。
  4. 文件安全:上传的文件要做类型校验、大小限制、病毒扫描,防止恶意文件。
  5. 2026 生态动态:LangGraph 在 2026 年已支持多模态 Agent 编排,Deep Agents 框架也加入了多模态能力,生产级项目可以直接基于这些能力搭建。

相关推荐
Morning的Tech2 小时前
LangGraph 的 Swarm 多智能体机制
人工智能·agent
用户3134672143542 小时前
learn-claude-code笔记4: Hooks — 挂在循环上,不写进循环里
llm·agent
simon_1232 小时前
不到一个月,agent 的权限分类器拦了我 27 次:至少 7 次只是在"看"
agent
lucas_AI2 小时前
Agent 工具箱越大越难选?Tracer 砍掉八成工具 schema,选得反而更准
llm·agent
学IT的周星星3 小时前
记忆系统:让 Agent 记住你是谁、上次聊到哪
运维·服务器·人工智能·windows·大模型·llm·agent
yuanyxh3 小时前
我的工作流
agent·claude·deepseek
badhope3 小时前
AI Agent安全治理实战:从API密钥泄露到沙箱隔离的完整防线
后端·llm·agent
武子康3 小时前
旧工具结果写进新回合:两次消费之间的打断窗口
人工智能·后端·agent
许雪里4 小时前
Agent 记忆架构设计:原理 × 业界三大范式 × XXL-AI 落地实践
后端·llm·agent