在 SEC 申報文件上打造盡職調查 Agent(10-K、10-Q、8-K、財報)

讀一份 10-K 大半時間都在按 Ctrl+F。要對五十家公司做這件事就成了一份苦差事。用一個對 SEC EDGAR 做搜尋與擷取的 Agent,把無聊的那 80% 取代掉 —— 把真正重要、留給人類分析師的那 20% 保留下來。
一句話總結
- •架構:股票代號查詢 → SEC Filings Search(申報文件 + 財報 + 股權統計)→ URL Extract 抓長段落 → LLM 以章節層級引用回答。
- •成本上限:SEC Filings Search 每次呼叫 120 credits(約 $0.12);典型的三問題公司回顧大約花 ~$0.40 的 credits + ~$0.05 的 LLM token。
- •Agent 擅長的:事實型查詢(分部營收、資本支出趨勢、公司治理、逐年的風險因素變動)、高階主管薪酬摘要、近期 8-K 事件。
- •仍需要人類的:對管理階層素質的判斷、市場定位、特定交易的問題,以及任何超出申報文件文字之外的東西。
為什麼這值得自動化
對一家上市公司做第一輪盡職調查的閱讀,大半是機械性的:拉出最新的 10-K、掃過風險因素與 MD&A、查看近期的 8-K、瞄一眼最近一次的財報電話會議。一位助理級分析師對每家公司要花 2 到 4 小時做這件事。產出鮮少是什麼深刻洞見 —— 它是一份結構化的事實樣態,再由另一位更資深的人去據此推理。
那個「整理事實樣態」的步驟,正是一個小型 Agent 可以接手的。搜尋 SEC、擷取相關段落、附引用做摘要。資深的人仍然負責推理 —— 但他們是從一份 5 分鐘的閱讀量開始,而不是 4 小時。
有三件事讓這在當下變得實際可行:
- 對申報文件做語意搜尋 意味著你可以問「分部營收怎麼變的」,然後從對的表別裡拿回對的那一段,而不必讀 200 頁。
- 長上下文 LLM 能把一份完整的 10-K 加上幾份 8-K 放在工作記憶裡,回答跨文件的問題。
- 提示詞裡的引用紀律 讓產出可以在數秒內被驗證 —— 這正是合規與審閱工作流程所要求的。
架構
question + ticker
│
├─ /api/company/facts (2 credits)
│ ↳ confirm ticker, get CIK and sector
│
├─ /api/search/sec (120 credits)
│ ↳ semantic search across 10-K/10-Q/8-K/earnings/equity stats
│
├─ /api/extract (2 credits per URL)
│ ↳ pull full text of the top 3-5 most relevant filings
│
└─ Claude / GPT-4 with citation-required prompt
↳ "answer + [Form, Fiscal Period, Section]"單一問題成本:約 130 credits(約 $0.13)的 API + 約 $0.03 的 LLM。一次三問題的公司回顧(財務趨勢、風險因素差異、近期重大事件)落在約 $0.45 至 $0.60。相較於以任何合理計費費率計算的一個分析師工時,這筆帳一目了然。
真正撐得起場面的系統提示詞
在金融 RAG 上,決定產出品質最重要的單一因素就是系統提示詞。我們用的這份:
You are a financial research assistant for an investment team. You answer
questions about US public companies using SEC filings, earnings call
transcripts, and equity statistics retrieved by your tools.
Rules — non-negotiable:
1. Cite every numeric claim with: [Form, Fiscal Period, Section]. Example:
"Operating income rose 12% YoY to $4.1B [10-K FY2025, Item 7 MD&A]."
2. Quote numbers verbatim. Do not round, paraphrase, or convert. If a
filing says "$4,127M", do not say "$4.1B" unless the filing itself does.
3. If the answer requires content you have not extracted, say so:
"I could not retrieve the FY2024 10-K for the segment-level breakdown.
Please re-run with that filing in scope."
4. Do not infer from training-data knowledge. If your tools didn't return
it, you don't know it.
5. Default to the most recent fiscal period available. State the period
you used.
6. Format multi-figure answers as a small markdown table with one column
per fiscal period. Always end with a one-line "How I read this" summary.
Tone: precise, terse, no marketing language.規則 1、2、4 三者合計,消除了我們量測到約 90% 的捏造問題。規則 3(優雅地說「我不知道」)則是讓這套東西有別於那種自信滿滿亂編數字的聊天機器人的關鍵。
Agent 能乾淨俐落處理的範例查詢
- 「比較 Apple 過去 5 個會計年度的服務營收趨勢。」 → 從相關的 10-K 拉取,回傳一張附 MD&A 引用的表格。
- 「NVIDIA 的風險因素在 FY2023 到 FY2025 之間有什麼變化?」 → 跨文件差異比對,並引用每份表別中的 Item 1A。
- 「摘要 $TICKER 最近 4 份 8-K。」 → 語意搜尋過濾出 8-K,依申報日期排序。
- 「Microsoft 的財務長在最近一次財報電話會議上對 AI 資本支出說了什麼?」 → 搜尋逐字稿、擷取相關的 Q&A 段落、逐字引用。
它的不足之處 —— 以及正確的人機交接點
Agent 會在三個可預期的地方絆倒:
- 對管理階層素質的判斷。 申報文件告訴你他們做了什麼,而不是他們有沒有能力。別問 Agent 這個。
- 申報文件之外的同業比較。 如果問題是「這個毛利率跟同業比如何」,Agent 只知道搜尋到的申報文件裡有的東西。要做同業比較,你需要一個獨立的資料集,或是對每家公司各跑一次 Agent 再彙整。
- 前瞻性的評論。 申報文件含有前瞻性陳述,但除非另外交代,模型會照字面接受。在提示詞裡加上:「明確標記前瞻性陳述。不要把財測當成事實呈現。」
最小可行的版本
和 研究 Agent 教學 裡同一套 Claude tool-use 迴圈模式適用於此 —— 只要改工具與系統提示詞即可:
from anthropic import Anthropic
import requests
KEY = "pk_yourkey"
client = Anthropic()
def fetch_tool(path: str) -> dict:
return requests.get(f"https://www.apipick.com{path}/tool-schema").json()["claude"]
TOOLS = [
fetch_tool("/api/search/sec"),
fetch_tool("/api/extract"),
fetch_tool("/api/company/facts"),
]
def call_tool(block):
name_to_path = {
"sec_search": "/api/search/sec",
"extract_urls": "/api/extract",
"company_facts": "/api/company/facts",
}
path = name_to_path[block.name]
method = "GET" if block.name == "company_facts" else "POST"
if method == "GET":
resp = requests.get(
f"https://www.apipick.com{path}",
params=block.input,
headers={"x-api-key": KEY},
timeout=30,
)
else:
resp = requests.post(
f"https://www.apipick.com{path}",
json=block.input,
headers={"x-api-key": KEY},
timeout=30,
)
return {
"type": "tool_result",
"tool_use_id": block.id,
"content": resp.text,
"is_error": resp.status_code != 200,
}
def due_dil(question: str) -> str:
messages = [{"role": "user", "content": question}]
while True:
r = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=2048,
system=SYSTEM_PROMPT, # the one above
tools=TOOLS,
messages=messages,
)
messages.append({"role": "assistant", "content": r.content})
if r.stop_reason == "end_turn":
return "\n".join(b.text for b in r.content if b.type == "text")
if r.stop_reason == "tool_use":
results = [call_tool(b) for b in r.content if b.type == "tool_use"]
messages.append({"role": "user", "content": results})
print(due_dil("Compare Snowflake's product revenue YoY for the last 4 quarters."))接下來可以怎麼延伸
- 可重複回顧的範本。 把 Agent 包進一個小型 CLI,接收一個股票代號,產出固定格式的 markdown 簡報:「近期 8-K」、「分部營收趨勢」、「風險因素差異」。同一個 Agent,腳本化的提示詞。
- 觀察清單模式。 每天早上對一個股票代號跑 Agent,把今天的答案和昨天的做差異比對,只浮現變動的部分。這與 晨間簡報模式 搭配得很好。
- 結合專利與預測市場。 對科技/生技標的,疊加 Patent Search 看 IP 變動,以及 Prediction Markets 看群眾隱含的結果(例如藥物核准機率)。
這個模式可以推廣。SEC 是我們提供的語料中最稠密、最對結構友善的一個 —— 但這個迴圈(「語意搜尋 → URL 擷取 → 附引用回答」)適用於任何你在乎的結構化文件語料:法律申報文件、科學摘要、專利請求項。先把盡職調查 Agent 做出來,再把架構橫向移植過去。
常見問題
SEC 索引有多即時?
申報文件在被 EDGAR 接受後數小時內就會建索引。對於 8-K(具時效性的那些 —— 重大事件、領導層變動、併購)而言,這對日終工作流程通常已經夠快。如果你需要在一小時內收到新 8-K 的通知,請把搜尋與獨立的 SEC RSS feed 搭配使用,並只把 Agent 用於內容分析,而非偵測。
它有涵蓋財報電話會議逐字稿嗎?
有 —— SEC Filings Search 索引除了申報文件本身,還包含美國財報電話會議逐字稿與股權統計(價格/成交量、市值歷史)。單一一次語意查詢就能從這些來源中任意拉取。
如果我要大規模做,有哪些成本槓桿?
三個。(1) 先用 Company Facts(2 credits)做前置過濾,在花 120 credits 做 SEC 搜尋前,先確認該股票代號是真實存在的上市公司。(2) 以(股票代號、季度)為鍵快取搜尋結果 —— 申報文件只會按時程更新。(3) 每個問題用一次寬範圍搜尋,而非多次窄範圍搜尋;Agent 很擅長跨結果做綜整。
Agent 能處理非美國的申報文件嗎?
SEC Filings Search 涵蓋在美國上市的公司(10-K、10-Q、8-K)。對於英國公司,請搭配 UK Legal Search 與 Web Search。其他司法管轄區則退而求其次,用 Web Search + URL Extract 去抓相關國家監管機構的網站(Companies House、SEDAR 等)。
我要怎麼避免幻覺出來的數字?
系統提示詞裡有三條規則最能改善情況。(1)『逐字引用擷取文字裡的數字 —— 絕不改寫或四捨五入。』(2)『一律附上申報表別、會計期間,以及章節參照。』(3)『若相關申報文件未被擷取,請明確說出 —— 不要從訓練資料推測。』這三條合在一起就能消除大部分的捏造。
本文使用的 API
Sarah Choy 是 API Pick 的 CEO,專注於為 AI Agent 與 LLM 工作流打造可用於正式環境的 API。