news 2026/9/10 4:27:12

Milvus 2.0 服务端 RPC 接口全解析:集合、分区、数据写入与索引构建

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Milvus 2.0 服务端 RPC 接口全解析:集合、分区、数据写入与索引构建

Milvus 2.0 服务端 RPC 接口全解析:集合、分区、数据写入与索引构建

【免费下载链接】milvusMilvus is a high-performance, cloud-native vector database built for scalable vector ANN search项目地址: https://gitcode.com/GitHub_Trending/mi/milvus

本指南以 Milvus 2.0 时代开发者文档中的 API Reference(appendix_b_api_reference.md)为主体,系统梳理 Milvus 对外暴露的核心 RPC 服务:从集合定义(CreateCollection)、分区管理、数据写入(Insert)到索引构建(CreateIndex)的完整请求/响应消息结构。读完本文,你将掌握每个 RPC 的接口签名、参数语义、返回结构,并理解这些接口在 Proxy、RootCoord、DataCoord、IndexCoord 等组件间的实际调用链路,可直接用于协议分析、SDK 二次开发或服务端问题排查。

一、RPC 服务总览与文档背景

该文档是 Milvus 2.0 时期的服务端 API 参考,描述的是 Milvus 对外 gRPC 服务的核心接口。当时 Milvus 采用"存算分离 + 四层协调器"架构(Proxy、RootCoord、DataCoord、IndexCoord、QueryCoord),客户端 SDK 通过 gRPC 向 Proxy 发起请求,Proxy 再按请求类型转发给相应协调器。

原文档开篇给出了 22 个核心 RPC 的一览表,全部保留如下:

RPC说明
CreateCollection基于 schema 声明创建集合
DropCollection删除集合
HasCollection检查集合是否存在
LoadCollection将集合加载进内存,供后续检索使用
ReleaseCollection将集合从内存中释放
DescribeCollection查看集合的 schema 及其描述性统计信息
GetCollectionStatistics查看集合的统计信息
ShowCollections列出所有集合
CreatePartition创建分区
DropPartition删除分区
HasPartition检查分区是否存在
LoadPartition将分区加载进内存,供后续检索使用
ReleasePartitions将分区从内存中释放
GetPartitionStatistics查看分区的统计信息
ShowPartitions列出集合下的所有分区
CreateIndex为集合中的字段创建索引
DescribeIndex获取集合中某字段的索引详情
GetIndexStates获取索引构建状态
DropIndex删除集合中某字段的指定索引
Insert向集合或分区批量插入行数据
Search使用 ANNS 语句与布尔表达式查询集合或分区的列
Flush将内存中的数据持久化存储

从当前仓库的 proto 定义看,这些 RPC 的职责划分依然清晰:DDL 类(CreateCollection、DropCollection、HasCollection、DescribeCollection、ShowCollections、CreatePartition、DropPartition、HasPartition、ShowPartitions)定义在 root_coord.proto 的RootCoordservice 中(第 23-144 行);索引类定义在 index_coord.proto 的IndexCoordservice 中(第 12-23 行)。而客户端实际调用的是MilvusService(来自外部 milvus-proto 仓库,见 how_to_develop_with_local_milvus_proto.md),Proxy 侧实现位于 internal/proxy/impl.go,例如Insert(第 2474 行)、Search(第 2869 行)、Flush(第 3707 行)。

二、公共基础结构:MsgBase 与通用响应

2.1 MsgBase:每个请求的"身份证"

MsgBase是每个请求中携带的基础结构体,用于消息队列层面的消息标识与溯源:

message MsgBase { MsgType msg_type = 1; int64 msgID = 2; uint64 timestamp = 3; int64 sourceID = 4; }
  • MsgType:枚举类型,用于区分消息队列中的不同消息类型,如插入消息(insert msg)、检索消息(search msg)等;
  • msgID:消息的唯一标识符;
  • timestamp:消息生成的时间戳;
  • sourceID:消息来源的唯一标识。

在 2.0 时代的 Go 接口定义中,对应结构为commonpb.MsgBase,见 chap06_root_coordinator.md 中RootCoord接口的注释:CreateCollectionDropCollectionHasCollectionDescribeCollection等所有 DDL 请求都要求携带MsgBase,且不少接口支持"在指定时间戳查询"的语义(specified timestamp)。

2.2 Status:服务端错误码载体

几乎每个 RPC 的响应都包含common.Status。它不含 gRPC 层面的错误,而是承载服务端业务错误码:

message Status { ErrorCode error_code = 1; string reason = 2; }
  • error_code:枚举类型,区分执行错误的类型,完整枚举见原文档附录 D(appendix_d_error_code.md),包括Success=0CollectionNotExists=4IllegalArgument=5IllegalDimension=7IllegalIndexType=8IllegalCollectionName=9IllegalTOPK=10BuildIndexError=21IllegalMetricType=23IndexNotExist=25DDRequestRace=1000等;
  • reason:描述详细错误的字符串。

需要说明的是:附录 D 已在当前仓库中被标注为 Deprecated(⚠️),现代版本改用merrsentinel 错误码体系(如ParameterInvalid=1100ServiceInternal=5FunctionFailed=2400),通过commonpb.Status.Code传递,权威定义在 pkg/util/merr/errors.go。从 root_coord.go 的CreateCollection实现可以印证这一演变:现代实现统一通过merr.Status(err)包装错误、merr.Success()表示成功。

2.3 BoolResponse:布尔查询的响应包装

对于HasCollectionHasPartition这类存在性检查接口,返回的是带 status 的布尔响应:

message BoolResponse { common.Status status = 1; bool value = 2; }

value即查询结果:存在为true,不存在为false

三、集合定义类 RPC(Definition Requests)

3.1 CreateCollection:创建集合

接口签名:

rpc CreateCollection(CreateCollectionRequest) returns (common.Status){}

请求结构:

message CreateCollectionRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; // `schema` 是序列化后的 `schema.CollectionSchema` bytes schema = 4; }

CreateCollectionRequest包含MsgBasedb_namecollection_name以及序列化的集合 schema。服务端会创建与collection_name同名的集合。值得注意:schema 以bytes序列化字节形式传输,这是早期版本的典型做法,现代版本的请求中还增加了shards_numconsistency_levelproperties等字段。

CollectionSchema 结构:

message CollectionSchema { string name = 1; string description = 2; bool autoID = 3; repeated FieldSchema fields = 4; }

集合 schema 包含集合的全部基础信息:

  • name / description:集合名称与描述(描述由数据库管理员定义);
  • autoID:决定每行数据的 ID 是否由用户自定义。为true时系统自动为每条数据生成唯一 ID;为false时用户必须在插入时为每条实体指定 ID;
  • fieldsFieldSchema列表。

FieldSchema 结构:

message FieldSchema { int64 fieldID = 1; string name = 2; bool is_primary_key = 3; string description = 4; DataType data_type = 5; repeated common.KeyValuePair type_params = 6; repeated common.KeyValuePair index_params = 7; }

字段 schema 包含字段的全部基础信息:

  • fieldID / name / description:字段 ID、名称、描述;
  • is_primary_key:是否主键;
  • data_type:枚举类型,区分不同数据类型(如 FloatVector、Int64、Float 等),完整枚举见原文档末尾附录;
  • type_params:data_type 的详细参数。例如向量类型需要指定维度信息,传入<dim, 8>键值对即可让该字段存储 8 维向量;
  • index_params:为加速检索而构建索引时的索引详细信息。

底层实现链路:从 root_coord.go 可以看到,现代 RootCoord 收到CreateCollection后先做健康检查(merr.CheckHealthy)、记录指标(RootCoordDDLReqCounter),随后调用broadcastCreateCollectionV1落库元数据;若集合已存在且 schema 相同,则直接忽略并返回成功。整体流程如下图所示(来源:figs/root_coord_create_collection.png):

3.2 DropCollection:删除集合

接口签名:

rpc DropCollection(DropCollectionRequest) returns (common.Status) {}

请求结构:

message DropCollectionRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; }

与同名collection_name对应的集合将被删除。在现代 RootCoord 中对应 root_coord.go 的DropCollection实现。

3.3 HasCollection:检查集合是否存在

接口签名:

rpc HasCollection(HasCollectionRequest) returns (BoolResponse) {}

请求结构:

message HasCollectionRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; }

服务端通过collection_name查找集合并判断是否存在,结果通过BoolResponse.value返回。在 root_coord.proto 中该 RPC 同样定义在RootCoordservice 内。

3.4 LoadCollection:将集合加载进内存

接口签名:

rpc LoadCollection(LoadCollectionRequest) returns (common.Status) {}

请求结构:

message LoadCollectionRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; }

与同名collection_name对应的集合将被加载进内存以备检索。在现代版本中该操作已演化为异步任务式接口(Go SDK 中LoadCollection返回可Await的任务,见 client/milvusclient/collection.go),并且响应会携带加载进度信息。

3.5 ReleaseCollection:释放集合

接口签名:

rpc ReleaseCollection(ReleaseCollectionRequest) returns (common.Status) {}

请求结构:

message ReleaseCollectionRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; }

与同名collection_name对应的集合将从内存中释放,释放后无法直接检索,需重新 Load。

3.6 DescribeCollection:获取集合 schema

接口签名:

rpc DescribeCollection(DescribeCollectionRequest) returns (CollectionDescription) {}

请求结构:

message DescribeCollectionRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; int64 collectionID = 4; }

服务端通过collection_name查找集合并返回详细信息;collectionID供内部组件按 ID 获取集合详情。

响应结构:

message DescribeCollectionResponse { common.Status status = 1; schema.CollectionSchema schema = 2; int64 collectionID = 3; }

schema与 CreateCollection 中的 CollectionSchema 相同。在现代 Go SDK 中,DescribeCollection会把响应解析为entity.Collection(含 ID、Schema、物理/虚拟通道、分片数、一致性级别、属性等),见 client/milvusclient/collection.go。

3.7 GetCollectionStatistics:获取集合统计信息

接口签名:

rpc GetCollectionStatistics(GetCollectionStatisticsRequest) returns (GetCollectionStatisticsResponse) {}

请求结构:

message GetCollectionStatisticsRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; }

响应结构:

message GetCollectionStatisticsResponse { common.Status status = 1; repeated common.KeyValuePair stats = 2; }

stats是保存各类统计信息的键值对列表,例如可通过 key'row_count'获取集合的行数。该统计逻辑在现代由 DataCoord 提供(见 data_coord.proto 中的GetCollectionStatistics)。

3.8 ShowCollections:列出所有集合

接口签名:

rpc ShowCollections(ShowCollectionsRequest) returns (ShowCollectionsResponse) {}

请求结构:无参数(早期版本)。在现代版本中增加了db_nametypecollection_names等字段以支持按库/按类型过滤。

响应结构:

message ShowCollectionsResponse { common.Status status = 1; repeated string collection_names = 2; }

collection_names为包含所有集合名称的列表。Go SDK 中ListCollections即直接调用ShowCollections并读取resp.GetCollectionNames(),见 client/milvusclient/collection.go。

四、分区管理类 RPC

分区(Partition)是集合内按逻辑划分的子集,可用于按租户或业务维度隔离数据、缩小检索范围。

4.1 CreatePartition / DropPartition

接口签名:

rpc CreatePartition(CreatePartitionRequest) returns (common.Status) {} rpc DropPartition(DropPartitionRequest) returns (common.Status) {}

请求结构(两者相同字段布局):

message CreatePartitionRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; string partition_name = 4; } message DropPartitionRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; string partition_name = 4; }

CreatePartition在名为collection_name的集合中创建名为partition_name的分区;DropPartition删除集合中同名分区。现代 RootCoord 实现见 root_coord.go 的CreatePartition,并提供了返回 partitionID 的CreatePartitionV2变体(见 root_coord.proto)。

4.2 HasPartition:检查分区是否存在

接口签名:

rpc HasPartition(HasPartitionRequest) returns (BoolResponse) {}

请求结构:

message HasPartitionRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; string partition_name = 4; }

判断同名partition_name分区是否存在于collection_name集合中,结果由BoolResponse.value承载(存在为true)。

4.3 LoadPartitions / ReleasePartitions:批量加载与释放分区

接口签名:

rpc LoadPartitions(LoadPartitionsRequest) returns (common.Status) {} rpc ReleasePartitions(ReleasePartitionsRequest) returns (common.Status) {}

请求结构(两者相同):

message LoadPartitionsRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; repeated string partition_names = 4; }

partition_names是分区名列表;这些集合中的分区将被一次性加载进内存(Load)或从内存释放(Release)。注意原文档中 Load 版本为复数LoadPartitionsRequest,一次可加载多个分区。

4.4 GetPartitionStatistics:获取分区统计信息

接口签名:

rpc GetPartitionStatistics(GetPartitionStatisticsRequest) returns (GetPartitionStatisticsResponse) {}

请求结构:

message GetPartitionStatisticsRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; string partition_name = 4; }

服务端通过partition_namecollection_name集合中定位分区并返回其统计信息。响应结构与集合统计一致,stats同样是键值对列表,可通过'row_count'获取分区行数:

message GetPartitionStatisticsResponse { common.Status status = 1; repeated common.KeyValuePair stats = 2; }

4.5 ShowPartitions:列出集合下所有分区

接口签名:

rpc ShowPartitions(ShowPartitionsRequest) returns (StringListResponse) {}

请求结构:

message ShowPartitionsRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; int64 collectionID = 4; }

响应结构:

message ShowPartitionsResponse { common.Status status = 1; repeated string partition_names = 2; repeated int64 partitionIDs = 3; }
  • partition_names:所有分区的名称列表;
  • partitionIDs:所有分区的 ID 列表,与partition_names按下标一一对应。

五、数据写入类 RPC(Manipulation Requests)

5.1 Insert:批量插入数据

接口签名:

rpc Insert(InsertRequest) returns (InsertResponse){}

请求结构:

message InsertRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; string partition_name = 4; repeated common.Blob row_data = 5; repeated uint32 hash_keys = 6; } message Blob { bytes value = 1; }

将一批row_data(每条为Blob,即字节值)插入到collection_name集合的partition_name分区中;hash_keys用于数据分片路由(hash 到不同分片)。

响应结构:

message InsertResponse { common.Status status = 1; int64 rowID_begin = 2; int64 rowID_end = 3; }

rowID_beginrowID_end是本次插入数据分配到的 ID 区间,SDK 可据此确认数据已成功分配主键。现代版本的InsertResponse已扩展为MutationResult(含IDstimestampsucc_index等),对应实现见 internal/proxy/impl.go 的Proxy.Insert

5.2 Delete:按 ID 删除

原文档中 Delete 部分仅列出DeleteByID一个入口,未展开消息定义。在现代 Milvus 中删除请求已演进为支持过滤表达式(DeleteRequest携带expr字段),由 Proxy 处理后写入消息流,最终由 DataNode 在 L0 段或删除日志中落地。

六、索引构建类 RPC(Index)

索引用于加速向量近似检索(ANNS)。以下四个 RPC 构成索引的完整生命周期:创建 → 查看详情 → 查询构建状态 → 删除。索引类型与参数的详细说明见原文档第 2.2.3 章对应的 chap03_index_service.md。

6.1 CreateIndex:创建索引

接口签名:

rpc CreateIndex(CreateIndexRequest) returns (common.Status){}

请求结构:

message CreateIndexRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; string field_name = 4; repeated common.KeyValuePair extra_params = 5; }

collection_name集合中名为field_name的字段创建索引。extra_params用于指定索引详细信息(索引类型、metric type、nlist 等)。

底层实现链路:从 internal/proxy/impl.go 的Proxy.CreateIndex到 IndexCoord 的CreateIndex(见 index_coord.proto),最终由 DataCoord 调度索引构建任务。现代IndexCoord.CreateIndexRequest已改用collectionID/fieldID定位字段,并携带type_paramsindex_paramsindex_nameis_auto_index等字段(见 index_coord.proto)。完整调用流程如下图所示(来源:figs/root_coord_create_index.png):

6.2 DescribeIndex:获取索引详情

接口签名:

rpc DescribeIndex(DescribeIndexRequest) returns (common.Status){}

请求结构:

message DescribeIndexRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; string field_name = 4; string index_name = 5; }

获取collection_name集合中field_name字段的索引详情。index_name语义:一个字段可创建多个索引,可通过 index_name 指定具体索引。

响应结构:

message DescribeIndexResponse { common.Status status = 1; repeated IndexDescription index_descriptions = 2; } message IndexDescription { string index_name = 1; int64 indexID = 2; repeated common.KeyValuePair params = 3; }

index_descriptions为索引描述列表:若请求中指定了 index_name,列表长度将为 0(按原文档语义应为返回该索引的描述);若 index_name 为空,则返回集合字段上的全部索引。params为索引详细参数。

在现代实现中该接口由 IndexCoord 提供(见 index_coord.proto),响应DescribeIndexResponse携带完整的IndexInfo(含indexed_rowstotal_rowsstateindex_state_fail_reason等索引构建进度与状态信息)。

6.3 GetIndexStates:获取索引构建状态

接口签名:

rpc GetIndexStates(GetIndexStatesRequest) returns (GetIndexStatesRequest){}

注:原文档此处返回值类型误写为请求类型,实际应为GetIndexStatesResponse

请求结构:

message GetIndexStatesRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; string field_name = 4; string index_name = 5; }

获取collection_name集合中field_name字段的索引构建进度信息。index_name用于指定要查询状态的索引。

响应结构:

message GetIndexStatesResponse { common.Status status = 1; common.IndexState state = 2; } enum IndexState { IndexStateNone = 0; Unissued = 1; InProgress = 2; Finished = 3; Failed = 4; Deleted = 5; }

index state是区分索引构建不同阶段的枚举:

  • IndexStateNone:无状态(默认值);
  • Unissued:任务尚未下发;
  • InProgress:构建中;
  • Finished:构建完成;
  • Failed:构建失败;
  • Deleted:索引已删除。

在现代 IndexCoord 中,该能力由GetIndexState(已标记 Deprecated,见 index_coord.proto)演变为DescribeIndex返回的IndexInfo.state字段,状态枚举同样定义于common.IndexState

6.4 DropIndex:删除索引

接口签名:

rpc DropIndex(DropIndexRequest) returns (common.Status){}

请求结构:

message DropIndexRequest { common.MsgBase base = 1; string db_name = 2; string collection_name = 3; string field_name = 4; string index_name = 5; }

删除collection_name集合中field_name字段的指定索引。index_name用于指定删除哪个索引(一个字段可建多个索引)。现代 IndexCoord 的DropIndex定义见 index_coord.proto,请求结构已改为collectionID + index_name + drop_all定位索引。

七、从源码看 RPC 的落地与演变

原文档是 2.0 时代的接口快照,结合当前仓库源码可以更完整地理解这些 RPC 的职责边界与现代演变:

  1. 职责划分保持稳定:DDL 与分区管理由 RootCoord 承接,索引生命周期由 IndexCoord 承接,写入与检索由 Proxy 承接。可在 root_coord.proto 与 index_coord.proto 中逐一核对。

  2. 定位字段从名称转向 ID:现代内部 proto 大量使用collectionIDfieldIDsegmentID替代名称定位(如 index_coord.proto 的DescribeIndexRequest),减少跨组件解析开销、避免重名歧义。

  3. 同步接口向异步任务演进:LoadCollection、CreateIndex 在现代版本中普遍返回异步任务(Go SDK 中通过task.Await(ctx)等待完成,见 client/milvusclient/collection.go),并携带进度信息(如IndexInfo.indexed_rows/total_rows)。

  4. 错误处理统一收敛:2.0 的ErrorCode枚举已被merrsentinel 体系取代,RPC 返回统一通过commonpb.Status.Code + Reason表达业务错误(见 pkg/util/merr/errors.go)。

  5. 协议定义外置:对外MilvusService的 proto 定义位于独立的 milvus-proto 仓库,通过go.mod引入(github.com/milvus-io/milvus-proto/go-api/v3)。本地调试或新增 API 时可参考 how_to_develop_with_local_milvus_proto.md 使用go mod edit -replace指向本地 proto 仓库。

八、结语

本文完整继承了 Milvus 2.0 API Reference 中 22 个核心 RPC 的接口签名、请求/响应消息结构与字段语义,并结合当前仓库的 pkg/proto 定义、internal/rootcoord/root_coord.go、internal/proxy/impl.go 与 client/milvusclient 的现代实现,勾勒出这些接口从 2.0 到现代的演进脉络。对于需要做协议层开发、SDK 移植或排查 DDL/索引构建问题的开发者,这张接口地图可以作为快速定位的起点;更深入的系统架构背景可继续阅读同目录下的 chap01_system_overview.md、chap06_root_coordinator.md 与 chap03_index_service.md。

【免费下载链接】milvusMilvus is a high-performance, cloud-native vector database built for scalable vector ANN search项目地址: https://gitcode.com/GitHub_Trending/mi/milvus

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 4:27:06

Keras图像分类调优七开关:让CNN稳定识别T恤与外套

1. 这不是在调参&#xff0c;是在给AI做“触觉训练”你有没有试过把一件T恤和一件牛仔外套同时塞进洗衣机&#xff1f;机器不会管你是棉质还是牛仔布&#xff0c;它只认“一堆能转的软东西”。但人眼一扫就知道&#xff1a;袖子长度、领口形状、下摆是否收束、肩线是否硬挺——…

作者头像 李华
网站建设 2026/9/10 4:26:17

STM32C5轮询读取LSM6DSV320X陀螺仪的原理与实战避坑指南

1. 这不是“跑个例程”那么简单&#xff1a;为什么轮询读LSM6DSV320X是STM32C5项目里最常踩坑的起点你手头刚拿到一块崭新的STM32C5开发板&#xff0c;芯片丝印清晰&#xff0c;配套的LSM6DSV320X传感器模块也焊得工整。你打开CubeMX&#xff0c;勾选IC1&#xff0c;生成初始化…

作者头像 李华
网站建设 2026/9/10 4:25:52

STM32F103RC全桥驱动死区PWM配置与补偿解析

简介&#xff1a;面向STM32全桥驱动与电机控制场景的PWM死区实验资源&#xff0c;使用Keil开发环境基于STM32F103RC寄存器方式实现四路带死区PWM输出&#xff0c;适合嵌入式初学者及需要理解死区配置、TIM定时器寄存器操作的开发者。压缩包共60个文件&#xff0c;以h头文件、c源…

作者头像 李华
网站建设 2026/9/10 4:25:10

考虑需求响应的电热综合能源系统两阶段优化调度及Matlab实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 4:24:57

AI Agent记忆系统实战:从架构到遗忘机制

你有没有过这种体验&#xff1a;昨天刚和某个AI助手聊完旅行计划&#xff0c;今天再打开它&#xff0c;对方一脸无辜地反问“你想去哪儿玩来着”。如果你只是个普通用户&#xff0c;顶多吐槽一句“人工智障”&#xff1b;但如果你正在做AI Agent开发&#xff0c;这种“金鱼记忆…

作者头像 李华