ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Harness Engineering 在软件工程层面的理论与实践:用 AGENTS.md 与 Linter 搭建 AI Coding 质量门禁

Harness Engineering 在软件工程层面的理论与实践:用 AGENTS.md 与 Linter 搭建 AI Coding 质量门禁 1. 为什么 AI Coding 需要 Harness Engineering你可能已经习惯了让 AI 帮你补全函数、生成单测、甚至整段重构。但真正把 AI Coding 放进一个持续交付的团队项目里问题会立刻暴露Agent 今天写的代码能跑明天就绕过了分层这次生成的 import 没问题下次就把 UI 层直接连到了数据库。代码量越大这种“隐性腐化”越难靠人工 Review 拦住。Harness Engineering 要解决的就是这件事。它不是让模型更聪明而是给模型套上一副“马具”——用 AGENTS.md 定义行为边界用 Linter 把架构约束变成可执行的检查让 AI 在写代码之前就知道什么不能做在写完代码之后立刻收到可操作的反馈。这套思路在 OpenAI 的 Codex 实验里被验证过人类工程师不再逐行写代码而是设计环境、定义规则、搭建反馈回路。这篇文章面向正在把 AI Coding 引入日常开发的工程师。我会给出可直接复制的 AGENTS.md 骨架、Python 与 JavaScript 两套 Linter 配置片段、本地验证命令以及如何通过 TaoToken 统一 Key/API 通道接入 AI 工具完成端到端校验。你不需要先成为 AI 专家只要有一个能跑测试的项目就可以跟着做。2. TaoToken 前置统一 Key 与 API 通道在搭建 Harness 之前先解决一个现实问题你的 AI 工具可能不止一个。Codex CLI、Claude Code、Cursor、自建脚本每个都配一套 Key 和 Base URL管理成本高切换模型时还要改环境变量。TaoToken 的作用是把这些统一到一个入口。TaoToken 是一个 AI 模型 API 聚合服务提供兼容 OpenAI 风格的接口。你可以在官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后拿到 API Key然后在各个工具里把 Base URL 指向 https://taotoken.net/api。这样无论是跑 Codex 做代码审查还是用 Claude 做交叉评审都走同一个 Key计费和额度也集中管理。具体操作分三步。第一步登录后进入控制台创建 API Key建议按项目或按工具分别建 Key方便后续排查用量。第二步在本地环境变量里配置export TAOTOKEN_API_KEYsk-你的key export OPENAI_BASE_URLhttps://taotoken.net/api export OPENAI_API_KEY$TAOTOKEN_API_KEY第三步验证通道是否通。用 curl 发一个最小请求curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o-mini, messages: [{role: user, content: reply with ok}] }如果返回里有choices字段说明通道正常。这一步很关键因为后面 Agent 自动审查、Linter 报错修复都依赖这个通道。如果你更习惯用现成的对话界面先试模型可以直接打开模型对话页面如果准备长期跑编码 Agent建议了解 Coding Plan 的额度方案需要管理多个 Key 时API Keys 页面可以随时创建和吊销。3. 可复制配置AGENTS.md 骨架与 Linter 规则3.1 AGENTS.md 骨架AGENTS.md 是 Agent 每次会话开始时自动读取的文件。它的核心原则是“导航而非内容”——保持在一百行左右指向更深层的 docs/ 目录而不是把所有规则堆在一起。下面是一个可以直接改用的骨架# AGENTS.md ## 项目简介 前后端分离的订单管理系统前端 React后端 FastAPI数据库 MySQL。 ## 技术栈 - 前端JavaScript React 19 Vite 6 - 后端Python 3.12 FastAPI SQLAlchemy - 数据库MySQL 8.0 - 缓存Redis 7 ## 快速开始 ### 后端 cd backend uv sync uv run alembic upgrade head uv run uvicorn app.main:app --reload ### 前端 cd frontend pnpm install pnpm dev ## 测试 make test-backend # 覆盖率要求 80% make test-frontend make test-all ## 架构原则 - 后端依赖方向types - config - repo - service - api单向流动禁止反向 - 前端依赖方向types - config - services - hooks - pages单向流动 - 前后端只通过 REST API 通信前端不直接访问数据库 - 详见docs/architecture/dependency-rules.md ## 编码规范 - 后端RuffLint 格式化mypy类型检查 - 前端ESLint Prettier - 单文件不超过 200 行单函数不超过 30 行 ## 常见陷阱 - 不要在 repo 层写业务逻辑只做 CRUD - 不要在 api 层直接操作数据库必须经过 service 层 - 前端不要用 fetch 直接调 API统一走 services/ 层 - 数据库变更必须通过 alembic 迁移禁止手动改表结构 ## 深入阅读 - 架构详情docs/architecture/overview.md - 设计约束docs/design/constraints.md - 技术债追踪docs/plans/tech-debt.md这个文件的关键在于“常见陷阱”部分。每次 Agent 犯错你就把教训写进去下次它就不会再犯。这比反复在 Prompt 里提醒有效得多。3.2 后端 Linterimport-linter 强制分层Python 项目用 import-linter 把分层规则变成可执行检查。在 backend 目录下创建.importlinter[importlinter] root_packages app [importlinter:contract:backend-layers] name 后端分层架构约束 type layers layers api service repo config types containers app这条规则的含义是api 可以 import serviceservice 可以 import repo但 repo 不能反过来 import service。一旦违反lint-imports会直接报错并指出违规的 import 路径。配合 Ruff 做代码质量检查在pyproject.toml里配置[tool.ruff] line-length 100 [tool.ruff.lint] select [E, F, I, N, UP, B, SIM] ignore [E501] [tool.ruff.lint.per-file-ignores] __init__.py [F401]3.3 前端 Lintereslint-plugin-boundaries前端用 eslint-plugin-boundaries 做同样的约束。先安装依赖cd frontend pnpm add -D eslint eslint/js eslint-plugin-boundaries然后在eslint.config.js里定义元素类型和允许的依赖方向import boundaries from eslint-plugin-boundaries; export default [ { files: [src/**/*.{js,jsx}], plugins: { boundaries }, rules: { boundaries/element-types: [error, { default: disallow, rules: [ { from: types, allow: [] }, { from: config, allow: [types] }, { from: services, allow: [types, config] }, { from: hooks, allow: [types, config, services] }, { from: pages, allow: [types, config, services, hooks] }, { from: components, allow: [types, config, hooks] }, ], }], }, settings: { boundaries/elements: [ { type: types, pattern: src/types/** }, { type: config, pattern: src/config/** }, { type: services, pattern: src/services/** }, { type: hooks, pattern: src/hooks/** }, { type: pages, pattern: src/pages/** }, { type: components, pattern: src/components/** }, ], }, }, ];这样当 Agent 在 pages 层直接 import repo 或数据库相关模块时ESLint 会立刻报错而不是等到运行时才暴露问题。4. 验证请求与成功结果配置写完之后必须验证门禁真的能拦住违规代码。我建议用“故意写错再修复”的方式做端到端校验。4.1 后端验证先在后端制造一个反向依赖。在app/repo/order_repo.py里加一行from app.service.order_service import OrderService # 故意违规然后运行cd backend uv run lint-imports预期输出类似app.repo.order_repo - app.service.order_service (api - service - repo) LINTER ERROR: app.repo.order_repo - app.service.order_service violates contract 后端分层架构约束看到这个报错说明门禁生效。删掉违规 import再跑一次应该输出Contracts: 1 kept, 0 broken。4.2 前端验证在前端src/pages/OrderList.js里故意写import { getUserFromDB } from ../repo/userRepo; // 违规运行cd frontend pnpm lint预期报错error Dependency violation: pages cannot import from repo Allowed: types, config, services, hooks4.3 接入 AI 工具做自动修复门禁报错之后让 Agent 根据报错信息自动修复。用 TaoToken 通道跑 Codex CLInpx codex --approval-mode full-auto \ 运行 make lint根据报错信息修复所有架构违规不要改变业务逻辑Agent 会读取 AGENTS.md 里的架构原则结合 Linter 的具体报错把违规 import 改成通过 service 层调用。修复完成后再次运行make lint全部通过即表示闭环成立。如果你更想先手动确认模型输出质量可以在模型对话里贴上报错信息让它给出修复方案再落地。5. 本篇常见错排查5.1 lint-imports 报 “Could not find module”通常是root_packages配置和实际包名不一致。检查.importlinter里的root_packages app是否对应你项目里真实的 Python 包目录名。如果后端代码在src/app/下需要改成root_packages src.app或调整工作目录。5.2 ESLint 报 “boundaries/element-types rule not found”说明插件没装成功或配置里没注册。确认pnpm add -D eslint-plugin-boundaries执行成功并且eslint.config.js的plugins字段里有boundaries。如果用的是旧版.eslintrc配置方式不同建议统一升级到 flat config。5.3 Agent 不读 AGENTS.md不同工具读取规则文件的位置不同。Codex 默认读项目根目录的 AGENTS.mdClaude Code 读 CLAUDE.md部分工具读.cursorrules。如果你用的工具不识别 AGENTS.md可以在工具配置里显式指定或者建一个软链接ln -s AGENTS.md CLAUDE.md5.4 TaoToken 请求返回 401先确认环境变量TAOTOKEN_API_KEY是否在当前 shell 生效用echo $TAOTOKEN_API_KEY检查。如果 Key 正确但仍 401检查 Base URL 是否写成了https://taotoken.net/api而不是带/v1的完整路径——不同工具对 Base URL 的拼接方式不同OpenAI 兼容工具通常只需要到/api。需要重新生成 Key 时去 API Keys 页面操作。5.5 Linter 通过但运行时仍然出错Linter 只能检查静态的 import 关系不能覆盖运行时动态调用。比如通过字符串反射调用、事件总线跨层通信Linter 是拦不住的。这类问题需要在 AGENTS.md 的“常见陷阱”里明确写出禁止模式并配合集成测试覆盖关键路径。6. 把门禁接进 CI 与日常流程本地验证通过后把检查接进 CI让每次 PR 都自动跑一遍。后端质量门禁的 GitHub Actions 片段name: Backend Quality Gate on: [pull_request] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: astral-sh/setup-uvv4 - run: cd backend uv sync - run: cd backend uv run ruff check app/ tests/ - run: cd backend uv run mypy app/ - run: cd backend uv run lint-imports前端同理把pnpm lint和pnpm test串进 workflow。这样 Agent 提交的 PR 如果违反架构约束CI 会直接失败Agent 收到失败反馈后可以自动修复形成“违规 - 检测 - 修复”的闭环。最后分享一个实用技巧把 Linter 的报错信息格式改成对 Agent 友好的结构。普通报错只说“不允许”Agent 需要自己推断怎么改如果你在自定义规则里输出“违规文件、违规层级、允许的层级、修复建议”Agent 一次修复的成功率会明显提高。这个改动不大但能省下大量来回调试的时间。
返回列表