設計與實現(xiàn))
城市遙感變化檢測這幾年幾乎是所有測繪院、規(guī)劃院和衛(wèi)星應用公司的剛需。但凡是做過實際項目的人都清楚傳統(tǒng)流程是把兩期影像丟進模型里出一張差分圖再人工看一遍寫報告過程又慢又費人。我這兩年一直在琢磨一件事能不能把“變化在哪”和“為什么變、影響多大、該不該管”這一整條鏈路交給一個智能體去組織把那些固定套路的事自動化讓人只做判斷和決策。于是就有了這個基于LLM智能體框架的城市遙感圖像變化分析系統(tǒng)。這篇文章把我從架構設計到落地實現(xiàn)的過程、踩過的坑、調過的參都整理出來給想做這個方向的同行一個參考。1. 先想清楚LLM智能體到底解決了遙感變化檢測的什么問題1.1 傳統(tǒng)變化檢測流程的五個真實痛點先說傳統(tǒng)流程。以前做一個城市變化監(jiān)測項目大概是這個節(jié)奏拿兩期影像做輻射歸一化、幾何配準然后跑一個變化檢測網絡輸出二值圖或者差異圖再人工目視解譯圈出變化斑塊核實屬性最后整理成報告。聽起來不復雜但實際做起來至少有五個痛點非常要命。第一語義碎片化。像素級的檢測結果往往是一片一片的噪聲同一個真實變化被切成好幾塊或者不同性質的微小變化混在一起。每次都要靠人去合并、去分類規(guī)則寫著寫著就成了一堆沒人敢動的補丁邏輯。第二后處理鏈條太長。柵格轉矢量、小斑塊過濾、形態(tài)學運算、邊界平滑、屬性賦值每一步都有參數(shù)參數(shù)之間還互相耦合。項目換了數(shù)據(jù)源整套參數(shù)就得重新調一遍。第三閾值選擇靠拍腦袋。變化檢測模型輸出的概率圖定多少算變化不同傳感器、不同季節(jié)、不同城市都不一樣。很多項目直接取0.5結果不是漏檢就是誤報。第四結果不可解釋。深度學習模型的輸出就是一張圖領導問“這個地方為什么算變化”你只能說“模型這么判斷的”這在業(yè)務上沒法落地的。第五報告寫作費人力。圖斑統(tǒng)計表、位置描述、面積匯總、合規(guī)性初判這些工作看起來簡單批量起來極其消耗人天。一個城市級項目全手工整理報告大概要占掉三分之一的人力。這些痛點其實指向同一個方向變化檢測這個事真正值錢的不是“找出變化”而是“理解變化”。而理解變化恰好是LLM智能體最擅長的事。1.2 LLM智能體切入的三個核心環(huán)節(jié)LLM智能體不是來替代變化檢測模型的它是來接管“組織、理解、輸出”這三個環(huán)節(jié)的。第一個環(huán)節(jié)叫工具編排。智能體可以根據(jù)用戶的一句話需求自己決定調哪個數(shù)據(jù)預處理工具、跑哪個檢測模型、做什么后處理而不是像傳統(tǒng)pipeline那樣寫死流程。比如用戶說“看看東片區(qū)近兩年有多少新增建筑”智能體就會規(guī)劃先按行政區(qū)劃裁剪影像調用建筑變化模型再做矢量化和面積統(tǒng)計最后結合規(guī)劃數(shù)據(jù)判斷合規(guī)性。第二個環(huán)節(jié)是語義解讀。變化檢測模型輸出的是柵格和矢量這些幾何數(shù)據(jù)本身沒有語義。智能體能結合地理知識、城規(guī)知識甚至網絡公開信息把“這里有個圖斑”翻譯成“該地塊存在疑似新增工業(yè)廠房面積約1.2公頃位于生態(tài)控制線內需進一步核實”。第三個環(huán)節(jié)是報告生成與交互。智能體可以把表格、圖例、文字描述、風險提示組織成一份結構化報告并且允許用戶追問比如“這個變化區(qū)域周邊的路網情況怎么樣”智能體可以繼續(xù)調工具查數(shù)據(jù)而不是重新跑一整套流程。1.3 適用邊界別指望它什么都干我必須先潑一盆冷水。LLM智能體不是萬能的有些場景它真不適合。適合的場景包括變化檢測結果的后處理與語義化、點對點的問答式查詢、多源數(shù)據(jù)的匯總研判、報告草稿生成、閾值的自適應解釋等等。不適合的場景也有高精度的像素級定位、實時視頻流變化檢測、對模型推理速度有硬性要求的在線系統(tǒng)。這些事還是乖乖用傳統(tǒng)CV和專用深度學習模型。我現(xiàn)在的產品定位就是“面向分析研判的半自動系統(tǒng)”模型負責干活智能體負責組織干活并解釋結果。這個定位切得越清楚落地越順。2. 系統(tǒng)總體架構與技術選型2.1 核心架構規(guī)劃—工具—記憶—反思四層我最終采用的智能體架構本質上是一個帶工具調用能力的多輪對話系統(tǒng)分為四層。第一層是規(guī)劃層。智能體接收用戶意圖后把任務拆解成若干子步驟。這里我初始用的就是ReAct模式的改寫版讓模型顯式地輸出“當前目標、下一步動作、調用參數(shù)”每一步都落在結構化的JSON里方便程序解析和校驗。第二層是工具層。典型工具包括影像檢索、柵格裁剪、變化檢測推理、矢量后處理、面積統(tǒng)計、地類標簽映射、圖斑報告生成。每個工具都是獨立服務通過統(tǒng)一的OpenAPI規(guī)范注冊給LLM。實測下來工具命名和描述寫得清不清楚直接決定智能體會不會調錯工具這塊后面細講。第三層是記憶層。短期記憶用對話上下文長期記憶我引入了兩塊一塊是業(yè)務知識庫存城市規(guī)劃控制線、地類分類標準、歷史項目報告模板另一塊是會話存檔同一個地塊的多次分析結果可以復用避免重復計算。第四層是反思層。每次工具調用的返回值會經過一個校驗器。比如矢量數(shù)據(jù)面積明顯不合理或者坐標系對不上就觸發(fā)重新規(guī)劃。這個反思機制在真實項目中救了我很多次。2.2 底層變化檢測引擎選型智能體調用的是變化檢測引擎引擎本身還是要選對的。我測試過好幾條路線。最簡單的是像素級差分法比如影像差值加閾值分割優(yōu)點是快、無需標注缺點是對輻射差異極其敏感兩期影像光照稍有不同整個城市都在“變化”。除非數(shù)據(jù)源高度一致否則不做首選。實用路線是孿生網絡。兩期影像經過共享權重的編碼器提取特征再對特征做差異增強最后解碼成變化概率圖。代表性的結構有SiamCRNN、SiamU-Net這類。我在項目里用的基線是SiamU-Net的改進版在自建的城市變化數(shù)據(jù)集上mIoU大概能到0.71左右基本滿足業(yè)務初篩要求。如果想追求精度更高可以換基于Transformer的ChangeFormer或者帶注意力融合的模型對小目標變化敏感度會高一些但是計算量也上來了。一個1024×1024的影像patch在單張消費級顯卡上可能要跑2-3秒全城推理要算好時間預算。我在真實項目里做了一個折中先用SiamU-Net出概率圖再用一個輕量的地物分類模型給變化區(qū)域打標簽比如區(qū)分建筑、道路、植被、水體。這樣一個模型管“變沒變”一個模型管“變成什么”配合起來效果好過單模型硬train。2.3 GIS工具層與數(shù)據(jù)格式中樞整個系統(tǒng)里最容易被低估的就是GIS工具層。很多做AI的人把精力放在模型上結果數(shù)據(jù)格式一塌糊涂最后分析結果根本導不進ArcGIS。我的建議是統(tǒng)一用GeoJSON作為矢量交換格式柵格統(tǒng)一用GeoTIFF投影信息在每一步都要保留。所有后端處理工具輸入輸出都走這兩個格式。坐標系我內部統(tǒng)一用EPSG:4326的經緯度前端展示再動態(tài)轉Web墨卡托。為什么不用投影坐標系因為影像原始數(shù)據(jù)五花八門有的地方是高斯投影有的是UTM統(tǒng)一轉WGS84經緯度最保險雖然面積計算要重新?lián)Q算但不會出大錯。工具層還用到了PostGIS做空間查詢。變化圖斑入庫后跟控制線、用地紅線、生態(tài)保護區(qū)做空間疊加分析這一步交給空間數(shù)據(jù)庫比讓模型自己算靠譜得多。2.4 LLM選型與工具調用方式LLM選型我前前后后測了三種路線。第一是純閉源大模型直接調API效果最好函數(shù)調用能力最穩(wěn)但是數(shù)據(jù)出域很多政務客戶不接受項目直接斃掉。第二是私有化部署開源模型數(shù)據(jù)不出域但函數(shù)調用能力弱稍微復雜一點的參數(shù)嵌套就漏參數(shù)比如讓它調一個裁剪工具五個參數(shù)能漏倆。第三是混合模式本地部署一個小模型做意圖識別和參數(shù)洗牌核心分析調用云端大模型。這個方案數(shù)據(jù)合規(guī)性可以解釋效果也過得去我目前部署默認用這個。不管哪種模式工具調用的核心機制都差不多把每個工具的入?yún)?、出參、用途寫成一個JSON Schema塞進上下文里讓模型輸出結構化的調用指令然后由程序去執(zhí)行執(zhí)行結果再回傳給模型。這塊比讓模型直接生成Python代碼要穩(wěn)得多因為代碼執(zhí)行風險高還要多一道沙箱收益卻不明顯。3. 關鍵實現(xiàn)細節(jié)與代碼級拆解3.1 數(shù)據(jù)準備裁剪、對齊、輻射歸一化三步走影像預處理這個階段80%的變化檢測效果都取決于它。很多模型精度不夠其實是數(shù)據(jù)沒對齊。第一步裁剪。原始影像動不動就是上萬像素寬直接丟進模型顯存直接爆掉。要按512×512或者1024×1024的patch切patch之間保留50像素的重疊防止變化目標恰好被切在邊界上。第二步對齊。兩期影像要用同名點做二次配準我用的是基于特征匹配的自動配準再檢查像元級對齊精度。如果配準誤差超過一個像元后續(xù)像素級對比就基本沒意義。第三步輻射歸一化。這個坑太典型了。兩期影像拍攝季節(jié)不同、傳感器不同灰度分布差異巨大。我用的是直方圖匹配加線性回歸校正把參考影像的輻射特征調整到與基準期一致。做完這步識別出的“變化”才是真實變化而不是光照變化。代碼上核心就是兩段。一段做配準我是用rasterio讀取影像、OpenCV提取特征、計算單應矩陣重采樣這個流程不多說。一段做輻射校正我直接貼個簡化版示意import numpy as np from skimage.exposure import match_histograms def radiation_normalize(base, target): # 將target影像的直方圖匹配到base影像 matched match_histograms(target, base, channel_axis-1) return matched.astype(np.uint16)實際項目里我還會加一個掩膜把云、云影和水體區(qū)域先摳出來不參與校正不然這些地物的直方圖會拖累全局統(tǒng)計。3.2 變化檢測推理與后處理從概率圖到干凈圖斑模型推理本身沒什么特別就是把兩期影像patch喂進去拿到變化概率圖。真正的功夫在后處理。我后處理的順序是概率圖閾值分割→連通域分析→小斑塊過濾→形態(tài)學平滑→柵格轉矢量→邊界簡化。閾值這塊我不用固定0.5而是用Otsu算法自適應確定或者根據(jù)類別頻率動態(tài)調整。一個我總結的小經驗城市區(qū)域建筑變化概率圖的分布往往呈雙峰Otsu的效果遠好于固定閾值而道路變化概率圖是單峰長尾需要人工指定一個較低的閾值比如0.35否則漏檢嚴重。后處理核心代碼示意import cv2 import numpy as np from shapely.geometry import shape from rasterio.features import shapes def postprocess(prob_map, threshold0.5, min_area_px200): binary (prob_map threshold).astype(np.uint8) # 形態(tài)學開運算去噪 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) binary cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations2) # 連通域過濾 num, labels, stats, _ cv2.connectedComponentsWithStats(binary) keep np.zeros_like(binary) for i in range(1, num): if stats[i, cv2.CC_STAT_AREA] min_area_px: keep[labels i] 1 return keep這一步之后我才把柵格轉成矢量。轉矢量用rasterio的shapes函數(shù)輸出GeoJSON然后做坐標轉換把像素坐標轉換為地理坐標。這里要注意輸出的多邊形邊界往往是鋸齒形的我會用Douglas-Peucker做一次簡化容差根據(jù)出圖比例尺設一般設3到5米。3.3 智能體工具注冊與分析鏈路實現(xiàn)工具注冊是整個智能體框架能不能跑通的關鍵。在每個工具的定義里描述要寫得像給實習生下指令一樣清楚包括它做什么、輸入輸出是什么、常見誤區(qū)是什么。比如裁剪工具的描述我這么寫{ name: crop_raster_by_geojson, description: 用GeoJSON范圍裁剪GeoTIFF影像返回裁剪后的GeoTIFF路徑。注意GeoJSON必須為EPSG:4326坐標影像路徑須來自影像檢索工具。, parameters: { type: object, properties: { input_path: {type: string, description: 輸入GeoTIFF路徑}, aoi: {type: object, description: GeoJSON Polygon對象}, output_path: {type: string, description: 輸出GeoTIFF路徑} }, required: [input_path, aoi, output_path] } }畫重點描述里一定要寫“從哪里拿這個參數(shù)”比如“路徑須來自影像檢索工具”這樣能顯著減少LLM亂傳路徑的問題。這是我從一個真實bug里悟出來的當時模型把用戶隨口說的一句地名當成了文件路徑分析直接跑飛加了這句約束后類似問題少了很多。智能體的主循環(huán)核心邏輯如下def agent_loop(user_query, available_tools, max_steps8): messages [{role: system, content: SYSTEM_PROMPT}] messages.append({role: user, content: user_query}) for step in range(max_steps): resp llm_chat(messages, toolsavailable_tools) if resp.tool_calls: for call in resp.tool_calls: result execute_tool(call.name, call.arguments) messages.append(tool_result_message(call.id, result)) else: return resp.content return 已達到最大步驟數(shù)中止。每一步工具結果回傳后模型會決定是繼續(xù)調用下一步工具還是直接生成最終回答。我在幾個月度項目里觀察平均一個完整的地塊變化分析任務需要4到7次工具調用。3.4 提示詞設計與多輪狀態(tài)管理提示詞設計這事容易被當成寫作文其實它是工程。我的系統(tǒng)提示詞固定分四塊角色設定、工具使用規(guī)則、分析流程規(guī)范、輸出格式規(guī)范。角色設定不要寫“你是遙感專家”這種空話要寫“你是城市遙感變化分析助手負責組織工具完成分析并解釋結果”。工具使用規(guī)則里有一條很關鍵一次只調用一個工具等結果返回后再決定下一步。如果讓它一次并行調多個工具參數(shù)混亂的概率會直線上升。輸出格式規(guī)范也要明確。我對變化分析報告規(guī)定了固定的markdown結構摘要、變化區(qū)域列表、圖斑明細表、風險研判、建議復核清單。模型如果自由發(fā)揮報告格式每次都不一樣下游沒法接。多輪狀態(tài)管理我踩過一個坑用戶意圖變化后上一輪的工具結果還堆在上下文里模型容易串。解決方案是每輪對話前做一次歷史壓縮把已經完成的工具調用結果摘要化只保留關鍵數(shù)值比如“建筑新增總面積3.2公頃已提取圖斑12個”而不是堆完整的GeoJSON。這樣既省token也防止串數(shù)據(jù)。4. 實操過程一個城市地塊的完整變化分析演示4.1 場景設定與數(shù)據(jù)情況拿一個我做過測試的模擬項目X來說。數(shù)據(jù)是某城市高新區(qū)一塊約4平方公里的區(qū)域兩期高分影像間隔兩年。一期是六月份拍的二期是同年份另一天的不同時段包含云量很少地物有建筑新建、裸土翻整、道路拓寬等變化。我要求智能體完成以下任務識別新增建筑區(qū)域、評估對周邊生態(tài)控制線的影響、生成變化分析簡報。用戶原始輸入就一句話“高新區(qū)西北片區(qū)兩年內新增了哪些建筑有沒有越線的給我出一份簡報?!?.2 智能體完整工作流演練整套工作流我一步步拆給你看。第一步意圖解析。LLM把用戶問題拆成子任務清單檢索兩期影像、裁剪AOI區(qū)域、執(zhí)行變化檢測、提取新增建筑類變化圖斑、與生態(tài)控制線疊加分析、生成簡報。這六步會被動態(tài)編排而不是全部執(zhí)行。第二步影像檢索。LLM調用imagery_search工具范圍設置為高新區(qū)西北片區(qū)、獲取時間為兩個年份。影像檢索返回兩個GeoTIFF路徑。這里注意LLM自己不會判斷影像是否含云工具層在返回時自動帶上云量檢查結果。第三步裁剪與預處理。LLM調用crop工具和normalize工具。normalize返回的輻射統(tǒng)計信息顯示兩期影像直方圖相關性0.92符合處理要求。第四步變化檢測推理。LLM調用change_detect工具模型輸出概率圖后后處理模塊自動生成圖斑矢量。工具返回的信息不是完整GeoJSON而是摘要圖斑數(shù)27個總面積3.8公頃置信度均值0.86。第五步地類分類。LLM發(fā)現(xiàn)用戶關心“建筑”所以接著調用land_cover_classify工具給27個圖斑打地類標簽。結果顯示新增建筑類圖斑13個面積2.4公頃。第六步疊加分析。LLM調用spatial_overlay工具把這些圖斑與生態(tài)控制線疊加。工具返回其中有2個圖斑面積0.3公頃與生態(tài)控制線范圍相交屬于疑似越線。第七步生成簡報。LLM整合所有結果輸出一份帶表格的markdown報告。這一步一步你可能會問是不是太慢了實測下來從提問到拿到完整簡報耗時約1分20秒。其中變化檢測推理占了大頭大約50秒LLM規(guī)劃加工具調用本身只占20多秒。和人工流程相比這個速度已經快了一個數(shù)量級。4.3 輸出內容形式與人工復核閉環(huán)智能體最終生成的簡報長這樣開頭一段摘要概述區(qū)域內新增建筑主要分布方向中間是變化圖斑明細表每一行包含圖斑編號、面積、地類、是否越線再往下是空間分析結論重點標出越線風險區(qū)域最后是一份建議人工復核清單列出需要現(xiàn)場拍照驗證的3個圖斑。這里我特別想強調人工復核閉環(huán)。智能體不是替代人做最終決策的它是把人的注意力引導到真正需要看的地方。我見過很多團隊把目標定成“全自動檢測、全自動報告”結果模型一出錯沒人發(fā)現(xiàn)最后領導問責。我的方案是所有智能體生成的結論都必須標注置信度低于閾值的圖斑自動進入“待現(xiàn)場核實”列表。這樣模型有錯也只會漏在明面上不會被無聲無息地掩蓋。5. 常見問題與排查技巧實錄5.1 LLM幻覺編造不存在的圖斑或面積這個問題在最開始幾乎天天碰到。模型在總結時會一本正經地寫出“新增建筑面積5.6公頃”之類的數(shù)字但你回頭對賬所有圖斑加起來只有2公頃。原因很簡單模型不是在計算它是在“生成一個像樣的數(shù)字”。我的解決辦法是強約束工具結果的傳遞。所有統(tǒng)計數(shù)字不從LLM對話里提取而是工具結果里帶一段machine-readable的JSON摘要顯示給用戶看的文本由模板生成。LLM只負責解釋和分析不負責編數(shù)字。另外我在反思層加了一個校驗器每次LLM生成最終回答前會檢查它引用的數(shù)字是否與工具計算結果一致不一致就打回重寫。5.2 坐標對不上圖斑飛到十萬八千里外這個坑我印象太深了。第一次聯(lián)調時模型生成的圖斑疊加在底圖上竟然漂到了幾萬公里外的海上。排查后發(fā)現(xiàn)是工具返回GeoJSON時坐標沒做轉換一個環(huán)節(jié)輸出的是投影坐標另一個環(huán)節(jié)按經緯度解析空間關系全部錯亂。從此我立了一個鐵律所有工具接口的GeoJSON一律EPSG:4326并且在工具描述里高亮強調。另外我在后處理服務的邊界上加了個合理性檢查輸出坐標范圍如果超出城市邊界范圍一定會彈告警。5.3 大影像處理顯存不夠和推理時間失控城市級影像動輒幾十GB直接整景推理根本不現(xiàn)實。我測試過幾種切patch的方式最優(yōu)方案是按固定瓦片網格切而不是滑動窗口切。瓦片網格的好處是結果可緩存下次分析同一區(qū)域就不用重算。每個瓦片1024×1024檢測模型推理一次2-3秒整個高新區(qū)大概400個瓦片并行8卡約15分鐘出完整底圖。這個速度對離線監(jiān)測足夠用了。實時性需求更強的場景可以降采樣到512×512或者干脆只分析用戶框選的重點區(qū)域不跑全城。5.4 漏檢和誤檢閾值與樣本的雙重博弈漏檢和誤檢是變化檢測永恒的話題。智能體排錯時我發(fā)現(xiàn)誤檢的圖斑往往集中在影像配準誤差大的邊緣地區(qū)尤其是建筑物立面傾斜產生的鬼影。解決辦法是分區(qū)設置閾值影像中心區(qū)域閾值0.45邊緣區(qū)域閾值調到0.6。漏檢問題更麻煩頻繁發(fā)生在小目標變化上比如一棟小房子拆除。這類變化像素占比可能只有幾十個像素后處理最小圖斑面積參數(shù)設大了直接過濾掉。我的調試思路是把最小圖斑面積參數(shù)做成可配置項由智能體根據(jù)任務類型自動選擇做建筑監(jiān)測時設300像素做細碎變化監(jiān)測時設80像素。樣本層面實在的辦法是持續(xù)標注。每季度用智能體篩選出的低置信度圖斑做一次人工標注回流微調。我見過很多項目忽略了這一步模型效果隨著城市變化越來越差還不自知。5.5 長上下文與工具調用沖突對話一長上下文里堆了幾輪工具結果后LLM開始出現(xiàn)“忘事”明明前面已經算過總面積后面又調一次工具重算或者干脆把歷史圖斑號張冠李戴。我做了三件事一是上文說過的歷史摘要壓縮二是限定工具調用步驟不超過8次超過就強制輸出階段性小結三是輪次結束時清理臨時文件避免臟數(shù)據(jù)殘留影響下一輪分析。這三板斧加上去長會話穩(wěn)定性改善非常明顯。至少在我現(xiàn)在的壓力測試里連續(xù)10輪對話不串線、不重復計算已經能做到。最后分享一點個人體會做這個系統(tǒng)最大的體會是LLM智能體在遙感里的價值不是讓模型變得更強而是讓整個分析流程變得像人一樣會思考和會溝通。以前做變化檢測用戶拿到的是一張圖和一堆參數(shù)現(xiàn)在用戶拿到的是能看懂、能追問、能直接輔助決策的結論。這套東西后續(xù)還能往兩個方向延伸一個是把多源數(shù)據(jù)接入比如把夜間燈光、POI、路網數(shù)據(jù)一起喂給智能體做綜合研判另一個是端到端微調一個小參數(shù)模型把工具調用和領域知識內化進去減少對外部大模型的依賴。如果你也在琢磨這個方向建議從一個小地塊、一個明確的監(jiān)測需求開始先把工具鏈跑通再去追求規(guī)模和智能感。