Metadata-Version: 2.4
Name: local-automata
Version: 0.2.2
Summary: ローカルLLM（mlx / llama.cpp / OpenAI互換）で動く、ツール・プロファイル対応の汎用エージェントのコア
Author: ToPo-ToPo-ToPo
License-Expression: Apache-2.0
Project-URL: Repository, https://github.com/ToPo-ToPo-ToPo/local-automata
Keywords: llm,agent,coding-agent,local-llm,mlx,llama.cpp,openai
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development
Classifier: Operating System :: OS Independent
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: openai>=1.0
Requires-Dist: mcp>=1.27
Requires-Dist: pypdf>=4.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: mlx-lm>=0.31.3; sys_platform == "darwin" and platform_machine == "arm64"
Requires-Dist: mlx-vlm>=0.5.0; sys_platform == "darwin" and platform_machine == "arm64"
Requires-Dist: mlx-whisper>=0.4; sys_platform == "darwin" and platform_machine == "arm64"
Requires-Dist: twine>=6.2.0
Provides-Extra: science
Requires-Dist: matplotlib>=3.10; extra == "science"
Requires-Dist: numpy>=2.0; extra == "science"
Requires-Dist: pandas>=2.2; extra == "science"
Requires-Dist: scikit-learn>=1.5; extra == "science"
Requires-Dist: scipy>=1.13; extra == "science"
Requires-Dist: sympy>=1.13; extra == "science"
Dynamic: license-file

# local-automata

ローカルLLM（mlx / llama.cpp）で動く、最小の汎用コーディングエージェント。
LLM とツール（ファイル読み書き・編集・検索・シェル実行）を組み合わせた agent loop が土台で、
設定（`agent.toml` ＋ 指示 `AGENTS.md`）だけでコーダー以外のエージェント
（キャラクターチャット等）にも転用できる。

> 挙動の詳細な仕様は [SPEC.md](https://github.com/ToPo-ToPo-ToPo/local-automata/blob/main/SPEC.md) を参照。

## 目次

- [クイックスタート](#クイックスタート)
- [使い方](#使い方)
- [設定（agent.toml）](#設定agenttoml)
- [ツールと安全性](#ツールと安全性)
- [機能とオプション](#機能とオプション)
- [ローカルLLMサーバー](#ローカルllmサーバー)
- [サンプル](#サンプル)
- [ライブラリとして使う](#ライブラリとして使う)
- [構成とテスト](#構成とテスト)

## クイックスタート

[uv](https://docs.astral.sh/uv/) を使う。3ステップで動く。

```sh
# 1. 環境構築（OS を自動判定。Apple Silicon では mlx も入る）
uv sync

# 2. ローカルLLMサーバーを起動（別ターミナルで）
uv run local-automata-server

# 3. エージェントを起動
uv run local-automata          # ターミナルで対話
uv run local-automata-web      # ブラウザのチャット GUI（http://127.0.0.1:8765）
```

モデル・接続先・ツールなどの挙動はすべて **`agent.toml`（無ければ既定値）** で決まる
（→ [設定](#設定agenttoml)）。`uv run` の代わりに `.venv` を有効化すれば
`local-automata` を直接呼べる。

## 使い方

### ターミナル（`local-automata`）

対話形式。プロンプトを入力するたびに、エージェントがツールを使ってタスクを進める。
会話履歴はセッション内で保持される。

```sh
uv run local-automata
# > src/ のテストを実行して落ちている箇所を直して
# > さっきの修正にテストを追加して
```

- コマンド: `/help`・`/reset`（履歴クリア）・`/img`/`/file`/`/video`（添付）・`exit`（終了、Ctrl-D も可）
- 応答はストリーミング表示（既定）。不具合が出る場合は `agent.toml` で `stream = false`。
- メモリは無駄を貯めない: 古くなったファイル全文や `edit_file` で削除したコードは自動で省略し、各ファイルの**最新の内容だけ**を履歴に残す。それでも `max_context_tokens` を超えたら、古いやり取りを要約して圧縮する。

**画像・動画・ファイルの添付**（ターミナル）:

```
> /img photo.png この画像を説明して          # vision 対応モデル＋サーバーが必要。複数はカンマ区切り、URL も可
> /video clip.mp4 何が起きてる?              # ffmpeg で代表フレームを抽出して画像化（vision モデル必要）
> /file paper.pdf この論文の要点をまとめて    # PDF はテキスト抽出。CSV/TXT/JSON/MD も可
```

> 大きなデータは添付せず `workspace/` に置き、pandas 等で処理させる方が文脈を消費せず確実。

### ブラウザ GUI（`local-automata-web`）

追加依存なし（標準ライブラリのみ）の簡易チャット画面。設定は CLI と同じ `agent.toml` から読む。

```sh
uv run local-automata-web                 # http://127.0.0.1:8765 を開く
uv run local-automata-web --port 9000 --no-browser
```

- 応答を SSE でストリーミング表示し、**生成したコードや画像（グラフ）も画面に表示**する。
- 入力欄の **🖼 ボタン**（またはクリップボードからの貼り付け）で**画像を添付**して送れる。
  画像入力には vision 対応モデル・サーバーが必要（[画像・動画入力（vision）](#画像・動画入力vision)を参照）。
- **🎤 ボタン**で**音声入力**ができる（`[voice]` を有効にしたときだけ表示）。[音声入力（voice）](#音声入力voice)を参照。
- **数式**は LaTeX 記法（`$...$` / `$$...$$`）を **KaTeX** できれいに表示する（同梱・オフライン動作）。
- 生成物はセッションごとの `workspace/<日時>_<タイトル>/` に保存。「＋ 新しいチャット」で別フォルダに切替。
- **LLM サーバーの自動起動も CLI と同じ**。`agent.toml` に `auto_start = true` を書けば、サーバー未起動時に
  agent.toml の `model`/`backend` で自動起動し、GUI 終了時に停止する（手動で `local-automata-server` を立てなくてよい）。
- フロントエンドの静的ファイルはパッケージ内 `local_automata/frontend/` に同梱（package-data）。`pip install` だけで GUI が動く。

## 設定（agent.toml）

モデル・パラメータ・運用設定はすべて **`agent.toml`（＋既定値）** で指定する。
**CLI 引数や環境変数では指定しない**（CLI は設定・プロファイル選択用の `--config` / `--profile` のみ）。
設定はトップレベル、または `[profiles.<name>]` 配下に書く。

```toml
# agent.toml
model = "mlx-community/Qwen3.6-27B-4bit"
base_url = "http://localhost:8080/v1"
backend = "mlx-vlm"        # mlx-vlm（vision・mac既定）/ mlx（テキスト専用・軽量）/ llama-cpp / router。省略時は OS 自動判定
temperature = 0.2
max_tokens = 16384
max_steps = 50
max_context_tokens = 16384
max_context_images = 2     # 文脈に残す画像メッセージ数。古い画像は自動で外す（-1 で無効）
image_context_mode = "recent"  # recent=単純省略 / on_demand=id を振りモデルが view_image("img_N") で選んで呼び戻す
enable_thinking = false
stream = true
tool_mode = "prompt"       # prompt（既定・自作方式）/ native（function calling）
verbose = true             # 過程を全部表示（ツール引数・結果の全文）。CLI/GUI とも既定 on。false で1行要約
allow_install = false      # run_command でのパッケージ導入を許可するか
auto_start = false         # サーバーが無ければ自動起動するか
parallel = 1               # 自動起動時の同時処理スロット数（llama.cpp）
workdir = "workspace"      # ツールの作業ディレクトリ（'.' で現ディレクトリ）
planning = false           # 着手前に計画を立てさせるか
```

| キー | 既定 | 説明 |
| --- | --- | --- |
| `model` | `mlx-community/Qwen3.6-27B-4bit` | モデル名/パス |
| `base_url` | `http://localhost:8080/v1` | OpenAI 互換エンドポイント |
| `backend` | OS自動（macOS arm64→`mlx-vlm`、他→`llama-cpp`） | 起動するバックエンド。mac 既定は vision 対応の `mlx-vlm`（画像・動画もそのまま動く）。テキスト専用で軽くするなら `mlx` |
| `temperature` | `0.2` | サンプリング温度 |
| `max_tokens` | `16384` | 1応答の最大生成トークン |
| `max_steps` | `50` | 1タスクの最大ツール往復回数 |
| `max_context_tokens` | `16384` | これを超えると古い履歴を要約圧縮。手前で「古いファイル全文・削除コード」を自動で刈り取る |
| `max_context_images` | `2` | 文脈に残す画像メッセージ数。古い画像は自動で外しプレースホルダ化（毎ターンの画像再送を防ぐ）。`-1` で無効 |
| `image_context_mode` | `recent` | 古い画像の扱い。`recent`=単純省略 / `on_demand`=id を振り `view_image("img_N")` でモデルが必要な画像を選んで呼び戻せる |
| `enable_thinking` | `false` | 思考(thinking)モード |
| `stream` | `true` | ストリーミング表示 |
| `request_timeout` | （無制限） | LLM 応答の読み取りタイムアウト秒数（0/未指定で無制限） |
| `mcp_call_timeout` | `120` | MCP ツール呼び出しの既定タイムアウト秒数（0/負で無制限。各サーバーの `timeout` で上書き） |
| `tool_mode` | `prompt` | ツール呼び出し方式 `prompt`（自作・既定）/ `native`（function calling） |
| `verbose` | `true` | 過程を全部表示（ツール引数・結果の全文。prompt モードは生出力も）。**CLI/GUI とも既定 on**。`false` で1行要約 |
| `allow_install` | `false` | `run_command` でのパッケージ導入許可 |
| `auto_start` | `false` | サーバー自動起動（既定は接続専用） |
| `parallel` | （なし） | 自動起動時の並列スロット数（llama.cpp） |
| `workdir` | `workspace` | ツールの作業ディレクトリ |
| `planning` | `false` | 着手前の計画 |
| `debug` | `false` | LLM 呼び出しの所要時間・サイズや各ステップを stderr に出す |

**指示（システムプロンプト）は Markdown** で分けて書く（Codex / Claude Code と同様）。
既定で `./AGENTS.md` を読み込み、`instructions_file` でパス変更、無ければ組み込み既定を使う。
サンプルは `AGENTS.md.example` / `agent.toml.example`。

```markdown
<!-- AGENTS.md -->
あなたは読み取り専用アシスタントです。まずファイルを読んでから判断してください。
```

## ツールと安全性

利用可能なツール: `read_file` / `write_file` / `edit_file` / `list_dir` / `grep` / `glob` / `run_command`。
`agent.toml` の `tools` で絞り込める（省略＝全部、`[]`＝無し）。

- **検索（大規模コード対応）**: `grep` は `glob`（種別絞り込み）・`ignore_case`・`context`（前後行）に対応。
  **ripgrep（`rg`）があれば自動で優先**し、高速かつ `.gitignore` 尊重で検索する（無ければ純Python で代替し、
  `node_modules`/`.git`/`.venv` 等は枝刈り）。`rg` を入れておくと巨大リポジトリでも実用的。
- **サブエージェント探索 `dispatch_agent`**: 読み取りツールがあるとき自動で使える。読み取り専用の子エージェントが
  多数のファイルを調べて**結論だけ**を返すので、本体の文脈を膨らませずに「どこに何があるか」を調査できる。

**作業ディレクトリ**:

- 既定で `./workspace`（自動作成）に閉じ込められる。`workdir = "."` で現ディレクトリ（既存プロジェクト編集用）。
- 各実行は `workspace/<日時>_<タイトル>/` に分離保存され、検討ごとに混ざらない（タイトルは最初のタスクから LLM が生成）。
- ファイルツールは workspace の外を拒否（`..`・絶対パス・シンボリックリンクをブロック）。
- `run_command` は workspace を作業ディレクトリとして実行する。ただしシェルは絶対パスや `cd` で外へ出られるため、
  これは隔離ではない。**信頼できないモデル/タスクはコンテナ/VM（できればネットワーク制限付き）で実行する**こと。
- パッケージ導入は既定でブロック（`pip/uv/conda/npm/apt …` を検出して拒否）。許可は `allow_install = true`。
  導入する場合は `uv pip install`、恒久的に必要なら `pyproject.toml` に追加して `uv sync`。

## 機能とオプション

### ツール呼び出し方式（`tool_mode`）

- **prompt（既定）**: ツール仕様をプロンプトに注入し、応答中の `tool` コードブロック（JSON）を解析する
  自作方式。**モデルの出力がそのまま見える**ので過程を確認しやすく、コード生成もストリーミングされ、
  ツール非対応モデルでも動く。形式はモデルの指示追従に依存。
- **native**: OpenAI の function calling。形式は正確だが、ツール呼び出しの中身は API 経由の構造データに
  なるため過程は見えにくい。mlx ではツール呼び出しが一括生成のためコード部分はストリーミングされない。

### 過程の表示（`verbose`）

`verbose`（**既定 true、CLI/GUI 共通**）で「何をしているか」を全部表示する: ツール呼び出しの**全引数**・
ツール結果の**全文**を出し、prompt モードでは生出力（ツール呼び出しの JSON も隠さない）を流す。
簡潔な1行要約に戻したいときは `agent.toml` で `verbose = false`。

Web GUI（`local-automata-web`）では、各ツールの呼び出し＋結果が**折り畳みブロック**になっており、
`● ツール名` の見出しをクリックすると後から畳める（既定は開いた状態）。

### 思考(thinking)モード

`enable_thinking`（既定 false）。サーバー側は `local-automata-server --thinking` / `--no-thinking` で切替。
mlx_lm.server は起動時に固定（リクエスト単位の上書き不可）、vLLM 等はリクエスト単位で反映。
MLX 変換モデルの一部はテンプレートが未対応で効かないことがある。

### 計画モード・固定ワークフロー

- `planning = true` … 着手前にタスクを分解する `update_plan`（チェックリスト）ツールを追加し、
  「計画 → 実行 → 検証」の手順を促す。多段タスクの成功率が上がりやすい。
- `workflow_file = "..."` … 決まった手順を外部ファイルで渡す。拡張子で方式が決まる。
  - `*.md` … 助言型。手順をプロンプトに足すだけ（従うかは LLM 任せ）。
  - `*.yaml` … 確実型。手順を1ステップずつ順番に実行（順序・ツール制限・やり直しを管理）。

```toml
workflow_file = "workflow.yaml"   # 確実に手順を踏ませたいとき
```

書き方・GUI での試し方・注意点は **[使い方ガイド: docs/workflows.md](docs/workflows.md)** を参照。

### 長期メモリ

`[memory] enabled = true` で、セッションをまたいで記憶する `remember` / `recall` ツールが有効になる。
`path` をプロファイルごとに分ければキャラクター単位の記憶になる。

```toml
[memory]
enabled = true
path = ".coder/memory.json"
```

### MCP サーバー

`[mcp.servers.<name>]` に `command`（ローカルプロセス＝stdio）か `url`（リモート＝Streamable HTTP）を書くと、
その MCP サーバーのツールが起動時に自動で取り込まれ、`<name>_<ツール名>` で使える（追加インストール不要）。

| キー | 対象 | 説明 |
| --- | --- | --- |
| `command` / `args` / `env` | stdio | 起動コマンド・引数・環境変数 |
| `cwd` | stdio | サーバープロセスの作業ディレクトリ（リポジトリルートでの起動が必須なサーバー向け） |
| `url` | Streamable HTTP | リモート MCP サーバーのエンドポイント |
| `timeout` | 共通 | このサーバーのツール呼び出しタイムアウト秒数。**0/負で無制限**。全体既定（`mcp_call_timeout`）を上書き |

呼び出しタイムアウトの全体既定は runtime の `mcp_call_timeout`（既定 120 秒、**0/負で無制限**）で決める。
無制限でも進捗が分かるよう、サーバーが送る進捗通知（`notifications/progress`）はログに表示し、
タイムアウト・Ctrl-C のときはサーバーへキャンセル（`notifications/cancelled`）を伝える。

```toml
# 既定タイムアウト（120 秒）を無制限にしたい場合は runtime に書く
mcp_call_timeout = 0

[mcp.servers.fs]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/data"]

# リポジトリルートでの起動が必須なサーバー（cwd を指定）。
# 1 回の処理が長い場合は timeout = 0 で無制限にできる。
[mcp.servers.optimizer]
command = "julia"
args = ["-t", "auto", "--project=.", "main/server/mcp_server.jl"]
cwd = "/abs/path/to/Optimizer"
timeout = 0

# リモート MCP サーバー（Streamable HTTP）。url を書くと現行標準の
# Streamable HTTP トランスポートで接続する（エンドポイントは通常 /mcp）。
[mcp.servers.remote]
url = "http://localhost:3000/mcp"
# timeout = 0        # リモートの長時間ジョブを待ち切る場合
```

### プロファイル

用途ごとに「指示（Markdown）＋ツール＋モデル・パラメータ」を1セットとして `[profiles.<name>]` に定義し、
`--profile <name>` または `default_profile` で切り替える。

```toml
default_profile = "coder"

[profiles.coder]
instructions_file = "AGENTS.md"
tools = ["read_file", "write_file", "edit_file", "list_dir", "run_command"]

[profiles.luna]            # キャラクターチャットの例
instructions_file = "characters/luna.md"
tools = []                 # ツールを使わない純粋な会話
```

```sh
uv run local-automata --profile luna
```

## ローカルLLMサーバー

`local-automata-server` が mlx / mlx-vlm / llama.cpp を OpenAI 互換 API で起動する（応答可能になるまで待つ）。
`--backend` の既定は OS 自動判定で、**macOS Apple Silicon → `mlx-vlm`（vision 対応）**、他 → `llama-cpp`。
既定モデル Qwen3.6 はマルチモーダルなので、mac では**何もせず画像・動画入力までそのまま動く**。

```sh
uv run local-automata-server                                  # 既定（mac は vision 対応の mlx-vlm ＋ 既定モデル）
uv run local-automata-server --backend mlx                    # テキスト専用で軽くしたいとき（画像は不可）
uv run local-automata-server --backend llama-cpp --model /path/to/model.gguf
uv run local-automata-server --model model.gguf -- --ctx-size 8192 --jinja   # -- 以降はバックエンド固有引数
```

- `--host` / `--port`（既定 `127.0.0.1:8080`）、`--thinking` / `--no-thinking`。
- **並列処理**: llama.cpp は `--parallel N`（continuous batching。スロット数だけ KV キャッシュを消費）。
  mlx 系は逐次処理なので `--instances N` で連番ポートに N 個起動する（重みは N 倍のメモリ）。

### 画像・動画入力（vision）

mac の既定バックエンド **`mlx-vlm`** は vision 対応なので、**画像・動画入力はそのまま使える**
（CLI の `/img` / `/video`、ブラウザ GUI の画像添付、`Agent.run(..., images=/videos=)` 共通）。
既定モデルの Qwen3.6 はマルチモーダルなので、1プロセスでテキストも画像も扱える。

> テキスト専用の `mlx` / `llama.cpp` バックエンドは画像（`image_url`）を含むリクエストでエラーになる。
> `backend = "mlx"` を選んだ場合や非 mac では、画像を使うなら `mlx-vlm`（または vision 対応サーバー）に切り替える。

- **動画**: `/video <パス>` または `Agent.run(..., videos=[...])`。**ffmpeg** で動画から代表
  フレーム（既定8枚、全体から均等サンプリング）を抜き出し、複数画像として送る（動画ネイティブ
  入力ではなくフレーム画像化での代替）。ffmpeg が無い場合は導入を促すエラーになる。
- **生成画像をエージェント自身が確認（`view_image`）**: `run_command` で作った図（matplotlib の
  コンター図など）は保存されるだけでモデルには見えない。`view_image` ツールを使うと、その画像を
  vision 入力として読み込み、**見た目を確認してから次の処理に進める**（読み取りツールがあるとき
  自動で使える。vision モデル必要）。

```sh
# mac では既定が mlx-vlm なので明示不要。非 mac やテキスト既定から切り替える場合のみ:
uv run local-automata-server --backend mlx-vlm
```

既定の `tool_mode = "prompt"` は function calling 非対応の VLM でもそのまま動く（native を使いたいときだけ `agent.toml` で指定）。

**自動振り分け（`router`）**: テキストは軽い LLM、画像を含むときだけ別の VLM——のように
モデルを使い分けたい場合は `--backend router`。テキスト LLM（mlx）と vision VLM（mlx-vlm）を
**同時に起動**し、単一のエンドポイントを公開する。受信リクエストに画像が含まれれば VLM、
無ければ LLM へ**内部で自動転送**する（クライアントは `base_url` を 1 つ指定するだけ。
2 つのプロセスを起動するためメモリは増える）。

```sh
uv run local-automata-server --backend router        # 既定はテキスト・画像とも Qwen3.6
# 使い分けるなら個別指定:
uv run local-automata-server --backend router \
  --model <テキスト用モデル> --vision-model <画像用モデル>
# 公開: http://127.0.0.1:8080/v1（agent.toml の base_url はこれ 1 つ）
```

- `--model` がテキスト用、`--vision-model` が画像用（env: `CODER_MODEL` / `CODER_VISION_MODEL`。既定はどちらも Qwen3.6）。
- 内部ではテキストを `port+1`、vision を `port+2` で起動する（公開は `--port`、既定 8080）。

### 音声入力（voice）

ブラウザ GUI のマイクボタンで話した内容を、**ローカルの mlx-whisper** で文字起こしして
入力欄へ差し込む（Apple Silicon 専用、クラウド送信なし）。エージェントのコアはテキストを
受け取るだけで、音声は入力層（GUI）でテキストに変換される。

**手順**

1. **導入**: mlx-whisper は**既定の依存**なので、Apple Silicon で `uv sync`（または `uv add local-automata`）すれば自動で入る（追加の extra は不要）。
2. **有効化**: `agent.toml`（または `[profiles.<name>]` 配下）に書く。既定は無効で、有効なときだけ 🎤 が出る:
   ```toml
   [voice]
   enabled = true
   # model = "mlx-community/whisper-large-v3-mlx"   # 省略可（既定＝最高精度の full large-v3）
   # language = "ja"                                # 指定で精度・速度が安定（省略で自動判定）
   # correct = true                                 # 文字起こしを LLM で校正（漢字の誤変換を文脈で直す）
   ```
3. **モデルの取得**（次のいずれか。HF キャッシュ `~/.cache/huggingface/hub/` に保存、以降はオフライン可）:
   - **自動**: 🎤 で一度録音・送信すれば初回にダウンロードされる（その回だけ時間がかかる）。
   - **事前取得**: 本番前に落としておく。
     ```sh
     uv run huggingface-cli download mlx-community/whisper-large-v3-mlx
     ```
   - **取得＋動作確認**（おすすめ）: 無音を1回流してダウンロード＋ロード＋動作を確認。
     ```sh
     uv run python -c "from local_automata.stt import Transcriber; print('OK:', repr(Transcriber().transcribe_pcm(b'\x00'*32000)))"
     ```
4. **使う**: `uv run local-automata-web` → 🎤 を押して話す → 止めると文字起こしが入力欄に入る。

**モデルの選び方**

| モデル（`model`） | 精度 | サイズ目安 | 用途 |
| --- | --- | --- | --- |
| `mlx-community/whisper-large-v3-mlx`（既定） | ★最高 | 数 GB | 精度優先（日本語含む多言語で turbo よりわずかに上） |
| `mlx-community/whisper-large-v3-turbo` | ほぼ同等 | 約 0.8GB | 速度・省メモリ優先 |
| `mlx-community/whisper-large-v3-mlx-8bit` | やや低下 | 中 | さらに省メモリ |
| `kaiinui/kotoba-whisper-v2.0-mlx` | 日本語◎ | 中 | **日本語特化・高速**（large-v3 の約6倍速）。`language = "ja"` 推奨 |

変更したら、そのモデル名で `huggingface-cli download` しておくと初回待ちが無くなる。

**漢字の誤変換を減らす（`correct = true`）**

日本語の音声認識では同音異義語・漢字の誤変換がどうしても起きる。`[voice] correct = true` にすると、
**文字起こし結果を LLM で校正**してから入力欄へ入れる（文脈から自然になるよう最小限だけ直す）。
1回 LLM 呼び出しが増えるぶん少し遅くなるが、漢字の精度が上がる。失敗時は素の文字起こしを使う。
日本語特化の `kotoba-whisper` に変える（誤りを元から減らす）のと併用も可。

**補足**

- 仕組み: ブラウザが録音を **16kHz モノラル PCM** に変換して送り、サーバーが mlx-whisper で
  文字起こしして返す（GUI 経路は ffmpeg 不要）。ライブラリから音声ファイルを直接渡す場合は
  `Transcriber.transcribe_file("clip.wav")`（任意フォーマット可・要 ffmpeg。[ライブラリとして使う](#ライブラリとして使う)）。
- マイクは https か `localhost` でのみ使える（ブラウザの制約）。既定の `127.0.0.1` ならそのまま使える。
- ダウンロード自体に ffmpeg は不要（HF からの取得は `huggingface_hub` 経由）。

### 接続のしかた

`local-automata` は起動時に `base_url` のサーバーが応答するか判定する。

- **既定（接続専用）**: 既存サーバーに相乗りする（停止しない）。無ければ起動方法を案内して終了。
- **`auto_start = true`**: サーバーが無ければ自動起動し、終了時に停止する。

**複数エージェントの同時実行**: 共有サーバーを1台だけ起動し（llama.cpp は `--parallel` をエージェント数以上に）、
各ターミナルで別プロファイルを接続する。接続専用が既定なので相乗りが安全。

```sh
uv run local-automata-server --backend llama-cpp --model model.gguf --parallel 2
uv run local-automata --profile analyzer   # ターミナル1
uv run local-automata --profile luna       # ターミナル2
```

## サンプル

`examples/` に用途別のエージェント一式がある。サーバーを起動した上で、各ディレクトリで実行する。

| ディレクトリ | 内容 |
| --- | --- |
| `examples/character/` | 会話のみ（ツールなし）＋長期メモリの「ルナ」 |
| `examples/coder/` | 力学（放物運動など）の数値計算コードを作成 → 実行 → 検証 |
| `examples/optimizer/` | 作成 → 実行 → 評価 → やり直しを固定ワークフローで反復 |
| `examples/ml/` | CSV から前処理 → 学習 → 評価 → 保存 |

```sh
cd examples/coder
uv run local-automata                       # CLI（agent.toml を自動読込。生成物は ./workspace/）
uv run local-automata-web                    # 同じ設定をブラウザ GUI で
uv run python run_physics.py             # ライブラリとして埋め込む例
```

詳細は各ディレクトリの `README.md` を参照。

## ライブラリとして使う

PyPI から `uv` でインストールして組み込める（自分のプロジェクトの依存に追加する）。

```sh
uv add local-automata                # コア（mac は mlx、PDF 抽出の pypdf も自動で入る）
uv add 'local-automata[science]'     # 科学計算の例も動かす（numpy/scipy/pandas/...）
# 音声入力（mlx-whisper）は既定の依存。Apple Silicon なら追加指定なしで入る
```

```python
from local_automata import Agent, Config, LLMClient, build_registry

config = Config.load()  # 既定。agent.toml の設定を使うなら Config.load(load_agent_config(...).runtime)
agent = Agent(LLMClient(config), build_registry(), config)  # 既定で workspace/ を作る
print(agent.run("..."))
```

設定ファイル（`AGENTS.md` / `agent.toml`）から組み立てるなら `load_agent_config` を使う（`examples/` 参照）。
公開 API は `local_automata.__all__` を参照。

### 入力アダプタ（音声・画像・動画）と高度ツール

**エージェントのコアはテキスト入力**。音声・画像・動画は「入力層」でテキスト/画像に変換してから
`Agent.run(...)` に渡す（外部ライブラリは独自 UI で集めた入力をこの形にするだけ）。

```python
from local_automata import (
    Agent, Config, LLMClient, build_registry, attach_agentic_tools, Transcriber,
)
from local_automata.tools import Workspace

config = Config.load()
ws = Workspace.new_session("workspace")            # 生成物の保存先（任意）
agent = Agent(LLMClient(config), build_registry(None, ws), config)
attach_agentic_tools(agent, ws)                    # view_image / dispatch_agent を有効化（任意）

# 音声入力: 録音をファイルにして渡すだけ（mlx-whisper が ffmpeg でデコード。mlx-whisper は既定で入る）。
text = Transcriber(language="ja").transcribe_file("clip.wav")  # or transcribe_pcm(16k mono f32)
agent.run(text)

# 画像・動画: パス/URL/データURI をそのまま渡す（vision モデル＝mlx-vlm 必要）
agent.run("この図を説明して", images=["plot.png"])
agent.run("何が起きてる?", videos=["clip.mp4"])    # ffmpeg で代表フレーム抽出 → 複数画像
```

- `Transcriber` … 音声→テキスト（`transcribe_file(path)` が手軽。任意フォーマット可、要 ffmpeg）。
  GUI を介さずライブラリ単体で音声を使うときの入口。変換後の文字列を `agent.run(text)` に渡す。
- `attach_agentic_tools(agent, ws)` … `view_image`（生成画像の視認）と `dispatch_agent`
  （サブエージェント探索）を後付け登録。CLI/GUI は自動で付くが、手で `Agent` を組むときに 1 行で同じに。
- `build_user_content` / `video_to_image_urls` / `to_image_url` … 入力を OpenAI 互換 content に
  変換する低レベル関数（自前で組み立てたいとき）。

> mlx は環境マーカーで判定され、**Apple Silicon のときだけ自動で入る**（`uv add local-automata`・
> `uv sync` 共通。他OSでは自動スキップ）。非mac は llama.cpp（`llama-server` バイナリ）を別途用意する。
> 開発環境（`uv sync`）はさらに科学計算ライブラリと pytest が全OSで入る。Python は `.python-version`（3.11）。

## 構成とテスト

```
AGENTS.md.example    システムプロンプト（指示 Markdown）のサンプル
agent.toml.example   設定（TOML）のサンプル
backend/local_automata/  Python パッケージ本体（PyPI に公開される）
  config.py        実行時設定（agent.toml ＋ 既定値）
  settings.py      指示 Markdown と設定 TOML の読み込み
  images.py        画像・動画・文書入力（パス/URL → content パーツ、動画フレーム抽出）
  stt.py           音声→テキスト（mlx-whisper）と LLM 校正
  tokenizer.py     トークン数の計測（コンテキスト圧縮の判定用）
  agent.py         エージェントループ（ツール・刈り取り・圧縮）
  llm.py           OpenAI 互換クライアント
  workflow.py      オーケストレーション型ワークフロー（YAML）
  cli.py           CLI / 対話モード（local-automata）
  webapp.py        Web GUI サーバー（local-automata-web）
  frontend/        Web GUI の静的ファイル（index.html / style.css / app.js ＋ vendor/katex/。同梱）
  mcp_client.py    MCP サーバー連携（ツール自動取り込み）
  server.py        ローカルLLMサーバーのランチャー
  router.py        テキスト/画像でモデルを振り分けるルーター
  server_cli.py    サーバー起動 CLI（local-automata-server）
  spinner.py       進捗表示（生成中／実行中）
  tools/           ツール群（filesystem / shell / memory / plan / workspace / subagent / vision）
tests/             pytest テスト一式
scripts/           開発用スクリプト（MCP 連携の結合確認など）
```

```sh
uv run pytest
```

MCP 連携（cwd / タイムアウト / 進捗 / キャンセル）を実 MCP サーバー込みで一括確認するには：

```sh
bash scripts/verify_mcp.sh   # uv sync → pytest → 実サーバー結合確認
```
