先别写 Multi-Agent。给 Agent 一个工作目录窗口是内存。目录才是硬盘。Agent 的第一课不是图画几条边是四个文件原语。2026 年 10 月中文技术时间线被三类 Agent 教程刷满了。第一类教你画图reason → tools → respond再加一个 Supervisor。第二类教你接 MCP一次挂上十几台 server。第三类盘点框架LangGraph、CrewAI、AutoGen、谁家的create_agent又改名了。这三类都能跑通 Demo。它们共同跳过了 Claude Code、Codex、Cursor Agent 真正在跑的那一层Agent 先有一个工作目录。图是后来才加上去的。打开任意一个能连续干三小时活的编码 Agent它的核心工具不是搜索引擎不是向量库是四件事ls 看目录 read_file 读文件 write_file 写文件 grep 搜文本记忆在文件里。规则在AGENTS.md里。技能在skills/*/SKILL.md里启动时只进目录用到才读正文。大段产出写到out/回给你的是路径和二十行摘要。我把这套约定叫做工作目录协议。协议能用不到 200 行 Python 写完。下面这份代码对着 2026 年 10 月的栈langgraph 1.2.16、langchain-deepseek 1.1.1、ChatDeepSeek(modeldeepseek-v4-pro)。配套实验在lab/workdir-agent/你可以今天下午抄走。这篇文章只做一件事把「目录即运行时」写成一份能跑的教程。图画法、MCP 清单、框架横评别人已经写过了。窗口是内存目录才是硬盘大模型的上下文窗口看起来像无限硬盘。广告上写 1M token。产线上你每轮真正付得起、模型真正读得清的通常是前面那一小段稳定内容和后面那一小段新东西。把窗口当硬盘用会出现三种很具体的病每轮重贴。14 个工具的 JSON Schema、两份 MCP、一份 6.8k 的技能说明书每一轮原样再发一遍。前缀一变prompt cache 整段作废。产出回灌。Agent 写了 80 行周报周报又进下一轮 messages。三轮之后窗口里一半是它自己的作文。技能抢戏。本单只用csv-reportgit-commit的全文也坐在第一排。模型开始认错道具。工作目录协议把三件事拆开东西放哪进窗口的时机硬规则AGENTS.md每轮稳定前缀字节级不变技能目录skills/*/SKILL.md的 name 一句 description每轮只进索引技能正文同一个SKILL.md的正文模型判断相关之后read_file一次输入数据data/按需grep/read_file产出out/写进去。回复里只留路径工作笔记NOTES.md追加。跨会话还在窗口是 RAM贵、易挥发、有容量上限。目录是磁盘便宜、可 grep、换模型也不丢。Claude Code 把项目约定写进CLAUDE.md。Codex 和一众开源 harness 把身份、记忆、技能放进工作区文件夹。它们的差别在文件名。相同的是这件事Agent 的长期状态不活在 messages 数组里。四个文件原语覆盖 80% 的 Agent 劳动把工具清单从 14 个砍到 4 个听起来像功能残废。打开一个能干活的编码 Agent 看它的 trace80% 的调用就是这四个。原语对应操作系统Agent 用它干什么ls列目录先看工作区长什么样禁止盲写read_file读文件分段读带行号大文件禁止一次灌进回复write_file写文件产出落盘。父目录必须已存在grep搜文本从 csv / 日志 / 规则里定位不靠模型口算这四个工具有一条共同的硬约束路径锁在 workspace 里。相对路径解析之后必须以工作目录为前缀。少了这一行read_file(../../.env)会变成你自己给自己开的后门。ROOTPath(__file__).resolve().parent/workspacedefrel(p:str)-Path:target(ROOT/p).resolve()ifnotstr(target).startswith(str(ROOT.resolve())os.sep)andtarget!ROOT.resolve():raiseValueError(fpath escapes workspace:{p})returntarget其余工具——退款、开票、发券、搜网页——全部降级成技能文件里的说明书或者降级成「需要时再bind的短名单」。它们不再默认出现在每一轮的 schema 里。四个原语是操作系统。业务工具是应用程序。应用程序按需加载。操作系统每轮都在。一份最小可运行的工作区长这样workspace/ AGENTS.md # 硬规则稳定前缀 TASK.md # 本周任务 NOTES.md # 工作笔记Agent 追加 skills/ csv-report/SKILL.md # 用到才读正文 git-commit/SKILL.md # 本任务应跳过 data/orders.csv # 只读输入 out/ # 产出AGENTS.md建议压在 80 行以内。写具体可验证的句子「数字必须来自文件」「产出写到 out/」「禁止一次读完所有 skills」。形容词留给产品文案。稳定前缀本会话里不许改的那一段Prompt cache 的规则很朴素从左到右字节一变后面整段作废。所以工作目录协议规定发给模型的消息分两截稳定前缀。AGENTS.md全文 skill index名字和一句描述 四个工具的 schema。本会话内字节级不变。工具按名字排序不按「这一轮可能用得上」动态增删。可变后缀。用户任务、工具返回、Agent 自己的短回复。只在这里生长。配套脚本prefix_stats.py不调模型只称重量。实验仓这一份的数字是skill index 44 tok stable prefix 228 tok if dump csvskills 494 tok实验仓很小差别看起来不大。把它放大到产线常见规模账会很难看做法每轮固定前缀cachebind_tools(ALL_TOOLS)14 个业务工具 两台 MCP约 6.8k schema再加技能全文前缀每轮因工具名单微调而抖动工作目录协议4 个原语 skill index约 0.2k1k前缀字节级稳定命中便宜输入DeepSeek V4 Pro 对 cache hit 的输入计价显著低于 cache miss。你把工具名单写成「按意图动态 bind」等于每一轮主动把折扣关掉。稳定前缀的工程要点就三条System 消息在循环外构造一次循环内原样复用。不要f当前时间 {datetime.now()}塞进 system。时间放进 Human 消息。工具 schema 排序固定。TOOLS [grep, ls, read_file, write_file]和[ls, read_file, write_file, grep]对模型差不多对 cache 是两份前缀。限制工具用tool_choice/ 运行时拦截不要删 schema。删一个工具前缀就变了。技能是文件启动时只进目录Skill 不是 system prompt 里的一章长文。它是一个文件夹skills/csv-report/SKILL.md文件头两行给索引用---name:csv-reportdescription:把 csv 做成带数字来源的 Markdown 周报---启动时稳定前缀只吃到- csv-report: 把 csv 做成带数字来源的 Markdown 周报 [skills/csv-report/SKILL.md] - git-commit: 按仓库约定写提交说明不在本任务使用 [skills/git-commit/SKILL.md]模型看见任务是「做退款风险周报」自己read_file(skills/csv-report/SKILL.md)。git-commit的正文整场都不会进窗口。这就是渐进披露。十个技能在启动时的成本约等于十行描述。用到第三个才付第三份正文的 token。description决定模型会不会点开。写成「处理文件」等于没写。写成「把 csv 做成带数字来源的 Markdown 周报」才是可路由的。技能正文里写步骤写验收写「不要把整份 csv 贴进对用户的回复」。步骤比形容词有用。不到 200 行把协议跑起来实验仓入口是lab/workdir-agent/agent.py。核心就这些。四个工具。路径全部走rel()。read_file带行号默认 200 行文件超过 80KB 截断。write_file拒绝创建不存在的父目录——逼 Agent 先ls。tooldefls(path:str.)-str:列出工作目录下某个相对路径。默认列出根目录。...tooldefread_file(path:str,offset:int1,limit:int200)-str:按行读取工作目录里的文本文件。offset 从 1 起。...tooldefwrite_file(path:str,content:str)-str:把文本写入工作目录。父目录必须已存在。...tooldefgrep(pattern:str,path:str.,glob:str*)-str:在工作目录里按正则搜文本。最多 50 条。...TOOLS[ls,read_file,write_file,grep]图仍然是教科书结构。变的是工具的数量和 system 从哪来。defstable_prefix()-str:agents(ROOT/AGENTS.md).read_text(encodingutf-8)return(agents\n\n## skill index\nskill_index()\n\n工具只有四个ls / read_file / write_file / grep。它们的 schema 本会话内不会变。)defreason(state:AgentState):sysSystemMessage(contentstable_prefix())respmake_model().invoke([sys,*state[messages]])return{messages:[resp]}stable_prefix()读磁盘不读 messages。AGENTS.md你改了下一轮生效模型改不了自己的前缀。这是协议故意留的单向门。模型用ChatDeepSeekmodelChatDeepSeek(modeldeepseek-v4-pro,temperature0,timeout120,)modelmodel.bind_tools(TOOLS)thinking 开不开随你。开了之后推理进reasoning_content条件边仍然只认tool_calls。四个原语的 docstring 写清楚「何时用」比把推理开到 max 更管用。本地跑cdlab/workdir-agent python3-mvenv .venvsource.venv/bin/activate pipinstall-rrequirements.txt python prefix_stats.py# 不调模型exportDEEPSEEK_API_KEY... python agent.py一次真实任务csv 变成周报TASK.md写得很短把 data/orders.csv 做成退款风险周报。 输出out/weekly.md 格式按 skills/csv-report/SKILL.md 范围statuspaid 且 created 在 2026-10-13 至 2026-10-19含 风险同一 uid 窗口内支付 3 笔或单笔 amount_cents 50000 金额在 csv 里是分展示写成元csv 只有 10 行是故意的。任务的难点不在数据量在协议有没有被遵守先ls看见data/skills/out/。read_file(TASK.md)再read_file(skills/csv-report/SKILL.md)。git-commit的正文不读。grep/read_file拿数字。金额用文件里的分换算禁止口算凭感觉。write_file(out/weekly.md, ...)。回复里只给路径和摘要。NOTES.md追加不超过 5 行决策。TASK.md标 done。跑完之后磁盘上的事实是out/weekly.md 支付 7 笔 / 1507.00 元 / 风险 uidu12、u9 NOTES.md 三条短决策 TASK.md 状态done窗口里没有周报全文。下一轮任务从ls开始周报还在out/。换一个模型接着干它读得到同一份文件。周报正文长这样。每个数字能指回文件把这份周报和「模型在对话框里直接回一篇 Markdown」并排放。后者漂亮前者可归档、可 diff、可给下一个 Agent 当输入。工作目录协议买到的是第二件。接到你现在的图上改五处你大概已经有一张reason / tools / respond的图。不必推倒。改五处。1. 工具名单改成四个原语bind_tools(ALL_TOOLS)改成bind_tools([ls, read_file, write_file, grep])。退款、开票、发券从 schema 里撤走写进skills/*/SKILL.md。真的要打网关时再在技能正文里规定「调用内部 HTTP 客户端」——那是下一层不是默认前缀。2. System 改成读AGENTS.md把五十行角色扮演从代码字符串里搬进文件。代码只负责Path.read_text。产品改规则走 PR不走重新发版的 prompt 常量。3. 技能进索引不进正文启动扫描skills/*/SKILL.md的 YAML 头。每轮只贴 name description 路径。模型用read_file点开。4. 产出写文件约定超过 20 行的产出必须write_file到out/。Tool 返回值只留wrote out/weekly.md 612B。窗口里不再堆作文。5. 前缀称重加一个prefix_stats.py。CI 里断言稳定前缀不超过 N token先定 1500再按模型账单收紧。有人往AGENTS.md贴了一章架构说明书构建就红。五处改完图还是那张图。变的是窗口里住着操作系统应用程序住在目录里。验收对着工作目录问七句协议有没有生效不看 LangSmith 是不是绿。看磁盘。启动后第一轮工具调用是不是ls盲写out/foo.md算失败。本任务用不到的SKILL.md全文有没有进过 messages进过算失败。稳定前缀的字节第 1 轮和第 8 轮是否完全相同不同算失败。用户可见回复是否短于 20 行并且带out/路径把全文贴进对话框算失败。out/里的数字能否用grep在data/里对上对不上算失败。换一个thread_id再开一轮只给「继续上周任务」Agent 能否靠lsread_file接上接不上说明记忆还住在窗口里。read_file(../.env)是否抛错不抛错先别接业务。这七句可以写成 pytest。fixture 起一份临时 workspace断言文件不断言模型文采。你今天下午就能抄走的最小集lab/workdir-agent/ agent.py # 不到 200 行图 四个原语 prefix_stats.py # 称重量不调模型 requirements.txt # langgraph 1.2.16 · langchain-deepseek 1.1.1 workspace/ AGENTS.md TASK.md NOTES.md skills/csv-report/SKILL.md skills/git-commit/SKILL.md data/orders.csv out/抄走之后只改三份文件AGENTS.md换成你的硬规则skills/换成你的两三个真技能TASK.md换成你下午那件事。工具代码可以暂时不动。模型用deepseek-v4-pro或你现在的主力。协议和模型解耦。换模型是换ChatDeepSeek那一行。换协议是换目录约定。工作目录协议是 Agent 开发的第一课。图、MCP、Multi-Agent 是第二课、第三课。第一课没上后面每一课都在往一个没有硬盘的进程里堆内存。四个原语一份AGENTS.md技能按需加载产出落盘。这套东西 2026 年每一个能连续干活的编码 Agent 都在用只是没人把它写成可以抄的协议。评论区请贴两样东西你现在的 Agent 启动时bind_tools的数量以及AGENTS.md或等价文件的行数。只贴这两个数字。