AIに指示されるだけの人間になれたかも

https://qiita.com/EasyCording/items/fceef0255168852a1b7a

とりあえず、システムの実装が完成した。

# システム構成

* Redmine　生活ログデータを取得
* EASYRAG　テキストデータをRAGで検索
* 生成AIに渡す


```plantuml
@startuml
skinparam componentStyle rectangle

actor ユーザー
component Redmine
component EASYRAG
component "生成AI" as AI

ユーザー -down-> EASYRAG: 指示 + クエリ
ユーザー -down-> Redmine: 記入
Redmine -right-> EASYRAG: ログデータ
EASYRAG -right-> AI: 指示 + 参考情報
AI -right-> ユーザー: 回答

note top of Redmine: 日々の活動、予定\nタスクを保存
note top of EASYRAG: クエリの類似度検索で\nログデータを取得
note top of AI: EASYRAGのプロンプト\nを生成AIにコピペ
 
@enduml
```

# 生活ログでテスト実施

一か月分のRedmineの作業時間をCSVでエキスポート

# プロンプト

指示内容:　明日の計画を立てて

#### クエリ内容:
* 肉と野菜はある
* 玄米を焚こうかな
* 山に行きたいが、やめてウォーキング
* 洗濯は終わり
* 台所のスポンジに自動で洗剤をつけたい
* 英語のレッスンは無くなった
* 肩こり、運動不足、節約　

# 途中経過
RedmineのCSVデータを少しだけ解説する。
詳細は恥ずかしいので省略

```
プロジェクト,日付,作成日,週,作成者,ユーザー,作業分類,チケット,トラッカー,親チケット,ステータス,カテゴリ,対象バージョン,コメント,時間,済,ChatGPT,Answer
メイン,2024/06/05,2024/06/05 14:49,23,xxxx,xxxx,タスク,タスク #301: すること,タスク,"",計画,"","",韓国語のリピーティング　口と耳の慣れ,2.00,いいえ,"",""
```

メインエンジン部分
* 大したことはやっていない
* ほとんどＧＰＴ４に書かせている。

```python
import re
from janome.tokenizer import Tokenizer
from janome.analyzer import Analyzer
from janome.charfilter import UnicodeNormalizeCharFilter
from janome.tokenfilter import POSKeepFilter, POSStopFilter, LowerCaseFilter, CompoundNounFilter

# カスタム辞書を使用してTokenizerを初期化
UDIC = r"user_simpledic.csv"
tokenizer = Tokenizer(UDIC, udic_type="simpledic", udic_enc="utf8")

# 前処理用の正規表現
preprocessing_regex = re.compile(r'[^a-zA-Z0-9ぁ-んァ-ヶー一-龥\s]')

from janome.tokenfilter import TokenFilter

class NumericFilter(TokenFilter):
    def apply(self, tokens):
        for token in tokens:
            if not token.part_of_speech.startswith('名詞,数'):  # 数詞を除外
                yield token

class LengthLimitFilter(TokenFilter):
    def __init__(self, max_length=10):
        self.max_length = max_length

    def apply(self, tokens):
        for token in tokens:
            if len(token.surface) <= self.max_length:
                yield token

# Token filtersのリストに追加
token_filters = [
    #CompoundNounFilter(),  # 複合語の分割
    POSKeepFilter(['名詞', '動詞', '形容詞']),  # 名詞、動詞、形容詞のみ残す
    POSStopFilter(['助詞', '助動詞']),  # 助詞と助動詞を除去
    LowerCaseFilter(),  # 小文字化
    NumericFilter(),  # 数字を除去
    LengthLimitFilter(max_length=4)  # 10文字以上の複合語を除去
]

def preprocess(text):
    text = preprocessing_regex.sub('', text)
    return text



def keitaiso(texts):
    char_filters = [UnicodeNormalizeCharFilter()]
    analyzer = Analyzer(char_filters=char_filters, tokenizer=tokenizer, token_filters=token_filters)
     
    words = []
    for text in texts:
        text = preprocess(text)
        tokens = [token.surface for token in analyzer.analyze(text)]
        words.append(' '.join(tokens))

    return words

def tokenize(text):
    tokenizer = Tokenizer()
    return [token.surface for token in tokenizer.tokenize(text)]

def load_data_from_excel(file_path):
    df = pd.read_excel(file_path)
    df['Column1'] = df.apply(lambda row: ' '.join(row.dropna().astype(str)), axis=1)
    df = df[['Column1']]
    return df

def load_data_from_file(file_path):

    file_parts = file_path.rsplit('.', 1)
    if len(file_parts) == 2:
        file_extension = file_parts[1].lower()
    else:
        file_extension = ''

    if file_extension == 'csv':
        df = pd.read_csv(file_path)
    elif file_extension == 'txt':
        with open(file_path, 'r') as file:
            content = file.read()
        df = pd.DataFrame({'Column1': [content]})
    else:
        df = pd.read_excel(file_path)
    
    df['Column1'] = df.apply(lambda row: ' '.join(row.dropna().astype(str)), axis=1)
    df = df[['Column1']]
    return df

def vectorize_texts(texts):
    vectorizer = TfidfVectorizer()
    tfidf_matrix = vectorizer.fit_transform(texts)
    return tfidf_matrix, vectorizer

def train_and_save_model(texts_to_analyze, file_name='model.pkl', model=None):
    
    analyzed_texts = keitaiso(texts_to_analyze)
    
    vectorizer = TfidfVectorizer()
    tfidf_matrix = vectorizer.fit_transform(analyzed_texts)
    print('shape ->',tfidf_matrix.shape)
    print(tfidf_matrix)
    print(tfidf_matrix.toarray())

    with open(file_name, 'wb') as file:
        pickle.dump((vectorizer, tfidf_matrix, model), file)

def load_model(file_name='model.pkl'):
    with open(file_name, 'rb') as file:
        vectorizer, tfidf_matrix, model = pickle.load(file)
    return vectorizer, tfidf_matrix, model

def predict_with_model(query_text, vectorizer, tfidf_matrix, df, top_n=1):
    query_analyzed_text = keitaiso([query_text])[0]
    closest_entries, valid_words = find_closest_entries(query_analyzed_text, tfidf_matrix, vectorizer, df, top_n=top_n)
    
    if valid_words:
        print(f"Valid words in query: {', '.join(valid_words)}")
    
    return closest_entries

def find_closest_entries(query, tfidf_matrix, vectorizer, df, top_n=1, top_words=10):
    query_vec = vectorizer.transform([query])
    cosine_similarities = cosine_similarity(query_vec, tfidf_matrix).flatten()
    top_n_indices = np.argsort(-cosine_similarities)[:top_n]
    top_n_similarities = cosine_similarities[top_n_indices]
    closest_entries = df.iloc[top_n_indices]

    # 関連する単語の抽出
    feature_names = vectorizer.get_feature_names_out()
    related_words = []
    for idx in top_n_indices:
        entry_vec = tfidf_matrix[idx].toarray().flatten()
        top_word_indices = np.argsort(-entry_vec)[:top_words]
        top_words_list = [feature_names[i] for i in top_word_indices]
        related_words.append(top_words_list)

    # 有効な単語の抽出
    query_words = query.split()
    valid_words = [word for word in query_words if word in feature_names]

    results = [(entry.Column1, similarity, words)
               for entry, similarity, words in zip(closest_entries.itertuples(index=False), top_n_similarities, related_words)]

    return results, valid_words

```

# 生成ＡＩ回答

### GPT4
>明日の計画:
食事計画:

朝食: ありあわせの「肉気と野菜」を使ってシンプルな朝食を準備。
昼食: 「米、食べようかな」と思っていたので、玄米を炊いて健康的なランチを楽しむ。
夕食: 夕方のウォーキングの後で、軽く食事を取る予定。
>アクティビティ:

登山の代わりに朝のウォーキングを行う。天気が良ければ、近くの公園まで足を伸ばす。
洗濯はすでに終了しているため、明日は家の掃除に集中。
> その他のタスク:

台所の改善のために、「スポンジに洗剤を自動でつける」システムを設置する計画を検討。
英語のレッスンがなくなったため、自習で語学の勉強をする時間を作る。
「肩こりと運動不足」に対処するために、夕方に短いストレッチングセッションを行い、「節約」にも注意しながら生活の質を高める。

このプランは、指示の内容を元に具体的な日常活動に結びつけ、実行可能なアクションプランとして整理しました。


### Gemini
>明日の計画

食事
朝食: 米と、肉と野菜を使った料理
昼食: 外出先で
夕食: 自宅で軽食
運動
午前中: ウォーキング
その他
自動で洗剤をつけるスポンジについて調べる
> 検討中のタスク

冷蔵庫の購入 (予算2万円)
電子レンジの購入 (予算8千円)
JIRAチケットからの情報
完了済み: 洗濯
不要になった: 英語のレッスン
課題: 肩こり、運動不足、節約

> 関連する過去のタスク
タスク #181: 節約: 節約に関するタスク。冷蔵庫、電子レンジの購入はここに関連する可能性があります。
タスク #270: フードマネジメント: 食事に関するタスク。今日の食事の計画に役立つかもしれません。

>その他の情報

チケットに記載されている「自動で洗剤をつけるスポンジ」は、具体的な製品名や購入場所が不明なため、明日改めて調査が必要です。
注意: この計画はチケットの内容と過去のタスクに基づいて作成されています。状況に応じて変更が必要な場合があります。

### claude3

>はい、以下のようにAI assistantとして自由作文を作成しました。指示内容と類似した内容を太字で示しています。

明日の計画を立てよう
朝は肉気と野菜を使って、スープを作ろうと思います。スープにすれば野菜もたっぷり取れて健康的ですね。お昼は米を食べようかな。炊きたてのご飯にお味噌汁があれば、それだけでも幸せな気分になれます。
午後は登山の予定を変更してウォーキングに行こうと思います。近所の公園を1時間ほどゆっくり歩いて、自然を楽しみたいです。洗濯は今日中に終わらせておきましたので、明日は外出する時間が取れそうです。
ウォーキングの後は、部屋の掃除をしようと思います。特に気になるのがキッチンのスポンジです。洗剤を自動でつけられたら便利だなーなんて考えています。いつか IoT 化されたスポンジが開発されるのを楽しみにしています。
夜は語学の勉強をする予定でしたが、英語のレッスンが無くなってしまいました。でも、独学で勉強を続けようと思います。運動不足と感じているので、筋トレに時間を当てるのもいいかもしれません。節約のためにジムには通わず、自宅で行うつもりです。肩こりの改善にもなればいいな。
明日はこんな感じで過ごそうと思います。健康面と生活面のバランスを考えつつ、充実した1日を過ごせたら嬉しいです。

# まとめ

いくつか反省点はあるが、とりあえずシステムは完成した。
なんとなく、ＲＡＧのデータが無くても良いような気もする。

とにかく楽になればよい

明日はどれかの指示に従って一日過ごしてみようと思う。

# 今後の予定

* 天気予報については調べて手入力　→　将来的にはＡＰＩで引っ張ってくる
* 家計簿データとの連携　→　REDMINEの作業時間としてGPTに転記させる