はじめに
七尾百合子さん、お誕生日 323日目 おめでとうございます! nikkie(望月杏奈1)です。
ADK 1系のロマンあふれるバージョニングに対抗する話です
目次
- はじめに
- 目次
- Agent Development Kit の session
- 「ADK をバージョンアップしようかな」が招く悲劇
- ADK はマイナーバージョンアップで DB テーブル定義を(サイレントに)変更😫
- 私は、テーブル定義の差分を知りたい!!
- v1.22.0 で大きな変更が入っている!
- 終わりに
Agent Development Kit の session
session は ADK における大事な概念の1つで、ユーザとエージェントの会話スレッドです。
https://google.github.io/adk-docs/sessions/#core-concepts
Represents a single, ongoing interaction between a user and your agent system.
session は session service によって管理されます。
https://google.github.io/adk-docs/sessions/session/#managing-sessions-with-a-sessionservice
ADK 全体のフローを表すシーケンス図があるのですが、session service がこまめに session を永続化しています。
https://google.github.io/adk-docs/runtime/event-loop/#how-it-works-a-simplified-invocation
ADK には session service の実装が複数用意されています。
https://google.github.io/adk-docs/sessions/session/#sessionservice-implementations
有力な選択肢は、VertexAiSessionServiceとDatabaseSessionServiceかと思います。
VertexAiSessionService:Vertex AI Agent Engine を session service に使うDatabaseSessionService:PostgreSQL や MySQL データベースを session service に使う
(他に開発中に便利なInMemorySessionServiceと、現時点でドキュメントにはないSqliteSessionService2があります)
なお、A2A では Vertex AI Agent Engine を session service に指定できません(プルリクエスト出してます)
「ADK をバージョンアップしようかな」が招く悲劇
SemVer を前提にしていたらあってはならないことだと思うのですが、悲劇に見舞われます。
簡易的なコマンドで示しましょう
データベースを session service として、ADK のエージェントを API としてサーブし始めました。
% docker run --name adk-pg -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres:18
% uvx --python 3.13 --from google-adk==1.13.0 --with psycopg --with greenlet adk api_server --session_service_uri postgresql+psycopg://postgres:mysecretpassword@localhost:5432/postgres
session が蓄積されていきます
% curl -X POST http://127.0.0.1:8000/apps/my_agent/users/test_user/sessions -H 'Content-Type: application/json' -d '{}'
ADK は執筆時点では2週に1回のリリースです。
ある程度期間が経過した後、バージョンアップをしようと思い立ちます(サーバの立ち上げ直しで表します)
% uvx --python 3.13 --from google-adk==1.18.0 --with psycopg --with greenlet adk api_server --session_service_uri postgresql+psycopg://postgres:mysecretpassword@localhost:5432/postgres
バージョンアップ後の ADK のサーバで、session は作れます。
% curl -X POST http://127.0.0.1:8000/apps/my_agent/users/test_user/sessions -H 'Content-Type: application/json' -d '{}'
{"id":"6f8a469b-102f-4f4f-8190-f3f8052a479b","appName":"my_agent","userId":"test_user","state":{},"events":[],"lastUpdateTime":1770088107.369811}
ところが/runでエージェントを呼び出すと...
% curl http://localhost:8000/run \
--json '{
"app_name": "my_agent",
"user_id": "test_user",
"session_id": "6f8a469b-102f-4f4f-8190-f3f8052a479b",
"new_message": {
"role": "user",
"parts": [
{"text": "こんにちは"}
]
}
}'
Internal Server Error
サーバ側で出力されたエラーです
sqlalchemy.exc.ProgrammingError: (psycopg.errors.UndefinedColumn) column events.custom_metadata does not exist
LINE 1: ....grounding_metadata AS events_grounding_metadata, events.cus...
^
[SQL: SELECT events.id AS events_id, events.app_name AS events_app_name, events.user_id AS events_user_id, events.session_id AS events_session_id, events.invocation_id AS events_invocation_id, events.author AS events_author, events.actions AS events_actions, events.long_running_tool_ids_json AS events_long_running_tool_ids_json, events.branch AS events_branch, events.timestamp AS events_timestamp, events.content AS events_content, events.grounding_metadata AS events_grounding_metadata, events.custom_metadata AS events_custom_metadata, events.usage_metadata AS events_usage_metadata, events.citation_metadata AS events_citation_metadata, events.partial AS events_partial, events.turn_complete AS events_turn_complete, events.error_code AS events_error_code, events.error_message AS events_error_message, events.interrupted AS events_interrupted
FROM events
WHERE events.app_name = %(app_name_1)s::VARCHAR AND events.user_id = %(user_id_1)s::VARCHAR AND events.session_id = %(session_id_1)s::VARCHAR ORDER BY events.timestamp DESC]
[parameters: {'app_name_1': 'my_agent', 'user_id_1': 'test_user', 'session_id_1': '6f8a469b-102f-4f4f-8190-f3f8052a479b'}]
(Background on this error at: https://sqlalche.me/e/20/f405)
この例外はランタイムで発生するので、動作確認を怠って気づけないと痛い目を見ます。
session は作れるんですよ。/runが常に落ちます
ADK はマイナーバージョンアップで DB テーブル定義を(サイレントに)変更😫
上記の原因は、テーブル定義が更新されたためです。
1.13.0 -> 1.18.0 とマイナーバージョンアップをしたときに、ADK のソース中のテーブル定義(SQLAlchemy 使用)が更新されているんですよね。
新しいコードで期待するテーブルになっていないので実行時にエラーになります。
テーブル定義の更新は、私が観測した範囲ではこのあたりです。
- 1.14.0
NOTE: This requires DB migration, run
ALTER TABLE events ADD COLUMN custom_metadata JSON;to migrate existing database tables.
- 1.17.0
- (無言で変更っぽいです👹)
- 1.19.0
Add transcription fields to session events
- 1.22.0
Introduce new JSON-based database schema for DatabaseSessionService, which will be used for newly-created databases. A migration command and script are provided.
私は、テーブル定義の差分を知りたい!!
この事象をたびたび踏むので、対策を考えました。
gpt-5.2-codex に作業3させて用意したのがこちらのリポジトリです。
各バージョンでテーブル定義を dump して公開しています。
元々 sqldef というツールを知っていました。
現在の DB(AS IS)と変えた後の DB(TO BE)のスキーマから、ALTER TABLEだけ自動生成して適用できます。
ALTER TABLEが自動でできるの、めっちゃ便利! 天才!!
% psqldef --version 3.9.4 (d9016f9) % psqldef -h localhost -p 5432 -U postgres -W mysecretpassword --dry-run postgres < schemas/v1.18.0/postgresql.sql -- dry run -- BEGIN; ALTER TABLE "public"."events" ADD COLUMN "custom_metadata" jsonb; ALTER TABLE "public"."events" ADD COLUMN "usage_metadata" jsonb; ALTER TABLE "public"."events" ADD COLUMN "citation_metadata" jsonb; COMMIT;
--dry-runを外すとALTER TABLEが実行されます(Apply)。
テーブル定義がコードの期待と合うので、/runが正常に動作するようになります。
ALTER TABLEを見ていたら、session は作れて/runが落ちる理由が分かりました。
session のテーブルに変更はありません。
/runで session を取得するのですが、合わせて取る event でテーブル定義の変更があるのです。
ref: https://github.com/google/adk-python/blob/v1.23.0/src/google/adk/sessions/database_session_service.py#L317-L322
v1.22.0 で大きな変更が入っている!
バージョン間を比較できたことで気づきました。
% psqldef schemas/v1.21.0/postgresql.sql < schemas/v1.22.0/postgresql.sql
-- dry run --
BEGIN;
CREATE TABLE "public"."adk_internal_metadata" (
"key" character varying(128) NOT NULL,
"value" character varying(256) NOT NULL,
PRIMARY KEY ("key")
);
ALTER TABLE "public"."events" ADD COLUMN "event_data" jsonb;
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "output_transcription";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "input_transcription";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "interrupted";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "error_message";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "error_code";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "turn_complete";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "partial";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "citation_metadata";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "usage_metadata";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "custom_metadata";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "grounding_metadata";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "content";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "branch";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "long_running_tool_ids_json";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "actions";
-- Skipped: ALTER TABLE "public"."events" DROP COLUMN "author";
COMMIT;
adk_internal_metadataテーブルで v0 スキーマと v1 スキーマを区別し始めたようです。
eventsテーブルは劇的に変更されています(全カラム削除して、event_dataだけにする)。
上で引いたリリースノートによれば、マイグレーション方法が提供されているそうです(未検証です)
終わりに
ADK は1系のマイナーバージョンアップにテーブル定義の変更をたびたび忍ばせてきます。
session service にデータベースを使っていると、ADK をバージョンアップした後にテーブル定義が古いままなために壊れます。
痛い目を見たので、ADK の各バージョンのテーブル定義を公開しました。
sqldef でALTER TABLEを確認できます。
これで対抗できると感じた方はぜひ使ってみてください!
ADKは1系ですが、LangChain 0.0.x系と同じくらい、APIが安定しないんですよね。
— nikkie(にっきー) / にっP (@ftnext) 2026年2月1日
「experimentalと言うだけで、どんな破壊的変更も通る」とADK開発陣は思っているように見えちゃいます。
ADKは偽りの1系。LangChain 0.0.x系の方がセマンティックバージョンニングできてましたね
-
姉妹になりました
↩皆さまへ
— nikkie(にっきー) / にっP (@ftnext) 2026年2月2日
私は今日から望月杏奈ちゃんになります。
どうぞよろしくお願いいたしますhttps://t.co/R26UkdoyfB
応援ください!!!!!!
百合子お姉様♡ https://t.co/I79z3IWweO - 直近で取り上げました ↩
- 多くの学びを得たので後日エントリにしたいと思っています↩