AI Search 现已进入正式可用阶段。此次能力升级的重点不只是“让聊天模型多读几份文档”,而是把图片像素、扫描版 PDF 和普通文件统一纳入检索流程:图片可以直接生成视觉向量,扫描 PDF 会先经过 OCR,单文件上限为 10 MiB,并且检索结果可以交给任意聊天模型使用。
对开发团队而言,这意味着搜索层与生成层可以分开设计。你可以保留现有聊天模型,只替换知识摄取和召回链路,而不必重新搭建整套问答系统。
图片与扫描文档终于不再是检索盲区
传统文档搜索通常依赖可提取文本。对于带文本层的 PDF、Markdown 或办公文档,这条路径很直接;但产品截图、流程图、扫描合同和历史档案往往无法提供可靠的文本内容。
新的处理方式覆盖了两类不同问题:
- 图片直接按像素嵌入:视觉内容本身被编码为向量,不必先把图片强行转换成一段文字。这样更适合搜索界面截图、商品外观、图表布局或视觉上相似的素材。
- 扫描 PDF 执行 OCR:没有文本层的页面先经过文字识别,再进入文本检索链路。合同编号、标题、段落和表格中的可识别文字因此有机会被召回。
这两条链路不能互相替代。OCR 擅长回答“图片上写了什么”,视觉嵌入更适合回答“图片看起来像什么”。例如搜索“红色错误提示框”时,视觉特征可能比 OCR 更重要;搜索扫描发票中的订单号时,则应优先依赖 OCR 文本。
落地前仍要注意边界:低分辨率扫描件、手写文字、倾斜页面和复杂表格可能降低 OCR 准确率;视觉相似也不等于业务语义一致。因此,高风险场景不应把一次召回直接当作最终结论。
与聊天模型解耦,架构会更容易替换
“可与任意聊天模型配合”最重要的工程含义,是 AI Search 可以承担独立的检索层职责。一个典型请求链路可以拆成:
- 用户提交问题,或者提供一张用于相似搜索的图片。
- 搜索服务返回相关文本片段、图片或文档引用。
- 应用把召回内容整理成上下文。
- 当前选定的聊天模型根据上下文生成回答。
- 前端展示答案,同时保留来源引用。
模型无关并不意味着所有模型的效果相同。不同模型支持的上下文长度、图片输入、结构化输出和引用遵循能力不同。应用层最好定义一个稳定的模型适配接口,而不是让搜索代码直接依赖某个 SDK:
from typing import Protocol
class ChatModel(Protocol):
def answer(self, question: str, context: str) -> str:
...
def answer_with_search(question: str, passages: list[str], model: ChatModel) -> str:
context = "\n\n---\n\n".join(passages)
return model.answer(question=question, context=context)
这样做以后,更换模型只需要提供新的 ChatModel 实现。检索索引、OCR 结果和文件摄取流程可以继续复用。
可直接改造的上传前检查与调用示例
单文件上限是 10 MiB。不要只依赖服务端返回错误,客户端和批处理任务都应在上传前检查文件大小,否则大批量导入时会产生不必要的失败请求。
下面是一个可以运行并改造的 Python 示例。由于摘要没有给出实际 API 路径和字段名,示例假设服务使用 POST /v1/search/files 接收 multipart 文件;运行前请把地址、令牌和字段名替换为你所使用服务的真实配置。
先安装依赖:
python -m pip install requests
保存为 upload_to_search.py:
import os
import sys
from pathlib import Path
import requests
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10 MiB
def upload(path_string: str) -> None:
path = Path(path_string)
if not path.is_file():
raise SystemExit(f"文件不存在: {path}")
size = path.stat().st_size
if size > MAX_FILE_SIZE:
raise SystemExit(
f"文件大小为 {size / 1024 / 1024:.2f} MiB,超过 10 MiB 上限"
)
endpoint = os.environ["AI_SEARCH_ENDPOINT"].rstrip("/")
token = os.environ["AI_SEARCH_TOKEN"]
with path.open("rb") as file_handle:
response = requests.post(
f"{endpoint}/v1/search/files",
headers={"Authorization": f"Bearer {token}"},
files={
"file": (
path.name,
file_handle,
"application/octet-stream",
)
},
data={"collection": "product-docs"},
timeout=120,
)
response.raise_for_status()
print(response.json())
if __name__ == "__main__":
if len(sys.argv) != 2:
raise SystemExit("用法: python upload_to_search.py <文件路径>")
upload(sys.argv[1])
运行方式:
export AI_SEARCH_ENDPOINT="https://your-search-service.example.com"
export AI_SEARCH_TOKEN="replace-with-your-token"
python upload_to_search.py ./samples/scanned-contract.pdf
生产环境还应补充 MIME 类型白名单、重试与退避、文件哈希去重、恶意文件扫描、上传状态轮询,以及 OCR 或索引失败后的死信队列。接收用户文件时,不要把访问令牌放在浏览器代码中;应由后端签发短期上传凭证,或者由后端代理上传。
价格要按整条链路核算
摘要没有提供具体单价,因此不能仅凭“正式可用”推断实际账单。评估成本时,应根据服务当前公布的计费单位,把整条链路拆开观察:
- 文件摄取、OCR 或图片处理是否产生费用;
- 索引存储是否按容量和保存时间计费;
- 搜索请求是否按次数、计算量或套餐额度计费;
- 聊天模型的输入与输出是否另行计费;
- 重复上传、重新索引和失败重试是否增加消耗。
可以在应用侧记录一组最小成本指标:原始文件大小、页数、文件类型、索引状态、查询次数、召回片段数,以及送入聊天模型的 token 数。具体计费项目和免费额度应以实际控制台及当前价格表为准。
上线前的检查清单
AI Search 正式可用后,最稳妥的采用方式不是一次性迁移所有资料,而是选择一批包含普通文档、截图和扫描 PDF 的真实样本做对照测试:
- 确认每个文件都不超过 10 MiB,并明确超限文件的拆分策略;
- 分别测试文本查询、视觉相似查询和扫描件关键词查询;
- 为 OCR 准确率、召回率和引用正确率建立人工评测集;
- 保留原文件与页码引用,避免聊天模型生成无法追溯的答案;
- 将搜索、索引处理和模型调用的成本分开监控;
- 对合同、身份材料等敏感文件设置权限、保留周期和删除流程。
真正值得采用的变化,是搜索入口开始覆盖此前难以处理的视觉和扫描内容,同时不再把应用锁定到单一聊天模型。只要控制好文件限制、评测、权限与成本,这套解耦架构就能逐步接入现有知识库,而无需推倒重来。