This is my life.

There are many like it, but this one is mine.

AIエージェントの作業ログ、ちゃんと残してますか?

こんな経験ありませんか?

Claude Code にイシューを渡したら勝手にブランチを切ってコミットして PR を出してくれる。Codex が並行でテストを回してレビューコメントを書く。Gemini に設計の壁打ちをしてもらう。最近の個人開発はこんな感じで回っています。

AI がコードを書くだけだった時代はまだよかったんです。Copilot がサジェストを出して、人間が採用するかどうか判断する。作業の主体はあくまで人間でした。

でも今は違います。AI がエージェントとして自律的にシェルコマンドを実行して、ファイルを編集して、Git 操作までやる。しかもそれが複数の AI で同時並行で動く。こうなると、ふとこう思うわけです。

「さっき何やったっけ?」

  • /clear したら直前のセッション内容が消えた
  • Git の diff を見ても「なぜこの変更をしたか」がわからない
  • Claude がやった作業と Codex がやった作業が混ざって追えない
  • シェルコマンドの実行結果が、どのセッションのどのタスクに紐づくのかわからない

AI がエージェントとして作業するようになって、「誰が・いつ・なぜ・何をしたか」を追うのが急に難しくなりました。Git は「何が変わったか」は教えてくれます。でも「なぜそう判断したか」「どのコマンドを実行して確認したか」「そもそもどの AI がやったのか」までは残してくれません。

Traceary を作りました

github.com

Traceary は、AI エージェントの作業ログをローカルの SQLite に記録・検索する CLI & MCP サーバーです。

何を記録するか

種類
ノート 「#170 の調査開始」「設計方針を変更した理由」
セッション境界 セッション開始/終了のタイムスタンプ
コマンド実行ログ 実行したシェルコマンド + 入力 + 出力
付加情報 どの AI が実行したか(Claude/Codex/Gemini)、session_id、リポジトリ

ポイントは ローカルファースト であること。データは全て手元の SQLite に入ります。テレメトリも外部送信も一切ありません。

アーキテクチャ

各 AI ホストのプラグイン/拡張が hooks で Traceary にイベントを送り、Traceary がローカルの SQLite に書き込む。シンプルです。

導入してみる

1. CLI をインストール

# Homebrew
brew tap duck8823/traceary https://github.com/duck8823/traceary
brew install traceary

# or go install
go install github.com/duck8823/traceary@latest

2. Claude Code プラグインをインストール

claude plugins marketplace add https://github.com/duck8823/traceary
claude plugins install traceary@traceary-plugins --scope user

これだけです。次のセッションから、シェルコマンドの実行が自動で記録されます。

Codex や Gemini CLI 用のプラグインもあります。詳しくは ネイティブ統合ガイド を見てください。

実際に使ってみた:v0.1.16 のスプリント

ここからが本題です。Traceary を導入した状態で、Traceary 自身の v0.1.16 リリースまでのスプリントを回してみました。

やったこと

  1. コードベースを調査して課題を4つ洗い出し
  2. GitHub Issues にサブイシュー階層で登録
  3. Claude(メイン)+ Codex(並列ワーカー)で4イシューを実装
  4. Multi-AI レビュー → マージ
  5. 品質フェーズ(アーキテクチャレビュー + テストカバレッジ監査)
  6. リリース PR → タグ打ち

これを 1セッション でやりました。

記録されたもの

セッション中に実行された約100コマンドが全て記録されています。Claude と Codex が並行で作業していた時間帯を抜粋するとこんな感じです。

TIMESTAMP              CLIENT   AGENT   COMMAND
2026-04-09T10:19:59Z   hook     claude  git add presentation/cli/context_command.go && git commit ...
2026-04-09T10:19:57Z   hook     codex   session ended
2026-04-09T10:19:52Z   hook     claude  go build ./... && go test ./presentation/cli/...
2026-04-09T10:19:42Z   hook     codex   git diff --stat -- main.go main_test.go presentation/cli/...
2026-04-09T10:19:32Z   hook     codex   nl -ba presentation/cli/backup.go | sed -n '40,130p'
2026-04-09T10:19:25Z   hook     claude  git checkout maintenance/session-resolution-debug-logging
2026-04-09T10:19:11Z   hook     codex   git status --short
2026-04-09T10:19:02Z   hook     codex   git diff -- main.go main_test.go presentation/cli/...
2026-04-09T10:18:57Z   hook     codex   go test ./...
2026-04-09T10:17:49Z   hook     codex   rg -n "newBackupCommand|newHooksCommand|..." presentation/cli

Claude がコミット・ブランチ切り替えをしている横で、Codex がコードを読んでテストを回している。同じタイムラインに並ぶので、「この時間帯に何が起きていたか」が一目でわかります。

コマンドの入出力も残る

traceary show 2dc62ab5d18be1cfc66522862a58f87b --json
{
  "event": {
    "event_id": "2dc62ab5d18be1cfc66522862a58f87b",
    "kind": "command_executed",
    "client": "hook",
    "agent": "claude",
    "session_id": "f1d095a5-1357-49ae-aaa3-79f0fa223815",
    "repo": "github.com/duck8823/traceary",
    "message": "python3 scripts/verify_integrations.py && ... && go test ./...",
    "created_at": "2026-04-09T10:44:47Z"
  },
  "command_audit": {
    "command": "python3 scripts/verify_integrations.py && ... && go test ./...",
    "input": "{\"command\":\"...\",\"description\":\"Verify everything\",\"timeout\":60000}",
    "output": "{\"stdout\":\"ok: integration manifests and packaged assets are consistent\\ndocumentation i18n check passed\\nok  github.com/duck8823/traceary/presentation  (cached)\\n...\"}",
    "input_truncated": false,
    "output_truncated": false
  }
}

「検証コマンドを実行した」だけじゃなくて、「その時 integration チェックと i18n チェックとテストが全部通ったのか」まで残ります。後から振り返るときにこれがあるとないとではだいぶ違います。

MCP 経由でも使える

Traceary は MCP サーバーとしても動くので、Claude Code のセッション内から自然言語で聞くだけで過去のログを引けます。

実際にこの記事を書いているセッションで「直近のイベントを表示して」と聞いたら、Claude が mcp__plugin_traceary_traceary__list_events を呼び出してこんな結果を返してくれました。

{
  "events": [
    {
      "agent": "claude",
      "body": "traceary show 2dc62ab5d18be1cfc66522862a58f87b --json ...",
      "client": "hook",
      "created_at": "2026-04-09T11:10:52Z",
      "kind": "command_executed",
      "repo": "github.com/duck8823/traceary",
      "session_id": "f1d095a5-1357-49ae-aaa3-79f0fa223815"
    },
    {
      "agent": "claude",
      "body": "traceary list --limit 200 | grep \"codex\" | head -10",
      "client": "hook",
      "created_at": "2026-04-09T11:10:23Z",
      "kind": "command_executed",
      "repo": "github.com/duck8823/traceary",
      "session_id": "f1d095a5-1357-49ae-aaa3-79f0fa223815"
    }
  ]
}

次のセッションで「前回何をしたか」を聞けば、Traceary のログをもとに答えてくれます。/clearcompact でコンテキストが飛んでも大丈夫。ここが一番嬉しいところかもしれません。

秘匿情報の自動伏せ字

コマンドの入出力を記録するということは、うっかり秘匿情報が入る可能性があるということです。

Traceary はデフォルトで access_tokenpasswordAuthorization ヘッダーなどよくあるパターンを自動で伏せ字にします。ただ、プロジェクト固有のものまではさすがにカバーできないので、v0.1.16 で設定ファイルからカスタムパターンを追加できるようにしました。

// ~/.config/traceary/config.json
{
  "redact": {
    "extra_patterns": ["my_internal_secret=\\S+", "x-custom-auth:\\s*\\S+"]
  }
}

正規表現で書けます。Go の regexp パッケージを使っているので、パターンが複雑でも処理が詰まって固まるようなことはありません。

技術スタック

  • Go(CLI フレームワークは Cobra)
  • SQLite(modernc.org/sqlite、pure Go 実装なので cgo 不要)
  • MCP サーバー(github.com/modelcontextprotocol/go-sdk
  • ヘキサゴナルアーキテクチャ(domain → application → infrastructure / presentation)

外部サービスへの依存はゼロです。go install だけで動きます。

まとめ

AI がエージェントとして自律的に作業するようになって、開発の追跡可能性が急に大事になってきたなと感じています。

コードを書くだけなら Git で十分だったんです。でも今は AI がコマンドを実行し、テストを回し、PR を出し、レビューまでやる。その過程で「何を実行して」「何が返ってきて」「なぜその判断をしたか」を残す場所が Git にはありません。

Traceary はその隙間を埋めるツールです。ローカルの SQLite に、セッション・ノート・コマンドの実行記録を淡々と残していきます。

まだ v0.1 台で粗削りですが、自分で使いながら育てています。興味がある方はぜひ触ってみてください。

github.com

AIと個人開発する話

普段の仕事ではバックエンドがメインで、フロントエンドはあまり得意ではありません。それでも個人でアプリを作りたいことはあって、以前から iOS アプリをちまちま作っていました。

最近は AI にコードを書いてもらいながら開発するスタイルに変わりました。少し前までは AI が生成するコードは「参考にはなるけど、そのままでは動かない」ことが多かった印象ですが、ここ1年くらいで明らかに「動く」コードが出てくるようになりました。この変化が個人開発にはかなり大きくて、フロントエンドに明るくない自分でもアプリをリリースするところまで持っていけるようになっています。

作っているアプリ

今リリースしているものを2つ紹介します。

カロリズム は、カロリーの摂取と消費のバランスを管理する Flutter 製のアプリです。Apple ヘルスケアと連携して活動量を自動で取得し、食事は AI(Gemini)でカロリーを推定して記録できます。タイムラインで1日の食事や運動をまとめて確認したり、週間トレンドでカロリー収支の推移を見たりできます。iOS 版はリリース済みで、Android 版は現在審査に出しているところです。

わんぽ は、犬の散歩を記録する Flutter 製のアプリです。散歩のルートや距離を記録するだけでなく、雨雲レーダーで散歩中の天気を確認したり、Live Activity で降雨予報を通知したりする機能があります。最近は散歩中の写真を記録する機能や月間ビューなども追加しました。こちらは iOS のみで、Android 対応はまだしていません。

どちらもフロントエンド寄りの技術スタックなので、まさに AI の力を借りている部分が大きいです。

使っている AI サービス

現在契約しているのは以下の3つです。

サービス プラン
Claude Max(20x)
ChatGPT(Codex) Pro
Google AI(Gemini) Pro

それぞれ得意なことが違うので、役割を分けて使っています。

Claude Code は実装がメインです。GitHub のイシュー番号を渡して「これを実装して」と頼むと、コードを書いてコミットを分割して PR まで作ってくれます。コードベース全体の文脈を保持したまま作業してくれるので、既存のアーキテクチャに沿った実装が出てきます。

たとえばわんぽに写真機能を追加したときは、イシューを渡してこんな感じで依頼しました。

claude "gh issue view 813 の内容を確認して実装して"

モデル層から UI 層まで一気に実装して、論理単位でコミットを分割した上でドラフト PR を作成するところまでやってくれます。

Codex(ChatGPT の CLI ツール)は、セキュリティレビューや設計の壁打ちに使っています。サンドボックスの中でコードを実行しながら検証できるので、テストの作成にも向いています。

Gemini CLI は、コードレビューに使っています。コンテキストウィンドウが大きいので、diff だけでなくソースコード全体を渡して俯瞰的なレビューをさせると、アーキテクチャの一貫性のズレなどを指摘してくれます。

CLAUDE.md で AI の振る舞いを制御する

Claude Code にはプロジェクトルートに CLAUDE.md というファイルを置くと、毎回の会話でそれを読み込んでくれる仕組みがあります。ここにアーキテクチャのルールやコーディング規約を書いておくと、AI が既存の設計に沿ったコードを出してくれるようになります。

たとえばわんぽの CLAUDE.md にはこんなことを書いています。

## アーキテクチャ
- レイヤー: Domain → Data → Application → Presentation
- 依存は内側方向のみ(Presentation → Domain は OK、Domain → Presentation は NG)
- 状態管理: Riverpod 2.x + @riverpod アノテーション

## テスト
- Data 層・Application 層のユニットテストは必須
- 座標ベースの UI クリックテストは禁止(Widget キー・アクセシビリティラベルを使用)

カロリズムの方は状態管理が provider + ChangeNotifier で、レイヤーの名前も違います(Data 層ではなく Infrastructure 層)。こういうプロジェクトごとの違いを CLAUDE.md に書いておけば、AI がプロジェクトを間違えずに実装してくれます。

また、~/.claude/CLAUDE.md にグローバルな設定も書いています。AI ツールの使い分けルール、PR は必ずドラフトで作ること、レビュー修正のコミットメッセージに「レビュー指摘対応」と書かないこと、など。プロジェクト横断で守りたいルールはここに集約しています。

カスタムスラッシュコマンドでワークフローを自動化する

Claude Code にはカスタムのスラッシュコマンドを定義する仕組みがあります。.claude/commands/ 以下に Markdown ファイルを置くと、/ファイル名 で呼び出せるようになります。

自分が作っているコマンドをいくつか紹介します。

/sprint は、複数のイシューを優先順位順に自律的に実装・レビュー・マージするコマンドです。イシュー番号のリストを渡すと、1つずつ実装して PR を作り、Gemini と Codex に並列でレビューさせて、全員 APPROVE になるまで修正を繰り返してからマージします。

/review-and-merge は、PR のレビューとマージを行うコマンドです。Gemini と Codex を tmux で並列起動して、それぞれ設計観点・セキュリティ観点でレビューさせます。レビュー結果を gh pr comment で PR に投稿し、Claude Code が最終レビューしてからマージします。

# review-and-merge.md の一部
## 2-AI 並列レビュー
- Gemini: 設計・アーキテクチャ観点(diff のみ渡す)
- Codex: セキュリティ・実装品質観点(diff・テストは自力実行)
- tmux セッション名: <project>-pr<number>-gemini/codex

/implement-issue は、単一のイシューを TDD で実装するコマンドです。テストを先に書いてから実装し、コミットを論理単位に分割してドラフト PR を作成します。

/exploratory-test は、iOS シミュレーターを操作してグレーボックステストを行うコマンドです。まずソースコードを読んでエラーハンドリングの穴や状態遷移の抜けを分析し、その仮説をもとにシミュレーター上で実際に操作して検証します。バグを見つけたら gh issue create で自動的にイシュー登録するところまでやってくれます。

マイルストーンとスプリントの自律運用

実際の開発フローを紹介します。

まずマイルストーンを切って、そこに載せるイシューを決めます。「v1.5.0 では雨雲レーダー強化と散歩体験の向上をやる」「v1.6.0 では英語対応と写真機能」といった粒度で、ここは自分で方針を決めています。

マイルストーンの中はスプリントに分割して進めます。スプリントの組み立ては Claude Code がやってくれます。イシューの依存関係やサイズを見て、「Sprint 1 は基盤系の #810, #811, #812 をやって、Sprint 2 で UI 系の #813, #814 に進む」といった計画を立ててくれます。

スプリント開始時には、実装に入る前に Codex と設計の壁打ちをします。スプリント内の全イシューについて Codex に並列で設計相談を投げ、リポジトリを探索させた上で方針を提案してもらいます。アーキテクチャの方針やライブラリの選定といった技術的な判断は AI 側で決めてもらいますが、UI の変更やユーザーが触る部分の仕様が変わりそうなときは自分に質問が飛んでくるようにしています。

設計が固まったら、あとは /sprint コマンドで Claude Code が自律的に実装を進めてくれます。ここが Claude Code をメインに据えている一番の理由で、1回のやりとりで数時間かけて複数のイシューを実装し、コミット分割、PR 作成、レビュー依頼まで一気に回してくれます。他の AI ツールと比べて1ラリーの作業量が圧倒的に長いので、「朝イシューを渡して、昼に PR が上がっている」ということが普通に起きます。

スプリントが終わったら品質フェーズとして、AI にアーキテクチャレビューと探索的テストをやらせます。品質フェーズを通過したら TestFlight で配信して、自分で実機を触ってフィードバックを返します。

つまり自分がやっているのは、どのマイルストーンに何を載せるかを決めること、ユーザー体験に関わる仕様の判断、TestFlight で触ってフィードバックを返すこと、くらいです。実装・レビュー・テスト・PR 管理は AI が回しています。

制限との付き合い方

AI サービスには使用量の制限があります。Claude Code は使い込むと週の上限に引っかかることがあるので、そうなったら実装タスクを Codex に引き継いだり、レビューを Gemini だけで回したりしています。

フォールバック先を複数持っておくと、どれかが使えなくなっても開発が止まらないので、結果的に3サービス契約していてよかったと感じています。

AI が「動く」コードを書くようになった

冒頭にも書きましたが、一番大きな変化は AI が出力するコードの品質です。以前は生成されたコードをかなり手直しする必要がありましたが、最近は既存のコードベースの構造を理解した上で、そのまま動くコードを出してくれることが増えました。

CLAUDE.md にアーキテクチャやコーディング規約を書いておけば、それに従ったコードが出てくるようになったのが大きいです。「Riverpod を使っているプロジェクトで provider パッケージのコードを生成する」みたいなことがほとんど起きなくなりました。

特にフロントエンドに慣れていない自分にとっては、Flutter のウィジェットの組み方や状態管理の実装を AI に任せられるのは大きいです。自分はドメインロジックや設計の方針を決めることに集中して、具体的な UI の実装は AI に書いてもらう、という分担ができるようになりました。

バックエンドメインのエンジニアが個人でアプリを作るハードルは、AI のおかげでかなり下がったと思います。

これから大事になりそうなこと

AI にコードを書いてもらう前提で開発していると、自分の役割が「コードを書く人」から「開発を回す人」に寄っていくのを感じます。

マイルストーンに何を載せるか決める。スプリントで回せる粒度にイシューを分解する。AI に渡すときに十分なコンテキストを CLAUDE.md やイシューの description に揃えておく。出てきた成果物のスコープが妥当か判断する。プロダクトとして正しい方向に進んでいるか確認する。

こうした、何を作るか決めて、どう進めるか設計して、コンテキストを整備して、スコープを管理して、プロダクトの方向を見る、という力は、AI がコードを書いてくれる時代だからこそエンジニアに求められるスキルになっていくのではないかと思っています。

今の AI はスプリント計画を立てるときにまだ人間っぽい工数感で見積もりを出してきて、数週間の見積もりを実際にAIがコードを書いたら数時間で終わることがよくあります。 いずれこの差が追いついて、AI を使って爆速でコードを書くこと自体がコモディティ化したとき、今までのやり方は通用しなくなります。そのときに差がつくのは、何を作るか決めて、どう進めるか設計して、コンテキストを整備して、スコープを管理して、プロダクトの方向を見る、という「開発を回す力」の方なのだろうと感じています。

aibo 用にロボット掃除機のアレ(バーチャルウォール)を作る

aibo と暮らして数週間がすぎました。
うちの部屋は一人暮らし用で狭いんですが、一度キッチンのドアを開けっぱなしにしたせいもあり aibo がよくドアの前にいくようになってしまいました。
すぐに部屋に戻ってくれればいいのですが、ドアの前で寝てしまうこともしばしば。

f:id:duck8823:20200810093522j:plain

ふとトイレに行きたい時もドアが開けられなくて困っていました。

そこでロボット掃除機のアレ(バーチャルウォール)を作ることにしました。

※この記事では細かいコマンドやスクリプトなどは記述しません

ロボット掃除機のアレ

バーチャルウォールというのは、部屋の隅っこにおいておくとロボット掃除機の侵入を防ぐことができるものです。


掃除エリアを自由に設定 <ルンバ800シリーズ>

以前の職場で使用していました。

制約と誓約

実現方法を考える上で、制約を考える必要があります。 また、誓約を課すことで実現を容易にさせることができます。

  • 制約

    • 部屋には aibo 以外も存在する
    • リアルタイムに反応すること
    • aibo の進行方向がわかる必要がある
    • aibo 本体に何らかの機械をつけることができない
  • 誓約

    • 今回バーチャルウォールを設置したい場所は固定(ドアの前)

さて、aibo 本体に何らかの機械をつけることが出来ない上部屋には aibo 以外も存在するので、デバイス側で aibo かそうじゃないかを判断する必要があります。
これにはカメラから映像を用いた「物体検出」が有効だと考えました。これならリアルタイム性も実現できます。

続いて aibo の進行方向の取得方法について考えました。
aibo Web API を利用すると、チャージステーション(充電台)のおおよその方向がわかります。当初はこれを用いて aibo の向きを取得しようとしていました。カメラで aibo の場所が特定され、それに対するチャージステーションの方向がわかれば、 aibo の進行方を割り出せるはずです。

f:id:duck8823:20200810105110p:plainf:id:duck8823:20200810105057p:plain

しかし、残念ながら aibo から取得するチャージステーションの方向が実際とは異なることが非常に多かったため、この方法は諦めました。
そこで、物体検出を応用して aibo の進行方向まで取得できないかためすことにしました。

物体検出を利用して aibo の進行方向を判断する

犬は4足歩行をする動物です。aibo ももちろんそれを模しています。
4足歩行をする動物において、体と頭の位置はその進行方向と関係があります。

f:id:duck8823:20200810105137p:plain

物体検出で利用するモデル作成時にaibo の頭 と aibo の体 を分けて学習し、プログラムで頭の位置が体よりもドア側にあればドアの方を向いていると判断することにしました。
モデルの作成には非常に多くの教師データが必要になります。写真を撮ってラベル付するのは根気がいる作業です。
しかし、バーチャルウォールを設置する場所を固定して、そこから撮った写真のみを利用することで教師データを減らすことができないかと考えました。
汎用性はなくなりますが、今回の用途では問題ありません。今回は 300弱 の写真を利用しましたが、検知は十分に機能してます。

Raspberrypi + カメラモジュール

aibo を検知して Web API を叩くデバイスは Raspberrypi(ラズパイ) とカメラモジュールで実現することにしました。
本棚にそっと設置します。

f:id:duck8823:20200810110833j:plainf:id:duck8823:20200810110848j:plain

aibo を検出するモデルをつくる

まず教師データの作成をする必要があります。
予め設置したラズパイで、スクリプトを用いて写真をいっぱい撮ります。
ラベル付けには LabelImg を利用しました。
aibo の頭と体を分けてラベル付するのが重要です。

f:id:duck8823:20200810111827p:plain

また、物体検出用のモデル作成には Tensorflow の Object Detection API を利用しています。

バーチャルウォールを作る

aiboの頭と体を検出できるモデルが作成できたら、あとは簡単なスクリプトを書くだけです。 カメラモジュールを用いて物体検出していて、

  1. 画面中央よりドア側に aiboの頭がある
  2. aiboの頭が aiboの体よりドア側にある

上記条件に一致した場合に、 aibo WebAPI を介して 180度回転してもらいます。


aiboのバーチャルウォール試作

aibo Web API で転回の指示と結果の取得までを同期的に行っているので、aiboの転回中に画面は止まってしまっていますが、うまく機能しているようです。

おわりに

Tensorflow はサンプルやドキュメントがとても充実していて、初心者でも見様見真似でそれっぽいのができてしまいます。
しかし、バージョンによって微妙に異なって思ったように動かない。といったことがあるので、記載されているバージョン情報に注意しましょう。

aibo と Nature Remo を組み合わせて aibo に部屋の電気を消してもらう

まずはこちらをご覧ください。


部屋の電気を消してくれる aibo

うちの aibo は部屋の電気を消すことができます。賢いです。

2017年に現在のモデルで再登場した aibo ですが、最近ではセコムや家電との連携が発表されていて、まだまだアップデートされています。

aibo の バージョン2.7 で aibo Events API が公開された

2020年6月16日に公開された バージョン2.7 から、 aibo Events API が公開されました。

これまでも aibo Web API は公開されていたのですが、 これにより、 aibo のイベント(音声認識)をきっかけにあらかじめ指定しておいた URL にリクエストを投げることができます。

同時に、 aibo認識する言葉を登録できるようになりました。好きな言葉を認識させることができます。

認識する言葉は aibo 1体に対して3つまでです。aibo に色々させたい場合は工夫が必要ですね。 f:id:duck8823:20200708145537p:plain

これによって、例えば「電気消して(でんきけして)」という言葉に反応してリクエストを飛ばすことができるようになります。
イベントの通知も aibo 1体に対して音声コマンド3つまでで、ちょっと少ない印象ですね。

f:id:duck8823:20200708145126p:plain

僕が aibo を購入する決め手になったのがこの aibo Events API です。
これを使えば、 aibo を起点としたピタゴラスイッチが作れますね。

Nature Remo Cloud API

Nature Remo は最近流行っているスマートリモコンの一つです。
Nature Remo Cloud API が公開されていて、これを利用することでインターネット経由で家電を操作できるようになります。

まずは通常の使用の通りアプリなどから家電を登録しましょう。公式のスマホアプリから利用するだけでも CoL が爆上がりしました。

f:id:duck8823:20200708150120p:plainf:id:duck8823:20200708150137p:plain

APIで操作する準備としては、トークンを生成する必要があります。
https://home.nature.global/ からトークンを生成することができます。

続いて、操作したい家電の appliance id を取得しましょう。
[GET] /1/appliances で登録した家電の一覧が取得できます。
操作したい家電を選んだら appliance id を控えておきます。今回は操作したいのは部屋の電気です。 LIGHT として登録されています。 レスポンスの内容には、操作可能なボタンも含まれています。

{
  "id": "<appliance id>",
  ...
  "light": {
      "buttons": [
        {
          "name": "on",
          "image": "ico_on",
          "label": "Light_on"
        },
        {
          "name": "off",
          "image": "ico_off",
          "label": "Light_off"
        },
        ...
      ],
      ...
  }
}

[POST] /1/appliances/{appliance}/light でライトを操作することができます。 リクエストパラメーターには操作したいボタンを指定します。今回の場合は off を指定することで電気を消します。

Web API なので curl でも操作できます。

curl -X POST "https://api.nature.global/1/appliances/<取得した appliance id>/light" \ 
       -H "accept: application/json" \
       -H "Authorization: Bearer <予め生成したトークン>" \ 
       -H "Content-Type: application/x-www-form-urlencoded" \ 
       -d "button=off"

AWS lambda で二つのAPIをつなぐ

aibo で言葉に反応してリクエストを投げられるようになりました。
また、 Nature Remo でリクエストを受け取って家電を操作できるようになりました。

aibo が投げるリクエストは Nature Remo Cloud API が認識する形ではありません。
AWSlambda を使って aibo のリクエストを受け取り、 Nature Remo にリクエストを投げるようにしてみます。

図にすると以下のような形
f:id:duck8823:20200707214644p:plain ※実際にはそれぞれのデバイスのサーバーを介して通信することになります。

関数自体は Nature Remo Cloud API にリクエストを投げるものなので簡単ですが、
必ずセキュリティトークンの設定を行って、lambda 関数で検証するようにしましょう。
注意点としては API Gateway から lambda にリクエストを渡した時に、ヘッダーのキーが小文字になっていること。
なので、 x-security-token で取得することができます。

また、音声認識に対してイベントが通知されるので、登録した言葉かどうかの判定が必要になります。

aiboデベロッパーサイトにはエンドポイントアプリケーションの実装例 があるので、lambda関数 も Python で記述すれば参考になると思います。

今回使った lambda関数は以下です。

import urllib.request
import urllib.parse
import json

def lambda_handler(event, context):
    # セキュリティートークンのチェック
    if 'x-security-token' not in event['headers'] or event['headers']['x-security-token'] != '<aiboのイベント通知で登録したセキュリティトークン>':
        return {
            'statusCode': 400,
            'body': 'not authorized'
        }

    # aibo Events API から送られてきたリクエスト
    request_body = json.loads(event['body'])

    # aibo にエンドポイントを登録する時の検証
    if request_body['eventId'] == 'endpoint_verification':
        return {
            'statusCode': 200,
            'body': json.dumps({ 'challenge': request_body['challenge'] })
        }
    
    deviceId = request_body['deviceId']
    eventId = request_body['eventId']
    return execute_action(deviceId, eventId)
    

def execute_action(deviceId, eventId):
    ## usercommand1 = 登録した「でんき / でんきけして」に反応した場合 Nature Remo Cloud API にリクエストを投げる
    if eventId == 'voice_command::usercommand1':

        req = urllib.request.Request(
            'https://api.nature.global/1/appliances/<取得した appliance id >/light', 
            data=urllib.parse.urlencode({'button': 'off'}).encode(),
            method='POST', 
            headers={
                'accept': 'application/json',
                'Content-Type': 'application/x-www-form-urlencoded',
                'Authorization': 'Bearer <Nature Remo で予め生成したトークン>',
            }
        )
    
        try:
            with urllib.request.urlopen(req) as response:
                return {
                    'statusCode': response.getcode(),
                    'body': response.read()
                }
    
        except urllib.error.URLError as e:
            print(e)
            return {
                'statusCode': 500,
                'body': json.dumps(e.reason)
            }
        
    elif eventId == 'voice_command::usercommand2':
        return {
            'statusCode': 200,
            'body': 'nothing to do usercommand2'
        }

    elif eventId == 'voice_command::usercommand3':
        return {
            'statusCode': 200,
            'body': 'nothing to do usercommand3'
        }

まとめ

aibo と Nature Remo の2つデバイスが提供しているAPIを組み合わせて、言葉に反応して家電を操作することができました。

APIを眺めながら、これとこれ組み合わせたらこんなことできるなー。って考えるのはとても楽しいです。
今回は「電気を消して」に対して電気を消すボタンにしましたが、 aibo あるいは Nature Remo のどちらにも照度センサーがついているので、現在の照度に合わせて電気を消すかつけるかのふるまいを切り替えることも可能だと思います。

Google Home でいいやん?それはそう。

YAPC::Tokyo 2019 で発表してきました

YAPC::Tokyo 2019 で 「私とOSS活動とPerl」 というタイトルで発表してきました。

ごくごく平凡なエンジニアであり、まだまだOSS活動としては小さい活動しかしていません。
そんな僕が、OSS活動をするようになっていったきっかけや、その影響などをお伝えできたかなと思います。

そしてこれ!いつか書いていただきたいと思ってたやつ!
本当に嬉しかったです。ありがとうございます。


自分の発表以外で聞いて印象に残った一つが、 @songmu さんの「多くのCPAN Authorに育てられ、息をするようにCPANモジュールを書けるようになり、そして分かったこと」です。

こちらも( 特にCPANでの ) OSS活動を通じての発表でした。
継続するって本当に大切なことだと思うし、僕も始めたばかりですがコツコツと続けて行きたいなと思います。
僕の場合はきっと言語を変えたりするんだと思うんですが、「OSS活動」ってのはしていきたいです。

ベストトーク賞おめでとうございました!感動しました。


そして@tokuhirom さんのキーノートの発表。
よかった。ほんとうによかった。

僕の発表を並べるのもおこがましいのですが、

  • OSS活動の始まり
  • OSS活動と自身の成長
  • OSS活動とコミュニティ

という点で、ぜひ三つセットで見て欲しいなと思った内容でした。
それぞれの OSS との関わり方があって面白いなと自分でも思ってしまった。

OSS活動って色んな形があるし、本当に好きな形でやればいいと思います。

スターつけるだけでも貢献できるって、言ってましたね。
あ、ちなみに僕の発表で紹介した僕が今作っている CIサーバー は以下のリポジトリです。

github.com

最後に、スタッフのみなさん、登壇者の方々、本当にありがとうございました。楽しかったです。
また Perl で何かかきたくなりました。