房地产 API MCP:AI 智能体查一次迪拜房产,要花掉 17000 个 token
在 Bayut API 上,迪拜码头(Dubai Marina)一居室租房信息翻一页,就是 68,709 个字符的 JSON。按业界常用的「四个字符一个 token」估算,25 条房源大约要占掉 AI 智能体 17,000 个 token 的上下文窗口。如果一个智能体要在两个门户上对比三个区域,那它还没写下一个字的答案,就已经花掉了约 105,600 个 token。我想搞清楚这些 token 到底花在哪了——毕竟现在被问到「怎么把大语言模型(LLM)和房产数据连起来」时,标准答案都是「给它配一个 MCP 服务器」。于是我按 MCP 的方式接入了三个阿联酋房产 API,用它回答一个简单的问题,并记录下模型需要读到的所有内容。
我测了什么
这个问题就是用户真会拿去问智能体的那种:迪拜码头、朱美拉村圈(Jumeirah Village Circle)和商业湾(Business Bay)的一居室公寓租金各是多少?
2026 年 11 月 3 日,我通过三个 API 共发起了 18 次调用:Bayut API、Property Finder API,以及 UAE Real Estate API——后者用一个密钥就能同时访问这两个门户。针对每个区域,我在两个门户上都查询了位置,各跑了一页一居室租房搜索,并拉取了 Property Finder 的租赁成交记录。我还额外取了一条完整的房源记录,并在迪拜码头用 UAE Real Estate 对两个门户都做了搜索。
这些 API 的每个端点都能通过 RapidAPI 网关走 MCP 访问,一个端点对应一个工具(搭建方法见这里)。工具返回的结果就是 API 的响应,所以我直接测响应本身:紧凑 JSON 的字符数,除以四作为 token 估算值。真实数值取决于模型所用的分词器(Anthropic 的 token 计数文档里有获取精确值的方法),但下面的对比在哪种情况下都成立,因为每个数字都是按同样方式处理的。
成本一:工具清单,在第一个问题之前就已产生
MCP 客户端在每一轮对话中,都会把每个可调用工具的名称、描述和输入 schema 发给模型。我按照网关的做法,用三份公开的 OpenAPI 文件构建了这些定义:一个端点一个工具,每个查询参数都作为该工具输入 schema 的一个属性。
| API | 工具数 | 字符数 | Token 数 |
|---|---|---|---|
| Bayut | 17 | 17,828 | 4,457 |
| Property Finder | 18 | 23,886 | 5,972 |
| UAE Real Estate | 11 | 10,725 | 2,681 |
| 三者合计 | 46 | 52,439 | 13,110 |
Property Finder 最贵:它的端点更多,工具定义平均约 1,327 个字符,而 Bayut 是 1,049 个,其中参数描述占了 48.3%。这些描述列出了每个过滤条件允许的取值、默认值和示例。正是这些细节帮模型正确调用工具,所以我不会删掉它们。这也正是应该只加载任务所需 API 的原因。
另一种做法是搞一个发现型连接器。我们维护的开源连接器 Free Live Data MCP,把 48 个 API 藏在四个工具后面(search_apis、search_endpoints、describe_endpoint、call_endpoint)。这四个定义一共 2,146 个字符,约 536 个 token。模型只有在主动请求某个参数列表时才需要为它付费。代价是每个任务多一两次往返。
成本之二:响应
以 Dubai Marina 为例,Bayut 租赁搜索一页大约 17,177 个 token,Property Finder 搜索一页大约 16,324 个。Property Finder 完整的房源记录约 3,216 个 token,租赁交易约 1,794 个,位置查询只要 470 个。Bayut 的位置查询约 2,023 个,因为它返回了 25 个候选位置,每个都带完整路径和名称,而 Property Finder 只返回了 20 个短名称。
三个区域的搜索结果页都相当稳定:Bayut 在 17,098 到 19,527 个 token 之间,Property Finder 在 16,324 到 18,126 之间。体积是由格式决定的,跟区域无关。
字符都花在哪了
Bayut 的一条搜索结果行大约 2,700 到 3,100 个字符,视区域而定。最大的字段是 location,占一行字符的 28.3%。它是整棵位置树,从阿联酋往下四到六层,一直到楼栋,每一层都带着多语言名称:name,然后是 name_l1 阿拉伯语、name_l2 中文、name_l3 俄语。category 也占了 16.8%,原因相同:两层,每层都是多语言。而 agent 每层只需要其中一个词。
在 Property Finder 上,images 占一行字符的 37.8%:图片 URL 带着冗长的版本字符串。
location_tree 又占 9.0%,address 占 7.2%。真正出乎我意料的是联系人数据。在 Bayut 上,房源经纪人(ownerAgent)的 phoneNumber 和 contactName 两个字段加起来,占了每行数据的 14.1%;在 Property Finder 上,agent_details 占 14.7%。如果智能体把原始数据行整个转发出去,就等于花钱去读一堆自己根本用不上的姓名和电话,还把它们塞进模型的上下文和日志里。做一次裁剪,这两个问题就一起解决了。
修法:先裁剪,再让模型读
智能体回答一个租房问题,大约只需要九个字段。下面是 Bayut 版本,配上 RapidAPI key 就能直接跑:
import json
import os
import requests
HOST = "uae-real-estate3.p.rapidapi.com" # Bayut API on RapidAPI
BASE = os.environ.get("API_BASE", "https://" + HOST)
def headers():
if "API_BASE" in os.environ:
return json.loads(os.environ.get("API_HEADERS", "{}"))
return {
"x-rapidapi-host": HOST,
"x-rapidapi-key": os.environ["RAPIDAPI_KEY"],
}
def cost(x):
"""Characters of compact JSON, and a rough token count at 4 characters a token."""
n = len(json.dumps(x, ensure_ascii=False, separators=(",", ":")))
return n, round(n / 4)
def trim(row):
"""The nine fields an agent needs for a rent question, flat, with the unit in the key."""
return {
"id": row["externalID"],
"title": (row.get("title") or {}).get("en"),
"rent_aed": row.get("price"),
"per": row.get("rentFrequency"),
"beds": row.get("rooms"),
"baths": row.get("baths"),
"area_sqm": round(row["area"], 1) if row.get("area") else None,
"furnished": row.get("furnishingStatus"),
"building": (row.get("location") or [{}])[-1].get("name"),
}
# One page of 1-bedroom rentals in Dubai Marina (Bayut location id 5003)
body = requests.get(
BASE + "/search-property",
params={"purpose": "for-rent", "location_ids": "5003", "rooms": "1", "page": 1},
headers=headers(),
timeout=60,
)
json () rows = body [ " data " ][ " properties " ] small = [ trim ( r ) for r in rows ] print ( " rows: " , len ( rows )) print ( " full response: %d characters, about %d tokens " % cost ( body )) print ( " trimmed rows: %d characters, about %d tokens " % cost ( small )) print ( json . dumps ( small [ 0 ], indent = 2 ))
我这次运行打印出 25 行。完整响应 68,709 个字符(约 17,177 个 token),裁剪后是 4,907 个字符(约 1,227 个 token)。第一行是一条位于 Marina Diamond 4 的带家具一居室,年租金 70,000 迪拉姆,面积 62.6 平方米——正好覆盖 agent 回答问题所需的全部信息,没有多余的。
三个区域的 Bayut 页面中位数从约 17,177 个 token 降到 1,227 个,减少了 92.9%。Property Finder 则从约 17,391 个降到 2,460 个,减少了 85.5%。它裁剪后的行保留了挂牌 URL,因为 agent 需要展示链接,而那个 URL 很长。六个搜索页面加起来从约 105,600 个 token 降到约 11,250 个。
裁剪在哪里执行并不重要,重要的是它确实执行了。在自己的 agent 里,就是在工具调用附近加几行代码。如果用的是你无法控制的网关,就在它前面放一个自己的轻量 MCP 服务器,由这个服务器调用 API 并返回裁剪后的行。通用网关没办法知道哪些字段对你的任务重要。
我在数据里踩到的坑
美化打印的 JSON 会多花一半成本。同样 18 个响应,用两空格缩进比紧凑形式长了 52.4%。缩进对人看日志没什么成本,但对模型读工具返回值就很贵。序列化时用 separators=(",", ":")。
Bayut 的区域过滤器和区域字段用的单位不一样。area_min 和 area_max 过滤器接收平方英尺。一居室行上的 area 字段返回的是像 62.6 这样的值,带十四位小数,只能理解为平方米。Property Finder 的 size 字段标明了单位({"value": 592, "unit": "sqft"})。把单位写进键名(area_sqm、area_sqft),这样模型就不会在没注意的情况下用不同单位比较两个平台的数据。
两个平台对类型的处理不一致。Bayut 的 rooms 是数字,Property Finder 的 bedrooms 是字符串("1")。
Bayut 的 createdAt 是 Unix 秒级时间戳,Property Finder 的 listed_date 是 ISO 字符串。模型通常能应付,但在裁剪阶段统一格式,比指望模型自己处理更稳妥。Property Finder 的部分搜索结果根本没有租金。我三次租赁搜索总共 113 行,其中 14 行没有 price.value。这些是混入结果的新项目卡片,其中 12 行带的是销售起价,比如 {"from": 1000000, "down_payment_percentage": 20}。在模型看到页面之前就删掉它们,否则它可能把一百万迪拉姆的销售价当成租金读。位置 ID 只在各自的平台上有效。Dubai Marina 在 Bayut 是 5003,在 Property Finder 是 50。智能体如果跨平台复用同一个 ID,不会报错,只会查到错误的区域。让智能体在每个平台上单独查询地点,或者维护一张你关心的区域的小型 ID 对照表。Bayut 的搜索结果没有描述。我拉取的 75 行搜索结果里,description 全部返回 {"en": null},所以想要房源文本的智能体必须再请求完整记录,这又要花更多 token。如果是问租金,根本不需要描述。
接下来我会怎么做
如果只用一个平台,一个 API 的工具加上对搜索结果的裁剪就够了:工具定义约 4,500 到 6,000 token,每次裁剪后的搜索页约 1,200 到 2,600 token。如果要在多个数据源之间选择,加一个发现连接器可以将固定开销控制在 500 token 左右。
我用的 API 有:Bayut API(RapidAPI 免费套餐)、Property Finder API 和 UAE Real Estate API。其中 UAE Real Estate API 同时提供 Postman 集合,方便你在写代码前先试用接口。每个平台都有自己的站点和智能体接入说明:Bayut 看 bayutapi.dev,里面有函数调用指南;Property Finder 看 uaepropertyapi.com,里面有 Claude、Cursor 和 ChatGPT 的 MCP 配置方法。Bayut 数据本身覆盖哪些内容,可以看 LinkedIn 上的 Bayut API 概览。
如果你正在构建基于阿联酋房产数据的智能体,想要裁剪版作为服务,或者想要数据集而非接口调用,欢迎联系我们。
从 Bayut API 拉取迪拜码头(Dubai Marina)的一居室租房信息,一页就是 68,709 个字符的 JSON。按业界常用的估算——每 4 个字符约合 1 个 token——这 25 条房源就要占掉 AI 智能体上下文窗口里大约 17,000 个 token。如果一个智能体要在两个平台上对比三个区域,那它还没写出答案的第一个字,就已经烧掉了约 105,600 个 token。
我想搞清楚这些 token 到底花在了哪里。因为现在一提到「怎么把大语言模型(LLM)接到房产数据上」,默认答案就是「给智能体配一个 MCP 服务器」。