官公需情報ポータルの入札公告をClaudeで要件に分解する手順
官公需情報ポータルの検索APIで入札公告を集め、参加資格・提出物・仕様要件に分解してから、自社条件との突き合わせまでをClaudeに任せる流れと、原文引用を機械で検証するスクリプトをまとめます。
官公需情報ポータルサイトは、国・独立行政法人・地方公共団体の入札情報を横断して検索できる、中小企業庁のサイトです。検索APIが公開されているため、公告の収集まではスクリプトで自動化できます。
Claudeが効くのは、その先です。数万字に及ぶ公告文と仕様書から、参加資格・提出物・仕様要件を拾い出し、自社の条件と突き合わせて応札可否のたたき台を作る工程です。この記事では、収集、分解、突き合わせ、検証の4段階を、実際に動かしたスクリプトとプロンプトで組みます。
公告から応札可否のたたき台までの流れ
- 1
収集
検索APIで条件に合う公告をXMLで取り、公告ごとのテキストファイルに保存します。
- 2
分解
Claudeに公告文と添付の仕様書を読ませ、要件を決まった形式のJSONに抜き出させます。
- 3
突き合わせ
自社の条件を書いたファイルと照らし、要件ごとに満たす・満たさない・不明を判定させます。
- 4
検証
Claudeが付けた原文引用が、公告文に本当にあるかをスクリプトで機械的に確かめます。
検索APIで取れる項目と、取れない情報
官公需情報ポータルサイト検索APIは、官公需の入札情報を自動で取得するためのAPIです。提供元は中小企業庁で、レスポンスはXML、提供形式はRESTです。仕様はAPIガイド(V1.1)のPDFにまとまっています。
検索条件に使えるパラメーターは次のとおりです。
| パラメーター | 指定する内容 |
|---|---|
| Query | 指定する内容検索文字列(AND・OR・ANDNOT・NOTが使える) |
| Project_Name | 指定する内容件名での絞り込み(前後方・途中一致) |
| Organization_Name | 指定する内容機関名での絞り込み(前後方・途中一致) |
| LG_Code | 指定する内容都道府県コード(JIS X0401、2桁、コンマ区切りで複数可) |
| Category | 指定する内容1が物品、2が工事、3が役務 |
| Procedure_Type | 指定する内容1が一般競争入札、2が簡易公募型の競争入札、3が簡易公募型の指名競争入札 |
| Certification | 指定する内容入札資格(A、B、C、D。コンマ区切りで複数可) |
| CFT_Issue_Date | 指定する内容公告日またはデータ取得日(期間形式) |
| Count | 指定する内容返す最大件数(既定は10、1,000以上は1,000として扱う) |
Query、Project_Name、Organization_Name、LG_Codeのうち、いずれか1つは必須です。複数を指定するとAND条件になります。期間は2026-10-01/(その日以降)や2026-10-01/2026-10-31のように、YYYY-MM-DD形式の半角スラッシュ区切りで書きます。
Claudeに渡す前に、取れるデータの限界を押さえておく必要があります。
- ポータルが提供するのは、発注機関がホームページに載せた入札情報です。すべての公告の掲載は保証されていません。ユーザーIDとパスワードが要る検索サービスを持つ機関や自治体は、対象外です。
- 発注機関のホームページに公開されてから、ポータルのデータベースに入るまで1日程度かかります。
- 公告文(ProjectDescription)には、公告文以外が混ざることがあります。公告が画像などの場合は、全文が存在しません。
- ExternalDocumentURIと添付ファイルのUriは、掲載元のURLです。現在も有効かどうかは分からないと、APIガイドに書かれています。
- 参加資格(Certification)や公示種別(ProcedureType)などは、情報がなければ出力されません。
最後の点は、プロンプトの設計に直結します。「参加資格がXMLに無い」ことと「参加資格が不要」は別です。Claudeに「資格要件なし」と書かせない工夫は、後の節で扱います。
公告を取得してClaude向けのファイルにする
次のスクリプトは、検索条件を指定して公告を取り、1件1ファイルのMarkdownに書き出します。Python標準ライブラリだけで動きます。
import sys
import urllib.parse
import urllib.request
import xml.etree.ElementTree as ET
from pathlib import Path
params = {
"Query": "システム開発 AND 運用",
"Category": "3", # 役務
"LG_Code": "13", # 東京都
"CFT_Issue_Date": "2026-10-01/",
"Count": "20",
}
url = "https://www.kkj.go.jp/api/?" + urllib.parse.urlencode(params)
root = ET.fromstring(urllib.request.urlopen(url, timeout=30).read())
if root.find("Error") is not None:
sys.exit("API error: " + root.findtext("Error"))
out = Path("notices")
out.mkdir(exist_ok=True)
for r in root.iter("SearchResult"):
def g(tag):
return (r.findtext(tag) or "").strip()
lines = [f"# {g('ProjectName')}", "",
f"機関: {g('OrganizationName')}",
f"公告日: {g('CftIssueDate')}",
f"公告URL: {g('ExternalDocumentURI')}",
f"参加資格(等級): {g('Certification') or '記載なし'}", "",
"## 公告文", g("ProjectDescription"), "",
"## 添付ファイル"]
for a in r.iter("Attachment"):
lines.append(f"- {a.findtext('Name')}: {a.findtext('Uri')}")
(out / f"{g('ResultId')}.md").write_text(
"\n".join(lines), encoding="utf-8")
print(root.findtext("SearchResults/SearchHits"), "hits")ポイントは、等級を取得できないときに「記載なし」と明示して書き出す部分です。オプション項目は、情報が無いと出力されません。空欄のままファイルに落とすと、Claudeが「無い=不要」と読み違える余地が残ります。
実際に返ってきた公告の姿
Count=2で、役務(Category=3)かつ東京都(LG_Code=13)の「システム開発」を検索したところ、2件が返りました。1件目は区の公募型プロポーザルで、ProjectDescriptionだけで約2万6千字、2件目は約3万字でした。2件を書き出したMarkdownは、7万〜9万バイト近くに達しています。Countを大きくしてまとめて渡すと、コンテキストはすぐ埋まります。1回の依頼は1公告か数公告にとどめるのが現実的です。
この2件には、次の特徴がありました。
- 参加資格・公示種別・入札開始日・開札日などのオプション項目が、2件とも出ていない(1件目は公募型プロポーザルで、入札案件でも同じとは限らない)
- 添付ファイルが1〜3件付き、仕様書本体は
(別紙1)提案要求仕様書のように添付側にある - 1件目のFileTypeが
docxだった(2件目はpdf)。APIガイドの記述は「pdfあるいはhtml」で、実際のレスポンスとは食い違う - 公告文に、Wordの目次フィールドの断片(
TOC \o "1-2" \h \z \u PAGEREF ...)が混ざっていた
最後の2つは、実務で効きます。FileTypeはpdfとhtmlだけだと決め打ちしたコードは、docxで想定外の動きをします。公告文のノイズは、Claudeの読解を妨げるほどではありませんが、数百字分のトークンを無駄にします。スクリプト側でPAGEREFを含む行を落としておくと、軽くなります。
要件への分解をClaudeに頼む
公告文だけで要件が尽くされる案件は多くありません。実際の仕様は、添付の提案要求仕様書や入札説明書にあります。添付のUriから該当ファイルをダウンロードし、notices/1.mdと同じフォルダに置いて、一緒に読ませます。
Claudeに抜き出させる要件は、種類を分けます。
分解する要件の4区分
参加資格
等級、所在地、実績、認証(ISOやプライバシーマークなど)、欠格事由。応札できるかどうかを分ける項目です。
提出物と期限
提案書・見積書・参加申込書の別と、質問締切・提出期限・開札日。
仕様要件
必須(「〜すること」)と任意(「望ましい」)を分けて拾います。
契約条件
履行期間、履行場所、支払い、再委託、知的財産権の帰属。
出力形式と禁止事項をCLAUDE.mdに固定する
毎回同じ形式で抜き出させるには、プロジェクトのCLAUDE.mdに規約を書いておくのが近道です。たとえば、次のような断片です。
# 入札公告の要件抽出ルール
- 入力は notices/<番号>.md と、同じ番号の添付ファイル。
- 出力は analysis/<番号>.json。配列の各要素は次の形にする。
{"id": "P-01", "kind": "participation|submission|spec|contract",
"requirement": "要件の要約(60字以内)",
"mandatory": true | false | null,
"evidence": "原文からの逐語引用(改変しない)",
"where": "公告文|添付名"}
- evidence は原文の一続きの文字列を、そのまま写す。要約や言い換えをしない。
- 原文に書かれていない項目は推測で埋めず、mandatory は null にする。
- 「参加資格(等級)」が「記載なし」の場合、資格要件なしと書かない。
添付の入札説明書に書かれていないか探し、無ければ「未確認」と出力する。
- 公告文と添付で内容が食い違う場合は、両方の引用を並べて残す。効いているのは、evidenceを逐語引用に限り、推測を禁じる2行です。分解の精度は、Claudeの読解力より、原文に紐づかない要件を出させない設計に左右されます。
質問の与え方は、公告ごとに「読んで要約して」ではなく、要件単位に刻む方が安定します。複雑な質問を小さな問いに割る考え方は、Claudeに分解プロンプトで質問すると信頼性が上がる理由で理由が整理されています。
Claudeへの依頼文の例を示します。
notices/1.md と、同じフォルダの添付ファイルを読み、
CLAUDE.md の「入札公告の要件抽出ルール」に従って
analysis/1.json を作ってください。
まず参加資格だけを抜き出し、次に提出物と期限、
仕様要件、契約条件の順に追加してください。要件定義書づくりとの違い
要件定義書の作成は、顧客と一緒に要件を作る作業です。入札公告の分解は、発注側がすでに書いた要件を読み取る作業で、抜け漏れの出し方が逆になります。後者では、書かれていない要件を作らないことが最優先です。要件定義書をSkill化する手順はClaude CodeとCoworkで要件定義書をSkill化する手順にあり、読み取る側の本記事とは設計の向きが違います。
自社条件と突き合わせて応札可否のたたき台を作る
分解した要件を、自社の条件と比べます。自社条件は、company-profile.mdのような1ファイルにまとめておきます。
# 自社条件
- 所在地: 東京都
- 入札参加資格: 都内自治体の「情報処理」で等級C
- 認証: ISO/IEC 27001、プライバシーマーク
- 同種実績: 自治体向け基幹システム保守(3年、2件)
- 配置できる人員: SE 4名(PM経験者1名)
- 再委託: 原則しないClaudeには、analysis/1.jsonの要件ごとに、次の3値を判定させます。
| 判定 | 意味 |
|---|---|
| 満たす | 意味company-profile.mdの記載で要件を満たすと言える |
| 満たさない | 意味記載に反する、または必須の条件が欠けている |
| 不明 | 意味自社条件に該当する記載が無く、判定できない |
「不明」の枠を設けるのが肝心です。枠が無いと、Claudeは情報が足りない項目も、もっともらしく「満たす」か「満たさない」に倒します。判定ごとに、根拠となる自社条件の行と、公告側のevidenceを並べて出力させると、レビューする人が見る場所が決まります。
応札するかどうかの判断は、人が行います。たたき台が示すのは「どの要件でつまずきそうか」の一覧で、結論ではありません。入札情報の個別の内容については、各発注機関に直接問い合わせるよう、ポータル自身が注意書きで求めています。要件の解釈に迷う項目は、質問書で発注機関に確かめる前提で、「不明」のまま残します。
引用が原文にあるかをスクリプトで確かめる
Claudeが書いたevidenceが、実は原文に無い言い回しだった、という事態は起こり得ます。分解結果を信頼する前に、機械で突き合わせます。
import json
import re
import sys
from pathlib import Path
def squash(s):
return re.sub(r"\s+", "", s)
src = squash(Path(sys.argv[1]).read_text(encoding="utf-8"))
items = json.loads(Path(sys.argv[2]).read_text(encoding="utf-8"))
for it in items:
q = it.get("evidence")
if q and squash(q) not in src:
print("原文に無い引用:", it["id"], q[:40])python3 check_evidence.py notices/1.md analysis/1.json空白と改行を除いてから比べているのは、公告文の中に、文の途中で改行が入るためです。実際に取得した公告でも、「…という。」の直後で改行され、次の行が「)」で始まる箇所がありました。空白を残したまま比べると、正しい引用まで不一致になります。
一方、全角と半角の数字の違いなど、見た目が同じでも文字が異なる場合は、この検査で不一致と出ます。不一致が出た引用は、Claudeの捏造と決めつけず、元の文字列と並べて目で確かめます。
添付ファイルがPDFやWordの場合、このスクリプトで突き合わせられるのは、テキストに変換した後の文字列です。変換前の図表やスキャン画像に書かれた要件は、検査の外にあります。そうした資料は人が読みます。
つまずきやすい点
ひらがな1文字の検索語はエラーになる
Queryに、ひらがな・カタカナを1文字だけ含めると、エラーになります。エラーメッセージはOnly one Kana character in search expressionです。「システムの開発」ではなく、「システム開発」のように語として切る必要があります。AND・OR・ANDNOTの前後には、半角の空白が要ります。
0件とエラーは別物
エラーはXMLのErrorタグで返ります。Errorが無くSearchHitsが0なら、条件に合う公告が無いだけです。スクリプトで両者を区別します。
API自体が止まることがある
ポータルのお知らせには、午前2時から午前5時の間はメンテナンスのためシステムを停止する旨が出ています。夜間の定期実行は、この時間帯を外します。APIガイドも、サーバーが稼働中でない場合や高負荷時に結果を返せないことがあると明記しています。
AI応用検索版との関係
ポータルには、2024年4月以降のデータを対象にした「AI応用検索版」の実証実験があります。ここで扱ったのは、検索と収集を自前のスクリプトで行い、要件の分解以降をClaudeに任せる構成です。ポータルの検索機能を使うだけなら、ブラウザでの検索で足ります。
Claudeに渡す情報の範囲
公告は公開情報ですが、自社条件のファイルには、実績や人員が書かれます。Claudeに何を渡してよいかの社内ルールは、先に決めておきます。公共調達の側で生成AIの利用に除外条件がある点は、公共調達でClaude等生成AIが適用対象外になる条件で扱っています。
公告が数十件あるときの回し方
1件ずつ会話で依頼する方法は、数件までです。数十件を毎週さばくなら、公告ごとに同じ依頼を繰り返す形にします。Claude Codeをスクリプトから呼び出す方法は、Claude Code -pモードでスクリプトやパイプラインを自動化する基本に基本があります。
運用では、段階を分けると無駄が減ります。
- 取得時に、件名とOrganizationNameだけで一覧を作り、明らかに対象外の案件(職種違い、遠隔地)を人が落とす
- 残った公告だけを、Claudeに参加資格の分解だけさせる
- 参加資格を満たし得る案件にだけ、仕様要件と契約条件の分解を進める
参加資格の段階で外れる公告に、数万字の仕様書を読ませるのは、コストに見合いません。分解を4区分に分けたのは、この早期の足切りにも使えるからです。
次の一手
最初の1件は、手元で絞り込み条件を決めてから試すと、ずれに気づきやすくなります。
- 自社の都道府県コードとカテゴリで、
Count=5程度で検索する - 出てきた公告の添付仕様書を、公告文と同じフォルダに置く
- CLAUDE.mdの規約を、自社の言葉に合わせて直す
- 分解結果を、
check_evidence.pyで検査してから読む
分解の質が安定したら、突き合わせの工程を足していくと、無理なく自動化の範囲を広げられます。