
配套视频: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)是本次实验要实现的核心模式:
推理(Thought):生成口头推理过程,思考如何解决问题
行动(Action):决定调用哪个工具及参数
观察(Observation):分析工具返回的结果
迭代:根据观察结果继续推理和行动
终止:确定得出最终答案后退出循环
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"]
}
}重要提示:大语言模型只负责识别何时应该调用函数以及传递什么参数,并不会真正执行函数。实际的函数执行需要智能体系统中的代码来完成。
工具调用流程:
用户提问:"北京今天天气怎么样?"
LLM识别需要调用
get_weather工具LLM提取参数:
city="北京"智能体系统执行函数调用
将结果返回给LLM
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)出现之前,处理包含文本和图像的文档面临诸多挑战。
方法一:提取 + 摘要
使用视觉变换器或目标识别模型识别文档元素
分别提取文本、图片、表格
文本进行分块处理
图片和表格使用LLM生成文本摘要
将所有内容转为文本形式
使用文本嵌入模型生成向量
方法二:提取 + 多模态嵌入
使用视觉模型提取文本与非文本元素
文本进行分块处理
使用多模态嵌入模型处理所有元素
生成统一的向量表示
共同的局限性:
问题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的标准流程是:
文档分块
生成嵌入向量
存储到向量数据库
检索相关文本块
作为上下文传给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架构?
对比传统方案:
实际效果对比:
测试查询:"展示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 embeddings8.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.text9.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创业 #一人公司 #程序员 #软件工程师 #软件工程