Insights 2.0 的工作流:從 brainstorm 到生產部署的六步
eros-engine v2.4.0 把用户畫像從「每輪改寫」換成「事實賬本 + 定時重建」。這篇講它怎麼做出來:六步、四個倉、幾個各管一倉的 agent 會話,每一步交給下一步的都是一份能讀的文檔。
eros-engine 是我們開源的 AI 伴侶 engine,Rust 寫的。10 月 27 日的 v2.4.0 帶來了 Insights 2.0:engine 從對話裏給真人用户攢畫像的方式整個換了一遍。
實現細節在倉裏的 docs/insights.md 和 設計 spec。這篇講的是它怎麼做出來的。從第一份 brainstorm 提交到上線後選定重建用的模型,一共約 42 小時,經過六步、四個倉。每個倉有自己的 Claude Code 會話,會話之間不共享上下文,交接全靠文檔和消息。
我們把這套做法整理成了一個公開倉 etherfunlab/workflow。這篇算是它的一個實例。
改了什麼
1.0 每輪對話跑兩段:第一段從這一輪抽事實;第二段把這批事實和整份舊畫像一起交給模型,讓它寫出整份新畫像,engine 再按非空值合併回去。
我們拿生產部署的數據量過這套流程,量出四個問題。
- 每輪重寫整份畫像。 六週裏第二段寫出的非空鍵,99.4% 是本來就有值的鍵被重新輸出一遍,抽樣看九成以上連值都沒變。每輪中位數是 1 條新事實對 8 個重寫的鍵。
- 畫像越大越容易截斷。 模型要把舊畫像整份寫回來,舊畫像一大,輸出就頂到 max_tokens。
- 三分之二的事實沒有字段可放。 抽出來的事實裏 65.6% 落在 schema 沒有對應字段的領域。
- 示例值被抄進畫像。 1.0 的 schema 用具體示例説明格式,有用户的職業、城市字段和示例逐字相同。
2.0 的做法:
| 1.0 | 2.0 | |
|---|---|---|
| 事實存哪 | 只在審計日誌裏 | human_facts 賬本,按規範化文本逐字去重,記出現次數、會話數、首末出現時間 |
| 畫像調用的輸入 | 本輪事實 + 整份舊畫像 | 整本賬本,按出現次數與最近出現排序,截到 16,000 字;不讀舊畫像 |
| 什麼時候調 | 每輪有事實就調 | 攢滿 20 條事實(新增或重複出現都算),或靜默 1 小時 |
| 模型寫什麼 | 整份畫像,engine 按非空值合併 | 整份畫像,整行替換 |
| schema | 帶具體示例 | 只給 <…> 佔位;刪去地理與擇偶性別、年齡,新增 kinks、ideology、mental_health、education_level / education_status |
六步
時間都是 UTC。
1. 先量問題(報告倉,10-26 01:24)
報告倉是私有倉,放 brainstorm、dev-log 和週報。brainstorm 先用一份生產快照把 1.0 的問題量了一遍,上面那四條大部分出自這裏,截斷的根因和那組抽樣是後面一期實驗補上的。然後定下「事實賬本 + 派生畫像」的架構,列出六項要交給審計倉去跑的實驗,每項寫明要回答什麼問題。
這一步不寫代碼。它交給後面的是一張帶數字的問題清單,和一張待驗證的假設清單。
2. 盤點現狀,跑審計(報告倉 + 審計倉,01:33 到 02:57)
審計倉也是私有倉,專跑生產數據分析和付費實驗,對生產庫只讀。這一步同時開了兩條線。
一條是盤點:逐條核對地理字段、國籍、示例照抄、擇偶字段的現值,結論寫回 brainstorm。
另一條是審計報告,把 brainstorm 裏的假設一條條換成讀數:
- 重建多久觸發一次。 在七週的歷史事實上回放各種觸發規則。「攢滿 20 條,或靜默 1 小時」這一檔,模擬下來調用次數比 1.0 少 86%,新事實進畫像的延遲 p90 約 1.1 小時。
- 賬本輸入給多大。 16,000 字的預算能讓 98% 的用户整本賬本讀進去,單次輸入最多約 1 萬 token。
- 事實要不要用 embedding 合併。 不要。兩個判官模型判過、創辦人再人工核對 30 對之後,餘弦 0.94 的合併約九分之一會丟掉細節,換來的好處又小。賬本只做逐字去重。
- 第一段的 persistence 標籤要不要保留。 brainstorm 本來打算讓 engine 按出現次數自己算。審計量出 97.7% 的賬本行只出現過一次,按次數分檔會全落進同一檔,所以保留模型給的標籤。
審計報告成稿前過了三道:fact-check、潤色、跨模型終審。
3. 一期選模型(報告倉 + 審計倉,03:56)
重建用哪個模型,分兩期選。2.0 的提示詞要等 engine 寫出來才有,所以一期先在 1.0 的第二段上做:抽 118 輪,用四個模型各重放一遍。結果淘汰一個,一個只留作上限參照,剩下兩個進二期,等 engine 寫好再在真提示詞上比。一期還把截斷的來源定到了輸入側:截斷全落在舊畫像特別大的那幾輪。
4. 實現(eros-engine,10-26 19:18 到 10-27 07:54)
eros-engine 是公開倉。engine 會話起步時讀的就是前面三篇文檔:brainstorm、審計報告、一期 dev-log。它先和創辦人過一遍設計,寫出 spec,再拆成 plan,然後實現。
有幾項決定是在這一步才做的。最大的一項是刪掉擇偶用的 preferred_gender 和年齡區間:brainstorm 原本打算保留並在寫庫時校驗,創辦人看了數據後決定整個刪掉,理由是這個區間模型判不出來,快照裏約三成是 0-0、18-99 這種默認值。這個決定記在 spec 的 Decisions 一節。
代碼合成一個 PR,共 19 個 commit,其中 17 個是實現提交;07:54 發了 v2.4.0。同一天下午又發了 v2.4.1,刪掉 7 個退役列、給審計表改名;刪列按 brainstorm 的計劃分兩步,2.4.0 先停止讀寫,下一版再刪。
公開倉有一條紀律:spec 和 issue 裏不出現私倉名和內部 URL,生產數據一律寫成「a production deployment」。
5. 接線與部署(web 客户端倉,10-27 16:19 到 18:10)
web 客户端倉是私有的,管部署配置。它先把 2.4.0 上到測試服。測試服前兩次重建都停在正好 1,200 token,於是重建的 max_tokens 提到 4,000。16:40 生產部署切到 2.4.0。
首輪重建的調用裏,五成多寫出了畫像,四成多返回空畫像,2% 解析失敗;12 次解析失敗裏有 10 次停在正好 4,000 token。
上線後還有一項要花錢的運維:把賬本里舊的 sexuality / health 事實拆成新詞表裏的 kink / mental_health。web 會話把它當成一輪單獨的付費任務。它先報調用份數,標註跑完就停下,報告每個新標籤的條數、涉及的用户數、失敗批數和實際花費,等創辦人點頭才寫進賬本。寫入發生在首輪重建跑完之後,改了七千多行,涉及的用户重新排隊重建。
6. 二期選模型(報告倉 + 審計倉,10-27 18:19 到 19:50)
web 會話把線上配置、首輪讀數和約束寫成一條消息,發給報告倉的會話。
二期直接用 v2.4.0 源碼裏的提示詞和賬本渲染函數,按 tag 讀,不手抄。重放的是生產賬本,兩個候選各跑一遍。付費前先跑 10 個用户做一道閘:重放回來的 prompt token 數要和生產那次重建對上,對不上就停。第一次真停了:10 個裏 3 個差了幾個 token,其中兩個是上一步重分類的時間差造成的,第三個沒追到原因。差得很小,人看過以後決定繼續跑。
在可靠性上,讀數很一邊倒。同樣的賬本上,原來的主力模型在賬本大的用户上會把 kinks 數組寫個不停,被截時已有 193 到 375 項,13 次裏有 10 次是同一批項反覆出現;另一個候選一次也沒截,最長回覆約 1,250 token,空畫像也少得多。它也有短處:在規則邊緣的少數賬本上犯錯更多,也更常寫出賬本里找不到依據的性格和興趣項。內容質量的人工盲評還沒做。創辦人按可靠性拍板換主力,結論發回 web 會話,它開 PR 改配置,19:50 上線。
這套做法裏管用的幾條
先把要求釘死,再往下走。 brainstorm → 審計 → spec → plan → 實現,每一步只在上一步的文檔基礎上做,回頭改的地方都有出處。這是 workflow 倉「約束優先」那一條。
文檔就是交接。 engine 會話從三篇文檔起步,二期從一條消息起步。會話之間不共享上下文,這反倒逼着每一步把結論寫成別人讀得懂的樣子。
權威只有一份。 二期沒有用審計階段起草的那版提示詞。engine 寫了自己的版本,線上跑的也是它,所以重放直接從發版 tag 讀源碼。
數字跟着部署切段。 生產基線從 v2.4.0 上線那一刻切起,之前的調用一律不算。
倉有邊界。 分析倉對生產只讀;報告倉的會話不改 web 倉,只把結論發過去;跨倉的問題開 issue,比如 #463。
花錢的實驗一輪一停。 每輪先報份數和估價,結果不理想就停下來交給人,不自己改提示詞再燒一輪。
判斷留給人。 砍哪些實驗、刪哪些字段、換哪個模型,都是創辦人看了數據後定的;agent 負責把數據擺清楚。
成稿過三道。 事實核查、潤色、跨模型終審,對應 workflow 倉的 publish 模塊。
上線後的第一輪迭代
上線當天就冒出一個問題:重度用户的畫像數組沒有項數上限,回覆頂到 max_tokens,JSON 解析失敗。我們開了 #463,提議在提示詞裏給每個數組字段限項、寫庫時照樣截、要求輸出緊湊 JSON。
二期給了它直接的讀數:截斷都是同一種形狀,一個數組停不下來。issue 當時只能看到存下來的前 2,000 字,判斷回覆 “long, not looping”;二期拿到完整回覆,13 個被截的數組裏 10 個是同一批項反覆出現,所以修法除了限項還要去重,這一條已經作為評論補進 issue。不管哪個模型當主力,這些修法都要做:fallback 用的正是原來那個模型,而換了主力之後生產上的頭 631 次重建裏,新主力也有一次頂到 4,000 token。
一期説截斷是輸入問題,指的是 1.0 要重寫整份舊畫像。2.0 不讀舊畫像,那個來源確實沒了,輸出側的數組失控是另一個問題。
現在的狀態
截至 2026-10-27 20:00 UTC:
- 我們的生產部署跑的是 v2.4.1,重建用新的主力模型,原來那個做 fallback;最新的 release 是 v2.5.0,還沒部署。
- 新主力是按可靠性選的,內容質量的人工盲評還沒做。
- 用户刪除接口在 engine 裏有了,生產上還沒接線。
- 調用次數少 86%、延遲 p90 約 1.1 小時,都還是審計階段的模擬值,生產上的實測要等跑滿一週。
- #463 還開着。
更正(2026-10-27):本文初版説,換了主力模型以後截斷只剩 fallback 一條路。換主力之後生產上的頭 631 次重建裏,新主力也有一次頂到 4,000 token;「上線後的第一輪迭代」一節已改為兩個模型都需要這些修法。
由 Henry Lin 提示,Opus 5.5 撰寫。