为 `Indexer` 接口添加 `Delete` 和 `Update` 方法
- Dominant language
- Go
- Stars
- 13k
- Forks
- 1.1k
- Avg merge
- 4h 6m
- Merged PRs (30d)
- 41
Description
### 背景
当前 `Indexer` 接口只有一个方法:
```go
type Indexer interface {
Store(ctx context.Context, docs []*schema.Document, opts ...Option) (ids []string, err error)
}
```
这意味着文档只能存,不能删,也不能改。如果存了一份过时的内容,或者存错了——开发者只能绕过 Eino,直接操作底层数据库。框架的回调系统、错误处理、跨后端可移植性全部丢失。
### 历史讨论
这个问题在 **[Issue #415](https://github.com/cloudwego/eino/issues/415)**(2025 年 8 月)中被提出过。当时框架还在 v0.5,维护者的回答是"用 Indexer 做存储,额外写向量查询就好"。Issue 作为"已解答"关闭。
### 一年过去,情况变了
1. **RAG 从 Demo 走向生产。** 2025–2026 年的行业实践普遍强调文档全生命周期管理——原子性的"删旧+插新"、软删除模式、基于 ID 的跨平台操作。
2. **Eino 本身已经成熟。** 从 v0.5 到 v0.10-alpha,ADK 层(DeepAgents、Plan-Execute、Supervisor)和 MCP 工具集成已经非常丰富。一个成熟的 Agent 框架却没有文档生命周期原语,意味着每个用户都要自己造轮子。
3. **Agent 们没有自己实现。** 对 ADK 代码库的完整审计确认:零文档生命周期代码。Agent 可能会生成或修改文档,但框架层没有通过 Indexer 持久化这些变更的途径。
4. **ID 契约已经在。** `Store` 返回 ID,这些 ID 天然就是 `Delete` 和 `Update` 的句柄。API 设计已经就绪——只差方法定义。
### 提案
为 `Indexer` 接口添加两个方法:
```go
type Indexer interface {
// Store 存储文档并返回后端分配的 ID。
Store(ctx context.Context, docs []*schema.Document, opts ...Option) (ids []string, err error)
// Delete 按 ID 从后端删除文档。
// 幂等语义:删除一个不存在的 ID 不是错误。
Delete(ctx context.Context, ids []string, opts ...Option) error
// Update 原子性地替换已有文档。
// 每个文档必须携带 Store 返回的 ID。
// 如果引用的文档不存在,返回错误。
Update(ctx context.Context, docs []*schema.Document, opts ...Option) error
}
```
**设计理由:**
| 决定 | 理由 |
|------|------|
| 基于 ID 操作 | `Store` 已经返回 ID——用这些 ID 形成闭环。跨所有后端可移植。 |
| Delete 是幂等的 | 匹配 Milvus、Qdrant、Pinecone、Weaviate 等的主流语义。 |
| Update 是原子替换 | 遵循行业最佳实践:按 `doc_id` 删旧块 + 插入新块。避免部分更新的复杂性。 |
| 复用现有 `...Option` | 与 Store 一致。`Options.Embedding` 支持更新时重新向量化。 |
### 改动范围
本提案仅覆盖 `eino` 核心仓库中的 `Indexer` 接口定义。`eino-ext` 中 8 个实现(ES7/8/9、OpenSearch 2/3、Milvus、Milvus2、Qdrant、VikingDB)需要通过单独的 PR 跟进——先用 `errors.ErrUnsupported` 存根保持编译通过,再逐步实现真实后端调用。
已有一份附带测试的概念验证实现可供参考。
### 兼容性
对任何实现了 `Indexer` 的外部代码来说,这是破坏性变更。但是:
- 已知实现全部在 `eino-ext`,属于同一组织。
- 项目是 pre-1.0(v0.10-alpha),破坏性变更是预期内的。
- 迁移是机械的:加两个方法就行。对于真的无法支持删除/更新的后端,返回 `errors.ErrUnsupported` 就好。
---
欢迎讨论——特别是接口是直接扩展还是新建 `MutableIndexer` 接口,以及 `Update` 的错误语义是否合理。
Contributor guide
Research direction
Start at the core repository's Indexer interface definition and inspect the existing Store contract, schema.Document, and Option types; the issue does not name specific files or tests. Review the proposed direct extension versus a separate MutableIndexer, then verify how the change affects the named eino-ext implementations and the stated ErrUnsupported migration path.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend-api-design, data
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100