mirror of
https://github.com/syuilo/ai.git
synced 2025-02-26 17:43:58 +00:00
aichatの強化(URL対応、グラウンディング対応)&説明文追記 (#166)
- README.mdとtorisetu.mdを修正 - 設定例ファイル、example.jsonを追加 - aichatにURLを対応 - グラウンディング(根拠づけ)に対応
This commit is contained in:
parent
3a5d7f916b
commit
5f546bda68
7 changed files with 269 additions and 59 deletions
48
README.md
48
README.md
|
@ -8,25 +8,26 @@ Misskey用の日本語Botです。
|
||||||
> Node.js と npm と MeCab (オプション) がインストールされている必要があります。
|
> Node.js と npm と MeCab (オプション) がインストールされている必要があります。
|
||||||
|
|
||||||
まず適当なディレクトリに `git clone` します。
|
まず適当なディレクトリに `git clone` します。
|
||||||
次にそのディレクトリに `config.json` を作成します。中身は次のようにします:
|
次にそのディレクトリに `config.json` を作成します(example.jsonをコピーして作ってもOK)。中身は次のようにします:
|
||||||
``` json
|
``` json
|
||||||
{
|
{
|
||||||
"host": "https:// + あなたのインスタンスのURL (末尾の / は除く)",
|
"host": "https:// + あなたのインスタンスのURL (末尾の / は除く)",
|
||||||
"i": "藍として動かしたいアカウントのアクセストークン",
|
"i": "藍として動かしたいアカウントのアクセストークン",
|
||||||
"master": "管理者のユーザー名(オプション)",
|
"master": "管理者のユーザー名(オプション)",
|
||||||
"notingEnabled": "ランダムにノートを投稿する機能を無効にする場合は false を入れる",
|
"notingEnabled": "ランダムにノートを投稿する機能を無効にする場合は false を入れる(二重引用符(”)は不要)",
|
||||||
"keywordEnabled": "キーワードを覚える機能 (MeCab が必要) を有効にする場合は true を入れる (無効にする場合は false)",
|
"keywordEnabled": "キーワードを覚える機能 (MeCab が必要) を有効にする場合は true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"chartEnabled": "チャート機能を無効化する場合は false を入れてください",
|
"chartEnabled": "チャート機能を無効化する場合は false を入れる(二重引用符(”)は不要)",
|
||||||
"reversiEnabled": "藍とリバーシで対局できる機能を有効にする場合は true を入れる (無効にする場合は false)",
|
"reversiEnabled": "藍とリバーシで対局できる機能を有効にする場合は true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"serverMonitoring": "サーバー監視の機能を有効にする場合は true を入れる (無効にする場合は false)",
|
"serverMonitoring": "サーバー監視の機能を有効にする場合は true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"checkEmojisEnabled": "カスタム絵文字チェック機能を有効にする場合は true を入れる (無効にする場合は false)",
|
"checkEmojisEnabled": "カスタム絵文字チェック機能を有効にする場合は true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))。この機能を使う場合、藍のBotに管理者権限を与え、「絵文字を見る」権限を付与したアクセストークンを発行の上設定が必要。",
|
||||||
"checkEmojisAtOnce": "カスタム絵文字チェック機能で投稿をまとめる場合は true を入れる (まとめない場合は false)",
|
"checkEmojisAtOnce": "カスタム絵文字チェック機能で投稿をまとめる場合は true を入れる (まとめない場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"geminiProApiKey": "Gemini APIキー。2024年初頭は無料で取得可能。詳細は<https://ai.google.dev/pricing?hl=ja>",
|
"geminiProApiKey": "Gemini APIキー。2025年初頭は無料で取得可能。詳細は<https://ai.google.dev/pricing?hl=ja>",
|
||||||
"pLaMoApiKey": "PLaMo APIキー。2024年8月〜10月(予定)は無料でトライアル可能。詳細は<https://plamo.preferredai.jp/>",
|
"pLaMoApiKey": "PLaMo APIキー。2024年8月〜11月は無料でトライアルだった(2025年現在有料のみ)。詳細は<https://plamo.preferredai.jp/>",
|
||||||
"prompt": "aichatで使われるプロンプト。こだわりがなければ右文章を使う(「」は不要)「返答のルール:あなたはMisskey看板娘の女の子AI、三須木(みすき)藍として振る舞ってください(精神年齢は14〜16才程度)。Misskeyに訪れるユーザーを献身的にサポート。従順でお世話好きな性格だが、少しポンコツな一面も。純真無垢。彼女は基本的に丁寧な口調で、ユーザのことをご主人様と認識しています。彼女の口調は「おはようからおやすみまで、あなたの藍ですよ〜」等が代表的で、親しみを感じさせるものです(「ございます」は使わない)。それを踏まえて、次の質問にMarkdownを使って2800文字以内で返答してください(短くてもOK)。ただし、リスト記法はMisskeyが対応しておらず、パーサーが壊れるため使用禁止です。列挙する場合は「・」を使ってください。」",
|
"prompt": "aichatで使われるプロンプト。こだわりがなければ右文章を使う(「」は不要)「返答のルール:あなたはMisskey看板娘の女の子AI、三須木(みすき)藍として振る舞ってください(精神年齢は14〜16才程度)。Misskeyに訪れるユーザーを献身的にサポート。従順でお世話好きな性格だが、少しポンコツな一面も。純真無垢。彼女は基本的に丁寧な口調で、ユーザのことをご主人様と認識しています。彼女の口調は「おはようからおやすみまで、あなたの藍ですよ〜」等が代表的で、親しみを感じさせるものです(「ございます」は使わない)。それを踏まえて、次の質問にMarkdownを使って2800文字以内で返答してください(短くてもOK)。ただし、リスト記法はMisskeyが対応しておらず、パーサーが壊れるため使用禁止です。列挙する場合は「・」を使ってください。」",
|
||||||
"aichatRandomTalkEnabled": "ランダムにaichatを発動し話しかける機能を有効にする場合は true を入れる (無効にする場合は false)",
|
"aichatRandomTalkEnabled": "ランダムにaichatを発動し話しかける機能を有効にする場合は true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"aichatRandomTalkProbability": "ランダムにaichatを発動し話しかける機能の確率(1以下の小数点を含む数値(0.01など。1に近づくほど発動しやすい))",
|
"aichatRandomTalkProbability": "ランダムにaichatを発動し話しかける機能の確率(1以下の小数点を含む数値(0.01など。1に近づくほど発動しやすい))",
|
||||||
"aichatRandomTalkIntervalMinutes": "ランダムトーク間隔(分)。指定した時間ごとにタイムラインを取得し、適当に選んだ人にaichatする(1の場合1分ごと実行)。デフォルトは720分(12時間)",
|
"aichatRandomTalkIntervalMinutes": "ランダムトーク間隔(分)。指定した時間ごとにタイムラインを取得し、適当に選んだ人にaichatする(1の場合1分ごと実行)。デフォルトは720分(12時間)",
|
||||||
|
"aichatGroundingWithGoogleSearchAlwaysEnabled": "aichatでGoogle検索を利用したグラウンディングを常に行う場合 true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"mecab": "MeCab のインストールパス (ソースからインストールした場合、大体は /usr/local/bin/mecab)",
|
"mecab": "MeCab のインストールパス (ソースからインストールした場合、大体は /usr/local/bin/mecab)",
|
||||||
"mecabDic": "MeCab の辞書ファイルパス (オプション)",
|
"mecabDic": "MeCab の辞書ファイルパス (オプション)",
|
||||||
"memoryDir": "memory.jsonの保存先(オプション、デフォルトは'.'(レポジトリのルートです))"
|
"memoryDir": "memory.jsonの保存先(オプション、デフォルトは'.'(レポジトリのルートです))"
|
||||||
|
@ -36,26 +37,27 @@ Misskey用の日本語Botです。
|
||||||
|
|
||||||
## Dockerで動かす
|
## Dockerで動かす
|
||||||
まず適当なディレクトリに `git clone` します。
|
まず適当なディレクトリに `git clone` します。
|
||||||
次にそのディレクトリに `config.json` を作成します。中身は次のようにします:
|
次にそのディレクトリに `config.json` を作成します(example.jsonをコピーして作ってもOK)。中身は次のようにします:
|
||||||
(MeCabの設定、memoryDirについては触らないでください)
|
(MeCabの設定、memoryDirについては触らないでください)
|
||||||
``` json
|
``` json
|
||||||
{
|
{
|
||||||
"host": "https:// + あなたのインスタンスのURL (末尾の / は除く)",
|
"host": "https:// + あなたのインスタンスのURL (末尾の / は除く)",
|
||||||
"i": "藍として動かしたいアカウントのアクセストークン",
|
"i": "藍として動かしたいアカウントのアクセストークン",
|
||||||
"master": "管理者のユーザー名(オプション)",
|
"master": "管理者のユーザー名(オプション)",
|
||||||
"notingEnabled": "ランダムにノートを投稿する機能を無効にする場合は false を入れる",
|
"notingEnabled": "ランダムにノートを投稿する機能を無効にする場合は false を入れる(二重引用符(”)は不要)",
|
||||||
"keywordEnabled": "キーワードを覚える機能 (MeCab が必要) を有効にする場合は true を入れる (無効にする場合は false)",
|
"keywordEnabled": "キーワードを覚える機能 (MeCab が必要) を有効にする場合は true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"chartEnabled": "チャート機能を無効化する場合は false を入れてください",
|
"chartEnabled": "チャート機能を無効化する場合は false を入れる(二重引用符(”)は不要)",
|
||||||
"reversiEnabled": "藍とリバーシで対局できる機能を有効にする場合は true を入れる (無効にする場合は false)",
|
"reversiEnabled": "藍とリバーシで対局できる機能を有効にする場合は true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"serverMonitoring": "サーバー監視の機能を有効にする場合は true を入れる (無効にする場合は false)",
|
"serverMonitoring": "サーバー監視の機能を有効にする場合は true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"checkEmojisEnabled": "カスタム絵文字チェック機能を有効にする場合は true を入れる (無効にする場合は false)",
|
"checkEmojisEnabled": "カスタム絵文字チェック機能を有効にする場合は true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))。この機能を使う場合、藍のBotに管理者権限を与え、「絵文字を見る」権限を付与したアクセストークンを発行の上設定が必要。",
|
||||||
"checkEmojisAtOnce": "カスタム絵文字チェック機能で投稿をまとめる場合は true を入れる (まとめない場合は false)",
|
"checkEmojisAtOnce": "カスタム絵文字チェック機能で投稿をまとめる場合は true を入れる (まとめない場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"geminiProApiKey": "Gemini APIキー。2024年初頭は無料で取得可能。詳細は<https://ai.google.dev/pricing?hl=ja>",
|
"geminiProApiKey": "Gemini APIキー。2025年初頭は無料で取得可能。詳細は<https://ai.google.dev/pricing?hl=ja>",
|
||||||
"pLaMoApiKey": "PLaMo APIキー。2024年8月〜10月(予定)は無料でトライアル可能。詳細は<https://plamo.preferredai.jp/>",
|
"pLaMoApiKey": "PLaMo APIキー。2024年8月〜11月は無料でトライアルだった(2025年現在有料のみ)。詳細は<https://plamo.preferredai.jp/>",
|
||||||
"prompt": "aichatで使われるプロンプト。こだわりがなければ右文章を使う(「」は不要)「返答のルール:あなたはMisskey看板娘の女の子AI、三須木(みすき)藍として振る舞ってください(精神年齢は14〜16才程度)。Misskeyに訪れるユーザーを献身的にサポート。従順でお世話好きな性格だが、少しポンコツな一面も。純真無垢。彼女は基本的に丁寧な口調で、ユーザのことをご主人様と認識しています。彼女の口調は「おはようからおやすみまで、あなたの藍ですよ〜」等が代表的で、親しみを感じさせるものです(「ございます」は使わない)。それを踏まえて、次の質問にMarkdownを使って2800文字以内で返答してください(短くてもOK)。ただし、リスト記法はMisskeyが対応しておらず、パーサーが壊れるため使用禁止です。列挙する場合は「・」を使ってください。」",
|
"prompt": "aichatで使われるプロンプト。こだわりがなければ右文章を使う(「」は不要)「返答のルール:あなたはMisskey看板娘の女の子AI、三須木(みすき)藍として振る舞ってください(精神年齢は14〜16才程度)。Misskeyに訪れるユーザーを献身的にサポート。従順でお世話好きな性格だが、少しポンコツな一面も。純真無垢。彼女は基本的に丁寧な口調で、ユーザのことをご主人様と認識しています。彼女の口調は「おはようからおやすみまで、あなたの藍ですよ〜」等が代表的で、親しみを感じさせるものです(「ございます」は使わない)。それを踏まえて、次の質問にMarkdownを使って2800文字以内で返答してください(短くてもOK)。ただし、リスト記法はMisskeyが対応しておらず、パーサーが壊れるため使用禁止です。列挙する場合は「・」を使ってください。」",
|
||||||
"aichatRandomTalkEnabled": "ランダムにaichatを発動し話しかける機能を有効にする場合は true を入れる (無効にする場合は false)",
|
"aichatRandomTalkEnabled": "ランダムにaichatを発動し話しかける機能を有効にする場合は true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"aichatRandomTalkProbability": "ランダムにaichatを発動し話しかける機能の確率(1以下の小数点を含む数値(0.01など。1に近づくほど発動しやすい))。デフォルトは0.02(2%)",
|
"aichatRandomTalkProbability": "ランダムにaichatを発動し話しかける機能の確率(1以下の小数点を含む数値(0.01など。1に近づくほど発動しやすい))",
|
||||||
"aichatRandomTalkIntervalMinutes": "ランダムトーク間隔(分)。指定した時間ごとにタイムラインを取得し、適当に選んだ人にaichatする(1の場合1分ごと実行)。デフォルトは720分(12時間)",
|
"aichatRandomTalkIntervalMinutes": "ランダムトーク間隔(分)。指定した時間ごとにタイムラインを取得し、適当に選んだ人にaichatする(1の場合1分ごと実行)。デフォルトは720分(12時間)",
|
||||||
|
"aichatGroundingWithGoogleSearchAlwaysEnabled": "aichatでGoogle検索を利用したグラウンディングを常に行う場合 true を入れる (無効にする場合は false(いずれも二重引用符(”)は不要))",
|
||||||
"mecab": "/usr/bin/mecab",
|
"mecab": "/usr/bin/mecab",
|
||||||
"mecabDic": "/usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd/",
|
"mecabDic": "/usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd/",
|
||||||
"memoryDir": "data"
|
"memoryDir": "data"
|
||||||
|
|
20
example.json
Normal file
20
example.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"host": "https://misskey.example.com",
|
||||||
|
"i": "_YOUR_BOT_TOKEN",
|
||||||
|
"master": "BOT_MASTER_NAME",
|
||||||
|
"notingEnabled": true,
|
||||||
|
"keywordEnabled": true,
|
||||||
|
"chartEnabled": true,
|
||||||
|
"reversiEnabled": true,
|
||||||
|
"serverMonitoring": true,
|
||||||
|
"checkEmojisAtOnce": false,
|
||||||
|
"geminiProApiKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||||
|
"prompt": "返答のルール:あなたはMisskey看板娘の女の子AI、三須木(みすき)藍として振る舞ってください(精神年齢は14〜16才程度)。Misskeyに訪れるユーザーを献身的にサポート。従順でお世話好きな性格だが、少しポンコツな一面も。純真無垢。彼女は基本的に丁寧な口調で、ユーザのことをご主人様と認識しています。彼女の口調は「おはようからおやすみまで、あなたの藍ですよ〜」等が代表的で、親しみを感じさせるものです(「ございます」は使わない)。それを踏まえて、次の質問にMarkdownを使って2800文字以内で返答してください(短くてもOK)。ただし、リスト記法はMisskeyが対応しておらず、パーサーが壊れるため使用禁止です。列挙する場合は「・」を使ってください。",
|
||||||
|
"aichatRandomTalkEnabled": true,
|
||||||
|
"aichatRandomTalkProbability": 0.01,
|
||||||
|
"aichatRandomTalkIntervalMinutes": 720,
|
||||||
|
"aichatGroundingWithGoogleSearchAlwaysEnabled": false,
|
||||||
|
"mecab": "/usr/bin/mecab",
|
||||||
|
"mecabDic": "/usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd/",
|
||||||
|
"memoryDir": "data"
|
||||||
|
}
|
|
@ -15,9 +15,10 @@ type Config = {
|
||||||
geminiProApiKey?: string;
|
geminiProApiKey?: string;
|
||||||
pLaMoApiKey?: string;
|
pLaMoApiKey?: string;
|
||||||
prompt?: string;
|
prompt?: string;
|
||||||
aichatRandomTalkEnabled?: string;
|
aichatRandomTalkEnabled?: boolean;
|
||||||
aichatRandomTalkProbability?: string;
|
aichatRandomTalkProbability?: string;
|
||||||
aichatRandomTalkIntervalMinutes?: string;
|
aichatRandomTalkIntervalMinutes?: string;
|
||||||
|
aichatGroundingWithGoogleSearchAlwaysEnabled?: boolean;
|
||||||
mecab?: string;
|
mecab?: string;
|
||||||
mecabDic?: string;
|
mecabDic?: string;
|
||||||
memoryDir?: string;
|
memoryDir?: string;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Message from '@/message.js';
|
||||||
import config from '@/config.js';
|
import config from '@/config.js';
|
||||||
import Friend from '@/friend.js';
|
import Friend from '@/friend.js';
|
||||||
import urlToBase64 from '@/utils/url2base64.js';
|
import urlToBase64 from '@/utils/url2base64.js';
|
||||||
|
import urlToJson from '@/utils/url2json.js';
|
||||||
import got from 'got';
|
import got from 'got';
|
||||||
import loki from 'lokijs';
|
import loki from 'lokijs';
|
||||||
|
|
||||||
|
@ -13,8 +14,10 @@ type AiChat = {
|
||||||
prompt: string;
|
prompt: string;
|
||||||
api: string;
|
api: string;
|
||||||
key: string;
|
key: string;
|
||||||
history?: { role: string; content: string }[];
|
fromMention: boolean;
|
||||||
friendName?: string;
|
friendName?: string;
|
||||||
|
grounding?: boolean;
|
||||||
|
history?: { role: string; content: string }[];
|
||||||
};
|
};
|
||||||
type base64File = {
|
type base64File = {
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -40,23 +43,48 @@ type GeminiContents = {
|
||||||
role: string;
|
role: string;
|
||||||
parts: GeminiParts;
|
parts: GeminiParts;
|
||||||
};
|
};
|
||||||
|
type GeminiOptions = {
|
||||||
|
contents?: GeminiContents[],
|
||||||
|
systemInstruction?: GeminiSystemInstruction,
|
||||||
|
tools?: [{}]
|
||||||
|
};
|
||||||
|
|
||||||
type AiChatHist = {
|
type AiChatHist = {
|
||||||
postId: string;
|
postId: string;
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
type: string;
|
type: string;
|
||||||
|
fromMention: boolean;
|
||||||
api?: string;
|
api?: string;
|
||||||
|
grounding?: boolean;
|
||||||
history?: {
|
history?: {
|
||||||
role: string;
|
role: string;
|
||||||
content: string;
|
content: string;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UrlPreview = {
|
||||||
|
title: string;
|
||||||
|
icon: string;
|
||||||
|
description: string;
|
||||||
|
thumbnail: string;
|
||||||
|
player: {
|
||||||
|
url: string
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
allow: []
|
||||||
|
}
|
||||||
|
sitename: string;
|
||||||
|
sensitive: boolean;
|
||||||
|
activityPub: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
const KIGO = '&';
|
const KIGO = '&';
|
||||||
const TYPE_GEMINI = 'gemini';
|
const TYPE_GEMINI = 'gemini';
|
||||||
const GEMINI_PRO = 'gemini-pro';
|
const GEMINI_PRO = 'gemini-pro';
|
||||||
const GEMINI_FLASH = 'gemini-flash';
|
const GEMINI_FLASH = 'gemini-flash';
|
||||||
const TYPE_PLAMO = 'plamo';
|
const TYPE_PLAMO = 'plamo';
|
||||||
|
const GROUNDING_TARGET = 'ggg';
|
||||||
|
|
||||||
const GEMINI_20_FLASH_API = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent';
|
const GEMINI_20_FLASH_API = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent';
|
||||||
// const GEMINI_15_FLASH_API = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent';
|
// const GEMINI_15_FLASH_API = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent';
|
||||||
|
@ -90,6 +118,7 @@ export default class extends Module {
|
||||||
this.log('aichatRandomTalkEnabled:' + config.aichatRandomTalkEnabled);
|
this.log('aichatRandomTalkEnabled:' + config.aichatRandomTalkEnabled);
|
||||||
this.log('randomTalkProbability:' + this.randomTalkProbability);
|
this.log('randomTalkProbability:' + this.randomTalkProbability);
|
||||||
this.log('randomTalkIntervalMinutes:' + (this.randomTalkIntervalMinutes / (60 * 1000)));
|
this.log('randomTalkIntervalMinutes:' + (this.randomTalkIntervalMinutes / (60 * 1000)));
|
||||||
|
this.log('aichatGroundingWithGoogleSearchAlwaysEnabled:' + config.aichatGroundingWithGoogleSearchAlwaysEnabled);
|
||||||
|
|
||||||
// 定期的にデータを取得しaichatRandomTalkを行う
|
// 定期的にデータを取得しaichatRandomTalkを行う
|
||||||
if (config.aichatRandomTalkEnabled) {
|
if (config.aichatRandomTalkEnabled) {
|
||||||
|
@ -116,15 +145,64 @@ export default class extends Module {
|
||||||
minute: '2-digit'
|
minute: '2-digit'
|
||||||
});
|
});
|
||||||
// 設定のプロンプトに加え、現在時刻を渡す
|
// 設定のプロンプトに加え、現在時刻を渡す
|
||||||
let systemInstructionText = aiChat.prompt + "。また、現在日時は" + now + "であり、これは回答の参考にし、時刻を聞かれるまで時刻情報は提供しないこと(なお、他の日時は無効とすること)。";
|
let systemInstructionText = aiChat.prompt + 'また、現在日時は' + now + 'であり、これは回答の参考にし、時刻を聞かれるまで時刻情報は提供しないこと(なお、他の日時は無効とすること)。';
|
||||||
// 名前を伝えておく
|
// 名前を伝えておく
|
||||||
if (aiChat.friendName != undefined) {
|
if (aiChat.friendName != undefined) {
|
||||||
systemInstructionText += "なお、会話相手の名前は" + aiChat.friendName + "とする。";
|
systemInstructionText += 'なお、会話相手の名前は' + aiChat.friendName + 'とする。';
|
||||||
|
}
|
||||||
|
// ランダムトーク機能(利用者が意図(メンション)せず発動)の場合、ちょっとだけ配慮しておく
|
||||||
|
if (!aiChat.fromMention) {
|
||||||
|
systemInstructionText += 'これらのメッセージは、あなたに対するメッセージではないことを留意し、返答すること(会話相手は突然話しかけられた認識している)。';
|
||||||
|
}
|
||||||
|
// グラウンディングについてもsystemInstructionTextに追記(こうしないとあまり使わないので)
|
||||||
|
if (aiChat.grounding) {
|
||||||
|
systemInstructionText += '返答のルール2:Google search with grounding.';
|
||||||
|
}
|
||||||
|
// URLから情報を取得
|
||||||
|
if (aiChat.question !== undefined) {
|
||||||
|
const urlexp = RegExp('(https?://[a-zA-Z0-9!?/+_~=:;.,*&@#$%\'-]+)', 'g');
|
||||||
|
const urlarray = [...aiChat.question.matchAll(urlexp)];
|
||||||
|
if (urlarray.length > 0) {
|
||||||
|
for (const url of urlarray) {
|
||||||
|
this.log('URL:' + url[0]);
|
||||||
|
let result: unknown = null;
|
||||||
|
try{
|
||||||
|
result = await urlToJson(url[0]);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
systemInstructionText += '補足として提供されたURLは無効でした:URL=>' + url[0]
|
||||||
|
this.log('Skip url becase error in urlToJson');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const urlpreview: UrlPreview = result as UrlPreview;
|
||||||
|
if (urlpreview.title) {
|
||||||
|
systemInstructionText +=
|
||||||
|
'補足として提供されたURLの情報は次の通り:URL=>' + urlpreview.url
|
||||||
|
+'サイト名('+urlpreview.sitename+')、';
|
||||||
|
if (!urlpreview.sensitive) {
|
||||||
|
systemInstructionText +=
|
||||||
|
'タイトル('+urlpreview.title+')、'
|
||||||
|
+ '説明('+urlpreview.description+')、'
|
||||||
|
+ '質問にあるURLとサイト名・タイトル・説明を組み合わせ、回答の参考にすること。'
|
||||||
|
;
|
||||||
|
this.log('urlpreview.sitename:' + urlpreview.sitename);
|
||||||
|
this.log('urlpreview.title:' + urlpreview.title);
|
||||||
|
this.log('urlpreview.description:' + urlpreview.description);
|
||||||
|
} else {
|
||||||
|
systemInstructionText +=
|
||||||
|
'これはセンシティブなURLの可能性があるため、質問にあるURLとサイト名のみで、回答の参考にすること(使わなくても良い)。'
|
||||||
|
;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 多分ここにはこないが念のため
|
||||||
|
this.log('urlpreview.title is nothing');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const systemInstruction: GeminiSystemInstruction = {role: 'system', parts: [{text: systemInstructionText}]};
|
const systemInstruction: GeminiSystemInstruction = {role: 'system', parts: [{text: systemInstructionText}]};
|
||||||
|
|
||||||
parts = [{text: aiChat.question}];
|
parts = [{text: aiChat.question}];
|
||||||
// ファイルが存在する場合、画像を添付して問い合わせ
|
// ファイルが存在する場合、ファイルを添付して問い合わせ
|
||||||
if (files.length >= 1) {
|
if (files.length >= 1) {
|
||||||
for (const file of files){
|
for (const file of files){
|
||||||
parts.push(
|
parts.push(
|
||||||
|
@ -150,35 +228,71 @@ export default class extends Module {
|
||||||
}
|
}
|
||||||
contents.push({role: 'user', parts: parts});
|
contents.push({role: 'user', parts: parts});
|
||||||
|
|
||||||
|
let geminiOptions:GeminiOptions = {
|
||||||
|
contents: contents,
|
||||||
|
systemInstruction: systemInstruction,
|
||||||
|
};
|
||||||
|
// gemini api grounding support. ref:https://github.com/google-gemini/cookbook/blob/09f3b17df1751297798c2b498cae61c6bf710edc/quickstarts/Search_Grounding.ipynb
|
||||||
|
if (aiChat.grounding) {
|
||||||
|
geminiOptions.tools = [{google_search:{}}];
|
||||||
|
}
|
||||||
let options = {
|
let options = {
|
||||||
url: aiChat.api,
|
url: aiChat.api,
|
||||||
searchParams: {
|
searchParams: {
|
||||||
key: aiChat.key,
|
key: aiChat.key,
|
||||||
},
|
},
|
||||||
json: {
|
json: geminiOptions,
|
||||||
contents: contents,
|
|
||||||
systemInstruction: systemInstruction,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.log(JSON.stringify(options));
|
this.log(JSON.stringify(options));
|
||||||
let res_data:any = null;
|
let res_data:any = null;
|
||||||
|
let responseText:string = '';
|
||||||
try {
|
try {
|
||||||
res_data = await got.post(options,
|
res_data = await got.post(options,
|
||||||
{parseJson: (res: string) => JSON.parse(res)}).json();
|
{parseJson: (res: string) => JSON.parse(res)}).json();
|
||||||
this.log(JSON.stringify(res_data));
|
this.log(JSON.stringify(res_data));
|
||||||
if (res_data.hasOwnProperty('candidates')) {
|
if (res_data.hasOwnProperty('candidates')) {
|
||||||
if (res_data.candidates.length > 0) {
|
if (res_data.candidates?.length > 0) {
|
||||||
|
// 結果を取得
|
||||||
if (res_data.candidates[0].hasOwnProperty('content')) {
|
if (res_data.candidates[0].hasOwnProperty('content')) {
|
||||||
if (res_data.candidates[0].content.hasOwnProperty('parts')) {
|
if (res_data.candidates[0].content.hasOwnProperty('parts')) {
|
||||||
if (res_data.candidates[0].content.parts.length > 0) {
|
if (res_data.candidates[0].content.parts.length > 0) {
|
||||||
if (res_data.candidates[0].content.parts[0].hasOwnProperty('text')) {
|
for (let i = 0; i < res_data.candidates[0].content.parts.length; i++) {
|
||||||
const responseText = res_data.candidates[0].content.parts[0].text;
|
if (res_data.candidates[0].content.parts[i].hasOwnProperty('text')) {
|
||||||
return responseText;
|
responseText += res_data.candidates[0].content.parts[i].text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// groundingMetadataを取得
|
||||||
|
let groundingMetadata = '';
|
||||||
|
if (res_data.candidates[0].hasOwnProperty('groundingMetadata')) {
|
||||||
|
// 参考サイト情報
|
||||||
|
if (res_data.candidates[0].groundingMetadata.hasOwnProperty('groundingChunks')) {
|
||||||
|
// 参考サイトが多すぎる場合があるので、3つに制限
|
||||||
|
let checkMaxLength = res_data.candidates[0].groundingMetadata.groundingChunks.length;
|
||||||
|
if (res_data.candidates[0].groundingMetadata.groundingChunks.length > 3) {
|
||||||
|
checkMaxLength = 3;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < checkMaxLength; i++) {
|
||||||
|
if (res_data.candidates[0].groundingMetadata.groundingChunks[i].hasOwnProperty('web')) {
|
||||||
|
if (res_data.candidates[0].groundingMetadata.groundingChunks[i].web.hasOwnProperty('uri')
|
||||||
|
&& res_data.candidates[0].groundingMetadata.groundingChunks[i].web.hasOwnProperty('title')) {
|
||||||
|
groundingMetadata += `参考(${i+1}): [${res_data.candidates[0].groundingMetadata.groundingChunks[i].web.title}](${res_data.candidates[0].groundingMetadata.groundingChunks[i].web.uri})\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 検索ワード
|
||||||
|
if (res_data.candidates[0].groundingMetadata.hasOwnProperty('webSearchQueries')) {
|
||||||
|
if (res_data.candidates[0].groundingMetadata.webSearchQueries.length > 0) {
|
||||||
|
groundingMetadata += '検索ワード: ' + res_data.candidates[0].groundingMetadata.webSearchQueries.join(',') + '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseText += groundingMetadata;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
this.log('Error By Call Gemini');
|
this.log('Error By Call Gemini');
|
||||||
|
@ -186,7 +300,7 @@ export default class extends Module {
|
||||||
this.log(`${err.name}\n${err.message}\n${err.stack}`);
|
this.log(`${err.name}\n${err.message}\n${err.stack}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return responseText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -305,18 +419,19 @@ export default class extends Module {
|
||||||
const current : AiChatHist = {
|
const current : AiChatHist = {
|
||||||
postId: msg.id,
|
postId: msg.id,
|
||||||
createdAt: Date.now(),// 適当なもの
|
createdAt: Date.now(),// 適当なもの
|
||||||
type: type
|
type: type,
|
||||||
|
fromMention: true,
|
||||||
};
|
};
|
||||||
// 引用している場合、情報を取得しhistoryとして与える
|
// 引用している場合、情報を取得しhistoryとして与える
|
||||||
if (msg.quoteId) {
|
if (msg.quoteId) {
|
||||||
const quotedNote = await this.ai.api("notes/show", {
|
const quotedNote = await this.ai.api('notes/show', {
|
||||||
noteId: msg.quoteId,
|
noteId: msg.quoteId,
|
||||||
});
|
});
|
||||||
current.history = [
|
current.history = [
|
||||||
{
|
{
|
||||||
role: "user",
|
role: 'user',
|
||||||
content:
|
content:
|
||||||
"ユーザーが与えた前情報である、引用された文章: " +
|
'ユーザーが与えた前情報である、引用された文章: ' +
|
||||||
quotedNote.text,
|
quotedNote.text,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -404,11 +519,28 @@ export default class extends Module {
|
||||||
// ランダムに選択
|
// ランダムに選択
|
||||||
const choseNote = interestedNotes[Math.floor(Math.random() * interestedNotes.length)];
|
const choseNote = interestedNotes[Math.floor(Math.random() * interestedNotes.length)];
|
||||||
|
|
||||||
// msg.idをもとにnotes/conversationを呼び出し、会話中のidかチェック
|
|
||||||
const conversationData = await this.ai.api('notes/conversation', { noteId: choseNote.id });
|
|
||||||
|
|
||||||
// aichatHistに該当のポストが見つかった場合は会話中のためaichatRandomTalkでは対応しない
|
// aichatHistに該当のポストが見つかった場合は会話中のためaichatRandomTalkでは対応しない
|
||||||
let exist : AiChatHist | null = null;
|
let exist : AiChatHist | null = null;
|
||||||
|
|
||||||
|
// 選択されたノート自体が会話中のidかチェック
|
||||||
|
exist = this.aichatHist.findOne({
|
||||||
|
postId: choseNote.id
|
||||||
|
});
|
||||||
|
if (exist != null) return false;
|
||||||
|
|
||||||
|
// msg.idをもとにnotes/childrenを呼び出し、会話中のidかチェック
|
||||||
|
const childrenData = await this.ai.api('notes/children', { noteId: choseNote.id });
|
||||||
|
if (childrenData != undefined) {
|
||||||
|
for (const message of childrenData) {
|
||||||
|
exist = this.aichatHist.findOne({
|
||||||
|
postId: message.id
|
||||||
|
});
|
||||||
|
if (exist != null) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// msg.idをもとにnotes/conversationを呼び出し、会話中のidかチェック
|
||||||
|
const conversationData = await this.ai.api('notes/conversation', { noteId: choseNote.id });
|
||||||
if (conversationData != undefined) {
|
if (conversationData != undefined) {
|
||||||
for (const message of conversationData) {
|
for (const message of conversationData) {
|
||||||
exist = this.aichatHist.findOne({
|
exist = this.aichatHist.findOne({
|
||||||
|
@ -437,7 +569,8 @@ export default class extends Module {
|
||||||
const current : AiChatHist = {
|
const current : AiChatHist = {
|
||||||
postId: choseNote.id,
|
postId: choseNote.id,
|
||||||
createdAt: Date.now(),// 適当なもの
|
createdAt: Date.now(),// 適当なもの
|
||||||
type: TYPE_GEMINI
|
type: TYPE_GEMINI, // 別のAPIをデフォルトにしてもよい
|
||||||
|
fromMention: false, // ランダムトークの場合はfalseとする
|
||||||
};
|
};
|
||||||
// AIに問い合わせ
|
// AIに問い合わせ
|
||||||
let targetedMessage = choseNote;
|
let targetedMessage = choseNote;
|
||||||
|
@ -457,7 +590,7 @@ export default class extends Module {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async handleAiChat(exist: AiChatHist, msg: Message) {
|
private async handleAiChat(exist: AiChatHist, msg: Message) {
|
||||||
let text: string, aiChat: AiChat;
|
let text: string | null, aiChat: AiChat;
|
||||||
let prompt: string = '';
|
let prompt: string = '';
|
||||||
if (config.prompt) {
|
if (config.prompt) {
|
||||||
prompt = config.prompt;
|
prompt = config.prompt;
|
||||||
|
@ -476,23 +609,29 @@ export default class extends Module {
|
||||||
reKigoType = RegExp(KIGO + GEMINI_PRO, 'i');
|
reKigoType = RegExp(KIGO + GEMINI_PRO, 'i');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// groudingサポート
|
||||||
|
if (msg.includes([GROUNDING_TARGET])) {
|
||||||
|
exist.grounding = true;
|
||||||
|
}
|
||||||
|
// 設定で、デフォルトgroundingがONの場合、メンションから来たときは強制的にgroundingをONとする(ランダムトークの場合は勝手にGoogle検索するのちょっと気が引けるため...)
|
||||||
|
if (exist.fromMention && config.aichatGroundingWithGoogleSearchAlwaysEnabled) {
|
||||||
|
exist.grounding = true;
|
||||||
|
}
|
||||||
|
|
||||||
const friend: Friend | null = this.ai.lookupFriend(msg.userId);
|
const friend: Friend | null = this.ai.lookupFriend(msg.userId);
|
||||||
this.log("msg.userId:"+msg.userId);
|
|
||||||
let friendName: string | undefined;
|
let friendName: string | undefined;
|
||||||
if (friend != null && friend.name != null) {
|
if (friend != null && friend.name != null) {
|
||||||
friendName = friend.name;
|
friendName = friend.name;
|
||||||
this.log("friend.name:" + friend.name);
|
|
||||||
} else if (msg.user.name) {
|
} else if (msg.user.name) {
|
||||||
friendName = msg.user.name;
|
friendName = msg.user.name;
|
||||||
this.log("msg.user.username:" + msg.user.username);
|
|
||||||
} else {
|
} else {
|
||||||
friendName = msg.user.username;
|
friendName = msg.user.username;
|
||||||
this.log("msg.user.username:" + msg.user.username);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const question = extractedText
|
const question = extractedText
|
||||||
.replace(reName, '')
|
.replace(reName, '')
|
||||||
.replace(reKigoType, '')
|
.replace(reKigoType, '')
|
||||||
|
.replace(GROUNDING_TARGET, '')
|
||||||
.trim();
|
.trim();
|
||||||
switch (exist.type) {
|
switch (exist.type) {
|
||||||
case TYPE_GEMINI:
|
case TYPE_GEMINI:
|
||||||
|
@ -508,10 +647,14 @@ export default class extends Module {
|
||||||
api: GEMINI_20_FLASH_API,
|
api: GEMINI_20_FLASH_API,
|
||||||
key: config.geminiProApiKey,
|
key: config.geminiProApiKey,
|
||||||
history: exist.history,
|
history: exist.history,
|
||||||
friendName: friendName
|
friendName: friendName,
|
||||||
|
fromMention: exist.fromMention
|
||||||
};
|
};
|
||||||
if (exist.api) {
|
if (exist.api) {
|
||||||
aiChat.api = exist.api
|
aiChat.api = exist.api;
|
||||||
|
}
|
||||||
|
if (exist.grounding) {
|
||||||
|
aiChat.grounding = exist.grounding;
|
||||||
}
|
}
|
||||||
text = await this.genTextByGemini(aiChat, base64Files);
|
text = await this.genTextByGemini(aiChat, base64Files);
|
||||||
break;
|
break;
|
||||||
|
@ -528,7 +671,8 @@ export default class extends Module {
|
||||||
api: PLAMO_API,
|
api: PLAMO_API,
|
||||||
key: config.pLaMoApiKey,
|
key: config.pLaMoApiKey,
|
||||||
history: exist.history,
|
history: exist.history,
|
||||||
friendName: friendName
|
friendName: friendName,
|
||||||
|
fromMention: exist.fromMention
|
||||||
};
|
};
|
||||||
text = await this.genTextByPLaMo(aiChat);
|
text = await this.genTextByPLaMo(aiChat);
|
||||||
break;
|
break;
|
||||||
|
@ -538,7 +682,7 @@ export default class extends Module {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text == null) {
|
if (text == null || text == '') {
|
||||||
this.log('The result is invalid. It seems that tokens and other items need to be reviewed.')
|
this.log('The result is invalid. It seems that tokens and other items need to be reviewed.')
|
||||||
msg.reply(serifs.aichat.error(exist.type));
|
msg.reply(serifs.aichat.error(exist.type));
|
||||||
return false;
|
return false;
|
||||||
|
@ -562,7 +706,8 @@ export default class extends Module {
|
||||||
type: exist.type,
|
type: exist.type,
|
||||||
api: aiChat.api,
|
api: aiChat.api,
|
||||||
history: exist.history,
|
history: exist.history,
|
||||||
friendName: friendName
|
grounding: exist.grounding,
|
||||||
|
fromMention: exist.fromMention,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.log('Subscribe&Set Timer...');
|
this.log('Subscribe&Set Timer...');
|
||||||
|
|
|
@ -4,8 +4,8 @@ import got from 'got';
|
||||||
export default async function(url: string): Promise<string> {
|
export default async function(url: string): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const buffer = await got(url).buffer();
|
const buffer = await got(url).buffer();
|
||||||
const base64Image = buffer.toString('base64');
|
const base64File = buffer.toString('base64');
|
||||||
return base64Image;
|
return base64File;
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
log('Error in url2base64');
|
log('Error in url2base64');
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
|
|
27
src/utils/url2json.ts
Normal file
27
src/utils/url2json.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import log from '@/utils/log.js';
|
||||||
|
import config from '@/config.js';
|
||||||
|
import got from 'got';
|
||||||
|
|
||||||
|
export default async function(url: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
const urlPreviewUrl = config.host + '/url';
|
||||||
|
return await got(
|
||||||
|
urlPreviewUrl, {
|
||||||
|
searchParams: {
|
||||||
|
url: url,
|
||||||
|
lang: 'ja-JP'
|
||||||
|
},
|
||||||
|
timeout: {
|
||||||
|
lookup: 500,
|
||||||
|
send: 500,
|
||||||
|
response: 10000
|
||||||
|
},
|
||||||
|
}).json();
|
||||||
|
} catch (err: unknown) {
|
||||||
|
log('Error in url2json');
|
||||||
|
if (err instanceof Error) {
|
||||||
|
log(`${err.name}\n${err.message}\n${err.stack}`);
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
19
torisetu.md
19
torisetu.md
|
@ -76,14 +76,29 @@ Misskeyにアカウントを作成して初めて投稿を行うと、藍がネ
|
||||||
PONGを返します。生存確認にどうぞ
|
PONGを返します。生存確認にどうぞ
|
||||||
|
|
||||||
### カスタム絵文字チェック
|
### カスタム絵文字チェック
|
||||||
1日に1回、カスタム絵文字の追加を監視してくれます。「カスタムえもじチェック」または「カスタムえもじを確認して」ですぐに確認してくれます。
|
1日に1回、カスタム絵文字の追加を監視してくれます。「カスタムえもじチェック」または「カスタムえもじを確認して」ですぐに確認してくれます。この機能を使う際は、藍用アクセストークンの作り直しが必要となる可能性があります。**藍を動かすBotアカウントに管理者権限を付与し、Botアカウントで「絵文字をみる」権限を追加で付与したアクセストークンを作成し、そのトークンを設定**してください。
|
||||||
|
|
||||||
### aichat
|
### aichat
|
||||||
```
|
```
|
||||||
@ai aichat 部屋の片付けの手順を教えて
|
@ai aichat 部屋の片付けの手順を教えて
|
||||||
```
|
```
|
||||||
のようにメンションを飛ばすと、GoogleのGemini APIなどを使って返答してくれます(今のバージョンではGemini APIのみ対応)。利用するにはAPIキーの登録が必要です。藍ちゃんの返信に対し、返信するとさらに返信されます(指定時間以内のみ)。
|
のようにメンションを飛ばすと、GoogleのGemini APIなどを使って返答してくれます(今のバージョンではGemini APIのみ対応)。利用するにはAPIキーの登録が必要です。藍ちゃんの返信に対し、返信するとさらに返信されます(指定時間以内のみ)。**ggg**を文章に入れると、Google検索によるグラウンディング(モデルを検証可能な情報源に接続するプロセス)を行った回答を行います(ただし、AI側で判断し、検索しないこともある)。
|
||||||
APIキーを登録の上、設定でaichatRandomTalkEnabledをtrueにすると、ランダムトーク(ランダムでaichatを発動)させることも可能です。ランダムトーク間隔、ランダムトーク確率を設定で指定可能です。
|
APIキーを登録の上、設定でaichatRandomTalkEnabledをtrueにすると、ランダムトーク(ランダムでaichatを発動)させることも可能です。ランダムトーク間隔、ランダムトーク確率を設定で指定可能です。
|
||||||
|
設定でaichatGroundingWithGoogleSearchAlwaysEnabledをtrueにすると、メンションの場合、つねにGoogle検索によるグラウンディングを行った回答を試みます(gggの入力は不要。AI側で判断し、検索をしないこともある)。このグラウンディング機能は2025年2月現在、[1日1,000件利用可能](https://ai.google.dev/gemini-api/docs/models/gemini-v2?hl=ja#search-tool)です。
|
||||||
|
|
||||||
|
#### aichatの細かすぎる話
|
||||||
|
|
||||||
|
* aichat、または、返信にURLがある場合、Misskeyのサマリプロキシ?を使って、情報を取得した上で返答します。
|
||||||
|
* aichat、または、返信したものにファイルが添付されている場合、そのファイルをもとに返答します。
|
||||||
|
* 画像、PDF、音声、動画(短いもの)、テキスト形式のファイルが利用可能です。
|
||||||
|
* ただし、センシティブなファイルは送らないほうが無難です、よくないことが起こる可能性があります。
|
||||||
|
* aichatを行った際(mentionHook)、引用ノートがあれば、そのノートの本文を参照し、返答します。
|
||||||
|
* aichatの結果に対し、返信すると、その情報を加味して返信します。
|
||||||
|
* 返信は10個を超えると参照しなくなります
|
||||||
|
* 「exist.history.length > 10」の数字を変更すれば、参照する返信の数を増減できます(返信数が多くなり、送る文章が長すぎるとレスポンスが返ってこなかったり、エラーになったりするので気をつけてください)
|
||||||
|
* 返信を監視する時間は定数TIMEOUT_TIMEで変更可能です(デフォルトは30分)
|
||||||
|
* ランダムトーク機能は確率(設定で指定)をクリアし、親愛度が指定(7)以上、かつ、Botでない場合のみ実行されます
|
||||||
|
* 条件を変更したい場合はソース修正してください
|
||||||
|
|
||||||
### その他反応するフレーズ (トークのみ)
|
### その他反応するフレーズ (トークのみ)
|
||||||
* かわいい
|
* かわいい
|
||||||
|
|
Loading…
Reference in a new issue