我用了五個月,做了一個會自己活的明末世界,最後還是放棄了

作者:电子孔圣 發布時間:2026年10月25日 13:01

昨天看到一個做同型別遊戲的同行3001釋出了一篇文章,在談論ai推演遊戲的問題。我的專案上個月已經放棄了,遍讓ai從各個地方整理,梳理和程式碼分析完成了這一篇文章:

當然裡面有很多我親身經歷但是AI只能透過版本記錄整理,整理不到位我的心境和麵對的細節困難

這篇文章寫給完全不瞭解這個專案的人。

從五月到九月,我一直在做一個明末 AI 推演遊戲。玩家扮演皇帝,每天面對財政、災荒、邊患、官員爭鬥和地方訊息。遊戲裡的大臣不是幾個寫死的按鈕,也不是隻會按照劇情說臺詞的 NPC。他們各自有記憶、職位、關係、利益和對世界的片面認識,會根據自己知道的東西做決定。

舉個例子:玩家下旨,讓陝西地方籌糧賑災。這個命令不會立刻把資料庫裡的“糧食”減掉、把“災民”數字改小。命令要先從京師發出去,經過不同官員,再到地方;有人可能延遲,有人可能隱瞞,有人可能把它理解成另一件事。地方收到命令後,還要檢查當地有沒有糧、運輸是否通、官員有沒有許可權、其他軍政事務會不會搶走資源。最後,遊戲才結算這道旨意有沒有真正產生效果。

我想做的就是這種感覺:皇帝能發號施令,卻永遠不能直接看見世界;他只能透過奏報、密信、使者和傳聞認識天下。玩家面對的不是一張完全透明的地圖,而是一套會遲到、會失真、會被人利用的資訊。

為了方便講述,我把這五個月重新整理成 `v1` 到 `v9`。這些不是當時正式釋出的版本號,而是我現在回頭給不同階段加的標籤。當時實際存在過多個不同的工程和分支,名稱也一直在變化。讀者不需要記這些名字,重要的是我為什麼一次次重做。

先把“引擎”講明白

很多人看到我說“戰爭引擎”“天氣引擎”“財政引擎”,可能會以為我在說不同的遊戲畫面模組。其實不是。

這裡的引擎更像遊戲後臺的世界管理員。它負責每天推進日期,檢查糧食和白銀從哪裡來、到哪裡去,計算路程,處理天氣、生產、消耗、人口、戰爭和死亡,再把每一次變化記到賬本里。它不負責畫面,也不負責替人物寫臺詞。

AI 人物負責“我想做什麼”。例如一個將領可以想調兵,一個官員可以想隱瞞災情,一個大臣可以想請求加稅。中間的執行層負責把這些人話翻譯成有限的動作,世界引擎最後檢查這些動作能不能發生。

所以 AI 不能直接說“我已經籌到十萬石糧”,引擎會繼續檢查糧食從哪裡來;AI 也不能直接說“軍隊已經到了遼東”,引擎要按距離、行軍速度和輜重推進。這個限制是我後來反覆重寫的核心。

v1:我先做的是“皇帝怎麼看不清天下”

《我用了五個月,做了一個會自己活的明末世界,最後還是放棄了》配圖 1

五月二十七日到二十九日,我做了最早的原型。

那時的目標還很樸素:做一個明末朝政模擬器。玩家是皇帝,開局面對財政虧空、邊軍欠餉、陝西災荒和朝廷黨爭。大臣要有記憶、目標、秘密和情緒,玩家可以單獨召見,也可以召集多人議政。

最早我只想做“會回答問題的大臣”,很快發現那樣不夠。一個角色如果直接知道所有事情,玩家也能直接得到所有真相,遊戲就只剩下問答。於是我開始做資訊通道:明發、中旨、密旨、遣使,奏疏硃批,密疏和洩露,訊息路程和經手人。

這時我第一次確定了專案的核心:

> 下旨不等於執行,看到奏報也不等於知道真相。

這一版解決了“人物像不像活人”的問題,卻沒有解決“這麼多人物和訊息怎樣長期執行”的問題。程式碼裡很多東西還是為了演示效果臨時寫的,世界也沒有穩定的內部規則。

 v2:我開始把“人物”拆成世界裡的東西

《我用了五個月,做了一個會自己活的明末世界,最後還是放棄了》配圖 2

《我用了五個月,做了一個會自己活的明末世界,最後還是放棄了》配圖 3

接下來,我不滿足於寫幾個固定大臣。我想讓新的官員、機構、地區、軍隊和關係能夠加入世界,於是開始把世界拆成幾類基礎東西:實體、關係、訊息、流程和規則。

實體就是人、官府、城市、軍隊、派系;關係就是上下級、同僚、親屬、盟友、債務和敵對;訊息是某個人在某個時間收到的內容;流程則負責任命、出兵、運輸、上報和執行。

這樣做是為了擺脫“每增加一個人物,就給他單獨寫一套程式碼”。但它馬上帶來更難的問題:誰能發出一道命令?誰有資格看到密報?一條訊息被轉述三次以後,原文和傳聞怎麼同時儲存?AI 說錯了話時,這是角色故意撒謊、角色記錯了,還是程式把資料喂錯了?

我第一次感受到,設計文件可以很漂亮,真正的遊戲卻還沒有因此變得好玩。所謂“通用系統”,其實是把許多原來藏在程式碼裡的問題全部攤到桌面上。

v3:第一次做成相對完整的工程

這一階段我做了第一套相對完整的工程:有網頁、有後端、有資料庫,也有一批專門驗證世界規則的測試。早期版本大約有 67 個實體、88 條關係,包括 25 個歷史人物、25 個地區、6 支軍隊、5 個派系和 3 個危機。

這聽起來不算大,但如果每個人都可能根據自己的記憶和利益行動,規模已經開始影響執行方式。

最初為了讓流程跑通,我使用過假的人物回覆、假的事件和假的決策,也就是所謂的離線樁。它們適合測試按鈕和流程,卻不能證明 AI 世界真的能執行。

後來我決定把重要的離線樁刪掉。我的想法是:既然遊戲的核心就是獨立 AI 人物,那就不能用預設答案假裝它們活著。於是從這一版開始,測試逐漸換成真實模型呼叫。結果是,專案終於暴露出真實的問題。

v4:真實模型一進來,問題從程式碼變成了成本

純 AI 驅動以後,我做了十二天驗證,實際發生過 14 次真實喚醒呼叫,基礎驗收是 5/5 透過。但最典型的問題不是人物寫得不好,而是模型預算設計錯了。

當時 DeepSeek 的推理模型在 `maxTokens=26000` 時出現過空正文。我一開始以為模型失靈,後來發現模型的思考過程已經把 26000個 Token 全部用完,留給最終回答的空間是零。這個問題只能透過重新分配思考預算、輸出預算和上下文長度解決。

我也第一次看清楚 AI 遊戲的成本從哪裡來:每個角色都有自己的記憶和輸入;一條訊息經過多個官員時,每一站都可能再次呼叫模型;人物不是隻回答玩家一次,而是會在事件、上報、衝突和傳聞中被反覆喚醒;格式錯誤、重試、上下文壓縮也會產生消耗。

小規模測試時,我測到 DeepSeek 一天大約六元,感覺完全可以接受。後來才明白,六元只是幾十個角色、很短時間和很少事件下的數字。世界擴大以後,成本和等待時間會一起放大。

v5:為了省消耗合併勢力,最後又把它拆回去

我開始擴充套件世界,不再只模擬皇帝和幾個大臣。後金、八旗、明軍、邊疆和外部勢力都要加入,關係網從大約 96 個實體、126 條邊,擴到 107 個實體、144 條邊。

這時我做過一次很現實的最佳化:為了減少模型呼叫,把後金暫時合併成一個整體 AI。這樣活躍 agent 從 51 個降到 46 個,成本和排程壓力都會下降。

可是實測以後,我又把這個決定推翻了。因為皇太極、代善、阿敏如果只是同一個“後金大腦”的不同輸出,他們之間的利益衝突、權力關係和內部博弈就消失了。模型呼叫是少了,但世界也被我壓扁了。

這件事讓我第一次意識到,Token 最佳化不是簡單地把數字壓低。每一個被合併的人物,可能正是遊戲裡最有價值的矛盾來源。

v6:我重寫了世界規則,因為舊程式碼“能跑但底層不對”

六月,我進入了大重寫階段。舊版本能夠執行一些流程,但我越來越覺得底層概念不對:人物說了話,世界就直接接受;AI 輸出了一個數字,賬本就跟著變化;有些資訊雖然按設計應該保密,卻在上下文裡被間接洩露。

所以我重新劃分了三層職責:

1. 人物思考:AI 只根據角色當前知道的內容,提出意圖,比如“我想拖延”“我想隱瞞”“我想調糧”或者“我想請求援軍”。

2.*意圖翻譯:一箇中間層把自然語言意圖翻譯成有限的動作,比如發訊息、申請調動、轉移資源、執行任命。

3. 世界結算:確定性引擎檢查許可權、時間、地點、資源和因果關係,然後把真實結果寫進賬本和歷史。

這樣做的目的,是防止 AI 直接改寫現實。大臣可以說自己已經籌到十萬石糧,但引擎必須檢查糧食從哪裡來、由誰擁有、能不能運到目的地。AI 可以說軍隊要出發,但不能跳過路程和補給。

這套結構讓世界更可信,卻也讓每一個錯誤都變得更嚴重。一個 bug 不再只是某個函式寫錯了,我會開始懷疑是不是認知、資訊、許可權和守恆的原則沒有建立好。於是修一個問題,經常變成重寫一整層。

一次二十日真 AI 空跑裡,我記錄到 180 次 AI 呼叫,輸入二十多萬 Token,輸出十六萬Token,合計 三十八萬左右 Token,耗時 1,009 秒。另一輪十日跑測有 213 次呼叫,合計 一百八四多萬Token,耗時約 800 秒。

這些還只是測試,不是普通玩家完整玩一局的賬單。

v7:戰爭、天氣和賬本,終於不再只是“幾個數字”

到了這一階段,我花了大量時間打磨物理層。它的目標不是讓遊戲看起來複雜,而是讓每個結果都有來源。

天氣引擎:旱災不是一個開關

天氣引擎不直接寫“陝西旱災等級 8”。它每天記錄降雨、連續無雨天數、土壤墒情和近段時間的降雨量。墒情像一個會漏水的桶:下雨時增加,蒸發和作物生長時減少,連續不下雨就逐漸變低。

這樣,旱災不是突然從“正常”跳成“災難”,而是先影響收成預期,再影響糧食庫存,最後可能變成饑荒。每一局的雨水還會有隨機浮動,所以歷史背景提供的是“偏旱傾向”,不是寫死“某府某年必旱”。

天氣引擎只負責產生物理量,不負責替人物說“今年民不聊生”。地方官、百姓和皇帝看到的災情,要透過各自的資訊渠道形成不同認知。

 生產與消耗引擎:糧食不能憑空出現

生產引擎根據耕地、墒情和年景計算產糧;消耗引擎每天扣除人口和駐軍需要的糧食,也可以處理軍餉、勞役等資源消耗。

這裡我堅持了一條很麻煩的規則:資源不能直接把數值改成另一個數。秋收的糧食要記錄為“從天地源賬戶進入某地糧倉”,人口消耗的糧食要進入“湮滅”賬戶。這樣每一筆糧都能追溯,賬面總量不會因為一段 AI 輸出憑空增加。

 財政引擎:錢和糧不是同一條線

財政引擎沒有把銀子簡單做成糧食的附屬數字。商業、海貿和手工業可以讓民間財富增長,戰亂和商路中斷也可以讓財富真實減少;稅收是從民間財富池轉入國庫,軍餉和行政支出再從國庫轉出去。

稅收也不是引擎替明朝算出一個固定答案。它分成三層:底層是當地到底有多少田、人和商業活動;中層是皇帝或地方官設定的稅率、稅目和徵收方式;最上層是官員實際徵了多少、截留多少、瞞報多少。最後的實收,是政策和人共同跑出來的結果。

短缺與價格引擎:把旱災推到兵變

短缺引擎會觀察糧食庫存跌到警戒線和危險線的時間點,產生相應事件並喚醒相關人物。價格引擎根據需求和庫存計算當地糧價,庫存越接近底線,價格越高。

這樣可以讓一條歷史鏈條自己長出來:旱災導致收成下降,庫存減少,糧價上漲,軍餉雖然還是銀子,但軍隊買不到糧,欠餉和飢餓繼續壓低士氣,最後可能出現譁變或流民。這個過程不是我寫一句“發生兵變”,而是多個引擎連續作用的結果。

戰爭引擎:引擎不替歷史宣佈誰贏

戰爭也不是 AI 寫一段“明軍大勝”就結束。戰爭結算會讀取雙方兵力、糧餉、士氣、地利、行軍狀態和補給,再加入隨機因素,計算傷亡和戰場態勢。

傷亡不是把軍隊人數直接改小,而是把損失記成從軍隊流向“湮滅”的一筆轉移;佔領、撤退、失守和繼續推進則透過位置、關係和狀態變化留下記錄。引擎提供的是一次交戰後雙方損失多少、誰佔據了哪裡,最終的勝負從實力、補給、士氣和運氣裡出現。

我不希望引擎帶著價值判斷說“這一方應該贏”。強的一方通常更有優勢,但也可能因為斷糧、地形或運氣翻車。這和人物的死亡機制是同一個思路:引擎給傾向,不替世界做道德判決。

敵情引擎:將領知道的不是地圖真相

戰爭還有一個經常被忽略的部分:敵情。

玩家看到的地圖,不能等於明軍將領看到的地圖。敵方軍隊的位置、數量、糧道、調動方向,可能來自斥候、邊報、俘虜、密探和傳聞。不同來源的可靠程度不同,送到京師還要經過時間和官僚鏈。

所以敵情相關的系統要做三件事:記錄敵我關係和距離,處理偵察與訊息的送達,再把不同來源的內容以“已知、推測、傳聞、互相矛盾”的狀態提供給人物。將領可以根據錯誤情報出兵,皇帝也可能在幾天後才知道戰場已經變了。

這類系統最難的地方,是既要讓敵情不透明,又不能讓玩家覺得一切失敗都是隨機胡來。它必須儲存訊息來源、到達時間和經手鍊,讓玩家在事後能夠追查自己當時為什麼會判斷錯。

心境、聲望、謠言與洩密引擎:讓人物會被處境改變

心境引擎每天只對恐懼、忠誠、操守、士氣等屬性做很小的變化。一個官員不會因為一件小事立刻從忠臣變成叛徒,但長期欠餉、高壓、派系凝聚力下降、同僚被殺,都可能一點點改變他的行為傾向。

洩密和謠言引擎也不是給世界隨機發八卦。一個秘密被多少人經手、儲存了多久,會影響洩露機率;洩露物件優先考慮政敵、上級和監視者。洩出去的內容先以模糊、未證實的風聞傳播,收到訊息的人再決定相信、利用還是壓下去。

彙總和地理引擎:解決“縣加起來不等於府”的問題

地理引擎把距離換成驛程。公文、密探、流民和軍隊有不同速度,軍隊還要考慮輜重;訊息不是從 A 瞬移到 B,而是按真實距離進入在途狀態。

彙總引擎則負責把縣、府、省之間的數字重新算一遍。人口、糧食、田畝和兵額可以相加;墒情、民怨和士氣要按人口加權平均;糧價不能簡單相加,也不能把幾個縣的價格隨便平均。

我之所以專門做這個引擎,是因為實際跑起來後發現,縣級消耗了糧食,府級的糧食卻沒有同步變化,跑十天以後上級總量和下級總量對不上。這個問題看起來只是一個彙總 bug,實際上會破壞整個世界的可信度。

v8:我把“明末內容”和“世界底座”拆開了

重寫過程中,我又做了一個更大的決定:把引擎從明末內容裡抽出來。

以前的程式碼裡到處寫著明朝、皇帝、官員和糧食;我後來嘗試做一箇中立的世界底座。這個底座不認識“明朝”,只認識角色、能力、關係、位置、時間、資源和規則。

明末的地圖、人物、官職、制度和開局危機,屬於內容資料;引擎只負責讓這些內容按時間和規則執行。理論上,同一個底座以後可以裝進另一個朝代,或者一個完全虛構的世界。

這就是後來被稱為 `huoshijie-engine` 的工程。這個英文名只是那條引擎分支的工程名,不是《明末活世界》的統一英文譯名,也不是把原工程簡單換了一個資料夾。我這樣改,是因為舊程式碼已經和明末設定纏得太緊,繼續往上補功能會越來越難維護。

但這個決定也把專案目標從“做一個明末遊戲”推向了“做一個可以承載各種世界的通用引擎”。每次抽象都解決了一些長期問題,也讓真正的遊戲離我更遠了一點。

v9:資料更真實了,我卻主動放棄了一部分地圖顆粒度

到了九月,我把更多考證資料和地理資料接進來。這裡發生了一個很關鍵、也很容易被忽略的取捨。

一開始,我傾向於把縣作為主要地理實體。這樣做的好處是細:每個縣都可以有自己的糧食、人口、官員、災情和運輸路線。壞處也很明顯:縣的數量迅速增加,資料維護、關係數量、每日結算和 AI 上下文都會一起膨脹。

後來我把地圖從“縣級為主”改成“府級為主,每個府保留若干重要縣”。普通縣不再全部作為一個獨立、每天都要完整運轉的主體;它們更多被歸入府的統計和行政範圍,只有關隘、產糧地、交通節點、戰場或有特殊歷史意義的縣才保留更細的實體。

這不是因為我突然覺得縣不重要,而是因為我發現“每一個細節都實時模擬”會消耗掉真正的遊戲性。地圖越細,不代表玩家的決策越豐富。很多縣級資料最後只會變成資料庫裡的數字,既沒有獨立人物,也沒有能改變局勢的事件,反而拖慢世界執行。

我放棄的是一部分地圖顆粒度,保留的是能影響玩家判斷的關鍵地點。這個取捨本來應該更早做,但當時我總覺得少一個縣就意味著世界不完整。

這一階段的資料裡有 149 個府、164 名官員,180 名考證人物第一次真正進入資料庫。一次後期基線重建裡,資料庫約有 543 個實體、430 條關係、185 個陝西區劃、43 個官職、94 個賬戶、25 條義務和 4 本賬簿。

從純效能看,結果其實很好:三天首跑約 327 個效果/天,物理層約 145 毫秒/天;另一條實現線上,400 府、3000 個活人時,物理層平均每個遊戲日約 0.32 毫秒,1000 府約 0.83 毫秒,跑 6000 個遊戲日產生約 870 萬條流水,記憶體約 208MB。

這證明 CPU 不是主要瓶頸。真正慢的是人物思考、上下文和等待。一次 think 大約 7.4 秒,開局或重大事件日可能同時喚醒 40 到 50 人,玩家點選“下一天”後等一兩分鐘並不罕見。

我用 AI 做這個遊戲的消耗

我按自己的ccs記錄,把整個製作過程中使用 AI 產生的總消耗記成約 16.6B。這裡麵包含程式碼生成、設計討論、資料整理、反覆重寫、模型呼叫和除錯,不是某一輪遊戲測試的單獨數字。

《我用了五個月,做了一個會自己活的明末世界,最後還是放棄了》配圖 4

單獨拿出來看,專門跑遊戲測試的費用大約是 900 元左右。這兩個數字的口徑不同:16.6B 是整個製作過程的累計 AI 消耗,900 元是測試階段的實際費用。包含deepseek和中轉站的gpt。

這也是我後來沒有辦法迴避的問題。哪怕換成 DeepSeek,單次呼叫看起來很便宜,也不能消除幾百個角色各自儲存記憶、接收訊息、作出判斷,再乘上幾十天甚至幾百天世界時間後的總量。

成本還不只是錢。每一次呼叫都可能增加等待;每一次重試都可能打斷除錯;每一段越來越長的歷史都可能讓人物更難穩定地理解當前局勢。

我一直沒有真正解決的,是遊戲性

我花了大量時間證明世界能夠執行,卻沒有同樣認真地定義玩家為什麼要玩。

日誌裡確實出現過很有意思的事情:有人瞞報,有人甩鍋,有人透過密探繞過官僚鏈,有人重新解釋皇帝的命令。天氣、糧價、欠餉、敵情和人物心境也會互相影響。這些現象讓我覺得“活世界”已經出現了。

可是日誌精彩,不代表玩家體驗成立。玩家仍然會問:我每天究竟能做什麼?我是在解決財政、保住遼東,還是隻是看人物自己爭鬥?勝利條件是什麼?資訊失真是為了讓我做判斷,還是隻讓我感到困惑?一次失敗到底是我的決策錯了,還是模型隨機說錯了?

我曾經以為,只要人物、地圖、財政、軍隊、氣候、節氣、流民、官僚、外部勢力和死亡都足夠真實,遊戲性自然會長出來。後來才發現,真實世界不會自動給玩家一個明確目標。

我應該先做一個很小但完整的戰役:二十到五十個角色,一個核心危機,三十天流程,明確的勝負條件。然後讓玩家真正玩起來,再決定哪些細節值得擴大。我實際走的是相反的路:先把世界做大,再等待玩法從複雜度裡自己出現。

我為什麼最後放棄

我放棄的不是“AI 能不能做活世界”這個想法。技術上,很多事情已經被證明可行:物理層可以很快;賬本和流水可以支撐很長的時間;人物可以根據自己的認知、關係和利益行動;訊息可以遲到、失真和被截留;規則也可以不把某個人的背叛寫死。

真正讓我停下來的,是幾個問題疊在了一起。

第一,AI 總消耗會隨著世界規模和遊戲時間不斷放大。換便宜模型只能降低單價,不能消除幾百個角色和長期記憶的總量。

第二,AI 的等待時間會直接變成遊戲節奏。物理層只用零點幾毫秒,但一次人物思考可能要幾秒,重大事件日可能要等一兩分鐘。

第三,遊戲性始終沒有收束。我有很多規則、很多人物、很多引擎、很多漂亮的日誌,卻沒有一個足夠穩定的玩家閉環。

第四,專案範圍一直膨脹。從崇禎朝局模擬器,變成所有人物都是自主主體;從一個朝廷,變成整個明朝和多極世界;從一個遊戲,又變成通用引擎、Steam、創意工坊和 Mod。每一步都有道理,合在一起卻已經超過了一個人長期維護的能力。

最後是我自己的完美主義。我把每個 bug 都看成更深層的原則沒有建立,把每個硬編碼都看成對“活世界”的背叛。一個縣級資料不夠真實,我就想補更多縣;一個人物行為不自然,我就想重寫記憶和關係;一個戰爭公式不夠嚴謹,我就想重新打磨戰爭引擎;一個天氣結果不夠連續,我就想重做氣候和墒情。

我總覺得再補一點,世界就完整了。可“完整”沒有終點,玩家卻一直沒有真正開始玩。

所以我最後放棄的不是因為它完全做不出來,而是我不再認為繼續用同樣的 AI 消耗、時間和完美主義去做下去是值得的。

如果以後重來,我會先做一個小型封閉戰役,讓 AI 只在真正需要判斷的節點醒來,其餘部分由確定性規則推進。先讓它成為一個有目標、有節奏、有輸贏的遊戲,再讓它成為一個足夠大的世界。

後續我會把我做的九版完整程式碼,包含設計初稿開源。因為全部打磨底層程式碼和引擎,前端一直沒有開發,所以完整的程式碼可能只有簡陋的前端。但應該是有部分可玩性的。