文本轉(zhuǎn)化為計(jì)量回歸變量:綠色全要素生產(chǎn)率實(shí)證鏈路全解)
簡介面向經(jīng)濟(jì)學(xué)與人工智能交叉研究者這是一套基于RAG技術(shù)和大語言模型剖析A股上市公司年報(bào)、評估人工智能對企業(yè)綠色全要素生產(chǎn)率影響的統(tǒng)計(jì)建模項(xiàng)目資源。項(xiàng)目完整覆蓋了從年報(bào)數(shù)據(jù)爬取、文本清洗嵌入、LLM打分到統(tǒng)計(jì)建模與穩(wěn)健性檢驗(yàn)的流程并重點(diǎn)納入企業(yè)融資約束異質(zhì)性分析可幫助讀者快速上手將NLP前沿技術(shù)用于經(jīng)濟(jì)計(jì)量研究。資源包共20個(gè)文件壓縮后約2.29MB其中7個(gè)py腳本負(fù)責(zé)年報(bào)爬取、數(shù)據(jù)轉(zhuǎn)換、模型下載與GPT評分等7個(gè)ipynb筆記分步展示數(shù)據(jù)處理、建模與可視化另含txt說明、json配置、docx和md文檔構(gòu)成清晰的操作指引。已有66人學(xué)習(xí)下載適合有Python與計(jì)量基礎(chǔ)、希望復(fù)現(xiàn)完整研究鏈路或借鑒文本分析思路的研究者使用。1. 把年報(bào)喂給大模型之前RAG 在這條實(shí)證鏈路里解決什么問題一個(gè)做實(shí)證研究的人拿到這個(gè) RAG 與統(tǒng)計(jì)建模結(jié)合的項(xiàng)目第一反應(yīng)大概率是「大語言模型和綠色全要素生產(chǎn)率有什么關(guān)系」。關(guān)系在于你想量化企業(yè)的人工智能應(yīng)用程度但年報(bào)里從來沒有一列現(xiàn)成的「AI 采用率」。人工讀年報(bào)打分不現(xiàn)實(shí)純關(guān)鍵詞匹配又會(huì)把「人工智能可能帶來風(fēng)險(xiǎn)」這種反例當(dāng)證據(jù)。這個(gè)項(xiàng)目用 RAG 檢索增強(qiáng)生成把鏈路自動(dòng)化——爬年報(bào)、轉(zhuǎn)文本、語義切分、向量化檢索再用大語言模型按統(tǒng)一問題模板打分把分?jǐn)?shù)變成統(tǒng)計(jì)模型里的核心解釋變量跑綠色全要素生產(chǎn)率的基準(zhǔn)回歸、融資約束異質(zhì)性分析和穩(wěn)健性檢驗(yàn)。適合正在做 ESG、綠色經(jīng)濟(jì)、數(shù)字化轉(zhuǎn)型實(shí)證的碩博生也適合想把文本分析接進(jìn)計(jì)量流程的從業(yè)者。2. 年報(bào)爬取與文本預(yù)處理從 PDF 到可檢索語料的三道關(guān)2.1 按概念板塊爬年報(bào)增量更新比全量重跑更省事年報(bào)數(shù)據(jù)的第一道門檻是來源。項(xiàng)目里 熱門概念uri爬取.ipynb 走的是按熱門概念分類拿 uri、再回源下載 PDF 的路線好處是同一概念板塊的公司天然聚在一起后面做融資約束分組、行業(yè)異質(zhì)性分析時(shí)可以直接復(fù)用這份分組。如果爬取過程遇到動(dòng)態(tài)渲染的頁面配套的 安裝瀏覽器驅(qū)動(dòng).txt 就是給 selenium 準(zhǔn)備的按里面寫的瀏覽器版本號(hào)匹配下載對應(yīng)驅(qū)動(dòng)版本不匹配會(huì)直接報(bào) session not created。年報(bào)爬取_final.py 在下載層做了兩件實(shí)際的事本地已存在且文件大小正常的直接跳過每次請求之間強(qiáng)制 sleep。這兩件事看著簡單卻是整個(gè)爬取任務(wù)能跑完的關(guān)鍵。年報(bào) PDF 一份動(dòng)輒 5MB 上下幾千家公司全量重跑一次要十幾個(gè)小時(shí)且大概率中途被限流斷連。# 年報(bào)爬取_final.py 的增量下載邏輯 import os import time import requests def download_annual_reports(stock_codes, years, save_dirreports/): os.makedirs(save_dir, exist_okTrue) failed [] for code in stock_codes: for year in years: path os.path.join(save_dir, f{code}_{year}.pdf) if os.path.exists(path) and os.path.getsize(path) 1024 * 1024: continue # 已下載且大于 1MB跳過 url fhttps://api.cninfo.example/report/{code}/{year} # 按實(shí)際公告接口替換 try: r requests.get(url, timeout30, headers{User-Agent: Mozilla/5.0}) if r.status_code 200: with open(path, wb) as f: f.write(r.content) else: failed.append((code, year)) except Exception: failed.append((code, year)) time.sleep(1) # 限速年報(bào) PDF 體積大請求頻率過高必被斷連 return failed這段代碼的重點(diǎn)不是 requests 本身而是「先查本地再?zèng)Q定是否請求」的增量判斷和 sleep 限速。我一般會(huì)把 failed 列表落盤成 csv下次只處理失敗項(xiàng)成功項(xiàng)不再碰。項(xiàng)目里 年報(bào)數(shù)量統(tǒng)計(jì).ipynb 的作用也在這——先摸清每個(gè)板塊實(shí)際有多少份年報(bào)、缺失多少再?zèng)Q定要不要補(bǔ)爬。PDF 下載完真正的活是解析文本。年報(bào)的排版千奇百怪頁眉頁腳重復(fù)表格密集還有掃描件。直接整本丟給文本解析庫目錄、封面、封底的雜訊全都會(huì)混進(jìn)來后面向量化時(shí)這些噪音會(huì)平分檢索權(quán)重。2.2 PDF 轉(zhuǎn)文本先保結(jié)構(gòu)頁眉頁腳和表格是最大變量年報(bào)轉(zhuǎn)換_final.py 解決 PDF 到文本的轉(zhuǎn)換。處理這類文件我一般先看 PDF 有沒有內(nèi)置文本層——絕大多數(shù)上市公司年報(bào)是排版軟件生成的有文本層直接用 pdfplumber 提取即可只有早年掃描版年報(bào)才需要走 OCR 兜底。項(xiàng)目根目錄的 說明文件.txt 把安裝步驟和數(shù)據(jù)流程圖寫得很清楚跑之前先讀一遍能省半天時(shí)間。# 年報(bào)轉(zhuǎn)換_final.py 的解析主流程 import pdfplumber def pdf_to_pages(pdf_path): pages [] with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: text page.extract_text() pages.append({page_no: page.page_number, text: text or }) return pages def ocr_fallback(img_path): # 掃描頁兜底常見做法是接 PaddleOCR from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch) result ocr.ocr(img_path, clsTrue) return \n.join([line[1][0] for line in result[0]])pdfplumber 的 extract_text 按頁面坐標(biāo)順序輸出文本遇到雙欄排版會(huì)出現(xiàn)左右欄串讀。我一般會(huì)先按行 y 坐標(biāo)排序合并再輸出把同一水平線上的文字拼成一行再按 y 從大到小輸出到段落里。OCR 兜底只用于 pdfplumber 提取結(jié)果為空白的頁不要整本都走 OCR——速度差兩個(gè)量級而且識(shí)別誤差會(huì)傳導(dǎo)到后面的檢索和打分。判定標(biāo)準(zhǔn)很簡單如果連續(xù)五頁以上 extract_text 都是空基本可以認(rèn)定這份年報(bào)是掃描件直接批量走 OCR。轉(zhuǎn)出來的文本還要過一遍清洗。頁眉的「XX 公司 2023 年年度報(bào)告」、頁腳的「第 X 頁」會(huì)在切片后大量重復(fù)嚴(yán)重污染向量相似度——檢索「人工智能」時(shí)模型可能因?yàn)轫撁祭锍霈F(xiàn)過這個(gè)詞而把不相關(guān)頁面召回。清洗的坑在于不能一刀切刪除所有數(shù)字年報(bào)里「營業(yè)收入 12.3 億元」「同比增長 5.2%」是核心信息刪了就廢了。正確做法是只在每頁開頭和結(jié)尾的固定位置做規(guī)則替換正文原樣保留。這一步在項(xiàng)目里對應(yīng) 數(shù)據(jù)處理_final.ipynb列表數(shù)據(jù)處理.ipynb 則負(fù)責(zé)把清洗后的文本組織成公司-年份兩級結(jié)構(gòu)方便后續(xù)切片時(shí)追溯來源。2.3 語義切分按章節(jié)邊界切別按固定長度硬切切分是 RAG 系統(tǒng)里最容易被低估的環(huán)節(jié)。固定按 512 字符硬切會(huì)把「第三節(jié) 管理層討論與分析」從中間攔腰截?cái)嘁粋€(gè)完整觀點(diǎn)被拆成兩半后續(xù)檢索拿到的上下文是殘缺的。embedding 模型再強(qiáng)輸入本身缺了一段語義就不可能完整。我的習(xí)慣是先按章節(jié)標(biāo)題切大塊再對超長章節(jié)按段落邊界做二次切分。# 章節(jié)感知的切分實(shí)現(xiàn) import re SECTION_RE re.compile(r^(第[一二三四五六七八九十][節(jié)章])\s*(.*)$) def split_into_sections(pages): sections [] cur {title: 報(bào)告開頭, content: []} for page in pages: for line in page[text].splitlines(): stripped line.strip() m SECTION_RE.match(stripped) if m and len(stripped) 50: # 標(biāo)題行通常很短防止誤判正文 sections.append(cur) cur {title: stripped, content: []} else: cur[content].append(line) sections.append(cur) return [s for s in sections if \n.join(s[content]).strip()] def chunk_section(section, max_len800, overlap80): paras section[content] chunks, buf, buf_len [], [], 0 for p in paras: buf_len len(p) buf.append(p) if buf_len max_len: chunks.append((section[title], \n.join(buf))) buf buf[-1:] # 保留上一塊的最后一行形成輕微重疊 buf_len sum(len(x) for x in buf) if buf: chunks.append((section[title], \n.join(buf))) return chunksmax_len800 是中文年報(bào)場景里比較穩(wěn)的起點(diǎn)兼容主流 embedding 模型的 token 上限overlap80 的輕微重疊讓跨塊的觀點(diǎn)至少在一個(gè)塊里完整出現(xiàn)。關(guān)鍵點(diǎn)在「len(stripped) 50 才認(rèn)為是標(biāo)題」這個(gè)約束——年報(bào)正文里經(jīng)常出現(xiàn)「第三章」這種字樣不做長度限制會(huì)把正文當(dāng)標(biāo)題切碎導(dǎo)致章節(jié)結(jié)構(gòu)完全亂掉。切片時(shí)把章節(jié)標(biāo)題拼在每個(gè) chunk 開頭檢索時(shí)命中「管理層討論」和「會(huì)計(jì)附注」的文本就能區(qū)分開。提示切分參數(shù)直接影響檢索命中率。同一套代碼max_len 從 800 改到 1200top-5 召回結(jié)果可能完全不同。定稿前拿幾十個(gè)疑難問題做一次命中率抽查別等全量跑完再后悔。3. RAG 檢索增強(qiáng)生成大語言模型如何把年報(bào)文本變成回歸變量3.1 embedding 選型與向量入庫中文年報(bào)場景的實(shí)際選擇檢索增強(qiáng)的第一層是召回。項(xiàng)目里的 RAGGPT embedding.py 負(fù)責(zé)把上一章切好的 chunk 向量化。中文金融文本場景我一般選 bge-large-zh 這類中文 embedding 模型對年報(bào)這種滿是財(cái)務(wù)術(shù)語的文本語義區(qū)分度明顯好于通用多語言模型。config.json 里配了模型路徑、API 密鑰和輸出目錄跑之前先確認(rèn)模型路徑指向有效不然 SentenceTransformer 會(huì)嘗試現(xiàn)場下載等半天才報(bào)超時(shí)。download_model.py 的作用就是提前把模型權(quán)重拉到本地——bge-large-zh 的權(quán)重有好幾個(gè) GB建議先跑一遍確認(rèn)下載完整再去做向量化。# RAGGPT embedding.py 向量化入庫 from sentence_transformers import SentenceTransformer import json MODEL_PATH ./models/bge-large-zh # 本地權(quán)重提前用 download_model.py 拉取 model SentenceTransformer(MODEL_PATH, devicecuda:0) def build_vector_store(chunks, out_pathvector_store.jsonl): with open(out_path, w, encodingutf-8) as f: for title, text in chunks: vec model.encode(text, normalize_embeddingsTrue) f.write(json.dumps( {title: title, text: text, embedding: vec.tolist()}, ensure_asciiFalse ) \n)關(guān)鍵在 normalize_embeddingsTrue。年報(bào)的 chunk 長度差異很大不歸一化的話長文本向量模長天然偏大余弦相似度會(huì)被長度主導(dǎo)而不是語義主導(dǎo)。存儲(chǔ)用 jsonl 而不是單個(gè) npy 文件是因?yàn)楹竺嬉垂?、年份、章?jié)過濾jsonl 每行自帶字段過濾時(shí)逐行讀判斷就行。檢索量在幾萬條以內(nèi)jsonl 配合 numpy 矩陣暴力算余弦完全夠用到了十萬級以上我才會(huì)建議換 FAISS 或 Milvus。實(shí)證項(xiàng)目我傾向于少引入基礎(chǔ)設(shè)施能用一個(gè)文件解決的就不上服務(wù)出問題好排查。3.2 questions.json問題模板決定了打分質(zhì)量的天花板召回回來的是文本塊把文本塊變成變量的是大語言模型。questions.json 是整個(gè)項(xiàng)目里最值得細(xì)看的文件它定義了一套問題模板每個(gè)問題都對應(yīng)一個(gè)能進(jìn)回歸的變量。gpt_score.py 負(fù)責(zé)執(zhí)行打分questions.json 負(fù)責(zé)約束打分行為兩者分開設(shè)計(jì)的好處是換問題集不需要?jiǎng)哟a。{ ai_application: { question: 根據(jù)以下年報(bào)節(jié)選判斷該公司本年度是否在生產(chǎn)經(jīng)營中實(shí)際應(yīng)用了人工智能技術(shù), options: [0-未提及或無應(yīng)用, 1-僅提戰(zhàn)略規(guī)劃或風(fēng)險(xiǎn)討論, 2-有明確的研發(fā)或生產(chǎn)應(yīng)用], output_format: 只輸出數(shù)字編號(hào) }, ai_investment: { question: 年報(bào)節(jié)選中是否披露了與人工智能相關(guān)的研發(fā)投入金額, options: [0-未披露, 1-披露定性描述, 2-披露具體金額], output_format: 只輸出數(shù)字編號(hào) }, green_action: { question: 年報(bào)節(jié)選中是否披露了污染治理、碳減排相關(guān)的具體舉措或數(shù)據(jù), options: [0-未披露, 1-定性描述, 2-披露定量數(shù)據(jù)], output_format: 只輸出數(shù)字編號(hào) } }模板設(shè)計(jì)有三個(gè)原則。第一選項(xiàng)必須互斥且窮盡「僅提戰(zhàn)略規(guī)劃或風(fēng)險(xiǎn)討論」這一檔專門攔截那些虛晃一槍的公司避免把「提到了 AI」當(dāng)成「應(yīng)用了 AI」。第二output_format 強(qiáng)制只輸出數(shù)字編號(hào)不然模型寫一段「該公司在智能制造領(lǐng)域應(yīng)用了……」后處理要花大量時(shí)間做文本匹配。第三問題必須綁定節(jié)選讓模型做判斷題而不是問答題——給定證據(jù)范圍幻覺空間小得多。我在另一個(gè)項(xiàng)目里吃過沒約束格式的虧模型自由發(fā)揮的結(jié)果是解析腳本寫了一天最后還是要人工核對。3.3 gpt_score.py調(diào)用、解析與抽檢閉環(huán)評分腳本負(fù)責(zé)把檢索到的 top-k 塊拼進(jìn)提示詞、調(diào)用模型、解析輸出、寫回變量。targets_df.py 定義目標(biāo)變量的數(shù)據(jù)結(jié)構(gòu)跑完評分后生成公司-年份面板直接對接回歸。try.py 是調(diào)試接口用的臨時(shí)腳本正式跑批可以忽略它。# gpt_score.py 的打分與投票邏輯 import re import openai def score_one_chunk(chunk_text, question_cfg, client): prompt f{question_cfg[question]} 年報(bào)節(jié)選 {chunk_text[:1800]} {question_cfg[output_format]} resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: prompt}], temperature0, # 打分任務(wù)必須 0 溫度保證逐次結(jié)果一致 max_tokens5, # 壓到最短只允許輸出編號(hào) ) raw resp.choices[0].message.content.strip() m re.search(r[012], raw) return int(m.group()) if m else -1 def aggregate_company_votes(chunks, cfg, client): votes [score_one_chunk(c[text], cfg, client) for c in chunks] valid [v for v in votes if v 0] if not valid: return -1 return max(set(valid), keyvalid.count) # 眾數(shù)投票temperature0 是評分任務(wù)的基本紀(jì)律否則同一份年報(bào)兩次跑出來分?jǐn)?shù)不一樣后面回歸的測量誤差就無法解釋。max_tokens 壓到 5 是防止模型開始解釋它一解釋就會(huì)超長而且解釋文本里的數(shù)字可能干擾正則匹配。取眾數(shù)而不是平均是因?yàn)橥患夜静煌鹿?jié)里的表述經(jīng)常不一致——經(jīng)營討論里大談自研大模型會(huì)計(jì)附注里只字未提眾數(shù)投票比求平均更貼近多數(shù)章節(jié)的狀態(tài)。解析失敗的置為 -1 并單獨(dú)落盤后面統(tǒng)一人工處理不要悄悄跳過變成缺失值——缺失值傳導(dǎo)到回歸里樣本量縮水不說還可能有選擇偏誤。提示每個(gè)評分結(jié)果都要保存原始 chunk 和模型原始輸出。我在項(xiàng)目交付前會(huì)抽 30 條人工復(fù)核把打分和原文對一遍答辯和審稿時(shí)這就是最直接的證據(jù)。沒有原始記錄的自動(dòng)打分在審稿人眼里就是一個(gè)黑匣子。4. 統(tǒng)計(jì)建模GTFP 測算、基準(zhǔn)回歸與融資約束異質(zhì)性4.1 綠色全要素生產(chǎn)率SBM-GML 的計(jì)算口徑GTFP 的主流測算方法是 SBM-DEA 加 GMLGlobal Malmquist-Luenberger指數(shù)。投入端是勞動(dòng)、資本、能源產(chǎn)出端同時(shí)放進(jìn)期望產(chǎn)出營業(yè)收入和非期望產(chǎn)出污染排放或碳排放用全局參比技術(shù)集計(jì)算方向距離函數(shù)得到綠色全要素生產(chǎn)率增長率并累計(jì)成指數(shù)。附贈(zèng)資源.docx 里對這部分補(bǔ)充了背景資料和文獻(xiàn)清單算之前先把口徑定下來。# SBM-GML 方向距離函數(shù)的簡化示意 import numpy as np def sbm_directional_distance(X, Y_good, Y_bad, weights): # X: (n, k) 投入矩陣Y_good: (n, m) 期望產(chǎn)出Y_bad: (n, h) 非期望產(chǎn)出 # 完整求解需要調(diào)用線性規(guī)劃這里示意松弛變量的計(jì)算結(jié)構(gòu) n X.shape[0] slacks np.zeros(n) for i in range(n): # 每個(gè)決策單元求最大松弛量約束是投入和非期望產(chǎn)出可縮減、期望產(chǎn)出可擴(kuò)張 # 實(shí)際項(xiàng)目中建議直接用 pystoned 或 MaxDEA 求解不要自己寫單純形 pass return slacks這段代碼我故意留成骨架——DEA 的線性規(guī)劃求解不建議手寫直接用成熟的工具包驗(yàn)證結(jié)果。真正要盯的是口徑資本投入用固定資產(chǎn)凈值還是總資產(chǎn)能源消耗用披露電量還是行業(yè)均值估算污染產(chǎn)出選哪類污染物這些選擇會(huì)讓 GTFP 的絕對值差出數(shù)量級。GML 相對當(dāng)期 ML 指數(shù)的優(yōu)勢是跨期可比。用全局參比技術(shù)集不同年份的效率值才可以直接比較否則面板回歸里時(shí)間趨勢的估計(jì)會(huì)扭曲。項(xiàng)目里 lightgbm.ipynb 的存在說明作者還把機(jī)器學(xué)習(xí)模型拿來做了對照這類非參數(shù)方法對非線性關(guān)系的捕捉比線性回歸靈活可以作為穩(wěn)健性證據(jù)出現(xiàn)在附錄里。我的經(jīng)驗(yàn)是GTFP 的絕對值本來就有一定玄學(xué)成分但同一口徑下的排序和增速是可信的論文里要把「同比增速」作為主要解釋對象而不是糾結(jié)水平值。4.2 基準(zhǔn)回歸雙向固定效應(yīng)與聚類穩(wěn)健標(biāo)準(zhǔn)誤基準(zhǔn)模型是公司-年份雙向固定效應(yīng)面板回歸被解釋變量是 GTFP核心解釋變量是大模型打分得到的 AI 應(yīng)用程度控制變量取企業(yè)規(guī)模、杠桿率、成長性、企業(yè)年齡等常規(guī)集合。項(xiàng)目里的 TJJM24-AILearning-EconometricsWithLLM-main 文件夾可以理解為主工作區(qū)基準(zhǔn)回歸代碼就在這里面。# 基準(zhǔn)回歸個(gè)體時(shí)間雙向固定效應(yīng) import statsmodels.api as sm import pandas as pd def baseline_regression(df): df df.set_index([stock_code, year]) X df[[ai_score, size, leverage, growth, firm_age]].copy() X pd.concat([ X, pd.get_dummies(df.index.get_level_values(stock_code), prefixfid), pd.get_dummies(df.index.get_level_values(year), prefixyr), ], axis1) X sm.add_constant(X) model sm.OLS(df[gtfp], X).fit( cov_typecluster, cov_kwds{groups: df.index.get_level_values(stock_code)} ) return model.params[ai_score], model.tvalues[ai_score]聚類穩(wěn)健標(biāo)準(zhǔn)誤是必須的——同一公司不同年份的觀測存在序列相關(guān)不聚類 t 值會(huì)被系統(tǒng)性高估顯著結(jié)果可能只是假象。我一般按公司聚類如果樣本覆蓋期內(nèi)有行業(yè)政策沖擊再按行業(yè)聚類跑一遍做對照。固定效應(yīng)吸收的是公司和年份層面的不隨時(shí)間變化的異質(zhì)性。AI 打分變量是 0/1/2 離散值解釋上要表述為「AI 應(yīng)用程度的檔位提升對 GTFP 的影響」不要寫成彈性或百分比。跑回歸之前先看描述性統(tǒng)計(jì)ai_score 的分布如果嚴(yán)重偏向 0說明問題模板的判定標(biāo)準(zhǔn)太嚴(yán)要回過去調(diào)整選項(xiàng)——這種情況我遇到不止一次。4.3 融資約束異質(zhì)性交互項(xiàng)與分組回歸的配合融資約束的異質(zhì)性是標(biāo)題里明確標(biāo)注的研究點(diǎn)。兩種標(biāo)準(zhǔn)做法一是按 SA 指數(shù)中位數(shù)分組高約束組和低約束組分別跑回歸對比系數(shù)二是全樣本加交互項(xiàng) ai_score × SA。分組回歸回答「不同融資約束水平下 AI 的邊際效應(yīng)是否不同」交互項(xiàng)回答「融資約束連續(xù)變化時(shí) AI 效應(yīng)怎么變」兩者口徑不同可以互為補(bǔ)充。# 融資約束分組與交互項(xiàng) def financing_constraint_analysis(df): df[high_fc] (df[sa_index] df[sa_index].median()).astype(int) # SA 指數(shù)為負(fù)數(shù)數(shù)值越小代表約束越大注意方向 df[ai_x_fc] df[ai_score] * df[high_fc] beta_high baseline_regression(df[df[high_fc] 1]) beta_low baseline_regression(df[df[high_fc] 0]) # 交互項(xiàng)全樣本回歸看 ai_x_fc 的系數(shù)是否顯著 return {high_fc_beta: beta_high, low_fc_beta: beta_low}SA 指數(shù)取的是總資產(chǎn)對數(shù)和企業(yè)年齡的函數(shù)年報(bào)數(shù)據(jù)直接能算不用額外采集。方向別搞反——SA 指數(shù)是負(fù)數(shù)數(shù)值越小融資約束越大分組時(shí)要按小于中位數(shù)劃高約束組。我在第一次跑這個(gè)項(xiàng)目時(shí)就把方向?qū)懛催^高組低組結(jié)論完全顛倒排查了很久才發(fā)現(xiàn)是符號(hào)問題。交互項(xiàng)的解讀要謹(jǐn)慎如果 ai_x_fc 系數(shù)顯著為負(fù)說明高融資約束削弱了 AI 對 GTFP 的正向作用邏輯上說得通——AI 應(yīng)用需要前期投入融資約束緊的公司投不起。分組回歸的結(jié)果應(yīng)該和交互項(xiàng)方向一致如果不一致先檢查高組和低組的樣本量是否均衡再看是不是有極端值在作怪。5. 避坑指南RAG 加實(shí)證最容易翻車的五個(gè)現(xiàn)場5.1 現(xiàn)象檢索召回的是會(huì)計(jì)附注不是管理層討論向量檢索時(shí)明明問的是人工智能應(yīng)用返回的 top-1 文本塊卻是財(cái)務(wù)報(bào)表附注里「開發(fā)支出」科目的會(huì)計(jì)解釋。原因是固定長度切分把「管理層討論與分析」和后面的附注塞進(jìn)了同一個(gè) chunk語義被稀釋查「人工智能」時(shí)附注里的關(guān)鍵詞也能匹配上。解決按章節(jié)邊界切分第 2.3 節(jié)的做法把章節(jié)標(biāo)題拼在 chunk 開頭檢索時(shí)給「管理層討論與分析」相關(guān)章節(jié)更高的相似度權(quán)重附注降權(quán)。我一般會(huì)在向量檢索后加一道規(guī)則過濾——如果命中的 chunk 標(biāo)題屬于「財(cái)務(wù)報(bào)表附注」直接跳過取下一個(gè)非附注結(jié)果。管理層討論與分析才是描述戰(zhàn)略和實(shí)際業(yè)務(wù)的核心章節(jié)附注里的會(huì)計(jì)術(shù)語對變量打分幾乎沒有貢獻(xiàn)。5.2 現(xiàn)象大模型輸出帶解釋正則匹配到錯(cuò)誤的數(shù)字提示詞明明寫了「只輸出數(shù)字編號(hào)」模型偶爾輸出「2該公司有實(shí)質(zhì)應(yīng)用」。如果 max_tokens 設(shè)得太大模型有空間補(bǔ)解釋后處理時(shí) re.search(r[012]) 匹配到的第一個(gè)數(shù)字可能不是答案。解決把 max_tokens 壓到 5提示詞里加「不要輸出任何解釋」后處理先按「」切分再取末位。仍失敗的置為 -1單獨(dú)人工處理。還有一類情況是 API 版本更新后模型行為變化同一段提示詞在舊版輸出「2」新版輸出「2。」多了一個(gè)句號(hào)正則沒問題但字符串比對會(huì)翻車。所以解析邏輯里不要做精確字符串匹配一律走正則取編號(hào)。5.3 現(xiàn)象年報(bào)里的「人工智能」根本不是技術(shù)應(yīng)用年報(bào)常見表述是「人工智能技術(shù)的發(fā)展可能對公司業(yè)務(wù)產(chǎn)生不利影響」或「公司不涉及人工智能相關(guān)業(yè)務(wù)」這種句子按關(guān)鍵詞詞頻法會(huì)被計(jì)成 AI 應(yīng)用證據(jù)系統(tǒng)性高估應(yīng)用率。我見過有人用詞頻跑出來全市值前十大公司全部「應(yīng)用了 AI」人工一翻全是風(fēng)險(xiǎn)提示。解決問題模板里把判斷標(biāo)準(zhǔn)從「是否提及」改成「是否在生產(chǎn)經(jīng)營中實(shí)際應(yīng)用」并增設(shè)「僅提戰(zhàn)略規(guī)劃或風(fēng)險(xiǎn)討論」一檔。這樣模型就不會(huì)把風(fēng)險(xiǎn)提示當(dāng)成應(yīng)用證據(jù)。人工抽檢時(shí)重點(diǎn)看負(fù)例樣本確認(rèn)模型沒有把所有「提及」都打成「應(yīng)用」。5.4 現(xiàn)象GTFP 量級和公開文獻(xiàn)對不上差出十倍SBM-GML 的測算結(jié)果高度依賴投入產(chǎn)出口徑。資本用固定資產(chǎn)凈值還是總資產(chǎn)、能源用披露值還是行業(yè)均值、污染產(chǎn)出選哪些指標(biāo)都會(huì)讓結(jié)果發(fā)生數(shù)量級變化。這不算程序 bug而是口徑選擇問題。解決確定口徑后不要中途更換論文里完整披露指標(biāo)定義。用公開數(shù)據(jù)集做排序校驗(yàn)——絕對量不可比但各家公司的效率排序應(yīng)大致一致。我一般會(huì)拿同一個(gè)行業(yè)里 5 家知名公司的人工排序和模型排序做 Spearman 相關(guān)系數(shù)低于 0.6 就說明測算有問題需要回頭檢查數(shù)據(jù)。5.5 現(xiàn)象面板錯(cuò)位公司代碼和年份對不上年報(bào)文件名是「代碼_年份.pdf」但公司可能變更證券代碼年報(bào)披露日和報(bào)告期末也可能跨年——2023 年年報(bào)在 2024 年 4 月披露如果用公告日期定義年份面板時(shí)間維度會(huì)整體偏移。解決從 PDF 首頁解析報(bào)告期末日期用報(bào)告期年份作為面板的 year 字段。數(shù)據(jù)預(yù)處理最后加斷言每家公司年份序列連續(xù)無重復(fù)。# 面板完整性自檢發(fā)現(xiàn)錯(cuò)位的第一道防線 def validate_panel(df): for code, grp in df.groupby(stock_code): years sorted(grp[year].unique()) assert years list(range(min(years), max(years) 1)), \ f{code} 年份不連續(xù): {years} assert len(grp) len(years), f{code} 存在重復(fù)年份 print(面板校驗(yàn)通過所有公司年份連續(xù)無重復(fù))這個(gè)斷言放在數(shù)據(jù)預(yù)處理最后一步比放在回歸之前更合適——回歸報(bào)錯(cuò)時(shí)你還要回頭查是哪家公司出了問題前置斷言直接告訴你。跑批時(shí)把 year 字段統(tǒng)一成 int 類型別讓字符串和整數(shù)混著比大小這種隱晦的錯(cuò)位最耗時(shí)間。6. 穩(wěn)健性檢驗(yàn)與復(fù)現(xiàn)驗(yàn)證換口徑、換樣本、換估計(jì)量6.1 核心解釋變量的替換與對照最直接的穩(wěn)健性檢驗(yàn)是把 AI 應(yīng)用打分替換成基于同一套檢索文本的關(guān)鍵詞加權(quán)頻次。對每家公司年報(bào)的「管理層討論與分析」部分統(tǒng)計(jì)「人工智能」「機(jī)器學(xué)習(xí)」「深度學(xué)習(xí)」「大模型」的標(biāo)準(zhǔn)化出現(xiàn)頻次重新跑基準(zhǔn)回歸。如果核心系數(shù)符號(hào)和顯著性不變說明結(jié)論不是某一特定打分口徑的偶然產(chǎn)物。6.2 工具變量對內(nèi)生性做一次正面回應(yīng)人工智能應(yīng)用程度和 GTFP 之間存在反向因果的可能——生產(chǎn)率高的企業(yè)更有余力部署 AI。常見做法是用行業(yè)平均 AI 應(yīng)用程度作為工具變量或者用滯后一期的 AI 打分。# 兩階段最小二乘行業(yè)均值作為工具變量 from linearmodels.iv import IV2SLS def run_iv(df): df df.copy() df[const] 1.0 df[iv_industry_ai] df.groupby([industry, year])[ai_score].transform(mean) model IV2SLS( dependentdf[gtfp], exogdf[[const, size, leverage, growth, firm_age]], endogdf[[ai_score]], instrumentsdf[[iv_industry_ai]], ).fit() return model行業(yè)均值這個(gè)工具變量的邏輯是同行的 AI 應(yīng)用會(huì)通過競爭和示范效應(yīng)影響本公司但本公司的 GTFP 不會(huì)直接決定行業(yè)均值。跑完看第一階段 F 值大于 10 是底線小于 10 說明工具變量弱結(jié)果不可信。6.3 交付前的復(fù)現(xiàn)檢查清單項(xiàng)目收尾時(shí)我會(huì)把這張清單完整過一遍檢查項(xiàng)具體做法不過關(guān)的處理檢索命中質(zhì)量抽 50 個(gè) chunk 人工判斷召回文本是否貼合問題調(diào)整切分策略重新向量化打分可復(fù)現(xiàn)性同一份年報(bào)間隔一天跑兩次打分對比一致性確認(rèn) temperature0檢查 API 版本變化GTFP 排序校驗(yàn)與公開文獻(xiàn)或人工排序做 Spearman 相關(guān)檢查投入產(chǎn)出口徑面板完整性運(yùn)行 validate_panel 斷言回補(bǔ)缺失年報(bào)回歸敏感性換聚類層級、換控制變量集合對比核心系數(shù)在論文中如實(shí)報(bào)告敏感度這套清單的出發(fā)點(diǎn)不是「證明結(jié)果一定對」而是「換個(gè)合理設(shè)定結(jié)果不會(huì)反轉(zhuǎn)」。從那以后我每次跑類似項(xiàng)目都強(qiáng)制走一遍這個(gè)流程——先跑清單再寫結(jié)論寫完結(jié)論再抽檢原始文本。RAG 這條鏈路的黑匣子在于中間每個(gè)環(huán)節(jié)都有信息損耗你控制得越細(xì)審稿人問起來越不慌。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取