hirokawaguchi/open-genai
hirokawaguchi/open-genai has 139 GitHub stars and 11 forks. GitHot measured 0 stars in 24 hours. Last checked 2026-11-09 17:44 UTC. Last push 2026-11-01.
デジタル庁のガバメントAI「源内(GENAI)」を完全ローカル(ローカルLLM/OpenAI互換)で動かす非公式プロジェクト。SAML認証(Keycloak)・RAG(Qdrant)・文字起こし(Whisper)・画像生成(SD)・チーム単位ナレッジをローカル完結。
Measured star growth
- in 24h
- 0
- in 7 days
- Update delayed
- in 15 days
- Update delayed
- in 1 month
- Update delayed
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
README.md
OriginalOpen GENAI
デジタル庁がオープンソースで公開したガバメント AI「源内(GENAI)」を、 完全ローカル環境 × ローカル LLM(OpenAI 互換 API) で動かすためのプロジェクトです。
バージョン: 現在 v0.13.0(研修。手続きの書類と審査、日程調整のウィザード)。v0.12.0 = 二週間の仕事・図の draw.io 編集・画像生成のチャット・棟ごとのモデル制御、v0.11.0 = マルチテナント・おすすめアプリ・Markdown エディタの HTML/PPTX 書き出し、v0.10.0 = ノートブック=資料を集めて調べ項目に整理。スキル/MCP/OCR。Web SSH、アプリ表示の編集、ヒアリングシート、v0.9.0 = 提案実装 第3弾:Markdown エディタ+オプションの各種文書作成(生成 API はプラガブル)、v0.8.0 = 提案実装 第2弾:オンラインフォーム(patchform)/手続き(申請束)・申請受付/マイ手続き(docmaker)/手続き MCP、v0.7.0 = 提案実装(書類読取とチェック・日程調整・様式 Excel 文書生成)、v0.6.0 = 添付・ナレッジの個人情報検知/ナレッジ専用ページ/チャット大容量添付の mapreduce/Dify エラー分類、v0.5.0 = 本番静的ビルド/デプロイ整備・複数LLM/埋め込み/画像モデルの差し替え・SAML/認証堅牢化、v0.4.0 = 構造化 RAG・ナレッジ MCP・Dify 連携事例・マイナンバー検査、v0.3.x = 出典表示・画像生成一本化等、v0.2.x = 自治体・閉域向け拡張、v0.1.0 = ローカル源内化の第一段階。 変更履歴は CHANGELOG.md を参照。
免責 / Disclaimer: 本リポジトリは有志による非公式フォークです。デジタル庁とは一切関係がなく、 同庁による承認・支援を受けたものではありません。
genai-web/はデジタル庁の digital-go-jp/genai-web(MIT ライセンス)を ローカル動作向けに改変して同梱しています(原 LICENSE はgenai-web/LICENSEに保持)。 デジタル庁オリジナルのgenai-ai-apiは本リポジトリには含めていません(必要な場合は digital-go-jp/genai-ai-api を別途取得してください)。
ホスト OS / ハードウェアは特定環境に依存しません。macOS (Apple Silicon) でも、 Linux + NVIDIA GPU 機(例: NVIDIA DGX Spark)でも動作します。
源内はもともと AWS / Azure / Google Cloud などのクラウド前提 (Amazon Cognito 認証・Lambda・Bedrock 等)で作られているため、そのままでは ローカルで動きません。本プロジェクトは以下を行うことでローカル完結させます。
- 認証(SAML)を ローカル完結(
backendを SAML SP、Keycloakを SAML IdP として実装) - LLM 呼び出しを OpenAI 互換 API 経由で行う(既定は Ollama の
/v1。vLLM / LM Studio / OpenAI など任意の OpenAI 互換サーバに切替可) - クラウド API(チャット履歴・推論ストリーム)を ローカルバックエンド(FastAPI) で代替
[ブラウザ] ──▶ proxy (nginx :80) ──▶ web (源内 Web / Vite)
│ REST + ストリーミング
├──▶ backend (FastAPI) ──▶ OpenAI 互換 LLM(Ollama 等)
└──▶ keycloak (/kc) SAML IdP
本番は docker-compose.prod.yml で TLS(443) 終端。閉域検証は HTTP(80) のみ(docker-compose.verify.yml)。SeaweedFS(8333)は本番 compose ではホスト非公開で、成果物のダウンロードは S3_PUBLIC_ENDPOINT 経由のリバースプロキシを別途用意します(詳細は「成果物ファイル」節)。
変更履歴: CHANGELOG.md(v0.13.0 = 研修・手続きの書類と審査・日程調整のウィザード、v0.12.0 = 二週間の仕事・図の draw.io 編集・画像生成のチャット・棟ごとのモデル制御、v0.11.0 = マルチテナント・おすすめアプリ・Markdown エディタの HTML/PPTX 書き出し、v0.10.0 = ノートブック(調べて項目に整理・スキル/MCP/OCR)・Web SSH・アプリ表示の編集、v0.9.0 = 提案実装 第3弾:Markdown エディタ+オプションの各種文書作成(生成 API はプラガブル・契約は OSS 公開)、v0.8.0 = 提案実装 第2弾:オンラインフォーム(patchform)・手続き(申請束)・申請受付・マイ手続き(docmaker)・手続き MCP、v0.7.0 = 提案実装:書類読取とチェック・日程調整・様式 Excel 文書生成、v0.6.0 = PII 検知・ナレッジ専用ページ・大容量添付 mapreduce 等、v0.5.0 = 本番静的ビルド/デプロイ整備・複数LLM/埋め込み/画像モデル差し替え・SAML/認証堅牢化、v0.4.0 = 構造化 RAG・ナレッジ MCP・Dify 連携事例・マイナンバー検査、それ以前は CHANGELOG 参照)
設計思想:自治体・閉域運用への拡張
初期リリースでは「源内のクラウド依存を OSS/ローカルに置き換える」ことに集中していました。
その後、実際の自治体(閉域・LGWAN 等)で使うことを想定し、思想と構成を大きく広げています。
改修の多くは OpenGENAI レイヤ(backend/ と各 exApp)に集約し、上流の genai-web は
極力無改修のままにしています(マージ容易性・源内 UX の維持)。
なぜ思想を変えたか
| 課題 | 方針 |
|---|---|
| 監査・権限・データ削除など、クラウド版が暗黙に担っていたガバナンス | マネージドサービスに頼らず 自前実装(監査ログ・モデル制御・入力制限・契約終了時削除) |
| 組織の実態(課・横断プロジェクト)と源内のチームモデルのギャップ | チーム主体・非階層に整理。1 人が 複数チームに所属できるよう拡張 |
| 管理機能を Keycloak コンソールだけに頼る運用負荷 | 管理者向け機能を 源内 UI 内の exApp として提供(一般利用者には非表示) |
| Dify 等の第三者 URL をそのまま利用者に渡すリスク | 自前 S3 互換(SeaweedFS) へ再ホストし、署名付き URL で受け渡し |
| 各コンテナを個別ポート公開する開発構成 | nginx 単一入口に統一(本番 TLS・閉域 HTTP 検証) |
4 つの柱
- 自治体実務を想定した機能 — 監査ログ(3 年以上保持)、利用者 CSV 一括管理、モデル利用制御、禁止語/個人情報検知(添付警告・ナレッジラベル、任意の GiNZA NER)、プロンプトテンプレート、ノートブック(資料を構造化して項目・対話・ヒアリングシート。標準起動)、日程調整(オプション profile)、書類領域分割チェック(オプション profile)、オンライン申請フォーム・手続き(申請束)・マイ手続き(オプション profile)、Markdown エディタ+各種文書作成(調達仕様書・セキュリティポリシー/実施手順・業務マニュアル・アクションプラン等を AI 対話で。生成 API はプラガブル、オプション profile)、Web SSH 端末(オプション profile)、契約終了時の完全削除と報告書生成
- テナント(棟)+チーム — チームより上位に棟がある。主鍵で自棟、招待鍵で別棟、共通鍵で共有棟。主所属は1つ、兼務・プロジェクトは追加タグ。親チーム所属者は配下を閲覧できる。AI アプリ・RAG ナレッジ・チャット履歴・保存プロンプトは 活性棟 で分ける
- 源内 UI 制約の opt-in 拡張 — Form Spec v1(条件表示・リアクティブフォーム・プレビュー)、
dynamic_schemaによる動的フォーム、各画面の折りたたみヘルプ。既存 exApp は無改修で従来どおり動作 - 成果物のオブジェクトストレージ — AI アプリ(Dify 等)が生成したファイルを SeaweedFS に保存し、backend 経由で署名付き URL を提示
構成
| ディレクトリ | 内容 |
|---|---|
genai-web/ |
デジタル庁 源内 Web(フォーク + ローカル化パッチ。同梱) |
backend/ |
ローカル LLM 用の代替バックエンド(FastAPI / Team API も兼ねる) |
rag-app/ |
RAG を「行政実務用 AI アプリ」として提供するマイクロサービス(FastAPI) |
knowledge-mcp/ |
ナレッジ検索 MCP(Streamable HTTP。/retrieve 等の薄いラッパ) |
whisper-app/ |
文字起こしを「AI アプリ」として提供(faster-whisper / CPU) |
dify-app/ |
外部 Dify(ワークフロー / チャットフロー)を「AI アプリ」として連携する汎用プロキシ(FastAPI) |
shared/ |
共用モジュール(docextract.py ドキュメント抽出、ssrfguard.py SSRF 対策) |
audit-app/ |
監査ログ参照(管理者限定 exApp) |
usermgmt-app/ |
利用者 CSV 一括管理(Keycloak Admin API、管理者限定 exApp) |
modelpolicy-app/ |
モデル利用制御ポリシー管理(管理者限定 exApp) |
ngword-app/ |
禁止語・個人情報検知の設定(管理者限定 exApp。添付警告/ナレッジ検知/NER トグル) |
prompt-app/ |
プロンプトテンプレートカタログ(標準/個人/グループ共有) |
chosei-app/ |
日程調整(オプション・profiles: ["chosei"]。詳細は docs/chosei.md) |
nishukan-app/ |
二週間の仕事(オプション・profiles: ["nishukan"]。詳細は docs/nishukan.md) |
lms-app/ |
研修(オプション・profiles: ["lms"]。詳細は docs/lms.md) |
doccheck-app/ |
書類領域分割チェック(オプション・profiles: ["doccheck"]。詳細は docs/doccheck.md) |
patchform-app/ |
フォーム(オプション・profiles: ["patchform"]。詳細は docs/patchform.md) |
procedure-mcp/ |
手続きマスタ MCP(profiles: ["patchform"]。公開済みのみ。詳細は docs/procedure-mcp.md) |
procuretech-navigator-app/ |
情報化企画書ナビ(オプション・profiles: ["procuretech-navigator"]。詳細は docs/procuretech.md) |
procuretech-editor-app/ |
Markdown エディタ+各種文書作成(標準起動。詳細は docs/procuretech-editor.md) |
procuretech-generate-app/ |
文書生成/合成の公開・汎用リファレンス実装(標準起動・既定の合成バックエンド。契約は docs/procuretech-generate-contract.md) |
notebook-app/ |
ノートブック(標準起動。資料を構造化して項目・対話・ヒアリングシート Excel を作る。詳細は docs/notebook.md) |
ssh-app/ |
Web SSH 端末(オプション・profiles: ["ssh"]。詳細は docs/ssh.md) |
sengoku-app/ |
戦国国取り(お遊びのシミュレーションゲーム。オプション・profiles: ["sengoku"]。詳細は docs/sengoku.md) |
(profile worldmonitor) |
ワールドモニター(上流タグからビルド。ソースは同梱しない。詳細は docs/worldmonitor.md) |
| (web 同梱) | 紙面(文字・図形・画像を組む。詳細は docs/shimen.md) |
procuretech-spec-app/ |
調達仕様書の生成/合成(オプション・profiles: ["procuretech-spec"]) |
seaweedfs/ |
成果物配信用 S3 互換ストレージ設定 |
scripts/ |
運用スクリプト(契約終了時の完全削除・報告書生成 等) |
docs/ |
Open GENAI レイヤのガイド(ナレッジ API / MCP / Dify 事例 / DGX Spark) |
docker-compose.yml |
開発用。proxy + web(Vite dev server) / backend / … をまとめて起動(HTTP :80 のみ公開、コード変更は無ビルドで即時反映) |
docker-compose.prod.yml |
本番 TLS 構成(proxy :80/:443 のみ公開。web は静的ビルド genai-web/Dockerfile.prod を nginx で配信) |
docker-compose.verify.yml |
本番スタックを HTTP で検証/閉域運用するオーバーライド(自己署名不要。web は本番同様の静的ビルド) |
proxy/ |
nginx リバースプロキシ設定(開発=nginx.http.conf / 本番TLS=nginx.conf / 本番HTTP検証=nginx.verify.conf)。成果物ファイル(SeaweedFS)用の別経路は README「成果物ファイル」節を参照 |
オリジナル源内からの改修内容(クラウド依存 → オープンアーキテクチャ)
このプロジェクトの中心的な改修は、源内が依存するクラウドのマネージドサービスを、すべてオープンソース/ローカルの仕組みに置き換えることです。置換の全体像は次のとおりです。
| 機能 | 源内オリジナル(クラウド・マネージド) | Open GENAI(オープン/ローカル置換) |
|---|---|---|
| 認証 | Amazon Cognito(SAML は Cognito がブローカー) | Keycloak(SAML IdP)+ backend(SAML SP, python3-saml)+ アプリ JWT |
| API 認可 | API Gateway Authorizer / IAM | backend の JWT 検証ミドルウェア |
| LLM 推論 | Amazon Bedrock | OpenAI 互換 API(既定は Ollama の /v1。任意の互換サーバに切替可) |
| 推論ストリーム | Lambda InvokeWithResponseStream(Cognito Identity Pool 資格情報で直接呼び出し) |
backend の HTTP ストリーミング /predict/stream |
| チャット履歴 | Amazon DynamoDB | SQLite(backend) |
| 保存プロンプト(systemcontexts) | Amazon DynamoDB | SQLite(backend) |
| チーム / メンバー / AI アプリ管理 | DynamoDB + Cognito グループ | SQLite + Keycloak グループ + team_users.isAdmin |
| ファイル添付ストレージ | Amazon S3(署名付き URL) | チャット添付は backend ローカル保存。AI アプリ成果物は SeaweedFS(S3 互換) へ再ホストし署名付き URL で配信 |
| RAG(ベクトル検索・埋め込み) | OpenSearch / Bedrock Knowledge Base | Qdrant + Ollama mxbai-embed-large(rag-app として) |
| 文字起こし | Amazon Transcribe + S3 | faster-whisper(whisper-app を AI アプリとして) |
| 画像生成 | Amazon Bedrock(画像モデル) | 源内 Web /image + ホスト Stable Diffusion、または IMAGE_PROVIDERS のクラウド画像 API(backend/image_gen.py) |
| ドキュメント読取(PDF 等) | Bedrock の document 入力 | テキスト抽出(pypdf / python-docx / openpyxl, shared/docextract.py) |
追加したコンポーネント(すべてオープンソース)
backend/(FastAPI): 源内 Web が叩くクラウド API(genU API / Team API / 推論ストリーム)を代替。SAML SP・JWT 発行/検証・ファイル保存も担当rag-app/(FastAPI): RAG を源内の作法どおり「行政実務用 AI アプリ(exApp)」として提供keycloak(SAML IdP・ユーザー管理)/qdrant(ベクトル DB)をdocker-compose.ymlに追加
OpenGENAI レイヤで追加した機能(fc57e53 以降)
クラウド版源内が担っていたガバナンスを、マネージドサービスなしで再実装しています。 詳細は CHANGELOG.md を参照。
| 領域 | 実装 |
|---|---|
| 監査ログ | backend/app/audit.py + audit-app(3 年以上保持、利用者削除と非連動) |
| 利用者管理 | usermgmt-app(CSV 一括作成・更新・削除) |
| モデル制御 | backend/app/policy.py + modelpolicy-app(チーム/グループ単位) |
| 入力制限 | backend/app/ngwords.py + ngword-app + shared/pii_scan.py(禁止語・添付/ナレッジの個人情報検知) |
| プロンプト | prompt-app(テンプレート → チャットディープリンク) |
| 成果物配信 | backend/app/objstore.py + SeaweedFS(Dify 等の file artifact 再ホスト) |
| 内部認証 | 各 app/intauth.py(backend↔exApp 間 HMAC 署名) |
| SSRF 対策 | shared/ssrfguard.py(成果物取得・RAG URL 取込) |
| データ削除 | scripts/purge-and-report.sh(契約終了時の完全削除・報告書) |
源内 Web(genai-web/)への変更点
クラウド SDK の差し替え(初期ローカル化)に加え、opt-in の UI 拡張(自治体向け)を
最小限だけ加えています。Form Spec v1 の詳細は
FORM_SPEC.md を参照。
- 追加:
packages/web/src/local/localAuth.ts— ローカル SAML 認証のフロント側(JWT 取得/ログイン/サインアウト) - 追加:
packages/web/.env.example— ローカル向けVITE_APP_*の雛形(cp .env.example .env) - 改修:
src/main.tsx— Cognito/SAML ログインゲート → ローカル SAML ログインゲート(AWS Amplify 撤去) - 改修:
src/hooks/useAuth.ts—fetchAuthSession()→ ローカル JWT デコード - 改修:
src/lib/fetcher.ts— Cognito トークン → ローカル JWT を Bearer 送信 - 改修:
src/lib/chatApi.ts—predictStreamを Lambda 直接呼び出し →/predict/streamの fetch - 改修:
src/lib/fileApi.ts— S3 URL 前提 → ローカル http URL に対応 - 改修:
src/components/ui/Header.tsx— AmplifyuseAuthenticator→localAuth.signOut - 改修:
src/features/chat/hooks/useFileUploadable.ts— 添付可否を選択中モデルに連動 - 改修:
src/pages/SignedOutPage.tsx— 再ログイン導線を追加 - 改修:
packages/common/src/application/model.ts— ローカル(Ollama)モデルの定義 +doc(ドキュメント添付)フラグを有効化 - 改修:
packages/web/vite.config.ts— コンテナ実行向けにhost/ポーリング監視を設定 - 改修:
src/features/exapps/hooks/useGenUApps.ts— クラウド依存の組み込み「文字起こし」をメニューから除外(ローカル Whisper の AI アプリで代替) - 追加: AI アプリ ピン留め(利用者ごと・カテゴリ横断・上限8件)— トップページに「ピン留め」セクションを表示。
open-genai/拡張 +LandingPage/ExAppList/ExAppListCardの最小パッチ(OPENGENAI_PATCHES.md参照) - 追加: プロンプトテンプレート専用ページ(
/prompts)— 汎用 exApp フォームに代えて、一覧の検索・区分バッジ・変数入力+ライブプレビュー・「チャットで開く」を 1 画面で操作。open-genai/prompt-templates/+prompt-app構造化 REST + backend/prompts/*プロキシ。旧/apps/:teamId/promptは/promptsへリダイレクト(OPENGENAI_PATCHES.md参照) - 改修:
src/features/team-apps/utils/endpointUrl.ts— AI アプリのエンドポイント URL 検証をhttpも許可(ローカルのコンテナ間通信http://dify-app:8004/invoke等のため。従来はhttps必須) - 改修:
src/features/teams/components/DialogDeleteTeam.tsx— チーム削除時に知識ベースも消える旨の警告を追加 - 改修: 翻訳/ダイアグラム画面の説明文をローカル実態に合わせて修正
- 追加: 各画面の折りたたみ「使い方」ヘルプ(チャット・翻訳・文字起こし・文章生成・ダイアグラム・画像生成)
- 追加: 保存プロンプトの共有 UI(全体公開・所属チーム複数選択)と
/me/teamsAPI 連携 - 追加: exApp Form Spec v1(
visibleWhen/reactive/preview、/exapps/resolve) - 追加:
dynamic_schema: trueによるローカル exApp の動的フォーム生成(RAG 管理等) - 改修: ダイアグラム Mermaid 抽出の堅牢化(フェンス無し出力へのフォールバック)
- 削除:
src/components/auth/AuthWithUserpool.tsx/AuthWithSAML.tsx(Cognito 専用のため不要) - 依存削除:
aws-amplify/@aws-amplify/ui-react/@aws-sdk/client-lambda/@aws-sdk/client-transcribe/@aws-sdk/credential-providers(packages/web/package.json)+index.cssの Amplify CSS import - 表記修正: 翻訳画面の「AWS の Bedrock を利用」→「ローカルの LLM(Ollama) を利用」
注: 源内の CDK / IaC(
packages/cdk等)は AWS デプロイ専用のため、本プロジェクトでは使用しません(参照のみ)。
前提
- Docker / Docker Compose
- OpenAI 互換 API を提供できる LLM 実行環境(既定は Ollama)
対応ホスト / GPU
LLM・画像生成(SD)は計算資源が必要です。ホスト環境ごとの推奨構成は次のとおりです。 いずれの場合も、源内 Web / backend / 各 AI アプリのコンテナ自体はどの環境でも同じように動きます。
| ホスト | LLM(Ollama) の実行 | 画像生成(SD) の実行 | 備考 |
|---|---|---|---|
| macOS (Apple Silicon) | ホストで実行(Metal GPU で高速) | ホストで実行(A1111 等) | Docker は GPU(Metal) を使えないため、GPU を使う処理はホスト側で動かし、コンテナはそれにプロキシ |
| Linux + NVIDIA GPU(例: NVIDIA DGX Spark) | ホスト or コンテナで実行(CUDA GPU) | ホスト or コンテナで実行(CUDA GPU) | NVIDIA Container Toolkit を入れればコンテナでも GPU 利用可 |
| その他 / CPU のみ | ホスト or コンテナ(低速) | CPU は非現実的 | 任意の OpenAI 互換サーバ(vLLM / LM Studio / OpenAI 等)に向けることも可能 |
- LLM の接続先は OpenAI 互換 API です。既定は
OLLAMA_BASE_URL(+/v1)、OPENAI_BASE_URLで任意のサーバに切替できます(後述)。 host.docker.internalは Docker Desktop(macOS/Windows)に加え、Linux でもextra_hosts: host-gatewayで解決するよう設定済みです。- Linux + NVIDIA でコンテナに GPU を割り当てる場合は、対象サービス(例
ollama)にgpus: all(またはdeploy.resources.reservations.devices)を追加してください(macOS 既定構成では不要なため同梱していません)。
使い方
1. Ollama を起動してモデルを取得
# ホストで Ollama を起動(インストール済みの場合)
ollama serve # 別ターミナルで起動したままにする
# モデルを取得(日本語に強い Qwen2.5 を推奨)
ollama pull qwen2.5:7b
# 軽量に試すなら: ollama pull qwen2.5:3b / ollama pull qwen2.5:0.5b
Ollama をコンテナで動かしたい場合は
docker compose --profile ollama upを使い、.envのOLLAMA_BASE_URLをhttp://ollama:11434に変更してください。 Linux + NVIDIA GPU(DGX Spark 等)では、ollamaサービスに GPU を割り当てると コンテナのまま高速に動かせます(NVIDIA Container Toolkit導入のうえgpus: allを付与)。 macOS では Docker から GPU を使えないため、Ollama はホストで動かすのが推奨です。
2. 設定
cp .env.example .env # 必要に応じて DEFAULT_MODEL などを編集
cp genai-web/packages/web/.env.example genai-web/packages/web/.env # 必要に応じて VITE_APP_MODEL_IDS などを編集
利用したいモデルを増やす場合は genai-web/packages/web/.env の
VITE_APP_MODEL_IDS(Ollama のモデル名と一致させる)を編集してください。
モデルの表示名は genai-web/packages/common/src/application/model.ts に定義しています。
LLM バックエンドの差し替え(OpenAI 互換)
LLM(チャット/RAG 生成/埋め込み)はすべて OpenAI 互換 API 経由で呼び出します。
既定では Ollama の /v1(OLLAMA_BASE_URL + /v1)を使います。
Ollama 以外(vLLM / LM Studio / OpenAI 等)に向けたい場合は .env で設定します。
# 例: 別の OpenAI 互換サーバに向ける
OPENAI_BASE_URL=http://host.docker.internal:8001/v1
OPENAI_API_KEY=sk-... # サーバが要求する場合のみ
3. 起動
docker compose up --build
- 源内 Web: http://localhost/ (
.envのPROXY_HTTP_PORT/PUBLIC_URLで変更可) - バックエンド API: http://localhost/api/health
- Keycloak 管理: http://localhost/kc/
初回はフロントエンドの依存インストールに数分かかります。
ポート 80 が使用中の場合は
.envでPROXY_HTTP_PORT=8080とPUBLIC_URL=http://localhost:8080に設定してください。
動作確認
- http://localhost/api/health で
{"status":"ok"}が返ること(モデル一覧は認証付きGET /api/health/details) - http://localhost/ を開くと Keycloak のログイン画面に遷移し、
admin/passwordでログインできること - 「チャット」からメッセージを送り、ローカル LLM の応答がストリーミング表示されること
- 「AIアプリ」→「ナレッジ検索」で質問でき、出典付きで回答されること(起動済みアプリのみ一覧に表示)
- 「翻訳」「ダイアグラムを生成」「文章を生成」がローカル LLM で動作すること
- http://localhost:8333/ をブラウザで開くと XML の
AccessDeniedが表示されること(SeaweedFS S3 API の正常応答。開発時のみホスト公開。本番はS3_PUBLIC_ENDPOINT経由のリバースプロキシのみ公開) - (Dify 連携)[
dify-app/dsl/File Output Test.yml](dify-app/dsl/File Output Test.yml) をデプロイし、源内 AI アプリから実行して成果物リンクが表示されること - 開発構成では http://localhost/kc/ の Administration Console に
.envのKEYCLOAK_ADMINでログインでき、realmopen-genaiの Users / Groups が表示されること(利用者アカウント管理用。源内ログイン画面とは別)。本番/verify では/kc/adminは nginx 既定で全拒否(proxy/kc-admin-allow.confで CIDR 許可)
本番デプロイ
開発環境(docker compose up)と本番環境では、フロントエンド(源内 Web)の配信方式が異なります。ここでは本番デプロイの前提・初回手順・変更反映の運用をまとめます。
開発と本番の違い(配信方式)
| 項目 | 開発(docker-compose.yml) |
本番(docker-compose.prod.yml) |
|---|---|---|
| web の配信 | Vite dev server(Dockerfile.local, :5173)をバインドマウントで起動 |
静的ビルド(Dockerfile.prod で npm run web:build → nginx が dist を :80 配信) |
| フロント変更の反映 | ソース保存で即時反映(無ビルド・HMR) | web イメージの再ビルドが必要(静的バンドルにビルド時取り込み) |
| 公開 | proxy が HTTP :80 | proxy が TLS :443 終端(proxy/nginx.conf) |
| API 参照 | VITE_APP_* を実行時 env で注入 |
VITE_APP_* を build.args でビルド時に埋め込み |
backend / 各 exApp(Python)はどちらもイメージ内のコードで動くため、変更時はイメージ再ビルド+再作成が必要です(下記「変更の反映」参照)。開発で Python 側を即時反映したい場合は各サービスにバインドマウント等を追加してください(既定は web のみ即時反映)。
前提
.env.prodを用意(cp .env.prod.example .env.prodして編集)。最低限、次は本番用に必ず変更します。PUBLIC_URL(例:https://genai.example.lg.jp。末尾スラッシュ無し)APP_JWT_SECRET/INTERNAL_SIGNING_SECRET(十分長い乱数。例:openssl rand -hex 32)KEYCLOAK_ADMIN_PASSWORD(初回起動前に設定。詳細は認証節の「運用開始時」)S3_*(S3_PUBLIC_ENDPOINT/S3_ACCESS_KEY/S3_SECRET_KEY等。詳細は成果物ファイル節)
- TLS 証明書を
proxy/certs/{fullchain.pem,privkey.pem}に配置(docker-compose.prod.ymlが/etc/nginx/certsにマウント。詳細はproxy/certs/README.md)。 - Keycloak の SAML クライアント(SP)登録を
PUBLIC_URLに合わせる(初回 import 前にkeycloak/import/realm-open-genai.jsonを編集、または admin コンソールで更新。詳細はSAML 節)。庁内 FQDN とインターネット FQDN を分ける場合は ACS/SLS をPUBLIC_URL側、SAML_SP_ENTITY_IDだけ既存 clientId に固定できる。インターネット入口の ID/PW はPORTAL_LOGIN_HOSTS。本体の戻り先はFRONTEND_URL。前面プロキシ出口はOPERATOR_SAML_SOURCE_IPS。運用者ホストだけ Keycloak を使わない ID/PW はOPERATOR_LOGIN_HOSTS/OPERATOR_USERS(.env.prod.example)。
初回デプロイ(本番 TLS)
# 1. 設定と証明書を用意
cp .env.prod.example .env.prod # PUBLIC_URL・各シークレット等を編集
# proxy/certs/fullchain.pem, privkey.pem を配置
# 2. ビルドして起動(web は静的ビルドされる)
# ホストシェルの PUBLIC_URL 等が .env.prod を上書きしないよう
# scripts/compose-prod.sh を推奨。
./scripts/compose-prod.sh up --build -d
# 3. 状態確認
./scripts/compose-prod.sh ps
- 公開 URL(
PUBLIC_URL)でログイン画面(Keycloak)に遷移すること - 初回はフロントのビルドに数分かかります
- 起動後、初期ユーザー(
admin/user)の無効化・パスワード変更を必ず実施(運用開始時)
変更の反映(再デプロイ)
本番はビルド済みイメージで動くため、変更箇所に応じて対象サービスを再ビルド+再作成します。
| 変更した箇所 | 反映コマンド(-f docker-compose.prod.yml --env-file .env.prod を付与) |
|---|---|
フロント(genai-web/**。プロンプト・画面・ルーティング等) |
docker compose ... up -d --build web |
backend(backend/**) |
docker compose ... up -d --build backend |
各 exApp(rag-app/ 等) |
docker compose ... up -d --build <service> |
氏名 NER(GiNZA)(PII_INSTALL_NER=1) |
PII_INSTALL_NER=1 docker compose ... up -d --build backend rag-app(詳細は個人情報検知) |
proxy 設定(proxy/nginx.conf) |
docker compose ... restart proxy(設定はマウントのため再起動のみ) |
.env.prod の値(LLM 接続・S3 公開先・TITLE_MODE 等) |
対象サービスを up -d(再作成で env 再読込。フロント埋め込み値は再ビルド) |
# 例: フロントのプロンプト/画面を変更 → web を再ビルドして差し替え
docker compose -f docker-compose.prod.yml --env-file .env.prod up -d --build web
注意: フロントの見た目・文言・プロンプトの変更は、
webを再ビルドしない限り本番へ反映されません(静的バンドルにビルド時取り込みのため)。開発環境では即時反映されるので、この差に注意してください。
閉域・HTTP のみで運用/検証する場合
TLS 終端や自己署名証明書が使えず、ポート 80 のみで公開する環境(閉域検証など)向けに、本番スタックを HTTP で動かすオーバーライドを用意しています。web は本番同様の静的ビルドを使い、proxy だけを HTTP・静的上流用の proxy/nginx.verify.conf に差し替えます。
cp .env.prod.example .env.prod
# PUBLIC_URL=http://localhost(またはホスト名), PROXY_HTTP_PORT=80 等に編集
docker compose -f docker-compose.prod.yml -f docker-compose.verify.yml \
--env-file .env.prod up --build -d
PUBLIC_URLはhttp://で始めること(証明書は不要)- ポート 80 が使用中なら
.env.prodのPROXY_HTTP_PORT=8080等に変更し、PUBLIC_URLも合わせる
前段にゲートウェイ/別リバースプロキシがある場合(SAML 注意)
PUBLIC_URL が SAML の SP EntityID / ACS / SLS と Keycloak の公開ホスト(KC_HOSTNAME)の単一の正になります。前段に別ホスト名のゲートウェイを置く場合は、次のいずれかにしてください。
- 公開ホストを 1 つに統一: 利用者に見せるホスト名を
PUBLIC_URLに設定し、ゲートウェイは/・/api・/kcすべてを Host ヘッダを保持して転送する。Keycloak の SAML クライアント(ACS/EntityID)もそのホストで登録する。 - backend は
X-Forwarded-Proto/X-Forwarded-Host/X-Forwarded-Portから公開 URL を組み立てるため、前段プロキシはこれらを正しく付与すること(本リポジトリのproxy/nginx.confは TLS 終端時にhttps/$host/443を送出)。
複数の公開ホストで同時に SAML を成立させることは、現状の単一
PUBLIC_URL前提ではできません(EntityID/ACS が固定のため)。
埋め込みモデルの差し替え / ローカル日本語埋め込み(embed サイドカー)
RAG の埋め込みモデルは推論モデルと同様に差し替え可能です。既定は Ollama 上の
mxbai-embed-large(1024 次元)で、開発・検証・既定の本番はこのままで動きます(追加設定不要)。
挙動はすべて rag-app の環境変数で制御され、コードは単一パスのままです。
| 環境変数 | 既定 | 役割 |
|---|---|---|
EMBED_MODEL |
mxbai-embed-large |
埋め込みモデル名 |
EMBED_DIM |
1024 |
ベクトル次元(Qdrant コレクション作成時に使用) |
QDRANT_COLLECTION |
open_genai_rag |
保存先コレクション |
EMBED_BASE_URL |
空(=OPENAI_BASE_URL にフォールバック) |
埋め込みだけ別の OpenAI 互換エンドポイントへ分離する場合に設定 |
EMBED_API_KEY |
空(=OPENAI_API_KEY) |
上記エンドポイントの API キー |
EMBED_QUERY_PREFIX / EMBED_DOC_PREFIX |
mxbai 互換(クエリのみ英語 prefix・文書なし) | モデル依存の検索クエリ/文書 prefix |
重要(再インデックス): 埋め込みモデルを変えるとベクトル次元と意味空間が変わります。 Qdrant のコレクションは次元固定のため、既存コレクションへ別モデルのベクトルを混在させられません。 モデルを変更する場合は
QDRANT_COLLECTIONを別名にし、EMBED_DIMを合わせたうえで全件を再インデックスしてください。
ローカル日本語埋め込み(ruri-v3 等)を使う場合
Ollama や TEI(CPU/ORT) が配信できない埋め込みモデル(例: cl-nagoya/ruri-v3-310m は
ModernBERT-Ja・ONNX 非提供)は、同梱の汎用サイドカー embed(sentence-transformers/CPU)で配信できます。
このサイドカーは Compose の profiles: ["embed"] により任意起動で、既定では起動しません。
本番で日本語埋め込みが必要な場合にのみ有効化します。
# .env.prod に以下を設定(.env.prod.example の「ruri-v3-310m を使う場合」の例を参照)
# COMPOSE_PROFILES=embed
# EMBED_BASE_URL=http://embed:80/v1
# EMBED_API_KEY=-
# EMBED_MODEL=cl-nagoya/ruri-v3-310m
# EMBED_DIM=768
# QDRANT_COLLECTION=open_genai_rag_ruri_v3_310m # 768 次元の別コレクション
# EMBED_QUERY_PREFIX=検索クエリ:
# EMBED_DOC_PREFIX=検索文書:
# embed サイドカーを含めて起動(COMPOSE_PROFILES=embed を .env.prod に入れれば --profile 省略可)
docker compose -f docker-compose.prod.yml --env-file .env.prod --profile embed up -d --build embed rag-app
embedを起動しない運用ではEMBED_BASE_URLを外部の埋め込み API に向けても構いません(サイドカー不要)。- 開発・検証環境では
embedは不要です(docker-compose.yml/docker-compose.verify.ymlは既定 mxbai のまま)。 - 初回起動時にモデルを HuggingFace から取得し
embed_dataボリュームへキャッシュします(数百 MB〜。閉域では事前取得が必要)。
ファイル添付(画像 / ドキュメント)
チャットでは画像とドキュメントを添付できます(モデルの対応機能フラグに応じて添付ボタンが出ます)。
画像(マルチモーダルモデル)
- モデル選択で 「Gemma 3 27B (ローカル・画像対応)」 を選ぶと画像添付が使えます
- 対応形式:
.jpg / .jpeg / .png / .webp - 画像は推論時に OpenAI 互換の Vision 形式(
image_urldata URL)で LLM に渡されます - 他の画像対応モデル(例
llava,llama3.2-vision等)も、ollama pullのうえgenai-web/packages/web/.envとpackages/common/src/application/model.tsに画像フラグ付きで追加すれば利用できます
ドキュメント(PDF / Word / Excel / テキスト)
- すべてのローカルモデルで ドキュメント添付が使えます(
docフラグを有効化済み) - 対応形式:
.pdf / .docx / .xlsx / .txt / .md / .csv / .html / .jsonなど - ローカル LLM はドキュメントを直接読めないため、backend がテキストを抽出してプロンプトに注入します(PDF=pypdf、Word=python-docx、Excel=openpyxl、テキスト=そのまま)
- 大きい添付は 30,000 文字で黙って打ち切らず、その場でマップリデュース(チャンク化 → 読み計画 → 抜粋 or バッチ要約)してから回答します。しきい値
CHAT_DOC_INLINE_CHARS(既定 60,000 文字)以下はそのまま全文注入、超過分は圧縮して参照し、「どう参照したか」を応答冒頭に短く明示します - 安全弁として全文抽出は
MAX_CHAT_DOC_CHARS(既定 500,000 文字)を上限とし、超えた場合のみ明示注記を付けて先頭を保持します(全文が必要な場合はナレッジ登録を利用)。ベクトル RAG 簡易登録など従来経路は引き続きMAX_DOC_CHARS(既定 30,000 文字)で打ち切ります - レガシー形式(
.doc/.xlsのバイナリ旧形式)はテキスト抽出に未対応です - アップロード時に個人情報が検知された場合は警告のみ(送信・保存は継続)。詳細は入力制限と個人情報検知
アップロードしたファイルは backend(
backend_dataボリューム)に保存されます。
認証(SAML)
源内の SAML 認証を、ローカル完結する形で実装しています。
flowchart LR
SPA[源内 Web] -->|未認証なら redirect| Login["backend /auth/login"]
Login -->|SAML AuthnRequest| KC[Keycloak SAML IdP]
KC -->|SAML Assertion| ACS["backend /auth/saml/acs"]
ACS -->|アプリJWT発行 #token=| SPA
SPA -->|Authorization Bearer| API[backend API]
backendが SAML SP(python3-saml)として動作し、検証後にアプリ JWT を発行Keycloak(http://localhost/kc/)が SAML IdP 兼 利用者アカウントの台帳- 各 API は JWT(Bearer) で保護(未認証は 401)
Keycloak とは(このプロジェクトでの役割)
Keycloak は 「誰がログインできるか」 を担うコンポーネントです。源内 Web そのものではなく、 ログイン画面の裏側(IdP)と、利用者アカウントの保存場所として動きます。
| Keycloak が担うこと | 源内 / backend が担うこと |
|---|---|
| ログイン ID(ユーザー名)・パスワード | チャット履歴・保存プロンプト |
| メールアドレス(SAML の NameID 兼利用者 ID) | チーム・メンバー・AI アプリ(SQLite) |
権限グループ(SystemAdminGroup 等) |
チーム管理者(team_users.isAdmin) |
| SAML で backend に属性を渡す | モデル制御・入力制限・監査ログ等 |
整理: 利用者を「作る/止める/システム管理者にする」→ Keycloak(または後述の CSV 一括 exApp)。 「どのチームに所属させるか」→ 源内の チーム管理 UI。
2 種類の画面(混同しやすい)
| URL | 用途 | ログイン |
|---|---|---|
| http://localhost/kc/ | Keycloak(ログインフォーム+管理コンソール) | 管理コンソールは本番で /kc/admin を nginx が遮断。開発時は .env の KEYCLOAK_ADMIN / KEYCLOAK_ADMIN_PASSWORD(既定 admin / admin) |
| http://localhost/ | 源内 Web(利用者向け) | realm open-genai の利用者(例: admin / password) |
源内にログインするときに表示される画面は Keycloak の ログインフォーム(realm open-genai)です。
管理コンソールとは別物です。
ログインできないとき(よくある間違い)
スクリーンショットの画面(realms/master・Administration Console)には、源内用のadmin/passwordは使えません。
ここは Keycloak サーバ管理者用で、既定はadmin/admin(.envのKEYCLOAK_ADMIN/KEYCLOAK_ADMIN_PASSWORD)です。admin/passwordは http://localhost/(源内)へのログイン用です。
権限グループ
realm open-genai には次のグループがあります(keycloak/import/realm-open-genai.json で初期定義)。
| グループ | 意味 | 設定方法 |
|---|---|---|
SystemAdminGroup |
システム管理者。全チーム管理・管理者向け exApp(監査・利用者一括・モデル制御・入力制限等) | Keycloak 管理コンソールでユーザーをこのグループに追加 |
UserGroup |
一般利用者 | 新規ユーザー作成時に付与(既定) |
TeamAdminGroup |
チーム管理者(自チームのメンバー/アプリ管理) | Keycloak では設定しない。源内「チーム管理」でメンバーを管理者にすると、次回ログイン時に自動付与 |
SAML 経由で backend に渡る主な属性:
- NameID / 利用者 ID … メールアドレス(例:
[email protected]) - groups … 上記グループ名の一覧(カンマ区切り)
初期ユーザー
| ユーザー名 | パスワード | グループ | 備考 |
|---|---|---|---|
admin |
password |
SystemAdminGroup |
源内ヘッダーに管理メニュー・管理者 exApp が表示 |
user |
password |
UserGroup |
一般利用者 |
初回は Keycloak の起動に数十秒かかります。源内ログインや管理コンソールが開けない場合は少し待って再試行してください。
Keycloak 管理コンソールの操作
- ブラウザで http://localhost/kc/ を開く
- Administration Console をクリック
- ユーザー名
admin、パスワード.envのKEYCLOAK_ADMIN_PASSWORD(未設定時admin)でログイン - 左上の realm が
open-genaiになっていることを確認(masterのままだと利用者が見えません)
利用者を 1 人追加する
- 左メニュー Users → Add user
- Username(必須)・Email(推奨: 源内の利用者 ID として使われます)・姓名を入力 → Create
- Keycloak の Last name = 姓、First name = 名 です(日本語の「姓→名」とは欄の並びが逆)
- 源内の表示名は
姓 名(Last name + First name)になります
- Credentials タブ → Set password(Temporary を OFF にすると初回変更を求めません)
- Groups タブ → Join Group → 一般利用者なら
UserGroup、システム管理者ならSystemAdminGroup
パスワードをリセットする
- Users → 対象ユーザーを開く → Credentials → Set password
利用者を無効化する(削除せず止める)
- Users → 対象ユーザー → Enabled を OFF → Save
利用者を削除する
- Users → 対象ユーザー → Delete
自己登録(Sign up)は無効です(
registrationAllowed: false)。利用者は管理者が作成します。
システム管理者に昇格させる
- Users → 対象ユーザー → Groups → Join Group →
SystemAdminGroup - ユーザーに 再ログイン してもらう(SAML 属性が更新されるため)
源内側の設定変更時に必要な作業
.env の PUBLIC_URL を変えた場合(例: ポート 8080)は、Keycloak の SAML クライアント
(Clients → Open GENAI SP)の Valid redirect URIs / Assertion Consumer URL も
新しい URL に合わせる必要があります。開発用の既定 import(keycloak/import/realm-open-genai.json)
は http://localhost 前提です。
CSV 一括管理(源内の管理者 exApp)
人数が多い場合は、Keycloak 管理コンソールの代わりに源内の 「利用者一括管理」 AI アプリ (システム管理者のみ表示)から CSV で作成・更新・削除できます。
- ヘッダー → AI アプリ → 利用者一括管理(管理者限定)
- CSV 列:
action,username,email,name,password,groups,enabled,tenant(棟)等 - 棟(
tenant): 棟名か棟 ID を書く。新規登録は必須(共有棟は招待で付けるため指定不可)。Keycloak 反映のあと backend が主鍵として付与する - 1 名だけなら「利用者登録」タブのフォーム(棟は必須の選択)でも作れる
dry_runで事前確認 →applyで Keycloak に反映
詳細は exApp フォーム内の説明を参照してください。
運用開始時(本番・閉域)— パスワード変更
開発用の既定パスワード(admin/admin、admin/password 等)は そのまま運用してはいけません。
運用開始前に、少なくとも次を変更してください。
既定のまま起動すると backend が stderr に [SECURITY] 警告と設定手順を出力します(起動自体は継続します)。
| 対象 | 設定・操作 | 備考 |
|---|---|---|
| Keycloak サーバ管理者 | .env.prod の KEYCLOAK_ADMIN_PASSWORD |
管理コンソール(master realm)用。初回起動前に設定するのが確実(keycloak_data ボリューム作成後は環境変数だけでは変わらない) |
| 源内の初期利用者 | realm open-genai の Users でパスワード変更、または削除 |
import 済みの admin/user(いずれも password)は検証用。本番では削除するか強固なパスワードに変更 |
| 新規利用者 | 利用者一括管理 exApp(利用者登録/CSV。棟を必須指定) | 実運用の利用者は棟を指定して登録し、個別パスワードを発行。Keycloak 管理コンソールだけで作った人は棟に入らないため、招待か CSV で棟を付ける |
| backend JWT 署名 | .env.prod の APP_JWT_SECRET |
Keycloak とは別だが、認証まわりで同時に変更必須 |
| 内部 HMAC | .env.prod の INTERNAL_SIGNING_SECRET |
backend↔exApp の x-user-* 偽装対策 |
| 添付 URL 署名 | 任意 FILES_URL_SECRET(未設定時は APP_JWT_SECRET) |
/api/files の短命 HMAC |
初回デプロイの推奨手順:
.env.prodで次を十分長い乱数に設定(例:openssl rand -hex 32):KEYCLOAK_ADMIN_PASSWORD・APP_JWT_SECRET・INTERNAL_SIGNING_SECRETdocker compose -f docker-compose.prod.yml --env-file .env.prod up --buildで 初回起動(以降keycloak_dataに管理者パスワードが固定される)- 管理コンソールへは
proxy/kc-admin-allow.confに管理網 CIDR を追記してから到達(既定は/kc/admin全拒否) - realm
open-genai→ 初期ユーザーadmin/userを無効化またはパスワード変更 - 実利用者を登録(一括 exApp または Users から)
既に
keycloak_dataボリューム付きで起動済みの環境でKEYCLOAK_ADMIN_PASSWORDだけ変えても反映されません。管理コンソールから master 管理者のパスワードを変更するか、検証環境ならdocker compose down -vでボリュームごと再作成してください。
Keycloak でやらないこと(源内で行う)
- チームへの所属 … 「アカウント」→「チーム管理」でメンバー追加
- チーム管理者の指定 … チーム管理でメンバーの管理者フラグ(Keycloak の
TeamAdminGroupは自動) - AI アプリの登録 … チーム管理
- 監査ログ閲覧・モデル制御・禁止語 … 管理者 exApp(Keycloak では不可)
RAG(AI アプリ)
源内の作法どおり、RAG を 外部マイクロサービス「行政実務用 AI アプリ」 として実装しています
(ブラウザ → backend(Team API) → rag-app → Qdrant / Ollama)。
使い方:
- ヘッダーの 「AI アプリ」 を開く → 「ナレッジ検索」 を選択
- 質問を入力して「実行」。知識ベースを検索し、出典付きで回答します
- 必要ならタグで絞り込みます(タグ未付与の資料は検索対象外)
検索方式(ベクトル/構造化/ハイブリッド/全文)は UI では選びません。
対象資料の状態に応じて rag-app が自動選択します(後述の /retrieve と同じ方針)。
知識ベースのスコープ(チーム単位で分離)
ナレッジは チーム(= RAG アプリを所有するチーム)単位で分離されます。 フォルダ階層ではなく、タグ(フラットなラベル) と URL 取り込み で整理します。
- 共通チームの RAG: 全認証済みユーザーが使う共有ナレッジ
- チーム作成時に次の 4 アプリを自動登録(いずれも
dynamic_schema)- ナレッジ検索 / タグ管理 / ドキュメント登録 / ドキュメント管理
- 取り込み/検索/削除はすべて、そのアプリが属するチームの
scope(=teamId)内に限定 - ドキュメント登録
- 標準: ツリー索引(構造化)+ベクトル。規程・マニュアル向け
- 簡易: 全文保存+ベクトル(ツリーなし)。散発的な資料向け
- URL: ページ取り込み(全文+ベクトル)。定期再クロール対象
- URL 取り込み: 行政 HP 等の URL を登録し、定期再クロール(
URL_FETCH_ALLOWED_HOSTSで許可ホスト制限、SSRF 対策付き) - タグ: チャンク/文書に複数タグを付与し、検索時の絞り込みに利用(未付与は登録可・検索対象外)
- 重複排除: 同一内容のチャンクは(出典+本文のハッシュで)重複登録されません
ナレッジ管理 専用ページ(/knowledge)
タグ管理・ドキュメント登録・ドキュメント管理は、汎用 exApp フォームに代えて
専用ページ /knowledge(アカウントメニュー →「ナレッジ管理」)に統合しています。
- 画面先頭の スコープセレクタで「共有ナレッジ(共通)」と「所属チーム」を切り替え
- 共有ナレッジの書込はシステム管理者のみ(閲覧は全ユーザー)。チームスコープはメンバーが管理可能
- ドキュメント登録は非同期(
ingest_status: pending → ready / error)。登録ジョブ内で索引化のあと個人情報検知し、一覧にラベル表示(詳細は個人情報検知) - 旧管理 exApp(
rag-tags/rag-register/rag-maintainと各チームの「タグ管理/ドキュメント登録/ドキュメント管理」)は AI アプリ一覧から廃止し、/knowledgeに一本化(起動時に既存レコードを削除。新規デプロイでも最初から表示されない)。万一残った旧 URL / ピン留めは/knowledgeへ自動リダイレクト - ナレッジ検索は従来どおり「AI アプリ」の「ナレッジ検索」(
ragexApp)を使用 - 実装:
rag-appの構造化 REST(/knowledge/*)をbackendが認証・スコープ認可付きでプロキシ(詳細は docs/knowledge-api.md)
RAG Retrieval API(機械向け)
人間向け Q&A は POST /invoke(源内 exApp)。Dify 等から節/チャンクを取り出すときは POST /retrieve など機械向け API を使います(開発時ホスト :8001)。
詳細・curl 事例(タグ横断/source 指定/サンプル登録)は docs/knowledge-api.md を参照。
最短疎通:
curl -s -X POST http://127.0.0.1:8001/retrieve \
-H "x-api-key: local-rag-key" -H "Content-Type: application/json" \
-d '{"question":"管理者は誰ですか","mode":"auto","top_k":4,"scope":"00000000-0000-0000-0000-000000000000","tags":["規程"]}'
- 埋め込み: Ollama
mxbai-embed-large/ ベクトル: Qdrant / メタ: SQLite - E2E:
scripts/e2e-tree-rag.sh
文字起こし / 画像生成
文字起こしは 外部マイクロサービスの「AI アプリ」(whisper-app)として提供しています。
画像生成は源内 Web の 「画像を生成」ページ(/image)から利用します(backend の /image/generate がホスト SD へプロキシ)。
UX 方針(源内組み込み vs exApp)
クラウド版源内には組み込みの「文字起こし」(/transcribe)と「画像を生成」(/image)があります。
Open GENAI では 機能ごとに入口を分けています(どちらもローカルで動作するよう置き換え済み)。
| 機能 | Open GENAI での入口 | 理由(UX) |
|---|---|---|
| 画像生成 | 源内オリジナル /image + ホスト Stable Diffusion |
チャット連携・利用履歴・詳細設定など、源内組み込み UX を活かす。初期の SD 専用 exApp は重複のため廃止 |
| 文字起こし | exApp(whisper-app) |
源内 /transcribe は Amazon Transcribe + S3 前提のためメニューから除外。ローカル Whisper を exApp として提供 |
文字起こしで exApp を選んだ理由
- 源内
/transcribeの主な差分は 話者分離(diarization) だが、ローカルの faster-whisper では Transcribe 相当の話者認識は提供していない。あえて外し、言語指定・タイムスタンプ付き出力・exApp 利用履歴に寄せた - 音声は exApp 実行時にコンテナ内で処理され、クラウドへ送信されない(源内
/transcribeのコードはリポジトリに残るが、Open GENAI では/apps/.../whisperから利用する)
文字起こし(ローカル Whisper)
whisper-app(faster-whisper / CPU)。音声・動画を添付して実行すると文字起こし(タイムスタンプ付き)を返します。使い方に対応形式の表があります。- モデルは
.envのWHISPER_MODEL(既定medium。small/large-v3も可)。初回実行時にモデルを取得しwhisper_cacheボリュームにキャッシュします。 - クラウドの Amazon Transcribe + S3 への依存を置き換えています。
画像生成(源内 Web /image + Stable Diffusion)
backend の /image/generate が画像生成サーバへプロキシします。バックエンドは .env の
SD_BACKEND で切り替えます(既定 a1111)。画像生成サーバ本体はいずれもアプリ外で運用します
(RAG 埋め込みと同様、環境に応じて差し替える方針)。DGX Spark での一例は docs/spark.md。
SD_BACKEND |
用途 | サーバ | 既定接続先 | 初期 step/cfg 目安 |
|---|---|---|---|---|
a1111(既定) |
GPU で自前運用 | AUTOMATIC1111 互換(/sdapi/v1/txt2img) |
http://host.docker.internal:7860 |
50 / 7 |
fastsd |
CPU-only 環境 | FastSD CPU(LCM, /api/generate) |
http://host.docker.internal:8000 |
4 / 1 |
a1111(GPU 自前運用)- macOS は Docker から GPU(Metal) を使えないため SD 本体はホストで。AUTOMATIC1111 stable-diffusion-webui 等を
--api付きで:7860に起動。 - Linux + NVIDIA GPU は SD をコンテナで GPU 実行しても可。
SD_API_URLをそのアドレスに設定。
- macOS は Docker から GPU(Metal) を使えないため SD 本体はホストで。AUTOMATIC1111 stable-diffusion-webui 等を
fastsd(CPU-only)- FastSD CPU は A1111 非互換の独自 API(
POST /api/generate)で、backend 側に専用アダプタを実装済み。 - FastSD 本体を外部で起動:
python src/app.py --api(既定:8000)。.envにSD_BACKEND=fastsdと、必要ならSD_API_URLを設定。 - モデル/高速化は env で指定:
SD_FASTSD_USE_OPENVINO/SD_FASTSD_USE_LCM_LORA/SD_FASTSD_USE_TINY_AUTO_ENCODER/SD_FASTSD_USE_SAFETY_CHECKER/SD_FASTSD_MODEL_ID/SD_FASTSD_OPENVINO_MODEL_ID(未指定は FastSD 既定)。
- FastSD CPU は A1111 非互換の独自 API(
- 初期 step/cfg はフロントにビルド時埋め込み(
VITE_APP_IMAGE_DEFAULT_STEP/VITE_APP_IMAGE_DEFAULT_CFG。既定 50/7)。FastSD/LCM を使う本番ビルドは 4/1 を指定(.env.prodに設定してwebを再ビルド)。開発はgenai-web/packages/web/.envで指定。 - 検証用モック:
python3 scripts/mock-sd-server.py(a1111=:7860)/--port 8000(fastsd)。両バックエンドのエンドポイントに対応。 - 動作確認:
bash scripts/verify-image-gen.sh(SD_BACKENDを見て疎通先を切り替え)
クラウドの画像 API は、ローカル SD と同時に使えます。画面の「モデル」で local-sd とクラウドモデルを切り替えます(チャット用の LLM_PROVIDERS とは別です)。モデル未指定と local-sd は従来どおり SD_BACKEND へプロキシします。
実装済みの protocol は openai_images(OpenAI 互換の POST /images/generations。gpt-image-1 など)です。別の API(Bedrock、Gemini など)を足すときは backend/app/image_gen.py の _PROTOCOLS に関数を 1 つ登録します。
有効にするには次の 3 点を設定します。API キーはリポジトリに書かず、デプロイ先の環境変数に入れます。
.envのOPENAI_PROVIDER_API_KEY.envのIMAGE_PROVIDERS(1 行の JSON 配列)
[{"name":"openai","protocol":"openai_images","base_url":"https://api.openai.com/v1","api_key_env":"OPENAI_PROVIDER_API_KEY","models":["gpt-image-1"]}]
genai-web/packages/web/.envのVITE_APP_IMAGE_MODEL_IDSにgpt-image-1を足し、web を再起動する(モデル一覧はビルド時埋め込み。既定はlocal-sdのみ)
画面のモデル ID gpt-image-1 はテキストからの生成のみです。gpt-image-1 は 2026-10-23 に提供終了したため、Images API には gpt-image-2.5-flare を送り、表示名は GPT Image 2.5 です。gpt-image-2 以降は input_fidelity を付けません。幅・高さは 1024x1024 / 1536x1024 / 1024x1536 のいずれか近いアスペクトに寄せます。ネガティブプロンプトは Avoid: として本文に足します。steps / cfg / seed は送りません。
ローカル SD が止まっていても IMAGE_PROVIDERS が 1 件以上あれば、画像アプリは表示したままです。その状態で local-sd を選ぶと、接続先 URL は出さず、選べる他モデルへの切り替えか管理者への起動依頼を案内します。どちらも無いときは、これまでどおり画像アプリを隠します。
AI アプリの表示(ヘルスチェック)
AI アプリ一覧(/apps)は各 exApp の /health を確認し、起動していない(到達できない)アプリは自動的に利用者向け一覧から隠します。おすすめ(トップ/サイド)も同じです。管理画面(おすすめ設定・棟の機能)には存在する公式アプリをすべて出し、起動中かどうかを表示します。画像生成は、ローカル SD サーバの到達か IMAGE_PROVIDERS の登録を見ます。
Dify 連携(AI アプリ)
外部の Dify で作成した ワークフロー / チャットフロー を、源内の「AI アプリ」として呼び出せます。dify-app という汎用プロキシを 1 つ立て、Dify のフローごとに「AI アプリ」を登録する方式です(フロー単位に接続先・APIキー・種別を設定)。
[ブラウザ] → backend(Team API) → dify-app → 外部 Dify(/v1)
dify-appは源内の AI アプリ・プロトコル(同期{inputs}→{outputs})を、Dify の API(/v1/workflows/runまたは/v1/chat-messages)に変換します。- UI は種別で出し分きます。
dify_app_typeがworkflowのアプリは従来のフォーム実行型 UI、chatのアプリは対話型 UI(吹き出し形式のチャット画面)で開きます。どちらも「AI アプリ」一覧に並びます。 dify-appは Dify API を常に streaming(SSE)で受信します。チャットフロー(chat)は、そのトークンをブラウザまで NDJSON で中継し、対話 UI がタイプライター表示で逐次描画します(/exapps/invoke/stream)。ワークフロー(workflow)は従来どおりサーバ側で集約して同期outputsとして返します。- streaming を採用する理由: Dify は長時間実行やプロキシ切断への耐性から streaming を推奨しており、Agent 系フローは streaming のみ対応です。加えて 1.4.1〜1.13 系には blocking 指定でも
text/event-streamを返す既知の不具合がありました(1.16 系で修正)。
- streaming を採用する理由: Dify は長時間実行やプロキシ切断への耐性から streaming を推奨しており、Agent 系フローは streaming のみ対応です。加えて 1.4.1〜1.13 系には blocking 指定でも
- Dify 本体は本リポジトリには含めません(既存/外部の Dify に接続します)。セルフホスト版は
host.docker.internal経由でホスト上の Dify にも接続できます。Dify クラウド版 はdify_base_urlにhttps://api.dify.ai/v1を指定します(後述)。 - ワークフロー用アプリとチャットフロー用アプリはエンドポイントが同じ(
dify-app)でも問題ありません。APIキー(ワークフロー用 / チャット用)とdify_app_typeで区別します。
登録手順(チーム管理 → アプリの作成)
ヘッダー右上「アカウント」→「チーム管理」→ 対象チーム →「アプリの作成」で、以下を入力します。フォームの項目名と入れる内容の対応に注意してください(接続情報は「コンフィグ」、フォーム定義は「APIリクエストのデータ形式」です)。
| フォーム項目 | 入れる内容 |
|---|---|
| APIエンドポイントのURL | http://dify-app:8004/invoke |
| APIキー | Dify アプリの API キー(Dify の「APIアクセス」で発行) |
| APIリクエストのデータ形式(JSON) | フロー入力に合わせたフォーム定義 JSON(後述) |
| コンフィグ(JSON) | Dify 接続情報 JSON(接続先・種別など。後述) |
コンフィグ(JSON)の例(= AI アプリの config)
コンフィグは 1 行 JSON を推奨します(改行入りだと HTTP ヘッダ経由で dify-app に渡せず、404 や空フォームになることがあります)。整形表示は問題ありませんが、保存時は次のように 1 行にしてください。
{"dify_base_url":"https://api.dify.ai/v1","dify_app_type":"workflow","response_field":"http_status"}
Dify クラウド版
Dify Studio の「APIアクセス」に表示される Base URL(https://api.dify.ai/v1)をそのまま使います。
{"dify_base_url":"https://api.dify.ai/v1","dify_app_type":"workflow","response_field":"http_status"}
- APIキーは
app-で始まるワークフロー用キー(そのアプリ専用。別フローのキーは不可) - ワークフローを 公開 してから呼び出す
- 成果物の再ホスト(Dify ファイル URL の取得)用に
.envへ以下を追加(ローカル/セルフホストと併用する場合はホストを追記):
ARTIFACT_FETCH_ALLOWED_HOSTS=files.dify.ai,upload.dify.ai,host.docker.internal
セルフホスト版(Docker 等)
接続先は環境により異なります。Dify Studio の「APIアクセス」に表示される Base URL をそのまま dify_base_url に指定してください。
| 構成 | dify_base_url の例 |
|---|---|
標準(API が /v1) |
http://host.docker.internal/v1 |
nginx 等で /api/v1 にマウント |
http://host.docker.internal/api/v1 |
成果物ファイルの URL ホスト(FILES_URL)は API ホストと異なることがあります。再ホストのため .env の ARTIFACT_FETCH_ALLOWED_HOSTS に ファイル URL のホスト名 を追加してください(例: host.docker.internal、公開ドメイン、リバプロのホスト名)。allowlist に載せたホストは private IP 解決も許可されます。
{"dify_base_url":"http://host.docker.internal/v1","dify_app_type":"workflow","response_field":"result"}
{"dify_base_url":"http://host.docker.internal/v1","dify_app_type":"chat","query_field":"query"}
dify_base_url: Dify の API ベース URL(末尾/v1または環境の表示どおり)。必須(未設定時は.envのDIFY_BASE_URLを使用)。dify_app_type:workflowまたはchat(既定chat)。query_field(chat): ユーザー入力をどの入力キーから取るか(既定query、後方互換でquestionも可)。response_field(workflow): Dify のoutputsから表示に使うキー。 文字列("report")または配列(["report","citations"])を指定可。 エイリアスresponse_fieldsも可。未指定ならreport等を優先。- 出典アコーディオン: workflow の
citation_artifacts(JSON 配列、またはmime_type=text/x.open-genai.citation相当)をdify-appが artifacts に載せ、 源内 UI(ExAppCitations)でリンク風見出し+展開本文として表示する。 file_var(任意, chat / workflow 共通): 添付ファイルを渡す Dify の入力変数名。通常は/v1/parametersから自動検出するため指定不要。excel_map/excel_var/excel_sheet/excel_forward(任意): 様式 Excel のセル値を開始変数へ注入する opt-in 設定。未設定時は従来どおり。output_mode=xlsx_fill/excel_write_map/excel_values_field/excel_output_filename(任意): 様式への書き戻し成果物。未設定時は従来どおり。詳細は後述の「フォーム/様式 Excel からファイル生成」。
「コンフィグ(JSON)」の既定値
{"max_payload_size":"6MB"}は、上記の Dify 接続情報に置き換えて構いません。
OpenGENAI ナレッジ連携(Dify)
検索の正本は rag-app の HTTP API。Dify 側は HTTP ノード か Agent + MCP で組みます。
| 経路 | DSL | ガイド |
|---|---|---|
| HTTP 固定 WF(入門・本丸) | OpenGENAI-KnowledgeAgent.yml |
docs/dify-knowledge.md 事例 1 |
| Agent + MCP | OpenGENAI-KnowledgeAgent.chatflow.yml |
docs/knowledge-mcp.md / 事例 2 |
| 応用(議事録スタンス) | OpenGENAI-MinutesStance.yml |
docs/dify-knowledge.md 事例 3 |
サンプル文書: knowledge-qa-sample.md(入門)、minutes-stance-sample.md(応用)。
セルフホスト Dify では SSRF 許可が必要です(詳細はガイド):
SSRF_PROXY_ALLOW_PRIVATE_DOMAINS=host.docker.internal
⚠️ セキュリティ(ナレッジ MCP の公開範囲):
knowledge-mcpは 無認証で、scope(teamId) を呼び出し側が任意指定できるため、到達できる者は全チームのナレッジを読めます。 既定のKNOWLEDGE_MCP_BINDは127.0.0.1(同一ホストの Dify 向け)。 LGWAN-ASP の転送対象に載せないでください(詳細: docs/knowledge-mcp.md)。
procedure-mcp(profiles: ["patchform"])も無認証です。到達できる者は公開済み手続きの対応表を読めます。 既定のPROCEDURE_MCP_BINDは127.0.0.1。詳細: docs/procedure-mcp.md。
源内登録の例(workflow / chat):
{"dify_base_url":"http://host.docker.internal:8088/v1","dify_app_type":"workflow","response_field":"report"}
{"dify_base_url":"http://host.docker.internal:8088/v1","dify_app_type":"chat","query_field":"query"}
検証用ワークフロー(SeaweedFS ファイル出力テスト)
リポジトリ同梱の DSL を Dify にインポートして、ファイル出力 → SeaweedFS 再ホストの経路を検証できます。
| ファイル | 内容 |
|---|---|
[dify-app/dsl/File Output Test.yml](dify-app/dsl/File Output Test.yml) |
公開 URL から PDF を取得し result_file として返すワークフロー |
dify-app/dsl/MultiFileGenerator.yml |
複数文書から markdown / html / text / json / docx / pptx を生成。html は単一自己完結(デジタル庁デザインシステム風)。署名 URL またはローカルでブラウザ表示可能 |
dify-app/dsl/FormFileGenerator.yml |
フォーム項目(開始変数)から Dify 側でプロンプトを組み立て、同系のファイル成果物を生成。任意の参考資料(ref_files)と様式 Excel セル注入(excel_map)を併用可 |
手順:
- Dify Studio →「DSL から作成」→ 上記 YAML をインポート → 公開
- 「APIアクセス」で API キー(
app-...)を発行 - 源内「チーム管理」→「アプリの作成」
- endpoint:
http://dify-app:8004/invoke - APIキー: 手順 2 のキー
- コンフィグ: 上記クラウド版 JSON(
response_fieldはhttp_status推奨) - データ形式: 空(
/schemaからfile_urlフォームが自動生成)
- endpoint:
- アプリを実行 → レスポンスに
200と「生成されたファイル」欄が出ること - 配信方式(
ARTIFACT_DELIVERY_MODE)に応じて表示を確認open: 「生成されたファイル」がダウンロードリンク/ボタンになり、SeaweedFS の署名付き URL(開発時はlocalhost:8333、本番はS3_PUBLIC_ENDPOINT)で取得できることcarrier: URL は画面に出ず、ファイル名と LGWAN 向け案内+「〇〇 のリンクファイル」ボタンが表示されること(後述の LGWAN 端末での成果物取得 で検証)auto: アクセス Host が*.lgwan.jpならcarrier、それ以外はopen
Dify API の疎通確認(任意):
curl -s -w "\nHTTP %{http_code}\n" \
-X POST "https://api.dify.ai/v1/workflows/run" \
-H "Authorization: Bearer app-<あなたのキー>" \
-H "Content-Type: application/json" \
-d '{"inputs":{"file_url":"https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"},"response_mode":"blocking","user":"test"}'
HTTP 200 かつ status: succeeded なら Dify 側は正常です。
フォーム/様式 Excel からファイル生成(FormFileGenerator)
既存の dify-app を使った opt-in 拡張です。excel_map を書かない従来アプリの挙動は変わりません。
役割分担
| 関心事 | 正本 |
|---|---|
| 変数名・型・必須 | Dify 開始変数 |
| プロンプト組み立て | Dify(FormFileGenerator の「プロンプト組み立て」Code) |
| ラベル・説明・記入例 | 源内 Form Spec(title / desc)。キー名=開始変数名 |
| 様式 Excel → 変数 | exApp config の excel_map(dify-app が invoke 前に注入) |
A. フォーム駆動
FormFileGenerator.ymlを Dify にインポートして公開し、API キーを発行- 源内「アプリの作成」
- endpoint:
http://dify-app:8004/invoke - コンフィグ(1 行)例:
{"dify_base_url":"https://api.dify.ai/v1","dify_app_type":"workflow","response_field":"result","file_var":"ref_files"} - データ形式:
form-file-generator-placeholder.jsonの内容を貼る(descで入力支援。キーはtitle/dept/request/ 任意のref_files等)
- endpoint:
- 実行 → 「生成されたファイル」が表示されること
自動フォーム(データ形式を空)でも動きますが、Dify の label しか出ないため、業務利用では Form Spec の desc 付きを推奨します。
**参考資料(ref_files)**は任意・複数可です。様式 Excel とは別欄で、PDF / Word 等を添付して生成の根拠に使えます。
B. 様式 Excel 駆動(同じ WF)
セル値を開始変数へ注入し、同じ FormFileGenerator でプロンプト組み立て〜生成します。参考資料も同じ画面から別途添付できます。
- 上記と同じ DSL / API キーで別アプリ(または同じアプリ)を登録
- データ形式:
form-file-generator-excel-placeholder.json(form_xlsxとref_filesが分かれている) - コンフィグ例(
form-file-generator-excel-config.jsonを 1 行化):
{"dify_base_url":"https://api.dify.ai/v1","dify_app_type":"workflow","response_field":"result","file_var":"ref_files","excel_var":"form_xlsx","excel_map":{"title":"B2","dept":"C5","request":"B12"},"excel_sheet":"様式","excel_forward":false}
| config キー | 意味 | 既定 |
|---|---|---|
excel_map |
開始変数名 → セル参照(B2 / Sheet1!C5 / '様式'!A1) |
未設定なら何もしない(後方互換) |
excel_var |
様式ファイルのフォームキー | form_xlsx |
excel_sheet |
セル参照にシートが無いときの既定シート | 先頭シート |
excel_forward |
true なら様式ファイルを Dify にも転送 |
false(セル注入のみ。参考資料 ref_files は別途転送される) |
file_var |
参考資料を渡す Dify 変数名 | 自動検出。Excel 併用時は ref_files を明示推奨 |
output_mode |
xlsx_fill で様式への書き戻し成果物を生成 |
未設定=従来の文章ファイル経路のまま |
excel_write_map |
書き戻しキー → セル参照 | output_mode=xlsx_fill 時に必要 |
excel_values_field |
Dify outputs 内の値辞書キー | excel_values |
excel_output_filename |
書き戻し後のファイル名 | <元名>_filled.xlsx |
既にフォームで値が入っているキーは Excel で上書きしません。.xlsx / .xlsm のみ対応です。
C. 様式 Excel 書き戻し(workflow / chat 共通)
文章ファイル(docx 等)ではなく、アップロードした様式のセルを更新した xlsx を成果物にします。判断・文案は Dify、セル書き込みは dify-app(config)です。
- データ形式は B と同じ(
form_xlsx+任意のref_files) - コンフィグ例:
form-file-generator-excel-fill-config.json - Dify は書き戻し値を次のいずれかで返す(値があるときだけ xlsx を付与。下書きターンでは付与しない)
- workflow: outputs の
excel_values(JSON/dict)。例:{"summary":"…","result":"…"} - または outputs に
excel_write_mapと同じキーを個別出力 - chat: 回答中の JSON(または ```json フェンス)。例:
{"summary":"…"}
- workflow: outputs の
dify-appがテンプレへ書き、artifacts としてダウンロード可能にする(backend が SeaweedFS へ再ホスト)
チャットでは C+B(下書き→「ファイル出力」等の明示)と組み合わせ、出力ターンだけ excel_values を返す設計を推奨します。
DSL を再生成する場合: python3 dify-app/scripts/generate_form_file_generator_dsl.py
トラブルシュート(Dify 連携)
| 症状 | よくある原因 | 対処 |
|---|---|---|
404(ワークフロー呼び出し失敗) |
dify_base_url が誤り(クラウドなのに host.docker.internal 等) |
https://api.dify.ai/v1 に修正 |
401 |
API キー不一致・未公開 | 正しい app- キー、ワークフロー公開を確認 |
| 入力フォームが出ない / 実行しても何も表示されない | コンフィグ JSON の改行 | 1 行 JSON に修正し backend 再起動 |
※必須 エラー(値が入っているのに) |
動的スキーマの default_value 未同期 |
ページをリロード(修正済み。古い web イメージの場合は再ビルド) |
ファイルリンクが Dify 直 URL のまま(host.docker.internal 等) |
再ホスト失敗。ARTIFACT_FETCH_ALLOWED_HOSTS に ファイル URL のホストが無い、または backend 未再起動 |
.env に該当ホスト(例: host.docker.internal)を追加し docker compose up -d backend。成功時は SeaweedFS の署名付き URL になる |
502(源内からの実行) |
dify-app 未起動、コンフィグ改行 |
docker compose ps、コンフィグを確認 |
APIリクエストのデータ形式(JSON) の例(= AI アプリの placeholder / 入力フォーム)
入力フォームは AI アプリ API 仕様 に従って定義します。フォームの各キーが Dify の入力変数名に対応します。
ワークフロー(フォーム型)の例:
{
"query": {
"title": "入力",
"type": "textarea",
"required": true
}
}
フォーム型(ワークフロー)は、このデータ形式を空のままにすると、アプリを開いたときに
dify-appが Dify の/v1/parametersから入力フォームを自動生成します(手書き不要)。Dify のコンポーネント(text-input/paragraph/number/select/file/file-list)を源内のフォーム項目に変換します。手書きで定義した場合はそちらが優先されます。
チャットフロー(dify_app_type: "chat")は対話型 UI で開くため、このフォーム定義は画面には使われません。空のままで構いません。
{
"query": { "title": "メッセージ", "type": "textarea", "required": true }
}
チャットフロー = 対話型 UI(対話できる)
dify_app_type を chat にしたアプリは、源内が 対話型 UI(吹き出し形式のチャット画面)で開きます。フォーム実行型ではなく、メッセージを送るたびに会話が継続します。
- 会話の文脈は、源内が会話ごとに発行する
sessionIdをdify-appが Dify のconversation_idに対応付けて保持することで維持されます(SQLite,dify_app_dataボリューム)。 - 画面の 「新しい会話」 ボタンで
sessionIdをリセットし、新しい Dify 会話を開始できます。 - 画像 / ドキュメントの添付に対応します(
dify-appが/v1/files/upload経由で Dify に渡します)。「ファイルを添付」ボタンは、アプリがファイルを受け付けられる場合のみ表示されます(下記「ファイル添付」の判定条件を参照)。ファイルを使わないエージェント(例: ナレッジ検索エージェント)ではボタンは出ません。 - チャットフロー用アプリには チャットフローの API キー を登録してください(ワークフローとはキーで区別)。
補足: フォーム型アプリ(ワークフロー等)でも、placeholder に
conversation_historyキーを含めると実行結果に「会話を続ける」ボタンが出ます(疑似チャット)。チャットフローは上記の対話型 UI を使うため、この指定は不要です。AI アプリの実行履歴は backend(SQLite,
backend_dataボリュームのopen-genai-teams.db) に保存されます。
ファイル添付
添付ファイルは Dify の /v1/files/upload にアップロードし、upload_file_id 参照として渡します。ファイル種別(image / audio / video / document)は MIME から自動判定します。
ファイル入力変数の解決は チャットフロー / ワークフロー共通 で、次の順に行います(変数名は源内側で固定しません。フロー作成により変わってよい)。
configの"file_var": "<変数名>"(画面から明示指定・任意の上書き)- Dify の
/v1/parametersからfile/file-list型の入力変数(例:upload_files)を自動検出 - 上記で解決できない場合のフォールバック
- チャットフロー: メッセージ添付(
sys.files)として送信 - ワークフロー: 源内フォームのキー名を Dify 変数名として割り当て
- チャットフロー: メッセージ添付(
- 変数の型(
file/file-list)も/v1/parametersから判定し、単一/配列で渡します。 - Dify 側でフローが「メッセージ添付」ではなく「入力変数(file-list)」でファイルを受け取る設計(
file_upload.enabled: falseでも入力変数は利用可)にも対応します。
添付ボタンの表示可否(能力検知)
対話型 UI の「ファイルを添付」ボタンは、dify-app の /schema が返す features.file_attach が true のときだけ表示されます。判定は次の順で行い、いずれにも該当しない場合や /parameters の取得に失敗した場合は 非表示(fail-closed) です。
configの"file_attach": true/false(明示指定。falseは他条件より優先して強制 OFF)configに"file_var"が設定されている- Dify の
/v1/parametersのuser_input_formにfile/file-list型の入力変数がある - Dify の
/v1/parametersのfile_upload.enabledがtrue(メッセージ添付sys.files経路)
これにより、file_upload.enabled: false かつ file 入力変数を持たないアプリ(例: ナレッジ検索エージェント)では、押しても効かない添付ボタンが表示されなくなります。
成果物ファイル(SeaweedFS 再ホスト)
Dify 等が返すファイル URL をそのまま利用者に渡さず、backend が SeaweedFS(S3 互換)
へ再アップロードし、署名付き URL を outputs に注入します。
[ブラウザ] → backend → (Dify からファイル取得) → SeaweedFS へ保存
↓
署名付き URL を outputs / 履歴に記録
↓
[ブラウザ] ──GET──→ (公開経路)──→ SeaweedFS
- ドキュメント類は Markdown のダウンロードリンクとして提示(画像はインライン
contentがあれば従来どおり) - 監査ログに
file.outputを記録 - オブジェクトキーにメールアドレス等は載せず、ユーザ ID の SHA-256 ハッシュ を使用
- 詳細は CHANGELOG.md の「生成ファイルのオブジェクトストレージ配置」を参照
環境変数(.env)
| 変数 | 既定(開発) | 説明 |
|---|---|---|
S3_ENDPOINT_URL |
http://seaweedfs:8333 |
backend → SeaweedFS(内部。アップロード・削除) |
S3_PUBLIC_ENDPOINT |
http://localhost:8333 |
利用者向け署名付き URL のホスト(下記リバースプロキシ) |
S3_BUCKET |
open-genai |
バケット名 |
S3_PRESIGN_EXPIRY |
86400(24h) |
署名付き URL の有効期限(秒)。ファイル本体の保持期限ではない |
S3_ARTIFACT_RETENTION_DAYS |
30 |
成果物と実行履歴の保持日数(超過分を日次削除。0 で無効) |
S3_ARTIFACT_PURGE_INTERVAL |
86400 |
上記パージの実行間隔(秒) |
ARTIFACT_FETCH_ALLOWED_HOSTS |
(空) | 成果物取得を許可するホスト。クラウドは files.dify.ai,upload.dify.ai、ローカルは host.docker.internal、セルフホストは FILES_URL のホストを追加。allowlist ホストは private IP も可 |
ARTIFACT_DELIVERY_MODE |
open(本番既定 carrier) |
配信方式。open=常に直接リンク、carrier=常にリンクファイル、auto=Host が *.lgwan.jp なら carrier(下記 LGWAN) |
ARTIFACT_CARRIER_FORMAT |
txt |
carrier 時のリンクファイル形式(txt / html / both) |
署名付き URL について
利用者に見える X-Amz-* クエリは S3 互換の一時ダウンロードチケット です。シークレットキーは含まれません。URL を知っている人は有効期限内にダウンロードできます(ブラウザ履歴・ログに残る点に注意)。
- URL の期限切れ(
S3_PRESIGN_EXPIRY)≠ ファイル削除(S3_ARTIFACT_RETENTION_DAYS) - 利用履歴を UI から削除すると、紐づく SeaweedFS 上のファイルも削除されます
- 保持日数を超えた成果物と実行履歴は、backend 起動時のバックグラウンド処理で自動削除されます
LGWAN 端末での成果物取得(配信方式: open / carrier / auto)
LGWAN 端末からは、SeaweedFS の署名付き URL も、後述のリバースプロキシの公開 URL も、
通常はそのままダウンロードできません(インターネット側の宛先に届かないため)。そこで
配信方式を ARTIFACT_DELIVERY_MODE で切り替えられます。
| モード | 挙動 | 想定環境 |
|---|---|---|
open |
結果画面・履歴に署名付き URL を直接リンク表示(クリックで取得) | 開発、成果物へ直接到達できる環境 |
carrier |
URL を画面に出さず、**URL を記載した「リンクファイル(.txt/.html)」**をダウンロードさせる | LGWAN のみ、または常に持ち出し運用にしたい環境 |
auto |
アクセス Host が *.lgwan.jp(または lgwan.jp)なら carrier、それ以外は open |
インターネット面と LGWAN 面を同じ系統で併設する環境 |
carrier の運用フロー:
flowchart LR
subgraph lgwan [LGWAN端末]
UI["源内 結果画面"]
Carrier["リンクファイル(.txt/.html)"]
end
subgraph net [インターネット接続端末]
File["成果物本体"]
end
UI -->|"リンクファイルをDL(源内API経由)"| Carrier
Carrier -->|"データ持ち出し経路で移送"| net
Carrier -->|"記載URLを開く"| File
- AI アプリ実行後、結果画面の「ファイル一覧」に ファイル名のみが表示されます(生 URL は非表示)。
Markdown エディタの「書き出し」も同じ配信方式です(
carrierではリンクファイル、openでは zip の直接ダウンロード)。 - 「〇〇 のリンクファイル」ボタンで
.txt(既定)または.htmlを源内ポータル(/api/...)経由でダウンロード。 - 職員が承認済みのデータ持ち出し経路でインターネット接続端末へ移送。
- リンクファイル内の URL を別端末で開き、成果物本体を取得。
補足:
- リンクファイルは backend の
GET /exapps/artifact-carrierが生成します。所有者チェック(オブジェクトキーのユーザーハッシュ照合)を行い、本人の成果物のみ発行します。 - リンクファイルには URL の有効期限(
S3_PRESIGN_EXPIRY起点)を記載します。持ち出しの遅延を見込み、本番では期限を長め(例: 72 時間)に設定することも検討してください。 - リンクファイル自体が有効な URL を含む秘密情報です。第三者に共有しないよう画面と文面で案内しています。
- 形式は
.txtが最も持ち出しフィルタを通しやすく、.htmlはクリック可能なリンクと URL コピー欄を備えます(外部スクリプト・外部リソースなしの静的 HTML)。
動作検証(carrier モード)
同梱の [dify-app/dsl/File Output Test.yml](dify-app/dsl/File Output Test.yml)(前述の「検証用ワークフロー」で登録した AI アプリ)を使って、carrier の一連の流れを確認できます。
.envにARTIFACT_DELIVERY_MODE=carrier(形式を試す場合はARTIFACT_CARRIER_FORMAT=txt/html)を設定し、backend を再起動:
docker compose up -d backend
- 上記 AI アプリを実行(
file_urlは既定の PDF のままで可)。 - 結果画面で次を確認:
- 「生成されたファイル」に URL が表示されず、ファイル名と LGWAN 向け案内が出ること
- 「〇〇 のリンクファイル」ボタンで
.txt(または.html)がダウンロードされること - リンクファイルの中身に ダウンロード URL と 有効期限 が記載されていること
- リンクファイル内の URL を(LGWAN 外の)ブラウザで開き、成果物 PDF が取得できること。
- 別ユーザーの成果物キーで
GET /exapps/artifact-carrierを叩くと403になること(所有者チェック。任意)。
curl でのリンクファイル取得確認(任意。objectKey は結果 API の artifacts[].object_key):
curl -s -D - -o link.txt \
-H "Authorization: Bearer <源内のアプリJWT>" \
"http://localhost:8000/exapps/artifact-carrier?objectKey=exapp/<hash>/<uuid>/dummy.pdf"
cat link.txt
openモードに戻すにはARTIFACT_DELIVERY_MODE=openにして backend を再起動します。同じ AI アプリで、今度は「生成されたファイル」がダウンロードリンクになることを確認できます。
リバースプロキシ(SeaweedFS の公開経路)
SeaweedFS のポート(8333)をインターネットに直接公開しない 構成を想定しています(docker-compose.prod.yml ではホスト公開なし)。利用者が取得する URL は S3_PUBLIC_ENDPOINT で決まり、コード変更は不要です。プロキシを用意したら .env の S3_PUBLIC_ENDPOINT だけ差し替えて backend を再起動してください。
| 役割 | 設定例 |
|---|---|
| 内部(backend → SeaweedFS) | S3_ENDPOINT_URL=http://seaweedfs:8333(変更不要) |
| 公開(署名付き URL のホスト) | S3_PUBLIC_ENDPOINT=https://files.example.lg.jp |
推奨: 源内本体(proxy/nginx.conf の /・/api/)とは別ホスト、または別サーバブロックで SeaweedFS に転送する。UI の 443 が IP 制限されている場合は、443 を開けた別サーバに置き proxy/files.public.conf.example を使う。8333 はホストに出さない。backend の書き込みも同じ公開 HTTPS(S3_ENDPOINT_URL)にできる。
nginx 設定例(専用サブドメイン files.example.lg.jp → SeaweedFS):
server {
listen 443 ssl;
server_name files.example.lg.jp;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
client_max_body_size 64m;
resolver 127.0.0.11 valid=30s ipv6=off;
location / {
set $s3_upstream http://seaweedfs:8333;
proxy_pass $s3_upstream;
proxy_http_version 1.1;
# 署名付き URL の検証のため Host をクライアントがアクセスしたホストに合わせる
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_buffering off;
}
}
この場合 .env.prod では:
S3_PUBLIC_ENDPOINT=https://files.example.lg.jp
署名付き URL は https://files.example.lg.jp/open-genai/exapp/<hash>/<uuid>/output.pdf?X-Amz-... の形になります。パスとクエリはプロキシがそのまま SeaweedFS に転送してください(書き換えないこと)。
開発時のみ docker-compose.yml が 8333:8333 をホストに公開しているため、S3_PUBLIC_ENDPOINT=http://localhost:8333 でそのまま検証できます。
LGWAN 端末がこの公開 URL にも到達できない場合は、上記「LGWAN 端末での成果物取得」の
carrierモードを使い、URL を記載したリンクファイルを持ち出して別端末で開いてください。
制約
- チャットフローは NDJSON ストリーミングでトークンを逐次表示します(
/exapps/invoke/stream)。ワークフロー / フォーム実行型は同期形式({inputs}→{outputs})で、完了後に一括表示します。いずれも非同期ポーリング形式は未対応です。 - 会話継続はチャットフローが対象です(ワークフローは状態を持ちません)。
チーム / AI アプリ管理
源内の「チーム管理」をローカル(SQLite)で実装しています。オリジナル源内のチーム概念を 拡張しており、自治体の実態に合わせた運用を想定しています。
テナント(棟)とチーム
- 棟:
tenantsがチームより上位。既存データはデフォルト棟。共通チームはデフォルト棟の部屋。共有棟の鍵は自動では付与しない - 登録は棟が先: 真のマルチテナント運用では、利用者は必ずどれかの棟の一員として登録する(利用者登録/CSV で棟を必須指定)。ログインだけでは棟を自動付与せず、どの棟の鍵も無い利用者はカタログ・履歴が空になる(画面に案内を出す)。デフォルト棟は既存データの移行寄せで、単一組織運用でも使える普通の組織棟として残す
- 履歴など: チャット履歴・保存プロンプト・ピン留め・AI アプリ実行履歴は活性棟ごと。ナレッジを使う処理が他棟へ混ざらないようにする。既存行はデフォルト棟へ移す
- 鍵: 主鍵(自棟)/招待鍵(別棟)/共通鍵(共有棟)。共有棟の鍵は自動では付与しない。別組織または共有棟の鍵がある人だけヘッダーで切り替える。スーパー管理者も棟が1つなら切り替えは出さない(「棟の管理」で操作する)
- 主所属 + 追加タグ: 主所属は1つ(
team_users.isPrimary)。兼務・プロジェクトは追加の明示所属 - 親子(任意):
teams.parentTeamId。同じ棟の中だけ。親所属者は配下課のアプリ・ナレッジを閲覧できる(書込・管理は明示メンバーのみ) - 複数所属: 1 人の利用者が 複数チーム に所属可能(
team_usersの(teamId, userId)複合キー) - 可視範囲: 利用者は 活性棟の所属チーム + 共有棟 の公開 AI アプリを利用可能。棟ごとの
featuresでカタログを隠せる - 保存プロンプトの共有: 作成時に「全体公開」または 所属チーム(複数選択) を指定可能。 チーム ID が共有タグとして保存され、所属メンバーが利用できる
権限
- 棟の作成/招待/機能出し分け(ヘッダー右上「アカウント」→「棟の管理」)
- おすすめアプリ(トップ/サイド)はシステム管理者が全体で設定(「アカウント」→「おすすめアプリ」)。AIアプリ一覧は対象外。利用者向け一覧は未起動を隠す。管理画面では公式アプリの存在と起動状態を出す
- ナレッジ管理はおすすめ(専用ページ)。ナレッジ検索はおすすめ設定と AI アプリ一覧。棟の機能でもそれぞれ出し分けできる
- チーム・メンバー・AI アプリの作成/編集/削除(ヘッダー右上「アカウント」→「チーム管理」)
- 権限モデル:
- スーパー管理者(
SystemAdminGroup): 全棟を見る。棟の作成/削除、チーム削除、管理者ツール。人数は最少(一人でも可) - 棟の管理者(
tenant_memberships.isAdmin): その棟の招待・機能出し分け・チーム作成・ナレッジ/アプリ/メンバー。他棟は見えない - チーム管理者(
team_users.isAdmin): 自チームのメンバー/アプリを管理。ログイン時に自動でTeamAdminGroupが付与され、Keycloak 側でのグループ手動設定は不要 - 一般メンバー: 活性棟の所属チームのアプリを閲覧・実行。棟は意識しない
- スーパー管理者(
- デフォルト棟の共通チーム(
00000000-0000-0000-0000-000000000000)は自組織の共通ナレッジ/カタログ。共有棟は招待した人だけが見る - 管理者ツール(監査・利用者一括・モデル制御・入力制限・RAG 管理)は専用チームに配置され、システム管理者のみに表示
- チーム作成時: そのチーム専用の「ナレッジ検索」アプリを自動登録(タグ管理・登録・管理は専用ページ
/knowledgeのスコープ選択で実施) - チーム削除時: メンバー・AI アプリ設定に加え、そのチームの知識ベース(Qdrant)も自動消去
- AI アプリは「リクエスト形式」(JSON) でフォームUIを定義し、外部マイクロサービスの REST API(同期:
{inputs}→{outputs})を呼び出します(AI アプリ API 仕様 準拠)。OpenGENAI 拡張の Form Spec v1 により条件表示・リアクティブフォームも利用可能
データは backend_data ボリュームの open-genai-teams.db に保存されます。
管理者向け exApp(システム管理者限定)
一般利用者には表示されず、実行も拒否されます。源内 UI 内で運用タスクを完結させるための exApp です。
| exApp | 用途 |
|---|---|
| 監査ログ参照 | 利用状況・内容ログの検索・閲覧 |
| 利用者一括管理 | CSV による Keycloak 利用者の作成・更新・削除 |
| モデル利用制御 | グループ/チーム別の利用可能モデル設定 |
| 入力制限 | 禁止語・カスタム正規表現、添付警告/ナレッジ検知/NER のトグル、マイナンバー検査 |
| プロンプトテンプレート | 標準/個人/グループ共有テンプレートの管理 |
入力制限と個人情報検知(添付・ナレッジ)
禁止語ブロックに加え、氏名・住所・電話番号・マイナンバーを種別付きで検知できます。
匿名化は行わず、警告/ラベル表示のみ(アップロードやナレッジ登録自体は止めません)。
実装の中核は shared/pii_scan.py です。
動作概要
| 経路 | タイミング | 利用者向けの見え方 |
|---|---|---|
| チャット等の添付 | PUT /files 保存と同時(同期)。先頭 PII_NER_MAX_CHARS(既定 8,000)文字まで NER |
添付行に種別と検知箇所の抜粋付き警告。送信は可能 |
| ナレッジ登録 | 非同期ジョブ内で索引化のあと検知(PII_KNOWLEDGE_NER_MAX_CHARS 既定 200,000) |
/knowledge のドキュメント一覧に pii_labels 等を表示 |
| プロンプト本文 | 従来どおり禁止語・カスタム正規表現・マイナンバー検査 | ヒット時はブロック(従来仕様) |
管理者向け 入力制限 exApp で次を切り替えられます。
- 添付アップロード時の個人情報警告(
warn_attachments) - ナレッジ登録時の個人情報検知(
scan_knowledge_pii) - 氏名・住所の NER 検知(
check_pii_ner。GiNZA 未導入時は実質オフ) - マイナンバー検査・禁止ワード・カスタム正規表現(従来どおり)
検知方式
| 種別 | 方式 | 備考 |
|---|---|---|
| 電話番号 | 正規表現 | イメージへの GiNZA 導入不要 |
| マイナンバー | 検査用数字(総務省令) | 単なる 12 桁数字では検知しない |
| 住所 | 都道府県+市区町村などのパターン + 任意の NER | NER は GiNZA 導入時 |
| 氏名 | 任意の NER(GiNZA / spaCy) | 未導入なら氏名 NER はスキップ。明らかな誤検知はフィルタ |
GiNZA(spaCy)のセットアップ
氏名 NER と住所 NER 強化には、spaCy 系の日本語モデル GiNZA(ginza + ja-ginza)を
backend / rag-app イメージに入れます。既定ビルド(PII_INSTALL_NER=0)では入れず、
電話・マイナンバー・住所パターンだけが動きます(イメージを軽量に保つため)。
Dockerfile(backend/Dockerfile / rag-app/Dockerfile)はビルド引数で分岐します。
ARG PII_INSTALL_NER=0
RUN if [ "$PII_INSTALL_NER" = "1" ]; then \
pip install --no-cache-dir 'ginza>=5.1.3' 'ja-ginza>=5.1.3'; \
fi
Compose は .env / .env.prod の PII_INSTALL_NER を build args に渡します。
# --- 開発 ---
# .env に PII_INSTALL_NER=1 を設定してから再ビルド(またはコマンド先頭で渡す)
PII_INSTALL_NER=1 docker compose up -d --build backend rag-app
# --- 本番 ---
# .env.prod に PII_INSTALL_NER=1 を設定してから:
PII_INSTALL_NER=1 docker compose -f docker-compose.prod.yml --env-file .env.prod \
up -d --build backend rag-app
# backend 再作成直後に proxy が一時的に名前解決に失敗する場合は:
docker restart open-genai-proxy
確認の目安:
- コンテナ内で
python -c "import spacy; spacy.load('ja_ginza'); print('ok')"が通る - 入力制限で「氏名・住所の NER 検知」を「する」にしたうえで、氏名を含むテキスト添付で警告が出る
- GiNZA 読込に失敗してもサービスは起動し、電話・マイナンバー等は継続動作する(ログに
[pii_scan] GiNZA 読込失敗)
| 変数 | 既定 | 役割 |
|---|---|---|
PII_INSTALL_NER |
0 |
ビルド時に GiNZA を入れるか(1 で有効) |
PII_NER_MAX_CHARS |
8000 |
同期添付経路の NER 対象文字数上限 |
PII_KNOWLEDGE_NER_MAX_CHARS |
200000 |
ナレッジ非同期経路の NER 対象文字数上限 |
注意:
- GiNZA 導入でイメージサイズとビルド時間が増えます(初回は依存取得のためネットワークが必要。閉域では事前に依存をキャッシュ/ミラーしてください)
ja-ginza-electraは既定では入れません(より重い)。必要なら Dockerfile のpip install行を拡張してください- 検知は補助であり、網羅や誤検知ゼロを保証しません。運用では警告を確認し、本番文書での誤検知を見てトグルやフィルタを調整してください
制限事項(ローカル版)
- 画像生成は源内 Web の 「画像を生成」(
/image)を利用します。ホストで A1111 互換 SD サーバ(またはscripts/mock-sd-server.py)が必要です。 - AI アプリの呼び出しは、Dify チャットフローが NDJSON ストリーミング、それ以外(ワークフロー / フォーム実行型)は同期形式(非同期のポーリング形式は未対応)
- 添付のうち 動画 はローカル LLM が直接扱えないため未対応(画像・ドキュメントは対応)
- 認証は SAML(Keycloak)で行います。開発用は HTTP・既定パスワードです。
閉域・本番では
docker-compose.prod.yml、TLS 証明書、INTERNAL_SIGNING_SECRET、S3_*、SSRF 許可ホスト等を必ず見直してください(.env.prod.example参照)
貢献・セキュリティ
- 貢献の進め方: CONTRIBUTING.md
- 行動規範: CODE_OF_CONDUCT.md
- 脆弱性報告: SECURITY.md(GitHub Security Advisories を優先。公開 Issue も可だが詳細・PoC は禁止)
ライセンス
Explore related projects
affaan-m/ECC
The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.
300,313 starsNousResearch/hermes-agent
The agent that grows with you
262,134 starsmultica-ai/andrej-karpathy-skills
A single CLAUDE.md file to improve Claude Code behavior, derived from Andrej Karpathy's observations on LLM coding pitfalls.
231,460 starsfirecrawl/firecrawl
Supercharge your AI agents with data from the web and beyond. Building the library for superintelligence. 🔥
200,178 stars