1. 从 Farming Code 这个名字说起它到底在解决什么问题第一次看到 Farming Code 这个标题很多人会以为是某种游戏外挂或者自动化脚本合集。但结合 agent、Language Server、Browser Use、Computer Use 这几个关键词来看它指向的其实是一个更具体的东西让 AI agent 像农民种地一样持续、稳定、可循环地耕作代码库和浏览器环境。我把它理解成一套 agent 的农作系统——不是一次性跑完就结束的脚本而是能长期驻留在开发环境里反复执行代码修改、页面操作、信息采集、状态维护的自动化循环。这和传统 CI/CD 脚本最大的区别在于CI 是触发式的、无状态的而 Farming Code 强调的是持续在场、有记忆、能感知环境变化。为什么这个概念现在值得单独拿出来讲因为过去一年 agent 领域最大的痛点不是能不能调用工具而是能不能稳定地持续工作。你让一个 agent 改一个文件很容易让它连续改二十个文件、中间还要打开浏览器验证、失败了要回滚、状态要保留——这才是真正难的地方。Farming Code 要解决的正是这个持续耕作的问题。这篇文章适合三类人看一是正在做 agent 应用开发、被状态管理和工具编排折磨的工程师二是想理解 Language Server、Browser Use、Computer Use 这些能力怎么组合起来用的人三是对 agent 架构感兴趣、想搞清楚 harness 和 agent 到底差在哪里的学习者。我会从架构拆解讲到实操细节尽量把每个为什么这么设计讲透。2. Farming Code 的架构骨架四个核心能力层怎么咬合2.1 Language Server 层agent 的代码感知器官大多数人做代码 agent 的第一反应是让模型直接读文件、写文件。这个做法在小项目上能跑但一旦代码库超过几千行就会崩——因为模型根本不知道改一个函数会影响到哪些调用方。Language Server 在这里的作用是给 agent 装上一套语义级的代码感知能力。它提供的不是文本而是符号这个函数定义在哪、被谁引用、类型是什么、有没有重载。agent 拿到这些信息之后改代码就从盲改变成了有依据地改。具体怎么接主流做法是通过 LSP 协议Language Server Protocol起一个语言服务进程agent 通过 JSON-RPC 和它通信。关键接口有这么几个textDocument/definition跳转到定义用来确认符号来源textDocument/references查找所有引用改函数签名前必查textDocument/diagnostics拿到编译错误和警告改完立刻验证workspace/symbol全局符号搜索用来定位相关代码我实测下来diagnostics 这个接口是性价比最高的。agent 每次改完代码主动拉一次诊断结果如果有 error 就自动进入修复循环比让模型自己猜哪里错了靠谱得多。很多 agent 项目失败就失败在改完不验证改错了也不知道。注意Language Server 启动有冷启动成本大型项目首次索引可能要几十秒。建议在 agent 会话开始时就把 LS 进程拉起来常驻不要每次操作都重启。2.2 Browser Use 层把网页变成 agent 可操作的结构Browser Use 和 Computer Use 经常被混为一谈但它们的抽象层级完全不同。Computer Use 是看屏幕像素、点坐标Browser Use 是操作 DOM 节点、读结构化内容。前者通用但脆弱后者精准但依赖页面结构。Farming Code 场景下我强烈建议优先用 Browser Use。原因很直接基于坐标的点击页面一改布局就全废基于选择器的操作只要语义没变就还能用。而且 Browser Use 能直接拿到页面文本、表单值、链接列表agent 处理起来信息密度高得多。一个典型的 Browser Use 工作流是这样的agent 通过 CDPChrome DevTools Protocol连接到一个浏览器实例导航到目标页面等待networkidle或特定选择器出现用querySelector定位元素读取文本或属性执行点击、输入、滚动等操作截图或提取 DOM 快照作为下一步决策的输入这里有个容易踩的坑等待策略。很多 agent 失败不是逻辑错是页面还没加载完就去操作了。我的经验是不要用固定sleep而是组合等待——先等domcontentloaded再等目标选择器可见最后加一个短的稳定期比如 300ms 内 DOM 无变化再动手。2.3 Computer Use 层兜底的全屏操作能力那 Computer Use 什么时候用我的答案是当 Browser Use 够不着的时候。比如操作一个桌面应用、处理一个非标准渲染的 canvas 界面、或者浏览器弹出了原生对话框。Computer Use 的核心是截图 → 模型理解 → 输出坐标动作这个循环。它的成本比 Browser Use 高一个数量级因为每步都要传图像、模型推理也慢。所以架构上应该把它设计成降级路径Browser Use 失败三次才切到 Computer Use。2.4 状态与记忆层让耕作能持续前面三层是能力这一层是灵魂。没有状态管理agent 每次都是失忆的谈不上farming。我见过的最实用的做法是分层记忆记忆类型存储内容生命周期工作记忆当前任务步骤、临时变量单次会话情节记忆做过什么、结果如何跨会话持久化语义记忆项目知识、代码约定长期可检索工作记忆用内存里的结构化对象就行情节记忆我一般落一个 append-only 的日志文件方便回溯语义记忆可以接向量库但小项目用 Markdown 文件加关键词检索也够用。3. 把 harness 和 agent 分清楚这是架构不跑偏的前提3.1 一个被严重混淆的概念对热词里有个 harness 和 agent 区别这个问题问得特别好因为很多人做 agent 项目失败就是因为把这两者搅在一起了。我的理解是agent 是决策者harness 是执行环境。agent 负责想——根据当前状态决定下一步做什么harness 负责做——提供工具、管理进程、处理 I/O、维护沙箱。打个比方agent 是司机harness 是车。司机决定去哪、怎么走车提供方向盘、油门、刹车还负责在撞车时保护司机。你不能让司机自己去造发动机那是 harness 的活。3.2 harness 应该承担哪些职责一个合格的 harness 至少要管这几件事工具注册与调用把 Language Server、Browser、文件系统这些能力包装成统一的工具接口沙箱隔离agent 执行的代码不能污染宿主环境尤其是跑用户提供的代码时超时与中断任何工具调用都要有超时agent 卡死要能强制中断错误归一化不同工具抛的异常格式五花八门harness 要统一成 agent 能理解的错误对象资源配额限制 token 消耗、内存占用、执行时间我踩过的一个坑早期我把工具调用逻辑直接写在 agent 循环里结果每加一个工具就要改 agent 代码很快就乱成一团。后来抽成 harness 层agent 只认一个call_tool(name, args)接口扩展性立刻不一样了。3.3 agent 循环该怎么写才不失控agent 的核心是一个循环观察 → 决策 → 行动 → 再观察。听起来简单但实际写起来有几个关键控制点第一循环上限。必须设最大步数否则 agent 可能陷入死循环。我一般设 50 步超过就强制退出并报告当前状态。第二终止条件要明确。不能只靠模型说我完成了要有客观信号——比如测试通过、目标文件存在、页面出现预期元素。第三每步都要有可观测输出。日志要记录当前步数、调用了什么工具、参数是什么、返回什么、模型下一步的推理。出问题时这些日志就是救命稻草。def agent_loop(task, max_steps50): state init_state(task) for step in range(max_steps): obs observe(state) action decide(obs) # 模型决策 if action.type finish: return action.result result harness.call_tool(action.name, action.args) state update_state(state, action, result) log_step(step, action, result) raise MaxStepsExceeded(state)这段伪代码看着简单但每一行背后都有讲究。observe要决定给模型看多少上下文decide要处理模型输出的解析和校验update_state要决定哪些信息保留、哪些丢弃。4. 实操从零搭一个能持续跑的 Farming Code 原型4.1 环境准备里最容易被忽略的三件事第一件是语言服务的版本匹配。你项目用的语言版本和 LS 支持的版本不一致会出现符号解析错误。比如 Python 项目用 pyright要确认它支持的 Python 版本覆盖你代码里用的语法。第二件是浏览器实例的复用。每次操作都新开浏览器启动成本高不说登录态、cookie 全丢。正确做法是启动一个持久化的浏览器实例agent 通过 CDP 连上去会话之间共享状态。第三件是工作目录的隔离。agent 操作的文件系统要限定在一个工作目录内绝对不能让它碰到系统文件。我用的是容器化方案把工作目录挂载进去agent 在容器里随便折腾宿主机安全。4.2 工具定义的具体写法工具定义的质量直接决定 agent 好不好用。我的经验是每个工具要包含四要素名称、描述、参数 schema、返回格式说明。以查找符号引用这个工具为例{ name: find_references, description: 查找某个符号在代码库中的所有引用位置。修改函数签名或删除符号前必须调用此工具确认影响范围。, parameters: { type: object, properties: { file_path: {type: string, description: 符号所在文件的相对路径}, line: {type: integer, description: 符号所在行号从1开始}, character: {type: integer, description: 符号所在列号从1开始} }, required: [file_path, line, character] }, returns: 引用位置数组每项包含 file_path、line、character 和上下文代码片段 }描述里那句修改函数签名前必须调用很关键——它是在引导模型的行为。工具描述不只是给人看的文档更是给模型的提示词。4.3 让 agent 学会先看后改新手写的 agent 经常上来就改代码改完发现改错了。正确的行为模式应该是先收集信息再制定计划最后执行。我在系统提示里会明确要求这个流程修改任何代码前你必须1) 用 find_references 确认影响范围2) 用 read_file 读取相关文件3) 说明你的修改计划4) 执行修改5) 用 diagnostics 验证结果。这五步看起来啰嗦但实测能把改错率降低一大半。模型不是不会改代码是它太急于动手了。4.4 浏览器操作的稳定性技巧Browser Use 最大的敌人是时序问题。页面元素还没渲染出来就去点必然失败。我总结了几个实用技巧优先用文本内容定位而不是 CSS 类名。类名会变文本相对稳定。操作前先滚动到元素可见很多点击失败是因为元素在视口外。输入框要先清空再输入否则会追加而不是替换。关键操作后加验证步骤比如点击提交后检查是否出现了成功提示。还有一个反直觉的经验不要追求 100% 自动化。有些关键节点比如涉及支付、删除数据让 agent 停下来等人确认比让它自己判断安全得多。5. 那些文档里不会写的坑我的踩坑记录5.1 上下文爆炸agent 为什么会越跑越傻agent 跑到二三十步之后经常出现忘记前面做过什么的情况。这不是模型的问题是上下文窗口被塞满了。我的排查过程是这样的先看日志发现每步都把完整的文件内容、完整的 DOM 快照塞进上下文几步下来就几万 token 了。模型注意力被稀释自然就傻了。解决方案是上下文压缩策略文件内容只保留相关片段不要整个文件DOM 快照只保留可交互元素和关键文本历史步骤做摘要只保留做了什么、结果如何丢弃中间细节用外部存储保存完整信息需要时再检索改完之后同样的任务从 40 步降到 15 步就完成了。5.2 工具调用的幻觉参数模型有时候会编造工具参数比如传一个不存在的文件路径或者行号超出范围。早期我没做校验结果 agent 一直报错但不知道错在哪。后来我在 harness 层加了参数校验每个工具调用前先检查参数格式和范围不合法就直接返回结构化错误给模型让它自己修正。这个改动让 agent 的自愈能力提升明显。5.3 沙箱逃逸的隐患这个必须重点说。agent 执行的代码如果没隔离可能删文件、改系统配置、甚至发起网络请求。我见过有人图省事直接在宿主机跑结果 agent 把项目目录删了。正确做法是容器化 只读挂载 网络白名单。工作目录可写其他目录只读网络只允许访问必要的服务。多花半小时配置能省掉无数麻烦。5.4 模型选择的取舍不同任务对模型的要求不一样。我的经验是任务类型推荐模型特征理由代码修改强代码能力、长上下文需要理解复杂依赖页面操作强视觉理解、快响应需要处理截图和 DOM计划制定强推理、强指令遵循需要拆解复杂任务不要指望一个模型搞定所有事。架构上支持模型切换按任务类型路由效果比死磕一个模型好得多。6. 从原型到可用让 Farming Code 真正种得下去6.1 可观测性决定你能不能调优agent 系统最怕的是黑盒。你不知道它为什么成功也不知道它为什么失败。所以从第一天就要把可观测性做起来。我一般会记录这几类数据每步的输入输出、工具调用耗时、token 消耗、成功/失败标记。有了这些数据才能分析出瓶颈在哪——是模型决策慢还是工具执行慢还是重试太多。一个实用技巧给每个任务打上唯一 ID所有日志都带上这个 ID排查问题时能一键拉出完整链路。6.2 失败恢复比一次成功更重要真实环境里 agent 失败是常态。关键不是追求不失败而是失败后能恢复。我的做法是检查点机制每完成一个关键步骤就把状态存一次。如果后续失败可以从最近的检查点重来而不是从头开始。对于长任务这个机制能省大量时间和 token。6.3 渐进式放权不要一上来就让 agent 全自动跑。我的建议是分三阶段第一阶段agent 只做建议人来执行。观察它的判断准不准。 第二阶段agent 执行低风险操作高风险操作等人确认。 第三阶段确认稳定后才放开全自动。这个过程中积累的失败案例就是后续优化的最好素材。6.4 关于 agent 学习路线的一点个人看法热词里有很多agent 学习路线agent 从入门到精通之类的问题。我的看法是别一上来就啃框架。LangChain、Dify、CrewAI 这些框架封装了很多东西但你如果不理解底层的 agent 循环、工具调用、状态管理用框架只会让你更迷糊。正确的顺序应该是先用最朴素的方式手写一个 agent 循环跑通观察-决策-行动这个基本盘然后加上工具调用理解 harness 的作用再然后处理状态和记忆最后才考虑用框架提效。这个顺序走下来你对 agent 的理解会比直接调框架深得多。Farming Code 这个方向我觉得最有价值的地方是它逼着你去思考持续运行这件事。一次性的脚本谁都能写但让 agent 稳定地、有记忆地、可恢复地长期工作这里面的工程问题才是真正拉开差距的地方。我现在做任何 agent 项目第一件事就是问自己它失败了怎么恢复状态存在哪下次启动能不能接着上次跑这三个问题答不上来项目就还没到能用的程度。