🖌

Geminiを用いた文章校正機能の作成 (Node.js使用)

2025/01/31に公開

はじめに

WED株式会社でMLエンジニアをしています、ishi2kiです。
弊社では、ユーザーの方々からレシートを買い取るアプリ「ONE」を開発運用しています。

現在弊社では、ONEに新しくレシート買取ミッションを追加する際、ミッション参加条件 (下図) の文章校正を人手で行っています。

しかし、この作業には少なくない労力がかかる上、人目でのチェックである以上どうしても抜けが生じてしまうことがありました。

そこで今回、Geminiを用いて自動的に文章校正を行う機能を作成することにしました。
校正する点は下の2つです。

  • 対象商品のタイポ修正
  • 対象店舗を「かな順」に並び替え

実装

まず、必要なライブラリをインストールします。

$ npm install @google-cloud/vertexai

※ GCPではなく、Google AIを使用する場合は、@google/generative-aiでもGeminiを利用することが可能です。

次に、Geminiにリクエストを送り、出力したテキストを返す関数を実装します。

import { VertexAI } from '@google-cloud/vertexai'

async function generate(prompt: string) {
  const vertexAI = new VertexAI({
    project: 'project_name',
    location: 'location_name',
  })
  const flash = vertexAI.getGenerativeModel({ model: 'gemini-1.5-flash' })
  const result = await flash.generateContent(prompt)
  const response = result.response
  const outText = String(response.candidates?.[0]?.content?.parts?.[0]?.text).trim()
  return outText
}

この関数を用いて対象商品のタイポ修正と、対象店舗の並び替えを行っていきます。

対象商品のタイポ修正

こちらはGeminiが上手く対応してくれて、比較的容易に実装することができました。

async function itemProofreading(itemText: string) {
  const base_prompt = `
  <INSTURCTIONS>
  文章中の商品名に誤植があれば修正してください。
  </INSTURCTIONS>
  
  <CONSTRAINTS>
  1. 商品名以外は修正しないでください。
  2. 修正後の文章のみを出力してください。出力の説明は不要です。
  2. 誤植がない場合は、入力をそのまま出力してください。
  </CONSTRAINTS>
  
  <FEW_SHOT_EXAMPLES>
  1. Example #1
  ...
  
  2. Example #2
  ...
  
  3. Example #3
  ...
  
  </FEW_SHOT_EXAMPLES>
  
  Input:
  <INSERT_INPUT_HERE>
  Output:
  `
  const prompt = base_prompt.replace('<INSERT_INPUT_HERE>', itemText)
  const proofreadText = await generate(prompt)
  return proofreadText

対象店舗を「かな順」に並び替え

こちらの要件に取り組むにあたり、まずは並び替えを行わせるプロンプトを与えてみました。しかし結果は惨敗、2回に1回は誤った出力を返していました...。

ただ、漢字→平仮名、アルファベット→平仮名への変換は高い精度でできていたので以下の2ステップで行うことにしました。

  1. Geminiで店舗を平仮名に直す
  2. 平仮名をソートするプログラムを作成してソートする

この手法により、機械的に処理できない部分をGeminiに任せ、かつ、高い精度で並び替えを行うことができました。

async function shopProofreading(shops: Array<string>) {
  const reviseReadingMap = (readingMap: Json) => {
    const entries = Object.entries(readingMap).map(([k, v]) => [
      k,
      v.replace(/\/|||||\(|\)| | ||-|!||\?|/g, ''),
    ])
    return Object.fromEntries(entries)
  }

  const sortShopsByHiragana = (shops: Array<string>, readingMap: Json) => {
    return shops.sort((a, b) => {
      const readingA = readingMap[a] || a
      const readingB = readingMap[b] || b
      return readingA.localeCompare(readingB, 'ja')
    })
  }

  const base_prompt = `
  <INSTRUCTIONS>
  以下の文字列を平仮名に変換してください
  </INSTRUCTIONS>
  
  <FEW_SHOT_EXAMPLE>
  ...
  </FEW_SHOT_EXAMPLE>
  
  Input:
  <INSERT_INPUT_HERE>
  
  Output:
  `
  const prompt = base_prompt.replace('<INSERT_INPUT_HERE>', shops.join('\n'))

  // かな読みをGeminiで生成
  const raw_result = await generate(prompt)
  // JSON形式に変換
  const readingMap = JSON.parse(raw_result.match(/\{[\s\S]*?\}/)[0])
  // 記号等を読みから削除
  const revisedReadingMap = reviseReadingMap(readingMap)
  
  // 読みを元にソート
  const sortedShops = sortShopsByHiragana(shops, revisedReadingMap)
  return sortedShops
}

結果

以上の機能を実装し、テストしてみました。

元の文章では、「ONEドリンク」の「ン」が消えていましたが、Geminiの力によって修正されました。
また、対象店舗についても「SuperMarket (すーぱーまーけっと)」が「魚の店 (さかなのみせ)」よりも前にきてしまっていましたが、こちらも正しい順序に修正されました。

まとめ

今回、Geminiを用いて2点の校正ポイントを自動化しました。
Geminiの力を実感した一方、「かな順並び替え」ができないなどの問題点もあり、LLMへの過信の危険性を再認識しました。
校正結果に関しては、一般的な名称・読みが率直な店舗名であれば高精度で校正ができた一方、マイナーな商品名・凝った読み方の店舗に対してはまだエラーも多かったです。
今後、Geminiのファインチューニングによって、このようなエラーを下げられるか検証したいと考えています。

WED Engineering Blog

Discussion