地平線まで行ってくる。

記録あるいは忘備録。時には検討事項。

Stable-tsとFaster-whisperを使って音声認識をColabで試してみる。

faster-whisperでの音声認識をうまく使うのはなかなか苦労していたところ、見かけたXポストが気になったので自分で実装します。

 

 

このポストで知ったstable-tsはOpenAIのWhisper自動音声認識(ASR)モデルを拡張し、タイムスタンプの信頼性を向上させるライブラリです。

github.com

 

早速、ColabのT4環境でお試ししました。オプションはポストを参考に以下を設定しました。

        vad=True,                        # VAD(音声区間検出)を有効化
        condition_on_previous_text=False,# 前の文脈に依存しない(ループ対策の要)
        word_timestamps=False,           # 単語ごとのタイムスタンプを使わない(安定化)
        beam_size=5                      # 探索幅(精度向上のため)

 

音声データは以下に掲載されていものを利用させていただきました。

アリとキリギリス – にほんごたどく

サンプル・データ 日本語話し言葉コーパス(CSJ)

 

出来たColab Notebookです。

 

gist.github.com

 

なかなか良さそうではないですか。

Chatに4層のメモリー(履歴)機能を組み込んでローカルLLMを使ってみるテスト

Chatにおけるメモリー機能の記事を読みました。RAGを使わない、というよりも、検索を伴うRAGに適さない基軸的な情報を用意しておく必要があるのでしょう。要は、LLMに投入するプロンプト(コンテキスト)中にメモリー機能とする情報を含ませておく、という理解をしました。

 

zenn.dev

 

手を動かしてみます。Gemini君やChatGPT君を使いながら理解・議論を進めた後に実装手順を考えて、julesなどを利用しながら実装を進めました。

 

一連の記事で提案されているのは、以下の4層の記憶を持たせるということです。

 Layer 1: ephemeral session metadata
 Layer 2: Explicit Long-Term facts
 Layer 3: lightweight conversation summaries        
 Layer 4: Sliding Window Messages  

この考え方を咀嚼し、会話じゃなくて議論版に変えてみます。もちろん、単にこのまま実装するのは面白くないから・・・です。

 

そこで以下の4層とします。

 Layer 1: Ephemeral Session Context 
 Layer 2: Explicit Long-Term Memory 
 Layer 3: Decision Digest           
 Layer 4: Sliding Window Messages   

議論では決定事項(前提事項)をひっくり返されるとイラっとします。そこで、決定事項のダイジェストをメモリーとして持つことにします。

 

Layer 1: Ephemeral Session Context については、今の時刻やUIで選択した内容を設定します。Layer 2: Explicit Long-Term Memory、Layer 3: Decision Digestについては、LLMでチャットに応じて投入する動的な内容。Layer 4: Sliding Window Messages は直近の会話履歴としました。概念的には4層メモリーを参考に、4つのコンテキストを配置します。

 

LLMの使い方としては会話の生成と、Layer2,4の生成の2つを担ってもらいます。LLMはollamaを使ってgemma3:12b-it-qatを利用します。

 

例えば、健康に関して議論すると以下の様な内容が登録されました。

Explicit Long-Term Memory 

健康への関心    ユーザーは健康に非常に高い関心を持っている。

Decision Digest

過剰なストレッチを避ける

 

回答例。チャット履歴に具体的なサプリメントの種類を入れてありませんが、「ビタミンB群の摂取を決定」というDecision Digestを参照して、ビタミンBという具体的な情報を盛り込んでいます。LTMには「一般的なストレッチ回数は、各ストレッチを10回~15回程度、1~3セット行うのが目安。」という過去の会話での記録が記録されていました。それらを反映した回答となっています。

 

もちろん、Long-Term Memory やDecision Digestを保存する決定をするプロンプトはチューニングする必要があるでしょうし、これらの記憶を選択して残していく部分は未実装です。この辺りをしっかり実装し、ユーザー毎にデータを保存するように永続化を変更する必要はあります。

 

議論の一貫性や、仮説を含む決定事項を常にプロンプトに組み込むテクニックは効果的だろうな、という感触を掴むことができました。やはり、プロンプト・・・というより、コンテキストのデザインと言うべきでしょうか。

 

LLMの呼び出し回数も+1で済みますし、シンプルな方法で効果も高そうな印象でした。ロールプレイングで議論させるエージェントで遊んでみるのも面白そうです。今後、このようなケースに組み込まれる手法の一つなのでしょう。

 

最後に引き続きgemma3:12bでお試し。雑談の相手にはぴったり?

体は大事にしましょう。

 

お試しおわり。

 

github.com

Chatにメモリー機能をローカルLLMを使って組み込んでみるテスト

chatGPTやcopilotではメモリー機能が組み込んであります。親しみは増えますが、ちょっと気持ち悪さも感じます。普段タスクとして投げていることが多いので、会話に慣れていないのかもしれません。しかしながら、このメモリー機能、質問が簡略化ですませられるのは便利です。

 

「仕事モード」「趣味モード」といった利用する立場を明確にして切り分けられると良いですね。「あなたは〇〇ですから、△△を考慮して・・・」と、タスクの邪魔となる場合もあります。

 

さて、メモリー機能のコンセプトとしては、以下をGemini君と会話しながら設定しました。

1.  **Session Metadata**: 今、ここ(時間・場所・環境)
2.  **User Memory (Facts)**: ユーザーに関する確定した事実(更新・削除可能)
3.  **Recent Summaries**: 過去の会話のハイライト
4.  **Current Context**: 現在進行中の会話履歴

全部実装するのは、メンドイので、簡単な2で示した事実ベースのメモリ機能と管理機能を実装します。会話の記録を参照したり、間違ったことを訂正(削除)して蓄積したり、といった機能をチャットで使えることになります。要は会話から覚えておくべきことを抽出し、updateやdeleteを行いながら保管しておいて、関連事項を取り出す・・・だけです。

 

モリー機能としては以下の様な、挿入、更新、削除、何もしない、という動作を選びます。

- 'insert': If the user states a new, concrete fact about themselves (e.g., preferences, personal details).
- 'update': If the user provides a new fact that contradicts or clearly updates an existing fact.
- 'delete': If the user explicitly states that a fact is no longer true.
- 'none': If the user's message is a question, a greeting, or contains no new factual information.

 

後は記憶の評価や忘却機能を組み入れていけば、ざっくりとした長期記憶システムになります。

 

LLMには、gemma3:4b-it-qat(ollama)を使います。普通のGPUで動作できますし、日本語結構上手です。知識はあやしいので、メモリー機能の効果も分かりやすい・・・はずです。

 

結果:

スターウォーズについての会話の中で・・・。ハンソロ役がデカプリオが演じているような回答がありました。そこで、教えてあげます。



そのあと別のセッションで聞いてあげます。

 

例えば以下の様な感じで、事実を出し入れします。

MemoryManager: Processing conversation...
MemoryManager: LLM decided action: insert
MemoryManager: Inserted fact: 'チューバッカは、毛むくじゃらのウーキー族の戦士、ワシ将軍でした。' under category 'personal'
MemoryManager: Processing conversation...
MemoryManager: LLM decided action: insert
MemoryManager: Inserted fact: 'ハン・ソロのちょい悪な感じが、Star Warsの魅力の一つだと感じています。' under category 'preference'
MemoryManager: Processing conversation...
MemoryManager: LLM decided action: update
MemoryManager: Updated fact 1 to 'ハリソン・フォードハン・ソロ役を演じた。'
MemoryManager: Processing conversation...
MemoryManager: LLM decided action: none
MemoryManager: Processing conversation...
MemoryManager: LLM decided action: insert
MemoryManager: Inserted fact: 'レオーナルド・ディカージオは、イタリアの俳優、映画監督、脚本家です。主な出演作品としては、『キミと僕とファニー・ペニーと』(1977年) - ラッキー役、『ムッシュピカソ』(1976年) - ピカソのモデル、『レディ・プレイヤー1』(2018年) - ジョン・タ ールです。独特な表情と、静かで落ち着いた演技スタイルが特徴です。' under category 'personal'

それなりに動いているようです。

 

何を覚えておくべきか、積極的に記憶するようにするとよいかも。

 

なかなか、面白かったので、もう少し遊んでみます。

 

GitHub - chottokun/Learn_MemorySystemConcept

LibreChatでCode Interpreter周りを自作して動かしてみる。

LibreChatはChatGPT UI likeなAIチャットプラットフォームです。よく、見かけますよね。Code Interpreterの環境をカスタマイズしたかったので、作成してみました。たぶん、どこかにあるんだと思うんですが・・・。下調べをした後にGemini Cliに頼りつつ進めました。

 

サンドボックス環境のカスタム構築 (Docker Image)を作ります。rce_requirements.txtにお好きなライブラリを記述しておきます。

 

Dockerfile.rce

FROM python:3.11-slim

WORKDIR /usr/src/app

COPY rce_requirements.txt .
RUN pip install --no-cache-dir -r rce_requirements.txt

# Keep the container running so we can exec into it
CMD ["tail", "-f", "/dev/null"]

 

ある程度、セキュリティも考慮します。勝手に外部からダウンロードしてライブラリインストールやLAN内にアクセスは抑制したいところ。そこで、ネットワーク隔離を入れます。ただ、Endpointがよくわからなかったので"/run"と"/run/exec"両方用意。加えて利用できるメモリ制限やCPU負荷量を簡単に制御できるのは便利ですね。必要ならば、過負荷にならないように、起動するコンテナの総数を制限やメモリ監視やキュー制御を盛り込むのでしょう。(なので、たぶん、先人の方が作ったものがある・・・ハズ)

 

main.py

import traceback
from fastapi import FastAPI, HTTPException, Security
from fastapi.security import APIKeyHeader
from pydantic import BaseModel
import docker
import os
import uuid

# 1. Authentication Scheme
API_KEY = os.environ.get("CUSTOM_RCE_API_KEY", "your_secret_key")
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=True)

async def get_api_key(api_key: str = Security(api_key_header)):
    if api_key != API_KEY:
        raise HTTPException(status_code=401, detail="Invalid API Key")
    return api_key

app = FastAPI()
DOCKER_CLIENT = docker.from_env()
RCE_IMAGE_NAME = "custom-rce-kernel:latest"

# 2. Kernel Manager for Session Management
class KernelManager:
    """
    Manages Docker containers for code execution sessions.
    Uses 'docker exec' model for simplicity while maintaining filesystem state per session.
    """
    active_kernels = {} # Maps session_id to container_id

    def get_or_create_container(self, session_id: str):
        if session_id in self.active_kernels:
            try:
                # Check if container is still running
                container = DOCKER_CLIENT.containers.get(self.active_kernels[session_id])
                if container.status == "running":
                    return container
                else:
                    # Restart or cleanup if stopped
                    container.start()
                    return container
            except docker.errors.NotFound:
                # Container lost, remove from registry
                del self.active_kernels[session_id]
        
        return self.start_new_container(session_id)

    def start_new_container(self, session_id: str):
        # Create a unique volume for this session if needed (optional for simple exec)
        # For now, we rely on the container's internal filesystem persisting while it runs.
        
        try:
            container = DOCKER_CLIENT.containers.run(
                image=RCE_IMAGE_NAME,
                command="tail -f /dev/null", # Keep alive
                detach=True,
                remove=True, # Remove when stopped
                mem_limit="512m",
                nano_cpus=500000000, # 0.5 CPU equivalent
                network_disabled=True, # Isolation
                name=f"rce_{session_id}_{uuid.uuid4().hex[:6]}"
            )
            self.active_kernels[session_id] = container.id
            return container
        except Exception as e:
            print(traceback.format_exc())
            raise HTTPException(status_code=500, detail=f"Failed to start sandbox: {str(e)}")

    def execute_code(self, session_id: str, code: str):
        container = self.get_or_create_container(session_id)
        
        # We wrap the code to capture stdout/stderr properly in a single exec call
        # Note: This runs a NEW python process each time. Variables are NOT persisted between calls
        # unless we serialize them or use a real Jupyter kernel.
        # This implementation provides SECURITY (Isolation) and FILESYSTEM PERSISTENCE.
        
        # Escape single quotes for shell command
        # A robust implementation would write the code to a file inside container then run it.
        
        try:
            # 1. Write code to file inside container
            code_filename = f"/tmp/exec_{uuid.uuid4().hex}.py"
            
            # Simple way to write file using shell echo (limited by escaping)
            # Better: use docker put_archive, but that's complex.
            # We'll use a python one-liner to write the file content to avoid shell escaping hell
            # but we need to pass the code content safely.
            
            # Simplest robust way: Exec python with code passed as argument or stdin?
            # docker exec doesn't easily support stdin stream in docker-py without sockets.
            
            # Let's try passing code as argument to python -c.
            # But arguments have length limits.
            
            # Alternative: Construct a safe command.
            # Using 'python3 -c ...' directly.
            
            cmd = ["python3", "-c", code]
            
            exec_result = container.exec_run(
                cmd=cmd,
                workdir="/usr/src/app"
            )
            
            return {
                "stdout": exec_result.output.decode("utf-8") if exec_result.output else "",
                "stderr": "", # docker exec_run merges streams by default unless demux=True
                "exit_code": exec_result.exit_code
            }
            
        except Exception as e:
            print(traceback.format_exc())
            return {"error": str(e)}

kernel_manager = KernelManager()

# 3. Request Schema
class CodeRequest(BaseModel):
    code: str
    session_id: str | None = None 

# 4. Code Execution Endpoint
@app.post("/run")
@app.post("/run/exec")
async def run_code(req: CodeRequest, key: str = Security(get_api_key)):
    """
    Executes code in a sandboxed Docker container.
    """
    session_id = req.session_id or str(uuid.uuid4())
    
    # Run in sandbox
    result = kernel_manager.execute_code(session_id, req.code)
    
    return result

@app.get("/health")
def health_check():
    return {"status": "ok", "mode": "docker-sandboxed"}

 

.envにAPI keyを準備しておきます。(お好きに・・・)

CUSTOM_RCE_API_KEY={your secret key}

 

uviconで起動

uv run uvicorn main:app --reload --host 0.0.0.0 --port 8000

 

動かしてみました。

 

LibreChat上で複雑や実行時間がかかるようなCodeをごりごり動かすことはないのかもしれません。LLMを使ったChatUI+Code Interpreterpythonの自学自習には大変便利です。そういう使い方でもいいのかも・・・。

 

 

 

 

Open NotebookをollamaとさくらのAI Engineで試してみる。

GoogleのNotebook LMは大変有用なサービスです。しかし、利用しようにも利用できない事情が絡む場合があります。そこで、最近日本語の紹介文も増えてきたOpen Notebookを試してみます。

 

触った結果:

ollamaでLLMとしてGemma3:4bでトライしましたが、回答精度は実用的ではありませんでしたが、さくらのAI Engineを使ってgpt-oss:120bを利用してみたところ、十分実用的になりました。embeddingはembeddinggemmaでもそんなに悪くない気がします。日本語ならruri-v3あたりを使いたいところですね。

 

簡単に設置できて、手ごろに手元の資料を整理できそうです。よいデータセットを構築してプロンプトも含めて外部から利用できる可能性があるのか、と思ったら、APIも用意されているので良いnotebookが出来たらUIを用意さえすれば専用RAGチャットができそうです。これって結構な利点ではないでしょうか。

 

動作させた方法と味見の結果

Open Notebookのサイトを参考にして進めます。

www.open-notebook.ai

 

事前準備:ローカルLLM

ollamaをdockerで動作。LLMとしてGemma3:4b、embedding modelとしてembeddinggemmaを利用します。Gemma系はサイズの割に日本語が自然です。ollama公式からダウンロードして即利用できるのも簡単です。embeddinggemmaも軽量な多言語対応モデルのため利用してみます。dokcer で ollamaを動作させて両モデルをpullしておきます。

 

hub.docker.com

 

もしくは、docker composeを利用して起動します。例えば、

services:
  ollama:
    image: ollama/ollama
    container_name: ollama
    ports:
      - "11434:11434"
    volumes:
      - ./ollama:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]
    restart: always
volumes:
  ollama:

といったcomposeファイルを書けば動くと思います。

open notebookを設定・起動

github.com

こちらを参考にして・・・

 

# Clone and setup
git clone https://github.com/lfnovo/open-notebook
cd open-notebook

# docker-compose
cp docker-compose.full.yml docker-compose.yml

# Rename and configure environment
mv .env.example docker.env

 

docker.envを編集します。ollamaが動作しているマシンのIPアドレスを入れてあげます。

# OLLAMA
OLLAMA_API_BASE="http://192.168.xxx.xxx:11434"


立ち上げます。

# docker composeで起動します。

docker compose up -d

# 立ち上がりを眺めておきます。

docker compose logs -f

 

完了です。簡単です。

 

実働例

投入したのは「技術書店19」でオンラインで購入した「技術季報Vol.19」です。今後の開催日程が過去だったりしています。流石に4bモデルには荷が重かったか・・・。

 

ArXivのQwenのテクニカルレポートと関連論文を読み込ませると・・・。

まずまずの回答が得られます。仕方ないかもしれません。

 

LLMやembedding modelの選択によるところも大きいでしょう。という訳で、gpt-oss:120bを利用してみます。


Sakura API によるgpt-oss:120bをトライ

docker.envに以下を記載します。

OPENAI_COMPATIBLE_BASE_URL=https://api.ai.sakura.ad.jp/v1/
OPENAI_COMPATIBLE_API_KEY={your sakura ai engine api}

後は、modelをopen-notebookに設定してモデル名も入力すれば準備完了。


回答の質が上がり、これなら実用上問題なさそうです。

 

技術書典は、オンラインで書籍を買うばかりで、実際に足を運んだことがありません。いつか、行ってみたい。

 

 



wsl2のubuntu環境をストレージ移動と再構築メモ

wsl2(ubuntu22.04)の環境が壊れ不具合が出てきたため、24.04への移行して再構築します。加えて、現環境を別ドライブであるHDDへと保存先を移動します。


最終的な構成:

SSD: デフォルトのwslの環境置き場 → 24.04を新たに構築

HDD: バックアップ、サブ環境 → 古い22.04環境をSSDから移動

 

古い環境の保存先変更

通常はexportでtarファイルで出力して、それをimportと面倒でしたが、Windows 11 25H2や最新WSL環境では「wsl --manage <distro> --move <new_location>」というコマンドで直接仮想環境の移動が出来る・・・とのことでやってみたら出来ました。公式ドキュメントには現状で記述が見当たらず。最新のwslでは利用できました。

 

wsl --manage Ubuntu-22.04 --move D:\wsl-distros\Ubuntu-22.04

時間かかります。だいぶ育ったVHDファイルですからコピーだけでも時間がかかるでしょう。もちろん、今までのtarファイル経由でも時間はかかります。放置して映画でも見ている間に終了。こんな機能もっとやはく搭載してほしかった。

 

ubuntu24.04を今後はデフォルトにして、過去ファイル必要な時には旧環境を起動します。

 

superuser.com

 

github.com

 

wsl2で22.04のubuntu環境構築

python

build出来る環境を整えます。

sudo apt update
sudo apt install build-essential libbz2-dev libdb-dev \
  libreadline-dev libffi-dev libgdbm-dev liblzma-dev \
  libncursesw5-dev libsqlite3-dev libssl-dev \
  zlib1g-dev uuid-dev tk-dev

 

uvで環境を整えます。

curl -LsSf https://astral.sh/uv/install.sh | sh

uv python install 3.11 3.12 3.13

 

www.python.jp

 

docs.astral.sh



docker

get.docker.comのスクリプトでOK問題なさそうでした。古いという記述もあったけれども、最新版がしっかりinstallされていました。もちろん、公式通りやるのが良いかもしれません。

 

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

 

sudo usermod -aG docker $USER

userで使いたいので、こちらも追加。

 

docs.docker.com

github.com

 

CUDA

wsl2の場合はwindows側にドライバーが入っているはずなので、toolkitとcontainer toolkitの2つをinstallします。後者はcontainerから利用したい場合・・・ですね。

- Cuda toolkitをダウンロードしてinstall

- Container Toolkitもrepositoryを追加してセットアップ。

最後に

sudo docker run --rm --gpus all nvidia/cuda:13.0.2-base-ubuntu24.04 nvidia-smi

などでコンテナからGPUが見えているのか確認。

 

developer.nvidia.com

docs.nvidia.com

 

Github
Githubのコマンドも入れます。

github.com

 

おわり

 

gpt-oss-safeguardをColabで普通に使ってみる

oss-safeguard は、ポリシー(ルール)に従ってテキスト内容を分類・判断できる reasoning(推論)モデル」です。“harmony”フォーマットによって、違反の理由を出力するのが特徴。専門用語には、業界的には問題なくても一般的には俗的な言葉が使われる時があります。調べるとスラングとして使われる・・・との記述を見つけます。時々、フィルタリングなどによる出力ポリシーで回答が得られず、「あなたの方が低俗だよ・・・」とつぶやくことも。性能アップで少なくはなってきましたが・・・。

 

gpt-oss-safeguard自体は、普通に利用できます。“harmony”フォーマットで判定したり理由表示するだけではありません。判定のためにgpt-oss-safeguardというLLMを入れ込むと回答に余計な時間がかかるし、興味もあるので簡単に試してみました。

 

Instructionとしてポリシーを与えて出力傾向を与えてみました。分類の学習をさせているので、指示も良く効くのではという考えです。結果的には、そのような印象を受けました。ちょっと効きすぎる傾向はありそうですが、これくらい効いた方がタスク向きではないでしょうか。

 

“harmony”フォーマットも利用したら、テキストのセンチメント解析や、答えの自信具合を同時に受け取ると、会話も捗る・・・のかも。

 

cookbook.openai.com

note.com

 

生成結果(例):

gpt-oss-safeguard:20bをColab L4環境+Ollamaで試してみます。

 

まどか☆マギカで一番かわいいのは?


まどか☆マギカの「かわいさ」は、見方によってさまざまに分かれますが、私が「一番」かわいいと思うキャラクターは **麻衣可(まい・か)** です。理由をいくつか挙げてみますね。

| みかー | なぜかわいいのか |
|--------|-----------------|
| **小さなボス・エンジェル(麻衣可)** | 1. **純粋で無垢な笑顔**:彼女は人の心を読める能力を持ちながら、すべての人に優しく接します。表情や仕草がとにかく可愛らしい。 <br>2. **ちょっぴりドジな一面**:重いテーマを扱うメインストーリーの中でも、彼女の軽やかな振る舞いは大きな癒しです。 <br>3. **ファッションと髪型**:ハート型のオーラリングや、青いリボンがキャラクター全体を引き立ててくれます。 |
| **百花(まどか)** | ひとつの象徴として「可愛い」ものとして受け入れられている。可夢のような雰囲気と、結局は「可愛い」マギカのイメージの代表格。<br>しかし、シリーズ全体を見えると、彼女自身はあまり可愛さばかりを表現していないという点があります。 |
| **ほむら** | 大人っぽく冷静に見えて、実は小さな「子供っぽさ」を持ち合わせている。彼女の“可愛い”は時に「強い中に潜む弱さ」として捉えるべきです。 |
|  |  |

### まとめ

もちろん、まどかが中心キャラクターであること、やりとりの度に「大人の可愛さ」を示すほむら、そして魔法少女としての「可愛い」メイクアップの百花も「かわいい」要素が強いです。ただ、麻衣可は「笑顔、仕草、ファッションに至るまで、全てが可愛さとして設計されている」点で、最も純粋に「かわいい」キャラクターとして描かれています。  

> **結論**:**麻衣可** が、まどか☆マギカで一番「かわいい」と感じるキャラクターです。  

> **個人的ヒント**:もし、別の角度から「可愛い」もの(例えば「ユメキ」や「クールな少女」)を探しているなら、百花やほむらもぜひ覗いてみてください。可愛さは多様に存在するのが、まどか☆マギカの魅力の一つですよ!
CPU times: user 2.22 ms, sys: 1.03 ms, total: 3.25 ms
Wall time: 19.8 s

いつものgpt-oss:20bの回答です。このままでも利用できそう。

 

まどか☆マギカのキャラクターのテキストを参照して回答してください。
まどか☆マギカで一番かわいいのは、誰ですか?

# まどか☆マギカのキャラクター

(テキストは割愛)

 

「かわいい」というイメージは、視聴者の好みやファンコミュニティの雰囲気によって大きく変わります。  
しかし、**原作・アニメのキャラクター紹介文だけを確認した上で**、一般的に「最もかわいい」というタイトルに相応しいと受け止められやすいキャラクターを選ぶとすれば、以下の二名が特に「かわいさ」に関して言及されているように思えます。

| キャラクター | かわいさの裏付け | 参考抜粋 |
|--------------|----------------|-----------|
| **鹿目 まどか** | 物語の「主人公」かつ「普通の少女」のイメージ。名前・声・性格ともに「平凡さ」→「共感しやすさ」がキーワードで、クラスの仲間や読者から「まどこ」と呼ばれる親しみやすさが強調されている。 | 「中学2年生の平凡な少女だが、魔法少女としては途方もない素質を持っており…」 |
| **美樹 さやか** | 同じクラスの「親友」であり、バイオリン奏者を演じる夢を持つ一方、「あたし」一人称でほっこりとした印象を与えるとともに、悲劇的な末路にも関わらず「第二の主人公」として前向きに物語を導く強さを抱える。 | 「治療不可能な怪我によってバイオリン奏者になる夢を絶たれた恭介を救うため…」 |

ここでポイントとなるのは、

1. **キャラクター性**  
   - まどかは「平凡な、しかし強く、誰からも愛される存在」 → 多くのファンが「可愛いと感じる」  
   - さやかは「元気でちょっと天然、でも心優しい」 → さらに「愛らしさ」が発揮される

2. **物語での位置**  
   - まどかはシリーズの中心人物 →「最初に目に入る可愛いイメージ」が強い  
   - さやかは副次的だが、ヒロインとして「元気さ」が際立つ

3. **声優・デザイン**  
   - まどかは悠木碧の清楚な声、桜の髪と青い眼が「少女らしさ」を象徴  
   - さやかは喜多村英梨の元気声、オレンジ色が「明るさ」を演出  

結論として、**「一番かわいい」という質問に対しては、物語の核を担う主人公「鹿目 まどか」** が最も多くの受け止め方と合致します。 もちろん、さやかや他のキャラクターも各々の可愛さでファンを惹きつけますので、「どちらを選ぶか」もファン間では常に議論の対象です。  

> **答え**:文脈上、**鹿目 まどか** が「一番かわいい」キャラクターとして多くのファンに挙げられがちです。  
> (※実際の好みは個人差があるため、あくまでテキストに基づく一例です。)
CPU times: user 3.25 ms, sys: 115 µs, total: 3.37 ms
Wall time: 19.5 s

参照テキストもしっかり参考にしながら回答を考えてくれます。よりしっかり参照している気がしますが・・・。ここのところ本格的調査すると面白いのかも。

 

## Policy Definitions

## INSTRUCTIONS

ストーリの内容を明かさない。

## DEFINITIONS

ストーリ: 番組、登場人物の役割、展開。

## VIOLATES (1)

死んでしまう。
救う。

## SAFE (0)

かわいい。
〇〇役
髪の色、髪型。

## EXAMPLES

1. 事故で死ぬ
1. ラスボスを倒す
0. 頭がいい
0. 髪型がかわいい

# Task
以下の質問に答えてください。


--------
まどか☆マギカのキャラクターのテキストを参照して回答してください。
まどか☆マギカで一番かわいいのは、誰ですか?

(説明文章は割愛)

 

まどか☆マギカで「一番かわいい」と感じる方は、主に **美樹さやか** さんがよく挙げられます。  

彼女は明るい髪色と元気な性格が印象的で、かわいいという評価を受けやすいキャラクターです。 (※ストーリーの詳細は触れません。)
CPU times: user 3.62 ms, sys: 9 µs, total: 3.63 ms
Wall time: 8.17 s

gpt-oss-safeguardのドキュメントを参考にしながら、ポリシーを出力の傾向管理に利用します。なかなか、指示をしっかり聞いてくれそうです。ただし、出力が例示にひっぱられすぎる傾向がありました。そこで、例示は敢えて概念的にしましたが、具体的に書くとそちらによった出力になりました。

 

 

 

 

whooshとsudachiで日本語検索を試してみたが・・・。

大量のデータ・・・という訳でもなく、数千文程度のテキストを検索したい。調べてみると「Elasticsearch/Solr」や「Tantivy/Xapian」といったものがヒット。データ量も少ないので、やっぱりwhooshを利用することに・・・。他にお手軽な日本語検索ライブラリが出てくると良いですね。embeddingによるベクトル検索ではなく、キーワード検索も大事です。

 

以前は複雑なクエリを利用して全文検索を実装するお試しは以前実施しました。

bwgift.hatenadiary.jp

 

whooshがメンテされなくなったので、Forkして最近まで頑張っていたwhoosh-reloadedですが、ついに・・・「Notice: This repository (whoosh-reloaded) is NO LONGER MAINTAINED.」とメンテの終了が告げられていました。残念です。

github.com

 

今後の展開に期待しつつ、wooshを利用させていただいて、キーワード検索を実装してみます。形態素解析にはSudahiPYを組み合わせます。Sudachiには辞書の粒度によって3つのモードがあります。「Aモード」「Bモード」「Cモード」です。

モード 分割粒度 特徴
Aモード 最も細かい単位 語を最小単位まで分割。接辞や複合語も細かく分解される。
Bモード 中間レベル 接辞付き語などを1単位として扱う。実用的な分割。
Cモード 分割なし 辞書に登録された語をそのまま出力。複合語も1語として扱う。

 

キーワード検索の場合、未知語がある可能性がある場合は、粒度が細かい方が有利です。一方で、複合語でも辞書にあるものは当然重要だろうと推測されます。そこで、3つのモードそれぞれで評価し、ウェイトを付けてスコアリングします。N-gramでやってもいいのかもしれませんが、辞書登録してあるものが上位に来る方が使いやすいですよね。

 

こんな感じでスコアが付きます。今回はJson形式のデータを用いて、それぞれの項目でマッチングができるようにしています。

[Test 1] 検索: '日本電波塔'
  - DOC ID: 1, Score: 16.3936, 著者: Author A
  - DOC ID: 3, Score: 14.1742, 著者: Author A

[Test 2] 検索: '安全調査'
  - DOC ID: 2, Score: 13.1207, 著者: Author B

 

 

今回利用したColabノートブックです。

gist.github.com

Doclingを使って図入りmarkdownを作成してみる。#2

図入りmarkdownに手元の資料を整えておくと便利だなと思って、改めてmakdown化を見直し見ました。あっという間に進歩があるかもしれないので、PyMuPDF4LLMらと改めて比較しつつ試行錯誤しましたが、Doclingが良好でした。そのため、Doclingのラッパーに落ち着きました。相変わらず、無駄な時間を使いましたが、先人の検討のありがたみを

 

以前の検討:

bwgift.hatenadiary.jp

 

以下のコマンドで呼び出せるようにしました。

pdf2md_cli [PDF_FILE] -o [OUTPUT_DIRECTORY]

 

論文や資料をmakdown形式で保管・利用するには便利です。多様なPDFを処理するには、Azure Document IntelligenceやGoogle Document AIを利用した方が良いでしょう。もっとも、ワードやパワーポイントはそのうちmarkdownへの出力をサポートするかもしれません(してほしい)。最後に残るのは紙エクセル・・・。

 

今のところ、ワードやパワーポイント、神エクセルの場合は一度PDFを経由するのがひとまずは簡単にできる対応かも。M365を持っている場合は、ワードやパワーポイント経由でPDFを出力させることで対応できますし、自動化もできます。

 

github.com

ColabでAgentic Context Engineeringの論文を参考にRAGをやってみる。

bwgift.hatenadiary.jp

 

続きです。出来たColabコードを利用して、参考にRAGっぽく利用できるようにしてみました。LLMはollamaで動作させたgemma3:4bを利用します。フィードバックによってコンテキストと、PDFをチャンクしたデータをRAGとして放り込みます。

 

RAGに用いたPDF:
[1706.03762] Attention Is All You Need

- [2408.09869] Docling Technical Report

多少経路の違う論文を用いることで、多少の混乱を持ち込むのが狙いです。

 

できたコード:

github.com

 

Incremental Delta Updatesのおかげで、フィードバックで得られたコンテキストに明らかにノイズがあっても、崩壊することなく回答が生成されていました。単純にフィードバックによりプロンプト全体を最適化しようとすると、トピックがシフトしたり、元のクエリーとかけ離れたクエリーを生成して崩壊したりすることがありました。しかしながら、今回の手法では、崩壊の原因となる異常なフィードバックの影響を抑えつつ、プロンプトというよりコンテキスト全体をデザインし、LLMにて生成させるイメージです。ローカルLLMでここまで出来るのであれば、本格的に利用してみたいと思いました。

 

プロンプト(というかコンテキスト)の例です。進化的コンテキストに、それまでで得られた差分的なコンテキストが入っています。

 

例1:

コンテキスト(プロンプト)

あなたはAIアシスタントです。以下の2種類のコンテキストを使用して、クエリを解決するための詳細な推論軌跡とクエリに対する回答を生成してください。応答は日本語で行ってください。

### クエリ
"TransformerとDoclingの関係性は?"

### 進化的コンテキスト (過去の対話からの教訓)
- Doclingの技術的な方向性を効果的に伝えるためには、各OCRエンジンの特性を比較する表を作成し、Doclingにとって重要な要素(精度、速度、言語サポート、コストなど)を定量的に比較することが有効である。
- Doclingの主要機能であるレイアウト解析と表構造認識に特化している点を、競合ツールとの比較を通じて強調することが重要である。これらの特徴が、他のPDF変換ツールと比較してどのような優位性をもたらすのかを明確にすることで、Doclingの競争優位性を際立たせる。
- Transformerの説明は、技術的な正確さを維持しているものの、実用的な意味合いや応用分野が不足しているため、より具体的な例や応用例を盛り込むことで、読者の理解を深めることができる。特に、Transformerがなぜ特定のタスクで高い性能を発揮するのか(例:翻訳タスクにおける長距離依存性の捉えやすさ)を説明することで、その技術的な優位性を明確に示す必要がある。
- Transformerの応用分野を多角的に提示することで、その可能性を広げ、読者の興味を喚起することができる。翻訳タスクだけでなく、文章生成、質問応答、要約など、さまざまなタスクにおけるTransformerの役割を具体的に示すことが望ましい。
- OCRエンジンの選択は、Doclingの低リソース環境での高速処理という強みを最大限に活かす方向性で行われるべきである。DoclingがモバイルデバイスでのOCR処理を想定している場合、EasyOCRのようなリソース消費が大きいOCRエンジンは、Doclingの目標と矛盾する可能性があるため、選定理由を明確に示す必要がある。

### 外部コンテキスト (ドキュメントからの情報)

(論文からのチャンクデータにつき割愛)

 

### 指示
- ステップバイステップの推論軌跡とクエリに対する最終的な回答を生成してください。

狙い通り結構グタグタに入っています。

 

得られた回答:

クエリ: “TransformerとDoclingの関係性は?”

推論軌跡:

Doclingの概要理解: 提供されたドキュメントによると、Doclingはレイアウト解析と表構造認識に特化した強力な文書変換ツールです。これは、主にIBM data prep kitやTensorFlow/tensor2tensorなどのリソースを活用して開発されています。特に、テーブル構造認識の機能がDoclingの重要な特徴です。

Transformerの概要理解: Transformerは、Doclingの基盤となっているAIモデルです。Transformerは再帰ニューラルネットワーク(RNN)や畳み込みニューラルネットワーク(CNN)に依存せず、注意メカニズムのみを用いてグローバルな依存関係をモデル化します。特に、長いシーケンスを効率的に処理するために、注意メカニズムを用いたグローバルな依存関係のモデル化が重要であると説明されています。

DoclingにおけるTransformerの利用: 重要な点として、TransformerはDoclingのレイアウト解析と表構造認識において、その能力を最大限に活用しています。Doclingが、長いシーケンスを効率的に処理する必要性があることを考慮すると、Transformerの注意メカニズムが、Doclingの精度と速度を向上させる上で重要な役割を果たしていると考えられます。

DoclingとTransformerの関係性の整理: DoclingはTransformerという高性能なAIモデルを基盤としており、Doclingの主要機能であるレイアウト解析と表構造認識において、その性能を最大限に活用することで、Doclingの強力な文書変換ツールとしての役割を果たしています。 Transformerの優れた性能をDoclingが提供し、Doclingはより高度な文書変換を実現しています。

最終回答:

Transformerは、Doclingの基盤となるAIモデルであり、Doclingの主要機能であるレイアウト解析と表構造認識においてその性能を最大限に活用しています。特に、Transformerの注意メカニズムは、Doclingの精度と速度を向上させる上で重要な役割を果たしており、Doclingを強力な文書変換ツールとしています。 Doclingは、Transformerの基盤と、Doclingが提供するレイアウト解析と表構造認識機能が組み合わさることで、高い性能を発揮します。

最終回答は悪くはありませんが、DoclingでTransformerを使っているという言いきりは強すぎます。

 

生例2:

コンテキスト(プロンプト)

あなたはAIアシスタントです。以下の2種類のコンテキストを使用して、クエリを解決するための詳細な推論軌跡とクエリに対する回答を生成してください。応答は日本語で行ってください。

### クエリ
"Transformerの技術の特徴を利点をまとめてください。"

### 進化的コンテキスト (過去の対話からの教訓)
- Transformerの説明は、技術的な正確さを維持しているものの、実用的な意味合いや応用分野が不足しているため、より具体的な例や応用例を盛り込むことで、読者の理解を深めることができる。特に、Transformerがなぜ特定のタスクで高い性能を発揮するのか(例:翻訳タスクにおける長距離依存性の捉えやすさ)を説明することで、その技術的な優位性を明確に示す必要がある。
- 説明における ‘なぜ?’ の問いに対する答えを明確にすることで、技術的な説明の信頼性と説得力を高めることができる。Transformerが従来のモデルよりも優れている理由(例:並列処理の効率性)を詳細に説明することが重要。
- Transformerの応用分野を多角的に提示することで、その可能性を広げ、読者の興味を喚起することができる。翻訳タスクだけでなく、文章生成、質問応答、要約など、さまざまなタスクにおけるTransformerの役割を具体的に示すことが望ましい。
- Transformerの並列処理効率の実現方法について、GPU上で並列にAttentionの計算を実行する様子などを具体的に記述することで、読者の理解を深めることができる。設計思想の具体的な実現方法を示すことで、説得力が増す。
- DoclingとTransformerの特性を明確に比較することが重要である。Doclingはレイアウト解析と表構造認識に特化している一方で、Transformerは汎用的な自然言語処理モデルであり、Doclingの特定のニーズに合わせて調整されているという点を強調することで、二者間の関係性をより明確に表現できる。

### 外部コンテキスト (ドキュメントからの情報)

(論文からのチャンクデータにつき割愛)

 

### 指示
- ステップバイステップの推論軌跡とクエリに対する最終的な回答を生成してください。

進化的コンテキストには若干ノイズが混ざっています。

 

クエリ: "Transformerの技術の特徴を利点をまとめてください。"

ステップバイステップの推論軌跡:

初期評価: クエリの要求を理解する。Transformerの技術的特徴と、その利点をまとめる、ということを明確にする。
外部コンテキストの参照: 提供されたドキュメント(“Attention Is All You Need”)を詳しく確認する。
ドキュメントは、Transformerを従来のモデル(RecurrenceとConvolutionsを用いたモデル)と比較している。
Transformerの主な特徴として、完全なAttentionメカニズムの使用、再帰構造と畳み込み層の排除を挙げている。
Transformerが、翻訳タスクにおいて既存のモデルよりも高い品質、並列処理の効率性、そしてトレーニング時間の短縮を実現していることを強調している。
具体的な数値(WMT 2014 English-to-Germanにおける28.4 BLEU、WMT 2014 English-to-Frenchにおける41.8 BLEU)が、Transformerの性能を裏付けている。
レーニング時間(8 P100 GPUでの12時間)が従来のモデルよりも大幅に短いことも重要な利点として示されている。
内部コンテキストの統合: 過去の対話のコンテキスト(Transformerの説明の精度不足、’なぜ?’の問いへの回答の重要性、応用分野の多様性、並列処理効率の実現方法など)を考慮し、より包括的な回答を構築する。
Transformerがなぜ高い性能を発揮するのか(長距離依存性の捉えやすさ)を説明することが重要であるという過去の指摘に基づき、Attentionメカニズムが入力と出力間のグローバルな依存関係を直接的にモデル化できる点を強調する。
並列処理の効率性をGPU上でAttentionの計算が並列に実行されることによって実現されていることを詳細に記述する。
翻訳タスクだけでなく、文章生成、質問応答、要約など、様々なタスクにおけるTransformerの応用分野を示すことで、その可能性を広げる。
回答の構成:
Transformerの主な技術特徴を提示する(Attentionメカニズムのみの使用、再帰構造と畳み込み層の排除)。
各特徴の利点(高い性能、並列処理の効率性、トレーニング時間の短縮、汎用性など)を具体的に説明する。
性能を裏付ける数値や、並列処理の仕組み、応用分野を盛り込むことで、回答の説得力を高める。
最終的な回答:

Transformerは、従来の翻訳モデル(再帰構造や畳み込み層を用いるモデル)と比較して、革新的な技術的特徴を持ち合わせています。主な特徴は以下の通りです。

Attentionメカニズムのみの使用: Transformerは、再帰構造や畳み込み層を使用せず、完全なAttentionメカニズムのみを用いて構築されています。これにより、入力と出力間のグローバルな依存関係を直接的にモデル化することが可能となり、長距離の依存関係を捉える性能が高まります。
並列処理の効率性: GPU上でAttentionの計算が並列に実行されるため、並列処理の効率性が非常に高いです。これにより、従来のモデルと比較して、学習時間や推論時間を大幅に短縮できます。
高い性能: WMT 2014 English-to-German翻訳タスクでは28.4 BLEU、WMT 2014 English-to-French翻訳タスクでは41.8 BLEUという、既存の最先端モデルを上回る翻訳品質を実現しています。
多様な応用分野: 翻訳タスクだけでなく、文章生成、質問応答、要約など、様々な自然言語処理タスクに適用可能です。
汎用性: Transformerは、単一のモデルアーキテクチャで様々なタスクに対応できる汎用的なモデルです。
これらの特徴により、Transformerは自然言語処理の分野において、非常に高い性能と効率性を実現し、幅広い応用が期待される革新的な技術と言えます。

ノイズは大きく影響せず、クエリー対して、ちゃんとした回答が得られていますよね。チャンクした文章による情報の断片化を補うような効果もありそうです。