如何利用 Codex 建立自我優化的外撥系統

@nifinet
英語2 天前 · 2026年7月19日
227K
358
22
13
1.7K

TL;DR

Nicolas Finet 詳細介紹了建構自我優化外撥銷售系統的技術框架。該系統利用 AI Agents 分析回覆率,並透過 Pull Request 提出訊息優化建議。

今年稍早,Andrej Karpathy(@karpathy)把一個 Agent 指向他自己的訓練程式碼,然後讓它跑了兩天。它執行了 700 次實驗,保留了其中擊敗基準的 20 次,並讓模型訓練速度加快了 11%。接著他講了一句很有意思的話:任何能低成本評估的指標,都可以交給一群 Agent 來處理。

回覆率就是一個可以低成本評估的指標。我花了一些時間研究,如果把這個循環套用到外呼流程上會是什麼樣子。

我的建置方式:

Codex 讀取上週的結果,編輯外呼系統用來運作的評分與腳本檔案,執行測試,然後開啟一個 Pull Request。它會附上證據與分數,提議修改操作手冊,然後等待人工核准。發送與合併的動作則留在循環之外。

我已經建立過幾次第一個循環:感知市場、為帳戶評分、根據訊號撰寫內容、檢查訊息、紀錄結果、從回覆中學習。這篇文章要講的是第二個循環,也就是用來編輯第一個循環的那個循環。

這就是建置的目標:將 GTM 視為可以從市場反饋中持續改進的版本化程式碼。

Nicolas Finet - inline image

存放庫

先從資料夾開始。結構很重要,因為 Codex 只能改進它能讀取和編輯的東西。

text
1codex-self-improving-outbound/
2 AGENTS.md
3 README.md
4 config/
5 scoring.yaml
6 plays.yaml
7 prompts/
8 improve_scoring.md
9 improve_prompt.md
10 pr_summary.md
11 memory/
12 outcomes.jsonl
13 evals/
14 fixtures.yaml
15 score.py
16 scripts/
17 append_outcome.py
18 run_codex_step.sh
19 propose_improvement.py
20 open_pr.sh
21 weekly_tune.sh
22 examples/
23 outcomes.sample.jsonl
24 weekly-pr.md

這個存放庫刻意保持簡潔。config/scoring.yaml 存放決定哪些訊號重要的規則。prompts/ 存放用來撰寫訊息的腳本。memory/outcomes.jsonl 存放市場的反應。evals/score.py 是判斷某個提議的修改是否有幫助的關卡。AGENTS.md 則是 Codex 在操作任何東西之前必須遵守的「法則」。

先用離線環境執行第一個版本。不需要 CRM、不需要資料擴充、不需要發送系統。改進循環應該先在本地端檔案上證明自己的價值,然後才能接觸真正的外呼系統。

步驟 1. 先寫下法則

在評分檔案和提示檔案之前,先寫好 AGENTS.md。這個檔案能讓 Agent 保持在有用的範圍內,不會亂搞。

markdown
1# 自我改進外呼系統規則
2
3你要根據結果來改進一個外呼系統。
4
5硬性規定:
6- 絕對不能發送訊息。
7- 絕對不能爬取或擴充真實人物的資料。
8- 絕對不能自行合併。
9- 只能編輯此存放庫中的檔案。
10- 一次只改一個概念。
11- 每次提議修改都必須引用 `memory/outcomes.jsonl` 中的結果。
12- 在修改正式成為 PR 之前,必須先改進 `evals/score.py`
13- 如果評估結果沒有進步,就還原你的編輯並停止。
14
15允許的編輯:
16- config/scoring.yaml
17- config/plays.yaml
18- prompts/*.md
19
20必要的輸出:
21- 修改過的檔案
22- 每個修改的原因
23- 修改前的分數
24- 修改後的分數
25- Pull Request 摘要

這個法則只有一個任務:縮小工作範圍。少了它,Codex 就會試圖擴大範圍來「幫忙」。它會加入更多資料、修改更多檔案、呼叫更多工具,或是自動化某個應該由人類控制的步驟。在這裡,它的工作範圍更小:讀取結果、提議修改一個檔案、證明修改有幫助,然後等待。

什麼是好成果。 你可以在核准 PR 之前閱讀這個法則,並確切知道 Codex 被允許做哪些事。

哪裡會出問題。 這個法則變成了合規文件。如果 AGENTS.md 還需要一個目錄,那就表示它已經太長了。保持它的可操作性。

步驟 2. 把判斷邏輯移到設定檔中

大部分的外呼判斷邏輯都存在某個人的腦袋裡。然後團隊買了軟體,卻期望這個軟體能改進一個它根本看不到的決策。

把判斷邏輯寫進一個檔案裡。

yaml
1signals:
2 competitor_comparison:
3 weight: 8
4 reason: "買家正在比較替代方案"
5 implementation_page_visit:
6 weight: 6
7 reason: "買家正在確認這個東西能不能安裝"
8 job_repost:
9 weight: 5
10 reason: "職缺仍然開放且急迫"
11 funding_event:
12 weight: 5
13 reason: "預算或任務可能已經改變"
14 generic_download:
15 weight: 1
16 reason: "對內容感興趣,購買意圖薄弱"
17
18thresholds:
19 draft: 6
20 human_review: 10
21
22negative_signals:
23 student_research: -8
24 vendor_pitch: -6
25 competitor: -10

這個檔案一開始就是一個可見的假設。如果一個一般下載行為應該被視為零分,團隊可以直接指出精確的那一行並修改它。如果一個實作頁面瀏覽的訊號比你想像中更強,Codex 就可以提出差異比較,並顯示證明這個修改合理性的結果記錄。

不要把這個邏輯埋藏在 Python 函式裡。如果規則可見,團隊就可以審查它、與它爭論、並改進它,而不需要把一個銷售判斷變成一個工程重構。

什麼是好成果。 檔案夠小,可以讓團隊拿來辯論。五個訊號就是一個不錯的初版。

哪裡會出問題。 評分檔案變成了一個雜物抽屜。二十個訊號、六個門檻,以及針對每個邊緣案例的例外規則,會讓改進器過度擬合。從狹窄的範圍開始,讓結果告訴你下一個調整點應該在哪裡。

步驟 3. 把結果寫成記憶

最重要的檔案是 memory/outcomes.jsonl

每次接觸記錄一行,在結果確定時寫入:

javascript
1{"date":"2026-07-01","account":"Northwind Finance","signal":"competitor_comparison","play":"migration_note","score":8,"outcome":"reply","reason":"asked for migration notes"}
2{"date":"2026-07-01","account":"Bluepeak Studio","signal":"generic_download","play":"resource_followup","score":1,"outcome":"no_reply","reason":"content-only intent"}
3{"date":"2026-07-02","account":"KiteOps","signal":"implementation_page_visit","play":"implementation_angle","score":6,"outcome":"reply","reason":"asked about implementation timeline"}
4{"date":"2026-07-02","account":"Atlas Recruiting","signal":"job_repost","play":"hiring_angle","score":5,"outcome":"bad_fit","reason":"student research request"}

「原因」欄位是整個機制的核心。「無回覆」幾乎無法告訴你任何事。「純內容意圖」則告訴下一次執行,這個訊號可能不應該產生草稿。「不適合」只有在原因能解釋為什麼不適合時才有用。「詢問了實作時程」是那種可以改變一個權重的細節。

在建立改進器之前,先建立驗證器:

text
1建立 scripts/append_outcome.py。
2
3它接受:
4- date
5- account
6- signal
7- play
8- score
9- outcome: reply | meeting | no_reply | bad_fit | bounced
10- reason
11
12它拒絕:
13- 缺少欄位
14- 未知的 outcome
15- 空白的 reason
16- 未來的日期
17
18將有效的行附加到 memory/outcomes.jsonl。
19印出附加的行。

這就是複利效應開始的地方。一個儀表板只能告訴你某個活動成效不佳。但一個乾淨的結果記錄檔可以告訴 Codex,在下一次執行之前,應該改變哪個訊號、腳本或措辭。

什麼是好成果。 一週之後,一個陌生人可以讀取這個檔案,並分辨出哪些訊號產生了回覆、哪些腳本產生了不適合的對話、以及哪些團隊內部的最愛被市場忽略了。

哪裡會出問題。 團隊在週五憑記憶回溯填寫結果。成功的案例被保留下來,不適合的原因變得模糊不清,然後系統從虛構的資料中學習。應該在結果發生的當下就寫入那行記錄。

步驟 4. 建立評估關卡

在 Codex 編輯任何東西之前,它需要一個它無法敷衍過去的測試。

建立 evals/fixtures.yaml

yaml
1cases:
2 - account: Northwind Finance
3 signals: [competitor_comparison, implementation_page_visit]
4 expected: human_review
5 note: "一個帳戶有兩個強烈訊號"
6
7 - account: Bluepeak Studio
8 signals: [generic_download]
9 expected: ignore
10 note: "純內容意圖"
11
12 - account: KiteOps
13 signals: [implementation_page_visit]
14 expected: draft
15 note: "實作意圖應超過草稿門檻"
16
17 - account: Atlas Recruiting
18 signals: [job_repost, student_research]
19 expected: ignore
20 note: "不適合標記抵消了訊號"

然後建立 evals/score.py

text
1建立 evals/score.py。
2
3讀取 config/scoring.yaml 和 evals/fixtures.yaml。
4
5對於每個案例:
61. 加總每個訊號的權重。
72. 加上負面訊號的懲罰。
83. 對帳戶進行路由:
9 - 分數 >= thresholds.human_review => human_review
10 - 分數 >= thresholds.draft => draft
11 - 否則 => ignore
124. 比較路由結果與預期結果。
13
14印出每個預測。
15將最終準確率印為 score=0.00 到 score=1.00。
16僅在準確率為 1.00 時以 Exit 0 結束。

第一個關卡應該夠小,容易理解,同時又夠敏銳,能抓住真正的失誤。在我的第一次執行中,基準線有一個案例失敗了:

text
1Northwind Finance: predicted=human_review expected=human_review
2Bluepeak Studio: predicted=ignore expected=ignore
3KiteOps: predicted=ignore expected=draft
4Atlas Recruiting: predicted=ignore expected=ignore
5score=0.75

這很好。系統將實作意圖的評分設在草稿門檻之下,所以它忽略了一個測試案例認為應該發送訊息的帳戶。在測試中發現這個問題,總比錯過一個月的帳戶後才發現要好。

什麼是好成果。 一個指令就能得到一個數字,而且每個失敗的案例都很容易檢查。

哪裡會出問題。 測試案例只包含了明顯的成功案例。然後任何魯莽的修改都能通過。應該把不好處理的案例也放進關卡:薄弱意圖、不適合、無回覆、過時訊號,以及那些你希望系統當初跳過的帳戶。

步驟 5. 讓 Codex 提議一個評分修改

現在 Codex 可以進行編輯了。

建立 prompts/improve_scoring.md

markdown
1你要改進外呼評分系統。
2
3讀取:
4- AGENTS.md
5- config/scoring.yaml
6- memory/outcomes.jsonl
7- evals/fixtures.yaml
8
9你的任務:
101. 找出一個應該改變的評分規則。
112. 原因必須引用 memory/outcomes.jsonl。
123. 只修改 config/scoring.yaml。
134. 執行 python3 evals/score.py。
145. 如果分數進步了,保留修改。
156. 如果分數持平或下降,還原你的修改並停止。
16
17輸出:
18- 被修改的精確行數
19- 導致修改的結果記錄行
20- 修改前的分數
21- 修改後的分數
22- 這個修改是否應該成為一個 PR
23
24不要編輯 prompts。
25不要新增訊號。
26不要碰觸發送系統。

透過存放庫包裝腳本來執行它:

bash
1scripts/run_codex_step.sh improve_scoring

我的改進器的第一個版本犯了一個有用的錯誤。它去追逐看起來最漂亮的回覆訊號。competitor_comparison 在小小的結果記錄中有最高的回覆率,所以改進器想增加它的權重。評估結果仍維持在 0.75,所以這個修改被拒絕了。

這正是關卡存在的理由。一個較弱的系統可能會因為這個故事聽起來合理而接受它。但這個系統問了一個更好的問題:這個修改是否修正了已知的失誤?

第二次嘗試找到了那個能解決問題的最小修改:

text
1- implementation_page_visit: 4
2+ implementation_page_visit: 6

評估通過了:

text
1Northwind Finance: predicted=human_review expected=human_review
2Bluepeak Studio: predicted=ignore expected=ignore
3KiteOps: predicted=draft expected=draft
4Atlas Recruiting: predicted=ignore expected=ignore
5score=1.00

這就是循環變得有用的那個時刻。它改變了一個規則,為了一個原因,並對一個測試案例證明了這個修改的效果。

Nicolas Finet - inline image

什麼是好成果。 提議的差異比較是無聊且可追溯的:改變了一行,附上了一個基於結果的原因,一個評估結果獲得了改善。

哪裡會出問題。 Codex 一次改變了三個權重和兩個提示。現在沒有人能分辨是哪個修改起了作用。嚴格遵守法則:一次提案只改一個概念。

步驟 6. 分別改進提示檔案

評分只是系統的一半。訊息模板也會隨著時間而老化。

上個月有效的句子開始聽起來像陳腔濫調。在某個區段獲得回覆的問題,在另一個區段卻被忽略。團隊內部覺得犀利的措辭卻被市場懲罰。把提示檔案改進視為一個獨立的工作流程,這樣 Codex 就不會在同一個 PR 裡混雜了評分和文案的更動。

建立 config/plays.yaml

yaml
1plays:
2 migration_note:
3 prompt_file: prompts/plays/migration_note.md
4 use_when:
5 - competitor_comparison
6 banned_lines:
7 - "thought this might be relevant"
8 - "quick question"
9
10 implementation_angle:
11 prompt_file: prompts/plays/implementation_angle.md
12 use_when:
13 - implementation_page_visit
14 banned_lines:
15 - "checking out our solution"
16 - "would love to chat"

然後建立 prompts/improve_prompt.md

markdown
1你要改進一個外呼腳本。
2
3讀取:
4- AGENTS.md
5- config/plays.yaml
6- memory/outcomes.jsonl
7- 所選腳本的提示檔案
8
9選擇一個至少有 10 個結果的腳本。
10
11找出:
12- 在正面結果中出現的句子或結構
13- 在 no_reply 或 bad_fit 結果中出現的句子或結構
14- 任何應該被禁止的片語
15
16對該腳本的提示做一個小修改。
17
18規則:
19- 不要改變評分。
20- 不要建立新的腳本。
21- 不要新增新的溝通管道。
22- 引用結果記錄行。
23- 寫出修改前和修改後的指示。
24
25然後執行文案評估(如果有的話)。
26如果沒有文案評估存在,則以 review_required 狀態開啟 PR。

有些改進可以自動評分。其他的仍然需要人的判斷。如果沒有文案評估,Codex 可以提議編輯提示,但它應該將 PR 標記為需要審查,而不是假裝這個編輯已經被驗證。

什麼是好成果。 Codex 說:「這個片語出現在七個無回覆的結果中,所以我把它加入了禁止清單。」或者「正面的回覆引用了實作細節(在第一句),所以我調整了腳本要求必須包含這個細節。」

哪裡會出問題。 改進器因為一則訊息得到回覆,就重寫了整個語氣。提示的編輯應該要比你的直覺所想的更小。

步驟 7. 透過 Pull Request 發佈修改

這是控制層。Codex 編輯檔案、執行評估、並撰寫 PR 摘要。由人類進行審查和合併。

Nicolas Finet - inline image

建立 prompts/pr_summary.md

markdown
1為這個外呼系統改進撰寫一個 Pull Request 摘要。
2
3包含:
41. 改了什麼。
52. 為什麼修改,引用結果記錄行。
63. 修改前的分數。
74. 修改後的分數。
85. 修改的檔案。
96. 風險。
107. 審查者應該檢查什麼。
11
12保持簡短。
13不要聲稱修改已上線。

建立 scripts/open_pr.sh

bash
1#!/usr/bin/env bash
2set -euo pipefail
3
4branch="codex/weekly-tune-$(date +%Y-%m-%d)"
5
6git checkout -b "$branch"
7git add config prompts evals memory
8git commit -m "Codex 每週外呼系統調校"
9
10body="$(cat outputs/pr-summary.md)"
11
12python3 scripts/create_pr.py \
13 "$branch" \
14 "Codex 每週外呼系統調校" \
15 "$body"

PR 應該看起來像是一個團隊成員寫的:

text
1Changed:
2- Raised implementation_page_visit from 4 to 6.
3
4Why:
5- KiteOps had implementation-page intent and replied with implementation timing.
6- The previous score routed this account to ignore.
7
8Before:
9- eval score 0.75
10
11After:
12- eval score 1.00
13
14Reviewer check:
15- Make sure implementation intent is specific enough.
16- Keep generic downloads low.
17- Merge only if this matches actual sales judgment.

這就是安全系統。Codex 做繁重的工作。操作者維持標準。

什麼是好成果。 每週一個 PR,差異小,原因清楚,評估通過。

哪裡會出問題。 有人給了 Codex 合併權限,因為審查感覺像是阻力。那一瞬間就區分了一個會改進的系統和一個會偏移的系統。

步驟 8. 設定固定節奏

不要在每次回覆後都執行這個循環。那樣會讓系統過度擬合到一個大聲量的帳戶。

讓一週過去,讓結果累積,然後再進行調校。

Nicolas Finet - inline image

建立 scripts/weekly_tune.sh

bash
1#!/usr/bin/env bash
2set -euo pipefail
3
4cd "$(dirname "$0")/.."
5
6python3 evals/score.py || true
7scripts/run_codex_step.sh improve_scoring
8python3 evals/score.py
9scripts/run_codex_step.sh pr_summary > outputs/pr-summary.md
10scripts/open_pr.sh

然後設定 cron:

bash
10 8 * * MON cd ~/codex-self-improving-outbound && scripts/weekly_tune.sh

如果你使用 GitHub Actions,保持同樣的結構:

yaml
1name: weekly-outbound-tune
2
3on:
4 schedule:
5 - cron: "0 8 * * 1"
6 workflow_dispatch:
7
8jobs:
9 tune:
10 runs-on: ubuntu-latest
11 steps:
12 - uses: actions/checkout@v4
13 - uses: actions/setup-python@v5
14 with:
15 python-version: "3.11"
16 - run: pip install -r requirements.txt
17 - run: python3 evals/score.py || true
18 - run: scripts/weekly_tune.sh

前兩次調校先手動執行。閱讀每一個差異。觀察 Codex 在樣本稀少時會試圖改變什麼。一旦它提出的修改變得無聊、可預期之後,再把它排入排程。

什麼是好成果。 每週自動出現一個 PR,附帶證據、差異和評估結果。你來決定合併、編輯或關閉它。

哪裡會出問題。 工作排程跑了,但沒有人審查,PR 開始堆積。一個自我改進的系統仍然需要一個人類習慣:閱讀差異。

可以直接複製執行的版本

這個存放庫應該附帶四個指令就能開始:

bash
1git clone <repo>
2cd codex-self-improving-outbound
3python3 -m venv .venv
4source .venv/bin/activate
5pip install -r requirements.txt
6python3 evals/score.py
7python3 scripts/propose_improvement.py

預期的首次執行結果:

text
1score=0.75
2changed config/scoring.yaml
3implementation_page_visit: 4 -> 6
4score=1.00
5open PR for human review

離線示範證明了檔案間的合約是有效的。Codex 的執行證明了編輯循環的可行性。之後,用你自己的結果替換範例結果,重新命名訊號,加入你的腳本,並建立一個能反映那些你希望系統當初做出不同路由決策的帳戶的測試案例。

不要從連接發送系統開始。先從證明改進循環有效開始。

完整版本:Max

這個存放庫是手動操作層。它從檔案、公開訊號和你設定的 Codex 計畫來運作。它展示了整個結構,因為每個規則都是可見的。

yourmax.ai 則是同一個系統,但隱藏了接縫。

它不是一個需要你自己動手串接的存放庫,Max 是你可以直接使用的 Agent。它偵測市場動向,決定誰值得聯絡以及為什麼現在聯絡,起草要透過 Email 和 LinkedIn 發送的內容供你核准,並持續從結果中學習改進。

這個存放庫展示了大多數團隊從未建立的自我調校層:結果變成提議的規則修改,提議的規則修改通過一個關卡,人類的合併決策決定什麼會變成正式設定。Max 採用同樣的運作邏輯,並以一個託管系統的形式來執行。

如果你想要完整的存放庫,可以讓我知道,我會把它寄給你。

二次創作

使用 YouMind 創作爆款文章

收集素材、拆解爆點、生成視覺資產、撰寫內容,並在一個 AI 工作空間裡完成分發。

了解 YouMind
寫給創作者

把你的 Markdown 變成乾淨的 𝕏 文章

圖片上傳、表格、程式碼區塊,往 𝕏 上手動重排太痛苦。YouMind 把整篇 Markdown 一鍵轉成乾淨、可直接發佈的 𝕏 文章草稿。

試試 Markdown 轉 𝕏

更多可拆解樣本

近期爆款文章

探索更多爆款文章