ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

openJiuwen 记忆引擎 SQL 迁移:AddColumnOperation 新增列操作实战指南

openJiuwen 记忆引擎 SQL 迁移:AddColumnOperation 新增列操作实战指南
  • 人工智能
  • AI Agent
  • Agent 框架
  • 工具调用
  • Agent 工作流
  • RAG
  • MCP Clients
  • 强化学习

【免费下载链接】agent-core-java

openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。

项目地址:https://gitcode.com/openJiuwen/agent-core-java
点击查看免费下载

AddColumnOperation是 openJiuwen agent-core-java 记忆(LongTermMemory)SQL 迁移体系中的核心操作对象,它以纯数据对象(DTO)的形式描述一次"新增数据库列"的变更意图。本文将以 AddColumnOperation.md 为骨架,结合memory模块源码、SqlMigrator执行器与测试用例,完整讲解该操作的字段语义、构造方式、执行链路与实战约束,帮助你在为记忆库 SQL 表演进 schema 时正确使用它。

一、AddColumnOperation 是什么

在 openJiuwen 的记忆引擎中,长期记忆数据既可以落盘到 SQL 数据库(如 H2、MySQL、SQLite),也可以存放到向量库或 KV 存储。为了让存储结构随产品迭代平滑演进,com.openjiuwen.memory.migration提供了一套声明式迁移框架:迁移以"操作对象"为单位描述变更,由注册表按版本组织,最终交给对应的 Migrator 执行。

AddColumnOperation就是这套框架中描述SQL 新增列的操作对象。它在 AddColumnOperation.java 中定义,继承自纯 DTO 基类BaseOperation,本身不包含任何执行逻辑,只负责承载"在哪张表、加哪一列、什么类型、是否可空、默认值是什么"这些信息。真正的执行由 SqlMigrator.java 完成。

public class AddColumnOperation extends BaseOperation

从源码注释可以确认,该实现对应 Python 版 openjiuwen 的AddColumnOperation(openjiuwen/core/memory/migration/operation/operations.py),两者保持语义一致。

二、核心字段与语义

AddColumnOperation共持有 5 个业务字段,语义如下:

字段类型说明
tableString目标表名。
columnNameString新增列名。
columnTypeString新增列的 SQL 类型(逻辑类型名,见下方类型映射)。
nullableboolean新增列是否允许NULL。
defaultValueObject新增列的默认值;可为null。

2.1 columnType 的逻辑类型与 SQL 类型映射

columnType传入的是逻辑类型名(如"STRING"、"INTEGER"),SqlMigrator会通过getSqlalchemyType将其转换为具体 SQL DDL 类型。完整映射关系见 SqlMigrator.java:

逻辑类型生成的 SQL 类型
STRING/VARCHARVARCHAR(255)
STRING(128)/VARCHAR(128)VARCHAR(128)(保留参数长度)
INTEGER/INTINTEGER
DATETIME/TIMESTAMPTIMESTAMP
BOOLEAN/BOOLBOOLEAN
TEXT/CLOBCLOB
FLOAT/DOUBLE/REALDOUBLE
其他 / 空值CLOB(兜底)

其中带参数的形式会被解析:STRING(128)会被识别为VARCHAR(128),TEXT则统一映射为CLOB(适配跨数据库方言)。

2.2 nullable 与 defaultValue 的 DDL 语义

  • nullable = false时,生成的 DDL 会附加NOT NULL;
  • defaultValue非空时,生成的 DDL 会附加DEFAULT <值>,且字符串默认值会被单引号包裹并对内部单引号做转义('→''),见 SqlMigrator.java。

例如构造nullable=false, defaultValue=42的新列,最终 SQL 形如:

ALTER TABLE user_message ADD COLUMN default_col INTEGER NOT NULL DEFAULT 42

三、构造方法与访问器

3.1 构造方法

AddColumnOperation提供两个构造方法:

// 便捷构造:默认 nullable = true、defaultValue = null public AddColumnOperation( OperationMetadata metadata, String table, String columnName, String columnType ) // 完整构造 public AddColumnOperation( OperationMetadata metadata, String table, String columnName, String columnType, boolean nullable, Object defaultValue )

其中metadata是 OperationMetadata,封装两个字段:

字段类型说明
schemaVersionint本次操作对应的 schema 版本号,迁移时按此版本过滤与排序。
descriptionString操作描述,可空;为空时BaseOperation.getDescription()回退为类名。

3.2 公开方法

方法说明
public String getTable()返回目标表名。
public String getColumnName()返回新增列名。
public String getColumnType()返回新增列的 SQL 类型。
public boolean isNullable()返回该列是否允许为空。
public Object getDefaultValue()返回默认值对象。

此外,通过基类 BaseOperation.java 还可调用getSchemaVersion()与getDescription(),它们被注册表排序和日志输出使用。

四、完整使用示例:创建并执行一次新增列迁移

4.1 单表迁移

下面以记忆库的user_message表为例,新增一列source(类型STRING、可空、默认值"manual"),并通过SqlMigrator直接执行:

import com.openjiuwen.memory.manage.mem_model.DbModel; import com.openjiuwen.memory.manage.mem_model.SqlDbStore; import com.openjiuwen.memory.migration.migrator.SqlMigrator; import com.openjiuwen.memory.migration.operation.AddColumnOperation; import com.openjiuwen.memory.migration.operation.OperationMetadata; // 1. 创建 AddColumnOperation AddColumnOperation operation = new AddColumnOperation( new OperationMetadata(2, "add source column"), DbModel.USER_MESSAGE_TABLE, // "user_message" "source", "STRING", true, "manual"); // 2. 用 SqlDbStore 构造迁移器并执行 SqlMigrator migrator = new SqlMigrator(sqlDbStore); boolean success = migrator.tryMigrate(DbModel.USER_MESSAGE_TABLE, List.of(operation)).join();

执行成功后,user_message表将新增source VARCHAR(255) DEFAULT 'manual'列,且memory_meta表中该表的schema_version被更新为2。

4.2 通过 RunMigrations 统一执行

如果操作已注册到全局 SQL 注册表,可改用 RunMigrations 的静态入口统一调度:

import com.openjiuwen.memory.migration.MigrationPlan; import com.openjiuwen.memory.migration.RunMigrations; // 注册:以"实体键"为维度组织操作链,实体键需与表配置一一对应 MigrationPlan.getSqlRegistry().register("user_messages", operation); // 启动阶段统一执行 SQL 注册表中的全部迁移 RunMigrations.runSqlMigrations(sqlDbStore).join();

MigrationPlan维护 SQL / 向量 / KV / 消息 / 索引五套注册表(见 MigrationPlan.java),RunMigrations.runSqlMigrations会按实体键顺序逐个调用SqlMigrator,任一实体失败则整体返回失败。

4.3 表白名单约束

SqlMigrator对可迁移的表有白名单限制:仅支持DbModelSupport.MEMORY_TABLES_CONFIG中声明的表,即:

  • user_message(实体键user_messages)
  • scope_user_mapping(实体键scope_user_mapping)

相关校验见 SqlMigrator.java 与 DbModelSupport.java。对白名单之外的表执行迁移会返回false并记录错误日志。

五、注册表、版本链与幂等执行

5.1 OperationRegistry 的版本递增校验

OperationRegistry 以实体键为单位维护"递增的操作链":register时要求新操作的schemaVersion严格大于该实体当前最大版本,否则抛出MEMORY_REGISTER_OPERATION_VALIDATION_INVALID错误。这保证了同一实体的操作可以按版本顺序线性执行。

5.2 tryMigrate 的过滤与幂等

SqlMigrator.tryMigrate的执行逻辑(SqlMigrator.java)如下:

  1. 从memory_meta读取该表当前schema_version;
  2. 过滤掉版本号不高于当前版本的操作(已应用过的操作不会重复执行,天然幂等);
  3. 开启事务,逐个执行待执行操作;
  4. 全部成功后,将目标版本写回memory_meta并commit;
  5. 任一操作失败则rollback并返回false。

测试 SqlMigratorTest.java 中的tryMigrateIsIdempotentForAlreadyAppliedVersion用例验证了同一操作连续执行两次不会产生重复列。

六、源码级执行原理:SqlMigrator 如何消费 AddColumnOperation

当操作链执行到AddColumnOperation时,SqlMigrator的executeOperation会分派到migrateAddColumn(SqlMigrator.java),其 SQL 拼接规则为:

ALTER TABLE <table> ADD COLUMN <columnName> <mappedType> [NOT NULL] [DEFAULT <value>]

其中:

  • 标识符安全校验:table与columnName必须匹配[A-Za-z_][A-Za-z0-9_]*,否则抛出IllegalArgumentException,见requireIdentifier(SqlMigrator.java);
  • 方言探测:迁移器通过 JDBCDriverName识别sqlite/h2/mysql/postgresql,见detectDialect(SqlMigrator.java);
  • 事务保证:迁移在单事务中执行,失败整体回滚,不会留下"列已加但版本未更新"的中间状态。

ALTER TABLE ... ADD COLUMN属于大多数数据库支持的轻量 DDL,因此新增列不需要像 SQLite 改列类型那样重建整表;SQLite 的重建策略(临时表复制数据再重命名)只用于UpdateColumnTypeOperation,与新增列无关。

七、测试用例与可验证依据

memory模块的测试直接验证了AddColumnOperation的完整行为:

  • SqlMigratorTest.javatryMigrateAddsColumnAndUpdatesMetaVersion:新增列并确认memory_meta.schema_version更新;
  • SqlMigratorTest.javatryMigrateAddsColumnWithDefaultValue:验证nullable=false, defaultValue=42的INTEGER NOT NULL DEFAULT 42场景;
  • SqlMigratorTest.javabatchMigrateReturnsPerTableResults:验证批量迁移对user_message与scope_user_mapping两张表分别执行;
  • RunMigrationsTest.javarunSqlMigrationsAppliesRegisteredOperations:验证注册到MigrationPlan.getSqlRegistry()后经RunMigrations.runSqlMigrations完整落地。

这些用例均基于 H2(MODE=MySQL)内存库与 SQLite 运行,可作为理解该操作行为的可靠参考。

八、使用约束与常见注意事项

  1. 版本必须严格递增:同一实体键下注册的多个操作,schemaVersion必须逐步增大,否则注册即失败。
  2. 仅支持白名单表:新增列目标表只能是user_message、scope_user_mapping;自定义表需自行扩展MEMORY_TABLES_CONFIG或走应用层 DDL。
  3. 默认值类型:字符串默认值会自动加单引号并转义内部单引号;数值、布尔值按原样拼接,请确保传入可被目标数据库接受的类型。
  4. 类型映射兜底:无法识别的columnType会映射为CLOB,声明类型前请确认符合预期。
  5. 迁移失败返回false:tryMigrate失败不会抛出异常,而是返回false并记录MEMORY日志;RunMigrations则在任一实体失败时抛出迁移执行错误,便于启动阶段快速感知。

九、扩展阅读

  • BaseOperation.md:所有迁移操作的抽象基类与描述回退规则;
  • OperationMetadata.md:schemaVersion与描述字段;
  • OperationRegistry.md:版本链注册与区间查询;
  • SqlMigrator.md:SQL 迁移执行器行为说明;
  • MigrationPlan.md 与 RunMigrations.md:注册表组织与统一执行入口;
  • operation.README.md:operation 包全部操作类型总览。
  • 人工智能
  • AI Agent
  • Agent 框架
  • 工具调用
  • Agent 工作流
  • RAG
  • MCP Clients
  • 强化学习

【免费下载链接】agent-core-java

openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。

项目地址:https://gitcode.com/openJiuwen/agent-core-java
点击查看免费下载

相关推荐

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进