※利用規約にご同意の上ご利用ください。
※候補者の選挙公報を学習したAIがあなた質問にお答えします。
※AIは事実と異なる事を言うこともあります。必ず自身で情報を確かめて下さい。
※ボートマッチの仕組みと使い方はコチラ
※システムのソースコードはコチラ
※選挙公報は安芸高田市選挙管理委員会HPから引用(予定)
学習済み候補者一覧
※届け出順(敬称略)
届出番号 | 候補者名 | 党派 |
1 | 藤本えつし | 無所属 |
2 | あかつ誠一郎 | 無所属 |
3 | くまたか昌三 | 無所属 |
4 | 森谷まさあき | 「浜田市職員飲酒運転」をもみ消した久保田市長を裁く党 |
ソースコード
“use client”;
import React from “react”;
function MainComponent() {
const [userInput, setUserInput] = React.useState(“”);
const [chatHistory, setChatHistory] = React.useState([]);
const [isLoading, setIsLoading] = React.useState(false);
const [conversationContext, setConversationContext] = React.useState(“”);
const handleInputChange = (e) => {
setUserInput(e.target.value);
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!userInput.trim()) return;
setIsLoading(true);
const newUserMessage = { role: “user”, content: userInput };
setChatHistory((prev) => […prev, newUserMessage]);
try {
const candidatesResponse = await fetch(“/api/db/2024-39”, {
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({
query: “SELECT * FROM `akitakata_mayor_election_2024`”,
}),
});
const candidatesData = await candidatesResponse.json();
const conversationHistoryResponse = await fetch(“/api/db/2024-187”, {
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({
query:
“SELECT * FROM `conversation_history` ORDER BY `serial_number` DESC LIMIT 5”,
}),
});
const conversationHistoryData = await conversationHistoryResponse.json();
const systemMessage = {
role: “system”,
content: `安芸高田市長選挙2024の候補者情報について回答してください。候補者の名前の後には「候補」とつけてください。回答は読みやすいように改行を入れてください。以下は候補者の情報です:\n${JSON.stringify(
candidatesData
)}\n\n過去の会話履歴:\n${JSON.stringify(
conversationHistoryData
)}\n\n前回の質問:${conversationContext}`,
};
const response = await fetch(
“/integrations/anthropic-claude-sonnet-3-5/”,
{
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({
messages: [systemMessage, newUserMessage],
}),
}
);
const data = await response.json();
const assistantResponse = data.choices[0].message.content;
setChatHistory((prev) => [
…prev,
{ role: “assistant”, content: assistantResponse },
]);
const now = new Date().toISOString();
const serialNumberResponse = await fetch(“/api/db/2024-187”, {
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({
query:
“SELECT MAX(`serial_number`) as max_serial FROM `conversation_history`”,
}),
});
const serialNumberData = await serialNumberResponse.json();
const newSerialNumber = (serialNumberData[0].max_serial || 0) + 1;
await fetch(“/api/db/2024-187”, {
method: “POST”,
headers: { “Content-Type”: “application/json” },
body: JSON.stringify({
query:
“INSERT INTO `conversation_history` (`timestamp`, `serial_number`, `user_question`, `assistant_response`) VALUES (?, ?, ?, ?)”,
values: [now, newSerialNumber, userInput, assistantResponse],
}),
});
setConversationContext(userInput);
setUserInput(“”);
} catch (error) {
console.error(“Error:”, error);
} finally {
setIsLoading(false);
}
};
return (
<div className=”flex flex-col h-screen bg-gradient-to-r from-blue-100 to-purple-100″>
<div className=”flex items-center justify-between p-4 bg-gradient-to-r from-blue-100 to-purple-100 shadow-md h-[63px]”>
<h1 className=”text-2xl font-bold text-indigo-800 font-noto-sans text-[15px]”>
AIボートマッチ丨安芸高田市長選挙2024
</h1>
<a
href=”https://demotech.jp/”
target=”_blank”
rel=”noopener noreferrer”
className=”text-[16px] text-indigo-600 hover:text-indigo-800 transition duration-300 ease-in-out font-noto-sans”
>
一般社団法人デモテク
</a>
</div>
<div className=”flex-grow overflow-y-auto p-4″>
<div className=”flex justify-center mb-4″>
<div className=”flex flex-col items-center mx-2″>
<a
href=”https://fujimoto-etsushi.com”
target=”_blank”
rel=”noopener noreferrer”
>
<img
src=”https://ucarecdn.com/233524db-1045-4a2f-a036-30801c61f019/-/format/auto/”
alt=”藤本えつし候補のウェブサイトへのリンク”
className=”w-16 h-16 object-cover rounded-full”
/>
</a>
<span className=”text-xs mt-1″>藤本えつし候補</span>
</div>
<div className=”flex flex-col items-center mx-2″>
<a
href=”https://akatsuseiichiro.wixsite.com/website”
target=”_blank”
rel=”noopener noreferrer”
>
<img
src=”https://ucarecdn.com/37670d7d-8b68-4f30-a744-357e02ec4356/-/format/auto/”
alt=”あかつ誠一郎候補のウェブサイトへのリンク”
className=”w-16 h-16 object-cover rounded-full”
/>
</a>
<span className=”text-xs mt-1″>あかつ誠一郎候補</span>
</div>
<div className=”flex flex-col items-center mx-2″>
<a
href=”https://shosokumataka.hp.peraichi.com/kouenkai/”
target=”_blank”
rel=”noopener noreferrer”
>
<img
src=”https://ucarecdn.com/2ab176c3-8b10-426f-8d41-0b1e65da7c74/-/format/auto/”
alt=”くまたか昌三候補のウェブサイトへのリンク”
className=”w-16 h-16 object-cover rounded-full”
/>
</a>
<span className=”text-xs mt-1″>くまたか昌三候補</span>
</div>
<div className=”flex flex-col items-center mx-2″>
<a
href=”https://moritani2023.jimdofree.com/”
target=”_blank”
rel=”noopener noreferrer”
>
<img
src=”https://ucarecdn.com/aa199cfa-cf96-4079-99f8-7722a8aa17fe/-/format/auto/”
alt=”森谷まさあき候補のウェブサイトへのリンク”
className=”w-16 h-16 object-cover rounded-full”
/>
</a>
<span className=”text-xs mt-1″>森谷まさあき候補</span>
</div>
</div>
{chatHistory.map((message, index) => (
<div
key={index}
className={`mb-4 ${
message.role === “user” ? “text-right” : “text-left”
}`}
>
<div
className={`inline-block p-3 rounded-lg ${
message.role === “user”
? “bg-indigo-500 text-white”
: “bg-gray-100”
}`}
>
{message.content.split(“\n”).map((line, i) => (
<p key={i} className=”mb-1 last:mb-0 text-lg”>
{line}
</p>
))}
</div>
</div>
))}
</div>
<form
onSubmit={handleSubmit}
className=”p-4 bg-gradient-to-r from-blue-100 to-purple-100″
>
<div className=”flex flex-col”>
<div className=”flex”>
<input
type=”text”
name=”userInput”
value={userInput}
onChange={handleInputChange}
className=”flex-grow p-3 border-2 border-indigo-300 rounded-l-lg focus:outline-none focus:border-indigo-500 text-lg font-noto-sans”
placeholder=”質問を入力してください…”
disabled={isLoading}
/>
<button
type=”submit”
className=”bg-indigo-500 text-white p-3 rounded-r-lg hover:bg-indigo-600 transition duration-300 ease-in-out”
disabled={isLoading}
>
{isLoading ? (
<i className=”fas fa-spinner fa-spin text-xl”></i>
) : (
<i className=”fas fa-paper-plane text-xl”></i>
)}
</button>
</div>
<p className=”text-[10px] text-gray-500 mt-1″>
回答は必ずしも正しいとは限りません。重要な情報は確認するようにしてください。
</p>
</div>
</form>
</div>
);
}
export default MainComponent;