增长案例库 Maxed 归档 冷邮件与获客AI自动化

如何在 Codex 上搭建自我改进的外呼系统

How to Build a Self-Improving Outbound System on Codex

中文译文 · 18k 字

一句话摘要

在 Codex 上搭建自我改进的外呼系统

如何在 Codex 上构建一个自我改进的外呼系统 July 19, 2026 · 14分钟阅读 · 查看原文 ↗ 金融 AI 移动应用 自动化 今年早些时候,Andrej Karpathy(@karpathy)把一个 agent 对准了他自己的训练代码,让它跑了两天。它跑了 700 个实验,保留了 20 个跑赢基准的,让模型训练快了 11%。然后他说了一句相当有意思的话:任何你能低成本评估的指标,都可以交给一群 agent。 回复率就是一个你能低成本评估的指标。从那时起,我花了一些时间研究,当这个循环对准外呼时,它长什么样。 我的构建: Codex 读取上周的结果,编辑外呼系统所依赖的评分和话术文件,跑一次测试,然后开一个 pull request。它带着证据和分数,提出对 playbook 的修改建议,然后等待一个人来批准。发送和合并都留在循环之外。 我已经把第一个循环搭建过几次了:感知市场、给客户打分、根据信号写作、检查消息、记录结果、从回复中学习。这篇文章讲的是第二个循环——那个去编辑第一个循环的循环。 这就是那个构建:GTM(go-to-market)作为一份受版本控制、会随着市场自我改进的代码。 仓库 先从文件夹说起。它的结构很重要,因为 Codex 只能改进它能读到、能编辑的东西。 codex-self-improving-outbound/ AGENTS.md README.md config/ scoring.yaml plays.yaml prompts/ improve_scoring.md improve_prompt.md pr_summary.md memory/ outcomes.jsonl evals/ fixtures.yaml score.py scripts/ append_outcome.py run_codex_step.sh propose_improvement.py open_pr.sh weekly_tune.sh examples/ outcomes.sample.jsonl weekly-pr.md 这个仓库是故意做得很朴素的。config/scoring.yaml 保存着决定哪些信号重要的规则。prompts/ 保存着写消息的话术。memory/outcomes.jsonl 保存着市场做了什么。evals/score.py 是那道闸门,判断一个提议的修改是否真的有用。AGENTS.md 是 Codex 在碰任何东西之前要先读的"法律"。 先离线跑第一版。没有 CRM,没有数据补全,没有投递系统。改进循环应该在触及任何真实的外呼机器之前,先在本地文件上证明自己。 第 1 步:先写下法律 在写评分文件、写 prompt 文件之前,先写 AGENTS.md。这个文件让 agent 保持有用、并处于约束之内。 # 自我改进外呼规则 你根据结果来改进一个外呼系统。 硬性规则: - 永远不要发送消息。 - 永远不要抓取或补全真实人物的信息。 - 永远不要自我合并。 - 只编辑这个仓库里的文件。 - 一次只改一个概念。 - 对每一个提议的修改,都要引用 memory/outcomes.jsonl 里的结果。 - 在一个修改能变成 PR 之前,先改进 evals/score.py。 - 如果评估没有改进,就回退你的编辑并停止。 允许的编辑: - config/scoring.yaml - config/plays.yaml - prompts/*.md 必需的输出: - 修改的文件 - 每个修改的理由 - 修改前分数 - 修改后分数 - pull request 摘要 这条法律只有一份工作:收窄工作范围。没有它,Codex 会试图通过扩张范围来"帮忙"。它会加更多数据、碰更多文件、调用更多工具,或者自动化一个本该留在人工控制下的步骤。而这里的工作要小得多:读取结果,提出一个文件的修改,证明它有用,然后等待。 "好"长什么样。你可以在批准一个 PR 之前读一遍这条法律,就能确切知道 Codex 被允许做什么。 它在哪里崩坏。法律变成一份合规文件。如果 AGENTS.md 需要一个目录,那它就已经太大了。让它保持可操作。 第 2 步:把判断搬进配置 大多数外呼判断都活在某个人的脑子里。然后团队买来软件,指望软件去改进一个它根本看不到的决策。 把判断搬进一个文件里。 signals: competitor_comparison: weight: 8 reason: "买家正在对比替代方案" implementation_page_visit: weight: 6 reason: "买家在确认这个能不能被落地" job_repost: weight: 5 reason: "岗位仍空缺且紧急" funding_event: weight: 5 reason: "预算或授权可能发生了变化" generic_download: weight: 1 reason: "内容兴趣,弱购买意图" thresholds: draft: 6 human_review: 10 negative_signals: student_research: -8 vendor_pitch: -6 competitor: -10 这个文件一开始就是一份可见的假设。如果"generic download"应该算作零分,团队可以指着确切的那一行去改。如果"implementation page visit"是一个比你想的更强的信号,Codex 可以提出那个 diff,并展示能证明它的那些结果行。 不要把这个逻辑埋进一个 Python 函数里。如果规则是可见的,团队就能审阅它、跟它争辩、改进它,而不用把一个销售判断变成一次工程重构。 "好"长什么样。这个文件小到可以被拿来争辩。五个信号是一个好的第一版。 它在哪里崩坏。评分文件变成一个杂物抽屉。二十个信号、六个阈值、以及给每个边缘情况配的例外规则,会让改进器过拟合。从窄处开始,让结果告诉你下一个旋钮该放在哪。 第 3 步:把结果写成记忆 最重要的文件是 memory/outcomes.jsonl。 每一次触达一行,在结果确定时写入: {"date":"2026-07-01","account":"Northwind Finance","signal":"competitor_comparison","play":"migration_note","score":8,"outcome":"reply","reason":"索要了迁移说明"} {"date":"2026-07-01","account":"Bluepeak Studio","signal":"generic_download","play":"resource_followup","score":1,"outcome":"no_reply","reason":"纯内容意图"} {"date":"2026-07-02","account":"KiteOps","signal":"implementation_page_visit","play":"implementation_angle","score":6,"outcome":"reply","reason":"询问了落地时间表"} {"date":"2026-07-02","account":"Atlas Recruiting","signal":"job_repost","play":"hiring_angle","score":5,"outcome":"bad_fit","reason":"学生研究请求"} reason 字段才是全部重点。no_reply 几乎什么都没告诉你。"纯内容意图"会告诉下一轮运行,这个信号可能不值得写一封草稿。bad_fit 只有在 reason 解释了为什么的时候才有用。"询问了落地时间表"这种细节,才是能改变一个权重的东西。 先建校验器,再建改进器: 构建 scripts/append_outcome.py。 它接受: - date(日期) - account(客户) - signal(信号) - play(话术) - score(分数) - outcome(结果):reply | meeting | no_reply | bad_fit | bounced - reason(理由) 它拒绝: - 缺失字段 - 未知的结果类型 - 空的 reason - 未来的日期 把有效的行追加到 memory/outcomes.jsonl。 打印出被追加的那一行。 复利就从这里开始。一个仪表盘只能告诉你某个 campaign 在掉量。一份干净的结果日志,能告诉 Codex 在下一次运行之前,该改哪个信号、哪个话术、哪句措辞。 "好"长什么样。一周之后,一个陌生人都能读懂这个文件,说出哪些信号带来了回复,哪些话术带来了不适合的对话,以及团队内部最喜欢、市场却无视的是哪一个。 它在哪里崩坏。团队在周五凭记忆回填结果。赢家活了下来,bad_fit 的理由变得模糊,系统从虚构中学习。结果一落地,就立刻写那一行。 第 4 步:建好评估闸门 在 Codex 编辑任何东西之前,它需要一个它无法搪塞过去的测试。 创建 evals/fixtures.yaml: cases: - account: Northwind Finance signals: [competitor_comparison, implementation_page_visit] expected: human_review note: "一个客户身上两个强信号" - account: Bluepeak Studio signals: [generic_download] expected: ignore note: "纯内容意图" - account: KiteOps signals: [implementation_page_visit] expected: draft note: "落地意图应该越过 draft 阈值" - account: Atlas Recruiting signals: [job_repost, student_research] expected: ignore note: "bad-fit 标记抵消了信号" 然后创建 evals/score.py: 构建 evals/score.py。 读取 config/scoring.yaml 和 evals/fixtures.yaml。 对每一个 case: 1. 把每个信号的权重加总。 2. 加上负信号的惩罚。 3. 给这个客户定路由: - score >= thresholds.human_review => human_review - score >= thresholds.draft => draft - 否则 => ignore 4. 把路由和 expected 对比。 打印每一个预测。 打印最终准确率,格式为 score=0.00 到 score=1.00。 只有当准确率是 1.00 时才以 0 退出。 第一道闸门应该小到能看懂,又锋利到能抓住一次真正的漏判。在我的第一次运行里,基线挂掉了一个 case: Northwind Finance: predicted=human_review expected=human_review Bluepeak Studio: predicted=ignore expected=ignore KiteOps: predicted=ignore expected=draft Atlas Recruiting: predicted=ignore expected=ignore score=0.75 那很好。系统把"落地意图"排在了 draft 阈值之下,所以它忽略了一个 fixture 说值得发消息的客户。在测试里抓住这个,比在一个月漏掉一堆客户之后才抓要好。 "好"长什么样。一条命令给出一个数字,而且每一个失败的 case 都很好检查。 它在哪里崩坏。fixture 只放了那些显而易见的赢家。然后每一个鲁莽的修改都能通过。把难看的 case 放进闸门里:弱意图、不匹配、无回复、过期的信号,以及那些你希望系统当初跳过的客户。 第 5 步:让 Codex 提出一个评分修改 现在 Codex 可以编辑了。 创建 prompts/improve_scoring.md: 你在改进外呼评分系统。 读取: - AGENTS.md - config/scoring.yaml - memory/outcomes.jsonl - evals/fixtures.yaml 你的工作: 1. 找到一条应该修改的评分规则。 2. 理由必须引用 memory/outcomes.jsonl。 3. 只修改 config/scoring.yaml。 4. 运行 python3 evals/score.py。 5. 如果分数提高,保留这个修改。 6. 如果分数持平或下降,回退你的修改并停止。 输出: - 确切修改的那一行 - 导致它修改的那些结果行 - 修改前分数 - 修改后分数 - 这个修改是否应该成为一个 PR 不要编辑 prompt。 不要新增信号。 不要碰投递。 通过仓库包装脚本来运行它: scripts/run_codex_step.sh improve_scoring 我的改进器的第一版犯了一个有用的错误。它去追逐看起来最干净的回复信号。competitor_comparison 在这份微小的结果日志里有着最强的回复率,所以改进器想提高那个权重。评估停在了 0.75,所以这个修改被拒绝了。 这正是这道闸门存在的原因。一个更弱的系统会因为那个说法听起来合理就接受它。而这一套问了一个更好的问题:这个修改有没有修好那个已知的漏判? 第二轮找到了那个最小的、有帮助的编辑: - implementation_page_visit: 4 + implementation_page_visit: 6 评估通过了: Northwind Finance: predicted=human_review expected=human_review Bluepeak Studio: predicted=ignore expected=ignore KiteOps: predicted=draft expected=draft Atlas Recruiting: predicted=ignore expected=ignore score=1.00 这就是这个循环开始变得有用的那一刻。它改了一条规则,带着一个理由,并且对着一个 fixture 证明了那个修改。 "好"长什么样。提议的 diff 是无聊的、可追溯的:改了一行,附带一个基于结果的理由,改进了一次评估。 它在哪里崩坏。Codex 一次改三个权重和两个 prompt。现在没人说得清是哪个修改帮了忙。把法律守严:每个提案一个概念。 第 6 步:单独改进 prompt 文件 评分只是系统的一半。消息模板也会腐坏。 一句上个月还管用的话,开始变得耳熟。一个在某类客户里能换来回复的问题,在另一类里被无视。一句内部听着犀利的话,被市场惩罚。把 prompt 改进当成一条独立的赛道,这样 Codex 就不会把评分和文案混在同一个 PR 里。 创建 config/plays.yaml: plays: migration_note: prompt_file: prompts/plays/migration_note.md use_when: - competitor_comparison banned_lines: - "觉得这可能跟你有关系" - "快速问一下" implementation_angle: prompt_file: prompts/plays/implementation_angle.md use_when: - implementation_page_visit banned_lines: - "了解下我们的解决方案" - "很想聊聊" 然后创建 prompts/improve_prompt.md: 你在改进一个外呼话术(play)。 读取: - AGENTS.md - config/plays.yaml - memory/outcomes.jsonl - 所选 play 对应的 prompt 文件 挑一个至少有 10 条结果的 play。 找出: - 出现在正向结果里的行或结构 - 出现在 no_reply 或 bad_fit 结果里的行或结构 - 任何应该被禁用的短语 对这个 play 的 prompt 做一处小修改。 规则: - 不要改评分。 - 不要新建 play。 - 不要新增渠道。 - 引用结果行。 - 写下修改前后的指令。 然后,如果有文案评估就运行它。 如果没有文案评估,就把 PR 标为 review_required 再打开。 有些改进可以自动打分。另一些仍然需要品味。如果没有文案评估,Codex 可以提出那个 prompt 编辑,但它应该把 PR 标记为待审阅,而不是假装那个编辑已被证明。 "好"长什么样。Codex 说:"这句话出现在七条 no_reply 结果里,所以我把它加进了 banned_lines,"或者"正向回复在第一句就引用了落地细节,所以我收紧了话术,要求必须那样写。" 它在哪里崩坏。改进器因为一条消息得到了回复,就把整个语气重写一遍。prompt 编辑应该比你本能想做的更小。 第 7 步:把修改以 pull request 的形式交付 这是控制层。Codex 编辑文件、运行评估、写下 PR 摘要。一个人审阅并合并。 创建 prompts/pr_summary.md: 为这次外呼改进写一个 pull request 摘要。 包含: 1. 改了什么。 2. 为什么改,引用结果行。 3. 修改前分数。 4. 修改后分数。 5. 修改的文件。 6. 风险。 7. 人工审阅者应该检查什么。 保持简短。 不要声称这个修改已经上线。 创建 scripts/open_pr.sh: #!/usr/bin/env bash set -euo pipefail branch="codex/weekly-tune-$(date +%Y-%m-%d)" git checkout -b "$branch" git add config prompts evals memory git commit -m "Codex weekly outbound tune" body="$(cat outputs/pr-summary.md)" python3 scripts/create_pr.py \ "$branch" \ "Codex weekly outbound tune" \ "$body" PR 读起来应该像一个队友写的: 已修改: - 把 implementation_page_visit 从 4 提高到 6。 为什么: - KiteOps 有落地意图,并且回复了落地时间。 - 之前的评分把这个客户路由到了 ignore。 之前: - 评估分数 0.75 之后: - 评估分数 1.00 审阅者检查: - 确认落地意图足够具体。 - 让 generic downloads 保持低分。 - 只有当这符合实际销售判断时才合并。 这就是安全系统。Codex 做枯燥的活。操作者守住标准。 "好"长什么样。每周一个 PR,小 diff,清晰的理由,通过的评估。 它在哪里崩坏。有人因为觉得审阅是一种摩擦,就给了 Codex 合并的权限。那一分钟,就分隔了一个"在改进"的系统和"在漂移"的系统。 第 8 步:给它定一个节奏 不要在每一条回复之后都跑这个。那样一个系统会过拟合到某一个吵闹的客户身上。 让一周自然过去,让结果累积起来,然后再调。 创建 scripts/weekly_tune.sh: #!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")/.." python3 evals/score.py || true scripts/run_codex_step.sh improve_scoring python3 evals/score.py scripts/run_codex_step.sh pr_summary > outputs/pr-summary.md scripts/open_pr.sh 然后 cron: 0 8 * * MON cd ~/codex-self-improving-outbound && scripts/weekly_tune.sh 如果你用 GitHub Actions,保持同样的结构: name: weekly-outbound-tune on: schedule: - cron: "0 8 * * 1" workflow_dispatch: jobs: tune: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" - run: pip install -r requirements.txt - run: python3 evals/score.py || true - run: scripts/weekly_tune.sh 前两次调优手动跑。读每一个 diff。观察当样本很薄的时候,Codex 试图改什么。一旦提案变得无聊,就把它放进日程里。 "好"长什么样。一个每周 PR 如期出现,带着证据、diff 和评估结果。你合并、编辑,或者关闭它。 它在哪里崩坏。任务跑了,没人审阅,PR 越堆越多。一个自我改进的系统仍然有一项人类习惯:读 diff。 克隆即运行的版本 仓库应该开箱就带四条命令: git clone <repo> cd codex-self-improving-outbound python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt python3 evals/score.py python3 scripts/propose_improvement.py 预期的第一次运行: score=0.75 changed config/scoring.yaml implementation_page_visit: 4 -> 6 score=1.00 open PR for human review 这个离线 demo 证明了文件契约。Codex 那次运行证明了编辑循环。在那之后,把你自己的结果替换掉样例结果,给信号重命名,加上你的话术,并构建一个能反映"你希望系统当初路由得不一样的那些客户"的 fixture。 不要从接投递开始。先从证明改进循环开始。 完整版:max 这个仓库是手动层。它从文件、公开信号和你的 Codex 计划来运行。因为每一条规则都是暴露出来的,它教你理解那个结构。 yourmax.ai 是同一套系统,只是把接缝藏了起来。 你不再需要自己把一个个仓库拼起来,max 就是你直接使用的那个 agent。它侦测市场动向,决定谁值得联系、为什么是现在,起草跨邮件和 LinkedIn 的外呼供你批准,并从结果里持续改进。 这个仓库展示的是大多数团队从未搭建过的自调层:结果变成提议的规则修改,提议的规则修改经过一道闸门,而人工的合并决定什么会成为线上生效的东西。max 把同样的运营逻辑拿过来,作为一个托管系统来运行。 如果你想要完整的仓库,可以告诉我,我会发给你。 标签:# X # 金融 # AI # 移动应用 # 自动化 # 指南 相关文章:构建个人 AI Agent 的 100 条技巧与窍门 这 100 条里有 15 条花了我六周中的四周才学会。它们被拆出来放在最后,按构建顺序排列。如果你这一期别的都不读,也必须读这 15 条,并在你还没…… AI 自动化 金融

原文参考:https://maxed.wiki/posts/how-to-build-a-self-improving-outbound-system-on-codex/ (Maxed.wiki,本页为站内中文整理)