PHPでDeepSeek APIを使ってみた。

2025/02/18 (火) - 09:00 PHP&CMS

何かと話題だったDeepSeekDeepSeek APIがずっとメンテナンスで使えなかったのですが、気がついたら動くようになっていたので軽く触ってみました。

APIを使う場合は予めDeepSeek API PlatformからAPIキーの取得を済ませておく必要があります。発行時にAPIキーは一度しか表示されないので忘れずにメモしましょう。今はAPIのプロモーションが終っているので、使う場合は課金しておく必要があります(追記 2025年2月現在は、課金が停止されているので、課金したくとも出来ない模様)。

ドラクエ3のゾーマの弱点と倒し方を教えて

DeepSeekの安全性や信頼性の是非はあるものの、料金体系がGPTやClaudeより安いので個人利用で試しに使うのはアリかなと。

コード例

入力フォームから質問したら次の画面に遷移して回答を表示する流れです。以下はHTMLコード。

<form method="POST" action="">
 <label for="prompt">質問を入れてね:
 <textarea name="prompt" id="prompt" placeholder="質問を入力"><?php echo $prompt; ?></textarea></label>
 <input type="submit" value="質問する">
</form>

以下は対応するPHPのコード(エラーやセキュリティ対策は省いてます)。ほぼGPTと一緒ですね…なぜなら公式のドキュメントですらOpen AIのSDKが使えるよ、と言ってるくらいなので…これって…。

<?php
$API_KEY = '{取得したAPIキーを入れる}';
$API_URL = 'https://api.deepseek.com/chat/completions';
$prompt = htmlspecialchars($_POST['prompt'], ENT_QUOTES, 'UTF-8');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
 $data = array(
  'model' => 'deepseek-chat', //モデルを選択
  'system' => [
   ['role' => 'system', 'content' => '語尾ににゃんを付けてください']
  ],  
  'messages' => [
   ['role' => 'user', 'content' => $prompt]
  ],
  'max_tokens' => 1000
 );
 $headers = array(
  'Content-Type: application/json',
  'Authorization: Bearer ' . $API_KEY
 );
 $ch = curl_init($API_URL);
 curl_setopt($ch, CURLOPT_POST, true);
 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
 $response = curl_exec($ch);
 $httpcode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
 curl_close($ch);
 if($httpcode===200){
  $result = json_decode($response, true);
  $answer = $result['choices'][0]['message']['content'];
  echo nl2br($answer);
 } else {
  echo 'エラーが発生したにゃん:'.$response;
 }
}
?>

また、利用モデルや料金体系などは以下のドキュメント参照のこと。

ねこですよろしくおねがいします

あなたは犬と猫どちらが好きですか?

にゃん、わたしは猫の方が好きにゃ!でも犬もかわいいにゃ~。あなたはどっちが好きにゃ?

犬派か猫派かでは猫派だそうです。

PHPでClaude APIを使ってみた。

2025/02/14 (金) - 09:00 PHP&CMS

大規模言語モデルの対話型生成AI、Claude APIをPHPと連携して使ってみたので、そのメモ。

ClaudeのAPIを使う場合は、Anthropic Consoleに登録・ログイン後に[Get API keys]で予めAPIキーを取得する必要があり、それとは別にクレジットカードでも課金しておく必要もあります。

Claude API連携イメージ

今回は単純にフォームのテキストエリアから送信した文に答えるだけの簡単なテストを実装しました。HTMLは以下の通り。

<form method="post" action="">
 <textarea name="prompt" id="prompt" placeholder="メッセージを入れてにゃん"><?php echo $prompt ?></textarea>
 <input type="submit" value="送信">
</form>

PHP部分は以下の通り。POSTで受け取ったデータをClaude APIにプロンプトをリクエストし、レスポンスを画面上に表示するのみです。例として回答文は語尾に「にゃん」を付けるような条件としました。

<?php	
 define(API,'https://api.anthropic.com/v1/messages');
 define(API_KEY,'{API キーを指定}');
 $system = '語尾ににゃんをつけて可愛く回答してください。';
 $prompt = htmlspecialchars($_POST['prompt'], ENT_QUOTES, 'UTF-8');
 if($prompt && $_SERVER['REQUEST_METHOD'] === 'POST'){
  $data = json_encode([
   'model' => 'claude-3-5-haiku-20241022', //モデルを指定
   'max_tokens' => 1024,
   'system' => $system,
   'messages' => [
    ['role' => 'user', 'content' => $prompt]
   ]
  ]);
  $ch = curl_init(API);
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
  curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
   'Content-Type: application/json',
   'anthropic-version: 2023-06-01',
   'x-api-key: ' . API_KEY
  ]);		
  $result = curl_exec($ch);
  curl_close($ch);
  $output = json_decode($result, true);
  if($output['type']==='error'){
   echo 'エラーですにゃん';
  }else{
   echo nl2br($output['content'][0]['text']);
  }
 }else{
  echo 'メッセージを入れてにゃん';
 }
?>

こう見るとGPT APIと似たような設計ですね。Claudeを開発した「Anthropic(アンソロピック)」は、ChatGPTを開発したOpenAI社に元メンバーが作った企業なので、その名残なのかもしれません。今回モデルはclaude-3-5-haiku-20241022を指定しておりますが、人にのモデルに変更が可能です。詳しいことは以下のドキュメントに案内があります。

プロンプトですが、例えば以下のような文面をリクエストした場合

碇ゲンドウとゼーレが進める人類補完計画とはなんですか?
碇ゲンドウとゼーレの目的は、人類補完計画を成功させることにゃん!

碇ゲンドウは、全ての人間の意識を一つに融合させて、個人の痛みや孤独から解放し、新たな進化の形を目指すにゃん。ゲンドウは息子・碇シンジを利用して、

〜略〜

最終的には、この計画が人類の存在そのものを根本的に変えようとする壮大な試みだったにゃん!

上記のようなレスポンスが返ってくるのでフロントでは自動的にHTMLで改行するようnl2brを実行しました。

Next.js(AppRouter)でRSSフィードを読み込む

2025/02/07 (金) - 09:00 HTML

Next.js(AppRouter)上から外部のRSSを呼び出す機会があったので、調べてみました。RSSとは、ブログやニュースサイトの更新情報を配信するための仕組みで、WordPressなどのブログやNoteなど文章配信サービスで利用されています。RSSをはXML形式で配信されているのでXMLを解析できればどんな環境でも使えます。もちろんRSSはこのブログでも配信しています。

今回は以下の環境で実装しました。

  • Next.js(AppRouter) 14.3.x
  • rss-parser 3.13.x

RSSはRSS 2.0を対象とし、以下のようなXMLフォーマットを前提とします。

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
 <channel>
  <title>サイト名</title>
  <link>サイトURL</link>
  <description>サイト説明文</description>
  <item>
   <title><![CDATA[記事タイトル]]></title>
   <pubDate>公開日付</pubDate>
   <link>記事URL</link>
   <description><![CDATA[記事の要約]]></description>
  </item>
  ・
  ・
 </channel>
</rss>

まずNext.jsでRSSを扱うために予めRSSをパースするモジュールをインストールします。

$ npm i rss-parser

Next.jsのpage.jsに以下のようにコードを入れます(注意:日時処理用にdate-fnsのモジュールを使っています)。

import parse from 'rss-parser';
import { format } from 'date-fns';
async function getRssFeed(url) {
 const parser = new parse();
 try {
  const feed = await parser.parseURL(url);
  return feed.items;
 } catch (error) {
  console.error('Error:', error);
  return [];
 }
}
export default async function Home() {
 const items = await getRssFeed('{RSSフィードのURL}');
 return (
 <main>
  <ul>
  {items.map((item) => (
   <li key={item.link}>
    <a href={item.link}>
     <h3>{item.title}</h3>
     <time>{format(new Date(item.pubDate), 'yyyy年MM月dd日')}</time>
    </a>
   </li>
  ))}
  </ul>
 </main>
 );
}

実行するとこのように表示されます(デザインは個別にCSSで設定ください)。

RSSを表示した例

RSSはやはり便利ですね。

ChatGPT APIでWordPressの記事の要約を表示する

2025/02/05 (水) - 09:00 PHP&CMS

WordPressの記事をAIを使いざっくりと要約したい…という場合の簡単な実装方法です。WordPressからChatGPT APIにアクセスし、記事の要約を自動化を行いました。実装する前に予めOpenAI developer platformからAPI Keysを取得しておきましょう。

ChatGPTが要約した内容を実際に表示させた例

まずfunction.phpに、ChatGPTに記事の要約を生成させるためのプロンプトを指定しAPIにリクエストします。今回は適当に「記事を100文字で要約して」という旨のプロンプトにしました。

正常にレスポンスが返ってきたら、その要約された文面をWordPressが取得して処理する…という流れです。

define('API_URL', 'https://api.openai.com/v1/chat/completions');
define('API_KEY', '【取得したAIキーを入れる】');
function get_summary_text() {
 $post_id = get_the_ID();
 $post_summary = '';
 if( get_post_meta($post_id, 'post_summary') ) {
  $post_summary = get_post_meta($post_id, 'post_summary',true);
 } else {
  $API = curl_init(API_URL);
  $content = wp_strip_all_tags(get_the_content());
  $content = str_replace(['\n','\r','\t'], '', $content);
  $prompt_text = "次の文章を100文字で要約にしてください。\n{$content}";
  $prompt_text_short = mb_substr($prompt_text, 0, 1000);
  $header = array(
   'Authorization: Bearer '.API_KEY,
   'Content-type: application/json',
  );
  $prompt = [
   [
    'role' => 'user',
    'content' => $prompt_text_short
   ]
  ];
  $params = json_encode([
   'messages' => $prompt,
   'model' => 'gpt-3.5-turbo' //モデルの指定(gpt-3.5-turbo,gpt-4-turbo等)
  ]);
  $options = array(
   CURLOPT_POST => true,
   CURLOPT_HTTPHEADER =>$header,
   CURLOPT_POSTFIELDS => $params,
   CURLOPT_RETURNTRANSFER => true,
  );
  curl_setopt_array($API, $options);
  $httpResponse = curl_exec($API);
  $httpCode = curl_getinfo($API,CURLINFO_RESPONSE_CODE);
  if($httpCode === 200){
   $jsonArray = json_decode($httpResponse, true);
   $post_summary = nl2br($jsonArray['choices'][0]['message']['content']);
  }
  curl_close($ch);
  update_post_meta($post_id, 'post_summary', $post_summary );
 }
 return $post_summary;
}

フロント表示部分。記事詳細画面(single.php)の任意の場所に要約を表示する記述します。すぐ読めるように記事の文頭に置くと良いかと思われます。

AIによる要約:<?php echo get_summary_text(); ?>

表示はHTMLやCSSで適当に整形してください。

カスタムフィールドを使ってキャッシュ化

記事の要約が実行されるタイミングは、初めて記事にアクセスした時です。従って初回はGPTからのレスポンスを待っているので、記事が表示されるまで少し時間がかかります。要約された文面は自動でカスタムフィールドpost_summaryというフィールドに登録しキャッシュ化します。

  • カスタムフィールドに値がない場合はGPTの要約を取得し、カスタムフィールドに登録して表示する
  • カスタムフィールドに値がある場合はカスタムフィールドの要約を取得して表示する

つまり、2回以降アクセスした際は自動登録されたカスタムフィールドの要約文を取得します。このときはAPIにリクエストしないので、過剰なAPIへのリクエストやパフォーマンスの低下、課金を抑えることが出来ます。

要約した文章はカスタムフィールドに登録される

もし要約の文章を少し変えたい…といった場合はWordPressの編集画面からカスタムフィールドの値を変更してもいいですし、値を削除してしまえば、再度GPTが記事を要約する命令が実行されます。

Let’s Encrypt(無償SSL)のEメールによる有効期限通知終了(SSLUpdates on Let’s Encrypt Subscriber Agreement & Ending Expiration Notification)

2025/01/31 (金) - 12:00 Server

以下のような表題のメールが来ました。

SSLUpdates on Let’s Encrypt Subscriber Agreement & Ending Expiration Notification

Let’s Encryptではメールアドレスを登録した会員に向けてEメールによる有効期限通知のサービスを提供していましたが、こちらが2025年6月4日に終了するとのことです。

引き続き、メールによる更新通知を受け取りたい場合はサードパーティの監視サービス Red Sift Certificates Lite を利用するなど検討してください。こちらはLet’s Encrypt が推奨しているサービスになります。

WebサーバーでLet’s Encrypt(certbot)のSSL更新を自動化する

Let’s Encrypt(certbot)の更新作業はサーバー側で自動化することもできます。サーバーにログイン後、以下のコマンドを実行すると、certbotのSSL更新のデモンストレーションを行えます。以下で正常(Success)に更新できることっ確認しておきましょう。

$ certbot renew --dry-run

certbotの更新を自動更新する場合はcrontabで定期実行すると便利です。例えば毎月1日の午前2時に更新する場合は以下のように指定します。certbotの更新実行後はwebサーバーの再起動が必要なのでそれも実行します(CentOS7 nginxの例)

$ crontab -u root -e
00 02 01 * * certbot renew && systemctl restart nginx

有償のSSLを契約している場合は定期的な更新手続や支払いが必要で。契約しているサービスからお知らせが来るので忘れづらいですが、レンタルサーバーでLet’s Encryptを使っている場合や、AWSのACM(Certificate Manager)で自動更新に慣れているとふと失念してしまうことがあるので注意しましょう。

Reactで簡易的なカレンダーを作成

2025/01/31 (金) - 09:00 JavaScript

Reactを使用して簡易的なカレンダーを作成しました。デフォルトでは今月のカレンダーを表示、前月と次月のボタンを押下することで月表示を切り替えることができます。実装環境は以下の通り。

  • React 19.0.x
  • Vite 5.x

カレンダーのイメージ

予めプロジェクトを立ち上げておき、App.jsxに以下のように記述します。前月、次月のボタンを押下した際にuseStateでカレンダーの表示を切り替える仕組みです。

import { useState } from 'react'
import './App.css'
const App = () => {
 const [currentDate, setCurrentDate] = useState(new Date());
 const getDaysInMonth = (year, month) => {
  return new Date(year, month + 1, 0).getDate();
 };
 const getFirstDayOfMonth = (year, month) => {
  return new Date(year, month, 1).getDay();
 };
 const renderCalendar = () => {
  const year = currentDate.getFullYear();
  const month = currentDate.getMonth();
  const daysInMonth = getDaysInMonth(year, month);
  const firstDayOfMonth = getFirstDayOfMonth(year, month);
  const blanks = Array(firstDayOfMonth).fill(null);
  const daysArray = Array.from({ length: daysInMonth }, (_, i) => i + 1);
  const allDays = [...blanks, ...daysArray];
  const totalCells = Math.ceil(allDays.length / 7) * 7;
  const remainingBlanks = totalCells - allDays.length;
  allDays.push(...Array(remainingBlanks).fill(null));
  const weeks = [];
  let week = [];
  allDays.forEach((day, index) => {
   week.push(day);
   if ((index + 1) % 7 === 0) {
    weeks.push(week);
    week = [];
   }
  });
  return weeks.map((week, weekIndex) => (
   <tr key={weekIndex}>
    {week.map((day, dayIndex) => {
     const dayOfWeek = (firstDayOfMonth + dayIndex) % 7;
     return (
      <td key={dayIndex}>
       {day || ''}
      </td>
     );
    })}
   </tr>
  ));
 };
 const goToPreviousMonth = () => {
  setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
 };
 const goToNextMonth = () => {
  setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
 };
 return (
  <div className="calendar">
   <div className="calendar-header">
    <button onClick={goToPreviousMonth}>前の月</button>
    <h2>
     <span className="calendar-year">{currentDate.getFullYear()}年</span>
     <span className="calendar-month">{currentDate.toLocaleString('default', { month: 'long' })}</span>
    </h2>
    <button onClick={goToNextMonth}>次の月</button>
   </div>
   <table>
    <thead>
     <tr>
      <th>日</th>
      <th>月</th>
      <th>火</th>
      <th>水</th>
      <th>木</th>
      <th>金</th>
      <th>土</th>
     </tr>
    </thead>
    <tbody>{renderCalendar()}</tbody>
   </table>
  </div>
 );
};
export default App;

市販の一般的なカレンダーに習い、日曜日を開始としてtd要素で並べます。今回日曜日を赤文字・土曜日を青文字としたかったので、こちらはCSSのセレクタを用いて色変更を行いました。

table{
 width: 100%;
 border-collapse: collapse;
 table-layout: fixed;
}
th,
td{
 padding: 10px;
 border: 1px solid #ccc;
 text-align: center;
 vertical-align: middle;
}
th:first-child,
td:first-child{
 color: red;
}
th:last-child,
td:last-child{
 color: blue;
}

細かいスタイルの反映については個別に適宜実装ください。

Grek,Gemini,Claude,GPT,DeepSeekの回答を比較してみた(物語のあらすじ編)

2025/01/24 (金) - 09:00 Others

大規模言語モデル(LLM)を用いた対話型AIや文章生成AIが群雄割拠の時代です。今回はとある作品のあらすじを聞いて比較してみました。なおプロンプトは、“初代『機動戦士ガンダム』(通称:ファーストガンダム)のあらすじを教えて下さい。”と、簡単なものとしました。

以下に回答文を羅列します。

Grek

『機動戦士ガンダム』(ファーストガンダム)は、1979年から1980年にかけて放送されたアニメシリーズで、リチャード・ガンダムの世界観を基にしたストーリーです。以下にそのあらすじを解説します。

あらすじ

時代背景:

宇宙世紀(UC)0079年。人類は地球上だけでなく、宇宙空間にもコロニーと呼ばれる人工衛星都市を建設し、生活していました。しかし、その中でも特にジオン公国と地球連邦政府との間で対立が激化し、戦争が勃発します。これが「一年戦争」と呼ばれることになる戦闘の始まりです。

物語の概要:

主人公アムロ・レイは、地球連邦軍の新兵で、偶然にも新型のモビルスーツ「ガンダム」に乗り込むことになります。ガンダムは、ジオン公国の開発したモビルスーツに対抗するために作られた最新鋭の兵器でした。

アムロとシャアの対決:

アムロは、ジオン公国のエースパイロットであるシャア・アズナブルと何度も戦闘を繰り広げます。シャアは、冷酷かつ戦略的な戦闘スタイルで知られ、アムロにとって最大のライバルとなります。

白兵戦と精神的成長:

アムロは戦闘を通じて、ニュータイプ(次世代の人類)としての能力を発揮し始めます。これは、他人の思考を感じ取る能力や、モビルスーツを超人的に操る力などを含みます。しかし、その一方で、彼は戦争の非情さや仲間との別れ、そして心の傷を深く受けます。

戦争の終結:

一年戦争は、ジオン公国の首都ソロモンで最終決戦が行われ、地球連邦軍が勝利します。しかし、戦争の結果は、勝利者も敗者も大きな犠牲を払ったことを示しています。アムロはシャアとの決戦に勝利しつつも、彼自身も深い傷を負います。

エピローグ:

戦争の終わりと共に、アムロは新たな未来を見据えつつも、過去の影を引きずりながらも生きていくことを選択します。物語は、彼の成長と戦争の悲惨さを描きつつ、次の世代への希望も示唆しています。

このシリーズは、戦争の無意味さや人間の生存と成長を描いた深いテーマを持ちつつ、魅力的なキャラクターとメカニックの戦闘シーンで知られています。

Gemini 1.0 Pro

宇宙世紀0079年、人類は宇宙へと進出し、地球連邦政府がその大半を統治していました。しかし、宇宙に移民した人々の不満が爆発し、ジオン公国が独立戦争を挑みます。

物語は、連邦軍が開発した新型モビルスーツ「ガンダム」を巡って展開されます。ジオン軍の奇襲により、ガンダムの開発拠点であるサイド7が攻撃され、主人公のアムロ・レイは偶然ガンダムに乗り込みます。

ガンダムの圧倒的な力

ガンダムは、ジオン軍の主力兵器であるモビルスーツ「ザク」をはるかに凌ぐ性能を持っていました。アムロは、持ち前の才能とガンダムの力によって、ジオン軍を相手に奮闘します。

ホワイトベース隊の誕生

ガンダムとアムロは、連邦軍の艦船「ホワイトベース」に搭載され、宇宙を転戦します。ホワイトベースには、アムロをはじめ、カイ・シデン、セイラ・マスといった若きパイロットたちが集まり、ジオン軍との戦いを繰り広げます。

ニュータイプの出現

物語が進むにつれて、「ニュータイプ」と呼ばれる特殊な能力を持つ人々の存在が明らかになります。ニュータイプは、通常の感覚を超えた能力を持ち、モビルスーツの操縦に大きな影響を与えます。アムロやシャア・アズナブルもまた、ニュータイプの一人であり、その能力を駆使して戦います。

戦争の悲劇と成長

ホワイトベース隊は、ジオン軍との戦いを経て、数々の苦難を乗り越えます。戦争の残酷さ、仲間の死、そして成長を繰り返しながら、彼らは自分たちの役割に気づき、人類の未来のために戦う決意を固めます。

最終決戦と新たな時代

物語は、ジオン軍の拠点であるア・バオア・クーでの最終決戦へと向かいます。ガンダムとシャアの赤い彗星との戦いは、多くの犠牲を伴いながらも、ついに終結を迎えます。

ファーストガンダムは、単なるロボットアニメにとどまらず、戦争の悲劇、人間の成長、そして未来への希望を描いた壮大な物語です。

キーワード

  • モビルスーツ:人型機動兵器
  • ジオン公国:独立を求める勢力
  • ニュータイプ:特殊な能力を持つ人類
  • ホワイトベース:ガンダムが搭載された艦船
  • シャア・アズナブル:ジオン軍のエースパイロット、赤い彗星

より深く知りたい方へ

ファーストガンダムは、その後のガンダムシリーズの礎を築いた作品です。キャラクターやメカニック、世界観など、魅力的な要素がたくさんあります。

もし興味があれば、ぜひアニメや漫画、小説など、さまざまな形で作品に触れてみてください。

何かもっと知りたいことがあれば、お気軽にご質問ください。

  • 特定のキャラクターについて詳しく知りたい
  • モビルスーツの種類や特徴を知りたい
  • ガンダムシリーズの歴史を知りたい
  • 物語の結末について詳しく知りたい

など、どんな質問でも構いません。

Claude 3.5

1979年に放送された「機動戦士ガンダム」の物語の主なあらすじをご説明します。

物語は宇宙世紀0079年を舞台とし、地球連邦とジオン公国による一年戦争の最中に展開されます。主人公のアムロ・レイは、スペースコロニー「サイド7」に住む15歳の少年です。

ある日、サイド7がジオン軍に襲撃された際、アムロは偶然にも地球連邦軍の新型モビルスーツ「ガンダム」のコックピットに乗り込みます。彼は天性の才能を発揮し、ガンダムを操縦して敵と戦います。

その後、アムロは避難民たちとともに連邦軍の戦艦「ホワイトベース」に乗船することになります。彼らは地球に向かう過程で、シャア・アズナブルをはじめとするジオン軍の追撃を受けながら、数々の戦いを経験します。

物語は単なる戦争物語ではなく、戦争の悲惨さや人間の成長、そして理想と現実の狭間での葛藤などのテーマを深く描いています。アムロは戦いを通じて成長していくと同時に、戦争がもたらす悲しみや苦しみにも直面していきます。

最終的には、地球連邦軍とジオン公国の決戦が宇宙要塞「ア・バオア・クー」で行われ、壮絶な戦いが繰り広げられます。

この作品は、それまでのロボットアニメの常識を覆し、より現実的な戦争描写とヒューマンドラマを展開したことで、アニメ史に大きな影響を与えました。

Chat GPT 4o

『機動戦士ガンダム』(通称「ファーストガンダム」)は、宇宙世紀0079年を舞台に、地球連邦と独立を宣言したジオン公国との間で繰り広げられる「一年戦争」を描いた作品です。

物語は、地球連邦が開発した新型モビルスーツ「ガンダム」を搭載した戦艦ホワイトベースが、サイド7のコロニーでジオン軍の襲撃を受ける場面から始まります。

民間人であった少年アムロ・レイは、偶然にもガンダムを操縦し、敵の攻撃を退けます。

その後、彼はホワイトベースのクルーとして戦いに身を投じ、成長していきます。

物語の中で、アムロはジオン軍のエースパイロットであるシャア・アズナブルと幾度も対峙します。

また、ニュータイプと呼ばれる新たな人類の可能性が示唆され、アムロ自身もその能力に目覚めていきます。

物語は、地球上での戦闘から宇宙へと舞台を移し、最終的にはジオン公国の本拠地であるア・バオア・クーでの決戦へと至ります。

アムロとシャアの因縁、そして人類の新たな可能性であるニュータイプの存在を通じて、戦争の悲惨さや人間の成長が描かれています。

この作品は、リアルな戦争描写や人間ドラマ、複雑なキャラクター設定などで高い評価を受け、多くの続編や関連作品が制作されるきっかけとなりました。

さらに詳しい解説や映像での紹介をお求めであれば、以下の動画も参考になるでしょう。

DeepSeek R1

初代「機動戦士ガンダム」(1979年放送)は、宇宙世紀(U.C.)0079年を舞台にした戦争ドラマで、後のロボットアニメの礎を築いた作品です。以下に簡潔なあらすじをまとめます。

【物語の背景】

人類が宇宙へ進出した「宇宙世紀」、地球連邦政府と宇宙コロニー側の独立勢力「ジオン公国」の間で「一年戦争」が勃発。ジオン軍は新型兵器「モビルスーツ(MS)」を投入し、戦争は苛烈化していました。

【あらすじ】

中立コロニー「サイド7」に住む少年アムロ・レイは、ジオン軍の奇襲攻撃に巻き込まれます。その際、偶然にも連邦軍の試作モビルスーツ「ガンダム」に乗り込み、撃退に成功。この出来事をきっかけに、アムロは地球連邦軍の宇宙艦「ホワイトベース」に乗組員として参加します。

ホワイトベースは、ガンダムや他のMSを運用しつつ、地球連邦の拠点である「ジャブロー」を目指して戦いながら撤退を続けます。アムロは戦いの中で「ニュータイプ」と呼ばれる特殊な能力に目覚め、成長していきます。

一方、ジオン軍のエースパイロット「シャア・アズナブル」(赤い彗星)は、ガンダムとホワイトベースを執拗に追撃。シャアはジオン公国の支配層「ザビ家」への復讐を企てるなど、複雑な思惑を抱えていました。

物語は、アムロとホワイトベースの仲間たち(ブライト艦長、カイ・シデン、フラウ・ボゥら)が数々の戦闘を経て絆を深め、最終的にジオンの要塞「ア・バオア・クー」での決戦に臨む様子を描きます。戦争の終結と共に、アムロはニュータイプとしての可能性を示しつつ、深い喪失感と共に物語は幕を閉じます。

【テーマと特徴】

戦争の現実: 英雄視点ではなく、市井の少年が戦争に巻き込まれる過程や、戦争の悲惨さをリアルに描写。

人間の成長: アムロの精神的成長や、仲間との関係性の変化に焦点。

ニュータイプ: 宇宙時代の新たな人類像としての思想や可能性が探られる。

政治的駆け引き: ジオン公国内の権力闘争や地球連邦の腐敗も背景に。

【影響】

「ガンダム」は「ロボットアニメ」の概念を変え、リアルなメカデザインや重厚なストーリーで社会現象に。続編や派生作品が多数生まれ、現在もシリーズが続く「ガンダム宇宙世紀」の原点となりました。

さらに詳しいポイントがあれば、ぜひお聞かせください!

まとめ

基本的にはどの解説もほぼ同じで、物語の始まりから終わりまで忠実に解説し、世界観や作品のモチーフ、ファーストが人気である理由などほぼ正確に解説されていますが、プラットフォーム毎に以下のような特徴がありました。

Grek

それぞれ項目ごとに細かく説明されており、ファーストの流れや世界観や人気の理由など要点を抑えて細かくまとめています。物語の特徴を分けて解説してくれているのは便利です。ただ最終決戦は宇宙要塞のア・バオア・クーだった気もしますが。

Gemini

Grek同様に項目ごとに説明されており、さらに作品の詳細(モビルスーツや人物の詳細)を聞くように誘導する流れになっています。AIアシスタントとしてはこの流れは非常に親切設計です。

Claude

作品全体の流れと世界観、人気の理由などを簡潔にまとめています。短いですがまとめとしては非常にわかりやすいです。簡単に概要だけ知りたい場合はこのほうがわかりやすいです。

Chat GPT

Claudeのように完結に作品の流れを解説しています。また参考文献や情報ソースへの誘導も行われました。

DeepSeek

Geminiのように各分類ごとに解説され、物語の流れや背景、世に与えた影響など詳しいことも把握できるようになっています。最後には続けてプロンプトを打ち込ませるように誘導するので親切です。

以上を見ると同じプロンプトでも、物語のあらすじだけを簡潔に説明するものから作品の特徴や人気の秘密などについて補足されるものまで様々で、一長一短といったところです。さて、何故ファーストガンダムのあらすじ?最近とある理由でまた機動戦士ガンダムをサブスク配信で見返し始めたので…。いずれはPythonやJavaなどコード生成の比較してみようと思います。

JavaScriptでスマホの加速度センサーを使用する

2025/01/17 (金) - 09:00 JavaScript

スマホのWebブラウザで加速度センサーを利用し、端末の向きや傾きでゲームを作ったり、ちょっとしたギミックを仕掛けることができます。スマホの加速度を利用するにはDeviceMotionEventで発火でき、傾きや方向などをX軸・Y軸・Z軸で取得できます。詳しくは以下のドキュメントにて。

ただし、iOS(iPhone)ではちょっと細工が必要だったのでメモです。HTMLは以下の通り。

<button id="btn_flg">加速度センサーを許可する</button>
<p id="txt_position"></p>

JavaScript部分は以下の通り。

const btn_flg = document.getElementById('btn_flg'); //iOS専用許可ボタン
const txt_position = document.getElementById('txt_position'); //座標表示用
let x,y,z;
const isMotionEvent = () => {
 DeviceMotionEvent.requestPermission().then((res) => {
  if(res === 'granted'){
   //許可した時
   alert('スマホを動かしてね');
  }else{
   //許可しなかった時
   alert('🥺');
  }
 }).catch((e) => {
  alert(e);
 });
}
const motionEvent = (e) => {
  x = Math.floor(e.accelerationIncludingGravity.x);
  y = Math.floor(e.accelerationIncludingGravity.y);
  z = Math.floor(e.accelerationIncludingGravity.z);
  //それぞれの座標を表示させる
  txt_position.textContent=`X:${x} Y:${y} Z:${z}`;
}
if(window.DeviceMotionEvent){
 if(DeviceMotionEvent.requestPermission){
  //iOS専用 センサー取得の許可ボタン
  btn_flg.addEventListener('click',() => {
   isMotionEvent();
  });
 }else{
  btn_flg.style.display = 'none';
 }
 window.addEventListener('devicemotion', motionEvent);
}

iOS13以降はユーザの許可無く加速度センサーの利用ができず、予めセンサーの利用の承諾をユーザに得なくてはなりません。その許可を行うダイアログを表示させるためにボタンを“タップさせて”、端末のセンサーの利用を許可してもらいます。この時使う関数がDeviceMotionEvent.requestPermissionの部分です。必ずユーザーがタップしてダイアログを表示させる必要があります。

DeviceMotionEvent.requestPermission().then((res) => {
 if(res === 'granted'){
  //許可した時
 }else{
  //許可しなかった時
 }
}).catch((e) => {
 //エラー
 console.log(e);
});

現状のAndroidOSのブラウザではそのまま加速度センサーが使えました。以下は加速度センサーのサンプル。

スマホを降るとプログレスバーのゲージが貯まり、ずんだもんの顔が赤くなります。なお、先述のようにiOSでは先に[Shake Hand!]のボタンを押下してください。2025年現在で最新のiOSのSafari及びAndroidのGoogleChromeで確認済み。

Next.js(App Router)でBASIC認証をかける

2025/01/09 (木) - 09:00 JavaScriptServer

Next.jsのApp RouterでBASIC認証(基本認証)をかける場合は、middlewareという機能を使用します。これを使うとクライアントとサーバーの中間の処理制御を行えます。今回は以下の環境にて実装テスト。

  • Next.js v14.xx

middlewareを使用するにはmiddleware.tsというファイルを作成し処理を記述します。このmiddlewareはクライアントからのリクエストに対し様々なレスポンス処理をするものです。例えばアクセス認証やリダイレクトなどを行うことができ、nginx.confや.htaccessみたいな事ができます。ただしNext.jsでは現時点でmiddlewareを1つしか配置できませんのでsrcディレクトリがあれば、src/middleware.tsに配置するようにします。

middleware.tsでBASIC認証をかける

middleware.tsに以下のように記述しました。

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

const username = process.env.BASIC_AUTH_USER;
const password = process.env.BASIC_AUTH_PASS;

export function middleware(request: NextRequest) {
 const authHeader = request.headers.get('authorization');
 if (authHeader) {
  const encodedCredentials = authHeader.split(' ')[1];
  const decodedCredentials = atob(encodedCredentials);
  const [reqUsername, reqPassword] = decodedCredentials.split(':');
  if (reqUsername === username && reqPassword === password) {
   return NextResponse.next();
  }
 }
 const response = new NextResponse('Authentication required', { status: 401 });
 response.headers.set('WWW-Authenticate', 'Basic realm="Secure Area"');
 return response;
}

export const config = {
 matcher: '/((?!api|_next/static|favicon.ico|sitemap.xml|robots.txt).*)'
};

config.matcherにマッチングルールを指定します。上記の例だとfavicon.icoや画像などの静的ファイルは認証から除外します。

そして、認証に必要なユーザ名とパスワードは安全のため.envファイルに記述します。

BASIC_AUTH_USER=user
BASIC_AUTH_PASS=password

認証がうまく行ったらアクセスしたコンテンツを表示し、認証に失敗したら401ステータスを返します。設定後Next.jsを再起動すると動くと思います。

メディアクエリとコンテナクエリの使い分け

2025/01/06 (月) - 09:00 CSS

レスポンシブデザインで必須となるメディアクエリ(Media Query)とコンテナクエリ(Container Query)。どちらを採用すべきなのか迷うことがありましたので、目的や使用シーンを整理しました。

メディアクエリ(Media Query)とは?

メディアクエリは、主にビューポート(viewport)のサイズに基づいてスタイルを変更するために使用されます。デバイスの画面幅や高さに応じて、異なるCSSスタイルを適用することができます。

ブレークポイントを設定することで、ブラウザの画面幅のピクセル数や、デバイスの向き(縦向き、横向き)などでCSSスタイルを切り替えることが出来ます。

@media screen and (max-width: 768px) {
 body {
  color: red;
 }
}

上記では、ブラウザの表示領域が768px以下だったら、文字色が赤くなります。

コンテナクエリ(Container Query) とは?

コンテナクエリは、親要素(コンテナ)のサイズに基づいてスタイルを変更するための技術です。特定の要素がどのようなサイズに収まるかによって、子要素のスタイルを動的に変更することができます。

<div class="parent">
 <p class="child">子供要素</p>
</div>

コンテナクエリを実現するには必ず親となる要素と、スタイルを適用する子要素が必要です。親要素のセレクタにはcontainer-typeプロパティを指定し、値にはinline-sizesizeのいずれかの値を指定をします。

  • normal : デフォルト値(解除)
  • inline-size : インライン軸(横幅)
  • size : インライン軸とブロック軸(縦横幅)
.parent {
 container-type: inline-size;
}
@container (max-width: 480px) {
 .child {
  color: red;
 }
}

子要素のセレクタには幅指定とスタイルを指定します。上記のコードでは親要素の幅が480px以下の場合、子要素の文字色が赤くなります。

コンテナクエリとメディアクエリの使いどころ

メディアクエリ

ページ全体のレイアウト制御に使うことが多いです。例えば画面幅によってがらっとレイアウトが変わるような制御や、スマホやタブレットの向きでレイアウトを変更する場合は、メディアクエリが最適です。

コンテナクエリ

特定の要素のサイズ変更に応じて細かいデザイン制御したい場合は、コンテナクエリが最適です。例えばナビゲーションやフォームなど、デザインパーツやコンポーネント単位で細かいデザインを制御したい要望ではコンテナクエリが最適です。

コンテナクエリのブラウザの対応状況は、Chromeは105以降、Safariは16以降、Firefoxは110以降で利用でき2025年1月現在の最新ブラウザでは利用することが可能ですので、今後積極的に使うケースは多くなると思います。

ページの先頭へ