2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OCI入門(3) OCIの AlwaysFree 枠で「MAGIシステム」的な仕組みを再現してみた

2
Last updated at Posted at 2026-03-04

はじめに

先月に A2A で MAGI システムを作ろう、という記事を書きましたが、諸般の事情により、まずは OCI を使って、無料で作成できる MAGI的Webシステム を作成してみました。

MAGIシステムというのは、3つの独立した人格の AI が議論し、最終決議を下すというものですが、このプロセスを、無料で使える OCI のクラウド基盤と、WebUI を簡単に作成できる Streamlit を使い、無料枠の範囲で Gemini 2.5 Flash API を呼び出すという組み合わせで、完全無料で実現してみましたので、ご参考までに見てみてください。

動作環境の準備

① インスタンス準備
 VM: VM.Standard.E2.1.Micro
 (無料枠ではメモリが1GBしかない上記のインスタンスしか作成できませんでした・・)
 OS: Ubuntu 22.04(メモリ消費が少ないため)

② メモリ不足対策
 OCIの無料枠(1GBメモリ等)でもスワップ領域を確保することで、OSやアプリケーションを安定動作させることができます。

# 2GBのスワップ領域を確保
sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

③ 各種パッケージのインストール
 pip の場所が不明になる問題を回避するため、仮想環境(venv)を構築します。
(こちらは Gemini3 に作成してもらったまま使用しております)

sudo apt update && sudo apt install -y python3-pip python3-venv
python3 -m venv venv
source venv/bin/activate
# langchain-google-genaiをインストール
pip install -U streamlit langchain-google-genai langchain-community chromadb

④ ネットワーク通信の開通
 外部ブラウザからアクセス可能にするための設定です。
 ・サーバー内ファイアウォールの設定

sudo iptables -I INPUT -p tcp --dport 8501 -j ACCEPT
sudo netfilter-persistent save

 ・OCIセキュリティ・リストの設定
  ソースCIDR: 0.0.0.0/0
  宛先ポート範囲: 8501 (TCP) を許可

※本件では、かなり簡易的な設定を行っていますが、実際にはきちんとセキュリティ設定を行うことをお勧めします。

💡 Streamlit とは?
HTML/CSS/JavaScriptを一切書かずに、Pythonだけで Web UI を作成できます。
また機会がありましたら、Streamlit について詳しくご紹介します。

プログラムの実装

あらかじめ、Google Gemini APIキーの取得(無料)を行って起き、下記プログラムの
「YOUR_API_KEY_HERE」部分に、APIキーを設定してください。
(あくまで実験用のため直書きしていますが、ソースコードにAPIキーを直書きすることは避けてください)

Python magi.py
import streamlit as st
import os
import time
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.messages import HumanMessage

# APIキーの設定
# 注: 本番運用時は環境変数等で管理することを推奨します
GEMINI_API_KEY = "YOUR_API_KEY_HERE"

# --- 画面設定とCSSハック (MAGI専用デザイン) ---
st.set_page_config(page_title="MAGI SYSTEM", layout="wide")

st.markdown("""
    <style>
    body { background-color: #000; }
    .stApp { background-color: #000; }
    .magi-card {
        padding: 20px; border-radius: 5px; margin-bottom: 10px;
        border: 1px solid #ff4b00; background-color: #0a0a0a;
        color: #ff4b00; font-family: 'Courier New', monospace;
        min-height: 250px; box-shadow: 0 0 10px #ff4b00;
    }
    .magi-header { font-weight: bold; font-size: 1.3em; border-bottom: 1px solid #ff4b00; margin-bottom: 10px; }
    
    /* 最終決議用の点滅アニメーションと巨大文字 */
    @keyframes blink { 0% { opacity: 1; } 50% { opacity: 0.3; } 100% { opacity: 1; } }
    .final-decision-box {
        padding: 40px; text-align: center; border: 5px double #ff4b00;
        background-color: #000; margin-top: 20px;
    }
    .status-code {
        font-size: 5em; font-weight: bold; animation: blink 0.8s infinite;
        letter-spacing: 10px; margin: 20px 0;
    }
    .verdict-text {
        font-size: 2em; border: 2px solid #ff4b00; display: inline-block; padding: 10px 30px;
    }
    </style>
    """, unsafe_allow_html=True)

st.title("🔴 MAGI SYSTEM : STRATEGIC DECISION")
st.info("OCI Always Free x Gemini 2.5 Flash:3つの独立した人格による分散合議プロセス")

# モデル初期化
model = ChatGoogleGenerativeAI(model="gemini-2.5-flash", google_api_key=GEMINI_API_KEY)

# 3者のペルソナ定義
magi_config = {
    "MELCHIOR-1": "科学者:論理的、効率的、技術的実現性を重視。感情を排した冷徹な判断を下す。",
    "BALTHAZAR-2": "母:倫理、安全、社会への影響、長期的な保護を重視。リスクを極端に嫌う。",
    "CASPER-3": "個人(女):直感、情熱、独自の美学、競争優位性を重視。時に大胆な賭けを支持する。"
}

if prompt := st.chat_input("審議事項を入力してください(例:新規事業へ投資すべきか?)"):
    with st.chat_message("user"):
        st.write(f"【諮問内容】: {prompt}")

    results = {}
    cols = st.columns(3)

    # ステータス管理
    with st.status("MAGI審議中... システム接続確認", expanded=True) as status:
        for i, (name, persona) in enumerate(magi_config.items()):
            with cols[i]:
                st.markdown(f"🧬 **{name}** System Online...")
                
                # レート制限(429)回避のリトライロジック
                for attempt in range(5):
                    try:
                        time.sleep(i * 1.5) 
                        sys_prompt = f"あなたはMAGIの{name}です。役割は{persona}です。諮問に対し150字程度で意見を述べ、最後に必ず【賛成】か【反対】か【条件付き賛成】を明記してください。"
                        response = model.invoke([HumanMessage(content=f"{sys_prompt}\n\n諮問: {prompt}")])
                        results[name] = response.content
                        
                        st.markdown(f"""<div class='magi-card'>
                            <div class='magi-header'>{name} ANALYSIS</div>
                            {response.content}
                        </div>""", unsafe_allow_html=True)
                        break
                    except Exception as e:
                        st.caption(f"再試行中... ({attempt+1}/5)")
                        time.sleep(10)
        status.update(label="審議完了。最終決議を生成中...", state="complete", expanded=False)

    # --- 合意形成ロジック ---
    votes = {"賛成": 0, "条件付き賛成": 0, "反対": 0}
    for res in results.values():
        if "条件付き賛成" in res: votes["条件付き賛成"] += 1
        elif "賛成" in res: votes["賛成"] += 1
        elif "反対" in res: votes["反対"] += 1

    summary_prompt = f"以下の3者の意見を統合し、最終的な結論を250字程度で出力してください。多数決の結果を含めてください。\n\n{results}"
    final_summary = model.invoke([HumanMessage(content=summary_prompt)])

    # 判定とカラーリング
    is_passed = (votes["賛成"] + votes["条件付き賛成"]) >= 2
    code = "601" if is_passed else "602"
    status_label = "PASSED (可決)" if is_passed else "REJECTED (否決)"
    main_color = "#00ff00" if is_passed else "#ff0000"

    # 最終決議の巨大表示
    st.markdown(f"""
        <div class="final-decision-box" style="border-color: {main_color};">
            <div style="color: {main_color}; font-size: 1.5em;">MAGI FINAL DECISION</div>
            <div class="status-code" style="color: {main_color};">CODE: {code}</div>
            <div class="verdict-text" style="color: {main_color}; border-color: {main_color};">
                {status_label}
            </div>
        </div>
    """, unsafe_allow_html=True)

    st.markdown(f"### 📢 最終報告サマリー\n{final_summary.content}")
    st.bar_chart(votes)

いよいよ実行!!

OCI のコンソールより、下記を実行します。

 python3 -m streamlit run magi.py

実行すると、下記の画面になります。

Collecting usage statistics. To deactivate, set browser.gatherUsageStats to false.

  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501
  Network URL: http://10.0.0.xxx:8501
  External URL: http://138.2.60.xxx:8501

External URLに表示されている、「http://138.2.60.xxx:8501 」をダブルクリックします。
すると Webブラウザが立ち上がり、プロンプト入力画面になりますので、適当な審議事項(諮問)を入れてみてください。

実行結果

今回は、エヴァンゲリオンでおなじみ(?)の質問(諮問)をしてみました。

3者がそれぞれの判断で、賛成、反対、条件付き賛成を回答し、とりまとめを行って最終回答を行っています。

まとめ

今回は、OCI の Always Free を利用して。「MAGIシステム」的な仕組みを再現してみました。A2A のような完全なマルチエージェントではありませんが、意外とお手軽にこのようなアプリケーションを作成できますので、みなさんもチャレンジしてみてください!

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?