diff --git a/docs/DATA_MODEL.md b/docs/DATA_MODEL.md
index d42774b02..a6e07907a 100644
--- a/docs/DATA_MODEL.md
+++ b/docs/DATA_MODEL.md
@@ -12,8 +12,8 @@ write, migrate, or interoperate with Graphium files.
>
> | UI label (EN / JA) | On-disk `WikiKind` |
> |---|---|
-> | Topics / 話題 | `topic` |
-> | Q&A / 問答 | `answer` |
+> | Topics / トピック | `topic` |
+> | Answers / 問答(Q&A) | `answer` |
> | Summaries / 要約 | `summary` |
> | Claims / 知見 | `claim` |
> | Insights / 洞察 | `atom` |
@@ -56,6 +56,34 @@ A few invariants underpin every schema in this document.
## 2. The note: `GraphiumDocument`
+### Knowledge Schema system Skill
+
+The Knowledge Schema is a single additive `GraphiumDocument` with `source:
+"skill"` and `skillMeta.systemSkillId: "knowledge-schema"`. Its storage ID is
+also the fixed string `knowledge-schema`. It uses the existing `SkillMeta`
+default version and prompt hash fields, and the existing
+`DocumentProvenance` revision chain for edits, default synchronization, and
+Reset. It is not a `WikiKind`, is never projected to the note index, and does
+not require a document-version or index-schema-version change.
+
+On first creation only, the bundled Schema body is chosen from the current
+Graphium UI locale (`ja` or `en`) and that language is stored in
+`skillMeta.language`. Later locale changes do not translate or overwrite the
+saved Schema. Version synchronization and Reset resolve the bundled default
+from the saved `skillMeta.language`, so an untouched older English Schema
+auto-updates to the current English default, while an edited older Schema only gets the
+standard newer-default notification.
+
+The only operation allowed to change the saved Schema language is the explicit,
+confirmed language switch shown while the fixed `knowledge-schema` document is
+open. It atomically replaces the body with the selected current bundled default and
+updates `skillMeta.language`, version, and default prompt hash while preserving
+the document version. Before replacement, the prior full document is stored in
+the existing version-snapshot store so it can be restored, and the replacement
+is recorded separately as a `knowledge_schema_language_switch`
+revision/activity. Ordinary Skill metadata updates still preserve the existing
+Schema language.
+
Each note is a single JSON file (or one row in IndexedDB for the `local`
provider). The top-level shape:
@@ -315,9 +343,10 @@ type TableSource = {
(`.txt` / `.dat` / `.csv`) are turned into ordinary tables, and the conversion
parameters — which lines were read, with which delimiter — are kept here rather
than thrown away. Dropping them would make the numbers untraceable back to the
- raw file, which is exactly the thing this app exists to prevent. The preamble a
- converter normally discards (`# Device Model: ENV-MONITOR-X9`) is kept as
- `meta`, because for a lab note those lines *are* the measurement conditions.
+ raw file, which is exactly the thing this app exists to prevent. A preamble a
+ converter normally discards (for example `# Source: service-export-v4`) is kept
+ as `meta`, because those lines may carry essential runtime, collection, or
+ decision context in any domain.
When the file was also registered as an asset, `fileId` links the table back to
it and the import can be re-run with the stored settings.
- **Delimited imports become a `dataTable` block by default.** The block
@@ -1020,7 +1049,7 @@ only then created as new.
There is no separate ingest-time "consolidation" step and no target
member-count per topic — those would be thresholds nobody could
justify. Instead, near-duplicate or over-fragmented topics (wording
-variants, particle differences, per-sample slices that should share a
+variants, particle differences, per-case slices that should share a
page) are caught later by two Karpathy-style *lint* mechanisms that
look at the whole topic corpus at once, rather than one Claim at a
time:
diff --git a/manual/ai-chat-and-ask.md b/manual/ai-chat-and-ask.md
index d665a8b4f..8771c0f89 100644
--- a/manual/ai-chat-and-ask.md
+++ b/manual/ai-chat-and-ask.md
@@ -21,7 +21,7 @@ By default the chat can see the note you have open. To focus it on a specific pa
| Select multiple blocks → **Ask AI about selection** | Exactly the blocks you selected |
| Select text → **Ask AI about selection** in the floating toolbar | The selected text |
-Whichever starting point you use, the note you have open is passed as background as well. A quoted sentence that leans on the lines before it — a pronoun, an abbreviation, "at this temperature" — is still understood. The answer stays on the quoted part; the rest of the note is there only for context. It is re-read on every message, so edits you make mid-conversation are picked up.
+Whichever starting point you use, the note you have open is passed as background as well. A quoted sentence that leans on the lines before it — a pronoun, an abbreviation, "under these conditions" — is still understood. The answer stays on the quoted part; the rest of the note is there only for context. It is re-read on every message, so edits you make mid-conversation are picked up.
When a chat starts from blocks or a selection, the passed content stays visible at the top of the panel under **Quote**, so you always know what the conversation is about.
@@ -191,3 +191,5 @@ Each skill's **Edit** dialog controls:
| **Auto-apply on Ingest** | When on, the skill's prompt is automatically appended as instructions when Graphium generates knowledge from your notes — and in AI chat conversations |
Skills are ordinary documents: click one to edit its prompt body like any note.
+
+The built-in **Knowledge Schema** is the exception to auto-apply: Graphium always supplies its saved body as a separate instruction when an assistant helps with Knowledge, or when Topics, Answers, and Claims are generated. The first saved default follows the UI language at creation time and does not change when you later switch the UI. To replace it deliberately, open the Schema and use the confirmed **Switch to Japanese** or **Switch to English** action; the previous body stays recoverable in History. It defines structure, citations, revision, and upkeep rather than writing voice; it can be edited and reset like a system skill, but cannot be deleted.
diff --git a/manual/ja/ai-chat-and-ask.md b/manual/ja/ai-chat-and-ask.md
index 1bc798074..0f2886e97 100644
--- a/manual/ja/ai-chat-and-ask.md
+++ b/manual/ja/ai-chat-and-ask.md
@@ -21,7 +21,7 @@
| 複数ブロックを選択 → **選択範囲を AI に聞く** | 選択したブロックだけ |
| テキストを選択 → フローティングツールバーの **選択範囲を AI に聞く** | 選択したテキスト |
-どの起点で始めても、開いているノートの本文が背景として一緒に渡ります。引用した一文が前の行に寄りかかっている場合 — 指示語、略語、「この温度で」といった書き方 — でも話が通じます。回答の主題は引用した部分のままで、残りの本文は背景として添えられるだけです。本文はメッセージのたびに読み直されるので、会話の途中で加えた編集も反映されます。
+どの起点で始めても、開いているノートの本文が背景として一緒に渡ります。引用した一文が前の行に寄りかかっている場合 — 指示語、略語、「この条件で」といった書き方 — でも話が通じます。回答の主題は引用した部分のままで、残りの本文は背景として添えられるだけです。本文はメッセージのたびに読み直されるので、会話の途中で加えた編集も反映されます。
ブロックや選択範囲からチャットを始めると、渡した内容がパネル上部の **引用** の下に表示され続けます。何についての会話なのかがいつでも分かります。
@@ -122,15 +122,15 @@ AI の返信には、それぞれ下にアクションボタンが付きます
| **置換** | 元のブロックを回答で置き換えます(特定のブロックからチャットを始めた場合に表示されます) |
| **別ノートとして派生** | 質問と回答から新しいノートを作り、元ノートへの来歴リンクを張ってサイドピークで開きます |
| **ナレッジにする** | 回答からナレッジ候補を抽出します(下記) |
-| **ナレッジに残す** | 回答をそのまま、ナレッジ層の問答ページとして保存します(下記) |
+| **ナレッジに残す** | 回答をナレッジ層の問答(Q&A)ページとして保存します(下記) |
### チャットからナレッジを取り出す
**ナレッジにする** は、良い回答を[ナレッジ層](/ja/knowledge-layer)のエントリに変えます。選んでいないものは何も保存されません。Graphium は「知見を抽出中…」、続いて「洞察を生成中…」と表示したあと、**ナレッジ候補(選んで保存)** というピッカーを出します。各候補には **知見** または **洞察** のバッジが付きます。**すべて選択** / **選択解除** で調整して **選択した n 件を保存** を押し、何も残したくなければ **キャンセル** で閉じます。ナレッジページになるのは、選んだ候補だけです。
-### 問答をまるごと残す
+### 問答(Q&A)をまるごと残す
-回答がすでに 1 つのまとまった説明として読めて、知見・洞察に分ける必要が無いなら、代わりに **ナレッジに残す** を押します。Graphium はまず AI をもう一度呼び、それまでの会話を渡して、その問答を単体で読める [問答ページ](/ja/knowledge-layer#種類) に書き起こします — 会話に寄りかかった回答で省かれていた前提を補い、タイトルも依頼の言い回しから離して書き直します。回答に表示されていた引用は、書き起こした文にそのまま引き継がれます。保存前に見直す画面はありません。書き起こしに失敗したとき、または元の回答にあった出典が 1 つも残らなかったときは、Graphium はメッセージの文章とタイトル(尋ねた質問)でそのまま保存します。保存が終わるとボタンは、新しいページへの **開く** リンク付きの確認表示に変わります。保存後はトピックと同じように保守され、資料が回答を更新・反証したときに改訂され、点検・出典照合の対象にもなります。詳しくは [ナレッジ層](/ja/knowledge-layer#問答をまるごと残す) を参照してください。
+回答がすでに 1 つのまとまった説明として読めて、知見・洞察に分ける必要が無いなら、代わりに **ナレッジに残す** を押します。Graphium はまず AI をもう一度呼び、それまでの会話を渡して、その問答を単体で読める [問答(Q&A)ページ](/ja/knowledge-layer#save-answer-page) に書き起こします — 会話に寄りかかった回答で省かれていた前提を補い、タイトルも依頼の言い回しから離して書き直します。回答に表示されていた引用は、書き起こした文にそのまま引き継がれます。保存前に見直す画面はありません。書き起こしに失敗したとき、または元の回答にあった出典が 1 つも残らなかったときは、Graphium はメッセージの文章とタイトル(尋ねた質問)でそのまま保存します。保存が終わるとボタンは、新しいページへの **開く** リンク付きの確認表示に変わります。保存後はトピックと同じように保守され、資料が回答を更新・反証したときに改訂され、点検・出典照合の対象にもなります。詳しくは [ナレッジ層](/ja/knowledge-layer#save-answer-page) を参照してください。
## Composer(⌘K)
@@ -191,3 +191,5 @@ AI の返信には、それぞれ下にアクションボタンが付きます
| **ナレッジ化時に自動適用する** | オンにすると、Graphium がノートからナレッジを生成するとき、そのスキルのプロンプトが指示として自動で追加されます。AI チャットの会話でも同じように使われます |
スキルはふつうのドキュメントです。クリックすれば、ノートと同じようにプロンプト本文を編集できます。
+
+組み込みの**ナレッジスキーマ**は自動適用スキルとは別です。アシスタントがナレッジ生成を支援するとき、およびトピック・問答(Q&A)・知見の生成時に、保存済み本文を独立した指示として渡します。初回保存される既定本文はその時点の UI 言語に従い、その後に UI 言語を変えても自動では切り替わりません。意図して置換するときだけ、ナレッジスキーマを開いて確認付きの **日本語版に切替** または **英語版に切替** を使います。置換前の本文は履歴から復元できます。文体ではなく構造、引用、改訂、点検を定義し、組み込みスキルと同様に編集・リセットできますが削除はできません。
diff --git a/manual/ja/knowledge-layer.md b/manual/ja/knowledge-layer.md
index d88e63ca1..4af163b8a 100644
--- a/manual/ja/knowledge-layer.md
+++ b/manual/ja/knowledge-layer.md
@@ -6,6 +6,14 @@
ナレッジ層は AI バックエンドがあって初めて動きます。バックエンドは[デスクトップアプリ](/ja/desktop-app)に同梱されており、モデルが 1 つ以上登録されている必要があります。[AI のセットアップ](/ja/ai-setup)を参照してください。ブラウザ版のプレビューでは、**ナレッジ** セクションにナレッジの中身ではなくデスクトップ版への案内が表示されます。
:::
+## ナレッジスキーマ
+
+**ナレッジスキーマ**は、**スキル**一覧にある1件だけの編集可能な組み込み文書です。トピック・問答(Q&A)・知見の生成、出典引用、段階的な改訂、点検の規約を定義します。文体(Voice)とは分離されており、コード側の引用照合、構造化出力検証、安全規則は変更しません。
+
+ナレッジスキーマはスキル保存領域に1件だけ作られます。初回作成時だけ、保存される既定本文はその時点の UI 言語に従います。日本語 UI なら日本語本文、英語 UI なら英語本文です。その後に UI 言語を変えても、本文は自動翻訳・上書き・切り替えされません。意図して変更する場合はナレッジスキーマを開き、バナーの **日本語版に切替** または **英語版に切替** を選びます。Graphium は確認後に対象言語の同梱既定本文へ置換し、置換前の本文は履歴から復元できます。
+
+通常ノートでもナレッジページでもないため、検索、ナレッジ件数、グラフには含まれません。編集は以後の生成だけに効き、既存ナレッジを自動再生成しません。組み込みスキルと同様に削除できず、未編集なら保存言語と同じ言語の新しい既定版へ同期されます。編集済みなら更新通知だけを表示し、**デフォルトに戻す**を選ぶまで本文は変わりません。リセットも保存言語と同じ言語の既定本文を使います。編集、既定版同期、リセット、明示的な言語切替は、それぞれ区別して履歴に残ります。
+
## ノートとナレッジ
実験ノートは「火曜日に何が起きたか」に答えます。ナレッジページは「いま自分は何を知っているか」に答えます。Graphium は両方を保ち、一方向にリンクします。ナレッジページの末尾には必ず **References** セクションがあり、その **Source:** の項目が蒸留元のノートへ戻るリンクになっています。いつでも元の文脈に降りていけます。
@@ -14,12 +22,12 @@
## 種類
-Graphium のナレッジは砂時計の形をたどります。文脈の濃いノートが短い一般的な記述へと絞られ、そこからまた自分の仕事全体へと外向きにつながっていきます — **ノート → 知見 → 洞察** です。この背後にある推論のモデルは [Inference types in Graphium](https://github.com/kumagallium/Graphium/blob/main/docs/inference-types.md) にまとめてあります。トピック(Topic)と問答(Q&A)はこの砂時計の外側にあります。知見ではなく元の資料(問答の場合はチャットのやり取り)を直接読み、洞察の発見には渡されません。
+Graphium のナレッジは砂時計の形をたどります。文脈の濃いノートが短い一般的な記述へと絞られ、そこからまた自分の仕事全体へと外向きにつながっていきます — **ノート → 知見 → 洞察** です。この背後にある推論のモデルは [Inference types in Graphium](https://github.com/kumagallium/Graphium/blob/main/docs/inference-types.md) にまとめてあります。トピックと問答(Q&A)はこの砂時計の外側にあります。知見ではなく元の資料(問答(Q&A)の場合はチャットのやり取り)を直接読み、洞察の発見には渡されません。
| 種類 | 何であるか |
|---|---|
| **トピック** | 概念ごとに束ねる元の資料(ノート・PDF・Word・URL・チャット)から直接組み立てるページ。出典は 1 ホップ(トピック → 資料) |
-| **問答** | [AI チャット](/ja/ai-chat-and-ask)の良い回答を 1 件残すページ。タイトルは尋ねた質問、出典は根拠にしたノート・素材 |
+| **問答(Q&A)** | [AI チャット](/ja/ai-chat-and-ask)の良い回答を 1 件残すページ。タイトルは尋ねた質問、出典は根拠にしたノート・素材 |
| **知見** | ノートや資料から取り出した命題(1 件に 1 つ) |
| **洞察** | 知見から取り出した、他の分野でも通じる関係の型 |
@@ -64,7 +72,7 @@ Graphium にはかつて 4 つ目の種類として **要約** — 1 つのノ
## ナレッジを見る
-サイドバーには **ナレッジ** セクション(既定では畳まれています)があり、**トピック**・**問答**・**知見**・**洞察** が件数付きで並びます(以前の要約が残っていれば末尾に **以前の要約** も出ます)。種類を押すと一覧ビューが開き、次のことができます。
+サイドバーには **ナレッジ** セクション(既定では畳まれています)があり、**トピック**・**問答(Q&A)**・**知見**・**洞察** が件数付きで並びます(以前の要約が残っていれば末尾に **以前の要約** も出ます)。種類を押すと一覧ビューが開き、次のことができます。
- 列: **タイトル**、**種別**、**生成元**(生成元ノートの数。トピックでは、束ねている知見の件数になります)、**参照先** / **被参照**、**モデル**、**作成日**、**更新日**、**世界**(最新の[世界照合](/ja/ai-grounding)の判定)
- 検索、列ごとの種別フィルタ、並べ替え、ドラッグまたは Shift+クリックでの範囲選択
@@ -89,7 +97,7 @@ Graphium にはかつて 4 つ目の種類として **要約** — 1 つのノ
## トピックの統合
-トピックは表記ゆれ・助詞の有無・細かすぎる試料単位のタイトルなどで、同じ概念なのに分かれてしまうことがあります。統合には 4 つの入口があり、コーパス全体をまとめて AI に判断させたい場合以外は設定を開く必要はありません。
+トピックは表記ゆれ・助詞の有無・細かすぎる事例単位のタイトルなどで、同じ概念なのに分かれてしまうことがあります。統合には 4 つの入口があり、コーパス全体をまとめて AI に判断させたい場合以外は設定を開く必要はありません。
- **トピックの一覧** — 2 件以上のトピックを選んで **統合** を押す。残すトピックを選びます。
- **トピックページ自身のバナー** — 近くに候補が見つかったときだけ **似たトピック** のチップが出ます(正規化タイトルの一致、または埋め込みモデル設定時は類似度による判定)。**統合** を押すと、今見ているページへ吸収します。
@@ -142,13 +150,13 @@ Graphium にはかつて 4 つ目の種類として **要約** — 1 つのノ
[AI チャット](/ja/ai-chat-and-ask)の回答に残しておきたいものがあれば、回答の下の **ナレッジにする** を押します。返答をまるごと保存するのではなく、Graphium は **ナレッジ候補(選んで保存)** というピッカーに個別の候補を並べます。それぞれに **知見** か **洞察** のバッジ、タイトル、プレビューが付きます。欲しいものにチェックを入れ(または **すべて選択**)、**選択した 2 件を保存** を押します。ナレッジに入るのは、選んだものだけです。
-## 問答をまるごと残す
+## 問答(Q&A)をまるごと残す {#save-answer-page}
**ナレッジにする** は回答を知見・洞察へ分解します。そうではなく、回答がそのまま良い説明になっていて、尋ねた質問で後から見つけたい — というときは、同じメッセージの下にある **ナレッジに残す** を押します。保存する前に、Graphium は AI をもう一度呼び、それまでの会話を渡した上でその問答を単体で読める記事に書き起こします。「その 1 番目について、どのくらいから試すのが妥当ですか。短くお願いします。」のように会話に寄りかかった回答は、指示語や省略が補われ、タイトルも依頼の言い回し(「短くお願いします」など)を落として単体で意味の通る題になります。回答に表示されていた引用は、書き起こした文にそのまま引き継がれます。保存が終わると、**開く** リンク付きの確認が表示されます。
書き起こしに失敗したとき(AI が設定されていない、呼び出しでエラーになったなど)でも保存は止まりません — これまでどおり回答の文章とタイトル(尋ねた質問)でそのまま保存します。書き起こした結果、元の回答にあった出典が 1 つも残らなかったときも同様にそのまま保存にフォールバックします — 読みやすさのために出典を失う方を避けるためです。共有ノートや素材のチャットでは使えませんが、ノートエディタのチャットパネルに加えて、ノートを開かない[サイドバーのチャット](/ja/ai-chat-and-ask#ノートを開かずに話す)からも使えます。
-保存された後の問答ページはトピックと同じように保守されます。手入れの **点検**(空ページ・出典欠落・古さの各判定)の対象になり、出典照合の対象にもなり、資料が変わったときは **再生成** で資料から作り直せます。新しいノートが既存の問答ページを改訂するのは、そのノートが実際に回答を更新・反証するときだけです — それ以外は問答ページを話題から逸らさず、そのノート自身が新しいトピックになります。
+保存された後の問答(Q&A)ページはトピックと同じように保守されます。手入れの **点検**(空ページ・出典欠落・古さの各判定)の対象になり、出典照合の対象にもなり、資料が変わったときは **再生成** で資料から作り直せます。新しいノートが既存の問答(Q&A)ページを改訂するのは、そのノートが実際に回答を更新・反証するときだけです — それ以外は問答(Q&A)ページを話題から逸らさず、そのノート自身が新しいトピックになります。
## 全体グラフのなかのナレッジ
diff --git a/manual/ja/mcp-server.md b/manual/ja/mcp-server.md
index 5f495b73d..6344682f5 100644
--- a/manual/ja/mcp-server.md
+++ b/manual/ja/mcp-server.md
@@ -111,11 +111,11 @@ pnpm bundle:mcp
どちらも vault に新しいものを書き足しますが、置き場所が違う層で、Graphium がその後の扱い方も層ごとに変えます。
- **`create_note`** は**ノート**を作ります — 人が保守するものです。Graphium はその後、自分からは一切手を触れません。
-- **`save_answer`** は**問答ページ**を作ります — ナレッジ層のページです(アプリ内チャットの「ナレッジに残す」ボタンが作るのと同じ `answer` 種別)。作られたあとは、関連する資料が取り込まれるたびに Graphium が改訂し、ほかのナレッジ層のページと同じく点検・出典照合の対象になります。アプリ内のボタンとの違いが1つあります。「ナレッジに残す」は保存前に会話を渡して単体で読める記事に書き起こしますが、`save_answer` は外部クライアント側の会話に Graphium から手が届かないため、渡された `question`/`answer` をそのまま保存します。本文はあらかじめ単体で読める形にして渡してください。
+- **`save_answer`** は**問答(Q&A)ページ**を作ります — ナレッジ層のページです(アプリ内チャットの「ナレッジに残す」ボタンが作るのと同じ `answer` 種別)。作られたあとは、関連する資料が取り込まれるたびに Graphium が改訂し、ほかのナレッジ層のページと同じく点検・出典照合の対象になります。アプリ内のボタンとの違いが1つあります。「ナレッジに残す」は保存前に会話を渡して単体で読める記事に書き起こしますが、`save_answer` は外部クライアント側の会話に Graphium から手が届かないため、渡された `question`/`answer` をそのまま保存します。本文はあらかじめ単体で読める形にして渡してください。
ノートが増えるにつれて Graphium に面倒を見続けてほしいものは `save_answer`、自分で保守するつもりの一回きりの記録は `create_note` を使います。
-`save_answer` に渡す本文では、**根拠のある文の文末に `[[source:]]` を書いてください**(id は `citations` に渡すもの。同じことを複数の資料が述べているなら並べて書けます)。出典照合は文ごとに「その文が引いた資料に本当に書いてあるか」を照らすので、本文に引用が 1 つも無い問答ページは照合の対象になりません。引用が無いまま保存すると、ツールの返事でその旨を知らせます。
+`save_answer` に渡す本文では、**根拠のある文の文末に `[[source:]]` を書いてください**(id は `citations` に渡すもの。同じことを複数の資料が述べているなら並べて書けます)。出典照合は文ごとに「その文が引いた資料に本当に書いてあるか」を照らすので、本文に引用が 1 つも無い問答(Q&A)ページは照合の対象になりません。引用が無いまま保存すると、ツールの返事でその旨を知らせます。
`save_answer` の引数:
@@ -182,13 +182,13 @@ Graphium はノートの上にナレッジ層を組み立てます。**知見(cl
## あえてやらないこと
-**既存のノートを書き換えることはありません。** `create_note` は新しいノートを足すだけです。`save_answer` も新しい問答ページを足すだけで、あなたのノートには触れません(ただし作られた問答ページ自体は、ノートと違って Graphium のナレッジ層の保守によって後から改訂されます)。MCP 経由でアシスタントが行うどの操作も、あなたが書いたものを上書きしません。
+**既存のノートを書き換えることはありません。** `create_note` は新しいノートを足すだけです。`save_answer` も新しい問答(Q&A)ページを足すだけで、あなたのノートには触れません(ただし作られた問答(Q&A)ページ自体は、ノートと違って Graphium のナレッジ層の保守によって後から改訂されます)。MCP 経由でアシスタントが行うどの操作も、あなたが書いたものを上書きしません。
**来歴を作り出すことはしません。** 実験について会話すれば来歴グラフができあがる、と期待されるかもしれませんが、そうはしていません。これは意図的なものです。来歴は、実際に起きたことの記録です。会話から組み立て直したグラフは、見た目は同じでも意味が違います。それはあなたの手順についての推測であり、照らし合わせる相手がありません。Graphium は、エディタであなたが行ったことから来歴を記録します。モデルが「たぶんこうしたのだろう」と推測したものからは記録しません。
一方で、**書き込みが起きたという事実そのもの**は自動で記録されます。MCP 経由で作られたノートには、誰の依頼で、どのクライアントを通って、どのモデルが書いたのかが残ります。これは推測ではなく観測なので、記録として残せます。
-**MCP で作ったノート・問答ページは再読み込み後に現れます。** どちらの一覧もアプリ側が組み立てるため、Graphium を開いたまま書き込んだものは、次に再読み込みするか再起動したときに一覧に出ます。
+**MCP で作ったノート・問答(Q&A)ページは再読み込み後に現れます。** どちらの一覧もアプリ側が組み立てるため、Graphium を開いたまま書き込んだものは、次に再読み込みするか再起動したときに一覧に出ます。
## うまくいかないとき
diff --git a/manual/ja/roadmap.md b/manual/ja/roadmap.md
index 62263e2d8..c5d6afba9 100644
--- a/manual/ja/roadmap.md
+++ b/manual/ja/roadmap.md
@@ -104,9 +104,9 @@ Graphium は 2026 年 3 月から小さなリリースを重ねてきました
| **v0.76.0** | 2026-09-16 | 取り込みのレポートから [まとめてナレッジ化](/ja/materials-and-citations#bring-in-what-you-already-have) — 今回持ち込んだものをまとめてナレッジ化のパイプラインに流し、件別のトーストで進みます。[回答の根拠](/ja/ai-chat-and-ask#choosing-what-grounds-the-answer) はトピック・知見・洞察・ノート本文を区別して渡すようになり、引用がどの種別に基づくか分かります。[MCP サーバー](/ja/mcp-server) からもナレッジ層を読めるようになりました — 索引を返す `list_topics`、トピックとその知見・出どころノートを一度に返す `get_topic`、そして `search_notes`・`get_note`・`trace_lineage` もナレッジ層まで辿ります。 |
| **v0.77.0** | 2026-09-17 | [出典照合](/ja/ai-grounding) — 知見とトピックの要点が、引いている出典に本当に書いてあるかを確かめられます。判定は「出典に書かれている」「出典と異なる」「出典に見当たらない」などに分かれ、原文からの引用つきで残ります。点検の画面は **ナレッジの手入れ** になり、出典照合のタブと、確認が要るものを溜める一覧が加わりました。AI の判定で知見が一覧から隠れることはなく、アーカイブするかはあなたが決めます。取り込みのたびに自動で照合する設定もあります(既定はオフ)。 |
| **v0.78.0** | 2026-09-17 | [トピックが資料から作られる](/ja/knowledge-layer#種類)ようになりました(カーパシーの LLM Wiki と同じ方式)。ノート・PDF・Word・URL・チャットをナレッジに追加すると、AI が資料を読んで関係するトピックを選び、資料を直接引きながら本文を改訂します。統合しても引用はすべて残り、以前のトピックは実行前に AI の呼び出し回数を確かめてから[資料から作り直せ](/ja/knowledge-layer#トピックを資料から作り直す)ます。[知見と洞察](/ja/settings#ai)は ON/OFF できる拡張になり、新しく使い始めるときはオフです。裏で走る点検は AI を呼ばず、勝手に統合もしなくなりました。1 ノートあたり知見 3 件までの上限もなくなりました。 |
-| **v0.79.0** | 2026-09-18 | [問答ページ](/ja/knowledge-layer#問答をまるごと残す) — AI チャットの良い回答を、尋ねた質問をタイトルにして 1 枚のナレッジページとして残せます。保存後はトピックと同じように AI が保守し、点検・出典照合・資料からの再生成の対象になります。[MCP サーバー](/ja/mcp-server)の `save_answer` を使えば、お使いのチャットからも同じページを作れます。ナレッジページを自分で編集したことがある場合、AI が書き換える直前に[版が自動で残る](/ja/notes-and-editor#history-and-versions)ようになりました。自動の点検を AI 解析(フル)にする[設定](/ja/settings#ai)も加わりました(既定はオフ)。 |
+| **v0.79.0** | 2026-09-18 | [問答(Q&A)ページ](/ja/knowledge-layer#save-answer-page) — AI チャットの良い回答を、尋ねた質問をタイトルにして 1 枚のナレッジページとして残せます。保存後はトピックと同じように AI が保守し、点検・出典照合・資料からの再生成の対象になります。[MCP サーバー](/ja/mcp-server)の `save_answer` を使えば、お使いのチャットからも同じページを作れます。ナレッジページを自分で編集したことがある場合、AI が書き換える直前に[版が自動で残る](/ja/notes-and-editor#history-and-versions)ようになりました。自動の点検を AI 解析(フル)にする[設定](/ja/settings#ai)も加わりました(既定はオフ)。 |
| **v0.80.0** | 2026-09-18 | [長い資料を窓に分けて読む](/ja/knowledge-layer#ノートとナレッジ) — これまで PDF は先頭 80,000 字までしか読まれず、100 ページ級の論文は後半がどの機能からも見えていませんでした。トピックは資料の全文を、少し重なる窓に分けて読み、窓ごとに振り分け・改訂します。進捗には窓の枚数が出て、**停止**は次の窓の境目で効きます。[出典照合](/ja/ai-grounding#出典照合-出典に実際に書いてあるか)も、引用が出典のどこにあったかを表示します(PDF は **12 ページ**、Word は **第 3 段落**)。照合がその場で読んだ原文から機械的に割り出し、モデルには書かせていません。 |
-| **v0.81.0** | 2026-09-18 | [ノートを開かずに話せるチャット](/ja/ai-chat-and-ask#ノートを開かずに話す) — 左サイドバーの **チャット** から、どのノートについてでもない会話ができます。答えるときは、ノート内のチャットと同じように自分のノートとナレッジを横断して探すので、手元に書いたことを踏まえた答えが返ります。⌘K の **AI に質問** はノートを開いていなくても使えるようになり、押すとこのチャットで会話が始まります(ノートを開いていれば、そのノートが引用として添います)。一覧はノート一覧と同じ表で、行を選ぶとピーク、そこから全画面へ。会話には **編集して再実行**・**回答を生成し直す**・**ここから新しいチャット**が付き、良い回答は **ナレッジに残す** で[問答ページ](/ja/knowledge-layer#問答をまるごと残す)になります。問答ページは、会話の文脈を補って単体で読める形に書き起こしてから保存されます。ナレッジの **回答** は **問答** に名前が変わりました。 |
+| **v0.81.0** | 2026-09-18 | [ノートを開かずに話せるチャット](/ja/ai-chat-and-ask#ノートを開かずに話す) — 左サイドバーの **チャット** から、どのノートについてでもない会話ができます。答えるときは、ノート内のチャットと同じように自分のノートとナレッジを横断して探すので、手元に書いたことを踏まえた答えが返ります。⌘K の **AI に質問** はノートを開いていなくても使えるようになり、押すとこのチャットで会話が始まります(ノートを開いていれば、そのノートが引用として添います)。一覧はノート一覧と同じ表で、行を選ぶとピーク、そこから全画面へ。会話には **編集して再実行**・**回答を生成し直す**・**ここから新しいチャット**が付き、良い回答は **ナレッジに残す** で[問答(Q&A)ページ](/ja/knowledge-layer#save-answer-page)になります。問答(Q&A)ページは、会話の文脈を補って単体で読める形に書き起こしてから保存されます。ナレッジの **回答** は **問答(Q&A)** に名前が変わりました。 |
## 要望をお寄せください
diff --git a/manual/ja/settings.md b/manual/ja/settings.md
index f605bf885..68e768c0f 100644
--- a/manual/ja/settings.md
+++ b/manual/ja/settings.md
@@ -77,13 +77,15 @@ AI 関連のすべて。まず状態表示、次にモデルの登録、任意
## ナレッジ管理
+編集可能な**ナレッジスキーマ**はこのタブではなく、サイドバーの**スキル**一覧にあります。これは1件だけの組み込み文書であり、ナレッジ件数、検索、グラフには含まれません。初回作成時だけ、その時点の UI 言語に合わせた既定本文が保存されます。その後に UI 言語を変えても自動翻訳・上書きされません。意図して別言語の同梱既定本文へ置換するときは、ナレッジスキーマを開いて確認付きの言語切替を使います。保存した規約は以後のトピック・問答(Q&A)・知見の生成へ適用され、編集、言語切替、リセットで既存ページが自動再生成されることはありません。
+
[ナレッジ層](/ja/knowledge-layer)のメンテナンス作業です。いずれも LLM を呼ぶので、各ジョブは実行前に確認を求め、トークンのコストは[使用量](#使用量)タブに記録されます。
| グループ | 何ができるか |
|---|---|
| **接続状態** | バックエンドの各コンポーネントの状態を表示します。デスクトップアプリでは **バックエンドを再起動** ボタンも出ます。 |
| **全ナレッジの埋め込みを作り直す** | AI チャットの引用検索を支える埋め込みを作り直します。引用の検索が動かなくなったときに使います。索引に今選んでいる埋め込みモデルのベクトルが残っているかも(全件走査せず)軽く確認し、モデルが変わって作り直しが要りそうなときはここに 1 行の案内を出します——費用が API キーにかかるため、自動では作り直しません。 |
-| **トピックを整理** | 同じ概念を指す既存のトピックどうし(表記ゆれ・助詞の有無・細かすぎる試料単位のトピックなど)を統合し、ページをまとめます。トピックページどうしの統合だけを行い、知見をトピックへ割り当てることはしません(2026-09-17 変更)。統合されたトピックは完全削除ではなくゴミ箱へ送られます。 |
+| **トピックを整理** | 同じ概念を指す既存のトピックどうし(表記ゆれ・助詞の有無・細かすぎる事例単位のトピックなど)を統合し、ページをまとめます。トピックページどうしの統合だけを行い、知見をトピックへ割り当てることはしません(2026-09-17 変更)。統合されたトピックは完全削除ではなくゴミ箱へ送られます。 |
| **ナレッジの一括再生成** | プロンプトやモデルを変えたあと、既存のナレッジページを作り直します。**対象の種類** のフィルタ(**トピック** / **知見** / **洞察** — 要約は新規生成を停止済みのため対象外)、任意のモデル指定、キャンセル、**失敗したナレッジだけ再試行** に対応しています。トピックは資料から作り直すため、トピックを含む一括実行はページ数ではなく資料数ぶん AI を呼びます — 対象件数の横に AI 呼び出し回数を出し、トピックを含むときは実行前に確認します。 |
| **知見をまたぐ洞察を発見** | 知見を似たもの同士のクラスタに分けて順にスキャンし、知見の中にある関係の型を洞察として取り出します(同じ型の知見は 1 件にまとめます)。すべての知見が一度は視野に入るまで回り、必要な LLM 呼び出し回数は手持ちの知見から計算して実行前に表示されます。途中で停止もできます。 |
diff --git a/manual/knowledge-layer.md b/manual/knowledge-layer.md
index 17f621ae9..7565221ce 100644
--- a/manual/knowledge-layer.md
+++ b/manual/knowledge-layer.md
@@ -6,6 +6,14 @@ Notes are your working memory: dated, contextual, full of detail. The Knowledge
The Knowledge layer runs only with an AI backend, which ships inside the [desktop app](/desktop-app), with at least one model registered. See [AI setup](/ai-setup). In the browser preview the **Knowledge** section shows an upgrade notice instead.
:::
+## Knowledge Schema
+
+**Knowledge Schema** is one built-in, editable document in the **Skill** list. It defines the generation conventions for Topics, Answers, and Claims: source citations, incremental revision, and upkeep. It is separate from the writing Voice and does not change Graphium's code-enforced citation checks, structured output validation, or safety rules.
+
+Graphium creates exactly one Schema in the Skill storage area. On first creation only, the saved default follows the current UI language: Japanese UI gets the Japanese Schema body, English UI gets the English body. After that, changing the UI language does not automatically translate, overwrite, or switch the Schema body. To change it deliberately, open the Knowledge Schema and choose **Switch to Japanese** or **Switch to English** in its banner. Graphium asks for confirmation before replacing the body with that language's bundled default; the previous body remains recoverable from History.
+
+It is not a note or a knowledge page, so it is not included in search, Knowledge counts, or the graph. Edits affect later generation only; they never regenerate existing knowledge automatically. Like other built-in skills, it cannot be deleted. A new app default updates an untouched Schema automatically in the Schema's saved language; an edited Schema shows an update badge and remains unchanged until you choose **Reset to default**, which also uses that saved language. Editing, default synchronization, Reset, and explicit language switches leave distinct entries in the document History.
+
## Notes vs. knowledge
A lab note answers "what happened on Tuesday". A knowledge page answers "what do I now know". Graphium keeps both, linked in one direction: every knowledge page ends with a **References** section whose **Source:** entries link back to the notes it was distilled from, so you can always drop back into the original context.
@@ -89,7 +97,7 @@ The body ends with a **References** section linking back to sources. Keep in min
## Merging topics
-Topics can drift apart over wording, particles, or an overly narrow per-sample title even when they're the same concept. There are four ways to merge them, none of which need you to open Settings unless you want the AI to judge the whole corpus at once:
+Topics can drift apart over wording, particles, or an overly narrow per-case title even when they're the same concept. There are four ways to merge them, none of which need you to open Settings unless you want the AI to judge the whole corpus at once:
- **Topics list** — select 2 or more Topics and press **Merge**; pick which one to keep.
- **A topic's own banner** — shows a **Similar topics** chip when a candidate is found nearby (same normalized title, or embedding similarity when an embedding model is set); press **Merge** to absorb it into the page you're viewing.
diff --git a/manual/settings.md b/manual/settings.md
index c46138b83..97487af45 100644
--- a/manual/settings.md
+++ b/manual/settings.md
@@ -77,13 +77,15 @@ A browser for the knowledge base that [world grounding](/ai-grounding) checks ag
## Knowledge
+The editable **Knowledge Schema** lives in the sidebar's **Skill** list rather than this tab. It is one built-in document, excluded from Knowledge counts, search, and graphs. On first creation only, Graphium saves the default body in the current UI language; later UI language changes do not translate or overwrite it. Open the Schema and use the confirmed language-switch action when you deliberately want to replace it with the other bundled language default. Its saved rules apply to later Topic, Answer, and Claim generation; changing, switching, or resetting it does not automatically regenerate existing pages.
+
Maintenance jobs for the [Knowledge layer](/knowledge-layer). These run LLM calls, so each job asks for confirmation and reports token cost on the [Usage](#usage) tab.
| Group | What it does |
|---|---|
| **Connection Status** | Shows the health of each backend component, with a **Restart backend** button on the desktop app. |
| **Re-embed all Knowledge** | Rebuilds the embeddings behind AI chat citation search — use it if citation lookup stops working. Graphium also checks (cheaply, without a full scan) whether the index still has any vectors from the currently selected embedding model, and shows a one-line notice here if it looks like the model changed and the index needs a rebuild — nothing rebuilds automatically, since it spends your embedding API budget. |
-| **Organize topics** | Consolidates existing topics that name the same concept (wording variants, particle differences, over-fragmented per-sample topics) and merges their pages. It only consolidates topic pages — it doesn't assign or reassign Claims to topics (changed 2026-09-17). Consolidated topics are sent to Trash, not deleted outright. |
+| **Organize topics** | Consolidates existing topics that name the same concept (wording variants, particle differences, over-fragmented per-case topics) and merges their pages. It only consolidates topic pages — it doesn't assign or reassign Claims to topics (changed 2026-09-17). Consolidated topics are sent to Trash, not deleted outright. |
| **Bulk regenerate Knowledge** | Rebuilds existing Knowledge pages after you change prompts or models, with **Target kinds** filters (**Topics** / **Claims** / **Insights** — Summaries are no longer generated and are not a regenerate target), an optional model override, cancel support, and **Retry failed only**. Topics are rebuilt from their sources, so a batch that includes Topics issues one AI call per source, not one per page — the AI-call total is shown next to the target count, and confirmed before running whenever Topics are included. |
| **Discover Insights from Claims** | Scans your Claims cluster by cluster until every one has been in view at least once, pulling out the relationship patterns in them as Insights (Claims that share a pattern are folded into one). The number of LLM calls needed is measured from your corpus and shown before running; you can stop anytime. |
diff --git a/src/features/ai-assistant/api.ts b/src/features/ai-assistant/api.ts
index b5125ff73..a826d192f 100644
--- a/src/features/ai-assistant/api.ts
+++ b/src/features/ai-assistant/api.ts
@@ -5,6 +5,9 @@ import { apiBase, isTauri } from "../../lib/platform";
import type { GroundingScope } from "../../lib/grounding-scope";
import { aiErrorFromResponse } from "../../lib/ai-error";
import { getEnabledMcpServers, getDefaultLLMModel, getChatSynthesisLLMModel, getChatSynthesisModelName } from "../settings/store";
+import { getActiveProvider } from "../../lib/storage/registry";
+import { migrateToLatest } from "../../lib/document-migration";
+import { loadKnowledgeSchemaPrompt } from "../skill/skill-service";
/**
* Registry URL・LLM API キーが設定されている場合はヘッダーに含める。
@@ -71,6 +74,8 @@ export type AgentRunRequest = {
grounding_scope?: GroundingScope;
/** 構造化出力用のシステムプロンプトに使う言語("en" | "ja") */
language?: string;
+ /** 保存済み Knowledge Schema。通常 Skill の custom_instructions とは別に注入する。 */
+ knowledge_schema?: string;
options?: {
max_turns?: number;
model?: string;
@@ -184,10 +189,17 @@ export async function runAgent(
req: AgentRunRequest,
signal?: AbortSignal,
): Promise {
+ // Assistant の全経路で同じ保存済み Schema を使う。欠落・読み込み失敗時は
+ // 既定本文へ置換せず、呼び出し元の既存エラー表示へ伝播する。
+ const knowledgeSchema = req.knowledge_schema ?? await loadKnowledgeSchemaPrompt(async (id) => {
+ const provider = getActiveProvider();
+ if (!provider.loadSkillFile) throw new Error("Skill 非対応のストレージプロバイダーです");
+ return migrateToLatest(await provider.loadSkillFile(id), id);
+ });
const res = await fetch(`${apiBase()}/agent/run`, {
method: "POST",
headers: apiHeaders("chat"),
- body: JSON.stringify(req),
+ body: JSON.stringify({ ...req, knowledge_schema: knowledgeSchema }),
signal,
});
diff --git a/src/features/document-provenance/activity-label.ts b/src/features/document-provenance/activity-label.ts
index 6253f8616..fc91f3d0a 100644
--- a/src/features/document-provenance/activity-label.ts
+++ b/src/features/document-provenance/activity-label.ts
@@ -19,6 +19,10 @@ export function activityTypeLabelKey(type: string): string | null {
case "wiki_atomize": return "history.type.wikiAtomize";
case "wiki_reinforce": return "history.type.wikiReinforce";
case "skill_default_update": return "history.type.skillDefaultUpdate";
+ case "knowledge_schema_edit": return "history.type.knowledgeSchemaEdit";
+ case "knowledge_schema_default_update": return "history.type.knowledgeSchemaDefaultUpdate";
+ case "knowledge_schema_reset": return "history.type.knowledgeSchemaReset";
+ case "knowledge_schema_language_switch": return "history.type.knowledgeSchemaLanguageSwitch";
case "snapshot_restore": return "history.type.snapshotRestore";
case "proposal_adopt": return "history.type.proposalAdopt";
default: return null;
diff --git a/src/features/document-provenance/tracker.human-edit.test.ts b/src/features/document-provenance/tracker.human-edit.test.ts
index f1dc91dee..9e4b2661e 100644
--- a/src/features/document-provenance/tracker.human-edit.test.ts
+++ b/src/features/document-provenance/tracker.human-edit.test.ts
@@ -8,10 +8,13 @@ function makeActivity(type: EditActivity["type"]): EditActivity {
}
describe("isHumanActivityType", () => {
- it("human_edit / human_derivation / derive_source / snapshot_restore / proposal_adopt は人間の操作", () => {
+ it("human-owned document の編集・Reset は人間の操作", () => {
expect(isHumanActivityType("human_edit")).toBe(true);
expect(isHumanActivityType("human_derivation")).toBe(true);
expect(isHumanActivityType("derive_source")).toBe(true);
+ expect(isHumanActivityType("knowledge_schema_edit")).toBe(true);
+ expect(isHumanActivityType("knowledge_schema_reset")).toBe(true);
+ expect(isHumanActivityType("knowledge_schema_language_switch")).toBe(true);
expect(isHumanActivityType("snapshot_restore")).toBe(true);
expect(isHumanActivityType("proposal_adopt")).toBe(true);
});
@@ -27,6 +30,7 @@ describe("isHumanActivityType", () => {
expect(isHumanActivityType("wiki_atomize")).toBe(false);
expect(isHumanActivityType("wiki_reinforce")).toBe(false);
expect(isHumanActivityType("skill_default_update")).toBe(false);
+ expect(isHumanActivityType("knowledge_schema_default_update")).toBe(false);
});
});
diff --git a/src/features/document-provenance/tracker.ts b/src/features/document-provenance/tracker.ts
index 750a15469..cb13d8c16 100644
--- a/src/features/document-provenance/tracker.ts
+++ b/src/features/document-provenance/tracker.ts
@@ -25,6 +25,9 @@ export function isHumanActivityType(activityType: EditActivityType): boolean {
activityType === "human_edit" ||
activityType === "human_derivation" ||
activityType === "derive_source" ||
+ activityType === "knowledge_schema_edit" ||
+ activityType === "knowledge_schema_reset" ||
+ activityType === "knowledge_schema_language_switch" ||
activityType === "snapshot_restore" ||
activityType === "proposal_adopt"
);
diff --git a/src/features/document-provenance/types.ts b/src/features/document-provenance/types.ts
index 32ece5654..1c91c4789 100644
--- a/src/features/document-provenance/types.ts
+++ b/src/features/document-provenance/types.ts
@@ -82,6 +82,14 @@ export type EditActivityType =
| "wiki_reinforce"
/** システムスキルのデフォルト内容への同期(未編集スキルの自動更新 / Reset to default) */
| "skill_default_update"
+ /** Knowledge Schema の通常編集 */
+ | "knowledge_schema_edit"
+ /** Knowledge Schema の同梱既定版への同期 */
+ | "knowledge_schema_default_update"
+ /** Knowledge Schema の明示 Reset */
+ | "knowledge_schema_reset"
+ /** ユーザーが Knowledge Schema の言語別既定本文へ明示的に切り替え */
+ | "knowledge_schema_language_switch"
/** 手動で残した版(スナップショット)への復元。ユーザー操作なので human agent に紐づく */
| "snapshot_restore"
/** 共有された「変更の提案」の取り込み。ユーザー操作なので human agent に紐づく。
diff --git a/src/features/skill/SkillBanner.test.tsx b/src/features/skill/SkillBanner.test.tsx
new file mode 100644
index 000000000..de54a9f5b
--- /dev/null
+++ b/src/features/skill/SkillBanner.test.tsx
@@ -0,0 +1,89 @@
+// @vitest-environment jsdom
+
+import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { LocaleProvider, syncLocale } from "../../i18n";
+import { SkillBanner } from "./SkillBanner";
+
+describe("SkillBanner", () => {
+ beforeEach(() => {
+ syncLocale("ja");
+ vi.restoreAllMocks();
+ });
+
+ afterEach(() => {
+ cleanup();
+ });
+
+ it("Knowledge Schemaを開くと現在と反対の言語への切替を表示する", () => {
+ render(
+
+
+ ,
+ );
+
+ expect(screen.getByRole("button", { name: "日本語版に切替" })).toBeTruthy();
+ });
+
+ it("確認をキャンセルすると切替を実行しない", () => {
+ const onSwitch = vi.fn();
+ vi.spyOn(window, "confirm").mockReturnValue(false);
+ render(
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByRole("button", { name: "英語版に切替" }));
+
+ expect(window.confirm).toHaveBeenCalledWith(expect.stringContaining("復元可能な改訂版"));
+ expect(onSwitch).not.toHaveBeenCalled();
+ });
+
+ it("確認すると対象言語への切替を実行する", async () => {
+ const onSwitch = vi.fn().mockResolvedValue(undefined);
+ vi.spyOn(window, "confirm").mockReturnValue(true);
+ render(
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByRole("button", { name: "英語版に切替" }));
+
+ await waitFor(() => expect(onSwitch).toHaveBeenCalledWith("en"));
+ });
+
+ it("通常Skillには言語切替を表示しない", () => {
+ render(
+
+
+ ,
+ );
+
+ expect(screen.queryByText("英語版に切替")).toBeNull();
+ });
+});
diff --git a/src/features/skill/SkillBanner.tsx b/src/features/skill/SkillBanner.tsx
index 0c4cadbd3..4104d9730 100644
--- a/src/features/skill/SkillBanner.tsx
+++ b/src/features/skill/SkillBanner.tsx
@@ -1,17 +1,46 @@
// Skill バナー(エディタヘッダーに表示)
// Wiki の WikiBanner と同等の役割
-import { Wrench, Zap, Pencil } from "lucide-react";
+import { useState } from "react";
+import { Languages, Wrench, Zap, Pencil } from "lucide-react";
import { useT } from "../../i18n";
type Props = {
availableForIngest: boolean;
+ systemSkillId?: string;
+ language?: "ja" | "en";
/** 「編集」ボタン押下時。未指定なら編集ボタンを表示しない。 */
onEdit?: () => void;
+ onSwitchKnowledgeSchemaLanguage?: (language: "ja" | "en") => Promise;
};
-export function SkillBanner({ availableForIngest, onEdit }: Props) {
+export function SkillBanner({
+ availableForIngest,
+ systemSkillId,
+ language,
+ onEdit,
+ onSwitchKnowledgeSchemaLanguage,
+}: Props) {
const t = useT();
+ const [switching, setSwitching] = useState(false);
+ const targetLanguage = language === "ja" ? "en" : "ja";
+ const canSwitchLanguage =
+ systemSkillId === "knowledge-schema" &&
+ (language === "ja" || language === "en") &&
+ onSwitchKnowledgeSchemaLanguage;
+
+ const handleSwitchLanguage = async () => {
+ if (!canSwitchLanguage || switching) return;
+ const targetLabel = targetLanguage === "ja" ? t("skill.langJa") : t("skill.langEn");
+ if (!window.confirm(t("skill.switchSchemaLanguageConfirm", { language: targetLabel }))) return;
+ setSwitching(true);
+ try {
+ await onSwitchKnowledgeSchemaLanguage(targetLanguage);
+ } finally {
+ setSwitching(false);
+ }
+ };
+
return (