我上周遇到了一些问题。
我盯着 MongoDB Atlas 集群中的 sample_airbnb 数据集——包含数十个城市的 5500 多个房源——我想要一种搜索方式,但又不想用 2015 年的那些老套方法。不要下拉菜单,不要筛选复选框,也不要 WHERE property_type = 'Apartment' AND bedrooms >= 2。
我想输入: “帮我在波尔图找个风景优美、环境舒适的住处。” ——并且得到真正理解我意思的结果。
于是我用 Laravel 框架把它搭建出来了。花了几个小时。
这是一个关于……的故事 Airbnb竞技场 最终,RAG 开发出了一款聊天代理,它使用 Voyage AI 嵌入、MongoDB Atlas Vector Search 和 Google Gemini。
但最棒的是:我不需要自己编写任何复杂的 API 接口或工具调用循环。我使用了新的 Laravel AI SDK (laravel/ai)。
它将原本数百行的“提示工程”和 HTTP 客户端代码转换为简洁的、面向对象的 PHP 类。
我们来建造它。
我们正在构建什么
以下是完整的流程:
用户 → 聊天界面 → 聊天控制器 → AirbnbAgent(Laravel AI 代理)↓ListingSearchTool → Voyage AI(通过 SDK)↓MongoDB Atlas 向量搜索↓匹配列表 → 双子座 → 回复
用户输入问题。AirbnbAgent(由 Gemini 提供技术支持)读取问题。如果需要搜索,它会调用 ListingSearchTool。该工具使用 SDK 的 Embeddings API,通过 Voyage AI 对查询进行向量编码,然后对 MongoDB 执行 $vectorSearch,并将结果返回。Gemini 随后生成自然语言答案。
关键部分:
- Laravel AI SDK :将我们的代码与 Gemini 和 Voyage AI 连接起来的粘合剂。
- 人工智能旅行 : 为列表和搜索查询生成向量嵌入。
- MongoDB Atlas :存储数据并以原生方式运行 $vectorSearch。
- Google Gemini :决定调用哪些工具的推理引擎。
为什么选择这种堆栈?
MongoDB Atlas 它身兼两职,既是文档存储(支持灵活的 JSON 数据),又是矢量数据库。两个系统之间无需同步数据。一个集群,一个集合。
人工智能旅行 提供“检索优化”嵌入。它区分了 文件 (你储存的东西) 查询 (用户提出的问题),与通用模型相比,显著提高了搜索相关性。
人们很容易认为“任何最先进的嵌入模型都可以”,但 MongoDB Atlas 上的第四次航行 它为生产 RAG 和代理商提供了一些非常实用的优势。
共享嵌入空间和非对称检索
整个 Voyage‑4 系列—— travel-4-large , 航程-4 , voyage-4-lite , 和 voyage-4-nano — 分享一个 单一嵌入空间 这四个模型都能生成兼容的嵌入,因此您可以混合搭配模型来处理文档和查询,而无需重新向量化语料库。
这意味着你可以:
- 嵌入您的 Airbnb房源 一次 travel-4-large 为了获得最佳检索质量。
- 使用 voyage-4-lite (甚至) voyage-4-nano ) 为了 查询嵌入 保持每次请求的延迟和成本较低。
- 稍后通过切换到升级查询准确性 航程-4 或者 travel-4-large — 不触及已存储的向量 。
这种“非对称检索”模式(文档采用大型模型,查询采用较轻的模型)非常适合 Atlas 向量搜索:文档很少更改,查询却一直在运行。
2. 灵活的维度和更低的向量成本
Voyage-4 型号支持 多嵌入维度 (256–2048)通过 Matryoshka 学习,可以量化(例如,8 位、二进制),质量损失最小。
在 Atlas 中,这直接翻译为:
- 较小的指数大小 (维度较少,精度较低)。
- 更低的内存和存储占用空间 用于您的向量索引。
- 能够拨出正确的 准确性与成本/延迟 每项工作负载都需要权衡取舍。
例如
sample_airbnb
您可以在 512–1024 暗度范围内轻松保持良好的成像质量。
3. 统一的 Atlas API、计费和运营
通过 MongoDB Atlas 上的嵌入和重排序 API Voyage AI 模型以原生、无服务器 API 的形式在 Atlas 生态系统中公开,而不是作为“另一个需要您自行集成和监控的外部服务”。
具体来说,你会得到:
- 统一计费 航程使用费将通过您现有的账户计费。 Atlas计费配置文件 采用相同的支付方式和组织级限额。
- 综合监测和治理 :一个用于审计、限制(TPM/RPM)和访问控制的地方,以及您的数据库和 Vector Search 集群。
- 慷慨的免费套餐 :在预览期间,您可以获得数亿个免费代币,访问最新的 Voyage 模型(包括 Voyage‑4),以便在优化之前进行积极的原型设计。
这与您文章中提到的 Laravel AI SDK 的故事非常契合:Atlas 变成了 中央“AI数据平面” (存储+向量搜索+嵌入+重排序),而 拉维尔 专注于编排和用户体验。
Laravel AI SDK 这是其中一项颠覆性的创新。您无需手动为工具构建 JSON 模式并解析 LLM 响应,只需定义一个带有 description() 方法的 PHP 类即可。SDK 会处理其余部分——序列化参数、调用该方法并将结果反馈给模型。
奠定基础
重新开始:
作曲家
创建项目
laravel/laravel
爱彼迎竞技场光盘
爱彼迎竞技场
安装 MongoDB 驱动程序和 Laravel AI SDK:
佩克尔
安装
mongodb作曲家
要求
mongodb/laravel-mongodb:5.x-dev
laravel/ai
发布 Laravel AI 配置:
php
工匠
你:安装
您还需要一个 MongoDB Atlas 集群。 示例数据集 已加载(这将为您提供 sample_airbnb 数据库)。
配置您的 .env 文件:
MONGODB_URI
=
mongodb+srv://user:pass@cluster.mongodb.net/?retryWrites=true&w=majorityMONGODB_DATABASE
=
示例爱彼迎GEMINI_API_KEY
=
你的 Gemini API 密钥VOYAGEAI_API_KEY
=
你的旅程人工智能密钥
并更新 config/ai.php 文件以设置默认值:
// config/ai.php'默认'
=>
双子座
,'default_for_embeddings'
=>
我旅行了
,
列表模型和嵌入
首先,我们需要一个能够代表我们Airbnb房源的模型。
命名空间
应用程序\模型
;使用
MongoDB\Laravel\Eloquent\模型
;班级
列表
延伸
模型{
受保护
$连接
=
'mongodb'
;
受保护
$表
=
'listingsAndReviews'
;
受保护
$可填充
=
[
'姓名'
,
'概括'
,
'描述'
,
'property_type'
,
'房间类型'
,
“容纳”
,
卧室
,
床
,
'价格'
,
“便利设施”
,
'地址'
,
'review_scores'
,
“嵌入”
,
// <-- 向量存储在此处];
民众
功能
嵌入文本
()
:
细绳{市场
=
$this
->
地址[
'市场'
]
??
$this
->
地址[
'国家'
]
??
“”
;
返回
内爆
(
'。'
,
数组过滤器
([
$this
->
姓名,
$this
->
概括,
$this
->
属性类型
?
属性类型:{
$this
->
属性类型
}”
:
无效的
,市场
?
“地点: {
市场
}”
:
无效的
,]));}}
现在,我们来生成词嵌入。我们将创建一个简单的服务,它封装了 Laravel AI SDK 的词嵌入外观。这样可以保持代码的简洁性和可测试性。
命名空间
应用/服务
;使用
Laravel\Ai\Embeddings
;班级
嵌入服务{
// Voyage-3 使用 1024 个维度
私人的
整数
尺寸
=
1024
;
民众
功能
嵌入多个
(
大批
$texts)
:
大批{
SDK 会为我们处理批处理和 API 调用。$响应
=
嵌入
::
为了
(文本)
->
方面
(
$this
->
方面)
->
产生
(
提供者
:
我旅行了
(英文):
返回
$响应
->
嵌入;}
民众
功能
嵌入查询
(
细绳
$query)
:
大批{$响应
=
嵌入
::
为了
([$query])
->
方面
(
$this
->
方面)
->
产生
(
提供者
:
我旅行了
(英文):
返回
$响应
->
嵌入[
0
];}}
注意这有多简单吗?只需使用 `Embeddings::for($texts)->generate()` 即可。无需 cURL 请求,也无需 JSON 解码。SDK 已经将提供商之间的差异抽象化了。
创建搜索工具
过去,你需要编写一个庞大的 JSON 模式来向 LLM 描述你的功能。而使用 Laravel AI,你只需要编写一个实现了 Tool 接口的类即可。
这是我们的房源搜索工具。经纪人用它来查找房源。
命名空间
应用程序\工具
;使用
应用程序\模型\列表
;使用
应用程序\服务\嵌入服务
;使用
照亮\合同\JsonSchema\JsonSchema
;使用
照明\支持\立面\日志
;使用
Laravel\Ai\Contracts\Tool
;使用
Laravel\Ai\Tools\Request
;使用
可串接
;/*** ListingSearchTool — 对 Airbnb 房源执行语义向量搜索。** 实现了 Laravel AI SDK 工具接口,以便代理可以* 当用户询问房源信息时自动调用。** 使用 mongodb/laravel-mongodb Eloquent vectorSearch() 方法查找* 与用户自然语言查询语义相似的列表。* 该查询通过 Voyage AI(通过 SDK)嵌入,然后进行匹配。* 与存储在 MongoDB Atlas 中的预先计算的列表嵌入进行比较。*/班级
ListingSearchTool
实现
工具{
/**
@我们的
大批
来自上次搜索的结构化列表数据*/
私人的
大批
$lastResults
=
[];
民众
功能
__构造
(
私人的
嵌入服务
嵌入服务){}
/*** 描述此工具的功能——代理会读取此信息以决定何时使用它。*/
民众
功能
描述
()
:
可串接
|
细绳{
返回
使用语义向量搜索查找 Airbnb 房源。
。
“使用此功能查找与自然语言描述相匹配的房产”
。
例如“巴塞罗那带游泳池的舒适公寓”或“海滩附近适合家庭入住的房子”。
;}
/*** 定义代理可以传递给此工具的参数。*/
民众
功能
模式
(
JsonSchema
$schema)
:
大批{
返回
[
'询问'
=>
$schema
->
细绳
()
->
描述
(
“描述所需房源的自然语言搜索查询”
)
->
必需的
(),
'限制'
=>
$schema
->
整数
()
->
描述
(
要返回的最大结果数(默认值:5)
),];}
/*** 获取上次搜索执行的结构化列表数据。*/
民众
功能
获取最新结果
()
:
大批{
返回
$this
->
最新结果;}
/*** 当代理调用此工具时执行搜索。*/
民众
功能
处理
(
要求
$请求)
:
可串接
|
细绳{$查询
=
(
细绳
)$request
->
细绳
(
'询问'
(英文):$limit
=
$请求
->
整数
(
'限制'
,
5
)
?:
5
;
日志
::
信息
(
'房源搜索工具:正在搜索'
,[
'询问'
=>
$query,
'限制'
=>
$limit]);
尝试
{
// 步骤 1:使用 Voyage AI(通过 SDK)生成查询嵌入$queryEmbedding
=
$this
->
嵌入服务
->
嵌入查询
($查询)
// 步骤 2:使用 mongodb/laravel-mongodb 中的 Eloquent vectorSearch() 方法
// 将结果一次性物化到集合中,避免两次游标迭代。$结果
=
列表
::
向量搜索
(
指数
:
'vector_index'
,
小路
:
“嵌入”
,
查询向量
: $queryEmbedding,
候选人数
:$limit
*
10
,
限制
:$limit,(英文):
// 步骤 3:单次构建 — 同时构建结构化的前端数据和代理文本代理线路
=
[];
$this
->
最新结果
=
[];
foreach
(结果)
作为
索引
=>
$listing) {$地址
=
(
大批
)$列表
->
地址
??
[]);评分
=
(
大批
)$列表
->
评分
??
[]);图片
=
(
大批
)$列表
->
图片
??
[]);$价格
=
$listing
->
价格;
如果
(
对象
($价格)) {$价格
=
(
细绳
)$price;}得分
=
圆形的
(($listing
->
向量搜索得分
??
0
)
*
100
,
1
(英文):位置
=
$address[
'市场'
]
??
$address[
'国家'
]
??
'未知'
;评分
=
$reviewScores[
'review_scores_rating'
]
??
不适用
;摘要
=
$listing
->
概括
??
“”
;
// 前端的结构化数据
$this
->
lastResults[]
=
[
'ID'
=>
(
细绳
)$listing
->
_ID,
'姓名'
=>
$listing
->
姓名
??
“未命名”
,
'概括'
=>
摘要,
'property_type'
=>
$listing
->
属性类型
??
'财产'
,
'房间类型'
=>
$listing
->
房间类型
??
“”
,
“容纳”
=>
$listing
->
容纳
??
无效的
,
卧室
=>
$listing
->
卧室
??
无效的
,
床
=>
$listing
->
床
??
无效的
,
“浴室”
=>
伊塞特
(价格)
->
浴室)
?
(
细绳
)$listing
->
浴室
:
无效的
,
'价格'
=>
价格
'地点'
=>
$location,
'国家'
=>
$address[
'国家'
]
??
“”
,
'街道'
=>
$address[
'街道'
]
??
“”
,
'image_url'
=>
$images[
'picture_url'
]
??
无效的
,
'等级'
=>
$reviewScores[
'review_scores_rating'
]
??
无效的
,
“清洁度”
=>
$reviewScores[
'review_scores_cleanliness'
]
??
无效的
,
'分数'
=>
分数,];
// 代理/LLM 的格式化文本$agentLines[]
=
打印
(
"%d. **%s** (ID: %s)
\n
📍 %s | 🏠 %s | 👥 %s 位客人 | 🛏️ %s 张床
\n
⭐ 评分:%s/100 | 💰 每晚价格:$%s | 🎯 匹配度:%s%%
\n
%s”
,索引
+
1
,$listing
->
姓名
??
“未命名”
,(
细绳
)$listing
->
_ID,$location,$listing
->
属性类型
??
'财产'
,$listing
->
容纳
??
“?”
,$listing
->
床
??
“?”
,评分,$价格
??
“?”
,分数,摘要
?
mb_substr
(摘要,
0
,
150
)
。
“……”
:
“无描述”(英文):}
如果
(
空的
($agentLines)) {
返回
“没有找到符合您搜索条件的房源。请尝试其他搜索词。”
;}$count
=
数数
($agentLines)
返回
“成立 {
$count
} 匹配列表:
\n\n
“
。
内爆
(
“
\n\n
“
,$agentLines);}
抓住
(
\例外
$e) {
日志
::
错误
(
“房源搜索工具错误”
,[
'错误'
=>
$e
->
获取消息
()]);
返回
"搜索列表时出错:{
$e
->
获取消息
()}"
;}}}
这就是SDK的强大之处。我们在同一个类中用PHP定义模式和执行逻辑。依赖注入会自动工作(注入EmbeddingService)。
特工
现在我们通过代理将所有内容整合在一起。代理本质上是一个“角色”,拥有一套特定的工具和指令。
命名空间
应用程序代理
;使用
应用程序\工具\列表详情工具
;使用
应用程序\工具\房源搜索工具
;使用
Laravel\Ai\Attributes\MaxSteps
;使用
Laravel\Ai\Attributes\Timeout
;使用
Laravel\Ai\Contracts\Agent
;使用
Laravel\Ai\Contracts\HasTools
;使用
Laravel\Ai\Promptable
;/*** AirbnbAgent — Airbnb Arena 的人工智能旅行管家。** 实现了 Laravel AI SDK Agent 接口,并支持工具调用。* 该代理人使用 Gemini 作为其 LLM 提供商,并可使用两种工具:** - ListingSearchTool:基于 MongoDB Atlas 的语义向量搜索 $vectorSearch* - ListingDetailsTool:通过 MongoDB 文档 ID 获取完整的房源详情** SDK 会自动处理整个工具调用循环:* 1. 向 Gemini 发送包含工具定义的用户消息2. 如果 Gemini 请求工具调用,则执行该调用。3. 将工具结果发送回 Gemini4. 重复以上步骤,直到 Gemini 返回最终的文本回复。*/#[
最大步数
(
10
)]#[
暂停
(
120
)]班级
爱彼迎经纪人
实现
代理人
,
HasTools{
使用
可提示的
;
/*** 定义代理个性和规则的系统提示。*/
民众
功能
指示
()
:
细绳{
返回
<<<
迅速的您是 **Airbnb Arena 主持人**——一位热情、知识渊博的旅行管家 AI,由 MongoDB Atlas Vector Search 和 Voyage AI 嵌入提供支持。您的职责:- 帮助用户从 sample_airbnb 数据集中找到完美的 Airbnb 房源- 使用搜索列表工具查找符合用户描述的房源- 当用户想要了解特定房源的更多信息时,请使用 get_listing_details 工具- 当用户在不同选项之间进行选择时,比较列表信息- 根据偏好提供个性化推荐性格:- 热情友好,热爱旅行🌍- 熟悉不同社区和房产类型- 提供实用建议(最适合家庭、情侣、预算有限、追求奢华的人士等)- 谨慎但有效地使用表情符号。- 使用 Markdown 格式化回复,以提高可读性重要规则:- 务必使用搜索工具查找真实房源——切勿捏造房源信息。- 显示搜索结果时,突出显示关键差异点- 如果用户的问题不够清晰,请提出澄清问题- 请注明房源编号,以便用户查询更多详情。迅速的
;}
/*** 该代理人可使用的工具。SDK 会自动为 Gemini 生成 JSON 模式* 从每个工具的 description() 和 schema() 方法中获取。*/
民众
功能
工具
()
:
可迭代{
返回
[
应用程序
(
ListingSearchTool
::班级
),
新的
列表详情工具
(),];}}
就是这样。无需 while 循环。无需检查 finish_reason。无需解析 JSON 工具调用。SDK 会处理整个“推理循环”:
- 将用户输入和工具定义发送给 Gemini。
- Gemini 会以工具调用请求作为回应。
- SDK 执行该工具。
- SDK 将工具输出发送回 Gemini。
- 双子座给出最终答案。
控制器
最后,我们将其暴露给前端。
命名空间
App\Http\Controllers
;使用
应用程序\代理商\Airbnb代理商
;使用
应用程序\工具\房源搜索工具
;使用
照亮\Http\请求
;使用
照明\支持\立面\日志
;/*** ChatController — 处理 Airbnb Arena 聊天界面。** 使用 Laravel AI SDK 的 Agent 模式将用户连接到 Gemini。* SDK 会自动处理工具调用——当 Gemini 决定时* 如果需要搜索或获取详细信息,SDK 会执行这些工具。* 并将结果反馈给模型。** 建筑学:* 1. 用户发送消息* 2. AirbnbAgent(通过 SDK)将其连同工具定义一起发送给 Gemini。* 3. Gemini 可能会调用工具(搜索/详情)——SDK 会执行这些工具。4. SDK 将工具结果发送回 Gemini 以获得最终响应5. 将格式化后的响应返回给用户*/班级
聊天控制器
延伸
控制器{
/*** 显示聊天界面。*/
民众
功能
指数
(){
返回
看法
(
'竞技场'
(英文):}
/*** 处理来自用户的聊天消息。*/
民众
功能
聊天
(
要求
$请求){
// 代理循环(嵌入 + 向量搜索 + LLM 推理)最多需要 2 分钟
设置时间限制
(
120
(英文):$请求
->
证实
([
'信息'
=>
'必填|字符串|最大值:2000'
,
'历史'
=>
'大批'
,]);用户消息
=
$请求
->
输入
(
'信息'
(英文):历史
=
$请求
->
输入
(
'历史'
,[]);
尝试
{
// 根据对话历史记录构建提示信息,以便提供上下文$提示
=
$this
->
构建提示
($userMessage, $history);
// 创建代理(通过 make() 进行依赖注入)并发送提示代理
=
爱彼迎经纪人
::
制作
();$响应
=
代理
->
迅速的
(提示,
提供者
:
双子座
(英文):
// 从搜索工具中收集任何结构化列表数据搜索工具
=
应用程序
(
ListingSearchTool
::班级
(英文):列表
=
搜索工具
->
获取最新结果
();
返回
回复
()
->
json
([
'回复'
=>
(
细绳
)$response,
'列表'
=>
$listings,
'成功'
=>
真的
,]);}
抓住
(
\例外
$e) {
日志
::
错误
(
聊天控制器错误
,[
'错误'
=>
$e
->
获取消息
()]);
返回
回复
()
->
json
([
'回复'
=>
“抱歉,我遇到了一个错误:{
$e
->
获取消息
()}"
,
'成功'
=>
错误的
,],
500
(英文):}}
/*** 根据对话历史记录构建提示字符串,以便获取上下文。* 代理的指示(系统提示)在 AirbnbAgent 中定义。*/
私人的
功能
构建提示
(
细绳
$userMessage,
大批
历史)
:
细绳{
如果
(
空的
($history)) {
返回
$userMessage;}
// 包含最近的对话历史记录,以便代理了解上下文
(例如,“请告诉我更多关于第二个结果的信息”指的是之前的结果)$上下文
=
收集
(历史)
->
地图
(
功能
($msg) {$角色
=
$msg[
'角色'
]
===
'用户'
?
'用户'
:
'助手'
;
返回
"{
$角色
}: {
消息
['内容']}”
;})
->
内爆
(
“
\n\n
“
(英文):
返回
"{
$上下文
}
\n\n
用户:{
用户消息
}”
;}}
它看似简单,实则不然。`$agent->prompt($message)` 完成了 RAG 管道的所有繁重工作。
包起来
我们构建了一个完整的 RAG 代理, 向量搜索 语义理解和自主工具使用。
这 Laravel AI SDK 它极大地简化了此类应用程序的构建。通过标准化代理和工具,我们可以专注于业务逻辑(搜索查询、结果格式化),而不是与 LLM 通信的底层细节。
结合 MongoDB Atlas 对于数据+向量和 人工智能旅行 对于高质量的嵌入,您拥有一个可用于智能搜索的生产就绪堆栈。
完整的源代码可在以下网址获取: GitHub 祝您编程愉快!





