作者:来自 Elastic Enrico Zimuel

了解如何将 Elasticsearch 配置为 OGX 向量存储,导入欧盟法规 PDF,并构建一个 Python RAG 代理,该代理运行混合 BM25 和向量搜索,并支持来源级引用。
Agent Builder 现已正式发布。你可以通过 Elastic Cloud Trial 开始使用,并查看 Agent Builder 的官方。
构建能够超越单轮问答的 AI 应用,是当今最令人兴奋也最具挑战性的工程问题之一。agents 必须具备规划、推理、工具使用能力,并能在多步骤中保持上下文一致性。而如何正确获取上下文 —— 包括检索什么信息、何时检索、以及如何将信息呈现给模型——这种实践通常被称为 context engineering。一个设计良好的 context pipeline,是区分 “看似合理但会幻觉的 agent” 和 “可靠、准确且真正有用的 agent” 的关键。
OGX 是一个开源的服务端 agentic loop,用于标准化 agentic AI 的构建模块:inference、安全性、工具使用、记忆以及 retrieval。该项目最初名为 Llama Stack,并于 2026 年 4 月重命名为 OGX,以体现其演进为一个多提供商、多 SDK 的服务端系统,可对接所有主流前沿模型厂商(OpenAI、Anthropic 和 Google)的原生 API。其中一个核心抽象层是 Vector IO,它为向量数据库提供统一接口,使你可以在不修改应用代码的情况下替换后端。截止 2026 年初,Elasticsearch 已成为 OGX 支持的远程 Vector IO provider,为 OGX 应用带来丰富的搜索能力(dense vector search、BM25 full-text search、hybrid search 以及 metadata filtering)。
在本文中,你将学习:
-
将 Elasticsearch 配置为 OGX 的向量存储。
-
从欧洲议会导入公开的 PDF 数据集。
-
构建一个基于 retrieval augmented generation (RAG) 的 Python agent,并使用 Elasticsearch 进行上下文检索。
-
运行结合语义与关键词信号的混合搜索查询。
完整 Python 应用会在文章中逐步构建,方便你跟随并运行。
Elasticsearch
你需要一个正在运行的 Elasticsearch 实例。你可以在 Elastic Cloud 上开通免费试用,或者使用 start-local 脚本在本地安装。
curl -fsSL https://elastic.co/start-local | sh
这会在你的机器上安装 Elasticsearch 和 Kibana,并在 elastic-start-local 文件夹内生成一个存储在 .env 文件中的 API key。你将在下一部分中使用这些值。有关 start-local 的更多信息,可以访问 https://github.com/elastic/start-local。
使用 Elasticsearch 配置 OGX
克隆示例仓库,并在虚拟环境中安装依赖。
安装
你可以从以下仓库安装 OGX 项目示例 https://github.com/elastic/ogx-elasticsearch-example:。
你可以按如下方式克隆该仓库并安装依赖,同时创建虚拟环境:
git clone https://github.com/elastic/ogx-elasticsearch-example.git
cd ogx-elasticsearch-example
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
现在我们需要配置 文件,并从 文件中复制结构。
我们可以直接将该文件复制为 .env:
cp .env.example .env
我们需要在 .env 中配置设置,补充缺失的信息(例如 ELASTICSEARCH_API_KEY、OPENAI_API_KEY):
ELASTICSEARCH_URL=localhost:9200
ELASTICSEARCH_API_KEY=your-api-key-here
ELASTICSEARCH_INDEX_NAME=eu-documents
OGX_URL=http://localhost:8321
OPENAI_API_KEY=your-api-key-here
INFERENCE_MODEL="openai/gpt-5-mini-2025-08-07"
EMBEDDING_MODEL="sentence-transformers/all-MiniLM-L6-v2"
该项目示例使用 OpenAI 的 GPT-5 mini 作为 大型语言模型(LLM),并使用 all-MiniLM-L6-v2 作为 嵌入模型。
如果你愿意,也可以更换这些模型。
配置 OGX 服务器
OGX 服务器是一个用于协调 agentic 应用工作的应用程序。该服务器的架构如图 1 所示。
OGX 服务器暴露了一个 HTTP API,可以被任何应用程序调用。目前,OGX 也提供了 Python 和 Typescript 客户端库。OGX 服务器可以与推理服务、向量存储、工具以及文件进行通信。
OGX 使用一个 config.yaml 配置文件来声明每个 API 使用的提供方。该文件包含如下配置:
version: '2'
image_name: elasticsearch-agent
apis:
- inference
- vector_io
- files
- file_processors
- responses
- tool_runtime
providers:
inference:
- provider_id: sentence-transformers
provider_type: inline::sentence-transformers
config:
trust_remote_code: false
- provider_id: openai
provider_type: remote::openai
config:
api_key: ${env.OPENAI_API_KEY}
vector_io:
- provider_id: elasticsearch
provider_type: remote::elasticsearch
config:
elasticsearch_url: ${env.ELASTICSEARCH_URL}
elasticsearch_api_key: ${env.ELASTICSEARCH_API_KEY}
persistence:
backend: kv_default
namespace: vector_io::elasticsearch
files:
- provider_id: localfs
provider_type: inline::localfs
config:
storage_dir: ${env.FILES_STORAGE_DIR:=~/.ogx/dummy/files}
metadata_store:
table_name: files_metadata
backend: sql_default
file_processors:
- provider_id: file-processor
provider_type: inline::auto
config: {}
responses:
- provider_id: responses
provider_type: inline::builtin
config:
persistence:
responses:
backend: sql_default
table_name: responses
tool_runtime:
- provider_id: file-search
provider_type: inline::file-search
config: {}
registered_resources:
models:
- model_id: sentence-transformers/all-MiniLM-L6-v2
provider_id: sentence-transformers
provider_model_id: sentence-transformers/all-MiniLM-L6-v2
model_type: embedding
metadata:
embedding_dimension: 384
vector_stores:
default_provider_id: "elasticsearch"
default_embedding_model:
provider_id: "sentence-transformers"
model_id: "all-MiniLM-L6-v2"
# Annotation: instruct the LLM to cite both document_id and chunk_id
annotation_prompt_params:
chunk_annotation_template: "[{index}] {metadata_text} cite as <|{file_id}|>\n{chunk_text}\n"
annotation_instruction_template: "Cite sources immediately at the end of sentences before punctuation. Each chunk has a document_id (the file_id shown as 'cite as') and a chunk_id inside its attributes. Use the format <|document_id:chunk_id|>. Example: 'This is a fact <|71213903-602c-460c-b0e8-ac43d38c41c6:a3f9e201-...|>.'. Do not add extra punctuation. Use only IDs visible in the metadata above."
# File processing during file ingestion
file_ingestion_params:
default_chunk_size_tokens: 200
default_chunk_overlap_tokens: 30
OGX 服务器采用可插拔系统,你可以决定哪些 API 被暴露。在本示例中,我们使用了 inference、vector_io、files、file_processors、responses 和 tool_runtime。
| API Provider 使用用途 | ||
|---|---|---|
inference | sentence-transformers(本地)、OpenAI(远程) | 嵌入与 LLM 补全 |
vector_io | Elasticsearch(远程) | 向量存储与检索 |
files | localfs(本地) | 文档上传与存储 |
file_processors | auto(本地) | 上传文档的分块与处理 |
responses | builtin(本地) | 对话历史与会话持久化 |
tool_runtime | file-search(本地) | agent 工具执行 |
这里有很多可选项,可以让你非常细粒度地自定义 OGX 服务器的行为。例如,我们自定义了回答提示,使其不仅返回 source 的 document-id,还返回 chunk-id。这个设置可以提升溯源能力,使你能够分析存储在 Elasticsearch 中的 chunk 级句子。
要运行 OGX 服务器,可以在项目根目录执行以下命令:
set -a && source .env && set +a && uvx --from 'ogx[starter]' --with 'sentence-transformers>=3.0.0' ogx stack run ./config.yaml
使用 Elasticsearch 和 OGX 构建 RAG 管道
要启动 RAG 应用,你需要执行以下命令:
python src/rag.py
第一次运行时,该脚本会读取 data/*.pdf 文件,从内容中创建 chunk,生成 embedding,并将其存储到 Elasticsearch 中。这一过程由 OGX 服务器自动完成。
当文件上传完成后,你将得到一个基础的聊天系统,如下所示:
Index already exists: eu-documents
Vector store ID: vs_32318e6f-4f48-41bb-8127-05fd067e52ea
Ask a question about EU documents (or 'exit' to quit):
你可以针对存储在 `` 文件夹中的欧洲联盟(EU)文档提出任何问题。
例如,你可以这样提问:
What are the risks of using AI as presented in the EU AI Act document?
RAG 会返回类似如下的结果:
🔧 Executing file_search (server-side)...
🤔 Brief summary of the main risks the EU AI Act identifies for AI use:
- Harm to health, safety or fundamental rights from specific AI applications and uses <|71213903-602c-460c-b0e8-ac43d38c41c6:3eadea8d-32dc-93f2-f3b9-1708eada93ac|>.
- Discrimination and biased outcomes (notably from biometric identification and categorisation, including risks for age, ethnicity, race, sex or disabilities) <|71213903-602c-460c-b0e8-ac43d38c41c6:befb889a-ecce-fb53-061c-126cbe1e0006|>.
- Risks from profiling and automated decision‑making that affect rights protected under data‑protection law <|71213903-602c-460c-b0e8-ac43d38c41c6:3eadea8d-32dc-93f2-f3b9-1708eada93ac|>.
- Privacy risks when AI processes special categories of personal data or sensitive information <|71213903-602c-460c-b0e8-ac43d38c41c6:aa70d4ad-2adc-a584-24ad-237ea03b9477|>.
- Cybersecurity threats specific to AI (e.g., data poisoning, adversarial attacks, membership inference and exploitation of model or data vulnerabilities) <|71213903-602c-460c-b0e8-ac43d38c41c6:d88f1a3b-9a97-f7e9-a870-0a597f91ff90|>.
- Lack of transparency, traceability or explainability that undermines accountability and remedies for affected persons <|71213903-602c-460c-b0e8-ac43d38c41c6:3eadea8d-32dc-93f2-f3b9-1708eada93ac|>.
- Misinformation and provenance risks from synthetic audio/visual/text content, which the Act requires to be marked/detectable where feasible <|71213903-602c-460c-b0e8-ac43d38c41c6:edd2f19d-30bd-c990-5ede-1952260547ac|>.
- Risks to critical infrastructure and public safety when AI is used as safety‑critical components <|71213903-602c-460c-b0e8-ac43d38c41c6:aa70d4ad-2adc-a584-24ad-237ea03b9477|>.
- Harms amplified by dependence, power imbalances, vulnerability of affected groups, or outcomes that are hard to correct or reverse <|71213903-602c-460c-b0e8-ac43d38c41c6:aa70d4ad-2adc-a584-24ad-237ea03b9477|>.
- Broader societal risks (including to democracy, the rule of law and environmental protection) if AI is deployed without proper safeguards <|71213903-602c-460c-b0e8-ac43d38c41c6:f657e2b2-44c8-e0d2-d830-e7b499e63d43|>.
The Act addresses these by (among other measures) designating certain uses as “high‑risk,” imposing transparency, documentation and registration obligations, and requiring robustness, cybersecurity and conformity assessments for such systems <|71213903-602c-460c-b0e8-ac43d38c41c6:befb889a-ecce-fb53-061c-126cbe1e0006|>.
请注意,每个响应末尾都有一个 <|document-id:chunk:id|> 。这些 ID 很重要,因为它们是用于生成答案的来源。RAG 架构的一个重要优势就是通过提供来源证据来减少 人工智能幻觉。
使用 OGX file_search 的 Python agent 实现
OGX 框架提供了一个 agent 类,可以用少量代码构建 agentic 应用。
from ogx_client import Agent, AgentEventLogger, OgxClient
VECTOR_STORE_NAME = os.getenv('ELASTICSEARCH_INDEX_NAME')
EMBEDDING_MODEL = os.getenv('EMBEDDING_MODEL')
INFERENCE_MODEL = os.getenv('INFERENCE_MODEL')
OGX_URL = os.getenv('OGX_URL', 'http://localhost:8321')
client = OgxClient(base_url=OGX_URL)
agent = Agent(
client,
model=INFERENCE_MODEL,
instructions="You are a helpful assistant",
tools=[
{
"type": "file_search",
"vector_store_ids": [vector_store_id],
}
],
)
在我们的示例中,OGX_URL、EMBEDDING_MODEL 和 INFERENCE_MODEL 都是存储在 .env 文件中的环境变量。
vector_store_id 是向量数据库的 ID。我们使用 Elasticsearch 作为向量数据库,并通过以下代码对存储在 data 文件夹中的 PDF 文件进行数据导入。这些文件与欧盟法规文档相关:
- AI_Act.pdf:欧盟 AI 法案(EU 2024/1689),144 页
- DMA.pdf:数字市场法案(EU 2022/1925),66 页
- DSA.pdf:数字服务法案(EU 2022/2065),102 页
- GDPR.pdf:通用数据保护条例(EU 2016/679),88 页
- NIS2.pdf:网络与信息安全指令 2(73 页)
我们使用客户端的 Files 功能 来存储这些文件。
vector_store = client.vector_stores.create(
name=VECTOR_STORE_NAME,
extra_body={
"provider_id": "elasticsearch",
"embedding_model": EMBEDDING_MODEL,
}
)
vector_store_id=vector_store.id
for file_path in Path(DATA_DIR).glob("*.pdf"):
with open(file_path, 'rb') as f:
try:
file_info = client.files.create(file=f, purpose="assistants")
client.vector_stores.files.create(
vector_store_id=vector_store_id, file_id=file_info.id
)
except Exception as e:
# Delete the vector_store if file upload fails
client.vector_stores.delete(vector_store.id)
print(f'Failed to upload {file_path.name}: {e}')
raise
这些文件是通过 client.files.create() 函数创建的,然后再通过 client.vector_stored.files.create() 函数添加到向量数据库中。
这些函数实现了上传文档、创建 chunk、生成 embedding,以及将其存储到向量数据库中的全部逻辑。所有这些步骤都在 OGX 服务器中完成。
当我们创建好 vector_store、上传文件并创建 agent 类实例之后,就可以创建一个新的 session,并使用 agent.create_turn() 函数与 LLM 进行交互。
session_id = agent.create_session("my_session")
while True:
question = input("\nAsk a question (or 'exit' to quit): ")
if question.lower() == 'exit':
break
# Just ask - agent handles retrieval automatically
response = agent.create_turn(
messages=[{"role": "user", "content": question}],
session_id=session_id,
stream=True,
)
for log in AgentEventLogger().log(response):
print(log, end="")
前面的代码实现了一个非常基础的聊天界面,用户可以针对上传的 PDF 提出任何问题。要结束对话,用户只需要输入 “exit”。
当我们创建一个新的 session 时,OGX 服务器会使用 session_id 的专用内存来管理聊天历史。
使用 Elasticsearch 进行混合搜索:结合 BM25 与向量搜索
OGX 的 memory API 默认使用 dense vector 搜索,这对于 语义相似性 非常有效。但在某些场景中(例如精确法律引用、文档编号),你可能需要将向量搜索与 BM25 全文搜索结合起来,这种技术在 Elasticsearch 中被称为 混合搜索。
下面的示例通过增加一个直接的 Elasticsearch 混合搜索步骤来扩展 RAG agent。我们不再完全依赖 OGX 内置的 memory 检索,而是使用 带有倒数排名融合的 Retriever API(RRF)直接对 Elasticsearch 发起 query,融合语义信号和关键词信号,然后将结果作为额外上下文传回 agent。
我们可以创建一个自定义 tool 来实现混合搜索,并将其传递给 agent 对象。
def get_embedding(text: str) -> list[float]:
"""Use OGX inference to embed the query text."""
response = client.embeddings.create(
model=EMBEDDING_MODEL,
input=text,
)
return response.data[0].embedding
def hybrid_file_search(query: str) -> str:
"""Search documents using hybrid BM25 + vector search in Elasticsearch.
:param query: the search query
"""
# 1. Get query embedding via OGX
embedding = get_embedding(query)
# 2. Run hybrid search (BM25 + kNN) on Elasticsearch
response = es.search(
index=ES_INDEX_NAME,
body={
"retriever": {
"rrf": {
"retrievers": [
{
"standard": {
"query": {"match": {"content": query}}
}
},
{
"knn": {
"field": "embedding",
"query_vector": embedding,
"k": 10,
"num_candidates": 100,
}
},
]
}
}
}
)
# 3. Return formatted results
hits = response["hits"]["hits"]
return "\n\n".join(
f"{h['_source']['content']}" for h in hits
)
在定义好自定义函数工具之后,就可以将其提供给 agent。agent 会在交互过程中根据需要自动触发该工具。
需要注意的是,在 instructions 参数中必须提供清晰的描述。
agent = Agent(
client=client,
model=INFERENCE_MODEL,
tools=[hybrid_file_search],
instructions="Use hybrid_file_search to retrieve relevant documents about EU regulations.",
)
为什么混合搜索在法律文档中比纯向量检索表现更好?
在精确匹配查询(例如法规引用如 “EU 2016/679”)上,混合搜索优于纯向量搜索,因为 BM25 会对字面匹配进行打分,而向量部分则捕获语义意图。
在这个例子中,rrf 指的是 reciprocal rank fusion(倒数排名融合),这是一种成熟的算法,用于合并来自多个检索器的排序结果。它不需要对 BM25 和向量搜索的分数进行尺度校准,因此开箱即用且非常稳定。
相比纯向量搜索,其核心优势在于对精确名称和关键词的高精度匹配。例如查询 “GDPR regulation EU 2016/679's requirements for personal data” 时,BM25 会对“GDPR regulation EU 2016/679”这种字面匹配给出高分,而向量部分则理解“personal data requirements”的语义意图。RRF 会将这两种信号融合成一个统一排序结果。
在生产环境中,我们建议使用 Elasticsearch 的 Ranking Evaluation API。该能力可以用来评估搜索效果,并对关键参数进行微调,包括 reciprocal rank fusion (RRF) 和 RRF 参数,以优化检索结果。
结论:使用 Elasticsearch 与 OGX 进行上下文工程
Elasticsearch 作为远程 Vector IO provider 集成到 OGX 中,为开发者提供了一个生产级向量数据库能力,具备混合搜索、丰富的元数据过滤,以及 Elasticsearch 一贯的高可扩展性,同时通过一个干净、可替换的 API 抽象层进行访问。在本文中,你配置了该 provider,导入了包含欧盟法规 PDF 文档的数据集,并构建了一个 Python agent,在生成答案之前从 Elasticsearch 中检索相关上下文,从而实现更可靠的回答。
上下文工程(context engineering)——也就是决定存什么数据、如何分块、以及在查询时如何检索正确片段——往往比模型微调本身更重要。借助 Elasticsearch 作为向量存储,你可以快速迭代:调整分块策略、实验混合搜索、添加元数据过滤,并在 Kibana 中监控检索质量,而无需修改任何 agent 代码。
资源
-
OGX Vector IO provider 文档:ogx-ai.github.io/docs/providers/vector_io/remote_elasticsearch
-
OGX GitHub:github.com/ogx-ai/ogx
-
Elasticsearch 混合搜索:https://elasticstack.blog.csdn.net/article/details/145697606
-
Elastic start-local:github.com/elastic/start-local
原文:Elasticsearch vector store for RAG agents with OGX - Elasticsearch Labs

2154

被折叠的 条评论
为什么被折叠?



