こんばんは。
最近はどこに行っても生成AIの話題ばかり・・。自分は全然ついていけていないのですが、それでも社内に導入されたAIツールやchatgptを使ってみれば、その便利さ、既存の生活を覆すような能力の高さには驚かされます。
新しい技術には抵抗感を持ってしまいがちですが、これまで挫折したプログラミングや技術の学習にはかなり大きな力を発揮してくれる事も実感しています。
今回は、chatgptに面倒な設定を助けてもらいながら、AIツールを組み込んだWebアプリを作ってみました。
0.作った経緯
・AIを触ってみて、そのすごさを実感したいと思ったこと。
・職場のミーティングでも意見が一切でない課題があり、本人を特定できないコメント投稿サイトを作ってみたらどうかな、と思ったこと。
1.作ったもの
簡素なサイトですが、こんな画面です。
ここにテキストを入力すると、それが建設的な意見か、批判的な意見かを判断し、入力した文章を整形して入力者の文章のクセや感情を抑えるような文章を出力します。
こんな感じ。
2.実装
全体像は以下の通りとなっています。
(1).php
Web入力フォームと、csvファイルとの入出力を記述します。
<?php
/***** Dify設定*****/
const DIFY_API_URL = 'https://api.dify.xxxxxxxxxxxxxxxxxx'; // 例: https://api.dify.ai/v1/chat-messages
const DIFY_API_KEY = 'xxxxxxxxxxxxxxxxxxxx'; // DifyのアプリAPIキー(Server側キー)
$csv = __DIR__ . '/comment.csv';
$flash_msg = null; // 結果メッセージ表示用
// ▼ CSVクリア要求(バックアップを作ってから中身を空にする)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'clear') {
if (is_file($csv)) {
// バックアップ作成
$backup = __DIR__ . '/comment_' . date('Ymd_His') . '.bak.csv';
if (!@copy($csv, $backup)) {
$flash_msg = 'バックアップの作成に失敗しました。パーミッションを確認してください。';
} else {
// 中身を空に(truncate)
if (($fp = @fopen($csv, 'w')) !== false) {
if (flock($fp, LOCK_EX)) {
ftruncate($fp, 0);
fflush($fp);
flock($fp, LOCK_UN);
}
fclose($fp);
$flash_msg = 'CSVをクリアしました(バックアップ: ' . htmlspecialchars(basename($backup), ENT_QUOTES, 'UTF-8') . ')。';
} else {
$flash_msg = 'CSVを書き込み用に開けませんでした。';
}
}
} else {
$flash_msg = 'CSVファイルが見つかりませんでした。';
}
}
function dify_chat_with_debug($query, $inputs = []) {
$payload = [
'inputs' => (object)$inputs,
'query' => $query,
'response_mode' => 'blocking',
'user' => 'user-' . session_id(),
];
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => DIFY_API_URL,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . DIFY_API_KEY,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 20,
]);
$body = curl_exec($ch);
$errno = curl_errno($ch);
$err = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// デバッグ情報をまとめる
$debug = "HTTP=$code\ncURL err=($errno) $err\nBODY:\n$body";
if ($errno) return [null, "cURL error: ($errno) $err", $debug];
if ($code < 200 || $code >= 300) return [null, "HTTP $code", $debug];
$json = json_decode($body, true);
if ($json === null) return [null, "JSON decode error", $debug];
$answer = $json['answer']
?? ($json['output_text'] ?? null)
?? ($json['data']['answer'] ?? null)
?? ($json['data']['output_text'] ?? null);
return $answer !== null
? [$answer, null, $debug]
: [null, "Unexpected response shape", $debug];
}
session_start();
/***** Dify呼び出し関数(blocking) *****/
function dify_chat($query, $inputs = []) {
$payload = [
'inputs' => (object)$inputs,
'query' => $query,
'response_mode' => 'blocking', // ストリーミングしない
'user' => 'user-' . session_id(),
];
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => DIFY_API_URL,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . DIFY_API_KEY,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 20,
]);
$body = curl_exec($ch);
$errno = curl_errno($ch);
$err = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errno) return [null, "cURL error: ($errno) $err"];
if ($code < 200 || $code >= 300) return [null, "HTTP $code: " . $body];
$json = json_decode($body, true);
if ($json === null) return [null, "JSON decode error. Raw: " . $body];
$answer = $json['answer']
?? ($json['output_text'] ?? null)
?? ($json['data']['answer'] ?? null)
?? ($json['data']['output_text'] ?? null);
return $answer !== null ? [$answer, null]
: [null, "Unexpected response shape: " . json_encode($json, JSON_UNESCAPED_UNICODE)];
}
//デバッグsS
$dify_result = null;
$dify_error = null;
$dify_debug = null;
//デバッグ
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['cap'])) {
$cap = $_POST['cap'];
// 改行を <br> に変換
$capwk = isset($_POST['cap']) ? $_POST['cap'] : ''; // ← 改行の変換やめる
// CSVファイルを追記モードで開く
$fp = fopen('comment.csv', 'a');
// 各項目を配列にしてまとめる(順番は元コードと同じ)
$data = [
$_POST['cap'],
];
// CSV形式で1行書き込む(自動エスケープ)
fputcsv($fp, [$dify_result]);
//fputcsv($fp, $data);
// ファイルを閉じる
//fclose($fp);
//echo "CSVに書き込みました。koredesu";
list($dify_result, $dify_error, $dify_debug) = dify_chat_with_debug($cap, []);
fputcsv($fp, [$dify_result]);
fclose($fp);
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style_c3-14-12.css">
</head>
<body>
<div class="cap">匿名意見交換システム</div><br>
<img src="kao.png" width=100px><br>
個人が特定できないよう、あなたのコメントを加工します。<br>
安心して意見を投稿しましょう。<br>
<!--
<iframe
src="https://udify.app/chatbot/G6WNzLs84OXoPjk4"
style="width: 500px; height: 100%; min-height: 700px"
frameborder="0"
allow="microphone">
</iframe>
-->
<br>
<hr>
<form name="form1" action="toko2.php" method="post">
<textarea type="text" name="cap" cols="100" rows="5"></textarea><br>
<input type="submit" value="追加"></form>
<form method="post" action="" onsubmit="return confirm('本当にCSVを空にしますか?この操作は元に戻せません。');" style="margin-top:8px;">
<input type="hidden" name="action" value="clear">
<button type="submit">CSVをクリア</button>
</form>
<!--デバッグ-->
<!--
<?php if ($dify_result !== null): ?>
<div class="box">
<strong>Dify 応答</strong>
<div><?= nl2br(htmlspecialchars($dify_result, ENT_QUOTES, 'UTF-8')) ?></div>
</div>
<?php elseif ($dify_error !== null): ?>
<div class="box err">
<strong>Dify エラー</strong>
<div><?= nl2br(htmlspecialchars($dify_error, ENT_QUOTES, 'UTF-8')) ?></div>
<pre><?= htmlspecialchars($dify_debug, ENT_QUOTES, 'UTF-8') ?></pre>
</div>
<?php endif; ?>
-->
<!--デバッグ-->
<?php
$fp = fopen('comment.csv', 'r');
while (($data = fgetcsv($fp)) !== false) {
echo '<div class="cap">';
// $data は配列で、CSVの1行を各要素に分解してくれている
list($cap) = $data;
echo nl2br(htmlspecialchars($cap, ENT_QUOTES, 'UTF-8')) . '';
echo '</div>';
}
?>
<?php
$rows = [];
$fp = fopen('comment.csv', 'r');
while (($data = fgetcsv($fp)) !== false) {
$rows[] = $data;
}
fclose($fp);
?>
</body>
</html>
こんなプロンプトをDifyに登録します。
・あなたは匿名性の意見投稿サイトを作るためのツールです。
・入力された文章を、個人の癖が出ないような文章に置き換えてください。
・誤字脱字を訂正する。
・ビックリマークやはてなマークなど、感情を表現する文字を別の文字に置き換える
・過激な言葉をソフトにする。
・批判的な言葉であれば、先頭に「(批判)」とつけてください。それ以外の意見は「(意見)」と先頭につけてください。
・語尾に、「にゃん」をつけて猫っぽくする。
語尾のにゃんは、子供が喜ぶのでそうしただけです(照)。
3.動かす。
「私は、残業時間が多いことより、業務時間が長いことのほうが気になる。一体管理職は何をしているのか?」という入力をすると、以下の通りになります。
「(批判)私は、高い残業時間よりも、長い業務時間の方が問題だと感じているにゃん。具体的に管理職がどのような業務を遂行しているのか詳しく知りたいにゃん。」
ここまで加工すれば、個人の文章のクセはだいぶわからなくなり、匿名効果はありそうです。
4.使ってみた結果(フィードバック)
目新しさから、好意的なコメントはもらえましたが、実用性には課題がありそうです。
・(一言だけ)おもしろい。
・好きな言葉を入力したところ、かなり違う結果が返ってきた。
・匿名投稿機能としては使えそうだが、どういうタイミングでチームに展開してつかってもらうか?
意見収集の場で活用して、改善を図ってみようと思います。