LangChain がオープンソース抽出サービスを発表
LangChain Blog は、データ抽出プロセスを効率化するための新しいオープンソースサービスの提供を開始したと発表した。
キーポイント
新サービスの開始発表
LangChain Blog がデータ抽出の効率化を目的とした新規オープンソースサービスを提供開始したことを公式に発表した。
開発者への実用性向上
既存のワークフローにおけるデータ抽出のボトルネックを解消し、開発者がより迅速かつ正確にデータを処理できる環境を整備する。
オープンソース戦略の継続
LangChain がコミュニティ主導で技術を進化させるという方針の一環として、ツール群の拡充を続けていることを示している。
重要な引用
データ抽出を効率化する新しいオープンソースサービスの提供を開始した
影響分析・編集コメントを表示
影響分析
この発表は、LangChain エコシステム内のデータ抽出プロセスを標準化・効率化するものであり、特に大規模な非構造化データを扱う AI アプリケーション開発において開発者の負担を軽減する効果が期待されます。オープンソースとしての提供により、コミュニティによる改善やカスタマイズが促進され、長期的な技術の成熟と普及に寄与すると考えられます。
編集コメント
データ抽出は AI アプリケーション開発の基盤となる重要な工程であり、これを効率化するツールのオープンソース化は、開発者の生産性向上に直結する意義深い動きです。LangChain のエコシステムがさらに拡張されることで、より複雑なワークフローへの対応が可能になるでしょう。

先月、私たちはオープンソースの活用事例加速ツールとして、「テキストや PDF などの非構造化データから構造化データを抽出するサービス」を発表しました。本日、このサービスのホスト版をシンプルなフロントエンドとともに公開します。
本アプリケーションは無料で利用できますが、本番環境での使用や機密データの処理には適していません。2024 年時点でこの分野で何が実現可能かを示し、開発者が自身のアプリケーション開発をスムーズにスタートできるよう支援することが目的です。
主要リンク:
- YouTube 解説動画:https://youtu.be/-FMUt3OARy0
- GitHub リポジトリ:https://github.com/langchain-ai/langchain-extract
なぜ今なのか
構造化データ抽出は、大規模言語モデル(LLM)の有用な活用事例として台頭しています。LLM は非構造化テキストの曖昧さを推論し、情報を所定のスキーマに強制変換する能力を持っています。モデルプロバイダーは現在、データ抽出において重要な機能である「長いコンテキストウィンドウ」と「関数呼び出し(ファンクション・コーリング)」機能を積極的にサポートしています。
また、LangChain におけるデータ抽出のサポートを最近強化しました。これにより、開発者はさまざまなファイルタイプ、スキーマ形式、モデル、few-shot(少サンプル)例、そしてツール呼び出しや JSON モード、構文解析といった抽出方法を容易に扱えるようになりました。リファレンスアプリケーションを公開することで、ユーザーは自らのユースケースに合わせて最新ツールを実験でき、その動作の背後にあるオープンソース実装 OSS implementation との関連性を理解することができます。
主な機能
- PDF、HTML、テキストへの対応
- スキーマと独自のカスタム指示を定義・保存できる抽出器(エクストラクター)の設定
- コンテキスト内学習のための few-shot 例を追加可能
- ユーザー間での抽出器の共有機能
- LLM モデルの差し替え対応
- コアな抽出ロジック用の LangServe エンドポイント。これにより、LangChain ワークフローに簡単に組み込むことが可能です。
- フロントエンド。自然言語で抽出スキーマを定義し、他のユーザーと共有、テキストやファイルでのテストが可能です(ただし、現時点では few-shot 例のサポートはありません)。
Walkthrough
ここでは、公開企業の決算電話会議から財務データを抽出する例を見ていきましょう。対象とするのは、Uber が投資家向けに公開している「2023 年第 4 四半期決算電話会議の準備資料」です。
Uber の Q4 2023 決算電話会議の準備資料 は、同社の IR 担当部門が オンライン上で公開 しています。
多くの公開企業は決算電話会議を開催し、経営陣が過去の業績や今後の計画について説明する機会を設けています。これらの会議の自然言語による議事録には有用な情報が含まれていますが、そのままでは分析や比較が困難です。そのため、まず文書から情報を抽出し、時系列や他社との比較が可能になるよう構造化する必要があります。
まずは PDF を取得しましょう。
import requests
pdf_url = "https://s23.q4cdn.com/407969754/files/doc_earnings/2023/q4/transcript/Uber-Q4-23-Prepared-Remarks.pdf"
PDF のバイナリデータを取得
pdf_response = requests.get(pdf_url)
assert(pdf_response.status_code == 200)
pdf_bytes = pdf_response.content
次に、自分自身に固有の識別子を生成します。本アプリケーションはユーザー管理や正規の認証機能を持たないため、抽出ツールや few-shot の例、その他のアセットへのアクセス制御はこの ID に依存しています。この ID は秘密扱いとし、紛失しないよう注意してください。
from uuid import uuid4
user_id = str(uuid4())
headers = {"x-key": user_id}
次に、抽出するデータのスキーマを定義します。ここでは財務データレコードの構造を指定しています。LLM には、レコードの期間など様々な属性を推論させることができます。
可読性を高めるために Pydantic を使用していますが、最終的にはサービス側で利用されるのは JSON スキーマです。
from pydantic import BaseModel, Field
class FinancialData(BaseModel):
name: str = Field(..., description="Name of the financial figure, such as revenue.")
value: float = Field(..., description="Nominal earnings in local currency.")
scale: str = Field(..., description="Scale of figure, such as MM, B, or percent.")
period_start: str = Field(..., description="The start of the time period in ISO format.")
period_duration: int = Field(..., description="Duration of period, in months")
evidence: str = Field(..., description="Verbatim sentence of text where figure was found.")ここで重要なのは、evidence 属性を含めている点です。これにより、予測の根拠となる文脈を提供し、後続の結果検証を容易にします。
スキーマ定義が完了したら、これをアプリケーションへ送信して抽出器を作成します。
必ず JSON 形式で返してください。translation フィールドのみ。他のフィールドは一切追加しないこと — 余計なフィールドを書こうとして本文がトークン上限で打ち切られる事故を防ぐため:
{"translation": "リライト全文"}
URL とリクエストの構築
まず、データ抽出サーバーの URL を定義します。
url = "https://extract-server-f34kggfazq-uc.a.run.app"次に、送信するデータを辞書形式で用意します。ここではユーザー ID に加え、「財務収益やその他の数値」という説明を付与し、スキーマとして FinancialData.schema() を指定しています。
重要なのは、抽出指示(instruction)です。「 earnings(利益)と revenue(売上高)といった標準的な財務数値のみを抽出すること」を明確に伝えます。さらに、「推計やガイダンスではなく、過去の事実のみを対象とする」という制約も加えています。
data = {
"user_id": user_id,
"description": "Financial revenues and other figures.",
"schema": FinancialData.schema(),
"instruction": (
"Extract standard financial figures, specifically earnings and "
"revenue figures. Only extract historical facts, not estimates or guidance."
)
}API 呼び出しとレスポンスの取得
準備したデータを送信し、抽出処理を実行します。requests.post メソッドで指定された URL の /extractors エンドポイントへ POST リクエストを送り、ヘッダー情報も併せて渡します。
response = requests.post(f"{url}/extractors", json=data, headers=headers)
extractor = response.json()レスポンス形式の厳格な指定
この API は、必ず JSON 形式で返却することを前提としています。ただし、出力されるフィールドは translation のみとし、他は一切含めないように設計されています。
これは、余計なフィールドを付け加えようとして本文がトークン上限に達し、レスポンスが途中で打ち切られてしまう事故を防ぐための重要な制約です。システムは、指定された形式以外のデータを生成しないよう厳格に制御されています。
抽出ツールを公開しました。これで固有 ID を用いてアクセスできるようになりました。次は、この PDF に対して抽出ツールを試してみましょう。
リライト全文
「Open Source Extraction Service(続き 6/12)」
同社の財務実績は、堅調な売上成長とコスト管理の徹底が相まって、調整後 EBITDA で 13 億ドル、GAAP 基準の営業利益で 6.52 億ドルを達成しました。
売上高については、為替レートを固定したベースで前年比 13% の成長を遂げ、99 億ドルに達しています。
一方、通期(2023 年度)の調整後 EBITDA については、12.6 億ドルから 13.4 億ドルの間になると予測されています。
出力結果の書式は、スキーマの説明と一部異なる場合があります。例えば、指示通り「MM」や「B」ではなく、「million」「billion」という単語を出力しています。モデルによってこの点への対応難易度は異なりますが、いきなり大規模なモデルを使う前に、few-shot(数例提示)の例文を適切に選ぶことで、意図を明確にする効率的な手段になることが多いです。
以下に、抽出器用の例を追加します:
examples = [
{
"text": "In 2022, Revenue was $1 million and EBIT was $2M.",
"output": [
FinancialData(
name="revenue",
value=1,
scale="MM",
period_start="2022-01-01",
period_duration=12,
evidence="In 2022, Revenue was $1 million and EBIT was $2M.",
).dict(),
FinancialData(
name="ebit",
value=2,
scale="MM",
period_start="2022-01-01",
period_duration=12,
evidence="In 2022, Revenue was $1 million and EBIT was $2M.",
).dict()
],
},
]
responses = []
for example in examples:
create_request = {
"extractor_id": extractor["uuid"],
"content": example["text"],
"output": example['output'],
}
response = requests.post(f"{url}/examples", json=create_request, headers=headers)
responses.append(response)
必ず JSON 形式で返してください。出力は translation フィールドのみとし、他のフィールドは一切含めないようにしてください。余計なフィールドを追加しようとして本文がトークン上限に達し、途中で切れてしまう事故を防ぐためです:
{"translation": "リライト全文"}
ここでは、"name" フィールドの大文字小文字を修正し、"scale" フィールドの書式を整えた 2 つのレコードを含む単一の例を追加します。この文書を再度抽出処理すると、意図した通りの書式が復元されます。
result = requests.post(
f"{url}/extract",
data={"extractor_id": extractor["uuid"], "model_name": "gpt-3.5-turbo"},
files={"file": pdf_bytes},
headers=headers,
)
result.json()
すると、以下のような結果が得られます。
調整済み EBITDA は 13 億ドル、GAAP 営業利益は 6.52 億ドルとなりました。これは、堅調な売上高の推移とコスト管理への継続的な取り組みが実を結んだ結果です。
総受注高(グロス・ブッキング)は、定常為替ベースで前年比 21% 増(輸送費を除くと 23%)と加速し、376 億ドルに達しました。売上高も同様に、定常為替ベースで前年比 13% 成長し、99 億ドルとなりました。
今後の見通しとして、調整済み EBITDA は 12.6 億ドルから 13.4 億ドルの間になると予想しています。
LangServe クライアント
最後に補足ですが、コアとなる抽出ロジックを LangServe でホストしているおかげで、RemoteRunnable インターフェース経由でアクセスし、より大規模なチェーンやエージェントワークフローに組み込むことが可能です。
このランナブルは、通常の呼び出し方と同じように使用できます:
from langserve import RemoteRunnable
runnable = RemoteRunnable(f"{url}/extract_text/")
response = runnable.invoke(
{
"text": "Our 2023 revenue was $100.",
"schema": FinancialData.schema(),
}
)
print(response)
実行すると、以下のような結果が得られます:
{'data': [{'name': 'revenue',
'value': 100,
'scale': '$',
'period_start': '2023-01-01',
'period_duration': 12,
'evidence': 'Our 2023 revenue was $100.'}]}
あるいは、以下のように検索シナリオに組み込むこともできます。ここでは入力に対して直接抽出を行うのではなく、まず文書群をインデックス化し、入力を検索クエリとして扱います。
"rev" で検索すると、収益(revenue)に関する文書が取得され、その文書内でのみ抽出処理が行われるようになります:
from operator import itemgetter
from langchain_community.vectorstores import FAISS
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import OpenAIEmbeddings
doc_contents = ["Our 2023 revenue was $100", "Our Q1 profit was $10 in 2023."]
vectorstore = FAISS.from_texts(doc_contents, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever()
larger_runnable = (
{
"text": itemgetter("text") | retriever | (lambda docs: docs[0].page_content), # fetch content of top doc,
"schema": itemgetter("schema"),
}
| runnable
)
larger_runnable.invoke({"text": "rev", "schema": FinancialData.schema()})
Which yields:
{'data': [{'name': 'revenue',
'value': 100,
'scale': '$',
'period_start': '2023-01-01',
'period_duration': 12,
'evidence': 'Our 2023 revenue was $100'}]}
We are excited to see what extraction workflows you build, and welcome both feedback on and contributions to LangChain's extraction capabilities!
Related content

Deep Agents
Agent Architecture
Open Source
How to Use RLMs in Deep Agents

Sydney Runkle
July 1, 2026
8
min

エージェントアーキテクチャ
Deep Agents
オープンソース
サンドボックスなしで信頼できないエージェントコードを実行する

ハンター・ロヴェル
2026 年 6 月 30 日
6 分

オープンソース
Deep Agents
エージェントアーキテクチャ
Deep Agents に動的サブエージェントを導入



S. ランクル、
C. フランシス、
H. ロヴェル
2026 年 6 月 29 日
9 分
エージェントの実際の動作を確認する
エージェントエンジニアリングプラットフォーム「LangSmith」を使えば、開発者はすべてのエージェントの判断をデバッグし、変更の評価を行い、ワンクリックでデプロイできます。
原文を表示

*Earlier this month we *announced* our most recent OSS use-case accelerant: a service for extracting structured data from unstructured sources, such as text and PDF documents. Today we are exposing a hosted version of the service with a simple front end. The application is free to use, but is not intended for production workloads or sensitive data. The intent is to showcase what is possible in this category in 2024, and to help developers get a running start with their own applications.*
Key Links:
- YouTube Walkthrough: https://youtu.be/-FMUt3OARy0
- GitHub Repo: https://github.com/langchain-ai/langchain-extract
Why now?
Structured data extraction has emerged as a valuable use case of large language models, which can reason through the ambiguities of unstructured text to coerce information into a desired schema. Model providers are increasingly supporting long context windows and function calling capabilities, both key features to data extraction. And we have recently improved LangChain’s support for data extraction, allowing developers to easily work with a variety of file types, schema formats, models, few-shot examples, and extraction methods (e.g., tool calling, JSON mode, or parsing). Hosting a reference application allows users to experiment with the latest tools for their own use-cases, and connect what they see to the underlying OSS implementation.
Features
- Support for PDF, HTML, and text;
- Defining and persisting extractors with their own schema and custom instructions;
- Adding few-shot examples for in-context learning;
- Sharing extractors among users;
- Swapping LLM models;
- A LangServe endpoint for the core extraction logic, allowing it to be plugged into your own Langchain workflows;
- A frontend that lets you define extraction schemas in natural language, share with other users, and test them on text or files (no support of few shot examples yet).
Walkthrough
Let’s walk through an example, extracting financial data from a public company earnings call. Here we use the prepared remarks from Uber’s Q4 2023 earnings call, which Uber investor relations makes available online.
Most public companies host earnings calls, providing their management opportunities to discuss past financial results and future plans. Natural language transcripts of these calls may contain useful information, but often this information must first be extracted from the document and arranged into a structured form so that it can be analyzed or compared across time periods and other companies.
Let’s first grab the PDF:
import requests
pdf_url = "https://s23.q4cdn.com/407969754/files/doc_earnings/2023/q4/transcript/Uber-Q4-23-Prepared-Remarks.pdf"
# Get PDF bytes
pdf_response = requests.get(pdf_url)
assert(pdf_response.status_code == 200)
pdf_bytes = pdf_response.contentNext, we will generate a unique identifier for ourselves. Our application does not manage users or include legitimate authentication. Access to extractors, few-shot examples, and other artifacts is controlled via this ID. Consider it secret, and don’t lose it!
from uuid import uuid4
user_id = str(uuid4())
headers = {"x-key": user_id}We next specify the schema of what we intend to extract. Here we specify a record of financial data. We allow the LLM to infer various attributes, such as the time period for the record. Here we use Pydantic for readability, but ultimately the service relies on JSON schema.
from pydantic import BaseModel, Field
class FinancialData(BaseModel):
name: str = Field(..., description="Name of the financial figure, such as revenue.")
value: float = Field(..., description="Nominal earnings in local currency.")
scale: str = Field(..., description="Scale of figure, such as MM, B, or percent.")
period_start: str = Field(..., description="The start of the time period in ISO format.")
period_duration: int = Field(..., description="Duration of period, in months")
evidence: str = Field(..., description="Verbatim sentence of text where figure was found.")Note that we include an evidence attribute, which provides context for the predictions and supports downstream verification of the results.
Once we've defined our schema, we create an extractor by posting it to the application:
url = "https://extract-server-f34kggfazq-uc.a.run.app"
data = {
"user_id": user_id,
"description": "Financial revenues and other figures.",
"schema": FinancialData.schema(),
"instruction": (
"Extract standard financial figures, specifically earnings and "
"revenue figures. Only extract historical facts, not estimates or guidance."
)
}
response = requests.post(f"{url}/extractors", json=data, headers=headers)
extractor = response.json()We’ve posted the extractor, which we can now access using its unique ID. We can now try the extractor on our PDF:
result = requests.post(
f"{url}/extract",
data={"extractor_id": extractor["uuid"], "model_name": "gpt-3.5-turbo"},
files={"file": pdf_bytes},
headers=headers,
)
result.json()And we get back:
{'data': [{'name': 'Adjusted EBITDA',
'scale': 'million',
'value': 1300,
'evidence': 'These strong top-line trends, combined with continued rigor on costs, translated to $1.3 billion in Adjusted EBITDA and $652 million in GAAP operating income.',
'period_start': '2023-10-01',
'period_duration': 3},
{'name': 'GAAP operating income',
'scale': 'million',
'value': 652,
'evidence': 'These strong top-line trends, combined with continued rigor on costs, translated to $1.3 billion in Adjusted EBITDA and $652 million in GAAP operating income.',
'period_start': '2023-10-01',
'period_duration': 3},
{'name': 'Revenue',
'scale': 'billion',
'value': 9.9,
'evidence': 'We grew our revenue by 13% YoY on a constant-currency basis to $9.9 billion.',
'period_start': '2023-10-01',
'period_duration': 3},
{'name': 'Adjusted EBITDA',
'scale': '$',
'value': 1.26,
'evidence': 'We expect Adjusted EBITDA of $1.26 billion to $1.34 billion.',
'period_start': '2023-01-01',
'period_duration': 12},
{'name': 'Adjusted EBITDA',
'scale': '$',
'value': 1.34,
'evidence': 'We expect Adjusted EBITDA of $1.26 billion to $1.34 billion.',
'period_start': '2023-01-01',
'period_duration': 12}]}Note that the formatting of the result has deviated in some ways from the descriptions in the schema– for example, we output “million” and “billion” instead of “MM” or “B” as instructed. Different models will struggle with this to varying degrees. Before reaching for a larger model, a judicious choice of few-shot examples can often be an efficient way to clarify our intent. Let’s add one to our extractor:
examples = [
{
"text": "In 2022, Revenue was $1 million and EBIT was $2M.",
"output": [
FinancialData(
name="revenue",
value=1,
scale="MM",
period_start="2022-01-01",
period_duration=12,
evidence="In 2022, Revenue was $1 million and EBIT was $2M.",
).dict(),
FinancialData(
name="ebit",
value=2,
scale="MM",
period_start="2022-01-01",
period_duration=12,
evidence="In 2022, Revenue was $1 million and EBIT was $2M.",
).dict()
],
},
]
responses = []
for example in examples:
create_request = {
"extractor_id": extractor["uuid"],
"content": example["text"],
"output": example['output'],
}
response = requests.post(f"{url}/examples", json=create_request, headers=headers)
responses.append(response)Here we add a single example that contains two records, with updated casing for the “name” field and formatting of the “scale” field. Re-running the extraction on the document, we recover the intended formatting:
result = requests.post(
f"{url}/extract",
data={"extractor_id": extractor["uuid"], "model_name": "gpt-3.5-turbo"},
files={"file": pdf_bytes},
headers=headers,
)
result.json()And we get:
{'data': [{'name': 'adjusted ebitda',
'scale': 'MM',
'value': 1300.0,
'evidence': 'These strong top-line trends, combined with continued rigor on costs, translated to $1.3 billion in Adjusted EBITDA and $652 million in GAAP operating income.',
'period_start': '2023-10-01',
'period_duration': 3},
{'name': 'gaap operating income',
'scale': 'MM',
'value': 652.0,
'evidence': 'These strong top-line trends, combined with continued rigor on costs, translated to $1.3 billion in Adjusted EBITDA and $652 million in GAAP operating income.',
'period_start': '2023-10-01',
'period_duration': 3},
{'name': 'gross bookings',
'scale': 'B',
'value': 37.6,
'evidence': 'Gross Bookings growth accelerated to 21% YoY on a constant-currency basis (23% excluding Freight), as we generated Gross Bookings of $37.6 billion.',
'period_start': '2023-10-01',
'period_duration': 3},
{'name': 'revenue',
'scale': 'B',
'value': 9.9,
'evidence': 'We grew our revenue by 13% YoY on a constant-currency basis to $9.9 billion.',
'period_start': '2023-10-01',
'period_duration': 3},
{'name': 'adjusted ebitda',
'scale': 'B',
'value': 1.3,
'evidence': 'We expect Adjusted EBITDA of $1.26 billion to $1.34 billion.',
'period_start': '2023-01-01',
'period_duration': 12}],
'content_too_long': False}LangServe client
A final note: because we’ve hosted the core extraction logic with LangServe, we can access it via the RemoteRunnable interface and plug it into larger chains and agent workflows.
The runnable can be invoked in the usual way:
from langserve import RemoteRunnable
runnable = RemoteRunnable(f"{url}/extract_text/")
response = runnable.invoke(
{
"text": "Our 2023 revenue was $100.",
"schema": FinancialData.schema(),
}
)
print(response)And we get:
{'data': [{'name': 'revenue',
'value': 100,
'scale': '$',
'period_start': '2023-01-01',
'period_duration': 12,
'evidence': 'Our 2023 revenue was $100.'}]}Or below, we incorporate it into a retrieval scenario. Here, instead of extracting directly on the input, we index some documents and treat the input as a search query. Searching for “rev”, below, will retrieve a document containing revenue and restrict the extraction to it:
from operator import itemgetter
from langchain_community.vectorstores import FAISS
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import OpenAIEmbeddings
doc_contents = ["Our 2023 revenue was $100", "Our Q1 profit was $10 in 2023."]
vectorstore = FAISS.from_texts(doc_contents, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever()
larger_runnable = (
{
"text": itemgetter("text") | retriever | (lambda docs: docs[0].page_content), # fetch content of top doc,
"schema": itemgetter("schema"),
}
| runnable
)
larger_runnable.invoke({"text": "rev", "schema": FinancialData.schema()})Which yields:
{'data': [{'name': 'revenue',
'value': 100,
'scale': '$',
'period_start': '2023-01-01',
'period_duration': 12,
'evidence': 'Our 2023 revenue was $100'}]}We are excited to see what extraction workflows you build, and welcome both feedback on and contributions to LangChain’s extraction capabilities!
Related content

Deep Agents
Agent Architecture
Open Source
How to Use RLMs in Deep Agents

Sydney Runkle
July 1, 2026
8
min

Agent Architecture
Deep Agents
Open Source
Running Untrusted Agent Code Without a Sandbox

Hunter Lovell
June 30, 2026
6
min

Open Source
Deep Agents
Agent Architecture
Introducing Dynamic Subagents in Deep Agents



S. Runkle,
C. Francis,
H. Lovell
June 29, 2026
9
min
See what your agent is really doing
LangSmith, our agent engineering platform, helps developers debug every agent decision, eval changes, and deploy in one click.
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み