mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-30 04:15:45 +08:00
Docs/rag engine plugin architecture (#82)
* docs: update wiki for RAG engine plugin architecture Replace KnowledgeRetriever component docs with RAGEngine across all three languages (zh/en/ja). Key changes: - Add new rag-engine.mdx component docs covering RAGEngine base class, creation_schema/retrieval_schema, capability declarations, lifecycle hooks, host RAG APIs, and complete code examples - Update knowledge base usage docs to reflect unified KB model (remove internal/external distinction, add RAG engine selection flow) - Update component list (add.mdx) and plugin intro to reference RAGEngine - Add RAG API section to plugin common APIs (invoke_embedding, vector_upsert/search/delete, get_rag_file_stream) - Update docs.json navigation to point to rag-engine pages Companion to LangBot PR #1995 and SDK PR #34. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: sync with sdk * docs: update vector API docs with metadata filters, search_type, and documents support - Replace Warning tags about unimplemented filters with Info tags documenting canonical filter format (Chroma-style where syntax) and supported operators - Document search_type/query_text params for full-text and hybrid search - Document documents param for vector_upsert - Update RetrievalContext.filters description from reserved to active - Add filter usage examples (en/zh/ja) Co-Authored-By: Claude Opus 4.6 <[email protected]> * docs: enhance RAG Engine documentation across multiple languages - Expanded the description of the RAG Engine component to include bridging capabilities with advanced knowledge base services like Dify, RAGFlow, and FastGPT. - Clarified the `DOC_INGESTION` capability description, specifying conditions under which the declaration can be omitted. - Updated documentation in English, Japanese, and Chinese to ensure consistency and clarity. * docs: add instructions for show_if conditional rendering * docs: add Parser component documentation (zh/en/ja) Add Parser as a new component type in plugin docs. Parser extracts structured text from binary files (PDF, Word, etc.) before RAG engine ingestion. Includes manifest format, parse method API, data models (ParseContext/ParseResult/TextSection), RAGEngine integration via parsed_content, and cross-plugin invoke_parser API. Also add DOC_PARSING capability to RAG engine docs and parsed_content field to IngestionContext. Companion to SDK PR langbot-app/langbot-plugin-sdk#37 and LangBot PR langbot-app/LangBot#2000. Co-Authored-By: Claude Opus 4.6 <[email protected]> * docs: rename RAGEngine components to KnowledgeEngine * refactor: change rag engine to knowledge engine --------- Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Junyan Qin <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
Junyan Qin
parent
ebfdefd2bb
commit
632c23e841
@@ -307,4 +307,192 @@ async def get_config_file(self, file_key: str) -> bytes:
|
||||
file_bytes = await self.plugin.get_config_file("key")
|
||||
```
|
||||
|
||||
これは[``fileまたはarray[file``](/en/plugin/dev/basic-info.html#type-file)型の設定フィールドと組み合わせて使用します。
|
||||
これは[``fileまたはarray[file``](/en/plugin/dev/basic-info.html#type-file)型の設定フィールドと組み合わせて使用します。
|
||||
|
||||
## RAG API
|
||||
|
||||
これらのAPIは`KnowledgeEngine`コンポーネントがLangBotホストの埋め込みモデル、ベクトルデータベース、ファイルストレージにアクセスするために使用できます。アクセス方法:
|
||||
|
||||
- `KnowledgeEngine`コンポーネントクラス内: `self.plugin`オブジェクトの内部メソッド。
|
||||
|
||||
### 埋め込みモデルの呼び出し
|
||||
|
||||
ホストに設定された埋め込みモデルを使用してテキストのベクトルを生成します。
|
||||
|
||||
```python
|
||||
async def invoke_embedding(
|
||||
self,
|
||||
embedding_model_uuid: str,
|
||||
texts: list[str],
|
||||
) -> list[list[float]]:
|
||||
"""埋め込みモデルを使用してベクトルを生成
|
||||
|
||||
Args:
|
||||
embedding_model_uuid: 埋め込みモデルUUID
|
||||
texts: 埋め込むテキストのリスト
|
||||
|
||||
Returns:
|
||||
ベクトルのリスト、入力テキストごとに1つ
|
||||
"""
|
||||
|
||||
# 使用例
|
||||
vectors = await self.plugin.invoke_embedding("model_uuid", ["Hello", "World"])
|
||||
```
|
||||
|
||||
### ベクトルの書き込み
|
||||
|
||||
ホストのベクトルデータベースにベクトルを書き込みます。
|
||||
|
||||
```python
|
||||
async def vector_upsert(
|
||||
self,
|
||||
collection_id: str,
|
||||
vectors: list[list[float]],
|
||||
ids: list[str],
|
||||
metadata: list[dict[str, Any]] | None = None,
|
||||
documents: list[str] | None = None,
|
||||
) -> None:
|
||||
"""ベクトルの書き込み
|
||||
|
||||
Args:
|
||||
collection_id: ターゲットコレクションID
|
||||
vectors: ベクトルのリスト
|
||||
ids: ベクトルの一意識別子リスト
|
||||
metadata: オプションのメタデータリスト
|
||||
documents: オプションの生テキストドキュメントリスト。全文検索や
|
||||
混合検索をサポートするバックエンドで必要です。
|
||||
"""
|
||||
|
||||
# 使用例
|
||||
await self.plugin.vector_upsert(
|
||||
collection_id="kb_uuid",
|
||||
vectors=[[0.1, 0.2, ...], [0.3, 0.4, ...]],
|
||||
ids=["chunk_0", "chunk_1"],
|
||||
metadata=[{"document_id": "doc1"}, {"document_id": "doc1"}],
|
||||
documents=["チャンクテキスト 0", "チャンクテキスト 1"],
|
||||
)
|
||||
```
|
||||
|
||||
### ベクトル検索
|
||||
|
||||
ホストのベクトルデータベースで類似ベクトルを検索します。
|
||||
|
||||
```python
|
||||
async def vector_search(
|
||||
self,
|
||||
collection_id: str,
|
||||
query_vector: list[float],
|
||||
top_k: int = 5,
|
||||
filters: dict[str, Any] | None = None,
|
||||
search_type: str = "vector",
|
||||
query_text: str = "",
|
||||
) -> list[dict[str, Any]]:
|
||||
"""ベクトル検索
|
||||
|
||||
Args:
|
||||
collection_id: ターゲットコレクションID
|
||||
query_vector: 類似性検索のクエリベクトル
|
||||
top_k: 返す結果の数
|
||||
filters: オプションのメタデータフィルター
|
||||
search_type: 検索方式、'vector'、'full_text'、'hybrid' のいずれか
|
||||
query_text: 生のクエリテキスト、全文検索と混合検索で使用
|
||||
|
||||
Returns:
|
||||
検索結果のリスト(id, score, metadataなどを含むdict)
|
||||
"""
|
||||
|
||||
# 使用例
|
||||
results = await self.plugin.vector_search(
|
||||
collection_id="kb_uuid",
|
||||
query_vector=[0.1, 0.2, ...],
|
||||
top_k=5,
|
||||
search_type="hybrid",
|
||||
query_text="検索クエリ",
|
||||
)
|
||||
# 返却フォーマット: [{"id": "chunk_0", "score": 0.123, "metadata": {"document_id": "doc1", ...}}, ...]
|
||||
```
|
||||
|
||||
<Info>
|
||||
`vector_search`が返す各結果はdictで、`id`(ベクトルID)、`score`(距離スコア)、`metadata`(upsert時に提供したメタデータ)の3つのフィールドを含みます。検索結果にテキスト内容が必要な場合は、取り込み時にmetadataにテキストを保存してください。
|
||||
</Info>
|
||||
|
||||
### ベクトルの削除
|
||||
|
||||
ホストのベクトルデータベースからベクトルを削除します。
|
||||
|
||||
```python
|
||||
async def vector_delete(
|
||||
self,
|
||||
collection_id: str,
|
||||
file_ids: list[str] | None = None,
|
||||
filters: dict[str, Any] | None = None,
|
||||
) -> int:
|
||||
"""ベクトルの削除
|
||||
|
||||
Args:
|
||||
collection_id: ターゲットコレクションID
|
||||
file_ids: 削除するファイルIDのリスト
|
||||
filters: オプションのメタデータフィルター
|
||||
|
||||
Returns:
|
||||
削除されたアイテム数
|
||||
"""
|
||||
|
||||
# 使用例
|
||||
deleted = await self.plugin.vector_delete(
|
||||
collection_id="kb_uuid",
|
||||
file_ids=["doc_001"],
|
||||
)
|
||||
```
|
||||
|
||||
<Info>
|
||||
`filters`パラメータはChromaスタイルの`where`構文によるメタデータフィルタリングをサポートしています。複数のトップレベルキーはAND条件として結合されます。サポートされる演算子:`$eq`、`$ne`、`$gt`、`$gte`、`$lt`、`$lte`、`$in`、`$nin`。
|
||||
|
||||
```python
|
||||
# 暗黙的な $eq
|
||||
results = await self.plugin.vector_search(
|
||||
collection_id="kb_uuid",
|
||||
query_vector=[0.1, 0.2, ...],
|
||||
filters={"file_id": "abc"},
|
||||
)
|
||||
|
||||
# 比較演算子
|
||||
results = await self.plugin.vector_search(
|
||||
collection_id="kb_uuid",
|
||||
query_vector=[0.1, 0.2, ...],
|
||||
filters={"created_at": {"$gte": 1700000000}},
|
||||
)
|
||||
|
||||
# リスト演算子
|
||||
results = await self.plugin.vector_search(
|
||||
collection_id="kb_uuid",
|
||||
query_vector=[0.1, 0.2, ...],
|
||||
filters={"file_type": {"$in": ["pdf", "docx"]}},
|
||||
)
|
||||
|
||||
# フィルターによる削除
|
||||
deleted = await self.plugin.vector_delete(
|
||||
collection_id="kb_uuid",
|
||||
filters={"file_type": {"$eq": "pdf"}},
|
||||
)
|
||||
```
|
||||
|
||||
**注意:** Chroma、Qdrant、SeekDBは完全なメタデータを保存し、任意のフィールドでフィルタリングできます。MilvusとpgvectorはDB側に`text`、`file_id`、`chunk_uuid`のみ保存しており、それ以外のフィールドでのフィルタリングは無視されます。
|
||||
</Info>
|
||||
|
||||
ホストストレージからアップロードされたファイルの内容を取得します。
|
||||
|
||||
```python
|
||||
async def get_knowledge_file_stream(self, storage_path: str) -> bytes:
|
||||
"""ファイル内容を取得
|
||||
|
||||
Args:
|
||||
storage_path: ファイルのストレージパス(FileObject.storage_pathから取得)
|
||||
|
||||
Returns:
|
||||
ファイル内容のバイトデータ
|
||||
"""
|
||||
|
||||
# 使用例
|
||||
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
|
||||
```
|
||||
@@ -56,6 +56,10 @@ spec:
|
||||
zh_Hans: 如果不填的話、图片可能會下載失败
|
||||
default: '' # 設定項目のデフォルト値、オプション。
|
||||
required: false # 設定項目が必須かどうか、オプション。
|
||||
show_if: # 条件付きレンダリング(条件を満たす場合にのみ表示)、オプション。
|
||||
field: mode
|
||||
operator: eq
|
||||
value: mode1
|
||||
- name: mode
|
||||
type: select
|
||||
...
|
||||
@@ -63,6 +67,21 @@ spec:
|
||||
|
||||
設定項目でサポートされるタイプとフィールドは次のとおりです:
|
||||
|
||||
### 条件付きレンダリング (`show_if`)
|
||||
|
||||
すべてのタイプの設定項目は、`show_if` フィールドを使用した条件付きレンダリングをサポートしています。同じ(または関連する)フォーム内の他のフィールドの値に基づいて、現在のフィールドをレンダリングして表示するかどうかを決定できます。
|
||||
|
||||
```yaml
|
||||
- name: advanced_setting
|
||||
type: string
|
||||
show_if:
|
||||
field: mode # 依存するフィールドの名前
|
||||
operator: eq # 判定演算子。'eq' (等しい)、'neq' (等しくない)、'in' (リストに含まれる) をサポートします
|
||||
value: advanced # 判断の基準値
|
||||
```
|
||||
|
||||
> **注意:** クロスフォームのカスケード関係(例:RAG エンジンの `creation_schema` 設定が `retrieval_schema` の値を読み取る、またはその逆)は、LangBot の最新フロントエンドでサポートされています。
|
||||
|
||||
### type: string
|
||||
|
||||
文字列型。
|
||||
|
||||
@@ -7,7 +7,8 @@ title: "コンポーネントの追加"
|
||||
- イベントリスナー(EventListener): パイプライン実行中のイベントをリッスンし、コンテキストやパイプラインを変更します。
|
||||
- コマンド(Command): `!`(または他の設定されたプレフィックス)で始まるユーザーコマンドメッセージによってトリガーされます。
|
||||
- ツール(Tool): LangBotの組み込みLocal Agentの実行中にLLMによって呼び出されます。
|
||||
- ナレッジリトリーバー(KnowledgeRetriever): LangBotに外部ナレッジベース検索機能を提供し、さまざまなRAGシステムとの統合をサポートします。
|
||||
- RAGエンジン(KnowledgeEngine): LangBotにナレッジベースのインデックス作成と検索機能を提供し、ドキュメントの取り込み、検索、削除を管理します。
|
||||
- パーサー(Parser): ドキュメント取り込みの前にPDF、Wordなどのバイナリファイルを構造化テキストに解析し、RAGエンジンで使用できるようにします。
|
||||
|
||||
## コンポーネントの追加
|
||||
|
||||
@@ -71,4 +72,5 @@ components:
|
||||
- [EventListener](./event-listener)
|
||||
- [Command](./command)
|
||||
- [Tool](./tool)
|
||||
- [KnowledgeRetriever](./knowledge-retriever)
|
||||
- [KnowledgeEngine](./knowledge-engine)
|
||||
- [Parser](./parser)
|
||||
|
||||
@@ -0,0 +1,479 @@
|
||||
---
|
||||
title: "コンポーネント: 知識エンジン"
|
||||
---
|
||||
|
||||
RAGエンジン(KnowledgeEngine)コンポーネントにより、プラグインはLangBotにナレッジベースの完全なインデックス作成と検索機能を提供できます。ユーザーがLangBotでナレッジベースを作成する際、プラグインが提供するRAGエンジンを選択して、ドキュメントの取り込み、検索、削除を管理できます。また、プラグインを通じてDify、RAGFlow、FastGPTなどの業界先進のナレッジベースサービスとブリッジすることも可能です。
|
||||
|
||||
## RAGエンジンコンポーネントの追加
|
||||
|
||||
1つのプラグインには任意の数のRAGエンジンを追加できます。プラグインディレクトリでコマンド`lbp comp KnowledgeEngine`を実行し、プロンプトに従ってRAGエンジンの設定を入力します。
|
||||
|
||||
```bash
|
||||
➜ MyRAGPlugin > lbp comp KnowledgeEngine
|
||||
Generating component KnowledgeEngine...
|
||||
Knowledge Engine name: simple_rag
|
||||
Knowledge Engine description: A simple Knowledge Engine with built-in chunking and retrieval
|
||||
Component KnowledgeEngine generated successfully.
|
||||
組件 KnowledgeEngine 生成成功。
|
||||
```
|
||||
|
||||
これにより、`components/knowledge_engine/`ディレクトリに`simple_rag.yaml`と`simple_rag.py`ファイルが生成されます。`.yaml`ファイルはRAGエンジンの基本情報と設定スキーマを定義し、`.py`ファイルはこのエンジンのハンドラです:
|
||||
|
||||
```bash
|
||||
➜ MyRAGPlugin > tree
|
||||
...
|
||||
├── components
|
||||
│ ├── __init__.py
|
||||
│ └── knowledge_engine
|
||||
│ ├── __init__.py
|
||||
│ ├── simple_rag.py
|
||||
│ └── simple_rag.yaml
|
||||
...
|
||||
```
|
||||
|
||||
## マニフェストファイル: RAGエンジン
|
||||
|
||||
```yaml
|
||||
apiVersion: v1 # 変更しないでください
|
||||
kind: KnowledgeEngine # 変更しないでください
|
||||
metadata:
|
||||
name: simple_rag # RAGエンジン名、このエンジンを識別するために使用
|
||||
label:
|
||||
en_US: Simple Knowledge Engine # エンジン表示名、LangBotのUIに表示、多言語対応
|
||||
zh_Hans: 简易 RAG 引擎
|
||||
ja_JP: シンプル RAG エンジン
|
||||
description:
|
||||
en_US: 'A simple Knowledge Engine with built-in chunking and retrieval'
|
||||
zh_Hans: '内置分块和检索能力的简易 RAG 引擎'
|
||||
ja_JP: 'チャンキングと検索を内蔵したシンプルなRAGエンジン'
|
||||
spec:
|
||||
creation_schema: # ナレッジベース作成時にユーザーが入力する設定パラメータ
|
||||
- name: chunk_size
|
||||
label:
|
||||
en_US: Chunk Size
|
||||
zh_Hans: 分块大小
|
||||
type: integer
|
||||
required: false
|
||||
default: 500
|
||||
- name: chunk_overlap
|
||||
label:
|
||||
en_US: Chunk Overlap
|
||||
zh_Hans: 分块重叠
|
||||
type: integer
|
||||
required: false
|
||||
default: 50
|
||||
retrieval_schema: # 検索時に設定可能なパラメータ
|
||||
- name: score_threshold
|
||||
label:
|
||||
en_US: Score Threshold
|
||||
zh_Hans: 分数阈值
|
||||
type: float
|
||||
required: false
|
||||
default: 0.5
|
||||
execution:
|
||||
python:
|
||||
path: simple_rag.py # エンジンハンドラ、変更しないでください
|
||||
attr: SimpleRag # エンジンハンドラのクラス名、simple_rag.pyのクラス名と一致
|
||||
```
|
||||
|
||||
設定項目フォーマットのリファレンスについては、[プラグインマニフェスト設定フォーマット](/ja/plugin/dev/basic-info)を参照してください。
|
||||
|
||||
### creation_schema と retrieval_schema
|
||||
|
||||
旧バージョンの`KnowledgeRetriever`コンポーネントが単一の`spec.config`を使用していたのとは異なり、KnowledgeEngineは2つの独立したスキーマを使用します:
|
||||
|
||||
- **creation_schema**: ナレッジベース作成時にユーザーが入力するパラメータ。取り込みと検索時に`creation_settings`を通じてプラグインに渡されます。
|
||||
- **retrieval_schema**: ナレッジベースのクエリ時にユーザーが調整できるパラメータ。`retrieval_settings`を通じてプラグインに渡されます。
|
||||
|
||||
## 能力宣言
|
||||
|
||||
KnowledgeEngineはサポートする能力を宣言でき、LangBotはこれらの能力宣言に基づいてUIの表示と利用可能な操作を決定します:
|
||||
|
||||
```python
|
||||
from langbot_plugin.api.definition.components.knowledge_engine.engine import KnowledgeEngine, KnowledgeEngineCapability
|
||||
|
||||
class SimpleRag(KnowledgeEngine):
|
||||
|
||||
@classmethod
|
||||
def get_capabilities(cls) -> list[str]:
|
||||
return [
|
||||
KnowledgeEngineCapability.DOC_INGESTION, # ドキュメントのアップロードと処理をサポート
|
||||
]
|
||||
```
|
||||
|
||||
利用可能な能力定数:
|
||||
|
||||
| 能力 | 説明 |
|
||||
|------|------|
|
||||
| `DOC_INGESTION` | ドキュメントのアップロードと処理をサポート。宣言すると、LangBotはナレッジベースの詳細に「ドキュメント」タブを表示します。プラグインが他のナレッジベースサービスとのブリッジ用である場合や、ユーザーによるドキュメントのアップロードが不要な場合は、この能力宣言を省略できます。 |
|
||||
| `DOC_PARSING` | ネイティブドキュメント解析(ファイルからテキストへの抽出)をサポート。宣言すると、ユーザーはドキュメントアップロード時にRAGエンジンの内蔵解析機能を選択でき、外部[Parserプラグイン](./parser)が不要になります。未宣言の場合、ドキュメントをアップロードするには外部Parserプラグインのインストールが必要です。 |
|
||||
|
||||
<Info>
|
||||
その他の検索動作(リランキング、ハイブリッド検索など)はプラグインの`retrieval_schema`で制御され、能力フラグは不要です。
|
||||
</Info>
|
||||
|
||||
## プラグインハンドラ
|
||||
|
||||
以下のコードがデフォルトで生成されます(`components/knowledge_engine/<エンジン名>.py`)。`ingest`、`retrieve`、`delete_document`の3つのコアメソッドを実装する必要があります。完全なコードは[langbot-plugin-demo](https://github.com/langbot-app/langbot-plugin-demo)の`SimpleKnowledgeEngine`で見つけることができます。
|
||||
|
||||
```python
|
||||
from langbot_plugin.api.definition.components.knowledge_engine.engine import KnowledgeEngine, KnowledgeEngineCapability
|
||||
from langbot_plugin.api.entities.builtin.rag.models import (
|
||||
IngestionContext,
|
||||
IngestionResult,
|
||||
)
|
||||
from langbot_plugin.api.entities.builtin.rag.context import (
|
||||
RetrievalContext,
|
||||
RetrievalResponse,
|
||||
RetrievalResultEntry,
|
||||
)
|
||||
from langbot_plugin.api.entities.builtin.rag.enums import DocumentStatus
|
||||
from langbot_plugin.api.entities.builtin.provider.message import ContentElement
|
||||
|
||||
|
||||
class SimpleRag(KnowledgeEngine):
|
||||
|
||||
@classmethod
|
||||
def get_capabilities(cls) -> list[str]:
|
||||
return [KnowledgeEngineCapability.DOC_INGESTION]
|
||||
|
||||
async def on_knowledge_base_create(self, kb_id: str, config: dict) -> None:
|
||||
"""ナレッジベース作成時のコールバック、リソースの初期化に使用できます"""
|
||||
pass
|
||||
|
||||
async def on_knowledge_base_delete(self, kb_id: str) -> None:
|
||||
"""ナレッジベース削除時のコールバック、リソースのクリーンアップに使用できます"""
|
||||
pass
|
||||
|
||||
async def ingest(self, context: IngestionContext) -> IngestionResult:
|
||||
"""ドキュメントをナレッジベースに取り込む"""
|
||||
|
||||
# 1. ファイル内容を取得
|
||||
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
|
||||
|
||||
# 2. ドキュメントを解析してチャンクに分割
|
||||
text = file_bytes.decode('utf-8')
|
||||
chunk_size = context.creation_settings.get('chunk_size', 500)
|
||||
chunk_overlap = context.creation_settings.get('chunk_overlap', 50)
|
||||
chunks = self._split_text(text, chunk_size, chunk_overlap)
|
||||
|
||||
# 3. ホストの埋め込みモデルを使用してベクトルを生成
|
||||
embedding_model_uuid = context.creation_settings.get('embedding_model_uuid', '')
|
||||
vectors = await self.plugin.invoke_embedding(embedding_model_uuid, chunks)
|
||||
|
||||
# 4. ホストのベクトルデータベースに書き込み
|
||||
collection_id = context.get_collection_id()
|
||||
ids = [f"{context.file_object.metadata.document_id}_{i}" for i in range(len(chunks))]
|
||||
metadata = [{"document_id": context.file_object.metadata.document_id, "chunk_index": i, "text": chunks[i]} for i in range(len(chunks))]
|
||||
await self.plugin.vector_upsert(collection_id, vectors, ids, metadata)
|
||||
|
||||
return IngestionResult(
|
||||
document_id=context.file_object.metadata.document_id,
|
||||
status=DocumentStatus.COMPLETED,
|
||||
chunks_created=len(chunks),
|
||||
)
|
||||
|
||||
async def retrieve(self, context: RetrievalContext) -> RetrievalResponse:
|
||||
"""ナレッジベースから関連コンテンツを検索"""
|
||||
|
||||
# 1. クエリベクトルを生成
|
||||
embedding_model_uuid = context.creation_settings.get('embedding_model_uuid', '')
|
||||
query_vectors = await self.plugin.invoke_embedding(embedding_model_uuid, [context.query])
|
||||
query_vector = query_vectors[0]
|
||||
|
||||
# 2. ベクトル検索
|
||||
collection_id = context.get_collection_id()
|
||||
results = await self.plugin.vector_search(collection_id, query_vector, top_k=context.retrieval_settings.get('top_k', 5))
|
||||
|
||||
# 3. 検索結果に変換
|
||||
entries = []
|
||||
for r in results:
|
||||
entry = RetrievalResultEntry(
|
||||
id=r.get('id', ''),
|
||||
content=[ContentElement.from_text(r.get('metadata', {}).get('text', ''))],
|
||||
metadata=r.get('metadata', {}),
|
||||
distance=r.get('score', 0.0),
|
||||
)
|
||||
entries.append(entry)
|
||||
|
||||
return RetrievalResponse(
|
||||
results=entries,
|
||||
total_found=len(entries),
|
||||
metadata={},
|
||||
)
|
||||
|
||||
async def delete_document(self, kb_id: str, document_id: str) -> bool:
|
||||
"""ナレッジベースからドキュメントと関連データを削除"""
|
||||
collection_id = kb_id
|
||||
deleted = await self.plugin.vector_delete(collection_id, file_ids=[document_id])
|
||||
return deleted > 0
|
||||
```
|
||||
|
||||
### ライフサイクルフック
|
||||
|
||||
KnowledgeEngineは、ナレッジベースの作成・削除時に呼び出される2つのライフサイクルフックを提供します:
|
||||
|
||||
```python
|
||||
async def on_knowledge_base_create(self, kb_id: str, config: dict) -> None:
|
||||
"""このエンジンを使用するナレッジベースが作成された時のコールバック
|
||||
|
||||
Args:
|
||||
kb_id: ナレッジベースUUID
|
||||
config: ユーザーが作成時に入力した設定(creation_schemaのフィールド)
|
||||
"""
|
||||
|
||||
async def on_knowledge_base_delete(self, kb_id: str) -> None:
|
||||
"""このエンジンを使用するナレッジベースが削除された時のコールバック
|
||||
|
||||
Args:
|
||||
kb_id: ナレッジベースUUID
|
||||
"""
|
||||
```
|
||||
|
||||
### ドキュメントの取り込み
|
||||
|
||||
`ingest`メソッドは、ユーザーがナレッジベースにドキュメントをアップロードした時に呼び出されます:
|
||||
|
||||
```python
|
||||
async def ingest(self, context: IngestionContext) -> IngestionResult:
|
||||
```
|
||||
|
||||
**IngestionContext**には以下の情報が含まれます:
|
||||
|
||||
```python
|
||||
class IngestionContext(pydantic.BaseModel):
|
||||
file_object: FileObject # 取り込むファイルオブジェクト
|
||||
knowledge_base_id: str # ターゲットのナレッジベースID
|
||||
collection_id: str | None # ベクトルコレクションID(デフォルトはknowledge_base_idにフォールバック)
|
||||
creation_settings: dict # ナレッジベース作成時の設定パラメータ
|
||||
parsed_content: ParseResult | None # 外部Parserプラグインの事前解析コンテンツ(存在する場合)
|
||||
```
|
||||
|
||||
**FileObject**にはファイルのメタ情報が含まれます:
|
||||
|
||||
```python
|
||||
class FileObject(pydantic.BaseModel):
|
||||
metadata: FileMetadata # filename, file_size, mime_type, document_id, knowledge_base_id等を含む
|
||||
storage_path: str # ストレージシステム内のファイルパス
|
||||
```
|
||||
|
||||
**IngestionResult**は取り込み結果を返す必要があります:
|
||||
|
||||
```python
|
||||
class IngestionResult(pydantic.BaseModel):
|
||||
document_id: str # ドキュメントID
|
||||
status: DocumentStatus # 処理ステータス: COMPLETED / FAILED
|
||||
chunks_created: int = 0 # 作成されたチャンク数
|
||||
error_message: str | None = None # 失敗時のエラーメッセージ
|
||||
metadata: dict = {} # 追加メタデータ
|
||||
```
|
||||
|
||||
### ナレッジ検索
|
||||
|
||||
`retrieve`メソッドは、ナレッジベースがクエリされた時に呼び出されます:
|
||||
|
||||
```python
|
||||
async def retrieve(self, context: RetrievalContext) -> RetrievalResponse:
|
||||
```
|
||||
|
||||
**RetrievalContext**には以下の情報が含まれます:
|
||||
|
||||
```python
|
||||
class RetrievalContext(pydantic.BaseModel):
|
||||
query: str # クエリテキスト
|
||||
knowledge_base_id: str | None # ナレッジベースID
|
||||
collection_id: str | None # ベクトルコレクションID
|
||||
retrieval_settings: dict # 検索時の設定パラメータ(retrieval_schemaのフィールド)
|
||||
creation_settings: dict # ナレッジベース作成時の設定パラメータ
|
||||
filters: dict # メタデータフィルター条件(Chromaスタイルwhere構文)
|
||||
```
|
||||
|
||||
<Info>
|
||||
LangBotホストは`retrieval_settings`をプラグインに渡す前にデフォルトの`top_k`値(デフォルト5)を自動的に注入します。プラグインは`context.retrieval_settings.get('top_k', 5)`でアクセスできます。
|
||||
|
||||
`filters`フィールドには`retrieval_settings`から抽出されたChromaスタイルの`where`フィルター条件が含まれます。呼び出し元が検索設定で`filters`を指定した場合(例:時間範囲、ファイルタイプ、カスタムメタデータフィールドによるドキュメントフィルタリング)、ホストがこのフィールドを設定し、プラグインは検索時にフィルタリングを適用できます。フィルターが指定されていない場合、このフィールドは空のdictです。
|
||||
</Info>
|
||||
|
||||
**RetrievalResponse**は検索結果を返す必要があります:
|
||||
|
||||
```python
|
||||
class RetrievalResponse(pydantic.BaseModel):
|
||||
results: list[RetrievalResultEntry] # 検索結果リスト
|
||||
total_found: int # 一致した総数
|
||||
metadata: dict # 追加メタデータ
|
||||
```
|
||||
|
||||
**RetrievalResultEntry**は単一の検索結果を表します:
|
||||
|
||||
```python
|
||||
class RetrievalResultEntry(pydantic.BaseModel):
|
||||
id: str # 結果ID
|
||||
content: list[ContentElement] # コンテンツ、ContentElement.from_text()を使用して作成
|
||||
metadata: dict # メタデータ
|
||||
distance: float # 距離スコア(小さいほど関連性が高い)
|
||||
score: float | None = None # 類似度スコア(大きいほど関連性が高い)
|
||||
```
|
||||
|
||||
### ドキュメントの削除
|
||||
|
||||
`delete_document`メソッドは、ユーザーがナレッジベースからドキュメントを削除した時に呼び出されます:
|
||||
|
||||
```python
|
||||
async def delete_document(self, kb_id: str, document_id: str) -> bool:
|
||||
"""ドキュメントと関連データを削除
|
||||
|
||||
Args:
|
||||
kb_id: ナレッジベースID
|
||||
document_id: ドキュメントID
|
||||
|
||||
Returns:
|
||||
削除が成功したかどうか
|
||||
"""
|
||||
```
|
||||
|
||||
## ホストRAG API
|
||||
|
||||
KnowledgeEngineコンポーネントは`self.plugin`を通じて、LangBotホストが提供する埋め込みモデル呼び出し、ベクトルデータベース操作、ファイル読み取りなどのRAG関連APIを呼び出すことができます。
|
||||
|
||||
### 埋め込みモデルの呼び出し
|
||||
|
||||
```python
|
||||
async def invoke_embedding(
|
||||
self,
|
||||
embedding_model_uuid: str,
|
||||
texts: list[str],
|
||||
) -> list[list[float]]:
|
||||
"""ホストの埋め込みモデルを使用してベクトルを生成
|
||||
|
||||
Args:
|
||||
embedding_model_uuid: 埋め込みモデルUUID
|
||||
texts: 埋め込むテキストのリスト
|
||||
|
||||
Returns:
|
||||
ベクトルのリスト、入力テキストごとに1つ
|
||||
"""
|
||||
|
||||
# 使用例
|
||||
vectors = await self.plugin.invoke_embedding("model_uuid", ["Hello", "World"])
|
||||
```
|
||||
|
||||
### ベクトルの書き込み
|
||||
|
||||
```python
|
||||
async def vector_upsert(
|
||||
self,
|
||||
collection_id: str,
|
||||
vectors: list[list[float]],
|
||||
ids: list[str],
|
||||
metadata: list[dict] | None = None,
|
||||
) -> None:
|
||||
"""ホストのベクトルデータベースにベクトルを書き込む
|
||||
|
||||
Args:
|
||||
collection_id: ターゲットコレクションID
|
||||
vectors: ベクトルのリスト
|
||||
ids: ベクトルの一意識別子リスト
|
||||
metadata: オプションのメタデータリスト
|
||||
"""
|
||||
|
||||
# 使用例
|
||||
await self.plugin.vector_upsert(
|
||||
collection_id="kb_uuid",
|
||||
vectors=[[0.1, 0.2, ...], [0.3, 0.4, ...]],
|
||||
ids=["chunk_0", "chunk_1"],
|
||||
metadata=[{"document_id": "doc1"}, {"document_id": "doc1"}],
|
||||
)
|
||||
```
|
||||
|
||||
### ベクトル検索
|
||||
|
||||
```python
|
||||
async def vector_search(
|
||||
self,
|
||||
collection_id: str,
|
||||
query_vector: list[float],
|
||||
top_k: int = 5,
|
||||
filters: dict | None = None,
|
||||
) -> list[dict]:
|
||||
"""ホストのベクトルデータベースで類似ベクトルを検索
|
||||
|
||||
Args:
|
||||
collection_id: ターゲットコレクションID
|
||||
query_vector: 類似性検索のクエリベクトル
|
||||
top_k: 返す結果の数
|
||||
filters: オプションのメタデータフィルター
|
||||
|
||||
Returns:
|
||||
検索結果のリスト(id, score, metadataなどを含むdict)
|
||||
"""
|
||||
|
||||
# 使用例
|
||||
results = await self.plugin.vector_search(
|
||||
collection_id="kb_uuid",
|
||||
query_vector=[0.1, 0.2, ...],
|
||||
top_k=5,
|
||||
)
|
||||
# 返却フォーマット: [{"id": "chunk_0", "score": 0.123, "metadata": {"document_id": "doc1", ...}}, ...]
|
||||
```
|
||||
|
||||
<Info>
|
||||
`vector_search`が返す各結果はdictで、`id`(ベクトルID)、`score`(距離スコア)、`metadata`(upsert時に提供したメタデータ)の3つのフィールドを含みます。検索結果にテキスト内容が必要な場合は、取り込み時にmetadataにテキストを保存してください。
|
||||
</Info>
|
||||
|
||||
### ベクトルの削除
|
||||
|
||||
```python
|
||||
async def vector_delete(
|
||||
self,
|
||||
collection_id: str,
|
||||
file_ids: list[str] | None = None,
|
||||
filters: dict | None = None,
|
||||
) -> int:
|
||||
"""ホストのベクトルデータベースからベクトルを削除
|
||||
|
||||
Args:
|
||||
collection_id: ターゲットコレクションID
|
||||
file_ids: 削除するファイルIDのリスト
|
||||
filters: オプションのメタデータフィルター
|
||||
|
||||
Returns:
|
||||
削除されたアイテム数
|
||||
"""
|
||||
|
||||
# 使用例
|
||||
deleted = await self.plugin.vector_delete(
|
||||
collection_id="kb_uuid",
|
||||
file_ids=["doc_001"],
|
||||
)
|
||||
```
|
||||
|
||||
<Info>
|
||||
`filters`パラメータはChromaスタイルの`where`構文によるメタデータフィルタリングをサポートしています。複数のトップレベルキーはAND条件として結合されます。サポートされる演算子:`$eq`、`$ne`、`$gt`、`$gte`、`$lt`、`$lte`、`$in`、`$nin`。例:`{"file_id": {"$eq": "abc"}}`。
|
||||
|
||||
**注意:** Chroma、Qdrant、SeekDBは完全なメタデータを保存し、任意のフィールドでフィルタリングできます。MilvusとpgvectorはDB側に`text`、`file_id`、`chunk_uuid`のみ保存しており、それ以外のフィールドでのフィルタリングは無視されます。
|
||||
</Info>
|
||||
|
||||
```python
|
||||
async def get_knowledge_file_stream(self, storage_path: str) -> bytes:
|
||||
"""ホストストレージからファイル内容を取得
|
||||
|
||||
Args:
|
||||
storage_path: ファイルのストレージパス(FileObject.storage_pathから取得)
|
||||
|
||||
Returns:
|
||||
ファイル内容のバイトデータ
|
||||
"""
|
||||
|
||||
# 使用例
|
||||
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
|
||||
```
|
||||
|
||||
## RAGエンジンのテスト
|
||||
|
||||
作成後、プラグインディレクトリでコマンド`lbp run`を実行してデバッグを開始します。その後、LangBotで:
|
||||
|
||||
1. 「ナレッジベース」ページに移動
|
||||
2. 「ナレッジベースを作成」をクリック
|
||||
3. プラグインが提供するRAGエンジンを選択し、エンジンの`creation_schema`に基づいて設定を入力
|
||||
4. 作成後、ドキュメントをアップロードして取り込み機能をテスト(エンジンが`DOC_INGESTION`能力を宣言している場合)
|
||||
5. パイプラインにナレッジベースをバインドし、検索機能をテスト
|
||||
@@ -0,0 +1,201 @@
|
||||
---
|
||||
title: "コンポーネント: パーサー"
|
||||
---
|
||||
|
||||
パーサー(Parser)コンポーネントにより、プラグインはLangBotにドキュメント解析機能を提供できます。ユーザーがナレッジベースにドキュメントをアップロードする際、LangBotはRAGエンジンの`ingest`ステップの前にパーサーを呼び出し、PDF、Word、Markdownなどのバイナリファイルから構造化テキストを抽出します。
|
||||
|
||||
ParserとKnowledgeEngineの関係:
|
||||
- **Parser** はファイルをテキストに変換する役割(file → text)
|
||||
- **KnowledgeEngine** はテキストのインデックス作成と検索を行う役割(text → chunks → vectors)
|
||||
|
||||
RAGエンジン自体がドキュメント解析機能を持っている場合(`DOC_PARSING`能力を宣言)、ユーザーはRAGエンジンの内蔵解析機能を使用するか、外部Parserプラグインを使用するかを選択できます。
|
||||
|
||||
## パーサーコンポーネントの追加
|
||||
|
||||
1つのプラグインには任意の数のパーサーを追加できます。プラグインディレクトリでコマンド`lbp comp Parser`を実行し、プロンプトに従ってパーサーの設定を入力します。
|
||||
|
||||
```bash
|
||||
➜ MyParserPlugin > lbp comp Parser
|
||||
Generating component Parser...
|
||||
Parser name: pdf_parser
|
||||
Parser description: A PDF document parser
|
||||
Component Parser generated successfully.
|
||||
組件 Parser 生成成功。
|
||||
```
|
||||
|
||||
これにより、`components/parser/`ディレクトリに`pdf_parser.yaml`と`pdf_parser.py`ファイルが生成されます。`.yaml`ファイルはパーサーの基本情報とサポートするMIMEタイプを定義し、`.py`ファイルはこのパーサーのハンドラです:
|
||||
|
||||
```bash
|
||||
➜ MyParserPlugin > tree
|
||||
...
|
||||
├── components
|
||||
│ ├── __init__.py
|
||||
│ └── parser
|
||||
│ ├── __init__.py
|
||||
│ ├── pdf_parser.py
|
||||
│ └── pdf_parser.yaml
|
||||
...
|
||||
```
|
||||
|
||||
## マニフェストファイル: パーサー
|
||||
|
||||
```yaml
|
||||
apiVersion: v1 # 変更しないでください
|
||||
kind: Parser # 変更しないでください
|
||||
metadata:
|
||||
name: pdf_parser # パーサー名、このパーサーを識別するために使用
|
||||
label:
|
||||
en_US: PDF Parser # パーサー表示名、LangBotのUIに表示、多言語対応
|
||||
zh_Hans: PDF 解析器
|
||||
description:
|
||||
en_US: 'A PDF document parser'
|
||||
zh_Hans: 'PDF 文档解析器'
|
||||
spec:
|
||||
supported_mime_types: # サポートするファイルのMIMEタイプを宣言
|
||||
- application/pdf
|
||||
execution:
|
||||
python:
|
||||
path: pdf_parser.py # パーサーハンドラ、変更しないでください
|
||||
attr: PdfParser # パーサーハンドラのクラス名、pdf_parser.pyのクラス名と一致
|
||||
```
|
||||
|
||||
### supported_mime_types
|
||||
|
||||
`supported_mime_types`はこのパーサーがサポートするファイルタイプを宣言します。一般的なMIMEタイプ:
|
||||
|
||||
| MIMEタイプ | 説明 |
|
||||
|----------|------|
|
||||
| `application/pdf` | PDFドキュメント |
|
||||
| `application/vnd.openxmlformats-officedocument.wordprocessingml.document` | Wordドキュメント(.docx) |
|
||||
| `text/markdown` | Markdownファイル |
|
||||
| `text/plain` | プレーンテキストファイル |
|
||||
| `text/html` | HTMLファイル |
|
||||
|
||||
## プラグインハンドラ
|
||||
|
||||
以下のコードがデフォルトで生成されます(`components/parser/<パーサー名>.py`)。`parse`メソッドを実装する必要があります。
|
||||
|
||||
```python
|
||||
from langbot_plugin.api.definition.components.parser.parser import Parser
|
||||
from langbot_plugin.api.entities.builtin.rag.models import (
|
||||
ParseContext,
|
||||
ParseResult,
|
||||
TextSection,
|
||||
)
|
||||
|
||||
|
||||
class PdfParser(Parser):
|
||||
"""ファイルからテキストを抽出するパーサーコンポーネント。"""
|
||||
|
||||
async def parse(self, context: ParseContext) -> ParseResult:
|
||||
"""ファイルを解析して構造化テキストを抽出。
|
||||
|
||||
Args:
|
||||
context: file_content(bytes)、mime_type、filename、metadataを含む。
|
||||
|
||||
Returns:
|
||||
抽出されたテキストとオプションの構造化セクションを含むParseResult。
|
||||
"""
|
||||
# TODO: 解析ロジックを実装
|
||||
text = context.file_content.decode("utf-8", errors="replace")
|
||||
|
||||
return ParseResult(
|
||||
text=text,
|
||||
sections=[
|
||||
TextSection(
|
||||
content=text,
|
||||
heading=context.filename,
|
||||
level=0,
|
||||
),
|
||||
],
|
||||
metadata={
|
||||
"filename": context.filename,
|
||||
"mime_type": context.mime_type,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### 解析メソッド
|
||||
|
||||
`parse`メソッドは、ドキュメントがナレッジベースにアップロードされた時(RAGエンジンの取り込み前)に呼び出されます:
|
||||
|
||||
```python
|
||||
async def parse(self, context: ParseContext) -> ParseResult:
|
||||
```
|
||||
|
||||
**ParseContext**には以下の情報が含まれます:
|
||||
|
||||
```python
|
||||
class ParseContext(pydantic.BaseModel):
|
||||
file_content: bytes # 生のファイルバイト(LangBotがストレージから読み取り)
|
||||
mime_type: str # 検出されたファイルのMIMEタイプ
|
||||
filename: str # 元のファイル名
|
||||
metadata: dict[str, Any] # FileObjectからの追加メタデータ
|
||||
```
|
||||
|
||||
**ParseResult**は解析結果を返す必要があります:
|
||||
|
||||
```python
|
||||
class ParseResult(pydantic.BaseModel):
|
||||
text: str # 抽出された完全なプレーンテキスト
|
||||
sections: list[TextSection] = [] # 構造化セクション(オプション)
|
||||
metadata: dict[str, Any] = {} # 解析メタデータ(例: page_count, language)
|
||||
```
|
||||
|
||||
**TextSection**はドキュメントから抽出されたテキストのセクションを表します:
|
||||
|
||||
```python
|
||||
class TextSection(pydantic.BaseModel):
|
||||
content: str # セクションのテキスト内容
|
||||
heading: str | None = None # セクション見出し
|
||||
level: int = 0 # ネストレベル
|
||||
page: int | None = None # ソースページ番号(PDFなど)
|
||||
metadata: dict[str, Any] = {} # 追加セクションメタデータ
|
||||
```
|
||||
|
||||
## KnowledgeEngineとの連携
|
||||
|
||||
ユーザーがドキュメントをアップロードする際、LangBotは以下のロジックで解析フローを決定します:
|
||||
|
||||
- ユーザーが外部Parserプラグインを選択した場合、LangBotはまずParserの`parse`メソッドを呼び出し、その結果を`IngestionContext.parsed_content`を通じてRAGエンジンの`ingest`メソッドに渡します。
|
||||
- RAGエンジンが`DOC_PARSING`能力を宣言しており、ユーザーが外部パーサーを選択していない場合、RAGエンジンが自身でドキュメント解析を処理します。
|
||||
|
||||
KnowledgeEngineは`IngestionContext.parsed_content`をチェックして、事前解析されたコンテンツが利用可能かどうかを判断できます:
|
||||
|
||||
```python
|
||||
async def ingest(self, context: IngestionContext) -> IngestionResult:
|
||||
if context.parsed_content:
|
||||
# 外部Parserの解析結果を使用
|
||||
text = context.parsed_content.text
|
||||
sections = context.parsed_content.sections
|
||||
else:
|
||||
# ドキュメントを内部で解析
|
||||
file_bytes = await self.plugin.get_knowledge_file_stream(context.file_object.storage_path)
|
||||
text = file_bytes.decode('utf-8')
|
||||
...
|
||||
```
|
||||
|
||||
## クロスプラグインパーサー呼び出し
|
||||
|
||||
KnowledgeEngineプラグインは`self.plugin.invoke_parser`を通じて、他のプラグインが提供するパーサーを呼び出すことができます:
|
||||
|
||||
```python
|
||||
result = await self.plugin.invoke_parser(
|
||||
plugin_author="author_name",
|
||||
plugin_name="plugin_name",
|
||||
storage_path=context.file_object.storage_path,
|
||||
mime_type=context.file_object.metadata.mime_type,
|
||||
filename=context.file_object.metadata.filename,
|
||||
metadata={},
|
||||
)
|
||||
# resultはtext、sections、metadataを含むdict
|
||||
```
|
||||
|
||||
## パーサーのテスト
|
||||
|
||||
作成後、プラグインディレクトリでコマンド`lbp run`を実行してデバッグを開始します。その後、LangBotで:
|
||||
|
||||
1. 「ナレッジベース」ページに移動
|
||||
2. ナレッジベースを選択してドキュメント管理に入る
|
||||
3. ファイルのアップロード時、パーサーセレクターでプラグインが提供するパーサーを選択
|
||||
4. アップロード後、ドキュメントが正しく取り込まれたことを確認
|
||||
Reference in New Issue
Block a user