跳到主要内容

04 · 搜索索引、边界与对比

本章先讲一个容易混淆的点——dir两套“搜索”,然后收束全局:边界、巧妙之处、横向对比、全局代码地图。

1. 两套搜索,别搞混

初学者最容易困惑的地方:dirctl searchdirctl routing search两个不同的东西,查的库不一样、能力不一样。

维度SearchService(本地索引)RoutingService.Search(路由层)
查什么本地数据库里所有记录的元数据本地缓存的远端 peer 标签
范围本节点存的记录(含自己 push 的)别的 peer 发布、被拉取式发现缓存的
查询能力通配符(web**ml*v1.0.?)、多字段层级前缀标签 + OR 打分
文件server/controller/search.goserver/routing/routing_remote.go
心智“翻我自己的索引卡片”“翻我记下的‘别人有啥’小本本”

本章讲的是前者(本地数据库二级索引);后者已在 02 章 讲透。

2. 本地搜索索引怎么来的

回忆 01 章:每次 Push 成功,StoreController 顺手调 s.db.AddRecord 把记录加进数据库(server/controller/store.go:401)。这就是本地搜索索引的数据来源——存储是真相、数据库是可查视图(索引失败不影响 Push)。

reconciler 的 Indexer 任务还会兜底:监控本地 OCI 仓库的 tag 快照,发现新 tag 就拉记录、校验、加进搜索库(reconciler/README.md Indexer Task)——保证就算某条记录绕过了 Push 控制器(比如 sync 复制进来的),也能被索引到。

3. 通配查询怎么走

SearchService 有两个方法:SearchCIDs(只返 CID)和 SearchRecords(返完整记录)。两者都把客户端查询翻译成数据库过滤条件(server/controller/search.go:31 SearchCIDs):

// server/controller/search.go:35-49(SearchCIDs 节选)
filterOptions, err := databaseutils.QueryToFilters(req.GetQueries())
// ... 追加 limit / offset / 排序
recordCIDs, err := c.db.GetRecordCIDs(filterOptions...)

查询类型很丰富,支持通配(proto/agntcy/dir/search/v1/record_query.proto:26 RecordQueryType):name、version、skill name/id、locator、module name/id、domain name/id、created_at、author、schema_version,以及一个 VERIFIED(按“名字是否已校验”过滤)。通配规则:* 任意多字符、? 恰好一字符(:18-22)。注意数值字段(skill_id/domain_id/module_id)只支持精确匹配,无通配。

4. 巧妙之处(可借鉴的技术)

  • 内容寻址 + Push 时双算 CID 自洽校验。 ID 即防伪封条,且把“序列化漂移”这类隐蔽 bug 在写入时就拍死(server/store/oci/oci.go:212)。任何做不可变内容存储的系统都能借鉴这招“写入即校验”。

  • 拉取式发现:DHT 只放‘谁有’,标签现抽现缓存。 用 DHT 最可靠的 provider 子系统 + 按需 RPC 拉取,绕开 DHT k-closest 的标签传播不可靠问题,换来上百 peer 的可扩展性,且标签永不与内容漂移(server/routing/routing_remote.go:682,ROUTING.md)。

  • 混合快慢路径(GossipSub + DHT+Pull)。 常态走 GossipSub 广播(~15ms、~95% 带宽节省),竞态/丢失时 DHT 通知触发拉取兜底;用密码学已验证的 peer ID 写缓存防投毒(:826-832)。

  • OR 逻辑 + min-score 一个 API 表达宽松/严格,加服务端查询去重防客户端刷分(:311server/routing/constants.go:52)。

  • 可信做成 referrer 叠加层。 签名/校验不改记录、不挡主路径,多签名者可并存,主 CID 恒定(见 03)。

  • 存储是真相、索引可重建。 数据库索引失败不阻塞写入,reconciler 兜底重建(server/controller/store.go:401、Indexer 任务)。

5. 边界与局限(诚实)

  • 它不运行 agent。 dir 只登记/分发关于 agent 的描述;agent 怎么跑、怎么调用,不在范围内(README “Source tree”、Features 全是发现/存储/信任)。

  • 单条记录 ≤ 4MB。 要塞进一个 gRPC 请求(api/core/v1/record.go:20、store proto :18)。大产物得放别处、记录里只放 locator。

  • Search 给的是 CID,不是内容。 路由层 Search 只返回引用,要 pull 才有字节(proto/.../routing_service.proto:39)。

  • 远端标签是缓存,可能陈旧或缺失。 拉取式发现依赖后台维护;GetProviderCount 是本地点态估计、不是实时全网查询(server/routing/routing.go:175、proto :145)。proto 也直说 Search 结果“可能 stale 或不存在”(routing_service.proto:35-37)。

  • OASF schema 是外部依赖。 记录“合法”的定义在外部 agntcy/oasf 仓库 + 远程 schema URL;本仓库只调校验器(server/server.go:149)。schema 服务慢/不可达时校验有 30s 超时(api/core/v1/record.go:24)。

  • 名字校验需要 http(s):// 前缀 + 已签名才进入流程,普通名字不被校验(强制点在 server/database/gorm/naming.go:133:SQL name LIKE 'http://%' OR LIKE 'https://%';reconciler/README.md Name Task 第 1 步亦述)。

6. 横向对比(同 shelf 兄弟)

dirai-protocol-reference(协议库)里属于**“agent 互联网基础设施”**这一类。它和典型的“agent 间通信协议”关注点不同:

关注点dir(本项目)的取舍
核心抽象目录/发现——“按能力找到 agent”,不定义 agent 怎么对话
寻址内容寻址(CID) + 人类可读名字(Docker 风格 name:version@cid)
分发P2P / libp2p DHT,去中心、无单点目录服务器
信任模型Sigstore 签名 + 域名所有权(JWKS),可验证声明、叠加式
存储底座复用 OCI 镜像仓库(ORAS),而非自造存储
与运行解耦刻意只管发现/分发,不管运行时(运行时是 AGNTCY 其他组件如 runtime/discovery 的事,见 proto/agntcy/dir/runtime/v1/)

相比“把元数据塞进中心化注册表”的做法,dir 的取舍是用内容寻址 + DHT 换去中心化和防篡改,代价是最终一致(缓存可能陈旧)和后台维护成本

7. 学习路径回顾

  1. 01 内容寻址存储 — CID 是地基。
  2. 02 路由与发现 — 拉取式发现是核心创新。
  3. 03 可信:签名与命名 — referrer 叠加层。
  4. [04 本章] — 两套搜索的区分 + 全局边界。

8. 全局代码地图(导航索引)

主题文件符号
服务进程组装(注册所有子服务)server/server.goNew / Run
内容寻址 / CIDapi/core/v1/record.goapi/core/v1/cid.go(*Record).GetCid / ConvertDigestToCID
OCI 存储后端server/store/oci/oci.go(*store).Push / Pull / Lookup
Store gRPC 控制器server/controller/store.go(storeCtrl).Push / pushReferrer
路由顶层(Publish/List/Search)server/routing/routing.go(*route) 方法集
远端路由 / 拉取式发现server/routing/routing_remote.go(*routeRemote).handleCIDProviderNotification
增强标签键server/routing/label_utils.goBuildEnhancedLabelKey
本地数据库搜索server/controller/search.go(searchCtlr).SearchCIDs / SearchRecords
查询类型(通配)proto/agntcy/dir/search/v1/record_query.protoRecordQueryType
签名(客户端)client/sign.go(*Client).Sign
命名/校验服务proto/agntcy/dir/naming/v1/naming_service.protoNamingService
后台 reconciler(索引/同步/校验)reconciler/README.mdIndexer / Regsync / Name Task
路由设计权威文档server/routing/ROUTING.md
CLI 子命令注册cli/cmd/root.gocli/cmd/RootCmd.AddCommand