Morgan Stanley — GPT-4 × RAGによるナレッジマネジメント革命
企業概要
| 項目 | 内容 |
|---|
| 企業名 | Morgan Stanley(モルガン・スタンレー) |
| 業種 | 金融(証券・資産運用) |
| 設立 | 1935年(米国) |
| FA(アドバイザー)数 | 16,000名以上 |
| リサーチ文書数 | 35万件以上(4,000万語) |
| 公式情報 | OpenAI公式ケーススタディ |
課題
- 35万件以上のリサーチレポートが蓄積されているが、FAが必要な情報を見つけるのに時間がかかる
- 顧客との面談後のフォローアップ作成に多くの時間を費やしている
- 数十年分の機関知識(institutional knowledge)が検索不可能な状態
技術選定の理由
LLMの選択: GPT-4(外部LLM)
- なぜOSSではなく GPT-4?: 金融ドメインの複雑な質問への回答品質が最も高かった
- なぜFine-tuningではなくRAG?: 規制対応(SEC)のため、回答の根拠となるソース文書の提示が必須。RAGなら根拠を明示できる
- Whisper: 会議音声の文字起こしにOpenAI Whisperを使用
データベース: ベクトルDB(Azure AI Search)
- Embedding: OpenAI
text-embedding-ada-002
- チャンク戦略: 文書のセクション単位でチャンキング
- メタデータ: 著者・日付・トピック・機密レベル
システムアーキテクチャ
┌──────────────────────────────────────────────────┐
│ AI @ Morgan Stanley │
├──────────────────────┬───────────────────────────┤
│ Assistant │ Debrief │
│ (リサーチ検索) │ (会議要約) │
├──────────────────────┼───────────────────────────┤
│ │ │
│ ┌────────────┐ │ ┌──────────────┐ │
│ │ ユーザー質問 │ │ │ Zoom録音 │ │
│ └─────┬──────┘ │ └──────┬───────┘ │
│ ▼ │ ▼ │
│ ┌────────────┐ │ ┌──────────────┐ │
│ │ Embedding │ │ │ Whisper │ │
│ │ 変換 │ │ │ 文字起こし │ │
│ └─────┬──────┘ │ └──────┬───────┘ │
│ ▼ │ ▼ │
│ ┌────────────┐ │ ┌──────────────┐ │
│ │ Azure AI │ │ │ GPT-4 │ │
│ │ Search │ │ │ 要約生成 │ │
│ └─────┬──────┘ │ └──────┬───────┘ │
│ ▼ │ ▼ │
│ ┌────────────┐ │ ┌──────────────┐ │
│ │ GPT-4 │ │ │ CRM自動連携 │ │
│ │ 回答生成 │ │ │ (フォローアップ) │ │
│ └────────────┘ │ └──────────────┘ │
└──────────────────────┴───────────────────────────┘
実装例: RAGパイプライン
1. 依存関係
pip install langchain langchain-openai chromadb tiktoken
2. 文書の前処理とEmbedding
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.schema import Document
import json
def load_research_documents(data_dir: str) -> list[Document]:
"""リサーチPDFをセクション単位のDocumentに変換"""
documents = []
sample_docs = [
{
"content": "2024年の米国株式市場はAIセクターが牽引し...",
"metadata": {
"title": "2024 US Equity Outlook",
"author": "Morgan Stanley Research",
"date": "2024-01-15",
"topic": "equity",
"confidentiality": "internal",
}
},
]
for doc in sample_docs:
documents.append(Document(
page_content=doc["content"],
metadata=doc["metadata"]
))
return documents
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n## ", "\n### ", "\n\n", "\n", " "]
)
docs = load_research_documents("./data/research/")
chunks = text_splitter.split_documents(docs)
print(f"Total chunks: {len(chunks)}")
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
collection_name="ms_research",
persist_directory="./chroma_db"
)
print(f"Vectorstore created with {vectorstore._collection.count()} documents")
3. RAG検索 + 回答生成
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
llm = ChatOpenAI(model="gpt-4o", temperature=0)
RAG_PROMPT = PromptTemplate(
template="""あなたはMorgan Stanleyのリサーチアシスタントです。
以下のリサーチ文書の抜粋に基づいて、ファイナンシャルアドバイザーの質問に
正確に回答してください。
【重要ルール】
1. 回答は必ず提供された文書に基づくこと
2. 文書にない情報は「該当するリサーチが見つかりませんでした」と回答
3. 参照した文書のタイトルと日付を末尾に記載
4. 投資アドバイスは行わず、リサーチの要約のみ提供
---
参照文書:
{context}
---
質問: {question}
回答:""",
input_variables=["context", "question"]
)
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 5, "fetch_k": 20}
)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
chain_type_kwargs={"prompt": RAG_PROMPT},
return_source_documents=True
)
result = qa_chain.invoke({"query": "2024年のAIセクターの見通しは?"})
print(result["result"])
print("\n--- 参照文書 ---")
for doc in result["source_documents"]:
print(f" - {doc.metadata['title']} ({doc.metadata['date']})")
4. 会議要約機能(Debrief)
from openai import OpenAI
client = OpenAI()
def transcribe_meeting(audio_path: str) -> str:
"""Whisperで会議音声を文字起こし"""
with open(audio_path, "rb") as f:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=f,
response_format="text",
language="ja"
)
return transcript
def generate_debrief(transcript: str) -> dict:
"""会議録からフォローアップ事項を自動生成"""
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": """会議録から以下を抽出してJSON形式で出力:
{
"summary": "会議の要約(200文字以内)",
"key_topics": ["議論されたトピック"],
"action_items": [{"task": "タスク", "assignee": "担当者", "deadline": "期限"}],
"client_notes": "CRMに記録すべきクライアントノート",
"follow_up_email_draft": "フォローアップメールの下書き"
}"""},
{"role": "user", "content": transcript}
],
response_format={"type": "json_object"}
)
return json.loads(response.choices[0].message.content)
成果
| 指標 | 結果 |
|---|
| アドバイザー利用率 | 98% |
| 対応文書数 | 7,000件 → 35万件以上 |
| 情報検索時間 | 数時間 → 数秒 |
| オンボーディング時間 | — |
RAG vs Fine-tuning の選択理由
| 観点 | RAG(採用) | Fine-tuning(不採用) |
|---|
| ソースの明示 | 参照文書を提示可能 → SEC規制に対応 | ソース追跡が困難 |
| データ更新 | ベクトルDBに追加するだけ | 再学習が必要 |
| ハルシネーション | 文書ベースなので低リスク | 学習データに依存 |
| コスト | 推論時のみ | 学習コストが高い |