メインコンテンツまでスキップ
Asgard の RAG ナレッジベースで質問応答を行う完全な例

ナレッジベースへの問い合わせ

Asgard は Knowledge Base と Retrieve Knowledge Processor で RAG(Retrieval-Augmented Generation)を実現します。アップロードした文書やデータに基づいて AI が質問に答えられるようになります。

仕組み​

RAG の問い合わせはすべて Workflow のサーバー側で行われ、外から呼ぶ API は通常のメッセージ送信とまったく同じです。

利用者が質問する
↓
Asgard API(同じエンドポイント)
↓
Retrieve Knowledge Processor(ナレッジベースから関連する内容を検索)
↓
LLM(検索結果をもとに回答を生成)
↓
SSE のストリーミング応答

API の呼び方を変える必要はありません。Workflow に Retrieve Knowledge Processor を追加してナレッジベースを指定すれば、RAG はそのまま動きます。

事前の設定​

API を呼ぶ前に、Asgard プラットフォームで次の設定を済ませてください。

  1. Knowledge Base を作る — PDF、テキストファイル、ウェブページなどをアップロードします
  2. Embedding Model を設定する — ベクトル化のモデルを選びます(OpenAI text-embedding-3-small など)
  3. Workflow に Retrieve Knowledge Processor を追加する — 検索の方針と対象のナレッジベースを設定します
  4. App を公開する — namespace と bot-provider-name が得られます

詳しい手順は Knowledge Base の設定 をご覧ください。


質問の組み立て方​

質問の組み立て方によって、ナレッジベースの検索精度は大きく変わります。

指針例
具体的なキーワードを使う✅ 返金の手続きには何営業日かかりますか
曖昧すぎる質問を避ける❌ すべての情報を教えてください
1 回につき 1 つの話題に絞る✅ 製品 A の保証期間はどれくらいですか
適切な前提を添える✅ 法人の利用者です。契約更新の手続きを教えてください

cURL の例​

curl -X POST "https://api.asgard-ai.com/generic/ns/your-namespace/bot-provider/your-bot-provider/message/sse" \
-H "Content-Type: application/json" \
-H "X-API-KEY: your-api-key" \
-d '{
"customChannelId": "kb-query-channel-001",
"customMessageId": "kb-msg-001",
"text": "返金の申請に必要な書類と、その手続きを教えてください",
"action": "NONE"
}'

JavaScript の例​

const BASE_URL = 'https://api.asgard-ai.com';
const NAMESPACE = 'your-namespace';
const BOT_PROVIDER = 'your-bot-provider';
const API_KEY = process.env.ASGARD_API_KEY;

/**
* ナレッジベースに質問し、RAG の回答を受け取る
* @param {string} question - 利用者の質問
* @param {string} channelId - 対話チャネルの ID(同じチャネルなら対話の記憶が保たれます)
*/
async function queryKnowledgeBase(question, channelId = 'kb-channel-001') {
const url = `${BASE_URL}/generic/ns/${NAMESPACE}/bot-provider/${BOT_PROVIDER}/message/sse`;

const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-KEY': API_KEY,
},
body: JSON.stringify({
customChannelId: channelId,
text: question,
action: 'NONE',
}),
});

if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}

const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let fullAnswer = '';

while (true) {
const { done, value } = await reader.read();
if (done) break;

buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';

for (const line of lines) {
if (!line.startsWith('data:')) continue;
const jsonStr = line.slice(5).trim();
if (!jsonStr) continue;

try {
const event = JSON.parse(jsonStr);

if (event.eventType === 'asgard.message.delta') {
const delta = event.fact.messageDelta.message.text;
fullAnswer += delta;
// 届いた順に表示する(タイプライター風の表示)
process.stdout.write(delta);
}

if (event.eventType === 'asgard.run.done') {
console.log('\n\n問い合わせ完了');
return fullAnswer;
}
} catch (_) {}
}
}

return fullAnswer;
}

// 使用例: 同じ channelId で続けて質問する(対話の記憶が保たれます)
async function main() {
const channelId = `kb-session-${Date.now()}`;

console.log('質問 1:');
await queryKnowledgeBase('返金の申請に必要な書類は何ですか', channelId);

console.log('\n\n質問 2(追加の質問):');
await queryKnowledgeBase('完了までにおおよそ何営業日かかりますか', channelId);
}

main().catch(console.error);

Python の例​

import requests
import json
import os
import time

BASE_URL = "https://api.asgard-ai.com"
NAMESPACE = "your-namespace"
BOT_PROVIDER = "your-bot-provider"
API_KEY = os.environ.get("ASGARD_API_KEY")


def query_knowledge_base(question: str, channel_id: str) -> str:
"""
Asgard のナレッジベースに質問し、RAG の回答を受け取ります。

Args:
question: 利用者の質問
channel_id: 対話チャネルの ID(同じチャネルなら対話の記憶が保たれます)

Returns:
回答の全文
"""
url = f"{BASE_URL}/generic/ns/{NAMESPACE}/bot-provider/{BOT_PROVIDER}/message/sse"

headers = {
"Content-Type": "application/json",
"X-API-KEY": API_KEY,
}

payload = {
"customChannelId": channel_id,
"text": question,
"action": "NONE",
}

full_answer = ""

with requests.post(url, headers=headers, json=payload, stream=True) as response:
response.raise_for_status()

for line in response.iter_lines():
if not line:
continue

decoded = line.decode("utf-8")
if not decoded.startswith("data:"):
continue

json_str = decoded[5:].strip()
if not json_str:
continue

try:
event = json.loads(json_str)
event_type = event.get("eventType")

if event_type == "asgard.message.delta":
delta = event["fact"]["messageDelta"]["message"]["text"]
full_answer += delta
print(delta, end="", flush=True)

elif event_type == "asgard.run.done":
print("\n")
break

except json.JSONDecodeError:
pass

return full_answer


if __name__ == "__main__":
channel_id = f"kb-session-{int(time.time())}"

questions = [
"返金の申請に必要な書類は何ですか",
"返金の完了までおおよそ何営業日かかりますか",
"返金の期限を過ぎていても申請できますか",
]

for i, question in enumerate(questions, 1):
print(f"\n質問 {i}: {question}")
print("回答: ", end="")
answer = query_knowledge_base(question, channel_id)
print(f"(全 {len(answer)} 文字)")

出典を含む応答の解析​

Workflow がナレッジベースの出典を返す設定になっている場合、その情報は asgard.message.complete イベントの template フィールドに入ります。

if (event.eventType === 'asgard.message.complete') {
const message = event.fact.messageComplete.message;

// 回答の全文
const answerText = message.text;

// ナレッジベースの出典(Workflow が返す設定になっている場合)
const template = message.template;
if (template && template.sources) {
console.log('参照した出典:');
template.sources.forEach((source, i) => {
console.log(` ${i + 1}. ${source.title} — ${source.url || source.fileName}`);
});
}
}
ヒント

template フィールドの中身は Workflow での設定によって決まります。詳しくは Message Template の説明 をご覧ください。


次に読む​