☰
Agent 加了检查:TypeScript 项目里用 TaoToken 统一 Key 的验证闭环
2026/9/26 16:06:43 网站建设 项目流程

1. 为什么 Agent 说“搞定了”却总翻车

如果你正在用 TypeScript 写 Agent 项目,大概率遇到过这种场景:Agent 改完代码,回你一句“任务完成”,你满心欢喜去跑npm run build,结果满屏红色类型错误。问题不在模型能力,而在流程——它把“文件写进去了”当成了“任务做对了”。

我试过在几个小 Agent 项目里复现这个坑,发现根因很一致:Agent 的完成信号是自证的。它写完文件、列一下目录、读一眼内容,就认为验证通过。系统没有强制它跑真正的检查命令,也没有在检查失败时把错误反馈回去让它继续修。于是“假完成”成了默认行为。

这篇要解决的就是这件事:在 TypeScript Agent 项目里,用 npm scripts 搭一道检查门禁,让 Agent 在声称完成之前必须先过类型检查和验证脚本,不通过就阻断完成信号。同时把模型调用统一走 TaoToken 的 Key,避免每个脚本里散落不同的 API 配置。适合正在做 Agent 工具链、想让“完成”这个词变得可信的开发者。

核心检索词先摆出来:Agent 检查、TypeScript 验证、npm 脚本门禁、TaoToken 统一 Key。下面从环境准备到可复制配置,再到验证请求和排错,一步步落地。

2. TaoToken 前置:统一 Key 与环境变量

在讲门禁之前,先把 Key 的事情理清楚。Agent 项目里通常有多个入口会调模型:主循环、验证脚本、可能还有单独的修复脚本。如果每个地方都硬编码 Key 或者读不同的环境变量,排查问题时会很痛苦。统一走 TaoToken 的 API,用一个环境变量管住所有调用点,是后面验证闭环能稳定跑的前提。

TaoToken 的 API 地址是https://taotoken.net/api,兼容常见的 OpenAI 风格调用方式。你需要在项目根目录建一个.env文件(记得加进.gitignore),里面放统一 Key:

# .env TAOTOKEN_API_KEY=sk-你的实际Key TAOTOKEN_BASE_URL=https://taotoken.net/api

然后在 TypeScript 代码里统一读取。建议封装一个src/llm/client.ts,所有模型调用都从这里走:

// src/llm/client.ts import OpenAI from "openai"; const apiKey = process.env.TAOTOKEN_API_KEY; const baseURL = process.env.TAOTOKEN_BASE_URL ?? "https://taotoken.net/api"; if (!apiKey) { throw new Error("缺少 TAOTOKEN_API_KEY,请检查 .env 配置"); } export const llm = new OpenAI({ apiKey, baseURL });

这样验证脚本、修复脚本、主循环用的都是同一个 client,Key 只在一处配置。如果你还没有 Key,可以去 TaoToken 的 API Keys 页面创建一个:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=apikeys 。创建后直接填进.env即可。

注意:不要把.env提交到仓库,也不要在日志里打印完整 Key。验证脚本输出时只打印 Key 是否存在,不打印内容。

3. 可复制配置:package.json scripts 骨架

门禁的核心在 npm scripts。思路是把“检查”拆成几个可组合的命令,Agent 完成信号触发时跑verify,verify内部先跑类型检查,再跑自定义验证脚本。任何一步非零退出,整个verify就失败,完成信号被阻断。

下面是我在项目里实际用的package.jsonscripts 骨架,你可以直接复制:

{ "name": "agent-verify-demo", "version": "1.0.0", "type": "module", "scripts": { "typecheck": "tsc --noEmit", "lint": "eslint \"src/**/*.ts\"", "verify:agent": "tsx scripts/verify-agent.ts", "verify": "npm run typecheck && npm run lint && npm run verify:agent", "agent": "tsx src/main.ts" }, "devDependencies": { "typescript": "^5.4.0", "tsx": "^4.7.0", "eslint": "^8.57.0", "@typescript-eslint/parser": "^7.0.0", "@typescript-eslint/eslint-plugin": "^7.0.0" }, "dependencies": { "openai": "^4.40.0", "dotenv": "^16.4.0" } }

几个关键点说明一下。typecheck用tsc --noEmit,只做类型检查不产出文件,速度快,适合门禁。verify:agent是自定义验证脚本,用来检查 Agent 产出的文件是否符合预期,比如某个导出是否存在、某个函数签名是否正确。verify把三者串起来,用&&保证前一步失败就中断。

tsconfig.json也要配好,确保tsc --noEmit能覆盖到 Agent 生成的文件:

{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", "strict": true, "noEmit": true, "skipLibCheck": true, "esModuleInterop": true, "resolveJsonModule": true, "include": ["src/**/*.ts", "scripts/**/*.ts", "exports/**/*.ts"] } }

注意include里加了exports/**/*.ts,因为 Agent 经常把生成的文件写到exports/目录。如果漏了这个路径,类型检查根本扫不到 Agent 的产出,门禁就是摆设。

自定义验证脚本scripts/verify-agent.ts可以这样写:

// scripts/verify-agent.ts import { existsSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; const target = resolve(process.cwd(), "exports/blog_verify_bad.ts"); if (!existsSync(target)) { console.error("[verify] 目标文件不存在:", target); process.exit(1); } const content = readFileSync(target, "utf-8"); // 检查是否包含明显的类型错误写法(示例规则) if (content.includes('"not-a-number"')) { console.error("[verify] 检测到类型不匹配的赋值,检查未通过"); process.exit(2); } console.log("[verify] 自定义验证通过");

这个脚本故意留了一条规则:如果文件里出现"not-a-number"这种明显类型不匹配的赋值,直接退出码 2。这样即使tsc因为某些配置没扫到,自定义脚本也能兜底。

4. 验证请求:跑一次 npm run verify 看结果

配置好了,来实际跑一次。先制造一个“坏文件”,模拟 Agent 声称完成但代码有问题的场景:

mkdir -p exports cat > exports/blog_verify_bad.ts <<'EOF' export const x: number = "not-a-number"; EOF

然后执行门禁:

npm run verify

预期输出会是这样,typecheck先报类型错误:

> agent-verify-demo@1.0.0 typecheck > tsc --noEmit exports/blog_verify_bad.ts:1:7 - error TS2322: Type 'string' is not assignable to type 'number'. 1 export const x: number = "not-a-number"; ~ Found 1 error in the same file.

因为tsc返回非零退出码,&&后面的lint和verify:agent不会执行,整个npm run verify失败。这就是门禁生效的表现:检查不过,完成信号被阻断。

现在把文件改对:

cat > exports/blog_verify_bad.ts <<'EOF' export const x: number = 42; EOF npm run verify

这次输出会一路绿到底:

> tsc --noEmit > eslint "src/**/*.ts" > tsx scripts/verify-agent.ts [verify] 自定义验证通过

看到[verify] 自定义验证通过并且退出码为 0,才允许 Agent 对外说“完成”。如果你在 Agent 主循环里接入这个逻辑,可以在它准备输出完成信号前调用npm run verify,捕获退出码,非零就把 stderr 摘要塞回给模型让它继续修。

关于模型调用,如果你想在验证脚本里用模型做语义级检查,可以走 TaoToken 的模型对话接口,统一用前面配好的 client。需要调试模型行为时,模型对话页面在:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=models 。长期跑编码类 Agent 任务的话,Coding Plan 更适合持续调用:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=codingplan 。

5. 本篇常见错排查

门禁跑不起来,通常卡在几个固定位置。下面按我踩过的坑列一下。

错误一:tsc报 “Cannot find module” 但文件明明存在。多半是tsconfig.json的include没覆盖到 Agent 的输出目录。检查include数组里有没有exports/**/*.ts或你实际用的目录。另一个可能是moduleResolution设成了Node而项目用了 ESM,改成Bundler或NodeNext试试。

错误二:npm run verify里&&没生效,前一步失败后面还在跑。检查是不是在 Windows 的 cmd 里跑,cmd 对&&的支持和 bash 不同。建议统一在 Git Bash 或 WSL 里执行,或者用npm-run-all这类工具替代&&。

错误三:环境变量读不到,client 初始化就抛错。确认.env在项目根目录,并且入口文件顶部有import "dotenv/config";。如果用的是tsx直接跑脚本,dotenv 不会自动加载,必须显式引入。另外确认TAOTOKEN_BASE_URL没有多余空格。

错误四:自定义验证脚本退出码不对,明明失败了却返回 0。Node 里process.exit(1)才是失败,console.error不会改变退出码。检查每个失败分支是否都调用了process.exit并传了非零值。另外tsx执行时如果脚本抛未捕获异常,退出码也是非零,但错误信息可能不直观,建议用 try/catch 包住主逻辑。

错误五:Agent 用花活命令绕过检查。比如它不跑npm run verify,而是自己拼一个echo "pass"。解决办法是把验证逻辑收进 Agent 的工具层,只暴露一个run_verify工具,内部固定执行npm run verify,不让模型自由拼命令。这样它没有绕过空间。

提示:排障时先把npm run typecheck单独跑通,再跑npm run verify。分层定位比一上来跑全量快得多。

6. 把门禁接进 Agent 主循环

配置和验证都跑通后,最后一步是把它接进 Agent 的完成信号逻辑。核心原则只有一条:模型不能自己宣布完成,必须由外部检查命令的退出码来裁决。

在src/main.ts里可以这样处理:

// src/main.ts import { execSync } from "node:child_process"; function runVerify(): { ok: boolean; output: string } { try { const output = execSync("npm run verify", { encoding: "utf-8", stdio: "pipe", }); return { ok: true, output }; } catch (err: any) { const output = (err.stdout ?? "") + (err.stderr ?? ""); return { ok: false, output }; } } // 当模型输出包含完成意图时 const result = runVerify(); if (!result.ok) { // 把错误摘要塞回对话,让模型继续修 const feedback = `检查未通过,请根据以下错误继续修改:\n${result.output.slice(0, 2000)}`; // 将 feedback 作为下一条用户消息发回模型 } else { // 只有这里才允许输出“任务完成” }

这段逻辑的关键在于:runVerify的返回值决定后续走向,模型没有投票权。检查失败时,把 stderr 截断后回传,模型看到具体错误再改,改完再跑,直到退出码为 0。如果来回多轮仍然失败,就如实输出“检查未通过”,而不是假装成功。

如果你想让 Agent 在修复阶段调用模型,统一走前面配好的 TaoToken client,接入文档在:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc 。需要看完整 API 能力时,API 入口是 https://taotoken.net/api 。Claude Code 相关的接入方式可以参考:https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=claudecode 。

整套闭环跑下来,你会发现“完成”这个词终于有了依据。检查不过,就不许说完成——这不是给 Agent 加负担,而是让它的输出变得可信任。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询