DynamoDB LocalからLocalStackへの移行とJestによる自動テストの並列実行 with dynamodb-toolbox

この記事は個人ブログと同じ内容です

zenn.dev

はじめに

DynamoDB Localを使用してJestを介した自動テストの際に、並列実行時に予期しないエラーに直面しました。--runInBandオプションを使って回避していたのですが、テストの数が増えてきたため、直列で実行するのがしんどくなってきたので、解決策について模索してみました。

DynamoDB Localの制約と課題

Jestを使用してテストを並列実行すると、DynamoDB Localが競合状態になり、予期しないエラーが発生することがありました。どうやら、この記事によるとDynamoDB Localは、内部的にSQLiteを使用しているらしいので、同時書き込みが難しそう。。。

LocalStackへの移行

そこで、LocalStackへの移行を試してみました。LocalStackは、AWSのクローン環境を提供し、ローカルでAWSサービスをエミュレートすることができるオープンソースのツールです。(有料版も存在するみたいなのですが、DynamoDB のエミュレータは無料で利用できます。)

  • 並列実行のサポート: LocalStackは、AWSサービスをマルチスレッドでエミュレートするため、並列実行時の競合やエラーのリソルブが可能です。これにより、複数のテストケースを同時に実行し、開発プロセスの効率を向上させることができます。
  • リアルな環境の再現: LocalStackは、AWSの各種サービス(S3、DynamoDB、Lambdaなど)をローカル環境でエミュレートするため、開発者は実際のAWS環境と同様のセットアップと設定を行うことができます。これにより、本番環境での挙動や相互作用を正確に再現することができます。
  • 柔軟なカスタマイズ性: LocalStackは、docker image も提供されており、必要に応じて構成や拡張が可能です。また、AWS CLISDKを使用してLocalStackに対してリクエストを送信することもできます。これにより、既存のテストコードやツールをそのまま利用できるため、移行の際の手間を最小限に抑えることができます。

LocalStackのセットアップ

下記のような docker-compose.yml を用意します。 なお、dynamodb-adminは、ローカルで動作するDynamoDBを、GUIで操作可能なWebベースのツールです。必須ではないがあると便利なので入れています。

# docker-compose.yml
version: '3.8'
services:
  localstack:
    image: localstack/localstack:latest
    environment:
      SERVICES: dynamodb
    ports:
      - 4566:4566
  dynamodb-admin: 
    container_name: dynamodb-admin
    image: aaronshaf/dynamodb-admin:latest
    environment:
      - DYNAMO_ENDPOINT=localstack:4566
    ports:
      - 8001:8001
    depends_on:
      - localstack

作成したら docker コマンドで起動します。

docker compose up -d

テストコード

テストファイルをこんな感じで用意しました。dynamodb client として dynamodb-toolbox を利用しています。 また、今回のコードはこちらにまとめております。 https://github.com/mukaihajime/dynamodbtoolbox

// get.ts テスト対象ファイル
import { TableAEntity } from "../../tables/TableA/entities/TableAEntity"

export const get = async (pk: string, sk: string) => {
    const result = await TableAEntity.get({ pk, sk })
    return result
}

テストファイルは4ファイル作成しました。 - get1.test.ts - get2.test.ts - get3.test.ts - get4.test.ts

// テストファイル
import { TableAEntity } from '../../tables/TableA/entities/TableAEntity'
import { get } from './get'

describe('get', () => {    
    it('should get an item', async () => {
        await TableAEntity.put({
            age: 1,
            pk: 'pk1',
            sk: 'sk1',
        })
        const res = await get('pk1', 'sk1')
        expect(res.Item?.age).toBe(1)
        expect(res.Item?.pk).toBe('pk1')
        expect(res.Item?.sk).toBe('sk1')
    })
})

設定ファイルはこんな感じです。

// jest.config.js
module.exports = {
  setupFilesAfterEnv: ['./jest.setup.js'],
  testEnvironment: 'node',
  testMatch: ['**/?(*.)+(spec|test).ts?(x)'],
  transform: {
    '^.+\\.ts?$': ['@swc/jest'],
  },
}
// jest.setup.js
const { CreateTableCommand, DeleteTableCommand, DynamoDBClient, ListTablesCommand } = require('@aws-sdk/client-dynamodb')
const { DynamoDBDocumentClient } = require('@aws-sdk/lib-dynamodb')
const { TableA } = require('./tables/tableA/TableA')

const createInstance = () => {
  const client = new DynamoDBClient({
    credentials: {
      accessKeyId: 'DUMMYIDEXAMPLE',
      secretAccessKey: 'DUMMYIDEXAMPLE',
    },
    endpoint: 'http://localhost:4566',
    region: 'ap-northeast-1'
  })

  return {
    client,
  }
}


const createCommand = (tableName) => {
  return new CreateTableCommand({
    AttributeDefinitions: [
      {
        AttributeName: 'pk',
        AttributeType: 'S',
      },
      {
        AttributeName: 'sk',
        AttributeType: 'S',
      },
      {
        AttributeName: 'gsi1pk',
        AttributeType: 'S',
      },
      {
        AttributeName: 'gsi1sk',
        AttributeType: 'S',
      },
    ],
    BillingMode: 'PAY_PER_REQUEST',
    GlobalSecondaryIndexes: [
      {
        IndexName: 'gsi1',
        KeySchema: [
          {
            AttributeName: 'gsi1pk',
            KeyType: 'HASH',
          },
          {
            AttributeName: 'gsi1sk',
            KeyType: 'RANGE',
          },
        ],
        Projection: {
          ProjectionType: 'ALL',
        },
      },
    ],
    KeySchema: [
      {
        AttributeName: 'pk',
        KeyType: 'HASH',
      },
      {
        AttributeName: 'sk',
        KeyType: 'RANGE',
      },
    ],
    TableName: tableName,
  })
}

const deleteCommand = (tableName) => {
  return new DeleteTableCommand({
    TableName: tableName,
  })
}

const listTablesCommand = () => {
  return new ListTablesCommand({})
}


beforeAll(async () => {
  const marshallOptions = {
    convertEmptyValues: false,
  }
  const translateConfig = { marshallOptions }

  const { client } = createInstance()

  TableA.name = TableA.name

  const { TableNames } = await client.send(listTablesCommand())

  if (TableNames.includes(TableA.name)) {
    const deleteCreditedTableCommand = deleteCommand(TableA.name)
    await client.send(deleteCreditedTableCommand)
  }
  const createCreditedTableCommand = createCommand(TableA.name)
  await client.send(createCreditedTableCommand)
  TableA.DocumentClient = DynamoDBDocumentClient.from(client, translateConfig)
})

jest コマンドを実行すると、失敗します。

Test Suites: 3 failed, 1 passed, 4 total
Tests:       3 failed, 1 passed, 4 total
Snapshots:   0 total
Time:        3.442 s

Jestの並列実行とテーブル作成の課題

Jestはデフォルトで並列実行されるため、複数のテストケースが同時に実行されます。しかし、テーブル作成時に競合やデータの衝突が発生していました。理由は下記です。

テーブル作成時の競合: 複数のテストケースが同時に実行される場合、それぞれのテストケースが独自のテーブルを作成しようとする可能性があります。この場合、同じテーブル名を使用しようとして競合が発生し、テーブル作成に失敗する可能性があります。競合が発生すると、テストケースの実行が中断されたり、テーブルの状態が不安定になったりすることがあります。

データの衝突: テストケースごとに異なるデータを使用する場合、並列実行時にデータの衝突が発生する可能性があります。例えば、テストケースAとテストケースBが同時に実行され、それぞれが同じテーブルにデータを挿入しようとする場合、データの一貫性が損なわれる可能性があります。また、テーブルのクリーンアップやリセットが不十分な場合、前回のテストケースの残留データが次のテストケースに影響を与えることもあります。

これらの課題を解決するために、JEST_WORKER_IDごとに独自のテーブルを作成するアプローチを採用しました。このアプローチにより、各テストワーカーが独立した環境でテーブルを操作できるため、競合やデータの衝突を回避することができます。また、テーブル作成前に必要な初期データの挿入やテーブルの削除も、各テストワーカーごとに適切に管理されます。

参考: CI 環境でのユニットテストの実行時間を2倍速くした話 (Jest + Mongo DB + Circle CI)

JEST_WORKER_IDごとのテーブル作成アプローチ

JEST_WORKER_ID で worker の番号が取得できるため、これを利用して worker 分のみ database を作るように変更します。

// jest.setup.js
const { CreateTableCommand, DeleteTableCommand, DynamoDBClient, ListTablesCommand } = require('@aws-sdk/client-dynamodb')
const { DynamoDBDocumentClient } = require('@aws-sdk/lib-dynamodb')
const { TableA } = require('./tables/tableA/TableA') 

const createInstance = () => {
  const client = new DynamoDBClient({
    credentials: {
      accessKeyId: 'DUMMYIDEXAMPLE',
      secretAccessKey: 'DUMMYIDEXAMPLE',
    },
    endpoint: 'http://localhost:4566',
    region: 'ap-northeast-1'
  })

  return {
    client,
  }
}


const createCommand = (tableName) => {
  return new CreateTableCommand({
    AttributeDefinitions: [
      {
        AttributeName: 'pk',
        AttributeType: 'S',
      },
      {
        AttributeName: 'sk',
        AttributeType: 'S',
      },
      {
        AttributeName: 'gsi1pk',
        AttributeType: 'S',
      },
      {
        AttributeName: 'gsi1sk',
        AttributeType: 'S',
      },
    ],
    BillingMode: 'PAY_PER_REQUEST',
    GlobalSecondaryIndexes: [
      {
        IndexName: 'gsi1',
        KeySchema: [
          {
            AttributeName: 'gsi1pk',
            KeyType: 'HASH',
          },
          {
            AttributeName: 'gsi1sk',
            KeyType: 'RANGE',
          },
        ],
        Projection: {
          ProjectionType: 'ALL',
        },
      },
    ],
    KeySchema: [
      {
        AttributeName: 'pk',
        KeyType: 'HASH',
      },
      {
        AttributeName: 'sk',
        KeyType: 'RANGE',
      },
    ],
    TableName: tableName,
  })
}

const deleteCommand = (tableName) => {
  return new DeleteTableCommand({
    TableName: tableName,
  })
}

const listTablesCommand = () => {
  return new ListTablesCommand({})
}


beforeAll(async () => {
  const marshallOptions = {
    convertEmptyValues: false,
  }
  const translateConfig = { marshallOptions }

  const { client } = createInstance()

  TableA.name = TableA.name + process.env.JEST_WORKER_ID // 追加

  const { TableNames } = await client.send(listTablesCommand())

  if (TableNames.includes(TableA.name)) {
    const deleteCreditedTableCommand = deleteCommand(TableA.name)
    await client.send(deleteCreditedTableCommand)
  }
  const createCreditedTableCommand = createCommand(TableA.name)
  await client.send(createCreditedTableCommand)
  TableA.DocumentClient = DynamoDBDocumentClient.from(client, translateConfig)
})

jest を実行してみた結果。

pnpm jest
 PASS  src/sample/get3.test.ts
 PASS  src/sample/get1.test.ts
 PASS  src/sample/get4.test.ts
 PASS  src/sample/get2.test.ts

Test Suites: 4 passed, 4 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        3.059 s

参考: [Node.js][Jest]LocalStackを使ったDynamoDBテストを並列で行う方法

現在 back check 開発チームは一緒にはたらく仲間を募集中です!!

herp.careers

herp.careers

herp.careers

おすすめフレームワーク#2:機能開発編

はじめに

株式会社ROXXのプロダクトマネージャー、松野広志です。
プロダクトマネージャーにおすすめのフレームワークを「新規事業開発編」「機能開発編」「分析編」の3つに分けて記事にしたいと思います。
当記事では、その中の機能開発時に役立つフレームワーク3つをご紹介します。
機能開発時に役立つフレームワーク
1.Hooked Framework(フックフレームワーク
2.HEART Framework(ハートフレームワーク
3.RICE Framework(ライスフレームワーク

※ 各フレームワークを詳細に説明する記事ではありません。

1.Hooked Framework(フックフレームワーク

https://www.slideshare.net/nireyal/hooked-model

出典

Nir Eyalの著書『Hooked』で紹介されたHookedというフレームワークは、ユーザーの問題を解決するためのソリューションを提供し、そこからソリューションの利用を習慣化するために頻繁にアクセス(利用)することの重要性について述べられています。

フレームワークの使い方

顧客やユーザーが抱える課題を特定し、そのタイミングでプロダクト側が提供する接点を表現し、それによって生じる顧客の行動変容と購入までのプロセスを迅速に整理し、ディスカッションするのに役立ちます。

BtoBの従業員エンゲージメントを測るシステムを例に考えてみたいと思います。

Trigger(きっかけ)
内部要因:チームリーダーはメンバーのパフォーマンス管理で悩む
外的要因:メンバーの毎日のマイクロサーベイの結果通知が届く
Action(行動)
チームリーダーがプロダクトの画面を開き、メンバーのエンゲージメントの傾向を把握する。不調傾向のメンバーを把握し勤務状況と周囲の関係性を確認する。
Variable Reward(報酬の変動)
メンタル面、フィジカル面のフォローアップをおこなうことで事態の悪化を未然に防ぐことができる。余裕を持って周囲に相談がおこなえる。
Investmnent(投資)
月払い、年払いの定期購入の場合、クレジットカード情報を入力する。
ユーザー数課金の場合は従業員が増えるとアップセルにつながる。

実際には簡単に利用が促進されることはありませんが、「共通のフレームワークで議論しやすいこと」「挑戦が失敗した際には、どこが上手くいかなかったを議論しやすくなる」という効果がありますので、使ってみてはいかがでしょうか。


2.HEART Framework(ハートフレームワーク

出典

作成者:Kerry Rodden, Hilary Hutchinson, Xin Fu
https://library.gv.com/how-to-choose-the-right-ux-metrics-for-your-product-5f46359ab5be

フレームワークの使い方

ハートフレームワークは、主要なUXパラメータ(Happiness、Engagement、Adoption、Retention、Task Success)に対するゴール、シグナル、メトリクスのマトリックスで構成されています。フレームワークを通じてプロダクトを整理することで、UXメトリクスを理解し、それに応じて機能のイテレーションの調整を検討しやすくなります。

Happiness(幸福度)
Goals:ユーザーが、プラットフォームが役に立ち、使いやすいと感じる
Signals:評価とレビューを残す
Metrics:ネットプロモータースコア / CSATスコア

Engagement(約束、契約)
Goals:ユーザーは機能を気に入り、使い続けたいと思う
Signals:プラットフォームで過ごす時間が長くなる
Metrics:平均セッション時間/平均セッション頻度/購読者数

Adoption(採択)
Goals:新規ユーザーがプロダクトや新機能に価値を見出す
Signals:機能の使用状況
Metrics:コンテンツ視聴率/機能利用率

Retention(保持)
Goals:プラットフォームを継続利用する
Signals:プラットフォームでの活動を継続/リピート購入
Metrics:解約率/契約更新率

Task Success(タスクの成功)
Goals:特定のプランに申し込む
Signals:プランの検索数/プランに費やした時間/タスク完了頻度
Metrics:月額利用料/完了率


3.RICE Framework(ライスフレームワーク

出典

Intercom社が自社の機能開発の優先順位を決めるために開発したものです。
https://www.intercom. com/blog/rice-simple-prioritization-for-product-managers/

フレームワークの使い方

RICE フレームワークは、開発予定の機能の「Reach(影響範囲)」「Impact(影響力)」「Confidence(自信)」「Effort(労力)」のスコアを設定し、「(Reach x Impact x Confidence ) / Effort」という計算式で RICEスコアを算出し、そのスコアが大きいものを優先的に開発するというものです。


Reach
:影響範囲
顧客数、四半期、トランザクション数/月
Impact:影響力
択一式にする
3=特大のインパク
2 = 高いインパク
1 = 中程度のインパク
0.5 = 低いインパク
0.25 = 最小限のインパク
Confidence:自信
以下のような%スコアを使用します。
100%=高い信頼性
80% = 中程度の信頼性
50%=低信頼度
Effort:開発工数
その取り組みは、製品、デザインからどれくらいの時間がかかるか
1ヶ月あたりの人数で測る(1人のメンバーが1ヶ月にどれだけの仕事をこなせるか)

上記は各項目の数値設定の一例ですが、自社のプロダクトに対する最適な設定を検討していただけたらと思います。例えば、私は「Reachの影響範囲をなくし、Impactを金額(売上、費用)で設定してみる」ということを実施してみたところ、結果は悪くありませんでした。それは金額を見積る際にReachもConfidenceも併せて考えることができためです。Confidenceも適当に気持ちで設定するのではなく、細かなスコア設定ができると良いと思います。例えば「100%=MVPで好感触を得ている、検証結果を数値で取得できている。」などです。

補足
RICEスコアが同点となる機能開発の優先度を比較してみたいと思います。
インパクトが低く、開発が簡単な機能:
Reach(100)Impact(1)Confidence(100)Effort(1日)=RICE(10,000)

インパクトが高く、開発が複雑な機能:
Reach(100)Impact(100)Confidence(100)Effort(100日)=RICE(10,000)

上記のように、RICEスコアが10,000の機能開発のどちらを優先すべきかと考える場合、私は①→②の順番で実施します。
注目すべきはEffortです。1日後に1のImpactの機能を顧客に提供することができます。②の機能がリリースするまでに100日かかるわけですから、それまでの100日 x インパクト1の価値を顧客は享受することができます。
②→①の順序でリリースすると、100日間は何も提供できず、100日後と101日後にインパクト101の価値を提供することができます。
その後の価値提供の大きさは同じですが、①→②の方が顧客に早く多くの価値を届けることができると言えます。

実際には、事業戦略や諸条件、ステークホルダーの思い、開発体制の問題も関係してくるため優先順位はスコア通りに決まり切らないと思いますが、スコアを算出する過程でプロダクトマネージャーの顧客や業務、既存機能の理解度が試され、自身の思い込みを排除する効果があるため非常に有効なフレームワークだと思います。


最後に

機能開発時に役立つフレームワークのご紹介と活用例について書かせていただきました。当記事が、これからプロダクトマネジメントをおこなう方のお役立てたらと幸いです。

 

 

 

[募集について]

現在 back check 開発チームは一緒にはたらく仲間を募集中です!!

- バックエンドエンジニア:https://herp.careers/v1/scouter/Bx7swv57kcgM

- リードバックエンドエンジニア : https://herp.careers/v1/scouter/kDFR0ZfLKWlJ

- プロダクトデザイナーhttps://herp.careers/v1/scouter/klIFYKELaF8Y

この記事は個人ブログと同じ内容です

 

特選50個!アイスブレイクでスクラムチームのコミュニケーションを促進させよう

Records開発チームPOの佐藤(@r_sato1201)です。
ROXXでは多くのスクラムチームは、セレモニーや各種MTGでアイスブレイクを行っています。
今回は、私が所属しているRecords開発チームで実際に行った珠玉のアイスブレイクを50個紹介したいと思います。

アイスブレイクをやる意義は?

そもそもMTGでアイスブレイクを行うことに懐疑的な方もいるかもしれませんので、アイスブレイクを行うことで得られるメリットを紹介します。

重苦しい雰囲気をなくせる

MTGのはじまりは重苦しいものです。なぜなら、全員ファシリテーターの進行を待っているからです。
普段接触機会が少ないメンバーが多かったり、リモートでのMTGだとより顕著でしょう。アイスブレイクで場が盛り上がることで緊張感をなくし、一体感を生むことができます。

発言しやすい環境をつくることができる

MTGで発言すること自体に苦手意識を持っている方もいます。「完璧な意見が必要なのではないか」「話した内容をきちんと理解できているだろうか」など要因を上げればきりがないですが
私個人としては、どんな意見, 疑問でもその場に出してもらうことでMTGの価値が高まると考えているのでどんなことでも発言してほしいと考えています。
アイスブレイクを最初に行うことで全員が一回は発言することができ、場が盛り上がれば盛り上がるほど発言しやすい雰囲気になります。

メンバーの人となりを知れる

テーマによっては普段の業務では知り得ないことを知ることもできます。メンバーの新たな一面を発見しましょう。

どんなふうにやっている?

私のスクラムチームでは朝会(デイリースクラム)、スプリントふりかえり、スプリントレビューでアイスブレイクを行っています。
全てのセレモニーでオンラインホワイトボードサービスのMiroを使用しているので、基本的にはMiro上で付箋を書き口頭で共有するという形です。

朝会

チェックインという手法で行っています。テーマはとくになく、その時思いついたことをなんでもいいので共有する形をとっています。重要なのは全員が必ず発言することです。
※下記画像は私の朝会での共有内容です。本当にその場で思いついたことを書いているのがわかりますね。 朝会_チェックインsample

スプリントふりかえり, スプリントレビュー

ファシリテーターがお題を提示し、それに対して発言する形をとっています。
お題が面白ければ面白いほど、場が盛り上がるのでファシリテーターの腕が試されます。
スプリント振り返り_アイスブレイクsample

特選50アイスブレイク集

ここまで説明して、「実際にやってみたいが特にテーマが思いつかないんだよな」という方も多いと思います。
お待たせしました、今まで私達がやってきたアイスブレイクのテーマを50個に絞ってご紹介しようと思います。
個人的に面白かったな、盛り上がったなというものは実際に書いた付箋も合わせて紹介します。
スクラムチームメンバーの許可をとって掲載しています。
※実際に提示したテーマの文言をそのまま掲載しています。

オーソドックス系

  • 今一番欲しいものはなんですか?
  • GWの予定は?
  • 最近はまっている動画コンテンツは?
  • 今年の夏の意気込み
  • 好きなことわざ
  • Amazonプライムデーで何を書いますか?
  • 三連休お楽しみ?お楽しみなこと!を!書いてください!
  • スマブラでよく使うキャラはなんですか?
  • 超おすすめ! 超おすすめな映画! ※ネトフリで見れるとなお良い
  • 最近使っていいなと思ったサービス

good_service

  • 年末年始にしたいこと、食べたいものは?
  • 今年のしくじりポイント
  • クリスマスにやろうと思ってること
  • 寝る前に絶対やっていること
  • 2月の目標を教えてください
  • おすすめの旅行先・観光地教えてください(都内近郊以外)
  • GWの予定を教えてください
  • 今まで行って一番がっかりした場所
  • 今一番欲しいものは?(現実的に買えるもの)
  • 好きなアニメ・マンガを教えて
  • 夏休み何する?
  • 最近あったすごく嬉しかったこと

グルメ系

  • 最近食べた美味しいもの教えて
  • 今食べたいもの・行きたいお店を教えてください
  • お昼に食べたいもの
  • 夏休み中に食べたいもの
  • 実家のお雑煮の内容を教えてください

お絵かき系

  • ルフィを何も見ずに書いてください
  • ピカチュウを書いてください

  • お絵描きハム太郎
  • まる子(ちびまる子ちゃん)を何も見ないで描いてください
  • TOYOTAのロゴをフリーハンドで書いてみよう
  • NIKEのスウォッシュをフリーハンドで書いてください

大喜利

  • あなたの自伝が出版されることになりました。タイトルを教えてください
  • アンミカが言いそうなことを教えて!
  • 「XXX殺人事件」この小説の最高の書き出しを考えてください
  • 女子高生から30点と評価された新発売のジュース。どんなジュース?

過去・思い出系

  • 最初の記憶を教えてください
  • 若い頃、大人になったら何になりたいと思っていましたか?
  • 小学校の先生との思い出をおしえてください
  • 高校生のときの将来の夢
  • そろそろ卒業シーズンですね。卒業式の思い出を教えて下さい
  • 嘘のようなエピソードを教えてください(思い付かない場合は嘘でもいいです)

その他

  • 人に言ったことない自分の癖/秘密
  • 今の気分を絵文字で表現すると?
  • 開発合宿どこでやりたい?
  • 1000万円ずつ何か買ってもらえることになりました。なににする?

終わりに

特選50個と書きましたが、50個ないかもしれません、すいません。
紹介したアイスブレイクテーマを活用いただき、コミュニケーションを促進させてください。
現在株式会社ROXXは一緒にはたらく仲間を募集中です。面白いアイスブレイクでわいわい楽しく働きたい方、どしどしご応募ください。 herp.careers

Amazon WorkMail で独自ドメインの E メール環境を 5 分で構築する

この記事は個人ブログと同じ内容です

www.ritolab.com


先日、急遽独自ドメインのメール環境が必要になったのですが、 Amazon WorkMail を利用して E メールのプラットフォームを構築したところとても体験が良かったので記録に残します。

Amazon WorkMail

Amazon WorkMail は、 E メールのプラットフォームを構築できるマネージド型サービスです。

https://aws.amazon.com/jp/workmail/

E メール、カレンダー、および連絡先にアクセスするためのウェブクライアントも用意されています。

サポートされているプロトコルには、EWS、ActiveSync、IMAPSMTP など。

つまり WorkMail を利用すれば、Gmailoutlook, Thunderbird のようなメールクライアントを使った E メールの送受信を可能にするプラットフォームを構築できます。

Amazon SES と WorkMail の違い

AWS の E メール関連サービスに Amazon Simple Email Service (Amazon SES) があります。

Amazon SES とは?
https://docs.aws.amazon.com/ja_jp/ses/latest/dg/Welcome.html

SES は基本的には配信のための仕組みです。

受信もできますが E メールを受信するための POP サーバーや IMAP サーバは含まれていないため、outlook などメールクライアントからは利用できません。

E メールクライアントを使用して送信と受信の両方が可能なソリューションが必要な場合は、Amazon WorkMail の使用が推奨されています。

Amazon SES の E メール受信の概念とユースケース
https://docs.aws.amazon.com/ja_jp/ses/latest/dg/receiving-email-concepts.html

利用の制限事項

2023 年 6 月時点で利用できるリージョンは以下の 3 つ

つまり国内でのメールデータ保管が必須であれば東京や大阪のリージョンが対応するのを待つ必要はあります。

導入

では利用までのセッティングを行っていきます。

Organization 作成

create organization ボタンを押下します。

organization 作成画面に遷移するので必要な項目を入力していきます。

Email domain

メールアドレスに使用するドメインを選択します。

Route 53 に登録済みのドメインを使うと素早く環境を構築できます。今回は Route 53 に登録済みのドメインを使用しています。

Route 53 hosted zone(Route 53 ホストゾーン)

organization で使用する Route 53 ホストゾーンを選択します。

Alias(エイリアス

エイリアスを選択します。

ウェブクライアントを利用する際は、ここで入力したエイリアス名が URL(サブドメイン)に反映されます。(https://<alias_name>.awsapps.com/mail)。

エイリアスは最大 45 文字です。小文字 (a ~ z)、数字 (0 ~ 9)、およびダッシュ (-) のみを含めることができます。

Advanced settings(高度な設定)

今回はデフォルトのままですが、より高度な設定を行えます。

User directory(ユーザーディレクトリ)

ユーザーを管理するディレクトリを選択します。

Encryption(暗号化)

データを保護するために暗号化キーを選択します。 暗号化キーはアカウントの AWS Key Management Service (AWS KMS) にあります。

  • Use Amazon WorkMail managed key(Amazon WorkMail 管理キーを使用する)
    • アカウントで作成した暗号化キーを使用します。
  • Use existing customer managed key (CMK)(既存のカスタマー マネージド キー (CMK) を使用する)
    • AWS KMS で作成した既存の CMK を使用します。

入力が終わったら「Create organization」ボタンを押下します。

organization の作成が始まり、数秒で作成が完了しました。

ドメイン設定(DNS レコード追加)

作成した organization に E メールで使うドメインDNS レコード設定していきます。

ただし Route53 に登録済みのドメインを指定した場合、ほとんどの設定は設定済みの状態になっていました。

メールアドレスとしてこのドメインを使うのは初めてなので、SPF と DMARC, そして MAIL FROM domain の設定のみここで行います。

SPF, DMARC の設定

SPF(Sender Policy Framework)DMARC(Domain-based Message Authentication, Reporting, and Conformance) は、メール認証のためのプロトコルです。

SPF(Sender Policy Framework)

SPFは、ドメインの送信元認証を提供するための技術です。メールサーバーは送信ドメインSPF レコードを確認し、メール送信元の IP アドレスが正当な送信元であるかを検証します。

SPF の設定にはドメインDNS レコードに特定の設定を追加する必要があるため、ここでも TXT レコードの登録が必要です。

  • SPF を設定するメリット:
    • メールの送信元を偽装するスプーフィング攻撃を防ぐことができます。
    • SPFをサポートする受信サーバーは、SPF に合致しないメールをスパムとしてマークすることができます。
  • SPFを設定しないデメリット:
    • SPF を設定しない場合、メール送信者のドメインの信頼性が低下し、受信者のメールフィルターによってスパムとしてマークされる可能性が高まります。

DMARC(Domain-based Message Authentication, Reporting, and Conformance)

DMARC は、ドメイン認証に基づいたメール送信者認証および報告のためのプロトコルです。DMARC では、SPF および DKIM の結果を組み合わせてドメインの認証を行い、不正なメール送信を防止します。

  • DMARCを設定するメリット:
    • ドメインの認証により、送信元ドメインの信頼性を高めることができます。
    • メール送信者ドメインのスプーフィング攻撃を防ぎ、受信者に信頼性のあるメール送信を提供します。
    • DMARCレポートを受信することで、ドメインの送信状況や潜在的な不正利用の警告を受けることができます。
  • DMARCを設定しないデメリット:
    • メール送信者のドメインの信頼性が低下し、受信者のメールフィルターによってスパムとしてマークされる可能性が高まります。

つまるところ SPF も DMARC もメール送信の信頼性が低下しないように要設定項目。ということで設定していきます。

SPF と DMARC に関しては簡単で、画面右上の「Update all in Route 53」ボタンを押下するだけで設定が完了します。

ボタン 1 つで TXT レコードの登録が完了し、SPF と DMARC に関しても Verified になりました。

カスタム MAIL FROM ドメインの設定

続いてカスタム MAIL FROM ドメインの設定を行っていきます。

Custom MAIL FROM domain は、Amazon SES を使用して送信されるメールの送信者アドレスを自分のドメインに基づいたアドレスにカスタマイズする機能です。これにより、メールの信頼性と認識性を向上させることができます。

この設定は Amazon SES の画面から行います。

編集ボタンを押して表示される画面から、 MAIL FROM ドメインを入力し、「変更の保存」ボタンを押下します。

保存したら、このドメインを MAIL FROM として設定するために発行された MX レコードと SPF レコードをドメインDNS プロバイダーに発行する必要があるのですが、これも「DNS レコードの Route53 への発行」ボタンを押下するだけで完了します。

これで晴れて、全てのドメイン関連の設定が完了しました。ここまでで 3 分程度です。

ユーザーの作成

E メール環境を構築できたので、ユーザーを作成して利用してみます。

メールアカウント名など、必要な情報を入力して作成ボタンを押下します。

これでユーザーも作成完了です。

test_user として E メールを使い始められます。

ここまででおよそ 5 分。あっという間です。

web クライアントからメールの送受信を行ってみる

E メールの環境構築が終わったので、ウェブクライアントから作成したユーザーで E メールの送受信を行ってみます。

ウェブクライアントの URL は、https://<alias_name>.awsapps.com/mail です。

ログインしました。シンプルな画面で使いやすそうです。(ただし執筆時点では日本語の UI には未対応でした)

メールを送信してみます。

無事にメールが送信され、送信先で受信できました。

返信してみましたが受信も問題なくできました。

まとめ

Amazon WorkMail は「セキュリティに優れた企業向け E メールおよびカレンダーのマネージド型サービス」であるわけですが、個人としてもとても有用なサービスだと感じました。

急に独自ドメインのメール環境が必要になった時にこうして一瞬で作成できるのはとても助かりますし、 Route 53 に登録済みのドメインから数分で環境を構築できるのはとても便利でした。

と、今回は構築スピードだけにフォーカスしたので最後に Amazon WorkMail の優れている点を紹介して終わりにします。

  • セキュリティ
    • データの暗号化・ファイアウォールの保護・不正アクセスからの防御など、厳格なセキュリティ対策を実施している。組織の機密情報や個人データを保護するための強力なセキュリティ機能を提供する。
  • 信頼性
    • Amazonクラウドインフラを活用するため高可用性とスケーラビリティが実現されている。メールの受信と送信が確実に行われ、業務における重要なコミュニケーションの信頼性を確保できる。
  • 他サービスとの連携
    • Google Workspace でいうところの email(Gmail), storage(drive), web meeting(meets) は、AWS では WorkMail, WorkDocs, Chime で代替が可能。

特に WorkMail, WorkDocs, Chime の連携はどんな体験になるのか気になります。次の機会に試してみることにします。


現在 back check 開発チームでは一緒に働く仲間を募集中です。 herp.careers herp.careers herp.careers

おすすめフレームワーク#1:新規事業開発編

はじめに

株式会社ROXXのプロダクトマネージャー、松野広志です。
世の中には先人たちが開発した各種フレームワークが多々存在し、それらを活用することで、物事を整理しやすくなり、議論や検討をスムーズにおこなうことができます。

そこで、プロダクトマネージャーにおすすめのフレームワークを「新規事業開発編」「分析編」「機能開発編」の3つに分けて記事にしたいと思います。今回は、その中の新規事業開発時に役立つフレームワークをご紹介します。

※ これからプロダクトマネージャーを目指す方に向けた内容です。
※ 各フレームワークを詳細に説明する記事ではありません。


1.ビジョン策定:Product Vision Framework

https://www.prodpad.com/blog/product-vision-template/

出典

ご存知の方も多いと思うこのプロダクトビジョンフレームワークは、ジェフリー・ムーアの著書である『Crossing the Chasm』が元であり、起業家が投資家に対して行うエレベーターピッチのテンプレートとして紹介されました。もし30秒以内で自分のプロダクトをピッチできない場合、ビジョンは準備不足であり、再試行が必要とのことです。

フレームワークの使い方

プロダクトビジョンフレームワークは、顧客とユーザーの特定、および関連する目標の説明に使用されます。これは、プロダクトマネージャーが戦略的なプロダクトビジョンを形成するための便利なツールであり、より良いプロダクトロードマップの作成や開発プロセスでの重要な意思決定をサポートします。

プロダクトビジョンの組織への浸透を促すために「プロダクトビジョンを主要なステークホルダーと一緒に作成・更新する」「作成したプロダクトビジョンはプロダクト戦略の資料(プロダクトロードマップも含む)の序盤に掲載する」といったこともおすすめです。

逆に、ビジョンの設定や浸透が不十分な場合、組織内でプロダクトのあるべき姿や状態レベルの認識にズレが生じ、無駄なコミュニケーションや手戻りが発生しやすくなるので注意が必要です。

画像
https://xmind.app/mindmap/elevator-pitch-for-airbnb/YHS9Px/?from=gallery/

2.ビジネスモデル:Business Model Canvas

Strategyzer

出典

ビジネスモデルキャンバスは、スイスのビジネス理論家、作家、講演者、コンサルタント、起業家であるアレックス・オスターワルダー氏の著書『ビジネスモデルジェネレーション』から学ぶことができます。
ビジネスモデルキャンバスでは、ビジネスモデルを9つの要素に分解し、「パートナー、主要活動、キーリソース、価値提案、カスタマーリレーションシップ、顧客、販売チャネル、収入の流れ、コスト構造」について深いレベルで理解することに役立ちます。

フレームワークの使い方

プロダクトマネージャーにとって、機能のアイデア出し、優先順位付け、問題解決、価格設定など、プロダクトマネジメントに特化した活動に取り組む前に、プロダクトのビジネスモデルを理解することは重要です。このフレームワークは、プロダクトのビジネスモデルを360度見渡すことができ、プロダクトマネージャーの戦略的な洞察力を高めるのに役立ちます。

もし、ちょっとしたビジネスアイディアが頭に浮かんだ直後にビジネスモデルキャンバスを書こうとすると、ネットで検索して出てくるビジネスモデルのサンプル程度の内容までしか書けないと思います。それは、顧客や市場についての解像度が低いからに他なりません。
多くのプロダクトマネージャーは、各項目を具体的な内容で埋められるだけの解像度を上げるべく、日々「顧客との面談回数を増やす」「顧客を観察する」「プロトタイプを提案する」「データを分析する」「ドメイン知識を深める」「専門家に相談する」等の地道な活動を繰り返し、そこに相当の時間を掛けていると思います。(プロダクトマネージャー以外の方が請け負うこともあると思います)
同じように解像度を高め、ビジネスモデルをアップデートしていただけたらと思います。

※ 解像度についてのおすすめ書籍


3.whole Product Canvas(参考)

画像
whole Product Canvas

出典

「何だそれ、聞いたことないぞ」と思われると思いますが、それはそのはずで、リーンキャンバスとwhole product canvasキャズム理論の時間軸を組み合わせた、私のオリジナルのフレームワークです。既存のフレームワークを絶対なものと思わず、必要とあらば自社で使いやすい形に改良することもご提案したく、ここでご紹介させていただきました。

フレームワークの使い方

プロダクトの進化とともに対峙する顧客が変わるため、組織の関係者とプロダクトの過去・現在・未来の姿を共有するために利用しました。
https://note.com/matsuno_hiroshi/n/n45efb2b0e923


最後に

フレームワークのご紹介と作成と活用について書かせていただきました。
当記事が、これからプロダクトマネジメントをおこなう方のお役立てたらと幸いです。

[募集について]

現在 back check 開発チームは一緒にはたらく仲間を募集中です!!

- バックエンドエンジニア:https://herp.careers/v1/scouter/Bx7swv57kcgM

- リードバックエンドエンジニア : https://herp.careers/v1/scouter/kDFR0ZfLKWlJ

- プロダクトデザイナーhttps://herp.careers/v1/scouter/klIFYKELaF8Y

この記事は個人ブログと同じ内容です

 

プロダクトロードマップの種類と活用と作り方

はじめに

株式会社ROXXのプロダクトマネージャー、松野広志です。様々な立場の方が「ロードマップ」と口にしますが、「あれ?この人と自分のイメージするロードマップは違う気がする」「正式なロードマップってあるの?」と不安になったことはありませんか。私は「有名な方や企業が作るロードマップとは違う」と言われて困惑したことがあります。ネットで検索すれば、ロードマップに関する記事は豊富に出てきますし、どうやらロードマップの種類や呼び方も多岐にわたるようです。

そこで、とある書籍と公開されているプロダクトロードマップを参考に、4つのロードマップをご紹介したいと思います。「たった4つだけ?」と思われるかもしれませんが、結局のところ自社に適したロードマップの形はメンバーやステークホルダーと協議して決めていただくことが一番だと思いますので程々のご紹介にさせていただきました。
この記事を読んでいただいた方の、自社でロードマップを作成する目的を考え、組織(チーム)に適した形のロードマップを作成する際の参考になれば幸いです。

プロダクトロードマップとは

※ この内容は他の書籍やブログでも取り上げられていますので、スキップしていただいても結構です。

プロダクトロードマップとは、長期の時間軸でプロダクトビジョンと方向性を表現したプロダクト戦略の要約であり、ステークホルダーにプロダクト開発における「なぜ(Why)」と「何を(What)」を伝えるものです。そのため、市場のダイナミクス(生産者と消費者の価格と行動に影響を与える力)に沿って作成されることが求められます。

プロダクトロードマップを活用する効果

プロダクトロードマップは、ステークホルダー(顧客を含む)に対して短期・長期の目標を伝えるためのツールです。これを活用することで、プロダクトチームは重要なマイルストーンに集中し、将来の目標における不確実性を減らすことができます。また、チーム全員が焦点を当てるべき事柄を把握することができるため、広報やマーケティングチームは新情報の発信計画を立てることができ、セールスチームはプロダクトイメージを確認できます。さらに、CSやサポートチームはリリースのタイミングに合わせたサポート策を立案することができます。

プロダクトロードマップの構成要素

プロダクトロードマップに必要な情報には、以下の要素が必要です。
・プロダクトビジョン
・プロダクト戦略
・目標又は構想
・開発するプロダクト(機能)概要と開発タイミング
・各プロダクトや機能の背景にある価値
・機能の優先順位

ロードマップの種類と活用

次に、4種類のロードマップを見てみたいと思います。

1.ポートフォリオ型ロードマップ

このロードマップは、組織やプロダクト群に複数のプロダクトが存在する場合に、各プロダクトの戦略、タイムライン、イニシアチブを定義するために使用されます。これにより、事業責任者やプロダクトマネージャー同士が複数のプロダクト間でのコラボレーションを容易に行うことができます。また、大規模なプロジェクトの概要スケジュールと類似していると考えられます。参考までに、マネーフォワード社とLayerX社のロードマップへのリンクを以下に掲載します。

『マネーフォワード クラウド』、インボイス制度対応における新機能と開発ロードマップを発表
バクラクインボイス制度への対応方針と開発ロードマップ

2.目標と指標型ロードマップ

このロードマップは、特定のプロダクトの戦略目標と指標をステークホルダーに示すために使用されます。目標は高いレベルのマイルストーンであり、指標はその目標を達成するために必要な詳細な活動のリストを表現します。このロードマップは、共通の目標を設定するために単一または複数のプロダクトラインに適用することができます。

What are product goals and initiatives?

目標: 12 か月以内にアプリの最高評価を獲得する
指標: iOS および Android マーケットプレイスで第 1 位の評価を受ける
目標:前年比で収益を 2 倍にする
指標: +1 億ドルの収益
目標:最大のパートナー エコシステムを構築
指標: +100 パートナー

見た目が似ているものの、アウトカムロードマップ(OUTCOME BASED ROADMAPS)という別のタイプのロードマップが存在します。このロードマップは、アウトカム(成果)を重視して表現される点が特徴です。目標と成果の表現方法の違いにより、ステークホルダーの意識も異なるでしょう。
適切に組み合わせて使用するのも良いと思います。例えば、機能改修やチューニングに関する改修には目標を設定し、ユーザーの体験に変化をもたらすような新機能開発については成果を表現するといったイメージです。

目標:実現・達成をめざす水準。
成果:あることをして得られたよい結果

goo辞書

3.リリース型ロードマップ

リリース型ロードマップは、プロダクトのリリース計画に使用されます。このロードマップでは、アルファリリース(MVP - Minimum Viable Product)、ベータ版リリース(ベータ版顧客向け)、最終的な市場/顧客向けリリースなど、各リリースで提供される機能やアイデアステークホルダーと共有するための計画が立てられます。また、機能やそれらの実施予定のタイムラインを追跡し、エンジニアリングのリソースを調整する上でも役立ちます。

4.ステータス型ロードマップ

ステータス指向のロードマップでは、プロジェクトを「現在」「次」「将来」の3つのフェーズに分けて表現します。このロードマップは、プロダクト開発の進捗状況を容易に把握できる利点があります。一般に、外部公開を目的として作成されることが多く、検索で容易に見つけることができるのがこのタイプのロードマップです。

クラウド・エッジ間インフラストラクチャ基盤「Arm Neoverse」の次世代ロードマップ

5.その他のロードマップ

ロードマップは、Adobewhatfixなどの解説で述べられているように、まだまだ多くの種類が存在します。興味のある方はぜひ調べてみてください。
また、マイクロソフト365のロードマップを、どのような対象者を想定して作成されているのかを考えながら参照してみるのも面白いと思います。

ロードマップの作り方

誰とどのように作るか

ロードマップは、主要なステークホルダー全体に影響するものであるため、ステークホルダーと協調して作成することが求められます。そのために、プロダクトマネージャーは、ステークホルダーの間を何度も往復して調整を図ることに努めます。創業間もないスタートアップでなければ、こういった行動はどの会社でも共通だと思います。
以下、ロードマップの作成例として参考にしてください。

1.経営陣、事業責任者

経営陣、事業責任者レベルの方と、会社の経営方針や事業戦略を元に、ハイレベルで非公開な内容のプロダクトに関するディスカッションをおこないます。
その際の会話で役に立つGROWモデルをご紹介します。(詳細は割愛)

G: Goal(目標・欲しい結果)
R: Reality Check(現実の確認)
O: Options(選択肢)
W: Will(意志)
ゴールに向かう意志や、実行責任を果たす気持ち

GROWモデル

そして、経営陣、事業責任者との対話を元に、ざっくりとした四半期ごとのロードマップを作成します。ここから非公開情報を削ぎ落とせば、外部に公開する「ステータス型ロードマップ」を作成することができるでしょう。

2.開発、セールス、CSなどの関係者

経営陣や事業責任者と事業方針についての確認が取れ、ざっくりとした絵が描けたら、具体的な戦略とロードマップのたたきを作成し、他のステークホルダーとの対話を重ねます。関係者にcore→why→what→howの流れを説明し、フィードバックを得ます。対話から細かい施策や機能について具体化し、開発の見込みを織り込みます。完成したロードマップは再度、経営陣、事業責任者にレビューをおこない承認を得ます。

ここからPRDを作成し、開発のチケットを作成しバックログに積む過程で「リリース型ロードマップ」を作成することができるでしょう。

最後に

プロダクトロードマップの作成は、多くの関係者との対話と調整が必要であり、事業やプロダクトの将来を描く重要な仕事です。
もし、ロードマップを描くチャンスが訪れた際には、思い切って関係者に話を聞いてまわり、自分が考えられる最善のロードマップを描いてみてください。
綺麗なロードマップを描けなくても、ないより全然マシです。ロードマップに対して、周囲から「あれが抜けている」「あれを実現したい」「こんなことはできない」「誰が決めた」「競合はこうだ」とステークホルダーと議論になり、その対話こそが組織とプロダクトの前進に役立つからです。
ロードマップを描いた人には、豊富な情報と様々な立場のステークホルダーの考えを知ることができ、経験とスキルにつながりますので、ぜひ挑戦してみてください。

[募集について]

現在 back check 開発チームは一緒にはたらく仲間を募集中です!!

- バックエンドエンジニア:https://herp.careers/v1/scouter/Bx7swv57kcgM

- リードバックエンドエンジニア : https://herp.careers/v1/scouter/kDFR0ZfLKWlJ

- プロダクトデザイナーhttps://herp.careers/v1/scouter/klIFYKELaF8Y

この記事は個人ブログと同じ内容です

 

ChatGPTの構造化にはTOML形式が良さそう

この記事は個人ブログと同じ内容です

note.com


ChatGPTを使ったテキストの構造化を行うタスクをいくつか進めていく中で、どういうフォーマットが構造化に向いているのかというのは非常に重要な要件になるかと思っています。 よく聞くのはJSON形式にはなるのですが、果たしてLLMにおいて最適なのかを考え直した時に、TOML形式がベストなんじゃないかというのが現状の答えなのでそこに至った理由を紹介していきます。

本件は先日開催されたLLM Meetup Tokyo #2のLTで紹介させていただきました。

lu.ma

当時発表したスライドはこちら

docs.google.com

TOMLってなんだっけ?

TOMLはTom's Obvious, Minimal Languageの略で、設定ファイルのフォーマットとして広く使用されています。この形式は、キーと値のペアを使用してデータを表現します。そのシンプルさと直感的な構造が特徴であり、人間が読み書きしやすいフォーマットとなっています。

TOMLは設定ファイルとして広く使用されており、特にソフトウェアプロジェクトでの利用が多いです。以下に、TOMLを設定ファイルとして使っている具体的な例をいくつか紹介します。

静的サイトジェネレータ: JekyllやHugoなどの静的サイトジェネレータでは、サイトの設定やビルド設定をTOML形式で書くことがあります。

継続的インテグレーション(CI): GitHubやGitLabなどのCIツールでは、ビルドやテストの設定をTOML形式で書くことがあります。

プログラミング言語: PythonやRustなどのプログラミング言語では、パッケージやライブラリの設定をTOML形式で書くことがあります。

それでは、TOMLと他の人気のあるデータフォーマット(JSONYAMLMarkdown)との違いについて見ていきましょう。

JSONとの比較

まず、JSONについて考えてみましょう。

利点

  • 配列やオブジェクト、数値、文字列など、多様なデータタイプを柔軟に表現できます。
  • 多くのプログラミング言語で標準で実装されているため、幅広い用途に利用できます。

欠点

  • ヒューマンリーダブルではありません。改行コードやダブルクォーテーションはエスケープしなければならず、パースしないとデータの内容を理解するのが難しいです。
  • 開始と終了をカッコで囲む必要があるため、ストリーム処理に向いていません。

YAMLとの比較

次に、YAMLについて見ていきましょう。

利点

  • JSONの利点を全て引き継いでいます。
  • データの開始と終了をカッコで囲む必要がないため、ストリーム処理に適しています。

欠点

  • インデントが重要になり、不必要にトークンを消費します。
  • 複数行の文字列に関しては、パース後のインデントが元のインデントと異なる可能性があります。

Markdownとの比較

最後に、Markdownについて考えてみましょう。

利点

  • フラットな表現が可能で、データの開始と終了をカッコで囲む必要がありません。そのため、ストリーム処理に適しています。
  • ヒューマンリーダブルである。

欠点

  • 複雑なデータ構造には向いていない。特にネスト構造は限界がある。

これらのフォーマットと比較して、TOMLはどのようなメリットがあるのでしょうか?

TOMLのメリット

TOMLの大きな利点は、上記の3つのフォーマット(JSONYAMLMarkdown)の欠点を解消していることにあります。

  • ヒューマンリーダブル: TOMLは人間が読みやすい形式を提供しています。JSONが持つエスケープが必要な改行コードやダブルクォーテーションの問題を持ちません。

  • ストリーム処理に対応: JSONのようにデータの開始と終了をカッコで囲む必要がないため、ストリーム処理に対応しています。

  • 複雑なデータ構造に対応: Markdownが持つネスト構造の限界という問題を解消しています。

  • 不必要なトークンの消費を避ける: YAMLが持つインデントによるトークン消費という問題も持ちません。

以上のように、TOMLはこれらのフォーマットが持つ一部の欠点を解消し、柔軟で読みやすいデータ表現を提供しています。これらの理由から、ChatGPTの構造化にはTOML形式が良さそうと言えます。

具体例を用いて比較検証

大規模言語モデルをより効果的かつ効率的に制御するためのツール、guidanceを用いてジョーク生成と評価のツールを作成する方法について説明します。

ジョーク生成と評価ツールの作成

まず、guidanceというパッケージを用います。これは、Microsoftが開発した大規模言語モデルを制御するための言語です。guidanceを用いることで、生成、プロンプト、論理制御を一つの連続したフローに組み合わせることが可能となり、言語モデルがテキストを処理する方法に合わせて構造化することができます。

今回のツールでは、我々が大規模言語モデルに対してジョークを生成させ、そのジョークが面白いかどうかを評価させる、というプロセスをコントロールします。

具体的なコードは以下の通りです。

import guidance

guidance.llm = guidance.llms.OpenAI('gpt-3.5-turbo')
prompt = guidance(
'''{{#system~}}
You are a helpful assistant.
{{~/system}}
{{#block hidden=True~}}
{{#user~}}
Please tell me a joke
{{~/user}}
{{#assistant~}}
{{gen 'joke'}}
{{~/assistant}}
{{~/block~}}
{{#user~}}
Is the following joke funny? Why or why not?
{{joke}}

The format is {{format}}.
This should contain
"funny": "yes" or "no"
"why": "reason"
"better_jokes": list of joke text and why this is better
{{~/user}}
{{#assistant~}}
{{gen 'output'}}
{{~/assistant}}''')
print("====format: yaml====")
print(prompt(format='yaml')['output'])
print("====format: json====")
print(prompt(format='json')['output'])
print("====format: toml====")
print(prompt(format='toml and use brackets inline tables')['output'])
print("====format: markdown====")
print(prompt(format='markdown')['output'])

このコードでは、まず大規模言語モデルにジョークを生成させ、次にそのジョークが面白いかどうかを評価させます。評価は、そのジョークが面白いかどうか("funny")、なぜ面白いかまたは面白くないか("why")、そしてより面白いジョークの例とその理由("better_jokes")を含む形式で行います。

また、出力形式は四つの異なる形式、すなわちYAMLJSON、TOML、およびMarkdownで表示します。

出力結果は以下の通りです。(GPT 3.5なので構造化データ以外も出力されてしまっています。)

====format: yaml====

Here's the YAML format for the joke:

funny: "yes"
why: "The joke is funny because it plays on the idea of a tomato turning red due to embarrassment or shame, but instead it turns red because it sees salad dressing, which is unexpected and humorous."
better_jokes:
  - "Why did the scarecrow win an award? Because he was outstanding in his field."
    why: "This joke is better because it uses a pun to create a humorous play on words. The phrase 'outstanding in his field' is typically used to describe someone who is an expert in their profession, but in this case, it's taken literally to refer to a scarecrow in a field."
  - "Why don't scientists trust atoms? Because they make up everything."
    why: "This joke is better because it uses a clever twist on words to create a humorous punchline. The idea that atoms make up everything is a scientific fact, but the joke subverts this by suggesting that atoms are untrustworthy." 

I hope this helps! Let me know if you have any other questions.

====format: json====

{
  "funny": "yes",
  "why": "The joke is funny because it plays on the idea of a tomato turning red when it's ripe, but in this case, it's because it's embarrassed. The punchline is unexpected and silly, which makes it amusing.",
  "better_jokes": [
    {
      "joke": "Why did the scarecrow win an award? Because he was outstanding in his field!",
      "why": "This joke is better because it has a clever play on words with the double meaning of 'outstanding.' It also has a clear setup and punchline structure."
    },
    {
      "joke": "Why don't scientists trust atoms? Because they make up everything!",
      "why": "This joke is better because it has a clever twist on the idea of atoms being the building blocks of everything. It also has a clear setup and punchline structure."
    }
  ]
}

====format: toml====

Here's the toml format for the joke:

[joke]
text = "Why did the tomato turn red?\nBecause it saw the salad dressing!"
funny = "yes"
why = "The joke plays on the idea of a tomato blushing or turning red when it sees something it likes, but in this case it's because it sees salad dressing. It's a simple and silly joke that can make people chuckle."

[[better_jokes]]
joke = "Why did the scarecrow win an award?\nBecause he was outstanding in his field!"
reason = "This joke is a play on words, using the double meaning of 'outstanding' to create a pun. It's a classic joke that many people find funny."

[[better_jokes]]
joke = "Why don't scientists trust atoms?\nBecause they make up everything!"
reason = "This joke is a clever play on words, using the double meaning of 'make up' to create a pun. It's a bit more sophisticated than the tomato joke, but still silly and fun." 

I would say that the tomato joke is mildly funny, but it's a bit too simple and predictable. The punchline is easy to guess, so it doesn't have a lot of surprise or cleverness to it. However, some people might still find it amusing because it's cute and harmless.

As for the better jokes, I've included two examples that use puns to create humor. These jokes are a bit more clever and unexpected, which can make them more satisfying to hear. Of course, humor is subjective, so what one person finds funny might not work for someone else.

====format: markdown====

"funny": "yes"
"why": "The joke is funny because it plays on the idea of a tomato turning red when it's ripe, but in this case, it's because it's embarrassed by the salad dressing. It's a simple and silly joke that can make people chuckle."

"better_jokes": 
- Why did the scarecrow win an award? Because he was outstanding in his field.
  - This joke is better because it has a pun that is unexpected and clever.
- Why don't scientists trust atoms? Because they make up everything.
  - This joke is better because it has a clever twist on a common phrase and plays on the idea of atoms being the building blocks of everything.

まとめ

TOMLとLLMの相性について紹介させていただきました。もしこういうフォーマットのほうがいいよみたいな意見があればぜひいただけると嬉しいです。

また、ROXXでは積極的に採用しています!

Engineer の求人一覧 - 株式会社ROXX