配套视频:https://www.bilibili.com/video/BV1SNBQBsEB1/

前言

在AI技术快速发展的今天,2025年被称为"智能体之年"。本文基于MongoDB开发者倡导者Apoorva Joshi的研讨会内容,系统介绍如何从零开始构建一个多模态AI智能体,涵盖核心概念、技术原理和实践方法。

一、理解AI智能体

1.1 什么是AI智能体?

AI智能体是一种使用大语言模型(LLM)进行推理、规划和执行的智能系统。与简单的聊天机器人不同,智能体具备以下核心能力:

  • 自主推理:能够分析问题并制定解决方案

  • 工具调用:通过外部工具采取实际行动

  • 迭代优化:根据执行结果调整策略

  • 目标导向:持续工作直到完成指定任务

简单来说,AI智能体赋予了大语言模型"手和脚",让它们不仅能思考,还能行动。

1.2 三种LLM交互范式对比

在与大语言模型交互时,目前主要存在三种范式:

简单提示(Simple Prompting)

这是最基础的交互方式,用户直接向大模型提问,模型依赖其预训练的参数知识回答。

  • 优势:简单直接,响应快速

  • 局限

  • 无法获取训练数据之外的信息

  • 无法处理复杂多步骤任务

  • 缺乏个性化能力

  • 无法自我优化

RAG(检索增强生成)

RAG通过外部数据源增强大模型的知识库,在回答问题前先检索相关信息。

  • 优势

  • 可访问最新或专有信息

  • 支持基本的个性化

  • 提高回答的准确性

  • 局限

  • 仍无法处理复杂多步骤任务

  • 缺乏自适应学习能力

  • 推理能力有限

AI智能体(AI Agents)

智能体是最高级的交互形式,结合了推理、工具使用和自适应能力。

  • 优势

  • 处理复杂多步骤任务

  • 深度个性化

  • 自适应学习

  • 灵活应对各种场景

  • 代价

  • 更高的计算成本

  • 更大的延迟

  • 非确定性输出

1.3 何时使用AI智能体?

并非所有场景都需要智能体。建议在以下情况使用:

复杂且无固定流程的任务 - 所需步骤序列难以预测

可接受延迟 - 用户能够等待智能体完成复杂推理

非确定性可接受 - 相同输入可能产生不同输出

需要个性化 - 长期受益于自适应行为

⚠️ 关键建议:仅在必要时使用智能体,避免增加不必要的复杂性。如果简单提示或RAG能解决问题,就不要使用智能体。

二、AI智能体的四大核心组件

理解智能体的工作原理,需要掌握其四个核心组件。有趣的是,这些组件的运作方式与人类解决问题的思路颇为相似。

2.1 感知(Perception)

感知是智能体获取环境信息的机制,相当于人类的五感。

输入来源

  • 用户交互:直接与智能体对话

  • 事件触发:邮件通知、Slack消息、系统事件等

输入类型

  • 文本:传统且最主流的输入方式

  • 图像:截图、照片、图表等

  • 语音:语音指令、对话录音

  • 视频:视频片段、实时流媒体

过去,文本一直是主要的交互方式,但近几个月来,图像、语音和视频正快速成为智能体感知机制的重要组成部分。

2.2 规划与推理(Planning & Reasoning)

这是智能体的"大脑",负责分析问题并制定解决方案。出人意料的是,这个组件的核心就是大语言模型本身。

无反馈规划

大模型根据对问题的初步理解制定行动计划,不会根据执行结果调整计划。

思维链(Chain of Thought) 是最常见的设计模式:

  • 零样本方式:直接提示"让我们逐步思考这个问题"

  • 少样本方式:提供示例展示如何逐步推理

示例提示:
"请逐步思考以下问题:
如果一辆火车以60公里/小时的速度行驶,需要多久才能行驶180公里?"

带反馈规划

大模型根据工具执行结果和自身推理过程动态调整计划。

React模式(Reasoning + Action)是本次实验要实现的核心模式:

  1. 推理(Thought):生成口头推理过程,思考如何解决问题

  2. 行动(Action):决定调用哪个工具及参数

  3. 观察(Observation):分析工具返回的结果

  4. 迭代:根据观察结果继续推理和行动

  5. 终止:确定得出最终答案后退出循环

React循环示例:
Thought: 我需要查询旧金山的天气
Action: 调用天气API,参数city="San Francisco"
Observation: 温度15°C,多云
Thought: 我已获得所需信息
Answer: 旧金山今天15°C,多云

2.3 工具(Tools)

工具是智能体与外部世界交互的接口,相当于人类的"手"。

工具类型

  • 简单API:天气查询、搜索引擎、货币转换

  • 数据库:向量数据库、关系型数据库

  • 专用模型:图像识别、语音合成、OCR

  • 其他LLM:特定领域的专业模型

工具定义结构

工具通常被定义为函数,并以JSON格式描述:

{
  "name": "get_weather",
  "description": "获取指定城市的当前天气信息",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "城市名称,例如:北京、上海"
      }
    },
    "required": ["city"]
  }
}

重要提示:大语言模型只负责识别何时应该调用函数以及传递什么参数,并不会真正执行函数。实际的函数执行需要智能体系统中的代码来完成。

工具调用流程

  1. 用户提问:"北京今天天气怎么样?"

  2. LLM识别需要调用get_weather工具

  3. LLM提取参数:city="北京"

  4. 智能体系统执行函数调用

  5. 将结果返回给LLM

  6. LLM生成自然语言回复

2.4 记忆(Memory)

记忆使智能体能够存储和回忆过往交互,从而实现学习和个性化。

短期记忆

存储和检索单次对话中的信息,确保对话的连贯性。

  • 实现方式:将对话历史作为上下文传递给LLM

  • 应用场景:多轮对话、上下文理解

  • 示例

用户:"我想订一张去上海的机票"
智能体:"好的,请问您想什么时候出发?"
用户:"下周五" (智能体记得用户想去上海)

长期记忆

存储和调取多次对话或长时间段内的信息。

  • 实现方式:使用数据库持久化存储用户偏好、历史交互

  • 应用场景:用户画像、个性化推荐、学习用户习惯

  • 示例:记住用户的饮食偏好、工作习惯、常用地点等

在本次实验中,我们将实现短期记忆功能,确保智能体能够在一次会话中保持上下文连贯性。

三、智能体工作流程示例

让我们通过一个完整的例子来理解这些组件如何协同工作。

场景:用户询问"旧金山今天天气怎么样?"

步骤1:感知

  • 用户查询通过文本输入到达智能体

步骤2:规划

  • 智能体将查询转发给大语言模型

  • LLM可访问工具列表:天气API、搜索API

  • LLM可访问过往对话记忆

步骤3:推理与决策

  • LLM分析:这是一个天气查询问题

  • LLM决定:天气API最适合获取此信息

  • LLM提取参数:city="San Francisco"

步骤4:工具执行

  • 智能体系统调用天气API

  • API返回:温度15°C,多云,湿度60%

步骤5:观察与决策

  • LLM接收到天气数据

  • LLM判断:信息已足够,无需调用更多工具

  • LLM决定生成最终答案

步骤6:响应

  • LLM生成自然语言回复:"旧金山今天的天气是15°C,多云,湿度60%"

  • 智能体将回复返回给用户

步骤7:记忆更新

  • 将本次交互存储到记忆中

  • 为后续对话提供上下文

四、多模态技术深度解析

4.1 什么是多模态?

多模态(Multimodal)指机器学习或人工智能中,模型能够处理、理解甚至生成多种类型数据的能力。

数据类型

  • 文本(Text)

  • 图像(Image)

  • 音频(Audio)

  • 视频(Video)

  • 表格(Tables)

  • 图表(Charts)

现实应用场景

现实世界中的大量数据都是多模态的:

  • 研究报告:文字说明 + 数据图表 + 示意图

  • 财务报表:文本分析 + 财务表格 + 趋势图

  • 医疗文档:病历文字 + X光片 + 检查报告

  • 技术文档:说明文字 + 架构图 + 代码示例

  • 产品手册:使用说明 + 产品图片 + 规格表

在本次实验中,我们将聚焦于文本 + 图像这两种模态的组合。

4.2 两种多模态模型

多模态嵌入模型(Multimodal Embedding Models)

这类模型的作用是将不同类型的数据转换为统一的向量表示。

  • 输入:文本、图像、音频等

  • 输出:固定维度的向量(嵌入)

  • 用途:使所有数据类型可以统一进行向量搜索与检索

  • 示例:Voyage AI多模态嵌入、OpenAI CLIP

多模态大语言模型(Multimodal LLMs)

这类模型能够接收多种数据类型作为输入,并生成多种格式的输出。

  • 输入:文本 + 图像 + 音频 + 视频

  • 输出:文本、图像、音频等

  • 能力:理解、推理、生成

  • 示例:GPT-4V、Claude 3、Gemini 2.0

多模态智能体的诞生

当我们为多模态大语言模型提供:

  • 使用多模态嵌入模型构建的检索工具

  • 其他专用工具(API、数据库等)

  • 推理和规划能力

  • 记忆机制

我们就得到了一个多模态AI智能体

4.3 传统多模态处理的挑战

在视觉语言模型(VLM)出现之前,处理包含文本和图像的文档面临诸多挑战。

方法一:提取 + 摘要

  1. 使用视觉变换器或目标识别模型识别文档元素

  2. 分别提取文本、图片、表格

  3. 文本进行分块处理

  4. 图片和表格使用LLM生成文本摘要

  5. 将所有内容转为文本形式

  6. 使用文本嵌入模型生成向量

方法二:提取 + 多模态嵌入

  1. 使用视觉模型提取文本与非文本元素

  2. 文本进行分块处理

  3. 使用多模态嵌入模型处理所有元素

  4. 生成统一的向量表示

共同的局限性

问题1:上下文丢失

分块处理会导致严重的上下文丢失问题:

原始文档:
"根据图1所示,2023年第三季度销售额达到500万美元,
相比第二季度增长了25%。"
[图1:季度销售趋势图]

分块后:
块1:"根据图1所示,2023年第三季度销售额"
块2:"达到500万美元,相比第二季度增长了25%。"
块3:[图1:季度销售趋势图]

问题:文字和图表被分离,失去关联

问题2:处理流程复杂

需要使用多个模型和工具:

  • 文档解析工具(Unstructured、LlamaParse)

  • 目标检测模型

  • OCR模型

  • 文本嵌入模型

  • 图像处理模型

问题3:模态鸿沟

这是最严重的问题。基于CLIP架构的多模态嵌入模型存在"模态鸿沟"现象:

问题示例:
查询:"展示销售趋势的图表"

检索结果:
1. 文本:"销售趋势分析报告" (相似度:0.85)
2. 文本:"趋势预测模型说明" (相似度:0.82)
3. 图像:[实际的销售趋势图] (相似度:0.75)

原因:同模态的无关内容比不同模态的相关内容更接近

这是因为CLIP架构使用独立的编码器分别处理文本和图像,导致:

  • 文本与文本之间的相似度天然更高

  • 图像与图像之间的相似度天然更高

  • 即使文本和图像内容相关,相似度也可能较低

4.4 VLM架构的突破性解决方案

视觉语言模型(Vision-Language Model, VLM) 通过统一架构彻底解决了上述问题。

核心创新:统一编码器

VLM使用同一个编码器同时处理文本和视觉数据:

传统CLIP架构:
文本 → 文本编码器 → 文本向量
图像 → 图像编码器 → 图像向量
(两个独立的编码器)

VLM架构:
文本 + 图像 → 统一编码器 → 统一向量表示
(一个编码器处理所有模态)

关键优势

1. 整体表征

文本和视觉特征被统一表征在同一向量空间中,消除了模态鸿沟。

VLM检索结果:
查询:"展示销售趋势的图表"

检索结果:
1. 图像:[销售趋势图] (相似度:0.92)
2. 文本+图:"Q3销售分析" + [图表] (相似度:0.89)
3. 文本:"销售趋势分析报告" (相似度:0.78)

✅ 相关图像排名最高

2. 保留上下文关系

无需分块,整页文档作为一个整体处理:

VLM处理方式:
将整页文档转为截图 → 生成单一嵌入向量

优势:
- 文字与图表的关联被保留
- 页面布局信息被保留
- 上下文完整性被保留

3. 大幅简化流程

传统流程:
PDF → 解析 → 提取元素 → 分类 → OCR → 摘要 → 嵌入

VLM流程:
PDF → 转截图 → 嵌入

✅ 减少了5个处理步骤
✅ 无需多个专用模型
✅ 降低了系统复杂度

4. 提高检索质量

由于保留了完整上下文和消除了模态鸿沟,检索质量显著提升:

  • 更准确地匹配相关内容

  • 更好地理解图文关系

  • 更少的误检和漏检

本次实验使用的VLM技术

  • 嵌入模型:Voyage AI多模态嵌入(基于VLM架构)

  • 大语言模型:Gemini 2.0 Flash实验版(支持多模态输入)

五、构建多模态智能体:完整实现方案

5.1 项目目标

我们要构建的多模态智能体具有两个核心功能:

目标1:文档问答

  • 回答关于包含图表、表格、示意图的复杂文档的问题

  • 理解文本与视觉元素的关联关系

目标2:图表分析

  • 解释和分析图表、示意图

  • 帮助用户理解复杂的视觉信息

实际应用场景

用户:"根据文档,2023年哪个季度的销售额最高?"
智能体:检索相关图表 → 分析数据 → 回答"第四季度,达到650万美元"

用户:"这个架构图中的数据流向是怎样的?"
智能体:检索架构图 → 分析图示 → 解释数据流向

5.2 技术挑战

核心挑战:如何准备多模态文档语料库以支持高质量检索?

对于纯文本文档,RAG的标准流程是:

  1. 文档分块

  2. 生成嵌入向量

  3. 存储到向量数据库

  4. 检索相关文本块

  5. 作为上下文传给LLM

但对于包含图像和表格的文档,这个流程会遇到:

  • 分块导致图文分离

  • 上下文关系丢失

  • 检索质量下降

我们的解决方案:利用VLM架构,将整页文档转为截图进行处理。

5.3 数据预处理流程

步骤1:文档转截图

# 伪代码示例
for page in pdf_document:
    screenshot = page.to_image()
    save_screenshot(screenshot, f"page_{page_number}.png")

为什么要转截图?

  • 保留完整的页面布局

  • 保持文字与图表的关联

  • 适配VLM的输入格式

步骤2:存储截图

将截图存储到对象存储服务:

# 存储选项
- 本地文件系统(开发测试)
- Amazon S3(生产环境)
- Google Cloud Storage
- Azure Blob Storage

步骤3:生成多模态嵌入

使用Voyage AI的多模态嵌入模型:

# 伪代码
for screenshot in screenshots:
    embedding = voyage_ai.embed_image(screenshot)
    embeddings.append(embedding)

步骤4:存储到向量数据库

将嵌入向量和元数据存储到MongoDB:

# 文档结构
{
    "_id": "doc_001_page_1",
    "embedding": [0.123, 0.456, ...],  # 1024维向量
    "image_path": "s3://bucket/doc_001_page_1.png",
    "page_number": 1,
    "document_id": "doc_001",
    "metadata": {
        "title": "2023年度财务报告",
        "date": "2024-01-15"
    }
}

完整预处理流程图

PDF文档
  ↓
转换为截图(每页一张)
  ↓
上传到对象存储(S3/GCS)
  ↓
生成多模态嵌入向量
  ↓
存储到MongoDB向量数据库
  ├─ 嵌入向量
  ├─ 图像路径
  └─ 元数据

5.4 智能体工作流程

现在让我们看看智能体如何响应用户查询。

完整工作流程

步骤1:接收查询

user_query = "2023年第三季度的销售额是多少?"
session_id = "user_123_session_456"

步骤2:查询历史记忆

# 从数据库获取该会话的历史记录
conversation_history = db.get_conversation_history(session_id)

# 构建完整上下文
context = {
    "current_query": user_query,
    "history": conversation_history
}

步骤3:LLM推理与决策(React循环)

# 第一轮推理
llm_response = llm.chat(context, tools=available_tools)

# LLM的思考过程
{
    "thought": "用户询问特定季度的销售额,我需要检索相关文档",
    "action": "vector_search",
    "action_input": {
        "query": "2023年第三季度销售额",
        "top_k": 3
    }
}

步骤4:执行向量搜索

# 生成查询嵌入
query_embedding = voyage_ai.embed_text(user_query)

# 在MongoDB中执行向量搜索
search_results = mongodb.vector_search(
    embedding=query_embedding,
    top_k=3,
    index="multimodal_embeddings"
)

# 搜索结果
[
    {
        "score": 0.92,
        "image_path": "s3://bucket/report_2023_page_15.png",
        "page_number": 15
    },
    {
        "score": 0.87,
        "image_path": "s3://bucket/report_2023_page_16.png",
        "page_number": 16
    },
    {
        "score": 0.81,
        "image_path": "s3://bucket/report_2023_page_8.png",
        "page_number": 8
    }
]

步骤5:获取实际图像

# 从对象存储下载图像
images = []
for result in search_results:
    image = s3.download(result["image_path"])
    images.append(image)

步骤6:LLM观察与最终推理

# 将图像和查询一起发送给多模态LLM
final_response = multimodal_llm.chat(
    query=user_query,
    images=images,
    history=conversation_history
)

# LLM的推理过程
{
    "observation": "我在第15页的图表中找到了Q3销售数据",
    "thought": "图表显示Q3销售额为500万美元",
    "answer": "根据2023年度报告第15页的图表,
               第三季度的销售额为500万美元,
               相比第二季度增长了25%。"
}

步骤7:更新记忆

# 保存本次交互到数据库
db.save_conversation(
    session_id=session_id,
    query=user_query,
    response=final_response,
    retrieved_images=search_results,
    timestamp=datetime.now()
)

步骤8:返回结果

return {
    "answer": final_response["answer"],
    "sources": [
        {"page": 15, "image": "report_2023_page_15.png"}
    ]
}

5.5 记忆管理机制详解

记忆管理是实现连贯对话的关键。

数据库结构设计

# conversations集合
{
    "_id": ObjectId("..."),
    "session_id": "user_123_session_456",
    "timestamp": ISODate("2024-01-15T10:30:00Z"),
    "query": "2023年第三季度的销售额是多少?",
    "response": "根据报告,Q3销售额为500万美元...",
    "retrieved_images": [
        {
            "image_path": "s3://bucket/report_2023_page_15.png",
            "score": 0.92
        }
    ],
    "metadata": {
        "model": "gemini-2.0-flash",
        "tokens_used": 1250
    }
}

记忆检索策略

def get_conversation_context(session_id, max_turns=5):
    """
    获取最近N轮对话作为上下文
    """
    history = db.conversations.find(
        {"session_id": session_id}
    ).sort("timestamp", -1).limit(max_turns)
    
    context = []
    for turn in reversed(list(history)):
        context.append({
            "role": "user",
            "content": turn["query"]
        })
        context.append({
            "role": "assistant",
            "content": turn["response"]
        })
    
    return context

上下文窗口管理

# 当对话历史过长时,使用摘要策略
if len(conversation_history) > MAX_CONTEXT_LENGTH:
    # 保留最近的对话
    recent_history = conversation_history[-5:]
    
    # 对早期对话生成摘要
    early_summary = llm.summarize(conversation_history[:-5])
    
    # 组合上下文
    final_context = [early_summary] + recent_history

多会话管理

# 用户可以有多个并行会话
sessions = {
    "user_123_session_456": "讨论2023年财报",
    "user_123_session_789": "分析市场趋势",
    "user_123_session_012": "产品规划讨论"
}

# 每个会话独立维护记忆
for session_id in sessions:
    context = get_conversation_context(session_id)

5.6 完整系统架构图

┌─────────────────────────────────────────────────────────────┐
│                          用户界面                            │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          ↓
┌─────────────────────────────────────────────────────────────┐
│                      智能体协调器                            │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │  查询路由    │  │  记忆管理    │  │  工具调度    │     │
│  └──────────────┘  └──────────────┘  └──────────────┘     │
└─────────────────────────┬───────────────────────────────────┘
                          │
          ┌───────────────┼───────────────┐
          ↓               ↓               ↓
┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│ 多模态LLM    │  │ 向量搜索工具  │  │ 其他工具     │
│ (Gemini 2.0) │  │              │  │              │
└──────────────┘  └──────┬───────┘  └──────────────┘
                          │
                          ↓
                  ┌──────────────┐
                  │   MongoDB    │
                  │  向量数据库   │
                  │              │
                  │ ┌──────────┐ │
                  │ │ 嵌入向量 │ │
                  │ │ 元数据   │ │
                  │ │ 对话历史 │ │
                  │ └──────────┘ │
                  └──────────────┘
                          │
                          ↓
                  ┌──────────────┐
                  │  对象存储    │
                  │   (S3/GCS)   │
                  │              │
                  │ ┌──────────┐ │
                  │ │ 文档截图 │ │
                  │ └──────────┘ │
                  └──────────────┘

六、技术栈选择与理由

6.1 核心技术组件

向量数据库:MongoDB Atlas

选择理由:

  • ✅ 原生支持向量搜索

  • ✅ 灵活的文档模型(存储嵌入、元数据、对话历史)

  • ✅ 强大的查询能力(混合搜索、过滤)

  • ✅ 易于扩展和部署

# MongoDB向量搜索示例
pipeline = [
    {
        "$vectorSearch": {
            "index": "multimodal_index",
            "path": "embedding",
            "queryVector": query_embedding,
            "numCandidates": 100,
            "limit": 5
        }
    },
    {
        "$project": {
            "image_path": 1,
            "page_number": 1,
            "score": {"$meta": "vectorSearchScore"}
        }
    }
]

多模态嵌入:Voyage AI

选择理由:

  • ✅ 基于VLM架构,消除模态鸿沟

  • ✅ 统一处理文本和图像

  • ✅ 高质量的向量表示

  • ✅ 简单的API接口

# Voyage AI使用示例
import voyageai

client = voyageai.Client(api_key="your_api_key")

# 嵌入图像
image_embedding = client.multimodal_embed(
    inputs=["path/to/image.png"],
    model="voyage-multimodal-3"
)

# 嵌入文本
text_embedding = client.multimodal_embed(
    inputs=["查询文本"],
    model="voyage-multimodal-3"
)

大语言模型:Gemini 2.0 Flash

选择理由:

  • ✅ 原生支持多模态输入(文本 + 图像)

  • ✅ 强大的推理能力

  • ✅ 支持函数调用(工具使用)

  • ✅ 快速响应(Flash版本)

  • ✅ 成本效益高

# Gemini多模态调用示例
import google.generativeai as genai

model = genai.GenerativeModel('gemini-2.0-flash-exp')

response = model.generate_content([
    "请分析这个图表中的销售趋势",
    image_data
])

6.2 为什么选择VLM架构?

对比传统方案

特性

传统方案

VLM方案

处理流程

复杂(5+步骤)

简单(2步骤)

所需模型

多个专用模型

单一VLM模型

上下文保留

容易丢失

完整保留

模态鸿沟

存在

不存在

检索质量

中等

维护成本

实际效果对比

测试查询:"展示2023年销售趋势的图表在哪一页?"

传统CLIP方案检索结果:
1. 文本:"2023年销售分析报告" (0.85)
2. 文本:"销售趋势预测模型" (0.82)
3. 图像:[实际的销售趋势图] (0.75) ❌ 排名第三

VLM方案检索结果:
1. 图像:[实际的销售趋势图] (0.92) ✅ 排名第一
2. 混合:文本+图表页面 (0.89)
3. 文本:"2023年销售分析报告" (0.78)

七、实践指南

7.1 环境准备

前置要求

  • Python 3.8+

  • MongoDB Atlas账号

  • Voyage AI API密钥

  • Google AI API密钥(Gemini)

安装依赖

pip install pymongo voyageai google-generativeai pillow pdf2image

配置环境变量

export MONGODB_URI="your_mongodb_connection_string"
export VOYAGE_API_KEY="your_voyage_api_key"
export GOOGLE_API_KEY="your_google_api_key"

7.2 学习路径

GitHub仓库:bit.ly/multimodal-agent-workshop

两种学习方式

方式1:lab.ipynb(推荐初学者)

  • 填空式编码练习

  • 内联文档和提示

  • 逐步理解每个组件

  • 适合深入学习

方式2:solutions.ipynb(快速上手)

  • 完整可运行代码

  • 直接查看实现

  • 快速测试效果

  • 适合经验丰富的开发者

7.3 核心代码实现

步骤1:文档预处理

from pdf2image import convert_from_path
import voyageai

def preprocess_document(pdf_path):
    """将PDF转为截图并生成嵌入"""
    # 转换为图像
    images = convert_from_path(pdf_path)
    
    # 初始化Voyage AI客户端
    vo = voyageai.Client(api_key=VOYAGE_API_KEY)
    
    documents = []
    for i, image in enumerate(images):
        # 保存截图
        image_path = f"screenshots/page_{i+1}.png"
        image.save(image_path)
        
        # 生成嵌入
        embedding = vo.multimodal_embed(
            inputs=[image_path],
            model="voyage-multimodal-3"
        )
        
        documents.append({
            "page_number": i + 1,
            "image_path": image_path,
            "embedding": embedding.embeddings[0]
        })
    
    return documents

步骤2:存储到MongoDB

from pymongo import MongoClient

def store_documents(documents):
    """存储文档到MongoDB"""
    client = MongoClient(MONGODB_URI)
    db = client["multimodal_agent"]
    collection = db["documents"]
    
    # 插入文档
    collection.insert_many(documents)
    
    # 创建向量搜索索引
    collection.create_search_index({
        "definition": {
            "fields": [{
                "type": "vector",
                "path": "embedding",
                "numDimensions": 1024,
                "similarity": "cosine"
            }]
        },
        "name": "vector_index"
    })

步骤3:实现向量搜索工具

def vector_search_tool(query, top_k=3):
    """向量搜索工具"""
    # 生成查询嵌入
    vo = voyageai.Client(api_key=VOYAGE_API_KEY)
    query_embedding = vo.multimodal_embed(
        inputs=[query],
        model="voyage-multimodal-3"
    )
    
    # 执行向量搜索
    client = MongoClient(MONGODB_URI)
    collection = client["multimodal_agent"]["documents"]
    
    pipeline = [
        {
            "$vectorSearch": {
                "index": "vector_index",
                "path": "embedding",
                "queryVector": query_embedding.embeddings[0],
                "numCandidates": 100,
                "limit": top_k
            }
        },
        {
            "$project": {
                "image_path": 1,
                "page_number": 1,
                "score": {"$meta": "vectorSearchScore"}
            }
        }
    ]
    
    results = list(collection.aggregate(pipeline))
    return results

步骤4:实现React智能体循环

import google.generativeai as genai
from PIL import Image

def agent_loop(user_query, session_id, max_iterations=5):
    """React智能体主循环"""
    # 初始化Gemini
    genai.configure(api_key=GOOGLE_API_KEY)
    model = genai.GenerativeModel('gemini-2.0-flash-exp')
    
    # 获取对话历史
    history = get_conversation_history(session_id)
    
    # 定义工具
    tools = [
        {
            "name": "vector_search",
            "description": "搜索相关文档页面",
            "parameters": {
                "query": "搜索查询",
                "top_k": "返回结果数量"
            }
        }
    ]
    
    # React循环
    for iteration in range(max_iterations):
        # 构建提示
        prompt = f"""
        你是一个多模态AI助手。使用以下工具回答用户问题:
        
        工具:{tools}
        
        对话历史:{history}
        
        用户问题:{user_query}
        
        请按照以下格式思考和行动:
        Thought: [你的推理过程]
        Action: [工具名称]
        Action Input: [工具参数]
        
        如果你已经有足够信息回答,请输出:
        Final Answer: [你的答案]
        """
        
        # LLM推理
        response = model.generate_content(prompt)
        
        # 解析响应
        if "Final Answer:" in response.text:
            # 保存对话
            save_conversation(session_id, user_query, response.text)
            return response.text
        
        # 提取行动
        action = parse_action(response.text)
        
        # 执行工具
        if action["name"] == "vector_search":
            search_results = vector_search_tool(
                action["input"]["query"],
                action["input"].get("top_k", 3)
            )
            
            # 加载图像
            images = []
            for result in search_results:
                img = Image.open(result["image_path"])
                images.append(img)
            
            # 将图像和查询发送给LLM
            final_prompt = f"""
            基于以下图像回答问题:{user_query}
            
            图像来自文档的第{[r['page_number'] for r in search_results]}页
            """
            
            final_response = model.generate_content([final_prompt] + images)
            
            # 保存对话
            save_conversation(session_id, user_query, final_response.text)
            return final_response.text
    
    return "抱歉,我无法在限定步骤内完成任务。"

步骤5:记忆管理

def get_conversation_history(session_id, max_turns=5):
    """获取对话历史"""
    client = MongoClient(MONGODB_URI)
    collection = client["multimodal_agent"]["conversations"]
    
    history = list(collection.find(
        {"session_id": session_id}
    ).sort("timestamp", -1).limit(max_turns))
    
    return [
        {"role": h["role"], "content": h["content"]}
        for h in reversed(history)
    ]

def save_conversation(session_id, query, response):
    """保存对话"""
    client = MongoClient(MONGODB_URI)
    collection = client["multimodal_agent"]["conversations"]
    
    collection.insert_many([
        {
            "session_id": session_id,
            "role": "user",
            "content": query,
            "timestamp": datetime.now()
        },
        {
            "session_id": session_id,
            "role": "assistant",
            "content": response,
            "timestamp": datetime.now()
        }
    ])

7.4 测试智能体

# 测试示例
if __name__ == "__main__":
    # 1. 预处理文档
    print("预处理文档...")
    documents = preprocess_document("annual_report_2023.pdf")
    store_documents(documents)
    
    # 2. 创建会话
    session_id = "test_session_001"
    
    # 3. 测试查询
    queries = [
        "2023年第三季度的销售额是多少?",
        "哪个季度的销售额最高?",
        "请解释第15页的趋势图。"
    ]
    
    for query in queries:
        print(f"\n用户:{query}")
        response = agent_loop(query, session_id)
        print(f"智能体:{response}")

八、最佳实践与优化建议

8.1 文档处理优化

分辨率选择

# 高分辨率保留更多细节,但增加存储和计算成本
images = convert_from_path(
    pdf_path,
    dpi=200  # 推荐值:150-300
)

批量处理

# 批量生成嵌入以提高效率
def batch_embed(images, batch_size=10):
    embeddings = []
    for i in range(0, len(images), batch_size):
        batch = images[i:i+batch_size]
        batch_embeddings = vo.multimodal_embed(
            inputs=batch,
            model="voyage-multimodal-3"
        )
        embeddings.extend(batch_embeddings.embeddings)
    return embeddings

8.2 检索质量优化

混合搜索

# 结合向量搜索和关键词搜索
def hybrid_search(query, top_k=5):
    # 向量搜索
    vector_results = vector_search_tool(query, top_k=10)
    
    # 关键词搜索
    keyword_results = collection.find(
        {"$text": {"$search": query}}
    ).limit(10)
    
    # 合并和重排序
    combined = merge_and_rerank(vector_results, keyword_results)
    return combined[:top_k]

查询扩展

# 使用LLM扩展查询
def expand_query(original_query):
    prompt = f"""
    将以下查询扩展为3个相关的搜索查询:
    原始查询:{original_query}
    
    输出格式:
    1. [扩展查询1]
    2. [扩展查询2]
    3. [扩展查询3]
    """
    expanded = llm.generate(prompt)
    return parse_expanded_queries(expanded)

8.3 成本控制

缓存策略

# 缓存常见查询的结果
from functools import lru_cache

@lru_cache(maxsize=100)
def cached_vector_search(query, top_k):
    return vector_search_tool(query, top_k)

智能路由

# 根据查询复杂度选择模型
def route_query(query):
    if is_simple_query(query):
        return "gemini-1.5-flash"  # 更便宜
    else:
        return "gemini-2.0-flash-exp"  # 更强大

8.4 错误处理

def robust_agent_loop(user_query, session_id):
    try:
        return agent_loop(user_query, session_id)
    except Exception as e:
        logger.error(f"智能体错误: {e}")
        
        # 降级策略
        return fallback_response(user_query)

def fallback_response(query):
    """简单的降级响应"""
    return "抱歉,我遇到了一些技术问题。请稍后再试或换一种方式提问。"

九、进阶话题

9.1 多智能体协作

# 专业化智能体
class DocumentAnalystAgent:
    """专注于文档分析"""
    pass

class ChartExpertAgent:
    """专注于图表解释"""
    pass

class CoordinatorAgent:
    """协调其他智能体"""
    def route_query(self, query):
        if "图表" in query or "趋势" in query:
            return ChartExpertAgent()
        else:
            return DocumentAnalystAgent()

9.2 流式响应

def streaming_agent_response(query, session_id):
    """流式返回智能体响应"""
    for chunk in model.generate_content_stream(query):
        yield chunk.text

9.3 评估与监控

# 记录智能体性能指标
def log_metrics(session_id, query, response, metrics):
    collection.insert_one({
        "session_id": session_id,
        "query": query,
        "response": response,
        "latency": metrics["latency"],
        "tokens_used": metrics["tokens"],
        "tools_called": metrics["tools"],
        "timestamp": datetime.now()
    })

十、常见问题与解答

Q1:为什么不直接使用传统的文档分块方法?

A:传统分块会导致:

  • 图文关联丢失

  • 上下文不完整

  • 模态鸿沟问题

VLM架构通过整页处理完美解决这些问题。

Q2:截图方式会不会导致存储成本过高?

A:确实会增加存储成本,但:

  • 可以使用压缩格式(JPEG,质量80-90%)

  • 对象存储成本已经很低(S3约$0.023/GB/月)

  • 检索质量提升带来的价值远超存储成本

Q3:如何处理跨页的内容?

A:可以采用重叠策略:

# 生成重叠的页面截图
for i in range(len(pages) - 1):
    combined = merge_pages(pages[i], pages[i+1])
    save_screenshot(combined, f"pages_{i}_{i+1}.png")

Q4:智能体的响应时间如何优化?

A:

  • 使用异步处理

  • 实现结果缓存

  • 选择更快的模型(Flash版本)

  • 优化检索的top_k参数

Q5:如何确保智能体的回答准确性?

A:

  • 要求智能体引用来源页码

  • 实现置信度评分

  • 添加人工审核环节

  • 持续评估和优化提示词

十一、总结与展望

11.1 核心要点回顾

AI智能体的本质

  • 感知 + 规划 + 工具 + 记忆

  • React循环:推理 → 行动 → 观察 → 迭代

多模态技术突破

  • VLM架构消除模态鸿沟

  • 统一编码器保留完整上下文

  • 大幅简化处理流程

实现关键

  • 文档转截图保留布局

  • 多模态嵌入实现统一检索

  • 多模态LLM提供推理能力

  • 记忆机制确保对话连贯

11.2 技术演进方向

短期(2025年)

  • 更强大的多模态模型

  • 更低的推理成本

  • 更快的响应速度

  • 更好的工具生态

中期(2026-2027年)

  • 原生视频理解能力

  • 实时多模态交互

  • 更复杂的推理能力

  • 自主学习和优化

长期愿景

  • 通用人工智能(AGI)的重要一步

  • 真正的人机协作

  • 跨领域知识整合

11.3 学习资源

官方文档

  • MongoDB Vector Search: docs.mongodb.com/atlas/vector-search

  • Voyage AI: docs.voyageai.com

  • Gemini API: ai.google.dev

进阶阅读

  • "ReAct: Synergizing Reasoning and Acting in Language Models"

  • "Multimodal Learning with Transformers: A Survey"

  • "Building LLM-Powered Applications"

社区资源

  • GitHub仓库:bit.ly/multimodal-agent-workshop

  • MongoDB开发者社区

  • AI Agent研究论文

结语

构建多模态AI智能体是一个激动人心的旅程。通过本文,你已经掌握了:

✅ AI智能体的核心概念和组件
✅ 多模态技术的原理和优势
✅ VLM架构如何解决传统难题
✅ 从零构建智能体的完整流程
✅ 实践中的最佳实践和优化技巧

现在,是时候动手实践了!打开GitHub仓库,跟随lab.ipynb的指引,构建你自己的多模态AI智能体。记住,最好的学习方式就是实践。

2025年是智能体之年,让我们一起探索AI的无限可能!


#AI编程 #VibeCoding #智能体 #ClaudeCode #独立开发者 #AI创业 #一人公司 #程序员 #软件工程师 #软件工程


Work Less, Earn More, Enjoy Life.