自然言語の検索がどう解決されるか
自然言語の query が company や observation にどう当たり、なぜヒットしたかを説明します。
このページの内容15項目
Signal Foundry の自然言語検索は、単純な全文検索ではありません。 会社群の依頼は list estimate、1 社特定は companies search、保存しない横断探索は observations/search で処理の仕方が違うため、使い分けを理解するとヒット率と保存体験がかなり上がります。
まず結論
- 会社を解決したいときは
sf companies search <query> - 保存したい会社群が対応済み条件で書けるときは
sf list estimate "<query>" - テーマに反応している会社群を探したいときは、まず
sf observations searchかsf job sales-list - 保存しない横断探索だけ
sf observations search [query] - 長い自然文をそのまま入れるより、短い query と明示的な filter に分けた方が安定します
まず何が起きるか
自然文の依頼を受けたとき、内部では次の 4 段階で処理されます。
- 依頼が
会社群作成か会社解決か横断探索か有報差分かを決める - 選ばれた surface ごとに query を正規化する
- candidate を作り、relevance を計算する
- なぜヒットしたかを
query_matchやobservation.summary / evidenceで返す
つまり、自然文 -> そのまま 1 本の search ではありません。最初にどの surface を選ぶかが最重要です。
よくある依頼と内部の分岐
| ユーザーの依頼 | 最初に選ぶ surface | 理由 |
|---|---|---|
トヨタを調べて | companies search | 会社特定が先だから |
生成AIに積極的な会社を出して | observations search | テーマ語は observation evidence を先に見る方が安定するから |
前回の有報との差分を見たい | companies search -> company filings -> filing show -> filing compare | まず company と filing を特定し、artifact / facts の状態を確認する必要があるから |
営業候補を作りたい | list estimate | 母集団と credit を先に確認する必要があるから |
companies search の解決順
sf companies search は、概ね次の順で company 候補を組み立てます。
- 入力を正規化する
sf_company_identifiersの exact match を見るcompany_idの exact candidate を見る- 正規化済み company name の exact match を見る
- 必要なときだけ normalized name の prefix match を足す
- relevance で並べる
実用上の意味は次です。
7203のような code っぽい query は強く exact 解決を狙うjpx_7203のような canonicalcompany_idも直接解決できるABEJAのような表記ゆれは正規化で吸収される- free text が曖昧なときは、社名 prefix と関連 field で補助する
companies search で relevance に使われる field
高く評価されやすい順に、概ね次を見ています。
company_iddisplay_namelegal_namewebsite_domainwebsite_urlmarket_segmentindustry_33industry_17
さらに次の補正があります。
- identifier exact match は強い加点
listedの会社は少し優先
つまり、上場コード、社名、website domain のいずれかが分かっているとかなり強いです。
list estimate の使いどころ
sf list estimate は、自然文の会社群を保存前に見積もる surface です。
sf list estimate "上場企業のうち、売上100億以上" --json sf list candidates --from-estimate <estimateId> --json
ここで重要なのは、estimate_id が frozen plan になることです。candidates と materialize は自然文を再解釈せず、この plan を使います。対応しやすい条件は、上場状態、market segment、会社名/識別子、EDINET 財務指標のしきい値、EDINET本文のkeyword/semanticテーマ検索です。
保存する時だけ、credit 上限を明示して実行します。
sf list materialize --from-estimate <estimateId> --name "AI CRM Targets" --execute --max-credits 100 --json
candidate preview は薄い確認用で、export できません。CSV が必要なら materialize 後に sf list export を使います。
確認する key
companies[].company.company_idcompanies[].query_matchestimate.estimate_idresolved_definition.warningscounts.matched_companiesbilling.materialize.estimated_creditsresults[].matched_observations[].summaryresults[].matched_observations[].evidence
companies search で実際に何がヒットするか
例:
sf companies search 7203 --json
この場合は、7203 が identifier exact match として強く扱われ、jpx_7203 や関連 identifier が上位に来ます。
別の例:
sf companies search "トヨタ" --json
この場合は、社名正規化と prefix match が効きます。ただし、曖昧な社名では別会社が先に出ることがあるので、確実に進めたいなら証券コードや domain を使ってください。
observations/search の解決順
POST /api/signal-foundry/observations/search は company を直接返すのではなく、まず observation を集めてから company ごとに group します。
処理は概ね次の順です。
queryから母集団語や接続語を落としてテーマ token に寄せる- SQL で
company_idsobservation_typesobservation_subtypessourcesを先に絞る - EDINET本文は keyword 検索に加えて、embedding backfill 済みなら semantic 検索も使う
LLMのような短い英語テーマ語は大規模言語モデル生成AIAIエージェント自然言語処理などへ展開してから EDINET 本文に当てる- app 側で company / profile filter をかける
- token が
summarytypesubtypesourceevidencecompany名industrywebsiteに入るか、または semantic similarity があるかを見る - relevance を計算し、company ごとにまとめる
ここで重要なのは、構造条件とテーマ語を混ぜすぎないことです。生成AIに関連する上場企業 のような自然文は、母集団語を落としてテーマ evidence に当てます。一方で、保存やcredit見積もりは list estimate で frozen plan にします。
observations/search で実際に何が起きるか
例:
sf observations search "生成AI CRM" \ --industry 情報・通信業 \ --limit 10 \ --observations-per-company 3 \ --json
この場合は、まず industry で候補 company 群を絞り、そのあと 生成AI と CRM が summary type subtype source evidence などに入る observation を探します。返ってくるのは company 単位のまとめですが、内部では先に observation を集めてから company ごとにまとめています。
そのため、ユーザーが感じる なんでこの会社が出たのか の答えは、最終的には observations[].summary と evidence にあります。
良い query と悪い query
悪い例:
{
"query": "東証プライムの情報通信企業で生成AIとCRMと営業効率化に積極的な会社"
}
良い例:
{
"query": "生成AI CRM",
"filters": {
"industries": ["情報・通信業"],
"market_segments": ["prime"]
}
}
構造条件は filters に移し、query はテーマ語だけに寄せてください。
まずは短く当てる
sf observations search "生成AI CRM" \ --industry 情報・通信業 \ --market-segment prime \ --limit 10 \ --observations-per-company 3 \ --json
結果が弱いときは、次の順で直します。
queryの token を減らす- 制約を
filtersへ移す - まず
companies searchでcompany_idを解決し、company_idsfilter を使う LLMGenAIのような短い英語略語だけなら、日本語同義語を 1 つ足す- それでも不足するなら
sourcesやobservation_typesを見直す
復旧方法
companies search が曖昧な場合は、証券コード、canonical company_id、website domain の順で query を狭めます。list estimate が unsupported 条件を返す場合は、保存せずに sf list plan "<query>" --json で条件を分解します。observations search が弱い場合は、自然文を短いテーマ語と explicit filter に分け直してください。
Claude Code / Codex がやるべき分解
自然文の依頼を agent が受けたら、まず次のように分解すると安定します。
- 固有名詞がある:
companies search - 対応済み条件で保存したい会社群:
list estimate - テーマ語しかない会社群:
observations searchまたはjob sales-list - 上場日や IPO 年で絞る会社群:
list planと capability map でlisting_eventscoverage を確認し、supported なら estimate に進む - 保存しない横断探索だけ:
observations search 差分前回有報がある:company filings、filing show、filing compare保存しておきたいがある:list materialize --execute --max-credits
たとえば 生成AI に積極的な競合を出して、あとで見直せるようにして と言われたら、1 本の search に流すのではなく、次の 2 段階に分けます。
observations searchまたはjob sales-listでテーマ根拠を確認する- 候補条件を上場状態、業種、財務指標などの対応済み条件に分解する
list estimateとlist candidatesで対象会社数と credit を見るlist materialize --execute --max-creditsで保存 loop を作る- 必要なら
list enrich --source websiteとlist exportへ進む
この分解をしないと、検索は当たったが後で見返せない か、保存したが何を根拠に入れたか分からない のどちらかになりやすいです。
このページに含めないもの
自然言語検索の結果を無確認で publish queue に積み上げる導線は、現時点の public product core ではありません。一方で、保存前の見積もりと確認として sf list estimate、sf list candidates、保存面として sf list materialize、追加情報として sf list enrich は使えます。