シリコンバレーでのスタートアップ立ち上げにおいて、法人設立(Incorporation)の手続きは常に課題です。デラウェア州でのC法人登記は定型化されているものの、専門家とのやり取りは複雑で、時間とコストがかかります。
「このプロセス、もっとスマートに自動化できないだろうか?」
LLMの登場で文書生成は容易になりましたが、法的手続きには1文字のミスも許されない正確性が求められます。ここで必要なのは、単発のプロンプトを投げることではなく、厳密なステート管理と検証ループを備えたエージェント・ワークフローです。
今回は最新のLangGraphを用い、米国法人設立に必要な「基本定款(Certificate of Incorporation)」のドラフト作成から検証、人間による承認までを行うAIエージェントのプロトタイプを実装します。「まず動くものを作る」というアプローチで、実動するPythonコードを通じてAgentic Workflowの構築手法を紐解いていきましょう。
【免責事項】
本記事で提供するコードおよび情報は技術的なデモンストレーションを目的としており、法的助言(Legal Advice)ではありません。実際の法人設立にあたっては、必ず弁護士や専門家の確認を受けてください。
2. アーキテクチャ設計:なぜ単一プロンプトでは不十分なのか
LLMに「デラウェア州の定款を作って」と一度に依頼すると、出力フォーマットが安定せず、必須条項の欠落や架空の法律引用といったリスクを制御できません。業務システム設計の観点から見れば、これは非常に危険な状態です。
信頼性の高いリーガルテック・エージェントの構築には、プロセスを細かく分解し、ステートマシン(状態遷移)として設計する必要があります。
法人設立プロセスのステートマシン化
構築するエージェントは、以下のステートを持ちます。
- Input Collection: ユーザーから会社名、株式数などのパラメータを受け取る。
- Drafting: LLMがパラメータに基づき、法的文書のドラフトを生成する。
- Validation: 生成されたドラフトが法的要件(デラウェア州会社法など)を満たしているか、別のLLMまたはルールベースで検証する。
- Human Review: 人間(ユーザーまたは弁護士)が内容を確認し、承認または修正を行う。
- Finalize: 承認された内容をPDF等の形式で出力する。
LangGraphによる循環フローの設計
LangGraphはLangChain上に構築されたライブラリで、グラフ構造を用いてLLMアプリケーションの制御フローを記述できます。特に循環(サイクル)を扱える点が非常に強力です。
本設計では、「Validation」でNGが出た場合、エラー内容を含めて「Drafting」に戻る自己修正ループ(Self-Correction Loop)を実装します。また、「Human Review」の前で処理を一時停止し、人間のフィードバック後に再開するHuman-in-the-Loopの仕組みも組み込みます。
これにより、AIの圧倒的なスピードと人間の確実な監督を両立させた、実用的なシステムが完成します。
2. 環境構築とデータモデルの定義
まずは開発環境を整えましょう。Python 3.10以上を推奨します。LangGraph周辺のエコシステムは進化が速いため、常に最新の安定版を使用することがセキュリティと機能面で重要です。
以下のコマンドで必要なライブラリをインストール(または更新)します。
pip install -U langchain langchain-openai langgraph pydantic
専門家の視点:
LangGraphおよびその関連ライブラリ(特にlanggraph-checkpoint)では、セキュリティ強化や機能改善が頻繁に行われています。例えば、公式情報によるとRCE脆弱性(CVE-2025-64439)への対応や、リモートグラフでのストリーム再開(resumableStreams)、チェックポイント機能の信頼性向上が図られています。開発を始める前に、必ず公式ドキュメントで最新のセキュリティ情報を確認し、対策済みのバージョンを使用してください。
Pydanticによる設立情報の厳格な型定義
LLMの出力制御で最も重要なのがデータモデルの定義です。法的文書に曖昧さは許されないため、Pydantic V2を使用して法人設立に必要な情報を厳格に定義します。長年の業務システム開発の経験からも、入力段階でのデータクレンジングはシステムの安定稼働に直結します。
ここでは、デラウェア州C法人の設立に必要な最小限の情報をモデル化します。
from typing import List, Optional
from pydantic import BaseModel, Field, field_validator
# 法人設立に必要な基本情報モデル
class CorporationInfo(BaseModel):
company_name: str = Field(..., description="正式な会社名(例: TechStart, Inc.)")
authorized_shares: int = Field(..., description="授権株式総数")
par_value: float = Field(..., description="額面金額(通常は0.00001や0.0001ドル)")
registered_agent_name: str = Field(..., description="デラウェア州の登録代理人名")
registered_agent_address: str = Field(..., description="登録代理人の住所")
incorporator_name: str = Field(..., description="発起人の氏名")
incorporator_address: str = Field(..., description="発起人の住所")
# バリデーション例:会社名はInc., Corp.などで終わる必要がある
@field_validator('company_name')
@classmethod
def validate_name_suffix(cls, v: str) -> str:
valid_suffixes = ['Inc.', 'Incorporated', 'Corp.', 'Corporation', 'Co.', 'Company']
if not any(v.endswith(suffix) for suffix in valid_suffixes):
raise ValueError(f"会社名は以下のいずれかで終わる必要があります: {', '.join(valid_suffixes)}")
return v
# エージェントの状態(State)を定義
from typing import TypedDict, Annotated
import operator
class AgentState(TypedDict):
corp_info: CorporationInfo # 入力データ
draft_text: Optional[str] # 生成された定款テキスト
critique: Optional[str] # 検証フェーズでの指摘事項
revision_count: int # 修正回数カウント
is_approved: bool # 人間による承認フラグ
このAgentStateが、LangGraphの各ノード間を流れる情報のコンテナとなります。最新バージョンではチェックポイント機能(Checkpointer)が強化されており、Stateの永続化により、プロセスの中断・再開や人間による承認(Human-in-the-loop)を堅牢に実装できます。
CorporationInfoでのバリデーションロジック(validate_name_suffix)実装に注目してください。入力段階で形式的なミスを排除し、LLMの無駄な推論を減らすことでエージェント全体の信頼性を高めます。
3. エージェントの実装:定款(Certificate of Incorporation)の生成
次にコアとなるロジックを実装します。ここではOpenAIのChatGPT(最新モデル)を想定しますが、AnthropicのClaudeなど、Function Calling(ツール使用)に対応した他の高性能モデルでも実装可能です。
ドラフト生成ノードの実装コード
まずは定款のドラフトを作成するノードです。前回の指摘事項(critique)がある場合は反映するように指示します。
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
# APIキーの設定(環境変数から読み込むことを推奨)
# os.environ["OPENAI_API_KEY"] = "sk-..."
# 高度な推論能力を持つ最新モデルを指定
llm = ChatOpenAI(model="ChatGPT", temperature=0)
def draft_node(state: AgentState):
print("--- DRAFTING CERTIFICATE OF INCORPORATION ---")
info = state['corp_info']
critique = state.get('critique')
# プロンプトの構築
system_msg = "あなたは米国の企業法務専門のAIアシスタントです。デラウェア州法に基づき、正確な定款を作成してください。"
# PydanticモデルのデータをJSON形式で埋め込む
user_msg = f"以下の情報に基づいて、Certificate of Incorporationを作成してください。\n\n{info.model_dump_json(indent=2)}"
if critique:
user_msg += f"\n\n【前回の修正指示】\n以下の指摘事項を反映して修正してください:\n{critique}"
prompt = ChatPromptTemplate.from_messages([
("system", system_msg),
("user", user_msg)
])
chain = prompt | llm
response = chain.invoke({})
return {
"draft_text": response.content,
"revision_count": state.get("revision_count", 0) + 1,
"critique": None # 修正したので指摘事項はクリア
}
法的要件チェックノードの実装
次に、生成されたドラフトを検証するノードです。LangChainの最新バージョンはPydantic V2を完全サポートしているため、標準的なPydanticモデルで構造化出力を定義します。
ここでは簡易的にLLMにセルフチェックさせますが、実運用ではルールベースのキーワード検索や別の検証用LLMを使うのがベストプラクティスです。
from typing import Optional
from pydantic import BaseModel, Field
# 構造化出力のための定義(Pydantic V2を使用)
class ValidationResult(BaseModel):
is_valid: bool = Field(description="法的に有効であればTrue")
critique: Optional[str] = Field(description="問題点の指摘。有効な場合はNone")
def validate_node(state: AgentState):
print("--- VALIDATING DRAFT ---")
draft = state['draft_text']
# 構造化出力モードを有効化
structured_llm = llm.with_structured_output(ValidationResult)
system_msg = "あなたはシニアパラリーガルです。提出された定款ドラフトをデラウェア州会社法(DGCL)の観点から厳しくチェックしてください。"
user_msg = f"以下のドラフトを確認し、必須条項の欠落や誤りがないか判定してください。\n\n{draft}"
prompt = ChatPromptTemplate.from_messages([
("system", system_msg),
("user", user_msg)
])
chain = prompt | structured_llm
result = chain.invoke({})
return {
"critique": result.critique if not result.is_valid else None
}
with_structured_outputを使うことで、検証結果をプログラムで扱える形式(BooleanとString)で確実に取得できます。これがエージェント制御の鍵となります。
StateGraphの構築
これらをLangGraphで繋ぎ合わせます。最新のLangGraph APIでは、STARTノードを使用してエントリーポイントを明示的に定義します。
from langgraph.graph import StateGraph, START, END
def should_continue(state: AgentState):
if state.get('critique'):
# 指摘事項があれば修正ループへ(ただし回数制限を設ける)
if state['revision_count'] > 3:
return "human_review" # 無限ループ防止のため人間に投げる
return "draft_node"
return "human_review"
workflow = StateGraph(AgentState)
# ノードの追加
workflow.add_node("draft_node", draft_node)
workflow.add_node("validate_node", validate_node)
# エッジの定義:開始点からドラフト生成へ
workflow.add_edge(START, "draft_node")
# 通常のエッジ:ドラフト生成後は検証へ
workflow.add_edge("draft_node", "validate_node")
# 条件付きエッジ:検証結果に基づいて分岐
workflow.add_conditional_edges(
"validate_node",
should_continue,
{
"draft_node": "draft_node", # 再生成ループ
"human_review": END # ここでは一旦終了し、Human-in-the-Loopへ
}
)
4. Human-in-the-Loopの実装:最終承認フロー
定款のような重要書類において、AIによる完全自動生成はリスクを伴います。コンプライアンスと正確性を担保するため、プロセスに人間が介在する「Human-in-the-Loop(HITL)」の設計が不可欠です。
LangGraphのCheckpointer機能を活用し、ワークフローの状態を永続化することで、特定のタイミングで処理を一時停止し、人間の判断を待って再開する実務的なフローが構築できます。
永続化と割り込み(Interrupt)の設定
開発段階の検証にはオンメモリで動作するMemorySaverが便利ですが、本番運用ではデータベースベースの永続化(例:PostgresSaverなど)が推奨されます。ここでは基本実装としてMemorySaverを用いて解説します。
グラフのコンパイル時にinterrupt_before引数を指定すると、指定ノードの実行直前で処理を一時停止できます。これにより、AIが勝手に最終決定を下すのを防ぎます。
from langgraph.checkpoint.memory import MemorySaver
# 状態を保存するためのチェックポインター設定
# ※本番環境ではPostgresSaverなどの永続化対応クラスの使用を推奨
checkpointer = MemorySaver()
# グラフのコンパイル時にinterruptを指定
# human_review_nodeの実行直前でワークフローが停止するように設定
app = workflow.compile(
checkpointer=checkpointer,
interrupt_before=["human_review_node"]
)
承認プロセスを組み込むため、human_review_nodeを定義してフローに追加します。このノードは、人間による承認アクション後に通過するゲートウェイとして機能します。
def human_review_node(state: AgentState):
# 人間による承認後に実行されるロジック
# 承認フラグの更新や、監査ログの記録などを行う
print("--- HUMAN APPROVED: Proceeding to finalization ---")
return {"is_approved": True}
# グラフへのノード追加
workflow.add_node("human_review_node", human_review_node)
# 条件付きエッジの修正:バリデーション後はレビューへ分岐
workflow.add_conditional_edges(
"validate_node",
should_continue,
{
"draft_node": "draft_node", # 修正が必要な場合
"human_review": "human_review_node" # バリデーション合格時はレビューへ
}
)
# レビュー完了で終了
workflow.add_edge("human_review_node", END)
# 設定を反映して再コンパイル
app = workflow.compile(
checkpointer=checkpointer,
interrupt_before=["human_review_node"]
)
実行と人間による介入操作
エージェントを実行すると、バリデーション通過後、human_review_nodeの直前でステータスが「待機中」となります。ここからが人間の出番です。
運用担当者は現在の状態(State)を取得して内容を確認し、そのまま承認して進めるか、修正指示を与えて手前のノードに戻すかを判断します。
# 初期ステートの定義
initial_state = AgentState(
corp_info=CorporationInfo(
company_name="FutureAI Inc.",
authorized_shares=10000000,
par_value=0.00001,
registered_agent_name="Delaware Agent Services",
registered_agent_address="123 Agent Way, Wilmington, DE",
incorporator_name="HARITA",
incorporator_address="456 Silicon Valley Blvd, CA"
),
draft_text=None,
critique=None,
revision_count=0,
is_approved=False
)
# スレッドIDを指定して実行(このIDで状態が管理されます)
thread_config = {"configurable": {"thread_id": "corp_setup_001"}}
# 1. エージェントの実行開始
# human_review_nodeの直前まで自動で進行し、停止します
for event in app.stream(initial_state, thread_config):
pass # 途中経過の出力処理等は省略
# 2. 停止状態での確認
# 現在のステートを取得し、生成されたドラフトを確認
current_state = app.get_state(thread_config)
draft = current_state.values.get('draft_text')
print(f"Current Draft for Review:\n{draft[:200]}...")
# --- ここで人間が判断を行います ---
# シナリオA: 内容に問題がない場合(承認)
# Noneを渡してストリームを再開すると、停止していたノードから実行が継続されます
# print("Approving draft...")
# for event in app.stream(None, thread_config):
# print(event)
# シナリオB: 修正が必要な場合(介入)
# update_stateを使用して、ステート内の情報を書き換えます
# ここでは「critique(批評)」フィールドを更新し、AIに修正意図を伝えます
print("Requesting changes...")
app.update_state(
thread_config,
{"critique": "第3条の目的規定に、AIソフトウェア開発に関する記述を具体的に追加してください。"},
# どのノードとして振る舞うかを指定(ここではバリデーションノードの結果として扱う例)
as_node="validate_node"
)
# 修正指示を反映させた状態で、処理を再開
# グラフのロジックに従い、ドラフト生成ノードへ戻って再生成が行われます
print("--- Sending feedback to AI agent ---")
for event in app.stream(None, thread_config):
print(event)
このように、get_stateで生成物の品質を担保し、必要に応じてupdate_stateで軌道修正を行う機能こそがLangGraphを採用する最大のメリットです。業務プロセスに組み込まれた信頼性の高いエージェントシステムを構築するには、このパターンの習得が欠かせません。
5. 出力と連携:PDF生成と電子署名への準備
テキストとして完成した定款は、最終的に署名可能なPDFにする必要があります。PythonであればMarkdownからPDFへの変換は容易です。
MarkdownからPDFへの変換
シンプルなアプローチとして、markdownライブラリとpdfkit (wkhtmltopdf) を組み合わせる方法があります。
import markdown
import pdfkit
def generate_pdf(text: str, filename: str = "Certificate_of_Incorporation.pdf"):
html = markdown.markdown(text)
# スタイルを追加して正式文書らしくする
styled_html = f"""
<html>
<head>
<style>
body {{ font-family: 'Times New Roman', serif; line-height: 1.6; margin: 40px; }}
h1 {{ text-align: center; text-transform: uppercase; }}
</style>
</head>
<body>
{html}
</body>
</html>
"""
pdfkit.from_string(styled_html, filename)
print(f"PDF generated: {filename}")
次のステップ:DocuSign API連携への展望
ここからは外部APIとの連携です。生成されたPDFをDocuSign eSignature APIに送信し、発起人(Incorporator)の署名用エンベロープ(Envelope)を自動作成するノードを追加すれば、設立プロセスはほぼ完了します。
# 概念コード(スタブ)
def send_to_docusign_node(state: AgentState):
pdf_path = "Certificate_of_Incorporation.pdf"
signer_email = "founder@example.com"
signer_name = state['corp_info'].incorporator_name
# DocuSign API Clientを用いてエンベロープを作成・送信
# envelope_id = docusign_client.create_envelope(...)
print(f"Sent to DocuSign for signature: {signer_name}")
return {"docusign_status": "sent"}
ここまで自動化できれば、従来数日かかっていたやり取りが数分で完結する未来が見えてくるでしょう。技術の本質を見抜き、ビジネスへの最短距離を描くことが、これからのAI開発には求められます。
まとめ
今回はLangGraphを用いて「米国法人設立エージェント」のプロトタイプを構築しました。ポイントを振り返ります。
- ステート管理: プロセスをInput, Draft, Validate, Reviewに分割し、循環フローを設計した。
- 構造化データ: Pydanticを用いて入力と検証結果を型安全に扱った。
- Human-in-the-Loop: チェックポイント機能を使い、人間の承認プロセスをシステム内に組み込んだ。
これは単なる効率化ではなく、法務プロセスをコードとして定義し、バージョン管理とテストを可能にすることを意味します。AI駆動開発(AI-Driven Development)のアプローチにより、複雑で不透明な業務プロセスを透明でスケーラブルなソフトウェア資産に変えることができます。
実運用にはセキュリティ対策や詳細な法的知識の埋め込み(RAGなど)が必要になりますが、その第一歩は踏み出しました。まずは動くものを作り、検証を重ねていくことが成功への近道です。
コメント