🦾 Give Codex ARMS: skills, routines, memory and apps, in Python · ติดแขน ARMS ให้ Codex: สกิล งานประจำ ความจำ และแอป ด้วย Python · 给 Codex 装上 ARMS:用 Python 做技能、例行任务、记忆和应用

This page teaches you to work with Codex, OpenAI's coding agent, in a way that gets better every week and does not waste what you pay for it. It teaches four habits, called ARMS: Apps, Routines, Memory and Skills. You learn them by building one workspace on a Mac, one folder that holds everything Codex works on for you, and by writing thirteen small Python files in it: · หน้านี้สอนวิธีทำงานกับ Codex เอเจนต์เขียนโค้ดของ OpenAI ในแบบที่ดีขึ้นทุกสัปดาห์และไม่เปลืองสิ่งที่คุณจ่ายไป มันสอนนิสัยสี่อย่างที่เรียกว่า ARMS: Apps (แอป), Routines (งานประจำ), Memory (ความจำ) และ Skills (สกิล) คุณเรียนมันด้วยการสร้างพื้นที่ทำงานหนึ่งแห่งบนแมค คือโฟลเดอร์เดียวที่เก็บทุกอย่างที่ Codex ทำให้คุณ และด้วยการเขียนไฟล์ Python เล็ก ๆ สิบสามไฟล์ในนั้น: · 这一页教你怎样和 OpenAI 的编程智能体 Codex 一起工作,让它每周都变得更好,又不浪费你为它付的钱。它教四个习惯,合称 ARMS:Apps(应用)、Routines(例行任务)、Memory(记忆)和 Skills(技能)。你通过在 Mac 上建一个工作区来学,也就是一个装着 Codex 为你做的所有事情的文件夹,并在里面写十三个小小的 Python 文件:

Every Python file on this page was run, and what it printed is shown under it. Most of them you can also run right here: press ▶ Run, and Python runs in the page, on a pretend Mac. A quiz after each step checks the idea. The moments where Codex itself has to answer are marked not run by us. The four habits come from a video for Claude Code, and this page does the same with Codex. Part 5 does every step again with Google's Gemini models. · ไฟล์ Python ทุกไฟล์ในหน้านี้ถูกรันจริง และสิ่งที่มันพิมพ์ออกมาแสดงไว้ข้างใต้ ส่วนใหญ่คุณรันได้ตรงนี้เลยด้วย: กด ▶ Run แล้ว Python จะรันในหน้าเว็บบนแมคจำลอง และหลังแต่ละขั้นตอนมีแบบทดสอบเช็กความเข้าใจ จังหวะที่ Codex เองต้องตอบมีเครื่องหมาย not run by us (เราไม่ได้รันเอง) นิสัยทั้งสี่มาจากวิดีโอสำหรับ Claude Code และหน้านี้ทำแบบเดียวกันด้วย Codex ส่วนที่ 5 ทำทุกขั้นตอนอีกครั้งด้วยโมเดล Gemini ของ Google · 这一页的每个 Python 文件都运行过,它打印的内容就显示在下面。大多数你也可以就在这里运行:按 ▶ Run,Python 就在这一页里、在一台假装的 Mac 上运行。每一步之后还有一个小测验,检查你是否懂了。需要 Codex 自己回答的地方都标着 not run by us(我们没有运行过)。这四个习惯来自一个讲 Claude Code 的视频,这一页用 Codex 做同样的事。第 5 部分用 Google 的 Gemini 模型把每一步再做一遍。

Before you start · ก่อนเริ่ม · 开始之前

🇬🇧 English

A chatbot like ChatGPT is different than an agent like Codex or Claude Code in that an agent can do things. In order to use an agent efficiently and save tokens, as well as make the agent more powerful, you can place an agent's abilities into the following four conceptual categories: Apps, Routines, Memory and Skills. Your plan gives you only so many tokens each week. The four habits on this page are about getting more done with fewer of them, and about not telling Codex the same thing twice.

The plan of this page comes from a video by Jay E of RoboNuggets, A NEW Agentic OS Standard for Claude 5.5 is here (21 October 2026). He calls the four habits ARMS, and teaches them from the bottom up: skills first, then routines, then memory, then apps. His tool is Claude Code; ours is Codex, and where Codex has its own way of doing something, this page uses it. Python does every job that does not need a model, because Python costs nothing to run.

What you need:

  • a Mac, with Codex installed and signed in. Steps 1 and 2 of the Codex on a Mac page do that;
  • a GitHub account. Making one is free, at github.com;
  • Python 3. Type python3 --version in the Terminal. If the Mac offers to install the Command Line Tools, say yes: they bring Python 3 and git;
  • a text editor. The pages on this site use vim, which every Mac has. You can also save each file with its Download button.

On this page you will:

  1. make your workspace, with a folder for each kind of work;
  2. write a skill that holds examples of how you write, with wild cards;
  3. write a skill that runs a Python checker, so that Codex reads a few lines, not a whole text;
  4. send easy jobs to a small model with route.py;
  5. write a routine that prints nothing on a normal day;
  6. let the Mac start that routine every Monday;
  7. write down what every Codex run cost, with ask.py, and stop a routine at a weekly cap;
  8. read the week back with week.py: which routine cost too much, and which job you keep typing by hand;
  9. give Codex a map of your workspace in AGENTS.md;
  10. see why a long chat costs more, and what to do about it;
  11. park fixes in a list for later with note.py;
  12. give Codex a short command instead of a long answer from a web service;
  13. build a small web app that shows every page Codex made for you.

🇹🇭 ไทย

แชตบอตอย่าง ChatGPT ต่างจากเอเจนต์อย่าง Codex หรือ Claude Code ตรงที่เอเจนต์ลงมือทำสิ่งต่าง ๆ ได้ ถ้าจะใช้เอเจนต์ให้คุ้มและประหยัดโทเค็น และทำให้เอเจนต์ทำได้มากขึ้นด้วย คุณจัดความสามารถของเอเจนต์ได้เป็นสี่หมวดตามแนวคิดต่อไปนี้: Apps (แอป), Routines (งานประจำ), Memory (ความจำ) และ Skills (สกิล) แผนของคุณให้โทเค็นได้จำนวนหนึ่งต่อสัปดาห์เท่านั้น นิสัยทั้งสี่ในหน้านี้ว่าด้วยการทำงานให้ได้มากขึ้นด้วยโทเค็นที่น้อยลง และการไม่ต้องบอก Codex เรื่องเดิมสองครั้ง

แผนของหน้านี้มาจากวิดีโอของ Jay E แห่ง RoboNuggets ชื่อ A NEW Agentic OS Standard for Claude 5.5 is here (21 ตุลาคม 2026) เขาเรียกนิสัยทั้งสี่ว่า ARMS และสอนจากล่างขึ้นบน: สกิลก่อน แล้วงานประจำ แล้วความจำ แล้วแอป เครื่องมือของเขาคือ Claude Code ของเราคือ Codex และที่ไหนที่ Codex มีวิธีของตัวเอง หน้านี้ก็ใช้วิธีนั้น Python ทำทุกงานที่ไม่ต้องใช้โมเดล เพราะการรัน Python ไม่มีค่าใช้จ่าย

สิ่งที่คุณต้องมี:

  • แมคหนึ่งเครื่องที่ติดตั้ง Codex และลงชื่อเข้าใช้แล้ว ขั้นตอนที่ 1 และ 2 ของหน้า Codex บนแมคทำสิ่งนี้;
  • บัญชี GitHub สมัครได้ฟรีที่ github.com;
  • Python 3 พิมพ์ python3 --version ใน Terminal ถ้าแมคเสนอให้ติดตั้ง Command Line Tools ให้ตอบตกลง: มันนำ Python 3 และ git มาด้วย;
  • โปรแกรมแก้ไขข้อความ หน้าต่าง ๆ ในเว็บนี้ใช้ vim ซึ่งแมคทุกเครื่องมี คุณยังบันทึกแต่ละไฟล์ด้วยปุ่ม Download ของมันได้ด้วย

ในหน้านี้คุณจะ:

  1. สร้างพื้นที่ทำงาน โดยมีโฟลเดอร์สำหรับงานแต่ละแบบ;
  2. เขียนสกิลที่เก็บตัวอย่างว่าคุณเขียนอย่างไร พร้อมไวลด์การ์ด;
  3. เขียนสกิลที่รันตัวตรวจภาษา Python เพื่อให้ Codex อ่านไม่กี่บรรทัด ไม่ใช่ทั้งข้อความ;
  4. ส่งงานง่ายไปให้โมเดลเล็กด้วย route.py;
  5. เขียนงานประจำที่ไม่พิมพ์อะไรเลยในวันปกติ;
  6. ให้แมคเริ่มงานประจำนั้นทุกวันจันทร์;
  7. จดว่า Codex แต่ละครั้งที่รันใช้ไปเท่าไร ด้วย ask.py และหยุดงานประจำเมื่อถึงเพดานรายสัปดาห์;
  8. อ่านย้อนดูสัปดาห์ด้วย week.py: งานประจำไหนใช้มากเกินไป และงานไหนที่คุณพิมพ์เองซ้ำแล้วซ้ำอีก;
  9. ให้แผนที่พื้นที่ทำงานแก่ Codex ใน AGENTS.md;
  10. ดูว่าทำไมแชตยาว ๆ ถึงแพงกว่า และควรทำอย่างไร;
  11. จอดสิ่งที่จะแก้ไว้ในรายการไว้ทำทีหลังด้วย note.py;
  12. ให้คำสั่งสั้น ๆ แก่ Codex แทนคำตอบยาว ๆ จากบริการบนเว็บ;
  13. สร้างเว็บแอปเล็ก ๆ ที่แสดงทุกหน้าที่ Codex ทำให้คุณ

🇨🇳 中文

像 ChatGPT 这样的聊天机器人和像 Codex 或 Claude Code 这样的智能体不同:智能体能动手做事。要高效地使用智能体、节省词元(token),同时让智能体更强大,你可以把智能体的能力放进下面四个概念类别:Apps(应用)、Routines(例行任务)、Memory(记忆)和 Skills(技能)。你的方案每周只给你这么多词元。这一页的四个习惯,讲的是用更少的词元做更多的事,以及同一件事不必对 Codex 说两遍。

这一页的思路来自 RoboNuggets 的 Jay E 的一个视频,A NEW Agentic OS Standard for Claude 5.5 is here(2026 年 10 月 21 日)。他把这四个习惯叫作 ARMS,并从下往上教:先技能,再例行任务,再记忆,最后应用。他用的工具是 Claude Code,我们用的是 Codex;凡是 Codex 有自己做法的地方,这一页就用它的做法。凡是不需要模型的事都交给 Python,因为运行 Python 不花钱。

你需要:

  • 一台装好并登录了 Codex 的 Mac。在 Mac 上用 Codex 那一页的第 1 和第 2 步就是做这件事的;
  • 一个 GitHub 账号,在 github.com 免费注册;
  • Python 3。在终端里输入 python3 --version。如果 Mac 提出要安装命令行工具,就同意:它们会带来 Python 3 和 git;
  • 一个文本编辑器。这个网站的页面用的是 vim,每台 Mac 都有。你也可以用每个文件的 Download 按钮来保存它。

在这一页你将:

  1. 建好你的工作区,每种工作一个文件夹;
  2. 写一个技能,里面放着你怎么写作的例子,还有“外卡”;
  3. 写一个运行 Python 检查程序的技能,让 Codex 只读几行,而不是整篇文字;
  4. 用 route.py 把简单的任务交给小模型;
  5. 写一个平常日子里什么也不打印的例行任务;
  6. 让 Mac 每个星期一启动这个例行任务;
  7. 用 ask.py 记下 Codex 每次运行花了多少,并在每周上限处叫停一个例行任务;
  8. 用 week.py 回看这一周:哪个例行任务花得太多,哪件事你一直在手动输入;
  9. 在 AGENTS.md 里给 Codex 一张你工作区的地图;
  10. 看看为什么长对话更贵,以及该怎么办;
  11. 用 note.py 把修改先放进清单,留到以后;
  12. 给 Codex 一条简短的命令,而不是网络服务的一大段回答;
  13. 做一个小网页应用,列出 Codex 为你做的每一个网页。

🇬🇧 English

Start on GitHub. Your workspace will be a repository there, so a copy of it lives off your Mac, and any other computer can take it with git clone.

  1. Sign in at github.com.
  2. Press + at the top right, then New repository.
  3. Name it arms. Choose Private: your skills will hold how you write, and your logs what you asked Codex. Turn on Add README, and press Create repository.
  4. Press the green Code button and copy the address under HTTPS. It looks like https://github.com/YOUR-NAME/arms.git.

A private repository can be copied only by someone who is signed in. If gh auth status in the Terminal does not say you are, run gh auth login, as in step 5 of the Codex on a Mac page: choose GitHub.com, then HTTPS, and say yes when it asks to authenticate Git with your GitHub credentials. Then type these in the Terminal, with your own name in place of YOUR-NAME:

🇹🇭 ไทย

เริ่มที่ GitHub พื้นที่ทำงานของคุณจะเป็นรีโพซิทอรีที่นั่น สำเนาของมันจึงอยู่นอกแมคของคุณด้วย และคอมพิวเตอร์เครื่องอื่นก็เอาไปได้ด้วย git clone

  1. ลงชื่อเข้าใช้ที่ github.com
  2. กด + ที่มุมขวาบน แล้วกด New repository
  3. ตั้งชื่อว่า arms เลือก Private (ส่วนตัว): สกิลของคุณจะเก็บวิธีที่คุณเขียน และบันทึกของคุณจะเก็บสิ่งที่คุณถาม Codex เปิด Add README แล้วกด Create repository
  4. กดปุ่มสีเขียว Code แล้วคัดลอกที่อยู่ใต้ HTTPS หน้าตาแบบนี้ https://github.com/YOUR-NAME/arms.git

รีโพซิทอรีส่วนตัวคัดลอกได้เฉพาะคนที่ลงชื่อเข้าใช้แล้ว ถ้า gh auth status ใน Terminal ไม่ได้บอกว่าคุณลงชื่อแล้ว ให้รัน gh auth login แบบในขั้นตอนที่ 5 ของหน้า Codex บนแมค: เลือก GitHub.com แล้วเลือก HTTPS และตอบ yes เมื่อมันถามว่าจะให้ Git ยืนยันตัวตนด้วยบัญชี GitHub ของคุณไหม จากนั้นพิมพ์คำสั่งเหล่านี้ใน Terminal โดยใส่ชื่อของคุณเองแทน YOUR-NAME:

🇨🇳 中文

从 GitHub 开始。你的工作区会是那里的一个仓库,所以在你的 Mac 之外也有它的一份副本,别的电脑也能用 git clone 把它拿走。

  1. 在 github.com 登录。
  2. 按右上角的 +,再按 New repository。
  3. 把它命名为 arms。选 Private(私有):你的技能会记着你怎么写作,你的日志会记着你问过 Codex 什么。打开 Add README,再按 Create repository。
  4. 按绿色的 Code 按钮,复制 HTTPS 下面的地址。它看起来像 https://github.com/YOUR-NAME/arms.git。

私有仓库只有登录了的人才能复制。如果在终端里运行 gh auth status 没有说你已登录,就像在 Mac 上用 Codex 那一页的第 5 步那样运行 gh auth login:选 GitHub.com,再选 HTTPS,它问要不要用你的 GitHub 账号为 Git 验证身份时,回答 yes。然后在终端里输入下面几行,把 YOUR-NAME 换成你自己的名字:

not run by us: in the Terminal, with your own GitHub nameเราไม่ได้รันเอง: ใน Terminal โดยใส่ชื่อ GitHub ของคุณเอง我们没有运行过:在终端里,换上你自己的 GitHub 名字

cd ~
git clone https://github.com/YOUR-NAME/arms.git
cd arms

🇬🇧 English

Those three commands copy the repository from GitHub into a new folder, ~/arms: the workspace. It is a git repository from the start, so there is no git init to type. Codex likes to work in one: codex exec, which steps 4 to 8 use, refuses to start outside a repository unless you tell it otherwise. Git also keeps every version of every file, so when Codex changes a skill or your AGENTS.md, git diff shows you what it changed, and git checkout takes it back. When you want GitHub to have your work too, git add, git commit and git push send it there. Save every file of this page in ~/arms, and run every command from there.

🇹🇭 ไทย

สามคำสั่งนั้นคัดลอกรีโพซิทอรีจาก GitHub มาไว้ในโฟลเดอร์ใหม่ ~/arms ซึ่งก็คือพื้นที่ทำงาน มันเป็นรีโพซิทอรี git ตั้งแต่แรก จึงไม่ต้องพิมพ์ git init Codex ชอบทำงานในรีโพซิทอรี: codex exec ที่ขั้นตอนที่ 4 ถึง 8 ใช้ จะไม่ยอมเริ่มนอกรีโพซิทอรีถ้าคุณไม่บอกเป็นอย่างอื่น git ยังเก็บทุกเวอร์ชันของทุกไฟล์ด้วย ดังนั้นเมื่อ Codex แก้สกิลหรือ AGENTS.md ของคุณ git diff จะแสดงว่ามันแก้อะไร และ git checkout เอากลับคืนได้ เมื่อคุณอยากให้ GitHub มีงานของคุณด้วย git add git commit และ git push จะส่งมันขึ้นไป บันทึกทุกไฟล์ของหน้านี้ไว้ใน ~/arms และรันทุกคำสั่งจากที่นั่น

🇨🇳 中文

这三条命令把仓库从 GitHub 复制到一个新文件夹 ~/arms,也就是工作区。它一开始就是 git 仓库,所以不用再输入 git init。Codex 喜欢在仓库里工作:第 4 到第 8 步用到的 codex exec,除非你另外告诉它,否则在仓库之外它不肯启动。git 还会保存每个文件的每个版本,所以当 Codex 改了一个技能或你的 AGENTS.md,git diff 会告诉你它改了什么,git checkout 能把它改回来。想让 GitHub 也有你的成果时,git add、git commit 和 git push 会把它送上去。把这一页的每个文件都保存在 ~/arms 里,每条命令也都从那里运行。

SSkillsสกิล技能
Saved instructions that Codex follows when you call them: what you know, and how a job is done.คำสั่งที่บันทึกไว้ซึ่ง Codex ทำตามเมื่อคุณเรียกใช้: สิ่งที่คุณรู้ และวิธีทำงานหนึ่ง ๆ保存好的指令,你一叫它,Codex 就照着做:你知道的东西,以及一件事该怎么做。
SKILL.md, check.py, route.py
→
RRoutinesงานประจำ例行任务
Jobs that run on a schedule: quiet ones, ones with a cap, and ones you did not know you repeat.งานที่รันตามตารางเวลา: งานที่เงียบ งานที่มีเพดาน และงานที่คุณไม่รู้ตัวว่าทำซ้ำ按时间表运行的任务:安静的、有上限的,以及你没发觉自己在重复的。
quiet_check.py, cron, ask.py, week.py
→
MMemoryความจำ记忆
What Codex knows about your work, and how cheaply it finds it: a map, a short chat, a list of fixes.สิ่งที่ Codex รู้เกี่ยวกับงานของคุณ และมันหาเจอได้ถูกแค่ไหน: แผนที่ แชตที่สั้น และรายการสิ่งที่จะแก้Codex 对你的工作知道些什么,以及它找到这些有多便宜:一张地图、一次短对话、一份修改清单。
AGENTS.md, make_map.py, how_big.py, note.py
→
AAppsแอป应用
What Codex reaches outside the chat, and the small apps you build so that you need the chat less.สิ่งที่ Codex เข้าถึงนอกแชต และแอปเล็ก ๆ ที่คุณสร้างเพื่อให้ต้องใช้แชตน้อยลงCodex 在对话之外能触及的东西,以及你做的那些让你少用对话的小应用。
weather.py, inbox.py

1 make_workspace.py: one folder for everything · make_workspace.py: โฟลเดอร์เดียวสำหรับทุกอย่าง · make_workspace.py:一个文件夹装下一切

🇬🇧 English

What is a workspace? It is one folder on your Mac, ~/arms, that holds everything Codex works on for you, like a desk with a labelled drawer for each kind of thing. Codex looks at this folder at the start of every chat. The tidier it is, the faster Codex finds what it needs, and the fewer tokens it spends looking.

The workspace is also where the four ARMS habits live. Each habit has its own place in it:

  • Skills (steps 2 to 4): .agents/skills holds the skills Codex can call.
  • Routines (steps 5 to 8): log holds what each run cost.
  • Memory (steps 9 to 11): AGENTS.md holds the map Codex reads first, and notes holds fixes for later.
  • Apps (steps 12 and 13): artifacts holds the pages Codex makes for you.

school and projects hold your own work, the things Codex helps you with. A workspace works when every folder has one job and says so. So each folder gets a README.md, a short file whose first line of text says the folder's job. Codex reads that line, and so does make_map.py in step 9.

🇹🇭 ไทย

พื้นที่ทำงานคืออะไร มันคือโฟลเดอร์เดียวบนแมคของคุณ ~/arms ที่เก็บทุกอย่างที่ Codex ทำให้คุณ เหมือนโต๊ะที่มีลิ้นชักติดป้ายสำหรับของแต่ละแบบ Codex ดูโฟลเดอร์นี้ตอนเริ่มทุกแชต ยิ่งมันเป็นระเบียบ Codex ก็ยิ่งหาสิ่งที่ต้องการเจอเร็ว และใช้โทเค็นในการหาน้อยลง

พื้นที่ทำงานยังเป็นที่อยู่ของนิสัย ARMS ทั้งสี่ด้วย นิสัยแต่ละอย่างมีที่ของตัวเองในนั้น:

  • Skills สกิล (ขั้นตอนที่ 2 ถึง 4): .agents/skills เก็บสกิลที่ Codex เรียกใช้ได้
  • Routines งานประจำ (ขั้นตอนที่ 5 ถึง 8): log เก็บว่าแต่ละครั้งที่รันใช้ไปเท่าไร
  • Memory ความจำ (ขั้นตอนที่ 9 ถึง 11): AGENTS.md เก็บแผนที่ที่ Codex อ่านก่อน และ notes เก็บสิ่งที่จะแก้ทีหลัง
  • Apps แอป (ขั้นตอนที่ 12 และ 13): artifacts เก็บหน้าเว็บที่ Codex ทำให้คุณ

school และ projects เก็บงานของคุณเอง คือสิ่งที่ Codex ช่วยคุณทำ พื้นที่ทำงานจะใช้ได้ดีเมื่อทุกโฟลเดอร์มีหน้าที่เดียวและบอกหน้าที่นั้นไว้ ดังนั้นแต่ละโฟลเดอร์จึงมี README.md ไฟล์สั้น ๆ ที่บรรทัดข้อความแรกบอกหน้าที่ของโฟลเดอร์ Codex อ่านบรรทัดนั้น และ make_map.py ในขั้นตอนที่ 9 ก็อ่านด้วย

🇨🇳 中文

什么是工作区?它就是你 Mac 上的一个文件夹 ~/arms,装着 Codex 为你做的所有事情,就像一张书桌,每种东西都有一个贴着标签的抽屉。Codex 每次开始对话时都会看这个文件夹。它越整齐,Codex 找到需要的东西就越快,找的时候花的词元也越少。

工作区也是 ARMS 四个习惯住的地方。每个习惯在里面都有自己的位置:

  • Skills 技能(第 2 到第 4 步):.agents/skills 放着 Codex 能调用的技能。
  • Routines 例行任务(第 5 到第 8 步):log 记着每次运行花了多少。
  • Memory 记忆(第 9 到第 11 步):AGENTS.md 放着 Codex 最先读的地图,notes 放着留到以后的修改。
  • Apps 应用(第 12 和第 13 步):artifacts 放着 Codex 为你做的网页。

school 和 projects 放你自己的作业和项目,也就是 Codex 帮你做的事。一个工作区好用,是因为每个文件夹都只有一项任务,并且写明了这项任务。所以每个文件夹都有一个 README.md,一个短短的文件,它的第一行文字写明这个文件夹的任务。Codex 会读这一行,第 9 步的 make_map.py 也会读。

🇬🇧 English

🎯 Your goal in this step: make the workspace's five folders, each with a README.md that says its job, plus a first AGENTS.md and a first page in artifacts, and never write over a file that is already there, so that you can run the program again safely.

🇹🇭 ไทย

🎯 เป้าหมายของขั้นตอนนี้: สร้างห้าโฟลเดอร์ของพื้นที่ทำงาน แต่ละโฟลเดอร์มี README.md ที่บอกหน้าที่ของมัน พร้อม AGENTS.md ฉบับแรก และหน้าเว็บแรกใน artifacts และไม่เขียนทับไฟล์ที่มีอยู่แล้วเลย เพื่อให้รันโปรแกรมซ้ำได้อย่างปลอดภัย

🇨🇳 中文

🎯 这一步的目标:建好工作区的五个文件夹,每个都有一个写明任务的 README.md,再加上第一版 AGENTS.md 和 artifacts 里的第一个网页,而且从不覆盖已经存在的文件,这样程序可以放心地再运行一次。

🇬🇧 English

Now it is your job to make the folders. Nothing exists on your Mac yet: your program has to create each folder and write each README.md. First the simplest way, then the full setup script. Way 1 practises in a folder called practice in your home folder, so save it in ~ and run it there, and your real workspace stays tidy. Press Run, then 📁 Look inside to see what it made.

🇹🇭 ไทย

ตอนนี้เป็นหน้าที่ของคุณที่จะสร้างโฟลเดอร์ ยังไม่มีอะไรอยู่บนแมคของคุณเลย: โปรแกรมของคุณต้องสร้างแต่ละโฟลเดอร์และเขียน README.md แต่ละไฟล์ อย่างแรกคือวิธีที่ง่ายที่สุด แล้วจึงเป็นสคริปต์ตั้งค่าแบบเต็ม วิธีที่ 1 ฝึกในโฟลเดอร์ชื่อ practice ในโฟลเดอร์บ้านของคุณ ดังนั้นให้บันทึกไว้ใน ~ และรันจากที่นั่น พื้นที่ทำงานจริงของคุณจะได้เป็นระเบียบ กด Run แล้วกด 📁 Look inside เพื่อดูว่ามันสร้างอะไร

🇨🇳 中文

现在轮到你来建文件夹了。你的 Mac 上还什么都没有:你的程序得建好每个文件夹,写好每个 README.md。先看最简单的做法,再看完整的设置脚本。做法 1 在你主文件夹里一个叫 practice 的文件夹里练习,所以把它保存在 ~ 里并在那里运行,你真正的工作区就能保持整齐。按一下 Run,再按 📁 Look inside 看看它建了什么。

🇬🇧 English

Way 1: one line for each folder. pathlib is the part of Python that works with files and folders, and import brings it in. pathlib.Path("practice") is a path, the name of a place on your disk, and home = stores it in a variable. The / joins two paths: home / "school" is practice/school. .mkdir() makes that folder, and exist_ok=True says: if it is already there, that is fine, do not stop with an error. It works, but five lines that look almost the same are a sign that Python could do the repeating for you.

🇹🇭 ไทย

วิธีที่ 1: หนึ่งบรรทัดต่อหนึ่งโฟลเดอร์ pathlib คือส่วนของ Python ที่ทำงานกับไฟล์และโฟลเดอร์ และ import นำมันเข้ามา pathlib.Path("practice") คือ พาธ ชื่อของที่แห่งหนึ่งบนดิสก์ของคุณ และ home = เก็บมันไว้ในตัวแปร เครื่องหมาย / ต่อสองพาธเข้าด้วยกัน: home / "school" คือ practice/school .mkdir() สร้างโฟลเดอร์นั้น และ exist_ok=True บอกว่า ถ้ามีอยู่แล้วก็ไม่เป็นไร ไม่ต้องหยุดพร้อมข้อผิดพลาด มันใช้ได้ แต่ห้าบรรทัดที่หน้าตาเกือบเหมือนกันเป็นสัญญาณว่า Python ทำส่วนที่ซ้ำแทนคุณได้

🇨🇳 中文

做法 1:一个文件夹一行。pathlib 是 Python 里处理文件和文件夹的部分,import 把它引进来。pathlib.Path("practice") 是一个路径,也就是你磁盘上一个地方的名字,home = 把它存进一个变量。/ 把两个路径接起来:home / "school" 就是 practice/school。.mkdir() 建出那个文件夹,exist_ok=True 的意思是:如果它已经在了也没关系,不要报错停下。这样能行,但是五行几乎一样的代码,说明 Python 本可以替你做这些重复的事。

~/way1_by_hand.py

# Way 1: make each folder by hand, one line each.
import pathlib

home = pathlib.Path("practice")      # a folder to practise in
home.mkdir(exist_ok=True)

(home / "school").mkdir(exist_ok=True)
(home / "projects").mkdir(exist_ok=True)
(home / "notes").mkdir(exist_ok=True)
(home / "log").mkdir(exist_ok=True)
(home / "artifacts").mkdir(exist_ok=True)

print("Made 5 folders with 5 lines that look almost the same.")
python3 way1_by_hand.py

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

Made 5 folders with 5 lines that look almost the same.

🇬🇧 English

The full setup script: make_workspace.py. This is the file for your real workspace. It makes the same folders as way 1 without the repeating, and writes each folder's README.md too. FOLDERS is a dictionary: pairs written key: value between curly brackets, each folder's name with the job its README.md will say. AGENTS_START and WELCOME are strings between three quote marks, so they can run over many lines; their names are in capitals because they never change while the program runs. for name, job in FOLDERS.items(): is a for loop: it takes each folder's name and job in turn, and runs the indented lines under it once for each. def make(path, text): is a function, a named recipe you can use again and again: if the file is already there, it returns "keep " and writes nothing, so running the program again never throws away your changes. Two more things are new. main() holds the steps, and if __name__ == "__main__": runs it only when you run this file, not when another file borrows from it. sys.argv is the list of words you typed after python3: if you name a folder, the workspace goes there, and if not, into the folder you are in. Save it in ~/arms and run it there. Press Run below too: this page has a pretend Mac, and the workspace appears in its ~/arms.

🇹🇭 ไทย

สคริปต์ตั้งค่าแบบเต็ม: make_workspace.py นี่คือไฟล์สำหรับพื้นที่ทำงานจริงของคุณ มันสร้างโฟลเดอร์ชุดเดียวกับวิธีที่ 1 โดยไม่ต้องเขียนซ้ำ และเขียน README.md ของแต่ละโฟลเดอร์ด้วย FOLDERS คือ ดิกชันนารี: คู่ที่เขียนแบบ key: value อยู่ในวงเล็บปีกกา ชื่อของแต่ละโฟลเดอร์คู่กับหน้าที่ที่ README.md ของมันจะบอก AGENTS_START และ WELCOME คือ สตริงที่อยู่ในเครื่องหมายคำพูดสามตัว จึงยาวได้หลายบรรทัด ชื่อของมันเป็นตัวพิมพ์ใหญ่เพราะมันไม่เปลี่ยนเลยระหว่างที่โปรแกรมรัน for name, job in FOLDERS.items(): คือ ลูป for: มันหยิบชื่อและหน้าที่ของแต่ละโฟลเดอร์ทีละคู่ และรันบรรทัดที่ย่อหน้าข้างล่างหนึ่งครั้งต่อหนึ่งคู่ def make(path, text): คือ ฟังก์ชัน สูตรที่มีชื่อซึ่งใช้ได้ซ้ำแล้วซ้ำอีก: ถ้ามีไฟล์นั้นอยู่แล้ว มันจะส่ง "keep " กลับมาและไม่เขียนอะไร การรันโปรแกรมซ้ำจึงไม่ทิ้งสิ่งที่คุณแก้ไว้เลย มีอีกสองอย่าง main() เก็บขั้นตอนทั้งหมด และ if __name__ == "__main__": รันมันเฉพาะตอนที่คุณรันไฟล์นี้ ไม่ใช่ตอนที่ไฟล์อื่นยืมของจากมัน sys.argv คือรายการคำที่คุณพิมพ์หลัง python3: ถ้าคุณบอกชื่อโฟลเดอร์ พื้นที่ทำงานจะไปอยู่ที่นั่น ถ้าไม่บอก ก็อยู่ในโฟลเดอร์ที่คุณอยู่ บันทึกมันไว้ใน ~/arms และรันที่นั่น กด Run ข้างล่างด้วย: หน้านี้มีแมคจำลอง และพื้นที่ทำงานจะปรากฏใน ~/arms ของมัน

🇨🇳 中文

完整的设置脚本:make_workspace.py。这是给你真正的工作区用的文件。它建出和做法 1 一样的文件夹,却不用重复写,还给每个文件夹写好 README.md。FOLDERS 是一个字典:写在花括号里的一对对 key: value,每个文件夹的名字配上它的 README.md 要写的任务。AGENTS_START 和 WELCOME 是写在三个引号之间的字符串,所以可以写很多行;它们的名字用大写,是因为程序运行时它们从不改变。for name, job in FOLDERS.items(): 是一个 for 循环:它依次拿出每个文件夹的名字和任务,每拿一对,就把下面缩进的那几行运行一次。def make(path, text): 是一个函数,一个有名字、能反复使用的食谱:如果文件已经在了,它就交回 "keep ",什么也不写,所以再运行一次程序也不会丢掉你改过的东西。还有两样。main() 装着所有步骤,if __name__ == "__main__": 只在你运行这个文件时才运行它,别的文件向它借东西时不运行。sys.argv 是你在 python3 后面输入的那些词的列表:如果你说了一个文件夹的名字,工作区就建在那里;如果没说,就建在你所在的文件夹里。把它保存在 ~/arms 里并在那里运行。也按一下下面的 Run:这一页有一台假装的 Mac,工作区会出现在它的 ~/arms 里。

~/arms/make_workspace.py

"""make_workspace.py - make the folders of your Codex workspace, each with a README.

    python3 make_workspace.py            in the folder you are in
    python3 make_workspace.py ~/arms     in that folder

A workspace is one folder that holds everything Codex works on for you. Each
folder inside it has one job, and its README.md says that job in its first line.
Codex reads that line, and so does make_map.py in step 9. Nothing that is
already there is changed, so the file is safe to run again.
"""
import pathlib
import sys

# Each folder, and the first line of its README.md.
FOLDERS = {
    "school": "Homework and class notes, one folder per subject.",
    "projects": "Programs I am building, one folder per project.",
    "notes": "Fixes for later: one file per skill, written by note.py.",
    "log": "What each Codex run cost, written by ask.py.",
    "artifacts": "Every web page Codex made for me, opened by inbox.py.",
}

# The workspace's AGENTS.md as it starts. make_map.py fills in the map.
AGENTS_START = """# AGENTS.md: how to work in this workspace

This folder is my workspace. Read the map first, then open only what you need.

## Rules

- Answer in short, plain English. I am a student.
- Save every web page you make in artifacts/, named like 2026-10-21-topic.html.
- Never delete a file. Move it to a folder named old/ instead.

## Map

<!-- map start -->
<!-- map end -->
"""

# A first page for artifacts/, so that inbox.py in step 13 has one to show.
WELCOME = """<!doctype html>
<title>Welcome</title>
<h1>My artifacts inbox</h1>
<p>Codex puts every page it makes for me in this folder.</p>
"""


def make(path, text):
    # Write a new file, but never over one that is already there.
    if path.exists():
        return "keep  "
    path.write_text(text, encoding="utf-8")
    return "create"


def main():
    # sys.argv[1] is the folder you named after the file name, if you named one.
    if len(sys.argv) > 1:
        home = pathlib.Path(sys.argv[1]).expanduser()
    else:
        home = pathlib.Path.cwd()
    home.mkdir(parents=True, exist_ok=True)
    for name, job in FOLDERS.items():
        folder = home / name
        folder.mkdir(exist_ok=True)
        state = make(folder / "README.md", "# " + name + "\n\n" + job + "\n")
        print(state, name + "/README.md")
    print(make(home / "AGENTS.md", AGENTS_START), "AGENTS.md")
    print(make(home / "artifacts" / "welcome.html", WELCOME), "artifacts/welcome.html")


if __name__ == "__main__":
    main()
python3 make_workspace.py

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

create school/README.md
create projects/README.md
create notes/README.md
create log/README.md
create artifacts/README.md
create AGENTS.md
create artifacts/welcome.html

🧠 Quiz 1: what will this print?🧠 แบบทดสอบ 1: มันจะพิมพ์อะไร🧠 小测验 1:它会打印什么?

Pick an answer. Then the page shows which one is right and why, and you can run the program yourself.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูกและเพราะอะไร และคุณรันโปรแกรมเองได้选一个答案。然后这一页会告诉你哪个是对的、为什么,你也可以自己运行这个程序。

# Quiz 1: what will this print?
FOLDERS = {"school": "Homework", "log": "Costs"}

for name, job in FOLDERS.items():
    print(name + "/README.md")

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

.items() hands the loop each key with its value. The first name, name, gets the key, the folder; job gets the value, which this loop never prints..items() ส่งคีย์แต่ละตัวพร้อมค่าของมันให้ลูป ชื่อแรก name ได้คีย์ คือชื่อโฟลเดอร์ ส่วน job ได้ค่า ซึ่งลูปนี้ไม่ได้พิมพ์ออกมาเลย.items() 把每个键和它的值一起交给循环。第一个名字 name 拿到键,也就是文件夹;job 拿到值,而这个循环从来没有打印它。

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

school/README.md
log/README.md

SPart 1 — Skills · ส่วนที่ 1 — สกิล · 第 1 部分 —— 技能

🇬🇧 English

A skill is a folder with a file named SKILL.md in it. The file starts with a name and a description, and the rest is instructions in plain words. Codex looks for skills in .agents/skills inside your workspace, and in ~/.agents/skills for skills you want everywhere. At the start of a chat it reads only each skill's name and description, which keeps that cheap. It reads the rest of a SKILL.md when the job matches the description, or when you call the skill by name: type $ and the skill's name in your message. The three steps here are three kinds of skill: one that holds knowledge, one that runs a program, and a rule for which model does the work.

🇹🇭 ไทย

สกิลคือโฟลเดอร์ที่มีไฟล์ชื่อ SKILL.md อยู่ข้างใน ไฟล์เริ่มด้วย name และ description ส่วนที่เหลือเป็นคำสั่งด้วยภาษาธรรมดา Codex หาสกิลใน .agents/skills ภายในพื้นที่ทำงานของคุณ และใน ~/.agents/skills สำหรับสกิลที่คุณอยากใช้ได้ทุกที่ ตอนเริ่มแชต มันอ่านเพียงชื่อและคำอธิบายของแต่ละสกิล ซึ่งทำให้ส่วนนั้นถูก มันอ่าน SKILL.md ที่เหลือเมื่องานตรงกับคำอธิบาย หรือเมื่อคุณเรียกสกิลด้วยชื่อ: พิมพ์ $ ตามด้วยชื่อสกิลในข้อความของคุณ สามขั้นตอนตรงนี้คือสกิลสามแบบ: แบบที่เก็บความรู้ แบบที่รันโปรแกรม และกฎว่าโมเดลไหนทำงาน

🇨🇳 中文

技能就是一个文件夹,里面有一个名叫 SKILL.md 的文件。这个文件以 name 和 description 开头,其余部分是用平常话写的指令。Codex 在你工作区里的 .agents/skills 中找技能,也在 ~/.agents/skills 中找你想在哪里都能用的技能。对话开始时,它只读每个技能的名字和说明,这样开销很小。当任务和说明对得上,或者你点名叫这个技能时,它才读 SKILL.md 的其余部分:在你的消息里输入 $ 和技能的名字。这里的三步是三种技能:一种装着知识,一种运行程序,还有一条决定由哪个模型干活的规则。

2 A skill that knows how you write · สกิลที่รู้ว่าคุณเขียนอย่างไร · 一个知道你怎么写作的技能

🇬🇧 English

Most people think of a skill as a list of steps. It can also be a store of knowledge: here, how you write. If you write the same kinds of things every week, such as messages to teachers, posts for a class group or video titles, put a few real examples of your own in one skill, and Codex will draft in your voice instead of its own. Replace the examples in this file with yours.

The file has two more sections, and both come from the video. Wild cards: examples make Codex copy them, and then it never surprises you. (In machine learning this is called overfitting.) So the skill asks for 2 ideas in every 10 that ignore your examples on purpose. When you like one, you add it to the examples. Lessons: when you correct Codex, it writes one line there, so the skill gets a little better each time you use it.

🇹🇭 ไทย

คนส่วนใหญ่คิดว่าสกิลคือรายการขั้นตอน แต่มันเป็นคลังความรู้ได้ด้วย: ในที่นี้คือคุณเขียนอย่างไร ถ้าคุณเขียนสิ่งแบบเดิม ๆ ทุกสัปดาห์ เช่น ข้อความถึงครู โพสต์ในกลุ่มห้อง หรือชื่อวิดีโอ ให้ใส่ตัวอย่างจริงของคุณเองสักสองสามอันไว้ในสกิลเดียว แล้ว Codex จะร่างด้วยน้ำเสียงของคุณแทนน้ำเสียงของมันเอง เปลี่ยนตัวอย่างในไฟล์นี้เป็นของคุณ

ไฟล์นี้มีอีกสองส่วน และทั้งสองมาจากวิดีโอ ไวลด์การ์ด: ตัวอย่างทำให้ Codex ลอกตาม แล้วมันก็ไม่เคยทำให้คุณประหลาดใจ (ในการเรียนรู้ของเครื่อง เรื่องนี้เรียกว่า overfitting) สกิลจึงขอไอเดีย 2 ใน 10 ที่ตั้งใจไม่สนใจตัวอย่างของคุณ เมื่อคุณชอบอันไหน ก็เพิ่มมันเข้าไปในตัวอย่าง บทเรียน: เมื่อคุณแก้ Codex มันจะเขียนหนึ่งบรรทัดไว้ตรงนั้น สกิลจึงดีขึ้นทีละนิดทุกครั้งที่คุณใช้

🇨🇳 中文

大多数人把技能想成一串步骤。它也可以是一个知识库:在这里,就是你怎么写作。如果你每周都写同样几类东西,比如给老师的消息、班级群里的帖子或视频标题,就把你自己的几个真实例子放进一个技能里,Codex 就会用你的口吻而不是它自己的口吻来起草。把这个文件里的例子换成你自己的。

这个文件还有两节,都来自那个视频。外卡:例子会让 Codex 照着抄,它也就再也不会给你惊喜。(在机器学习里这叫过拟合,overfitting。)所以这个技能要求每 10 个点子里有 2 个故意不理你的例子。你喜欢哪一个,就把它加进例子里。教训:你纠正 Codex 时,它会在那里写一行,于是你每用一次,这个技能就好一点。

mkdir -p .agents/skills/my-voice
vim .agents/skills/my-voice/SKILL.md

~/arms/.agents/skills/my-voice/SKILL.md

---
name: my-voice
description: How I write, with examples. Use when asked to write or draft anything in my name - a message to a teacher, a post, a title, a short email - or to suggest ideas for titles or posts.
---

# How I write

Use the examples below to write the way I do. Do not copy them word for word.

## My rules

- Short sentences. One idea in each.
- Plain words. If a short word works, use it.
- Polite to teachers. Friendly to friends. No slang to teachers.
- Never more than one exclamation mark in a whole message.

## Examples

A message to a teacher:

> Good morning, Kru Nari. I was sick yesterday, so I missed the test.
> Can I take it on Friday? Thank you.

A post for my class group:

> Our science project is about the river at Mae Ping. We measure the water
> every Monday. Come and help if you are free.

A title for a video:

> How I fixed my bike chain in ten minutes

## Wild cards

When I ask for ideas (titles, posts, names), make most of them from my
examples, and make 2 of every 10 a wild card: an idea that does not follow my
examples at all. Write WILD CARD before each one. If I like a wild card, I will
ask you to add it to the examples above.

## Lessons

When I correct how you wrote something, add one short line here that says what
to do next time, with the date. Do not change anything else in this file.

not run by us: type this in a Codex chat in ~/armsเราไม่ได้รันเอง: พิมพ์สิ่งนี้ในแชต Codex ที่ ~/arms我们没有运行过:在 ~/arms 的 Codex 对话里输入这一行

$my-voice give me 10 titles for a video about cleaning a bike chain

🇬🇧 English

Start Codex with codex in ~/arms, then type the line above. Eight of the ten titles should sound like your example title, and two should be marked WILD CARD. If Codex wrote something you would never write, tell it so in plain words, and then look at the Lessons section of the file: git diff shows the line it added.

🇹🇭 ไทย

เริ่ม Codex ด้วย codex ใน ~/arms แล้วพิมพ์บรรทัดข้างบน แปดในสิบชื่อควรฟังเหมือนชื่อตัวอย่างของคุณ และอีกสองควรมีเครื่องหมาย WILD CARD ถ้า Codex เขียนสิ่งที่คุณไม่มีวันเขียน บอกมันด้วยคำธรรมดา แล้วดูส่วน Lessons ของไฟล์: git diff แสดงบรรทัดที่มันเพิ่ม

🇨🇳 中文

在 ~/arms 里用 codex 启动 Codex,然后输入上面那一行。十个标题里应该有八个听起来像你的例子标题,另外两个应该标着 WILD CARD。如果 Codex 写了你绝不会写的东西,就用平常话告诉它,然后看看文件的 Lessons 一节:git diff 会显示它加的那一行。

🧠 Quiz 2🧠 แบบทดสอบ 2🧠 小测验 2

Pick an answer. Then the page shows which one is right, and why.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูก และเพราะอะไร选一个答案。然后这一页会告诉你哪个是对的,以及为什么。

When does Codex read the whole SKILL.md of a skill?Codex อ่าน SKILL.md ของสกิลทั้งไฟล์เมื่อไรCodex 什么时候会读一个技能的整个 SKILL.md?

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

At the start of a chat Codex reads only each skill's name and description, which costs few tokens. It reads the rest when the job needs it.ตอนเริ่มแชต Codex อ่านเพียงชื่อและคำอธิบายของแต่ละสกิล ซึ่งใช้โทเค็นน้อย มันอ่านส่วนที่เหลือเมื่องานต้องใช้对话开始时,Codex 只读每个技能的名字和说明,花的词元很少。等任务需要时,它才读其余部分。

3 A skill that runs a Python checker · สกิลที่รันตัวตรวจภาษา Python · 一个运行 Python 检查程序的技能

🇬🇧 English

Some checks need no model at all. A word written twice in a row, or a sentence of forty words, can be found by a few lines of Python, which give the same answer every time and cost nothing. So this skill brings its own program, check.py, and tells Codex to run it and work from what it prints, instead of reading the whole text itself. Codex then reads five short lines, not your whole essay, and spends its effort only on fixing what the checker found.

Save both files in the skill's folder, and a text to try it on in school/. This page's own text is homework.txt; it has one mistake of each kind.

🇹🇭 ไทย

การตรวจบางอย่างไม่ต้องใช้โมเดลเลย คำที่เขียนซ้ำติดกันสองครั้ง หรือประโยคที่ยาวสี่สิบคำ หาได้ด้วย Python ไม่กี่บรรทัด ซึ่งให้คำตอบเดิมทุกครั้งและไม่มีค่าใช้จ่าย สกิลนี้จึงนำโปรแกรมของมันเองมาด้วย คือ check.py และบอก Codex ให้รันมันแล้วทำงานจากสิ่งที่มันพิมพ์ แทนที่จะอ่านข้อความทั้งหมดเอง แล้ว Codex ก็อ่านแค่ห้าบรรทัดสั้น ๆ ไม่ใช่เรียงความทั้งเรื่องของคุณ และใช้แรงไปกับการแก้สิ่งที่ตัวตรวจหาเจอเท่านั้น

บันทึกทั้งสองไฟล์ไว้ในโฟลเดอร์ของสกิล และข้อความสำหรับลองไว้ใน school/ ข้อความของหน้านี้เองคือ homework.txt มันมีที่ผิดอย่างละหนึ่งแห่ง

🇨🇳 中文

有些检查根本不需要模型。一个词连写了两遍,或者一个四十个词的句子,几行 Python 就能找出来,每次答案都一样,而且不花钱。所以这个技能自带一个程序 check.py,并告诉 Codex 运行它、根据它打印的内容来工作,而不是自己去读整篇文字。这样 Codex 读的是五行短短的输出,而不是你的整篇作文,力气只花在修改检查程序找到的问题上。

把两个文件都保存在技能的文件夹里,再在 school/ 里放一篇用来试的文字。这一页自己的文字是 homework.txt,两种错误各有一处。

mkdir -p .agents/skills/text-check

~/arms/.agents/skills/text-check/SKILL.md

---
name: text-check
description: Check a piece of my writing for words written twice and for sentences that are too long. Use when asked to "check my text", "check my homework" or "proofread" a .txt file.
---

# Check a text

Do not read the whole file yourself. Run the checker that comes with this skill,
and work from what it prints:

    python3 .agents/skills/text-check/check.py FILE

Then answer in this shape:

1. One line: how many words, sentences and problems the checker found.
2. Each problem, with a corrected version of that sentence.
3. Nothing else. Do not rewrite the parts the checker did not flag.

If the checker found no problems, say so in one line and stop.

## Lessons

When I correct how you did this check, add one short line here that says what
to do next time, with the date.

~/arms/.agents/skills/text-check/check.py

"""check.py - check a piece of writing for three simple mistakes, with no AI at all.

    python3 check.py homework.txt

It prints how many words and sentences the text has, every word written twice
in a row ("the the"), and every sentence longer than LONG words. A program like
this gives the same answer every time, costs nothing, and prints only a few
lines, so Codex reads a few lines instead of the whole text.
"""
import re
import sys

# A sentence with more words than this is called long.
LONG = 25


def sentences_of(text):
    # A sentence ends at . ! or ? and the spaces after it.
    parts = re.split(r"(?<=[.!?])\s+", text.strip())
    return [p for p in parts if p]


def main():
    text = open(sys.argv[1], encoding="utf-8").read()
    words = re.findall(r"[A-Za-z']+", text)
    sentences = sentences_of(text)
    print("words:", len(words))
    print("sentences:", len(sentences))
    problems = 0
    # zip pairs each word with the one after it.
    for first, second in zip(words, words[1:]):
        if first.lower() == second.lower():
            print("twice in a row:", first, second)
            problems += 1
    for number, sentence in enumerate(sentences, start=1):
        count = len(sentence.split())
        if count > LONG:
            print("long sentence", number, "has", count, "words:", sentence[:40] + "...")
            problems += 1
    print("problems:", problems)


if __name__ == "__main__":
    main()

~/arms/school/homework.txt

My village is near the Mae Ping river. Every morning I walk to school with my brother and we cross the the old bridge. In the rainy season the water comes up very high and the people who live near the river have to move their rice and their motorbikes and their chickens to the temple on the hill until the water goes down again. I like the dry season better.

run the checker yourself first, the way Codex willรันตัวตรวจเองก่อน แบบที่ Codex จะรัน先自己运行一下检查程序,就像 Codex 会做的那样

python3 .agents/skills/text-check/check.py school/homework.txt

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

words: 71
sentences: 4
twice in a row: the the
long sentence 3 has 41 words: In the rainy season the water comes up v...
problems: 2

🇬🇧 English

The checker found the two mistakes: the the, and sentence 3, which has 41 words. Now ask Codex. It should run the same command, report the same two problems, and write a shorter version of sentence 3, and nothing else.

🇹🇭 ไทย

ตัวตรวจหาที่ผิดทั้งสองแห่งเจอ: the the และประโยคที่ 3 ซึ่งมี 41 คำ ทีนี้ถาม Codex มันควรรันคำสั่งเดียวกัน รายงานปัญหาสองข้อเดียวกัน และเขียนประโยคที่ 3 ให้สั้นลง แล้วไม่ทำอะไรอื่น

🇨🇳 中文

检查程序找到了两处错误:the the,以及第 3 句,它有 41 个词。现在去问 Codex。它应该运行同一条命令,报告同样的两个问题,把第 3 句写短一些,此外什么都不做。

not run by us: type this in a Codex chat in ~/armsเราไม่ได้รันเอง: พิมพ์สิ่งนี้ในแชต Codex ที่ ~/arms我们没有运行过:在 ~/arms 的 Codex 对话里输入这一行

$text-check check school/homework.txt

🧠 Quiz 3: what will this print?🧠 แบบทดสอบ 3: มันจะพิมพ์อะไร🧠 小测验 3:它会打印什么?

Pick an answer. Then the page shows which one is right and why, and you can run the program yourself.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูกและเพราะอะไร และคุณรันโปรแกรมเองได้选一个答案。然后这一页会告诉你哪个是对的、为什么,你也可以自己运行这个程序。

# Quiz 3: what will this print?
sentence = "My village is near the river"
words = sentence.split()
print(len(words))
if len(words) > 5:
    print("long")

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

split() cuts the sentence at its spaces into a list of 6 words, and 6 is more than 5, so the if prints long. check.py counts the words of each sentence the same way.split() ตัดประโยคตรงช่องว่างเป็นลิสต์ของคำ 6 คำ และ 6 มากกว่า 5 ดังนั้น if จึงพิมพ์ long check.py นับคำของแต่ละประโยคด้วยวิธีเดียวกันsplit() 在空格处把句子切成一个有 6 个词的列表,6 大于 5,所以 if 打印 long。check.py 也是这样数每个句子的词的。

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

6
long

4 route.py: a small model for small jobs · route.py: โมเดลเล็กสำหรับงานเล็ก · route.py:小任务交给小模型

🇬🇧 English

Codex can use several models. A big one, gpt-6.1-sol, is the one OpenAI suggests for hard coding work. A small one, gpt-6-luna, is OpenAI's most efficient model for focused, high-volume tasks: summaries, lists, small checks. It is faster and uses less of your plan. Many jobs never need the big model, and the skill of step 3 is one: the checker already did the hard part.

The video sends each job to a model with a second, very fast model that decides. This page uses a rule you can read instead: if the job's first word is one of a short list, such as check or list, it goes to the small model with low effort; anything else goes to the big one. codex exec runs Codex for one job without a chat, -m picks the model, and -c changes one setting for that run only. Without --go, the file only prints the command it would run.

🇹🇭 ไทย

Codex ใช้โมเดลได้หลายตัว ตัวใหญ่ gpt-6.1-sol คือตัวที่ OpenAI แนะนำสำหรับงานโค้ดยาก ๆ ตัวเล็ก gpt-6-luna คือ most efficient model for focused, high-volume tasks (โมเดลที่ประหยัดที่สุดสำหรับงานเฉพาะเรื่องที่มีจำนวนมาก) ของ OpenAI: สรุป รายการ การตรวจเล็ก ๆ มันเร็วกว่าและใช้แผนของคุณน้อยกว่า งานจำนวนมากไม่เคยต้องใช้โมเดลใหญ่ และสกิลของขั้นตอนที่ 3 ก็เป็นงานหนึ่งในนั้น: ตัวตรวจทำส่วนที่ยากไปแล้ว

วิดีโอส่งงานแต่ละงานไปยังโมเดลหนึ่งด้วยโมเดลตัวที่สองที่เร็วมากเป็นผู้ตัดสิน หน้านี้ใช้กฎที่คุณอ่านได้แทน: ถ้าคำแรกของงานเป็นหนึ่งในรายการสั้น ๆ เช่น check หรือ list มันไปที่โมเดลเล็กโดยคิดน้อย; อย่างอื่นไปที่ตัวใหญ่ codex exec รัน Codex สำหรับงานเดียวโดยไม่มีแชต -m เลือกโมเดล และ -c เปลี่ยนการตั้งค่าหนึ่งอย่างสำหรับการรันครั้งนั้นเท่านั้น ถ้าไม่มี --go ไฟล์นี้แค่พิมพ์คำสั่งที่มันจะรัน

🇨🇳 中文

Codex 可以用好几个模型。大的那个 gpt-6.1-sol,是 OpenAI 推荐用于困难编程工作的。小的那个 gpt-6-luna,是 OpenAI 所说的 most efficient model for focused, high-volume tasks(处理专注、大量任务时最省的模型):摘要、清单、小检查。它更快,占用你方案的额度也更少。很多任务从来不需要大模型,第 3 步的技能就是其中之一:难的部分检查程序已经做完了。

视频里,每个任务交给哪个模型,由第二个非常快的模型来决定。这一页改用一条你读得懂的规则:如果任务的第一个词在一个短清单里,比如 check 或 list,就交给小模型并少想一点;其余的都交给大模型。codex exec 不开对话、只为一个任务运行 Codex,-m 选模型,-c 只为这一次运行改一项设置。没有 --go 时,这个文件只打印它将要运行的命令。

~/arms/route.py

"""route.py - send each job to the model it needs: a small one for easy jobs.

    python3 route.py "check homework.txt"          print the Codex command it would run
    python3 route.py --go "check homework.txt"     run that command

A big model costs more and is slower. Many jobs do not need it: checking a
text, making a list, a summary. This file looks at the first word of the job
and picks a model. The rule is simple on purpose: you can read it, and you can
change it when it picks wrong.
"""
import subprocess
import sys

BIG = "gpt-6.1-sol"
SMALL = "gpt-6-luna"
# A job that starts with one of these words goes to the small model.
SMALL_JOBS = ["check", "list", "summarize", "summarise", "rename", "translate", "spell"]


def pick_model(job):
    first_word = job.split()[0].lower()
    if first_word in SMALL_JOBS:
        return SMALL
    return BIG


def command_for(job):
    model = pick_model(job)
    command = ["codex", "exec", "-m", model]
    if model == SMALL:
        # -c changes one setting for this run only: here, less thinking.
        command += ["-c", "model_reasoning_effort=low"]
    return command + [job]


def main():
    go = "--go" in sys.argv
    jobs = [word for word in sys.argv[1:] if word != "--go"]
    for job in jobs:
        command = command_for(job)
        print(pick_model(job), "<-", job)
        if go:
            # stdin=DEVNULL: with no keyboard behind it,
            # codex exec would wait for more input.
            subprocess.run(command, stdin=subprocess.DEVNULL)
        else:
            print("   ", " ".join(command[:-1]), '"' + job + '"')


if __name__ == "__main__":
    main()
python3 route.py "check school/homework.txt" "make a quiz from my science notes" \
  "list my projects" "fix the bug in projects/game/main.py"

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

gpt-6-luna <- check school/homework.txt
    codex exec -m gpt-6-luna -c model_reasoning_effort=low "check school/homework.txt"
gpt-6.1-sol <- make a quiz from my science notes
    codex exec -m gpt-6.1-sol "make a quiz from my science notes"
gpt-6-luna <- list my projects
    codex exec -m gpt-6-luna -c model_reasoning_effort=low "list my projects"
gpt-6.1-sol <- fix the bug in projects/game/main.py
    codex exec -m gpt-6.1-sol "fix the bug in projects/game/main.py"

🇬🇧 English

Two of the four jobs went to the small model. A rule this simple will sometimes be wrong: check why my game crashes starts with check, and is a hard job. When it picks wrong, change SMALL_JOBS, or type the job so that it starts with a different word. The test the video gives is a good one: if you never notice that a job went to the small model, the rule is right.

🇹🇭 ไทย

สองในสี่งานไปที่โมเดลเล็ก กฎที่ง่ายขนาดนี้บางครั้งจะผิด: check why my game crashes ขึ้นต้นด้วย check แต่เป็นงานยาก เมื่อมันเลือกผิด ให้แก้ SMALL_JOBS หรือพิมพ์งานให้ขึ้นต้นด้วยคำอื่น บททดสอบที่วิดีโอให้ไว้เป็นบททดสอบที่ดี: ถ้าคุณไม่เคยสังเกตว่างานหนึ่งไปที่โมเดลเล็ก กฎนั้นก็ถูกแล้ว

🇨🇳 中文

四个任务里有两个交给了小模型。这么简单的规则有时会错:check why my game crashes 以 check 开头,却是个难任务。它选错时,就改 SMALL_JOBS,或者把任务换个词开头。视频给的检验方法很好:如果你从来没察觉某个任务交给了小模型,这条规则就是对的。

not run by us: this one starts Codexเราไม่ได้รันเอง: คำสั่งนี้เริ่ม Codex我们没有运行过:这一条会启动 Codex

python3 route.py --go "check school/homework.txt"

🧠 Quiz 4: what will this print?🧠 แบบทดสอบ 4: มันจะพิมพ์อะไร🧠 小测验 4:它会打印什么?

Pick an answer. Then the page shows which one is right and why, and you can run the program yourself.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูกและเพราะอะไร และคุณรันโปรแกรมเองได้选一个答案。然后这一页会告诉你哪个是对的、为什么,你也可以自己运行这个程序。

# Quiz 4: what will this print?
SMALL_JOBS = ["check", "list", "summarize"]
job = "Fix the bug in game.py"

first_word = job.split()[0].lower()
if first_word in SMALL_JOBS:
    print("small model")
else:
    print("big model")

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

The first word, after .lower(), is fix, and in asks whether it is in the list. It is not, so the job goes to the big model. route.py decides the same way.คำแรกหลัง .lower() คือ fix และ in ถามว่ามันอยู่ในลิสต์หรือไม่ มันไม่อยู่ งานจึงไปที่โมเดลใหญ่ route.py ตัดสินใจแบบเดียวกัน第一个词经过 .lower() 后是 fix,in 问它在不在列表里。它不在,所以这个任务交给大模型。route.py 也是这样决定的。

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

big model

RPart 2 — Routines · ส่วนที่ 2 — งานประจำ · 第 2 部分 —— 例行任务

🇬🇧 English

A routine is a job that runs on a schedule, without you. There are two kinds. An active routine hands you something to read every time, such as a summary of the week's notes. A quiet routine runs in the background and speaks only when it finds something. Quiet routines matter more than they seem: ten active routines are ten things to read every day, and soon you read none of them. Every routine also costs tokens each time it asks Codex, so this part ends with a log of what each one cost, a weekly cap, and a report.

🇹🇭 ไทย

งานประจำคืองานที่รันตามตารางเวลาโดยไม่ต้องมีคุณ มีสองแบบ งานประจำแบบแอ็กทีฟส่งสิ่งที่ต้องอ่านให้คุณทุกครั้ง เช่น สรุปโน้ตของสัปดาห์ งานประจำแบบเงียบรันอยู่เบื้องหลังและพูดเฉพาะเมื่อเจออะไร งานประจำแบบเงียบสำคัญกว่าที่เห็น: งานประจำแบบแอ็กทีฟสิบงานคือสิบอย่างที่ต้องอ่านทุกวัน แล้วไม่นานคุณก็ไม่อ่านสักอย่าง งานประจำทุกงานยังใช้โทเค็นทุกครั้งที่ถาม Codex ส่วนนี้จึงจบด้วยบันทึกว่าแต่ละงานใช้ไปเท่าไร เพดานรายสัปดาห์ และรายงาน

🇨🇳 中文

例行任务是按时间表、不需要你在场就会运行的任务。它分两种。主动型例行任务每次都交给你一样要读的东西,比如这一周笔记的摘要。安静型例行任务在后台运行,只有发现了什么才开口。安静型比看起来更重要:十个主动型例行任务,就是每天要读的十样东西,没多久你就一样也不读了。每个例行任务每次问 Codex 也都要花词元,所以这一部分最后是一份记录每个任务花费的日志、一个每周上限和一份报告。

5 quiet_check.py: a routine that stays quiet · quiet_check.py: งานประจำที่อยู่เงียบ ๆ · quiet_check.py:一个保持安静的例行任务

🇬🇧 English

This routine looks for big files in a folder, such as ~/Downloads, where old installers pile up. The looking is done by Python, which costs nothing. Codex is asked only when Python found something, and only with --ask. So on a normal day this routine uses no tokens and prints nothing at all, and nothing printed means nothing to read.

🇹🇭 ไทย

งานประจำนี้หาไฟล์ใหญ่ในโฟลเดอร์หนึ่ง เช่น ~/Downloads ที่ตัวติดตั้งเก่า ๆ กองพะเนิน การหาทำโดย Python ซึ่งไม่มีค่าใช้จ่าย Codex ถูกถามเฉพาะเมื่อ Python เจออะไร และเฉพาะเมื่อมี --ask ดังนั้นในวันปกติ งานประจำนี้ไม่ใช้โทเค็นเลยและไม่พิมพ์อะไรเลย และการไม่พิมพ์อะไรก็หมายความว่าไม่มีอะไรต้องอ่าน

🇨🇳 中文

这个例行任务在一个文件夹里找大文件,比如旧安装包越堆越多的 ~/Downloads。找的工作由 Python 来做,不花钱。只有 Python 找到了东西、并且带了 --ask 时,才会去问 Codex。所以平常日子里,这个例行任务一个词元也不用,什么也不打印,而什么都没打印,就是什么都不用读。

~/arms/quiet_check.py

"""quiet_check.py - a quiet routine: say nothing unless a folder holds a big file.

    python3 quiet_check.py ~/Downloads 500           files over 500 MB, if any
    python3 quiet_check.py ~/Downloads 500 --ask     if there are, ask Codex about them

Python does the looking, and looking costs nothing. Codex is asked only when
there is something to ask about, so on most days this routine uses no tokens
and prints nothing at all. Nothing prints means nothing to read.
"""
import pathlib
import subprocess
import sys

# 1 MB is 1,000,000 bytes, the way a Mac's Finder counts.
MB = 1_000_000


def big_files(folder, limit_mb):
    found = []
    # rglob("*") walks into every folder inside this one, too.
    for path in folder.rglob("*"):
        if path.is_file() and path.stat().st_size > limit_mb * MB:
            found.append(path)
    return found


def main():
    folder = pathlib.Path(sys.argv[1]).expanduser()
    limit_mb = float(sys.argv[2])
    found = big_files(folder, limit_mb)
    if not found:
        return
    lines = []
    for path in found:
        size = path.stat().st_size / MB
        lines.append(f"{size:8.1f} MB  {path.relative_to(folder).as_posix()}")
    print("\n".join(lines))
    if "--ask" in sys.argv:
        job = ("These files in " + str(folder) + " are big. For each one, say in one "
               "line whether I can probably delete it, and why. Do not delete anything.\n"
               + "\n".join(lines))
        command = ["codex", "exec", "-m", "gpt-6-luna", job]
        # stdin=DEVNULL: with no keyboard behind it,
        # codex exec would wait for more input.
        subprocess.run(command, stdin=subprocess.DEVNULL)


if __name__ == "__main__":
    main()

🇬🇧 English

We ran it twice on a folder that stood in for ~/Downloads, with two files in it: a song of 2 MB and an old installer of 700 MB. First with a limit of 1000 MB, then with 500 MB.

🇹🇭 ไทย

เรารันมันสองครั้งกับโฟลเดอร์ที่ใช้แทน ~/Downloads ซึ่งมีสองไฟล์ในนั้น: เพลงขนาด 2 MB และตัวติดตั้งเก่าขนาด 700 MB ครั้งแรกด้วยเพดาน 1000 MB แล้วด้วย 500 MB

🇨🇳 中文

我们拿一个代替 ~/Downloads 的文件夹运行了两次,里面有两个文件:一首 2 MB 的歌和一个 700 MB 的旧安装包。第一次限度是 1000 MB,第二次是 500 MB。

python3 quiet_check.py ~/Downloads 1000

what it printed: nothingสิ่งที่มันพิมพ์ออกมา: ไม่มีอะไรเลย它打印的内容:什么也没有

python3 quiet_check.py ~/Downloads 500

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

   700.0 MB  old-game-installer.dmg

not run by us: with --ask it starts Codex, which says what each file probably isเราไม่ได้รันเอง: เมื่อมี --ask มันจะเริ่ม Codex ซึ่งบอกว่าแต่ละไฟล์น่าจะเป็นอะไร我们没有运行过:带上 --ask 它会启动 Codex,由 Codex 说出每个文件大概是什么

python3 quiet_check.py ~/Downloads 500 --ask
WHY stdin=subprocess.DEVNULL When codex exec is started by another program, it checks whether there is more to read on its input and waits for it. A routine has no keyboard, so it would wait for ever. stdin=subprocess.DEVNULL gives it an empty input, which ends at once. The same line is in route.py and ask.py.เมื่อ codex exec ถูกโปรแกรมอื่นเริ่ม มันจะตรวจว่ายังมีอะไรให้อ่านจากอินพุตอีกไหมและรอ งานประจำไม่มีคีย์บอร์ด มันจึงจะรอไปตลอดกาล stdin=subprocess.DEVNULL ให้อินพุตว่างแก่มัน ซึ่งจบทันที บรรทัดเดียวกันนี้อยู่ใน route.py และ ask.py ด้วย当 codex exec 由另一个程序启动时,它会检查输入里还有没有要读的东西,并等着。例行任务没有键盘,所以它会一直等下去。stdin=subprocess.DEVNULL 给它一个空的输入,立刻就结束。route.py 和 ask.py 里也有同样这一行。

🧠 Quiz 5: what will this print?🧠 แบบทดสอบ 5: มันจะพิมพ์อะไร🧠 小测验 5:它会打印什么?

Pick an answer. Then the page shows which one is right and why, and you can run the program yourself.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูกและเพราะอะไร และคุณรันโปรแกรมเองได้选一个答案。然后这一页会告诉你哪个是对的、为什么,你也可以自己运行这个程序。

# Quiz 5: what will this print?
sizes = {"song.mp3": 2, "game.dmg": 700, "photo.jpg": 5}

for name, mb in sizes.items():
    if mb > 500:
        print(name)

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

Only a file over 500 MB passes the if; the other two print nothing. A quiet routine says something only when there is something to say.มีเพียงไฟล์ที่ใหญ่กว่า 500 MB ที่ผ่าน if อีกสองไฟล์ไม่พิมพ์อะไรเลย งานประจำที่เงียบจะพูดก็ต่อเมื่อมีเรื่องให้พูดเท่านั้น只有超过 500 MB 的文件才能通过 if,另外两个什么也不打印。安静的例行任务只在有话要说时才开口。

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

game.dmg

6 Every Monday, by itself · ทุกวันจันทร์ โดยไม่ต้องสั่ง · 每个星期一,自己运行

🇬🇧 English

A routine needs a clock. The simplest clock on a Mac is cron, which reads one line per job from a list called the crontab. The line below runs the quiet check at 9 in the morning every Monday. Its five numbers and stars are minute, hour, day of the month, month and day of the week, and 1 is Monday. Whatever the job prints is added to log/quiet.txt, so on a quiet Monday the file does not grow.

A job that cron starts does not see the same PATH as your Terminal, the list of folders where the shell finds commands, so it may not find codex. Ask your Terminal where codex is, and put that folder in a PATH= line at the top of the crontab.

🇹🇭 ไทย

งานประจำต้องมีนาฬิกา นาฬิกาที่ง่ายที่สุดบนแมคคือ cron ซึ่งอ่านหนึ่งบรรทัดต่อหนึ่งงานจากรายการที่เรียกว่า crontab บรรทัดข้างล่างรันการตรวจแบบเงียบตอน 9 โมงเช้าทุกวันจันทร์ ตัวเลขและดอกจันห้าตัวของมันคือ นาที ชั่วโมง วันที่ของเดือน เดือน และวันในสัปดาห์ และ 1 คือวันจันทร์ อะไรที่งานพิมพ์ออกมาจะถูกต่อท้ายใน log/quiet.txt ดังนั้นในวันจันทร์ที่เงียบ ไฟล์นั้นจะไม่โตขึ้น

งานที่ cron เริ่มไม่เห็น PATH แบบเดียวกับ Terminal ของคุณ ซึ่งคือรายการโฟลเดอร์ที่เชลล์ใช้หาคำสั่ง มันจึงอาจหา codex ไม่เจอ ถาม Terminal ของคุณว่า codex อยู่ที่ไหน แล้วใส่โฟลเดอร์นั้นในบรรทัด PATH= ที่ด้านบนของ crontab

🇨🇳 中文

例行任务需要一个钟。Mac 上最简单的钟是 cron,它从一份叫 crontab 的清单里读任务,一个任务一行。下面这一行在每个星期一早上 9 点运行安静检查。它的五个数字和星号依次是分钟、小时、一个月中的哪一天、月份和星期几,1 就是星期一。任务打印的东西都会追加到 log/quiet.txt 里,所以在安静的星期一,这个文件不会变长。

cron 启动的任务看到的 PATH(shell 找命令时查的那串文件夹)和你的终端里的不一样,所以它可能找不到 codex。问问你的终端 codex 在哪里,再把那个文件夹写进 crontab 顶上的一行 PATH= 里。

which codex
crontab -e

not run by us: the two lines to type into the crontabเราไม่ได้รันเอง: สองบรรทัดที่ต้องพิมพ์ลงใน crontab我们没有运行过:要输入 crontab 的两行

PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin
0 9 * * 1 cd ~/arms && python3 quiet_check.py ~/Downloads 500 --ask >> log/quiet.txt 2>&1
CAREFUL macOS guards ~/Downloads, ~/Documents and ~/Desktop. A job started by cron may be refused there, and log/quiet.txt will then say Operation not permitted. If it does, open System Settings, Privacy & Security, Full Disk Access, and add /usr/sbin/cron. Only do this on your own Mac. If you would rather not, point the routine at a folder of your own inside ~/arms. The Mac's own scheduler, launchd, is the other way to do this; step 14 of the Codex on a Mac page sets up a nightly job with it.macOS ปกป้อง ~/Downloads, ~/Documents และ ~/Desktop งานที่ cron เริ่มอาจถูกปฏิเสธที่นั่น แล้ว log/quiet.txt จะบอกว่า Operation not permitted ถ้าเป็นอย่างนั้น ให้เปิด System Settings, Privacy & Security, Full Disk Access แล้วเพิ่ม /usr/sbin/cron ทำอย่างนี้บนแมคของคุณเองเท่านั้น ถ้าไม่อยากทำ ให้ชี้งานประจำไปที่โฟลเดอร์ของคุณเองภายใน ~/arms ตัวจัดตารางของแมคเอง คือ launchd เป็นอีกวิธีหนึ่ง ขั้นตอนที่ 14 ของหน้า Codex บนแมคตั้งงานทุกคืนด้วยมันmacOS 会保护 ~/Downloads、~/Documents 和 ~/Desktop。cron 启动的任务在那里可能被拒绝,这时 log/quiet.txt 里会写着 Operation not permitted。如果是这样,就打开 System Settings(系统设置)、Privacy & Security(隐私与安全性)、Full Disk Access(完全磁盘访问权限),加上 /usr/sbin/cron。只在你自己的 Mac 上这样做。如果你不想这样,就让这个例行任务去看 ~/arms 里你自己的一个文件夹。Mac 自带的调度器 launchd 是另一种做法;在 Mac 上用 Codex 那一页的第 14 步用它设了一个每晚的任务。

🧠 Quiz 6🧠 แบบทดสอบ 6🧠 小测验 6

Pick an answer. Then the page shows which one is right, and why.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูก และเพราะอะไร选一个答案。然后这一页会告诉你哪个是对的,以及为什么。

A crontab line starts 0 9 * * 1. When does its job run?บรรทัดหนึ่งใน crontab ขึ้นต้นด้วย 0 9 * * 1 งานของมันรันเมื่อไรcrontab 里有一行以 0 9 * * 1 开头。它的任务什么时候运行?

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

The five fields are minute, hour, day of the month, month and day of the week: minute 0, hour 9, any day, any month, and day 1 of the week, which is Monday.ห้าช่องคือ นาที ชั่วโมง วันที่ของเดือน เดือน และวันในสัปดาห์: นาที 0 ชั่วโมง 9 วันไหนก็ได้ เดือนไหนก็ได้ และวันที่ 1 ของสัปดาห์ คือวันจันทร์五个位置依次是分钟、小时、一个月中的哪一天、月份和星期几:0 分、9 点、哪天都行、哪月都行,以及一周的第 1 天,也就是星期一。

7 ask.py: write down what each run cost · ask.py: จดว่าแต่ละครั้งที่รันใช้ไปเท่าไร · ask.py:记下每次运行花了多少

🇬🇧 English

After a few weeks you will have several routines, and one of them will cost far more than it is worth. You cannot see that unless every run writes down what it cost. So this file is the one door to Codex for every routine, and for jobs you type yourself. You give it a name, such as hand for your own jobs or digest for a routine, and the job. It runs codex exec --json, which prints what Codex does as one event per line; the event turn.completed holds the tokens the job used. The file adds one line to log/asks.jsonl with the name, the job and the tokens.

It also enforces a cap. A name in CAPS may use only so many tokens in 7 days. Once it reaches that, ask.py skips it and says so, and the routine waits for next week. Choose your caps after a week of the log in step 8, not before.

🇹🇭 ไทย

ผ่านไปไม่กี่สัปดาห์ คุณจะมีงานประจำหลายงาน และหนึ่งในนั้นจะใช้มากกว่าคุณค่าของมันไปมาก คุณมองไม่เห็นเรื่องนี้ ถ้าทุกครั้งที่รันไม่ได้จดว่าใช้ไปเท่าไร ไฟล์นี้จึงเป็นประตูบานเดียวสู่ Codex สำหรับทุกงานประจำ และสำหรับงานที่คุณพิมพ์เอง คุณให้ชื่อหนึ่งแก่มัน เช่น hand สำหรับงานของคุณเอง หรือ digest สำหรับงานประจำ แล้วตามด้วยงาน มันรัน codex exec --json ซึ่งพิมพ์สิ่งที่ Codex ทำเป็นหนึ่งเหตุการณ์ต่อบรรทัด; เหตุการณ์ turn.completed เก็บโทเค็นที่งานใช้ ไฟล์นี้เพิ่มหนึ่งบรรทัดลงใน log/asks.jsonl พร้อมชื่อ งาน และโทเค็น

มันยังบังคับใช้เพดานด้วย ชื่อที่อยู่ใน CAPS ใช้โทเค็นได้เพียงจำนวนหนึ่งใน 7 วัน เมื่อถึงแล้ว ask.py จะข้ามมันและบอกไว้ แล้วงานประจำก็รอสัปดาห์หน้า ตั้งเพดานหลังจากดูบันทึกในขั้นตอนที่ 8 ไปแล้วหนึ่งสัปดาห์ ไม่ใช่ก่อนหน้านั้น

🇨🇳 中文

几周之后,你会有好几个例行任务,其中一个花的会远远超过它的价值。除非每次运行都记下花了多少,否则你看不出来。所以这个文件是每个例行任务、以及你自己输入的任务通往 Codex 的唯一一扇门。你给它一个名字,比如你自己的任务用 hand,某个例行任务用 digest,再给它任务本身。它运行 codex exec --json,这会把 Codex 做的事一行一个事件地打印出来;turn.completed 这个事件里装着任务用掉的词元。这个文件往 log/asks.jsonl 里加一行,写上名字、任务和词元数。

它还执行一个上限。CAPS 里的名字,在 7 天内只能用这么多词元。到了上限,ask.py 就跳过它并说明,这个例行任务就等到下一周。看了第 8 步一周的日志以后再定你的上限,不要提前定。

~/arms/ask.py

"""ask.py - the one door to Codex: run a job, write down what it cost, stop at a cap.

    python3 ask.py hand "summarize school/science/notes.md"     a job you typed
    python3 ask.py quiet "check ~/Downloads for big files"      a routine's job
    python3 ask.py --dry hand "list my projects"                show, run nothing

The first word after ask.py is a name you choose: "hand" for jobs you type
yourself, and one name for each routine. Every run adds one line to
log/asks.jsonl with the name, the job and the tokens Codex used. A name with a
weekly cap in CAPS is not run again once its last 7 days reach the cap.
week.py reads the same file and makes a report from it.
"""
import datetime
import json
import pathlib
import subprocess
import sys

LOG = pathlib.Path("log") / "asks.jsonl"
# Tokens a name may use in 7 days. A name not listed here has no cap.
CAPS = {"quiet": 50_000, "digest": 200_000}
SMALL = "gpt-6-luna"


def used_this_week(name):
    # Add up the tokens of every line with this name from the last 7 days.
    if not LOG.exists():
        return 0
    week_ago = datetime.datetime.now() - datetime.timedelta(days=7)
    total = 0
    for line in LOG.read_text(encoding="utf-8").splitlines():
        run = json.loads(line)
        when = datetime.datetime.fromisoformat(run["when"])
        if run["name"] == name and when > week_ago:
            total += run["input"] + run["output"]
    return total


def usage_of(events):
    # codex exec --json prints one event per line. "turn.completed" holds the tokens.
    for line in events.splitlines():
        if not line.startswith("{"):
            continue
        event = json.loads(line)
        if event.get("type") == "turn.completed":
            return event["usage"]
    return {}


def main():
    dry = "--dry" in sys.argv
    words = [word for word in sys.argv[1:] if word != "--dry"]
    name, job = words[0], words[1]
    used = used_this_week(name)
    cap = CAPS.get(name)
    if cap and used >= cap:
        print(f"skipped: {name} used {used:,} of its {cap:,} tokens this week")
        return
    LOG.parent.mkdir(exist_ok=True)
    answer_file = LOG.parent / ("last_" + name + ".txt")
    # An answer left from an earlier run must not pass for this run's answer.
    answer_file.unlink(missing_ok=True)
    command = ["codex", "exec", "--json", "-m", SMALL, "-o", answer_file.as_posix(), job]
    if dry:
        print(" ".join(command[:-1]), '"' + job + '"')
        print(f"{name} has used {used:,} tokens in the last 7 days")
        return
    # capture_output keeps the events here, so that Python can read the tokens.
    result = subprocess.run(command, capture_output=True, text=True,
                            stdin=subprocess.DEVNULL)
    usage = usage_of(result.stdout)
    run = {
        "when": datetime.datetime.now().isoformat(timespec="seconds"),
        "name": name,
        "job": job,
        "input": usage.get("input_tokens", 0),
        "cached": usage.get("cached_input_tokens", 0),
        "output": usage.get("output_tokens", 0),
    }
    with LOG.open("a", encoding="utf-8") as log:
        log.write(json.dumps(run) + "\n")
    # Codex writes its answer to answer_file. No file means it failed: show why.
    if answer_file.exists():
        print(answer_file.read_text(encoding="utf-8"))
    else:
        print(result.stderr)
    print(f"tokens: {run['input']:,} in ({run['cached']:,} of them cached), "
          f"{run['output']:,} out")


if __name__ == "__main__":
    main()
python3 ask.py --dry hand "check school/homework.txt"

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

codex exec --json -m gpt-6-luna -o log/last_hand.txt "check school/homework.txt"
hand has used 0 tokens in the last 7 days

not run by us: without --dry it starts Codex and writes the logเราไม่ได้รันเอง: ถ้าไม่มี --dry มันจะเริ่ม Codex และเขียนบันทึก我们没有运行过:不带 --dry 时它会启动 Codex 并写日志

python3 ask.py hand "check school/homework.txt"
python3 ask.py digest "summarize the newest notes in school/ in five lines"

🇬🇧 English

To log and cap the quiet routine of step 5 too, change the line in quiet_check.py that builds its command to command = ["python3", "ask.py", "quiet", job]. Its cap is in CAPS already. Every job in ask.py goes to the small model; change SMALL to "gpt-6.1-sol" for a routine that needs more. Of the input tokens, the ones marked cached are ones Codex had read a moment before. They are counted, and on OpenAI's API prices they cost much less than new ones, which is why one job that keeps going is cheaper than ten short jobs that each start from nothing.

🇹🇭 ไทย

ถ้าจะบันทึกและจำกัดเพดานให้งานประจำแบบเงียบของขั้นตอนที่ 5 ด้วย ให้แก้บรรทัดใน quiet_check.py ที่สร้างคำสั่งของมันเป็น command = ["python3", "ask.py", "quiet", job] เพดานของมันอยู่ใน CAPS แล้ว ทุกงานใน ask.py ไปที่โมเดลเล็ก; เปลี่ยน SMALL เป็น "gpt-6.1-sol" สำหรับงานประจำที่ต้องการมากกว่านั้น ในบรรดาโทเค็นขาเข้า ตัวที่มีเครื่องหมาย cached คือตัวที่ Codex เพิ่งอ่านไปเมื่อครู่ มันถูกนับ และตามราคา API ของ OpenAI มันถูกกว่าตัวใหม่มาก นั่นคือเหตุผลที่งานเดียวที่ทำต่อเนื่องถูกกว่างานสั้น ๆ สิบงานที่แต่ละงานเริ่มจากศูนย์

🇨🇳 中文

要让第 5 步的安静例行任务也被记录、也有上限,就把 quiet_check.py 里拼出命令的那一行改成 command = ["python3", "ask.py", "quiet", job]。它的上限已经写在 CAPS 里了。ask.py 里的每个任务都交给小模型;某个例行任务需要更多时,就把 SMALL 改成 "gpt-6.1-sol"。输入词元里,标着 cached 的是 Codex 刚刚读过的。它们也会被计数,但按 OpenAI 的 API 价格,比新的便宜得多,所以一个一直做下去的任务,比十个每次都从零开始的短任务便宜。

🧠 Quiz 7: what will this print?🧠 แบบทดสอบ 7: มันจะพิมพ์อะไร🧠 小测验 7:它会打印什么?

Pick an answer. Then the page shows which one is right and why, and you can run the program yourself.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูกและเพราะอะไร และคุณรันโปรแกรมเองได้选一个答案。然后这一页会告诉你哪个是对的、为什么,你也可以自己运行这个程序。

# Quiz 7: what will this print?
CAPS = {"quiet": 50_000, "digest": 200_000}
used = 60_000
name = "hand"

cap = CAPS.get(name)
if cap and used >= cap:
    print("skipped")
else:
    print("run")

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

CAPS.get(name) gives None for a name that is not in the dictionary, instead of an error, and None counts as false, so the job runs. In ask.py, a name with no cap is never stopped.CAPS.get(name) ให้ None สำหรับชื่อที่ไม่อยู่ในดิกชันนารี แทนที่จะเกิดข้อผิดพลาด และ None นับเป็นเท็จ งานจึงได้รัน ใน ask.py ชื่อที่ไม่มีเพดานจะไม่ถูกหยุดเลย对于不在字典里的名字,CAPS.get(name) 给出 None,而不是报错,None 算作假,所以任务会运行。在 ask.py 里,没有上限的名字从不会被叫停。

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

run

8 week.py: caps and repeats · week.py: เพดานและงานที่ทำซ้ำ · week.py:上限和重复的任务

🇬🇧 English

Once a week, read the log back. The first part of this report adds up each name's tokens over the last 7 days, next to its cap. The second part looks only at the jobs you typed yourself and lists every job you typed 3 times or more. The video's best idea about routines is this one: the best routines are jobs you already do by hand, and you rarely notice that you repeat them. A job on that list is a routine nobody has written yet.

We have no week of real Codex runs, so the page brings an example log, example_asks.jsonl. Its runs are made up, in the shape that ask.py writes, so that the report has something to show. Run week.py with no file name to read your own log.

🇹🇭 ไทย

สัปดาห์ละครั้ง อ่านบันทึกย้อนหลัง ส่วนแรกของรายงานนี้รวมโทเค็นของแต่ละชื่อในช่วง 7 วันที่ผ่านมา ไว้ข้างเพดานของมัน ส่วนที่สองดูเฉพาะงานที่คุณพิมพ์เอง และแสดงทุกงานที่คุณพิมพ์ 3 ครั้งขึ้นไป ไอเดียที่ดีที่สุดของวิดีโอเรื่องงานประจำคือข้อนี้: งานประจำที่ดีที่สุดคืองานที่คุณทำด้วยมืออยู่แล้ว และคุณแทบไม่สังเกตว่าทำซ้ำ งานในรายการนั้นคืองานประจำที่ยังไม่มีใครเขียน

เราไม่มีบันทึกการรัน Codex จริงหนึ่งสัปดาห์ หน้านี้จึงนำบันทึกตัวอย่างมาให้ คือ example_asks.jsonl การรันในนั้นเป็นของสมมุติ ในรูปแบบที่ ask.py เขียน เพื่อให้รายงานมีอะไรให้ดู รัน week.py โดยไม่ใส่ชื่อไฟล์เพื่ออ่านบันทึกของคุณเอง

🇨🇳 中文

每周一次,把日志读回来。这份报告的第一部分,把每个名字过去 7 天的词元加起来,放在它的上限旁边。第二部分只看你自己输入的任务,列出你输入过 3 次或更多的每一个。视频里关于例行任务最好的想法就是这一条:最好的例行任务,是你已经在手动做的事,而你很少发觉自己在重复。那张清单上的任务,就是还没人写出来的例行任务。

我们没有一周真实的 Codex 运行记录,所以这一页带了一份示例日志 example_asks.jsonl。里面的运行是编出来的,格式和 ask.py 写的一样,好让报告有东西可显示。不带文件名运行 week.py,读的就是你自己的日志。

~/arms/week.py

"""week.py - a weekly report from ask.py's log: what each name cost, and what you repeat.

    python3 week.py                         read log/asks.jsonl
    python3 week.py example_asks.jsonl      read another log, such as the page's example

Part 1 adds up the tokens of each name over the last 7 days, next to its cap.
Part 2 looks at the jobs you typed yourself (the name "hand") and lists every
job you typed 3 times or more. A job you keep typing by hand is a routine that
nobody has written yet.
"""
import collections
import datetime
import json
import pathlib
import re
import sys

# The same caps as in ask.py. Change them in both files.
CAPS = {"quiet": 50_000, "digest": 200_000}
REPEATS = 3


def same_job(job):
    # "Summarize notes.md!" and "summarize notes.md" are the same job.
    return re.sub(r"[^a-z0-9 ./_-]", "", job.lower()).strip()


def main():
    log = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else "log/asks.jsonl")
    runs = [json.loads(line) for line in log.read_text(encoding="utf-8").splitlines()]
    # The newest run in the log is "now", so an old log still makes a report.
    now = max(datetime.datetime.fromisoformat(run["when"]) for run in runs)
    week_ago = now - datetime.timedelta(days=7)
    tokens = collections.Counter()
    count = collections.Counter()
    for run in runs:
        if datetime.datetime.fromisoformat(run["when"]) > week_ago:
            tokens[run["name"]] += run["input"] + run["output"]
            count[run["name"]] += 1
    print("LAST 7 DAYS        runs      tokens         cap")
    for name, used in tokens.most_common():
        cap = CAPS.get(name)
        cap_text = f"{cap:,}" if cap else "-"
        flag = "  OVER" if cap and used > cap else ""
        print(f"  {name:<14}{count[name]:>6}{used:>12,}{cap_text:>12}{flag}")
    by_hand = [same_job(run["job"]) for run in runs if run["name"] == "hand"]
    typed = collections.Counter(by_hand)
    print()
    print(f"TYPED BY HAND {REPEATS} TIMES OR MORE: make these routines")
    for job, times in typed.most_common():
        if times >= REPEATS:
            print(f"  {times} x  {job}")


if __name__ == "__main__":
    main()

~/arms/example_asks.jsonl (made up, for the example)(สมมุติขึ้น เพื่อเป็นตัวอย่าง)(编出来的,作为例子)

{"when": "2026-10-14T19:12:40", "name": "hand", "job": "check school/english/essay.txt", "input": 6110, "cached": 0, "output": 420}
{"when": "2026-10-15T07:00:04", "name": "digest", "job": "summarize the newest notes in school/ in five lines", "input": 31200, "cached": 24100, "output": 1900}
{"when": "2026-10-16T07:00:04", "name": "digest", "job": "summarize the newest notes in school/ in five lines", "input": 33950, "cached": 25800, "output": 2100}
{"when": "2026-10-16T20:03:15", "name": "hand", "job": "Check school/english/essay.txt!", "input": 6240, "cached": 3100, "output": 390}
{"when": "2026-10-17T07:00:04", "name": "digest", "job": "summarize the newest notes in school/ in five lines", "input": 29870, "cached": 23300, "output": 1700}
{"when": "2026-10-17T18:44:02", "name": "hand", "job": "make a quiz from school/science/notes.md", "input": 14880, "cached": 0, "output": 2650}
{"when": "2026-10-18T07:00:04", "name": "digest", "job": "summarize the newest notes in school/ in five lines", "input": 35400, "cached": 27700, "output": 2300}
{"when": "2026-10-18T21:30:09", "name": "hand", "job": "check school/english/essay.txt", "input": 6020, "cached": 3050, "output": 410}
{"when": "2026-10-19T07:00:04", "name": "digest", "job": "summarize the newest notes in school/ in five lines", "input": 32010, "cached": 24900, "output": 2000}
{"when": "2026-10-20T07:00:04", "name": "digest", "job": "summarize the newest notes in school/ in five lines", "input": 30660, "cached": 23800, "output": 1800}
{"when": "2026-10-20T09:00:11", "name": "quiet", "job": "These files are big. For each one, say whether I can delete it.", "input": 8420, "cached": 0, "output": 610}
{"when": "2026-10-20T19:55:47", "name": "hand", "job": "list my projects and what is unfinished in each", "input": 11930, "cached": 0, "output": 1340}
{"when": "2026-10-21T07:00:04", "name": "digest", "job": "summarize the newest notes in school/ in five lines", "input": 34120, "cached": 26500, "output": 2200}
{"when": "2026-10-21T20:10:33", "name": "hand", "job": "check school/english/essay.txt", "input": 6180, "cached": 3080, "output": 400}
python3 week.py example_asks.jsonl

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

LAST 7 DAYS        runs      tokens         cap
  digest             7     241,210     200,000  OVER
  hand               5      50,440           -
  quiet              1       9,030      50,000

TYPED BY HAND 3 TIMES OR MORE: make these routines
  4 x  check school/english/essay.txt

🇬🇧 English

Two things to act on. The daily digest used 241,210 tokens in a week, over its cap of 200,000, so from that point ask.py would have skipped it. Ask Codex to make the digest cheaper (fewer notes, a shorter answer, the small model) before you raise the cap. And one job was typed by hand 4 times: checking the same essay. That is a routine waiting to be written, or a skill like the one of step 3.

🇹🇭 ไทย

มีสองเรื่องที่ต้องลงมือ สรุปรายวันใช้ไป 241,210 โทเค็นในหนึ่งสัปดาห์ เกินเพดาน 200,000 ดังนั้นตั้งแต่จุดนั้น ask.py จะข้ามมันไป ขอให้ Codex ทำให้สรุปถูกลง (โน้ตน้อยลง คำตอบสั้นลง ใช้โมเดลเล็ก) ก่อนที่คุณจะเพิ่มเพดาน และมีงานหนึ่งที่พิมพ์ด้วยมือ 4 ครั้ง: ตรวจเรียงความเรื่องเดิม นั่นคืองานประจำที่รอให้เขียน หรือสกิลแบบเดียวกับของขั้นตอนที่ 3

🇨🇳 中文

有两件事要处理。每日摘要一周用了 241,210 个词元,超过了它 200,000 的上限,所以从那时起 ask.py 会跳过它。先请 Codex 把摘要变便宜(少读些笔记、回答短一点、用小模型),再考虑提高上限。还有一个任务被手动输入了 4 次:检查同一篇作文。那是一个等着被写出来的例行任务,或者一个像第 3 步那样的技能。

🧠 Quiz 8: what will this print?🧠 แบบทดสอบ 8: มันจะพิมพ์อะไร🧠 小测验 8:它会打印什么?

Pick an answer. Then the page shows which one is right and why, and you can run the program yourself.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูกและเพราะอะไร และคุณรันโปรแกรมเองได้选一个答案。然后这一页会告诉你哪个是对的、为什么,你也可以自己运行这个程序。

# Quiz 8: what will this print?
import collections

jobs = ["check essay", "list projects", "check essay", "check essay"]
counts = collections.Counter(jobs)
print(counts["check essay"])

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

A Counter counts how many times each thing is in the list, and check essay is there 3 times. week.py uses this to find the job you keep typing by hand.Counter นับว่าแต่ละอย่างอยู่ในลิสต์กี่ครั้ง และ check essay อยู่ในนั้น 3 ครั้ง week.py ใช้สิ่งนี้หางานที่คุณพิมพ์เองซ้ำ ๆCounter 数列表里每样东西出现了几次,check essay 出现了 3 次。week.py 用它找出你一直在手动输入的那件事。

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

3

MPart 3 — Memory · ส่วนที่ 3 — ความจำ · 第 3 部分 —— 记忆

🇬🇧 English

Memory is what Codex knows about you and your work, and how quickly and cheaply it can find it. There are three kinds, and each step here is one. Long-term memory is your files. People call a workspace like yours a second brain, but it is folders and files, and what makes it useful is a map, so that Codex opens the one file it needs. Short-term memory is the one chat you are in, which Codex reads again every time you send a message. And a memory that improves itself is one where a correction is written down the first time, so that you never have to give it twice.

🇹🇭 ไทย

ความจำคือสิ่งที่ Codex รู้เกี่ยวกับคุณและงานของคุณ และมันหาเจอได้เร็วและถูกแค่ไหน มีสามแบบ และแต่ละขั้นตอนตรงนี้คือหนึ่งแบบ ความจำระยะยาวคือไฟล์ของคุณ คนเรียกพื้นที่ทำงานแบบของคุณว่า สมองที่สอง แต่มันคือโฟลเดอร์และไฟล์ และสิ่งที่ทำให้มันมีประโยชน์คือแผนที่ เพื่อให้ Codex เปิดไฟล์เดียวที่มันต้องการ ความจำระยะสั้นคือแชตเดียวที่คุณอยู่ ซึ่ง Codex อ่านใหม่ทุกครั้งที่คุณส่งข้อความ และความจำที่ปรับปรุงตัวเองคือความจำที่การแก้ไขถูกจดไว้ตั้งแต่ครั้งแรก เพื่อที่คุณจะไม่ต้องบอกซ้ำอีก

🇨🇳 中文

记忆是 Codex 对你和你的工作知道些什么,以及它能多快、多便宜地找到。它分三种,这里的每一步讲一种。长期记忆就是你的文件。人们把你这样的工作区叫作第二大脑,可它就是文件夹和文件,让它有用的是一张地图,好让 Codex 打开它需要的那一个文件。短期记忆是你所在的这一次对话,你每发一条消息,Codex 都要把它重读一遍。而自我改进的记忆,是指一次纠正第一次就被记下来,你再也不必说第二遍。

9 make_map.py: a map in AGENTS.md · make_map.py: แผนที่ใน AGENTS.md · make_map.py:AGENTS.md 里的一张地图

🇬🇧 English

Codex reads AGENTS.md at the start of every chat: the one in your workspace, and the one in ~/.codex if you made one. Whatever is in that file costs a little in every chat, so use it for two things only: the rules you always want followed, and a map. The map says which folder holds what, so that Codex goes straight to one folder instead of searching all of them. The video found that a plain-text map like this is enough even for a very large workspace, without any search database.

This file writes the map between two marker lines in AGENTS.md, from each folder's README.md, and leaves the rest of the file alone. Run it again whenever you add a folder.

🇹🇭 ไทย

Codex อ่าน AGENTS.md ตอนเริ่มทุกแชต: ไฟล์ในพื้นที่ทำงานของคุณ และไฟล์ใน ~/.codex ถ้าคุณสร้างไว้ อะไรที่อยู่ในไฟล์นั้นมีค่าใช้จ่ายเล็กน้อยในทุกแชต จึงใช้มันสำหรับสองอย่างเท่านั้น: กฎที่คุณอยากให้ทำตามเสมอ และแผนที่ แผนที่บอกว่าโฟลเดอร์ไหนเก็บอะไร เพื่อให้ Codex ตรงไปที่โฟลเดอร์เดียวแทนที่จะค้นทุกโฟลเดอร์ วิดีโอพบว่าแผนที่ข้อความธรรมดาแบบนี้ก็พอ แม้สำหรับพื้นที่ทำงานที่ใหญ่มาก โดยไม่ต้องมีฐานข้อมูลสำหรับค้นหาใด ๆ

ไฟล์นี้เขียนแผนที่ไว้ระหว่างบรรทัดเครื่องหมายสองบรรทัดใน AGENTS.md จาก README.md ของแต่ละโฟลเดอร์ และไม่แตะส่วนที่เหลือของไฟล์ รันมันอีกครั้งทุกครั้งที่คุณเพิ่มโฟลเดอร์

🇨🇳 中文

Codex 在每次对话开始时读 AGENTS.md:你工作区里的那一份,以及你若在 ~/.codex 里写了一份,也读那一份。那个文件里的内容,每次对话都要花一点,所以只用它装两样东西:你希望一直被遵守的规则,以及一张地图。地图写明哪个文件夹装着什么,好让 Codex 直接去一个文件夹,而不是把所有文件夹都搜一遍。视频发现,即使工作区非常大,这样一张纯文本地图也够用,不需要任何搜索数据库。

这个文件根据每个文件夹的 README.md,把地图写在 AGENTS.md 里两行标记之间,文件的其余部分不动。每次你加了文件夹,就再运行一次。

~/arms/make_map.py

"""make_map.py - write a map of the workspace into its AGENTS.md.

    python3 make_map.py            in your workspace

Codex reads AGENTS.md at the start of every chat. A map there tells it where
things are, so it opens one folder instead of searching all of them. The map is
each folder, the first line of its README.md, and how many files it holds. Run
this again whenever you add a folder: it replaces the old map and nothing else.
"""
import pathlib

START = "<!-- map start -->"
END = "<!-- map end -->"


def first_line(readme):
    # The first line that is not empty and not a heading says the folder's job.
    for line in readme.read_text(encoding="utf-8").splitlines():
        if line.strip() and not line.startswith("#"):
            return line.strip()
    return "(no README.md line)"


def map_lines(home):
    lines = []
    for folder in sorted(p for p in home.iterdir() if p.is_dir()):
        # A name that starts with a dot, like .git or .agents, is a tool's folder.
        if folder.name.startswith("."):
            continue
        readme = folder / "README.md"
        job = first_line(readme) if readme.exists() else "(no README.md)"
        files = sum(1 for p in folder.rglob("*") if p.is_file())
        lines.append(f"- `{folder.name}/`: {job} (files: {files})")
    return lines


def main():
    home = pathlib.Path.cwd()
    agents = home / "AGENTS.md"
    text = agents.read_text(encoding="utf-8")
    before = text.split(START)[0]
    after = text.split(END)[1]
    new_map = "\n".join([START] + map_lines(home) + [END])
    agents.write_text(before + new_map + after, encoding="utf-8")
    print(new_map)


if __name__ == "__main__":
    main()
python3 make_map.py

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

<!-- map start -->
- `artifacts/`: Every web page Codex made for me, opened by inbox.py. (files: 2)
- `log/`: What each Codex run cost, written by ask.py. (files: 1)
- `notes/`: Fixes for later: one file per skill, written by note.py. (files: 1)
- `projects/`: Programs I am building, one folder per project. (files: 1)
- `school/`: Homework and class notes, one folder per subject. (files: 2)
<!-- map end -->

~/arms/AGENTS.md afterwards~/arms/AGENTS.md หลังจากนั้น之后的 ~/arms/AGENTS.md

# AGENTS.md: how to work in this workspace

This folder is my workspace. Read the map first, then open only what you need.

## Rules

- Answer in short, plain English. I am a student.
- Save every web page you make in artifacts/, named like 2026-10-21-topic.html.
- Never delete a file. Move it to a folder named old/ instead.

## Map

<!-- map start -->
- `artifacts/`: Every web page Codex made for me, opened by inbox.py. (files: 2)
- `log/`: What each Codex run cost, written by ask.py. (files: 1)
- `notes/`: Fixes for later: one file per skill, written by note.py. (files: 1)
- `projects/`: Programs I am building, one folder per project. (files: 1)
- `school/`: Homework and class notes, one folder per subject. (files: 2)
<!-- map end -->

🇬🇧 English

When a folder grows big, give it a map of its own: a README.md that lists its subfolders, or an AGENTS.md inside it. Codex reads every AGENTS.md from the workspace down to the folder you started it in, and the nearer one wins when two disagree.

🇹🇭 ไทย

เมื่อโฟลเดอร์ไหนโตขึ้น ให้แผนที่ของมันเองแก่มัน: README.md ที่แสดงโฟลเดอร์ย่อยของมัน หรือ AGENTS.md ข้างในมัน Codex อ่านทุก AGENTS.md ตั้งแต่พื้นที่ทำงานลงมาจนถึงโฟลเดอร์ที่คุณเริ่มมัน และไฟล์ที่ใกล้กว่าชนะเมื่อสองไฟล์ขัดกัน

🇨🇳 中文

当一个文件夹变大时,就给它一张自己的地图:一个列出它子文件夹的 README.md,或者它里面的一个 AGENTS.md。Codex 会读从工作区往下、一直到你启动它的那个文件夹为止的每一个 AGENTS.md,两份说法不一时,离得近的那份说了算。

🧠 Quiz 9: what will this print?🧠 แบบทดสอบ 9: มันจะพิมพ์อะไร🧠 小测验 9:它会打印什么?

Pick an answer. Then the page shows which one is right and why, and you can run the program yourself.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูกและเพราะอะไร และคุณรันโปรแกรมเองได้选一个答案。然后这一页会告诉你哪个是对的、为什么,你也可以自己运行这个程序。

# Quiz 9: what will this print?
readme = """# school

Homework and class notes."""

for line in readme.splitlines():
    if line.strip() and not line.startswith("#"):
        print(line)
        break

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

The loop skips the empty line and the heading that starts with #, prints the first real line, and break stops the loop. make_map.py finds each folder's job this way.ลูปข้ามบรรทัดว่างและหัวข้อที่ขึ้นต้นด้วย # พิมพ์บรรทัดจริงบรรทัดแรก แล้ว break หยุดลูป make_map.py หาหน้าที่ของแต่ละโฟลเดอร์ด้วยวิธีนี้循环跳过空行和以 # 开头的标题,打印第一行真正的文字,然后 break 让循环停下。make_map.py 就是这样找出每个文件夹的任务的。

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

Homework and class notes.

10 how_big.py: the memory of one chat · how_big.py: ความจำของแชตหนึ่งแชต · how_big.py:一次对话的记忆

🇬🇧 English

A chat has a limit, the context window: everything Codex can hold in mind at once. Two things go wrong as a chat fills it. A model makes less good use of what is in a very long chat. And every time you send a message, Codex reads the whole chat again, so a long chat makes every new message cost more. This file shows the size of things in tokens. For English, about 4 letters make 1 token, so it divides each file's size by 4. It also checks AGENTS.md against Codex's limit: Codex reads only the first 32 KiB of it and drops the rest without saying so.

🇹🇭 ไทย

แชตมีขีดจำกัด คือ หน้าต่างบริบท (context window): ทุกอย่างที่ Codex จำไว้ในใจได้พร้อมกัน มีสองอย่างที่ผิดพลาดเมื่อแชตเติมมันเต็มขึ้น โมเดลใช้สิ่งที่อยู่ในแชตยาวมาก ๆ ได้ไม่ดีเท่า และทุกครั้งที่คุณส่งข้อความ Codex อ่านทั้งแชตใหม่ แชตยาวจึงทำให้ทุกข้อความใหม่แพงขึ้น ไฟล์นี้แสดงขนาดของสิ่งต่าง ๆ เป็นโทเค็น สำหรับภาษาอังกฤษ ราว 4 ตัวอักษรเท่ากับ 1 โทเค็น มันจึงหารขนาดของแต่ละไฟล์ด้วย 4 มันยังตรวจ AGENTS.md กับขีดจำกัดของ Codex ด้วย: Codex อ่านแค่ 32 KiB แรกของมันและทิ้งส่วนที่เหลือโดยไม่บอก

🇨🇳 中文

一次对话有个限度,叫上下文窗口(context window):Codex 一次能记在心里的全部内容。对话把它填满时,会出两种问题。对话很长时,模型对里面内容的利用就没那么好。而且你每发一条消息,Codex 都要把整段对话重读一遍,所以对话越长,每条新消息就越贵。这个文件用词元显示各样东西的大小。对英文来说,大约 4 个字母是 1 个词元,所以它把每个文件的大小除以 4。它还拿 AGENTS.md 和 Codex 的限度比:Codex 只读它的前 32 KiB,其余的不声不响地丢掉。

~/arms/how_big.py

"""how_big.py - how many tokens would it cost Codex to read every file here?

    python3 how_big.py            in your workspace

A token is a piece of a word. For English, 4 letters make about 1 token, so
this divides each file's size by 4. That is a guess, not a count, but it is
close enough to show the point: reading everything costs far more than reading
the map in AGENTS.md first. It also checks AGENTS.md against Codex's limit:
Codex reads at most 32 KiB of it, and quietly drops the rest.
"""
import pathlib

LETTERS_PER_TOKEN = 4
AGENTS_LIMIT = 32 * 1024


def main():
    home = pathlib.Path.cwd()
    sizes = {}
    for path in home.rglob("*"):
        # Files inside .git are git's own records, not your work.
        if path.is_file() and ".git" not in path.parts:
            sizes[path.relative_to(home)] = path.stat().st_size
    total = sum(sizes.values()) // LETTERS_PER_TOKEN
    biggest = sorted(sizes, key=sizes.get, reverse=True)[:5]
    print(f"{len(sizes)} files, about {total:,} tokens to read them all")
    print("the biggest:")
    for name in biggest:
        print(f"  {sizes[name] // LETTERS_PER_TOKEN:>8,} tokens  {name.as_posix()}")
    agents = sizes.get(pathlib.Path("AGENTS.md"), 0)
    state = "fine" if agents <= AGENTS_LIMIT else "TOO BIG: Codex drops the end"
    print(f"AGENTS.md: {agents:,} bytes of {AGENTS_LIMIT:,}, about "
          f"{agents // LETTERS_PER_TOKEN:,} tokens: {state}")


if __name__ == "__main__":
    main()
python3 how_big.py

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

23 files, about 6,888 tokens to read them all
the biggest:
       901 tokens  ask.py
       644 tokens  make_workspace.py
       610 tokens  inbox.py
       556 tokens  week.py
       531 tokens  example_asks.jsonl
AGENTS.md: 766 bytes of 32,768, about 191 tokens: fine

🇬🇧 English

A new workspace is small: 23 files, about 6,888 tokens. The map Codex reads first is 191 of them. A real workspace after a year holds thousands of files, and then the difference between reading the map and reading everything is the difference between a cheap answer and a chat that is full before it starts. Three commands inside a Codex chat look after its short-term memory:

  • /status shows how full the chat's context window is;
  • /compact makes Codex replace the chat so far with a short summary of it. Do it at a natural break, and say what must be kept in your next message, such as the decisions you made;
  • /new starts an empty chat in the same workspace. One job, one chat: start a new one when you start a new job.

🇹🇭 ไทย

พื้นที่ทำงานใหม่ยังเล็ก: 23 ไฟล์ ประมาณ 6,888 โทเค็น แผนที่ที่ Codex อ่านก่อนคือ 191 โทเค็นในนั้น พื้นที่ทำงานจริงผ่านไปหนึ่งปีจะมีไฟล์เป็นพัน ๆ และเมื่อนั้นความต่างระหว่างการอ่านแผนที่กับการอ่านทุกอย่าง ก็คือความต่างระหว่างคำตอบราคาถูกกับแชตที่เต็มตั้งแต่ยังไม่เริ่ม มีสามคำสั่งในแชต Codex ที่ดูแลความจำระยะสั้นของมัน:

  • /status แสดงว่าหน้าต่างบริบทของแชตเต็มแค่ไหน;
  • /compact ให้ Codex แทนที่แชตที่ผ่านมาด้วยบทสรุปสั้น ๆ ทำเมื่อถึงช่วงพักตามธรรมชาติ และบอกในข้อความถัดไปว่าอะไรต้องเก็บไว้ เช่น สิ่งที่คุณตัดสินใจแล้ว;
  • /new เริ่มแชตว่างในพื้นที่ทำงานเดิม หนึ่งงาน หนึ่งแชต: เริ่มแชตใหม่เมื่อคุณเริ่มงานใหม่

🇨🇳 中文

新的工作区很小:23 个文件,大约 6,888 个词元。Codex 先读的那张地图占其中 191 个。一个用了一年的真实工作区会有成千上万个文件,那时读地图和读全部之间的差别,就是一个便宜的回答和一次还没开始就满了的对话之间的差别。Codex 对话里有三条命令照看它的短期记忆:

  • /status 显示对话的上下文窗口满了多少;
  • /compact 让 Codex 用一段简短的摘要换掉到目前为止的对话。在自然停顿的时候做,并在你的下一条消息里说明哪些必须保留,比如你已经做出的决定;
  • /new 在同一个工作区里开一个空白的新对话。一件事,一次对话:开始新的事,就开一个新对话。

🧠 Quiz 10: what will this print?🧠 แบบทดสอบ 10: มันจะพิมพ์อะไร🧠 小测验 10:它会打印什么?

Pick an answer. Then the page shows which one is right and why, and you can run the program yourself.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูกและเพราะอะไร และคุณรันโปรแกรมเองได้选一个答案。然后这一页会告诉你哪个是对的、为什么,你也可以自己运行这个程序。

# Quiz 10: what will this print?
LETTERS_PER_TOKEN = 4
text = "Check my homework."

print(len(text), "letters")
print(len(text) // LETTERS_PER_TOKEN, "tokens, about")

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

len counts all 18 characters, the spaces and the full stop too. // divides and drops what is left over: 18 // 4 is 4. how_big.py guesses tokens the same way.len นับตัวอักษรทั้ง 18 ตัว รวมช่องว่างและจุดด้วย // หารแล้วทิ้งเศษ: 18 // 4 ได้ 4 how_big.py เดาจำนวนโทเค็นด้วยวิธีเดียวกันlen 数了全部 18 个字符,空格和句号也算。// 做除法并丢掉余数:18 // 4 等于 4。how_big.py 也是这样估算词元的。

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

18 letters
4 tokens, about

11 note.py: fixes for later · note.py: สิ่งที่จะแก้ทีหลัง · note.py:留到以后的修改

🇬🇧 English

In the middle of a job you will notice that a skill could do better. Fixing it there and then breaks off the job and fills the chat with something else. So write the fix down and carry on. This file adds a line to the skill's own file in notes/, with the date, and costs nothing, because Python writes it. Called with nothing after it, it counts the open notes.

🇹🇭 ไทย

ระหว่างทำงาน คุณจะสังเกตว่าสกิลหนึ่งน่าจะทำได้ดีกว่านี้ การแก้มันตรงนั้นเลยจะขัดจังหวะงานและทำให้แชตเต็มไปด้วยเรื่องอื่น ดังนั้นจดสิ่งที่จะแก้ไว้แล้วทำงานต่อ ไฟล์นี้เพิ่มหนึ่งบรรทัดในไฟล์ของสกิลนั้นใน notes/ พร้อมวันที่ และไม่มีค่าใช้จ่าย เพราะ Python เป็นคนเขียน ถ้าเรียกโดยไม่มีอะไรตามหลัง มันจะนับโน้ตที่ยังเปิดอยู่

🇨🇳 中文

做事做到一半,你会发现某个技能本可以做得更好。当场去改,会打断手上的事,还会让对话里塞满别的东西。所以把要改的地方记下来,接着做。这个文件在 notes/ 里那个技能自己的文件中加一行,带上日期,而且不花钱,因为是 Python 写的。后面什么都不跟地调用它,它就数一数还开着的笔记。

~/arms/note.py

"""note.py - park a fix for later, so that you do not break off what you are doing.

    python3 note.py text-check "say the line number of each problem"    add a note
    python3 note.py                                                     count the notes

Each skill gets its own file in notes/, one line per fix, with the date. When
you have time, ask Codex to apply them all at once (step 11 shows how). Writing
the note costs nothing, because Python writes it, not Codex.
"""
import datetime
import pathlib
import sys

NOTES = pathlib.Path("notes")


def main():
    NOTES.mkdir(exist_ok=True)
    if len(sys.argv) == 3:
        skill, fix = sys.argv[1], sys.argv[2]
        today = datetime.date.today().isoformat()
        with (NOTES / (skill + ".md")).open("a", encoding="utf-8") as notes:
            notes.write(f"- [ ] {today} {fix}\n")
    # Count the open notes, the lines that start with "- [ ]", in each file.
    for path in sorted(NOTES.glob("*.md")):
        lines = path.read_text(encoding="utf-8").splitlines()
        open_notes = [line for line in lines if line.startswith("- [ ]")]
        if open_notes:
            print(f"{len(open_notes)} open  notes/{path.name}")


if __name__ == "__main__":
    main()
python3 note.py text-check "say the line number of each problem"

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

1 open  notes/text-check.md

🇬🇧 English

At the end of the week, when you have tokens left, hand the whole list to Codex in one job. Then git diff shows what it changed in the skill.

🇹🇭 ไทย

ตอนสิ้นสัปดาห์ เมื่อคุณยังมีโทเค็นเหลือ ให้ส่งทั้งรายการให้ Codex ในงานเดียว แล้ว git diff จะแสดงว่ามันแก้อะไรในสกิล

🇨🇳 中文

到了周末,你还有剩下的词元时,就把整张清单作为一个任务交给 Codex。然后 git diff 会显示它在技能里改了什么。

not run by us: type this in a Codex chat in ~/armsเราไม่ได้รันเอง: พิมพ์สิ่งนี้ในแชต Codex ที่ ~/arms我们没有运行过:在 ~/arms 的 Codex 对话里输入这几行

Read notes/text-check.md. Apply every open note to .agents/skills/text-check,
then change "- [ ]" to "- [x]" on each note you applied.

🇬🇧 English

Codex also has a memory of its own, called Memories, which is off until you switch it on. With it on, Codex writes down useful things from your earlier chats, in files under ~/.codex/memories, and uses them in later chats. It does this in the background, some hours after a chat has gone quiet. To switch it on, add these two lines to ~/.codex/config.toml; /memories in a chat turns it on or off for that chat. Codex's own manual is clear about what it is for: a help for recall, not a place for rules. A rule that must always hold belongs in AGENTS.md or in a skill, where you can read it and git keeps it.

🇹🇭 ไทย

Codex ยังมีความจำของตัวเองด้วย เรียกว่า Memories ซึ่งปิดอยู่จนกว่าคุณจะเปิด เมื่อเปิดแล้ว Codex จะจดสิ่งที่มีประโยชน์จากแชตก่อน ๆ ของคุณ ไว้ในไฟล์ใต้ ~/.codex/memories และใช้มันในแชตต่อ ๆ ไป มันทำสิ่งนี้อยู่เบื้องหลัง หลายชั่วโมงหลังจากแชตเงียบไปแล้ว ถ้าจะเปิด ให้เพิ่มสองบรรทัดนี้ลงใน ~/.codex/config.toml; /memories ในแชตเปิดหรือปิดมันสำหรับแชตนั้น คู่มือของ Codex เองบอกชัดว่ามันมีไว้ทำอะไร: ตัวช่วยให้นึกออก ไม่ใช่ที่สำหรับกฎ กฎที่ต้องใช้เสมอควรอยู่ใน AGENTS.md หรือในสกิล ซึ่งคุณอ่านได้และ git เก็บไว้

🇨🇳 中文

Codex 还有一种它自己的记忆,叫 Memories,在你打开之前是关着的。打开以后,Codex 会把你以前对话里有用的东西记下来,放在 ~/.codex/memories 下面的文件里,并在以后的对话里用上。它在后台做这件事,在一次对话安静下来几个小时之后。要打开它,就在 ~/.codex/config.toml 里加上这两行;对话里的 /memories 可以为那一次对话打开或关掉它。Codex 自己的手册把它的用途说得很清楚:帮你回想,而不是放规则的地方。必须一直遵守的规则,应该放在 AGENTS.md 或某个技能里,在那里你读得到,git 也替你保存着。

~/.codex/config.toml

[features]
memories = true

🧠 Quiz 11: what will this print?🧠 แบบทดสอบ 11: มันจะพิมพ์อะไร🧠 小测验 11:它会打印什么?

Pick an answer. Then the page shows which one is right and why, and you can run the program yourself.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูกและเพราะอะไร และคุณรันโปรแกรมเองได้选一个答案。然后这一页会告诉你哪个是对的、为什么,你也可以自己运行这个程序。

# Quiz 11: what will this print?
import pathlib

notes = pathlib.Path("notes.md")
notes.write_text("one\n", encoding="utf-8")
with notes.open("a", encoding="utf-8") as file:
    file.write("two\n")
print(notes.read_text(encoding="utf-8"), end="")

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

write_text writes a new file. open("a") opens it to add at the end, so one stays and two goes under it. note.py adds each fix to the end of its notes file this way.write_text เขียนไฟล์ใหม่ open("a") เปิดไฟล์เพื่อเพิ่มต่อท้าย ดังนั้น one ยังอยู่ และ two ไปอยู่ข้างใต้ note.py เพิ่มสิ่งที่จะแก้แต่ละอย่างไว้ท้ายไฟล์โน้ตด้วยวิธีนี้write_text 写出一个新文件。open("a") 打开它,在末尾追加,所以 one 还在,two 接在它下面。note.py 就是这样把每个修改加到笔记文件末尾的。

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

one
two

APart 4 — Apps · ส่วนที่ 4 — แอป · 第 4 部分 —— 应用

🇬🇧 English

Apps are everything Codex reaches outside the chat, and the small apps you build so that you need the chat less. There are four ways to connect Codex to a service, and the video gives them in order, lightest first:

  1. the service's own connector, if it makes one;
  2. a command Codex can run in the Terminal, which answers in a few lines. The lightest of all;
  3. the service's API, the web address a program asks. Heavier: its answers are long, and Codex reads all of every answer;
  4. a tool server (MCP). The heaviest: Codex is told about all of its tools at the start of every chat, even a chat that never uses them.

Tool servers are still the right choice for some things, such as a browser; steps 4 to 10 of the Codex on a Mac page connect seven of them. This part shows the second way, a command, and then an app of your own.

🇹🇭 ไทย

แอปคือทุกสิ่งที่ Codex เข้าถึงนอกแชต และแอปเล็ก ๆ ที่คุณสร้างเพื่อให้ต้องใช้แชตน้อยลง มีสี่วิธีในการต่อ Codex เข้ากับบริการหนึ่ง และวิดีโอเรียงไว้ตามลำดับ เบาที่สุดก่อน:

  1. ตัวเชื่อมต่อของบริการเอง ถ้ามันทำไว้;
  2. คำสั่งที่ Codex รันได้ใน Terminal ซึ่งตอบในไม่กี่บรรทัด เบาที่สุดในทั้งหมด;
  3. API ของบริการ คือที่อยู่เว็บที่โปรแกรมใช้ถาม หนักกว่า: คำตอบของมันยาว และ Codex อ่านทุกคำตอบทั้งหมด;
  4. เซิร์ฟเวอร์เครื่องมือ (MCP) หนักที่สุด: Codex ได้รับคำบอกเล่าเรื่องเครื่องมือทั้งหมดของมันตอนเริ่มทุกแชต แม้แต่แชตที่ไม่เคยใช้มันเลย

เซิร์ฟเวอร์เครื่องมือยังเป็นตัวเลือกที่ถูกต้องสำหรับบางอย่าง เช่น เบราว์เซอร์; ขั้นตอนที่ 4 ถึง 10 ของหน้า Codex บนแมคต่อเข้าไปเจ็ดตัว ส่วนนี้แสดงวิธีที่สอง คือคำสั่ง แล้วตามด้วยแอปของคุณเอง

🇨🇳 中文

应用指的是 Codex 在对话之外能触及的一切,以及你做的那些让你少用对话的小应用。把 Codex 接到一项服务上有四种办法,视频按从轻到重的顺序给出:

  1. 服务自己做的连接器,如果它有的话;
  2. 一条 Codex 能在终端里运行的命令,几行就答完。这是最轻的;
  3. 服务的 API,也就是程序去问的那个网址。更重一些:它的回答很长,而 Codex 每次都要把整份回答读完;
  4. 一个工具服务器(MCP)。最重:每次对话开始时,Codex 都会被告知它的全部工具,哪怕这次对话一个也不用。

工具服务器对有些事仍是正确的选择,比如浏览器;在 Mac 上用 Codex 那一页的第 4 到第 10 步接上了七个。这一部分展示第二种办法,一条命令,然后是一个你自己的应用。

12 weather.py: a command, not a whole answer · weather.py: คำสั่งหนึ่งคำสั่ง ไม่ใช่คำตอบทั้งก้อน · weather.py:一条命令,而不是一整份回答

🇬🇧 English

Open-Meteo is a free weather service with no key. Ask its API for the weather in Chiang Mai and it sends back a long answer: the place, the time zone, the units, and a forecast for every hour of a week. This file asks once, picks out the weather now, and prints one line. Give Codex the command, and it reads one line. Let Codex ask the service itself, and it reads the whole answer, every time.

🇹🇭 ไทย

Open-Meteo เป็นบริการพยากรณ์อากาศฟรีที่ไม่ต้องใช้คีย์ ถาม API ของมันเรื่องอากาศที่เชียงใหม่ แล้วมันจะส่งคำตอบยาว ๆ กลับมา: สถานที่ เขตเวลา หน่วย และพยากรณ์ทุกชั่วโมงของหนึ่งสัปดาห์ ไฟล์นี้ถามหนึ่งครั้ง หยิบอากาศตอนนี้ออกมา แล้วพิมพ์หนึ่งบรรทัด ให้คำสั่งแก่ Codex แล้วมันจะอ่านหนึ่งบรรทัด ปล่อยให้ Codex ถามบริการเอง แล้วมันจะอ่านคำตอบทั้งหมด ทุกครั้ง

🇨🇳 中文

Open-Meteo 是一个不需要密钥的免费天气服务。向它的 API 问清迈的天气,它会送回一大段回答:地点、时区、单位,还有一周里每个小时的预报。这个文件问一次,挑出现在的天气,打印一行。把命令交给 Codex,它读一行。让 Codex 自己去问服务,它每次都要把整份回答读完。

~/arms/weather.py

"""weather.py - a small command that Codex can run: today's weather in Chiang Mai.

    python3 weather.py           one line: what Codex needs
    python3 weather.py --raw     the whole answer of the weather service, and its size

The weather comes from Open-Meteo, a free service with no key. Its answer is
long, and most of it is not the weather. This command reads the answer and
prints one line. Give Codex the command and it reads one line; give it the
service and it reads the whole answer, every time.
"""
import json
import sys
import urllib.request

# Chiang Mai: latitude 18.79 north, longitude 98.98 east.
URL = ("https://api.open-meteo.com/v1/forecast?latitude=18.79&longitude=98.98"
       "&current=temperature_2m,relative_humidity_2m,precipitation,wind_speed_10m"
       "&hourly=temperature_2m,precipitation_probability&timezone=Asia%2FBangkok")


def main():
    with urllib.request.urlopen(URL, timeout=20) as response:
        raw = response.read().decode("utf-8")
    if "--raw" in sys.argv:
        print(raw[:300] + " ...")
        print(len(raw), "characters in the whole answer")
        return
    now = json.loads(raw)["current"]
    line = (f"Chiang Mai {now['time']}: {now['temperature_2m']} C, "
            f"humidity {now['relative_humidity_2m']}%, rain {now['precipitation']} mm, "
            f"wind {now['wind_speed_10m']} km/h")
    print(line)
    print(len(line), "characters")


if __name__ == "__main__":
    main()
python3 weather.py
python3 weather.py --raw

what the first command printedสิ่งที่คำสั่งแรกพิมพ์ออกมา第一条命令打印的内容

Chiang Mai 2026-10-25T12:45: 32.0 C, humidity 55%, rain 0.0 mm, wind 4.6 km/h
77 characters

what the second command printed: the start of the answer, and its sizeสิ่งที่คำสั่งที่สองพิมพ์ออกมา: ตอนต้นของคำตอบ และขนาดของมัน第二条命令打印的内容:回答的开头,以及它的大小

{"latitude":18.80492,"longitude":99.02655,"generationtime_ms":0.0833272933959961,"utc_offset_seconds":25200,"timezone":"Asia/Bangkok","timezone_abbreviation":"GMT+7","elevation":315.0,"current_units":{"time":"iso8601","interval":"seconds","temperature_2m":"°C","relative_humidity_2m":"%","precipitati ...
5109 characters in the whole answer

🇬🇧 English

The command printed 77 characters. The service's whole answer was 5109. To let Codex use the command, add one line to the rules in AGENTS.md: For the weather, run python3 weather.py; never ask the weather service yourself. The same idea works for any service you use often: write the command once, and make it print only what you need.

🇹🇭 ไทย

คำสั่งพิมพ์ออกมา 77 ตัวอักษร คำตอบทั้งหมดของบริการมี 5109 ตัว ถ้าจะให้ Codex ใช้คำสั่งนี้ ให้เพิ่มหนึ่งบรรทัดในกฎของ AGENTS.md: For the weather, run python3 weather.py; never ask the weather service yourself. (เรื่องอากาศ ให้รันคำสั่งนี้ อย่าถามบริการพยากรณ์อากาศเอง) ไอเดียเดียวกันใช้ได้กับทุกบริการที่คุณใช้บ่อย: เขียนคำสั่งครั้งเดียว และให้มันพิมพ์เฉพาะสิ่งที่คุณต้องการ

🇨🇳 中文

这条命令打印了 77 个字符。服务的整份回答有 5109 个。要让 Codex 用这条命令,就在 AGENTS.md 的规则里加一行:For the weather, run python3 weather.py; never ask the weather service yourself.(查天气就运行这条命令,别自己去问天气服务。)同样的想法适用于你常用的任何服务:把命令写一次,让它只打印你需要的东西。

🧠 Quiz 12🧠 แบบทดสอบ 12🧠 小测验 12

Pick an answer. Then the page shows which one is right, and why.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูก และเพราะอะไร选一个答案。然后这一页会告诉你哪个是对的,以及为什么。

Why does weather.py give Codex one short line instead of the weather service's whole answer?ทำไม weather.py ถึงให้ Codex บรรทัดสั้น ๆ บรรทัดเดียว แทนคำตอบทั้งหมดของบริการพยากรณ์อากาศ为什么 weather.py 只给 Codex 短短一行,而不是天气服务的整份回答?

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

Codex spends tokens on everything it reads. The whole answer is thousands of characters; the line keeps only the numbers that matter.Codex ใช้โทเค็นกับทุกอย่างที่มันอ่าน คำตอบทั้งหมดยาวหลายพันตัวอักษร ส่วนบรรทัดนั้นเก็บไว้แค่ตัวเลขที่สำคัญCodex 读的每样东西都要花词元。整份回答有几千个字符,而那一行只留下要紧的数字。

13 inbox.py: an app of your own · inbox.py: แอปของคุณเอง · inbox.py:你自己的应用

🇬🇧 English

When a job comes back every week, ask whether a chat is the best place to do it. The video moves such jobs into small apps that Codex built: one to review videos, one for posts, one to find old work. This is the smallest of those: an inbox. The AGENTS.md of step 1 tells Codex to save every web page it makes for you in artifacts/. This app lists those pages, newest first, with a search box. Finding last week's page yourself costs nothing; asking Codex to find it costs tokens.

It uses only Python's own http.server, and it answers only this Mac: localhost is a name for the computer you are on. Start it, open http://localhost:8765 in your browser, and press Ctrl+C in the Terminal to stop it.

🇹🇭 ไทย

เมื่องานหนึ่งกลับมาทุกสัปดาห์ ให้ถามว่าแชตคือที่ที่ดีที่สุดสำหรับทำมันหรือเปล่า วิดีโอย้ายงานแบบนั้นไปไว้ในแอปเล็ก ๆ ที่ Codex สร้าง: แอปหนึ่งสำหรับตรวจวิดีโอ แอปหนึ่งสำหรับโพสต์ แอปหนึ่งสำหรับหางานเก่า นี่คือแอปที่เล็กที่สุดในนั้น: กล่องจดหมาย AGENTS.md ของขั้นตอนที่ 1 บอก Codex ให้บันทึกทุกหน้าเว็บที่มันทำให้คุณไว้ใน artifacts/ แอปนี้แสดงรายการหน้าเหล่านั้น ใหม่สุดก่อน พร้อมช่องค้นหา การหาหน้าของสัปดาห์ที่แล้วด้วยตัวเองไม่มีค่าใช้จ่าย การขอให้ Codex หาให้ต้องใช้โทเค็น

มันใช้แค่ http.server ของ Python เอง และตอบเฉพาะแมคเครื่องนี้: localhost คือชื่อของคอมพิวเตอร์ที่คุณใช้อยู่ เริ่มมัน เปิด http://localhost:8765 ในเบราว์เซอร์ของคุณ แล้วกด Ctrl+C ใน Terminal เพื่อหยุดมัน

🇨🇳 中文

当一件事每周都会回来,就问问对话是不是做它的最好地方。视频把这样的事搬进了 Codex 做的小应用里:一个审视频,一个管帖子,一个找旧东西。这是其中最小的一个:一个收件箱。第 1 步的 AGENTS.md 告诉 Codex,把它为你做的每个网页都存进 artifacts/。这个应用列出那些网页,最新的在前,还带一个搜索框。自己找上周的网页不花钱;请 Codex 去找要花词元。

它只用 Python 自带的 http.server,而且只回应这台 Mac:localhost 是你正在用的这台电脑的名字。启动它,在浏览器里打开 http://localhost:8765,在终端里按 Ctrl+C 停止它。

~/arms/inbox.py

"""inbox.py - a small app of your own: every page Codex made for you, newest first.

    python3 inbox.py             open http://localhost:8765 in your browser
    python3 inbox.py --list      print the list here instead

AGENTS.md tells Codex to save every web page it makes in artifacts/. This app
lists them, with a search box, so you can find last week's page yourself
instead of asking Codex to look for it, which costs tokens. It uses only
Python's own http.server. Press Ctrl+C in the terminal to stop it.
"""
import datetime
import html
import http.server
import pathlib
import sys
import urllib.parse

FOLDER = pathlib.Path("artifacts")
PORT = 8765


def pages(search=""):
    found = [p for p in FOLDER.glob("*.html") if search.lower() in p.name.lower()]
    # Newest first: sort by the time each file was last changed.
    return sorted(found, key=lambda p: p.stat().st_mtime, reverse=True)


def when(path):
    changed = datetime.datetime.fromtimestamp(path.stat().st_mtime)
    return changed.strftime("%Y-%m-%d %H:%M")


def list_page(search):
    rows = ""
    for path in pages(search):
        name = html.escape(path.name)
        rows += f'<li><a href="/artifacts/{name}">{name}</a> {when(path)}</li>\n'
    return (f"<!doctype html><title>Inbox</title><h1>Inbox</h1>"
            f'<form><input name="q" value="{html.escape(search)}" placeholder="search">'
            f"<button>Find</button></form><ul>{rows}</ul>")


class Inbox(http.server.SimpleHTTPRequestHandler):
    # SimpleHTTPRequestHandler already sends any file asked for, such as
    # /artifacts/welcome.html. Only the front page, "/", is made here.
    def do_GET(self):
        address = urllib.parse.urlparse(self.path)
        if address.path != "/":
            return super().do_GET()
        search = urllib.parse.parse_qs(address.query).get("q", [""])[0]
        body = list_page(search).encode("utf-8")
        self.send_response(200)
        self.send_header("Content-Type", "text/html; charset=utf-8")
        self.end_headers()
        self.wfile.write(body)


def main():
    if "--list" in sys.argv:
        for path in pages():
            print(when(path), path.name)
        return
    # "localhost" means only this computer can open the page.
    server = http.server.HTTPServer(("localhost", PORT), Inbox)
    print(f"inbox on http://localhost:{PORT} (Ctrl+C stops it)")
    server.serve_forever()


if __name__ == "__main__":
    main()
python3 inbox.py --list

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

2026-10-25 12:56 2026-10-21-mae-ping-river.html
2026-10-25 12:56 welcome.html
python3 inbox.py

what it printed, and the page it sent for a search for riverสิ่งที่มันพิมพ์ออกมา และหน้าที่มันส่งมาเมื่อค้นหาคำว่า river它打印的内容,以及搜索 river 时它送回的网页

inbox on http://localhost:8765 (Ctrl+C stops it)

GET /?q=river
<!doctype html>
<title>Inbox</title>
<h1>Inbox</h1>
<form>
<input name="q" value="river" placeholder="search">
<button>Find</button>
</form>
<ul>
<li>
<a href="/artifacts/2026-10-21-mae-ping-river.html">2026-10-21-mae-ping-river.html</a> 2026-10-25 12:56</li>
</ul>

🇬🇧 English

The second page in the list is one we put in artifacts/ to stand for a page Codex made. Ask Codex for a real one, and it will appear at the top. When you build more apps like this, keep the parts you reuse, such as a list, a search box, or a page frame, in one file that every app takes from, so that Codex does not write them again each time.

🇹🇭 ไทย

หน้าที่สองในรายการคือหน้าที่เราใส่ไว้ใน artifacts/ แทนหน้าที่ Codex ทำ ขอหน้าจริงจาก Codex แล้วมันจะขึ้นมาอยู่บนสุด เมื่อคุณสร้างแอปแบบนี้มากขึ้น ให้เก็บส่วนที่ใช้ซ้ำ เช่น รายการ ช่องค้นหา หรือกรอบหน้า ไว้ในไฟล์เดียวที่ทุกแอปดึงไปใช้ เพื่อที่ Codex จะไม่ต้องเขียนมันใหม่ทุกครั้ง

🇨🇳 中文

清单里的第二个网页,是我们放进 artifacts/、代表一个 Codex 做的网页的。请 Codex 做一个真的,它就会出现在最上面。当你做更多这样的应用时,把你反复用的部分,比如一个列表、一个搜索框或一个页面框架,放在一个所有应用都从中取用的文件里,这样 Codex 就不必每次重写一遍。

🧠 Quiz 13: what will this print?🧠 แบบทดสอบ 13: มันจะพิมพ์อะไร🧠 小测验 13:它会打印什么?

Pick an answer. Then the page shows which one is right and why, and you can run the program yourself.เลือกคำตอบ แล้วหน้านี้จะบอกว่าข้อไหนถูกและเพราะอะไร และคุณรันโปรแกรมเองได้选一个答案。然后这一页会告诉你哪个是对的、为什么,你也可以自己运行这个程序。

# Quiz 13: what will this print?
pages = ["2026-10-14-moon.html", "2026-10-21-river.html", "2026-10-18-rice.html"]

for page in sorted(pages, reverse=True):
    print(page)

✅ Right!✅ ถูกต้อง!✅ 答对了!

Not this one. The marked answer is the right one.ยังไม่ใช่ คำตอบที่มีเครื่องหมายคือคำตอบที่ถูก不是这个。标出来的那个才是对的。

sorted puts the names in order, and reverse=True turns the order round. A name that starts with its date sorts by date, so the newest comes first. inbox.py also puts the newest page first, by the time each file was changed.sorted เรียงชื่อตามลำดับ และ reverse=True กลับลำดับ ชื่อที่ขึ้นต้นด้วยวันที่จะเรียงตามวันที่ อันใหม่ที่สุดจึงมาก่อน inbox.py ก็เอาหน้าที่ใหม่ที่สุดขึ้นก่อนเหมือนกัน โดยดูจากเวลาที่แต่ละไฟล์ถูกแก้sorted 把名字排好顺序,reverse=True 把顺序倒过来。以日期开头的名字会按日期排,所以最新的排在最前面。inbox.py 也把最新的网页排在最前面,看的是每个文件最后一次被改的时间。

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

2026-10-21-river.html
2026-10-18-rice.html
2026-10-14-moon.html

GPart 5 — The same with Google Gemini · ส่วนที่ 5 — ทำแบบเดียวกันด้วย Google Gemini · 第 5 部分 —— 用 Google Gemini 做同样的事

🇬🇧 English

Everything in Parts 1 to 4 works with Google's Gemini models too. The tool is Google's coding agent for the Terminal, the Antigravity CLI, whose command is agy. Google used to offer this as Gemini CLI, the command gemini, but on 18 June 2026 Gemini CLI stopped answering free Google accounts and the Google AI Pro and Ultra plans. It now works only with a paid Gemini API key or a company licence. Antigravity CLI took its place, and you sign in to it with an ordinary Google account, on the free plan. You do not need an API key, and this part does not use one.

Most of your workspace does not change, because Antigravity reads the same two things Codex reads: the rules in AGENTS.md, and the skills in .agents/skills/. So make_workspace.py, the two skills, check.py, week.py, make_map.py, note.py, weather.py and inbox.py are the files of Parts 1 to 4, unchanged. Four files start the agent, and each has a Gemini version here: gemini_route.py, gemini_quiet_check.py, gemini_ask.py and gemini_how_big.py. The steps are numbered G1 to G13, and each one does what the step with the same number does for Codex.

What you need: the same Mac, Python 3 and GitHub account as in Before you start, and a Google account instead of Codex. Install agy in the Terminal, check that it is there, and start it once inside your workspace:

🇹🇭 ไทย

ทุกอย่างในส่วนที่ 1 ถึง 4 ใช้กับโมเดล Gemini ของ Google ได้ด้วย เครื่องมือคือเอเจนต์เขียนโค้ดของ Google สำหรับ Terminal ชื่อ Antigravity CLI ซึ่งมีคำสั่งว่า agy เดิม Google ให้สิ่งนี้ในชื่อ Gemini CLI คำสั่ง gemini แต่ในวันที่ 18 มิถุนายน 2026 Gemini CLI หยุดตอบบัญชี Google แบบฟรี และแผน Google AI Pro และ Ultra ตอนนี้มันใช้ได้เฉพาะกับคีย์ Gemini API แบบเสียเงินหรือใบอนุญาตของบริษัทเท่านั้น Antigravity CLI มาแทนที่ และคุณลงชื่อเข้าใช้ได้ด้วยบัญชี Google ธรรมดาในแผนฟรี คุณไม่ต้องใช้คีย์ API และส่วนนี้ก็ไม่ได้ใช้

พื้นที่ทำงานส่วนใหญ่ของคุณไม่เปลี่ยน เพราะ Antigravity อ่านสองอย่างเดียวกับที่ Codex อ่าน: กฎใน AGENTS.md และสกิลใน .agents/skills/ ดังนั้น make_workspace.py สกิลทั้งสอง check.py week.py make_map.py note.py weather.py และ inbox.py คือไฟล์ของส่วนที่ 1 ถึง 4 โดยไม่ต้องแก้ มีสี่ไฟล์ที่เริ่มเอเจนต์ และแต่ละไฟล์มีเวอร์ชัน Gemini อยู่ที่นี่: gemini_route.py gemini_quiet_check.py gemini_ask.py และ gemini_how_big.py ขั้นตอนมีหมายเลข G1 ถึง G13 และแต่ละขั้นตอนทำสิ่งที่ขั้นตอนหมายเลขเดียวกันทำให้ Codex

สิ่งที่คุณต้องมี: แมค Python 3 และบัญชี GitHub ชุดเดียวกับในก่อนเริ่ม และบัญชี Google แทน Codex ติดตั้ง agy ใน Terminal ตรวจว่ามันอยู่แล้ว และเริ่มมันหนึ่งครั้งในพื้นที่ทำงานของคุณ:

🇨🇳 中文

第 1 到第 4 部分的一切,用 Google 的 Gemini 模型也能做。工具是 Google 给终端用的编程智能体 Antigravity CLI,它的命令是 agy。Google 以前把它叫作 Gemini CLI,命令是 gemini,但从 2026 年 6 月 18 日起,Gemini CLI 不再回应免费的 Google 账号,也不再回应 Google AI Pro 和 Ultra 方案。现在它只能配付费的 Gemini API 密钥或公司许可证使用。Antigravity CLI 接替了它,你用一个普通的 Google 账号、在免费方案上就能登录。你不需要 API 密钥,这一部分也不用。

你的工作区大部分不用改,因为 Antigravity 读的正是 Codex 读的那两样东西:AGENTS.md 里的规则,和 .agents/skills/ 里的技能。所以 make_workspace.py、两个技能、check.py、week.py、make_map.py、note.py、weather.py 和 inbox.py 都是第 1 到第 4 部分的文件,原样不动。有四个文件会启动智能体,它们在这里各有一个 Gemini 版本:gemini_route.py、gemini_quiet_check.py、gemini_ask.py 和 gemini_how_big.py。步骤编号是 G1 到 G13,每一步做的事,就是同一编号的那一步为 Codex 做的事。

你需要:和开始之前里一样的 Mac、Python 3 和 GitHub 账号,再加一个 Google 账号来代替 Codex。在终端里安装 agy,检查它装好了,然后在你的工作区里启动它一次:

not run by us: in the Terminalเราไม่ได้รันเอง: ใน Terminal我们没有运行过:在终端里

curl -fsSL https://antigravity.google/cli/install.sh | bash
agy --version
cd ~/arms
agy

🇬🇧 English

The first time, agy opens your browser so that you can sign in with your Google account, asks for a colour scheme and a way to draw the screen, and asks whether you trust the folder. Say yes for ~/arms: agy starts reading a folder only once you trust it. Steps G4 to G8 run agy without a chat, and a run without a chat cannot open a browser, so it uses the sign-in that this first start saved. In a chat, /usage shows how much of your plan each model has left, and /exit leaves.

🇹🇭 ไทย

ครั้งแรก agy จะเปิดเบราว์เซอร์ให้คุณลงชื่อเข้าใช้ด้วยบัญชี Google ถามชุดสีและวิธีวาดหน้าจอ และถามว่าคุณไว้ใจโฟลเดอร์นี้ไหม ตอบ yes สำหรับ ~/arms: agy เริ่มอ่านโฟลเดอร์ก็ต่อเมื่อคุณไว้ใจมันแล้วเท่านั้น ขั้นตอน G4 ถึง G8 รัน agy โดยไม่มีแชต และการรันที่ไม่มีแชตเปิดเบราว์เซอร์ไม่ได้ มันจึงใช้การลงชื่อเข้าใช้ที่การเริ่มครั้งแรกนี้บันทึกไว้ ในแชต /usage แสดงว่าแผนของคุณเหลือให้แต่ละโมเดลเท่าไร และ /exit ใช้ออก

🇨🇳 中文

第一次运行时,agy 会打开浏览器让你用 Google 账号登录,问你要哪种配色、用哪种方式画屏幕,再问你是否信任这个文件夹。对 ~/arms 回答 yes:只有你信任了一个文件夹,agy 才开始读它。G4 到 G8 步不开对话地运行 agy,而不开对话的运行打不开浏览器,所以它用的是这第一次启动保存下来的登录。在对话里,/usage 显示你的方案给每个模型还剩多少,/exit 用来退出。

G1 The same workspace, read by Antigravity · พื้นที่ทำงานเดิม ให้ Antigravity อ่าน · 同一个工作区,由 Antigravity 来读

🇬🇧 English

Make the workspace exactly as in step 1: a private repository named arms on GitHub, cloned to ~/arms, and python3 make_workspace.py run in it. There is nothing to rename. Antigravity reads AGENTS.md in the folder you start it in and in each folder above it, up to the top of the repository, and it adds them all together. It also reads a file named GEMINI.md in the same places. Keep one file, AGENTS.md, so that Codex and Gemini follow the same rules. Rules for every folder on your Mac go in ~/.gemini/AGENTS.md.

🇹🇭 ไทย

สร้างพื้นที่ทำงานให้เหมือนขั้นตอนที่ 1 ทุกอย่าง: รีโพซิทอรีส่วนตัวชื่อ arms บน GitHub โคลนมาไว้ที่ ~/arms แล้วรัน python3 make_workspace.py ในนั้น ไม่มีอะไรต้องเปลี่ยนชื่อ Antigravity อ่าน AGENTS.md ในโฟลเดอร์ที่คุณเริ่มมัน และในทุกโฟลเดอร์ที่อยู่เหนือขึ้นไปจนถึงบนสุดของรีโพซิทอรี แล้วรวมทั้งหมดเข้าด้วยกัน มันยังอ่านไฟล์ชื่อ GEMINI.md ในที่เดียวกันด้วย ให้เก็บไว้ไฟล์เดียวคือ AGENTS.md เพื่อให้ Codex และ Gemini ทำตามกฎเดียวกัน กฎสำหรับทุกโฟลเดอร์บนแมคของคุณใส่ไว้ใน ~/.gemini/AGENTS.md

🇨🇳 中文

完全照第 1 步建工作区:在 GitHub 上建一个名叫 arms 的私有仓库,克隆到 ~/arms,再在里面运行 python3 make_workspace.py。没有要改名的东西。Antigravity 会读你启动它的那个文件夹里的 AGENTS.md,以及它上面每一层文件夹里的,一直到仓库最顶层,并把它们全部加在一起。它也会读同样位置上名叫 GEMINI.md 的文件。只留一个文件 AGENTS.md,让 Codex 和 Gemini 遵守同样的规则。给你 Mac 上所有文件夹的规则,放进 ~/.gemini/AGENTS.md。

G2 my-voice, called with / · my-voice เรียกด้วย / · my-voice,用 / 来叫

🇬🇧 English

Save the same SKILL.md as in step 2 in the same folder, .agents/skills/my-voice/. Antigravity reads skills the way Codex does: at the start of a chat it sees only each skill's name and description, and reads the rest when a job matches the description. One thing is new: agy turns each skill into a slash command, so you call it with / where Codex uses $. After you add or change a skill, type /skills reload, or start a new chat. /skills lists the skills it found.

🇹🇭 ไทย

บันทึก SKILL.md ไฟล์เดียวกับในขั้นตอนที่ 2 ไว้ในโฟลเดอร์เดียวกัน .agents/skills/my-voice/ Antigravity อ่านสกิลแบบเดียวกับ Codex: ตอนเริ่มแชตมันเห็นแค่ชื่อและคำอธิบายของแต่ละสกิล และอ่านส่วนที่เหลือเมื่องานหนึ่งตรงกับคำอธิบาย มีอย่างเดียวที่ใหม่: agy เปลี่ยนแต่ละสกิลเป็นคำสั่งทับ คุณจึงเรียกมันด้วย / ตรงที่ Codex ใช้ $ หลังจากเพิ่มหรือแก้สกิล ให้พิมพ์ /skills reload หรือเริ่มแชตใหม่ /skills แสดงรายการสกิลที่มันพบ

🇨🇳 中文

把和第 2 步一样的 SKILL.md 存进同一个文件夹 .agents/skills/my-voice/。Antigravity 读技能的方式和 Codex 一样:对话开始时它只看到每个技能的名字和描述,等某个任务和描述对上了,才读其余部分。只有一点是新的:agy 把每个技能变成一条斜杠命令,所以 Codex 用 $ 的地方,你用 / 来叫它。加了或改了技能之后,输入 /skills reload,或者开一个新对话。/skills 会列出它找到的技能。

not run by us: type this in an agy chat in ~/armsเราไม่ได้รันเอง: พิมพ์สิ่งนี้ในแชตของ agy ใน ~/arms我们没有运行过:在 ~/arms 里的 agy 对话中输入

/my-voice give me 10 titles for a video about cleaning a bike chain

🇬🇧 English

Eight titles should sound like your example, and two should be marked WILD CARD. Correct it in plain words as in step 2, and git diff shows the line it added to Lessons.

🇹🇭 ไทย

ชื่อแปดชื่อควรฟังเหมือนตัวอย่างของคุณ และสองชื่อควรมีคำว่า WILD CARD แก้มันด้วยคำพูดธรรมดาแบบในขั้นตอนที่ 2 แล้ว git diff จะแสดงบรรทัดที่มันเพิ่มใน Lessons

🇨🇳 中文

应该有八个标题听起来像你的例子,两个标着 WILD CARD。像第 2 步那样用平常的话纠正它,git diff 会显示它在 Lessons 里加的那一行。

G3 text-check, and agy asks before it runs check.py · text-check และ agy ถามก่อนรัน check.py · text-check,agy 运行 check.py 前会先问

🇬🇧 English

Save the two files of step 3 in .agents/skills/text-check/ and the text in school/, as there. check.py is Python, so it prints the same two problems whichever agent asks for it. Then call the skill. Before agy runs a command on your Mac it asks you; say yes. If you want it never to ask about this one command again, /permissions in a chat lets you allow it for good.

🇹🇭 ไทย

บันทึกสองไฟล์ของขั้นตอนที่ 3 ไว้ใน .agents/skills/text-check/ และข้อความไว้ใน school/ แบบที่นั่น check.py เป็น Python มันจึงพิมพ์ปัญหาสองข้อเดิมไม่ว่าเอเจนต์ไหนจะสั่ง จากนั้นเรียกสกิล ก่อนที่ agy จะรันคำสั่งบนแมคของคุณ มันจะถามคุณ ให้ตอบ yes ถ้าคุณอยากให้มันไม่ต้องถามเรื่องคำสั่งนี้อีก /permissions ในแชตให้คุณอนุญาตไว้ตลอดได้

🇨🇳 中文

像第 3 步那样,把那两个文件存进 .agents/skills/text-check/,把文本存进 school/。check.py 是 Python,所以不管哪个智能体叫它,它都打印同样的两个问题。然后叫这个技能。agy 在你的 Mac 上运行命令之前会先问你,回答 yes。如果你想让它以后再也不为这一条命令问你,在对话里用 /permissions 就能永久允许它。

not run by us: type this in an agy chatเราไม่ได้รันเอง: พิมพ์สิ่งนี้ในแชตของ agy我们没有运行过:在 agy 对话中输入

/text-check check school/homework.txt

G4 gemini_route.py: low effort for small jobs · gemini_route.py: คิดน้อยสำหรับงานเล็ก · gemini_route.py:小任务少想一点

🇬🇧 English

In step 4, route.py chose between a big model and a small one. With Gemini the choice is a different one. The models you can use are the ones your plan offers, and agy models lists them. What you choose for each job is the effort: --effort low, medium or high says how hard the model thinks. Low effort is faster and uses less of your plan. So gemini_route.py keeps the rule of step 4 and changes what it picks: low effort when the job starts with one of the easy words, high effort for anything else. -p runs agy for one job without a chat, as codex exec does.

To use one model every time, copy its name from agy models into MODEL, exactly as it is printed. Left empty, agy makes its own choice. If you name a model that agy does not know, a -p run stops with an error rather than quietly using another one.

🇹🇭 ไทย

ในขั้นตอนที่ 4 route.py เลือกระหว่างโมเดลใหญ่กับโมเดลเล็ก กับ Gemini ตัวเลือกเป็นอีกแบบ โมเดลที่คุณใช้ได้คือโมเดลที่แผนของคุณให้ และ agy models แสดงรายการมัน สิ่งที่คุณเลือกให้แต่ละงานคือความพยายาม: --effort low medium หรือ high บอกว่าโมเดลคิดหนักแค่ไหน คิดน้อยเร็วกว่าและใช้แผนของคุณน้อยกว่า ดังนั้น gemini_route.py คงกฎของขั้นตอนที่ 4 ไว้ และเปลี่ยนสิ่งที่มันเลือก: คิดน้อยเมื่องานขึ้นต้นด้วยคำง่าย ๆ คำใดคำหนึ่ง คิดหนักสำหรับงานอื่นทั้งหมด -p รัน agy สำหรับงานเดียวโดยไม่มีแชต แบบที่ codex exec ทำ

ถ้าจะใช้โมเดลเดียวทุกครั้ง ให้คัดลอกชื่อมันจาก agy models ใส่ใน MODEL ให้ตรงตามที่พิมพ์ออกมาทุกตัวอักษร ถ้าปล่อยว่าง agy จะเลือกเอง ถ้าคุณตั้งชื่อโมเดลที่ agy ไม่รู้จัก การรันด้วย -p จะหยุดพร้อมข้อผิดพลาด แทนที่จะแอบใช้โมเดลอื่น

🇨🇳 中文

在第 4 步里,route.py 是在大模型和小模型之间选。用 Gemini 时,要选的是另一样东西。你能用的模型就是你的方案提供的那些,agy models 会把它们列出来。你为每个任务选的是思考力度:--effort low、medium 或 high 说的是模型要想得多用力。少想一点更快,也少占你方案的额度。所以 gemini_route.py 保留第 4 步的规则,只改它选的东西:任务以某个简单的词开头时就少想一点,其余的都多想一点。-p 不开对话、只为一个任务运行 agy,就像 codex exec 那样。

想每次都用同一个模型,就从 agy models 里把它的名字原样抄进 MODEL。留空时,agy 自己选。如果你写了一个 agy 不认识的模型名,-p 运行会报错停下,而不会悄悄换用别的模型。

~/arms/gemini_route.py

"""gemini_route.py - send each job to the effort it needs: low effort for easy jobs.

    python3 gemini_route.py "check homework.txt"          print the agy command it would run
    python3 gemini_route.py --go "check homework.txt"     run that command

agy, the Antigravity CLI, runs Google's Gemini models. For each job you choose
how hard the model thinks: --effort low, medium or high. More thinking takes
longer and uses more of your quota. Many jobs do not need it: checking a text,
making a list, a summary. This file looks at the first word of the job and
picks the effort. The rule is simple on purpose: you can read it, and you can
change it when it picks wrong.
"""
import subprocess
import sys

# A model's name, copied exactly from what `agy models` prints. "" means agy's own choice.
MODEL = ""
# A job that starts with one of these words gets low effort.
SMALL_JOBS = ["check", "list", "summarize", "summarise", "rename", "translate", "spell"]


def pick_effort(job):
    first_word = job.split()[0].lower()
    if first_word in SMALL_JOBS:
        return "low"
    return "high"


def command_for(job):
    command = ["agy", "--effort", pick_effort(job)]
    if MODEL:
        command += ["--model", MODEL]
    # -p runs agy for this one job, without a chat, and then stops.
    return command + ["-p", job]


def main():
    go = "--go" in sys.argv
    jobs = [word for word in sys.argv[1:] if word != "--go"]
    for job in jobs:
        command = command_for(job)
        print(pick_effort(job), "<-", job)
        if go:
            # stdin=DEVNULL: with no keyboard behind it,
            # agy could wait for more input.
            subprocess.run(command, stdin=subprocess.DEVNULL)
        else:
            print("   ", " ".join(command[:-1]), '"' + job + '"')


if __name__ == "__main__":
    main()
python3 gemini_route.py "check school/homework.txt" "make a quiz from my science notes" \
  "list my projects" "fix the bug in projects/game/main.py"

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

low <- check school/homework.txt
    agy --effort low -p "check school/homework.txt"
high <- make a quiz from my science notes
    agy --effort high -p "make a quiz from my science notes"
low <- list my projects
    agy --effort low -p "list my projects"
high <- fix the bug in projects/game/main.py
    agy --effort high -p "fix the bug in projects/game/main.py"

🇬🇧 English

The same two jobs as in step 4 got the easy setting, and the rule can be wrong in the same way: change SMALL_JOBS when it is.

🇹🇭 ไทย

สองงานเดิมกับในขั้นตอนที่ 4 ได้การตั้งค่าแบบง่าย และกฎนี้ก็ผิดได้แบบเดียวกัน: เมื่อมันผิด ให้แก้ SMALL_JOBS

🇨🇳 中文

和第 4 步一样的那两个任务得到了简单的设置,这条规则也会以同样的方式出错:错了就改 SMALL_JOBS。

not run by us: this one starts agyเราไม่ได้รันเอง: คำสั่งนี้เริ่ม agy我们没有运行过:这一条会启动 agy

agy models
python3 gemini_route.py --go "check school/homework.txt"

G5 gemini_quiet_check.py: the quiet routine · gemini_quiet_check.py: งานประจำที่อยู่เงียบ ๆ · gemini_quiet_check.py:安静的例行任务

🇬🇧 English

This is the file of step 5 with one line changed: the command that --ask runs is now agy, with low effort. Python still does the looking, so on a normal day Gemini is not asked anything and nothing is used.

🇹🇭 ไทย

นี่คือไฟล์ของขั้นตอนที่ 5 ที่แก้ไปหนึ่งบรรทัด: คำสั่งที่ --ask รันตอนนี้คือ agy แบบคิดน้อย Python ยังเป็นผู้มองหาเหมือนเดิม ดังนั้นในวันปกติ Gemini จะไม่ถูกถามอะไร และไม่มีอะไรถูกใช้ไป

🇨🇳 中文

这就是第 5 步的文件,只改了一行:--ask 运行的命令现在是少想一点的 agy。找文件的仍然是 Python,所以平常日子里不会问 Gemini 任何事,什么也不花。

~/arms/gemini_quiet_check.py

"""gemini_quiet_check.py - a quiet routine: say nothing unless a folder holds a big file.

    python3 gemini_quiet_check.py ~/Downloads 500           files over 500 MB, if any
    python3 gemini_quiet_check.py ~/Downloads 500 --ask     if there are, ask Gemini about them

Python does the looking, and looking costs nothing. Gemini is asked only when
there is something to ask about, so on most days this routine uses no quota
and prints nothing at all. Nothing prints means nothing to read.
"""
import pathlib
import subprocess
import sys

# 1 MB is 1,000,000 bytes, the way a Mac's Finder counts.
MB = 1_000_000


def big_files(folder, limit_mb):
    found = []
    # rglob("*") walks into every folder inside this one, too.
    for path in folder.rglob("*"):
        if path.is_file() and path.stat().st_size > limit_mb * MB:
            found.append(path)
    return found


def main():
    folder = pathlib.Path(sys.argv[1]).expanduser()
    limit_mb = float(sys.argv[2])
    found = big_files(folder, limit_mb)
    if not found:
        return
    lines = []
    for path in found:
        size = path.stat().st_size / MB
        lines.append(f"{size:8.1f} MB  {path.relative_to(folder).as_posix()}")
    print("\n".join(lines))
    if "--ask" in sys.argv:
        job = ("These files in " + str(folder) + " are big. For each one, say in one "
               "line whether I can probably delete it, and why. Do not delete anything.\n"
               + "\n".join(lines))
        command = ["agy", "--effort", "low", "-p", job]
        # stdin=DEVNULL: with no keyboard behind it,
        # agy could wait for more input.
        subprocess.run(command, stdin=subprocess.DEVNULL)


if __name__ == "__main__":
    main()
python3 gemini_quiet_check.py ~/Downloads 500

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

   700.0 MB  old-game-installer.dmg

not run by us: with --ask it starts agyเราไม่ได้รันเอง: เมื่อมี --ask มันจะเริ่ม agy我们没有运行过:加上 --ask 就会启动 agy

python3 gemini_quiet_check.py ~/Downloads 500 --ask

G6 Every Monday: cron, not /schedule · ทุกวันจันทร์: ใช้ cron ไม่ใช่ /schedule · 每个星期一:用 cron,不用 /schedule

🇬🇧 English

The crontab of step 6 works with two changes: the file is gemini_quiet_check.py, and PATH holds the folder that which agy prints. The installer puts agy in ~/.local/bin, in your home folder, and cron does not turn ~ into your home folder in a PATH= line, so write the folder out in full, as which prints it. Everything else in step 6 holds, the CAREFUL box about Full Disk Access too.

agy also has a clock of its own: /schedule in a chat sets a timer, or a schedule that repeats, which wakes the agent with an instruction. Each time it wakes, the model runs and uses your plan, even on a day with nothing to say. cron starts Python, and Python asks Gemini only when there is something to ask, so the quiet routine stays on cron.

🇹🇭 ไทย

crontab ของขั้นตอนที่ 6 ใช้ได้โดยแก้สองอย่าง: ไฟล์คือ gemini_quiet_check.py และ PATH มีโฟลเดอร์ที่ which agy พิมพ์ออกมา ตัวติดตั้งวาง agy ไว้ใน ~/.local/bin ในโฟลเดอร์บ้านของคุณ และ cron ไม่แปลง ~ เป็นโฟลเดอร์บ้านของคุณในบรรทัด PATH= จึงต้องเขียนโฟลเดอร์ให้เต็มแบบที่ which พิมพ์ออกมา อย่างอื่นในขั้นตอนที่ 6 ยังใช้ได้ทั้งหมด รวมถึงกล่อง CAREFUL เรื่อง Full Disk Access ด้วย

agy ยังมีนาฬิกาของตัวเองด้วย: /schedule ในแชตตั้งตัวจับเวลา หรือตารางที่ทำซ้ำ ซึ่งปลุกเอเจนต์พร้อมคำสั่งหนึ่ง ทุกครั้งที่มันตื่น โมเดลจะทำงานและใช้แผนของคุณ แม้ในวันที่ไม่มีอะไรต้องพูด cron เริ่ม Python และ Python ถาม Gemini เฉพาะเมื่อมีเรื่องให้ถาม งานประจำแบบเงียบจึงอยู่กับ cron

🇨🇳 中文

第 6 步的 crontab 改两处就能用:文件换成 gemini_quiet_check.py,PATH 里放 which agy 打印出来的那个文件夹。安装程序把 agy 放在你主文件夹里的 ~/.local/bin,而 cron 在 PATH= 这一行里不会把 ~ 换成你的主文件夹,所以要照 which 打印的样子把文件夹写全。第 6 步里的其他内容都照旧,包括关于完全磁盘访问权限 Full Disk Access 的 CAREFUL 框。

agy 也有自己的时钟:在对话里用 /schedule 可以设一个定时器,或一个重复的时间表,到时候带着一条指令把智能体叫醒。每醒一次,模型就运行一次,就占用你的方案额度,哪怕那天什么也没得说。cron 启动的是 Python,而 Python 只在有事可问时才问 Gemini,所以安静的例行任务还是交给 cron。

not run by us: in the Terminalเราไม่ได้รันเอง: ใน Terminal我们没有运行过:在终端里

which agy
crontab -e

not run by us: the two lines to type into the crontab, with your own nameเราไม่ได้รันเอง: สองบรรทัดที่ต้องพิมพ์ลงใน crontab โดยใส่ชื่อของคุณเอง我们没有运行过:要输入 crontab 的两行,换上你自己的名字

PATH=/Users/YOUR-NAME/.local/bin:/usr/bin:/bin
0 9 * * 1 cd ~/arms && python3 gemini_quiet_check.py ~/Downloads 500 --ask >> log/quiet.txt 2>&1

G7 gemini_ask.py: write down what each run cost · gemini_ask.py: จดว่าแต่ละครั้งที่รันใช้ไปเท่าไร · gemini_ask.py:记下每次运行花了多少

🇬🇧 English

This is step 7's ask.py for Gemini. With --output-format json, agy -p prints its whole reply as one JSON object: the answer in response, whether it finished in status, and the tokens in usage, as input_tokens, output_tokens and cache_read_tokens, the input it had read a moment before. gemini_ask.py writes the same line to log/asks.jsonl that ask.py writes, so week.py reads a Gemini log without a change. It also passes --print-timeout 10m, so that a routine nobody is watching stops after ten minutes instead of waiting for ever.

🇹🇭 ไทย

นี่คือ ask.py ของขั้นตอนที่ 7 สำหรับ Gemini เมื่อมี --output-format json คำสั่ง agy -p จะพิมพ์คำตอบทั้งหมดเป็นออบเจกต์ JSON หนึ่งก้อน: คำตอบอยู่ใน response มันทำเสร็จหรือไม่อยู่ใน status และโทเค็นอยู่ใน usage เป็น input_tokens output_tokens และ cache_read_tokens ซึ่งคืออินพุตที่มันเพิ่งอ่านไปเมื่อครู่ gemini_ask.py เขียนบรรทัดแบบเดียวกับที่ ask.py เขียนลงใน log/asks.jsonl ดังนั้น week.py อ่านบันทึกของ Gemini ได้โดยไม่ต้องแก้ มันยังส่ง --print-timeout 10m ด้วย เพื่อให้งานประจำที่ไม่มีใครดูอยู่หยุดหลังสิบนาที แทนที่จะรอไปตลอด

🇨🇳 中文

这就是第 7 步的 ask.py 的 Gemini 版。加上 --output-format json 后,agy -p 会把整个回复打印成一个 JSON 对象:答案在 response 里,有没有做完在 status 里,词元在 usage 里,分成 input_tokens、output_tokens 和 cache_read_tokens,最后这个是它刚刚读过的输入。gemini_ask.py 往 log/asks.jsonl 写的那一行和 ask.py 写的一样,所以 week.py 不用改就能读 Gemini 的日志。它还加了 --print-timeout 10m,让一个没人看着的例行任务十分钟后就停下,而不是一直等下去。

~/arms/gemini_ask.py

"""gemini_ask.py - the one door to Gemini: run a job, write down what it cost, stop at a cap.

    python3 gemini_ask.py hand "summarize school/science/notes.md"     a job you typed
    python3 gemini_ask.py quiet "check ~/Downloads for big files"      a routine's job
    python3 gemini_ask.py --dry hand "list my projects"                show, run nothing

The first word after gemini_ask.py is a name you choose: "hand" for jobs you
type yourself, and one name for each routine. Every run adds one line to
log/asks.jsonl with the name, the job and the tokens Gemini used. A name with a
weekly cap in CAPS is not run again once its last 7 days reach the cap.
week.py reads the same file and makes a report from it.
"""
import datetime
import json
import pathlib
import subprocess
import sys

LOG = pathlib.Path("log") / "asks.jsonl"
# Tokens a name may use in 7 days. A name not listed here has no cap.
CAPS = {"quiet": 50_000, "digest": 200_000}
EFFORT = "low"


def used_this_week(name):
    # Add up the tokens of every line with this name from the last 7 days.
    if not LOG.exists():
        return 0
    week_ago = datetime.datetime.now() - datetime.timedelta(days=7)
    total = 0
    for line in LOG.read_text(encoding="utf-8").splitlines():
        run = json.loads(line)
        when = datetime.datetime.fromisoformat(run["when"])
        if run["name"] == name and when > week_ago:
            total += run["input"] + run["output"]
    return total


def reply_of(text):
    # agy --output-format json prints one JSON object. No object means it failed.
    if not text.strip().startswith("{"):
        return {}
    return json.loads(text)


def main():
    dry = "--dry" in sys.argv
    words = [word for word in sys.argv[1:] if word != "--dry"]
    name, job = words[0], words[1]
    used = used_this_week(name)
    cap = CAPS.get(name)
    if cap and used >= cap:
        print(f"skipped: {name} used {used:,} of its {cap:,} tokens this week")
        return
    # --print-timeout: a routine that nobody watches must not wait for ever.
    command = ["agy", "--output-format", "json", "--effort", EFFORT,
               "--print-timeout", "10m", "-p", job]
    if dry:
        print(" ".join(command[:-1]), '"' + job + '"')
        print(f"{name} has used {used:,} tokens in the last 7 days")
        return
    # capture_output keeps the answer here, so that Python can read the tokens.
    result = subprocess.run(command, capture_output=True, text=True,
                            stdin=subprocess.DEVNULL)
    reply = reply_of(result.stdout)
    usage = reply.get("usage", {})
    run = {
        "when": datetime.datetime.now().isoformat(timespec="seconds"),
        "name": name,
        "job": job,
        "input": usage.get("input_tokens", 0),
        "cached": usage.get("cache_read_tokens", 0),
        "output": usage.get("output_tokens", 0),
    }
    LOG.parent.mkdir(exist_ok=True)
    with LOG.open("a", encoding="utf-8") as log:
        log.write(json.dumps(run) + "\n")
    # "SUCCESS" means agy finished the job. Anything else: show why it did not.
    if reply.get("status") == "SUCCESS":
        print(reply["response"])
    else:
        print(result.stderr)
    print(f"tokens: {run['input']:,} in ({run['cached']:,} of them cached), "
          f"{run['output']:,} out")


if __name__ == "__main__":
    main()
python3 gemini_ask.py --dry hand "check school/homework.txt"

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

agy --output-format json --effort low --print-timeout 10m -p "check school/homework.txt"
hand has used 0 tokens in the last 7 days

🇬🇧 English

A run without a chat cannot ask you anything. When the job needs a tool that agy would ask you about, such as running a command, it skips that tool, carries on, and writes a notice about it to the error output. To let a routine run one command, allow it ahead of time in ~/.gemini/antigravity-cli/settings.json, with a rule like the one below; if the file already holds settings, add "permissions" to them. agy also has --dangerously-skip-permissions, which allows everything, writing and deleting files too. Never give that to a routine.

To log and cap the quiet routine of G5 too, change the line in gemini_quiet_check.py that builds its command to command = ["python3", "gemini_ask.py", "quiet", job].

🇹🇭 ไทย

การรันที่ไม่มีแชตถามอะไรคุณไม่ได้ เมื่องานต้องใช้เครื่องมือที่ agy จะถามคุณก่อน เช่นการรันคำสั่ง มันจะข้ามเครื่องมือนั้น ทำต่อไป และเขียนประกาศเรื่องนี้ลงในเอาต์พุตข้อผิดพลาด ถ้าจะให้งานประจำรันคำสั่งหนึ่งคำสั่ง ให้อนุญาตไว้ล่วงหน้าใน ~/.gemini/antigravity-cli/settings.json ด้วยกฎแบบข้างล่าง ถ้าไฟล์มีการตั้งค่าอยู่แล้ว ให้เพิ่ม "permissions" เข้าไป agy ยังมี --dangerously-skip-permissions ซึ่งอนุญาตทุกอย่าง รวมถึงการเขียนและลบไฟล์ อย่าให้สิ่งนั้นกับงานประจำเด็ดขาด

ถ้าจะบันทึกและจำกัดเพดานของงานประจำแบบเงียบใน G5 ด้วย ให้แก้บรรทัดใน gemini_quiet_check.py ที่สร้างคำสั่งของมันเป็น command = ["python3", "gemini_ask.py", "quiet", job]

🇨🇳 中文

不开对话的运行没法问你任何事。当任务需要一个 agy 本来会先问你的工具时,比如运行一条命令,它会跳过那个工具,接着做下去,并在错误输出里写一条相关的提示。想让一个例行任务能运行某一条命令,就提前在 ~/.gemini/antigravity-cli/settings.json 里用下面这样的规则允许它;如果这个文件里已经有设置,就把 "permissions" 加进去。agy 还有 --dangerously-skip-permissions,它什么都允许,连写文件和删文件也允许。千万别把它交给例行任务。

想把 G5 的安静例行任务也记进日志、加上上限,就把 gemini_quiet_check.py 里构造命令的那一行改成 command = ["python3", "gemini_ask.py", "quiet", job]。

not run by us: ~/.gemini/antigravity-cli/settings.jsonเราไม่ได้รันเอง: ~/.gemini/antigravity-cli/settings.json我们没有运行过:~/.gemini/antigravity-cli/settings.json

{
  "permissions": {
    "allow": ["command(regex:python3 .agents/skills/text-check/check.py .*)"]
  }
}

not run by us: without --dry it starts agyเราไม่ได้รันเอง: ถ้าไม่มี --dry มันจะเริ่ม agy我们没有运行过:不加 --dry 就会启动 agy

python3 gemini_ask.py hand "check school/homework.txt"
python3 gemini_ask.py digest "summarize the newest notes in school/ in five lines"

G8 week.py, unchanged · week.py ไม่ต้องแก้ · week.py,原样不动

🇬🇧 English

week.py from step 8 reads log/asks.jsonl, and gemini_ask.py writes the same fields, so run it as it is: python3 week.py. The caps are in CAPS in both gemini_ask.py and week.py; change them in both. /usage in a chat shows what your plan has left; your log shows which of your own jobs used it.

🇹🇭 ไทย

week.py จากขั้นตอนที่ 8 อ่าน log/asks.jsonl และ gemini_ask.py เขียนช่องข้อมูลแบบเดียวกัน จึงรันได้ตามเดิม: python3 week.py เพดานอยู่ใน CAPS ทั้งใน gemini_ask.py และ week.py ให้แก้ทั้งสองที่ /usage ในแชตแสดงว่าแผนของคุณเหลือเท่าไร ส่วนบันทึกของคุณแสดงว่างานไหนของคุณเองที่ใช้มันไป

🇨🇳 中文

第 8 步的 week.py 读的是 log/asks.jsonl,而 gemini_ask.py 写的字段一模一样,所以原样运行就行:python3 week.py。上限在 gemini_ask.py 和 week.py 两个文件的 CAPS 里,要改就两处都改。对话里的 /usage 显示你的方案还剩多少;你的日志显示是你自己的哪些任务用掉了它。

G9 make_map.py: one map for both · make_map.py: แผนที่เดียวสำหรับทั้งสอง · make_map.py:两个共用一张地图

🇬🇧 English

make_map.py from step 9 writes the map into AGENTS.md, and Antigravity reads that file at the start of every chat, so the same map serves Codex and Gemini. One difference matters for where you put rules: Antigravity does not read an AGENTS.md in a folder below the one you started it in, such as school/AGENTS.md, even while it works in that folder. Keep the map and the rules in the workspace's own AGENTS.md.

🇹🇭 ไทย

make_map.py จากขั้นตอนที่ 9 เขียนแผนที่ลงใน AGENTS.md และ Antigravity อ่านไฟล์นั้นตอนเริ่มทุกแชต แผนที่เดียวกันจึงใช้ได้ทั้ง Codex และ Gemini มีความต่างหนึ่งอย่างที่สำคัญต่อการวางกฎ: Antigravity ไม่อ่าน AGENTS.md ในโฟลเดอร์ที่อยู่ต่ำกว่าโฟลเดอร์ที่คุณเริ่มมัน เช่น school/AGENTS.md แม้ตอนที่มันทำงานอยู่ในโฟลเดอร์นั้น ให้เก็บแผนที่และกฎไว้ใน AGENTS.md ของพื้นที่ทำงานเอง

🇨🇳 中文

第 9 步的 make_map.py 把地图写进 AGENTS.md,而 Antigravity 每次对话开始时都会读这个文件,所以同一张地图 Codex 和 Gemini 都能用。有一个区别关系到规则放在哪里:在你启动它的文件夹下面的文件夹里,比如 school/AGENTS.md,Antigravity 不会去读,哪怕它正在那个文件夹里干活。把地图和规则都放在工作区自己的 AGENTS.md 里。

G10 gemini_how_big.py, and /context instead of /status · gemini_how_big.py และ /context แทน /status · gemini_how_big.py,用 /context 代替 /status

🇬🇧 English

This is step 10's how_big.py with Antigravity's limit in place of Codex's: it reads at most 24,000 bytes of each rules file, AGENTS.md included, and cuts off the rest.

🇹🇭 ไทย

นี่คือ how_big.py ของขั้นตอนที่ 10 ที่ใช้ขีดจำกัดของ Antigravity แทนของ Codex: มันอ่านไฟล์กฎแต่ละไฟล์ได้มากที่สุด 24,000 ไบต์ รวมถึง AGENTS.md และตัดส่วนที่เหลือทิ้ง

🇨🇳 中文

这是第 10 步的 how_big.py,把 Codex 的上限换成了 Antigravity 的:每个规则文件,包括 AGENTS.md,它最多读 24,000 字节,其余的截掉。

~/arms/gemini_how_big.py

"""gemini_how_big.py - how many tokens would it cost Gemini to read every file here?

    python3 gemini_how_big.py            in your workspace

A token is a piece of a word. For English, 4 letters make about 1 token, so
this divides each file's size by 4. That is a guess, not a count, but it is
close enough to show the point: reading everything costs far more than reading
the map in AGENTS.md first. It also checks AGENTS.md against Antigravity's
limit: it reads at most 24,000 bytes of a rule file, and cuts off the rest.
"""
import pathlib

LETTERS_PER_TOKEN = 4
RULE_FILE_LIMIT = 24_000


def main():
    home = pathlib.Path.cwd()
    sizes = {}
    for path in home.rglob("*"):
        # Files inside .git are git's own records, not your work.
        if path.is_file() and ".git" not in path.parts:
            sizes[path.relative_to(home)] = path.stat().st_size
    total = sum(sizes.values()) // LETTERS_PER_TOKEN
    biggest = sorted(sizes, key=sizes.get, reverse=True)[:5]
    print(f"{len(sizes)} files, about {total:,} tokens to read them all")
    print("the biggest:")
    for name in biggest:
        print(f"  {sizes[name] // LETTERS_PER_TOKEN:>8,} tokens  {name.as_posix()}")
    agents = sizes.get(pathlib.Path("AGENTS.md"), 0)
    state = "fine" if agents <= RULE_FILE_LIMIT else "TOO BIG: Antigravity cuts the end"
    print(f"AGENTS.md: {agents:,} bytes of {RULE_FILE_LIMIT:,}, about "
          f"{agents // LETTERS_PER_TOKEN:,} tokens: {state}")


if __name__ == "__main__":
    main()
python3 gemini_how_big.py

what it printedสิ่งที่มันพิมพ์ออกมา它打印的内容

23 files, about 6,848 tokens to read them all
the biggest:
       838 tokens  gemini_ask.py
       644 tokens  make_workspace.py
       610 tokens  inbox.py
       556 tokens  week.py
       531 tokens  example_asks.jsonl
AGENTS.md: 766 bytes of 24,000, about 191 tokens: fine

🇬🇧 English

The Gemini workspace holds 23 files, about 6,848 tokens, and its map is far inside the limit. Inside an agy chat, the commands for its short-term memory are not Codex's:

  • /context shows how full the chat's context window is, as a grid, with how much each kind of thing takes;
  • there is no command to compact: as a chat grows, agy itself replaces the older turns with short summaries, called checkpoints, and /rewind goes back to an earlier point;
  • /clear, or /new, starts an empty chat. One job, one chat, as in step 10. /btw asks a quick side question that stays out of the chat.

🇹🇭 ไทย

พื้นที่ทำงานของ Gemini มี 23 ไฟล์ ประมาณ 6,848 โทเค็น และแผนที่ของมันอยู่ต่ำกว่าขีดจำกัดมาก ในแชตของ agy คำสั่งสำหรับความจำระยะสั้นของมันไม่เหมือนของ Codex:

  • /context แสดงว่าหน้าต่างบริบทของแชตเต็มแค่ไหน เป็นตาราง พร้อมบอกว่าแต่ละอย่างกินที่เท่าไร;
  • ไม่มีคำสั่งให้ย่อ: เมื่อแชตยาวขึ้น agy จะแทนที่รอบเก่า ๆ ด้วยสรุปสั้น ๆ ที่เรียกว่าเช็กพอยต์เอง และ /rewind ย้อนกลับไปยังจุดก่อนหน้า;
  • /clear หรือ /new เริ่มแชตว่าง งานเดียว แชตเดียว แบบในขั้นตอนที่ 10 /btw ถามคำถามข้างทางสั้น ๆ ที่ไม่เข้าไปอยู่ในแชต

🇨🇳 中文

Gemini 的工作区有 23 个文件,大约 6,848 个词元,它的地图远在上限之内。在 agy 的对话里,管它短期记忆的命令和 Codex 的不一样:

  • /context 用一个方格图显示对话的上下文窗口有多满,以及每类东西各占多少;
  • 没有压缩的命令:对话变长时,agy 自己会把较早的几轮换成简短的摘要,叫作检查点,/rewind 可以回到更早的某一点;
  • /clear 或 /new 开一个空对话。一个任务,一个对话,和第 10 步一样。/btw 问一个不进入对话的简短旁支问题。

G11 note.py, and /learn instead of Memories · note.py และ /learn แทน Memories · note.py,用 /learn 代替 Memories

🇬🇧 English

note.py from step 11 is Python and needs no change. At the end of the week, give the list to agy in one job, with the same words as in step 11, and git diff shows what it changed in the skill.

Antigravity's manual describes no memory that writes itself in the background, as Codex's Memories does. What it has is /learn: at the end of a chat in which you corrected it, /learn turns your corrections into a rule or a skill, and shows you what it would write before it writes it. Either way the lesson ends up in a file you can read, and git keeps it.

🇹🇭 ไทย

note.py จากขั้นตอนที่ 11 เป็น Python และไม่ต้องแก้ ตอนสิ้นสัปดาห์ ให้ส่งรายการให้ agy ในงานเดียว ด้วยคำเดียวกับในขั้นตอนที่ 11 แล้ว git diff จะแสดงว่ามันแก้อะไรในสกิล

คู่มือของ Antigravity ไม่ได้อธิบายความจำที่เขียนตัวเองอยู่เบื้องหลังแบบ Memories ของ Codex สิ่งที่มันมีคือ /learn: ตอนท้ายของแชตที่คุณแก้มัน /learn เปลี่ยนสิ่งที่คุณแก้ให้เป็นกฎหรือสกิล และแสดงให้คุณดูว่ามันจะเขียนอะไรก่อนที่จะเขียน ไม่ว่าทางไหน บทเรียนก็จะไปอยู่ในไฟล์ที่คุณอ่านได้ และ git เก็บมันไว้

🇨🇳 中文

第 11 步的 note.py 是 Python,不用改。周末时,用和第 11 步一样的话,把清单作为一个任务交给 agy,git diff 会显示它在技能里改了什么。

Antigravity 的手册里没有讲到像 Codex 的 Memories 那样在后台自己写下来的记忆。它有的是 /learn:在一次你纠正过它的对话结束时,/learn 把你的纠正变成一条规则或一个技能,并在写之前先给你看它要写什么。不管哪种方式,教训最后都会落在一个你读得到的文件里,由 git 保存。

not run by us: type this in an agy chat, after you corrected itเราไม่ได้รันเอง: พิมพ์สิ่งนี้ในแชตของ agy หลังจากที่คุณแก้มันแล้ว我们没有运行过:纠正过它之后,在 agy 对话中输入

/learn

G12 weather.py, and where MCP servers go · weather.py และที่ที่เซิร์ฟเวอร์ MCP อยู่ · weather.py,以及 MCP 服务器放在哪里

🇬🇧 English

weather.py from step 12 needs no change, and nor does the rule it asks you to add to AGENTS.md: Antigravity reads the same rule and runs the same one-line command. When agy should talk to a service directly instead, through an MCP server, the workspace's servers go in .agents/mcp_config.json, and the ones for every workspace in ~/.gemini/config/mcp_config.json. /mcp in a chat shows them, and lets you turn off the ones you do not need: every server's tools take room in the context window.

🇹🇭 ไทย

weather.py จากขั้นตอนที่ 12 ไม่ต้องแก้ และกฎที่มันขอให้คุณเพิ่มใน AGENTS.md ก็ไม่ต้องแก้: Antigravity อ่านกฎเดียวกันและรันคำสั่งบรรทัดเดียวเดียวกัน เมื่อ agy ควรคุยกับบริการโดยตรงแทน ผ่านเซิร์ฟเวอร์ MCP เซิร์ฟเวอร์ของพื้นที่ทำงานจะอยู่ใน .agents/mcp_config.json และของทุกพื้นที่ทำงานอยู่ใน ~/.gemini/config/mcp_config.json /mcp ในแชตแสดงมัน และให้คุณปิดตัวที่ไม่ต้องใช้ได้: เครื่องมือของทุกเซิร์ฟเวอร์กินที่ในหน้าต่างบริบท

🇨🇳 中文

第 12 步的 weather.py 不用改,它让你加进 AGENTS.md 的那条规则也不用改:Antigravity 读的是同一条规则,运行的是同一条一行命令。如果要让 agy 改为通过 MCP 服务器直接和某个服务对话,工作区的服务器放在 .agents/mcp_config.json 里,给所有工作区用的放在 ~/.gemini/config/mcp_config.json 里。对话里的 /mcp 会列出它们,还能让你关掉不需要的:每个服务器的工具都要占上下文窗口的地方。

G13 inbox.py, unchanged · inbox.py ไม่ต้องแก้ · inbox.py,原样不动

🇬🇧 English

inbox.py from step 13 is Python and needs no change. The rule in AGENTS.md that sends every web page to artifacts/ holds for agy too, so a page you ask Gemini for lands at the top of the same inbox as a page Codex made. Start it with python3 inbox.py and open http://localhost:8765, as in step 13.

🇹🇭 ไทย

inbox.py จากขั้นตอนที่ 13 เป็น Python และไม่ต้องแก้ กฎใน AGENTS.md ที่ส่งทุกหน้าเว็บไปไว้ใน artifacts/ ใช้กับ agy ด้วย ดังนั้นหน้าที่คุณขอจาก Gemini จะไปอยู่บนสุดของกล่องจดหมายเดียวกับหน้าที่ Codex ทำ เริ่มมันด้วย python3 inbox.py แล้วเปิด http://localhost:8765 แบบในขั้นตอนที่ 13

🇨🇳 中文

第 13 步的 inbox.py 是 Python,不用改。AGENTS.md 里那条把每个网页都放进 artifacts/ 的规则,对 agy 也有效,所以你让 Gemini 做的网页,会出现在和 Codex 做的网页同一个收件箱的最上面。像第 13 步那样,用 python3 inbox.py 启动它,再打开 http://localhost:8765。