⚖️

法令APIハッカソンでLangChain×Next.js(NextUI/TailwindCSS)で高速開発した話

2023/11/25に公開

はじめに

デジタル庁主催の法令 API ハッカソンに参加し、
我々のチームは「Legal Lens」という Web サービスを開発しました。

https://twitter.com/y_uucu/status/1728278539500470562

このサービスは、入力された文書に対して、
AIを用いて法令遵守のスコア付けと関連法令の提示を行います。
今回は、ハッカソンの話を少しとシステムのアーキテクチャと技術スタックについて紹介します。


ハッカソンの概要などに関しては以下の記事がわかりやすいため、ぜひご覧ください。
https://note.com/growsic/n/n8f91f6e8fcfc

デジタル庁で他作品の紹介等もされています
https://www.digital.go.jp/policies/legal-practice/hackathon/participants

ハッカソンによる開発

私たちのチームは、1名のプランナーと2名のバックエンドエンジニアで構成されていました。
ハッカソンは2023年11月10日(金)から11月17日(金)まで行われました。

11 月 10 日(金) ~ 11 月 12 日(日): アイデア出し,企画練り

デザイナーがいないチームだったため、自分達が作りたいものを決めた後は、
ベンチマークとなる web サービスを決め、google slide でざっくり
アウトプットイメージがずれないよう画面イメージを作成しました。
(時間あれば figma を使ってみたかったです)

11 月 13 日(月) ~ 11 月 16 日(木): 検証および開発

残り4日間が実質の開発期間で、
普段の業務との兼ね合いもあり、限られた時間内での開発でした。
その中で、良い感じの UI になるかつ 高速 で完成に持っていくための技術選定を行い開発を進めました。
技術周りは後述しようと思います。

11 月 17 日(金): 資料作成・発表

最終日は資料作成及び、発表を行いました。

開発周り

ざっくり以下のような技術選定をしました。

  • Next.js / TypeScript
  • NextUI / tailwind CSS
  • LangChain / OpenAI

Next.js

チームメンバー内のエンジニアは 2 名で、
どちらも主軸はバックエンドで Go を扱うことが多いメンバーでした。

今回は開発期間を考えて、Next.js のみでフロントバックエンドどちらの開発も行うことにしました。

良かった点としては、route handler により、
API のリクエスト・レスポンスを openAPI にて書き起こしたりする必要なく、
そのまま typescript で定義した構造体で api のやりとりを行ったため、スムーズな開発を行えました。

また、デプロイもvercel任せでインフラ周りの作業も省略することができました。

NextUI / tailwind CSS

デザイン面は、NextUI と tailwind CSS を使用しています。
UI コンポーネントライブラリに関しては、いろいろ選択肢はありましたがどれも学習コストに差はなかったため
モダンそうな NextUI を選定しました。

https://nextui.org/

綺麗な UI とクリックした時のアニメーションなどが気に入っています。

NextUI 自体が tailwind CSS の上に作られているためそのまま CSS フレームワークとして tailwind CSS を使用しました。

https://tailwindcss.com/

文字の色付けや細かい UI の調整など、css を書くことなく class の指定のみでいけたので、開発効率もすごく良かったと思います。

また、サイトの大まかな配置などですが以下のサイトが便利でした。
tailwind CSS で書かれている よくあるサイトのレイアウト のチートシートです。
https://tailblocks.cc/

文字サイズなど細かい調整は入れていますが、今回は以下のテンプレを元にレイアウトを組んでいます。

LangChain / OpenAI

今回のサービスの肝となる AI 周りです。
今回はプロンプトテンプレートのみ使用していますが、
Retrieval などで AI の回答の正確性を高めるアーキテクチャなどを検討していたので、
初期から LangChain を導入しての開発をしました。

https://js.langchain.com/docs/modules/model_io/prompts/prompt_templates/
https://js.langchain.com/docs/modules/data_connection/

OpenAI のモデルに関しては、最新の GPT モデルを使用しています (gpt-4-1106-preview)
AI による回答を API 側で parse するために json モードで json のレスポンスになるように固定しています。

response_format で json を指定できるようになったのは、11/06 の新モデルにて発表された機能だったためすごくタイミングが良かったです。
https://openai.com/blog/new-models-and-developer-products-announced-at-devday

また、法令関連の正確な回答を得るために、temperature を 0 に設定しています。

import { OpenAI } from "langchain/llms/openai";

export const llm = new OpenAI({
  temperature: 0,
  modelName: "gpt-4-1106-preview",
  modelKwargs: {
    // json_mode: https://platform.openai.com/docs/guides/text-generation/json-mode
    response_format: {
      type: "json_object",
    },
  },
});

法令 API に関して

こちらはswagger(openAPI)が提供されているため、APIのクライアントを自動生成して叩くことができすごく便利でした。

かなり丁寧に記載されており、法に明るくない自分でもこちらのAPI仕様書があれば叩ける状況すごく良かったです。

びっくりした点としては、法令の本文取得APIのレスポンスの定義があり、かなり複雑だったと言うことです。

  "law_full_text": {
    "tag": "Law",
    "attr": {
      "DataInfo": "230629e81k80m01",
      "Era": "Meiji",
      "Lang": "ja",
      ...
    },
    "children": [
      { "tag": "LawNum", "attr": {}, "children": [ "明治四十年法律第四十五号" ] },
      {
        "tag": "LawBody",
        "attr": {},
        "children": [
          {
            "tag": "LawTitle", "attr": { "Kana": "けいほう", "KanaSeion": "けいほう", ... },
            "children": [ "刑法" ]
          },
          { "tag": "EnactStatement", "attr": {}, "children": [ "刑法別冊ノ通之ヲ定ム" ] },
          { "tag": "EnactStatement", "attr": {}, "children": [ "此法律施行ノ期日ハ勅令ヲ以テ之ヲ定ム" ] },
          { "tag": "EnactStatement", "attr": {}, "children": [ "明治十三年第三十六号布告刑法ハ此法律施行ノ日ヨリ之ヲ廃止ス" ] },
          { "tag": "EnactStatement", "attr": {}, "children": [ "(別冊)" ] },
          { "tag": "TOC", "attr": {}, "children": [
              { "tag": "TOCLabel", "attr": {}, "children": [ "目次" ] },
              { "tag": "TOCPart", "attr": { "Num": "1" }, "children": [
                  { "tag": "PartTitle", "attr": {}, "children": [ "第一編 総則" ] },
                  { "tag": "TOCChapter", "attr": { "Num": "1" }, 
                      "children": [
                          { "tag": "ChapterTitle", "attr": {}, "children": [ "第一章 通則" ] },
                          { "tag": "ArticleRange", "attr": {}, "children": [ "(第一条―第八条)" ] }
                        ]
                  },
  ...

法令の特性上、文字に対して色々な付与情報があるため、かなり複雑な構造になるのは仕方なく、
この複雑な構造を定義できていることがすごいなと思いました。

今回のサービスでは、文字に対しての付与情報を扱っていないため、ピュアな本文のみが欲しかったのですが、かなり苦戦しました。
OSSでのparseライブラリの発展や、ピュアな本文のみを取得できる仕組みなどが出来てくると開発側としては嬉しそうだなと思いました。

まとめ

  • Next.jsでフロント・バックエンド完結させた
  • NextUIによるモダンなUIと tailwind CSS での高速開発ができた
  • LangChain/OpenAIでの生成AI利用、jsonモードとtemperature: 0での正確な回答

おわりに

法令として文章を読んだり触れる機会は今まで少なく、
改めてこう言う機会で法に触れてみると、法に守られて生活しているんだな、という実感を得ました。

またハッカソン初参加だったのですが、アイデア案出しや開発もすごく楽しく、
参加した他のチームの形のアウトプットもレベルが高くすごく刺激になりました。

法令API発展により、より法が便利に・身近に感じれるサービスなどが出てきそうだなと感じています。
この機会を提供していただいたデジタル庁の皆様、ありがとうございました。

Discussion