人工智能代理基础设施后端
一个TypeScript/Fastify后端,用于协调对外部AI和数据提供程序的调用。它运行声明性工作流,根据显式标准验证其输出,并记录所有内容以供审计和可观察性。
它是 不 代理运行时。没有模型决定控制流。每个步骤和依赖项都固定在存储的工作流定义中,实际工作委托给外部提供者。
在你的电脑上运行
这个 getoat 命令使用您自己的提供程序密钥在您自己的机器上一起运行API和控制台。它会监听 127.0.0.1 只有,你的钥匙和数据留在里面 ~/.getoat (或 GETOAT_HOME).
npx getoat # first run asks for your API keys, then opens the Console
npx getoat --mock # try it with labelled sample output, no keys needed
需要Node.js 22.13或更新版本。提供程序的使用由每个提供程序对您自己的密钥计费。
| 命令 | 目的 |
|---|---|
getoat | 启动OAT并打开控制台(--port, --no-open, --mock) |
getoat setup | 添加或更改提供程序API密钥 |
getoat token | 打印您的本地访问令牌 |
快速启动
npm install
cp .env.example .env
npm run token:create -- alice admin # put the printed "alice:admin:<hash>" into AUTH_TOKENS
npm run migrate # optional; the server also migrates on start
npm run dev # http://127.0.0.1:3000/api/v1/health
要尝试没有任何提供程序密钥的工作流,请设置 ENABLE_MOCK_PROVIDERS=true (开发)。模拟输出带有前缀 [MOCK],每张唱片都贴上了标签 isMock: true。
| 脚本 | 目的 |
|---|---|
npm run dev | 观看模式(tsx) |
npm run build / npm start | 编译, dist/ /运行编译服务器 |
npm test | 单元+集成测试(模拟所有外部调用) |
npm run typecheck / npm run lint | 输入check / ESLint |
npm run migrate | 应用数据库迁移 |
npm run token:create -- <user> [admin|user] | 生成一个API令牌+ its AUTH_TOKENS 条目 |
要求节点≥22.13(使用内置) node:sqlite;在Node 24上开发的)。
体系结构
src/
api/ API gateway: Fastify app, envelope, error handling, auth hooks, routes (/api/v1)
auth/ Bearer-token auth (hashed tokens), roles → permissions, ownership scoping
config/ Strict env validation (zod) + .env loader
providers/ Provider Manager, capability contracts, adapters (anthropic, gemini, jina, tavily, exa, github, mock)
orchestration/ Workflow definition schema, static validation (DAG/refs), templates, coordinator
verification/ JSON Schema + acceptance-criteria engine with evidence
observability/ HTTP metrics (provider metrics are persisted)
storage/ SQLite (node:sqlite), migrations, repositories
lib/ Errors, secret redaction, SSRF guard, safe JSON paths
请求路径: 网关(请求ID→CORS/头盔→速率限制→auth→权限→zod验证)→路由→存储库/协调器→ProviderManager→适配器→外部API。
供应商管理器。 每个功能都有一个输入/输出合约(providers/capabilities.ts). 经理:
- 验证输入
- 为URL输入运行SSRF预飞行
- 使用配置的路由选择提供程序(首先是健康的提供程序)
- 应用每次尝试超时,AI文本生成时间更长(
LLM_TIMEOUT_MS) - 重试超时,429秒,5xx和网络错误与指数回退和抖动,荣誉
Retry-After - 根据合约验证适配器的输出
- 将功能限制为中指定的提供程序
PROVIDER_ROUTING(例如llm.generate=gemini). 路由是严格的:如果没有配置路由提供程序,则功能不可用,API将命名缺失的设置,不会静默切换到另一个提供程序或模拟。 - 只有在标记了功能后才回退到下一个提供者
fallbackCompatible:web.search,web.extract和text.rerank。text.embed被排除在外,因为来自不同供应商的矢量是不可互换的,和llm.generate和github.repo也被排除在外。 - 将错误归一化为代码,例如
timeout,rate_limited,auth_failed,malformed_response,refused,blocked_url和其他人 - 跟踪被动运行状况并保存每个呼叫的延迟、尝试、使用和估计费用
编排。 工作流是包含多达50个步骤的DAG。每一步都是一个功能调用 input 可以引用 {{input.*}} 或 {{steps.<dep>.output.*}}. 验证拒绝循环、未知依赖项和对非依赖项的引用。协调器:
- 并行运行独立的步骤(
maxParallelism) - 跳过失败步骤的依赖项
- 支持
continueOnError、取消、执行超时和全局并发上限 - 保存每个步骤的状态、持续时间、尝试、回退和错误
重新启动时,正在执行的执行将被标记 interrupted。
验证。 每一步都可以声明一个JSON Schema +标准: required, min_items, max_items, string_length, contains, not_contains, equals, number_range 和 urls_in_step. 最后一个是基础检查:输出中引用的每个URL必须出现在检索步骤的结果中。每张支票都与证据一起保存。
A验证记录 factualClaimsVerified: false 每一次,它的总结分为三件事:
- 结构的一致性: 输出与声明的模式和标准匹配。
- 引文跟踪: 引用的url实际上是由检索步骤返回的。
- 事实真相: 从来没有被这个热机建立过。
用户模式不受信任。 pattern 和 patternProperties 被拒绝(ReDoS风险),并且只有本地 $refS是允许的。
空气污染指数(均在 /api/v1)
成功响应使用 { data, meta: { requestId, page? } }. 错误使用 { error: { code, message, details? }, meta: { requestId } }. 每个回应都有一个 x-request-id 头;您可以发送自己的(8-128个字符的) [A-Za-z0-9._-]).
| 方法与路径 | 身份验证 | 目的 |
|---|---|---|
GET /health | 公共 | 活性 |
GET /health/ready | 公共 | DB +迁移准备(如果未准备好,则为503) |
GET /providers | 用户 | 具有输入/输出JSON模式的提供程序(已配置/运行状况)和功能 |
GET /providers/health | 用户 | 被动提供者健康状况 |
POST /providers/health/probe | 管理 | 主动探针,在没有支付配额的情况下可用(Anthropic模型查找,GitHub) /rate_limit); 经审计的 |
POST /workflows/validate | 用户 | 在不保存的情况下验证草稿:错误、平行阶段、准备情况 |
POST /workflows | 用户 | 创建工作流(无效为422,使用禁用功能为403) |
GET /workflows | 用户 | 列出自己的工作流(admin: all); limit, offset |
GET /workflows/:id | 业主/管理 | 得到工作流程 |
PUT /workflows/:id | 业主/管理 | 替换定义和凹凸 version (无效的422,不允许的403);过去的处决保留了他们执行的版本 |
DELETE /workflows/:id | 业主/管理 | 永久删除工作流、其执行和验证(当运行处于活动状态时);提供者统计信息和审计事件将被保留 |
POST /workflows/:id/validate | 业主/管理 | 对现有供应商进行复查;可选 { input } 验证对 inputSchema |
POST /workflows/:id/executions | 业主/管理 | 开始执行 { input } → 202 { id };422输入错误,409提供程序不可用 |
GET /executions | 用户 | 列出自己的执行情况;过滤器 workflowId, status |
GET /executions/:id | 业主/管理 | 执行+每步运行(状态、提供者、尝试、输出、错误、验证id) |
POST /executions/:id/cancel | 业主/管理 | 取消正在进行的执行(409) |
GET /verifications/:id | 业主/管理 | 检查、证据和评估 |
GET /metrics | 管理 | HTTP延迟/状态、提供者调用/错误/令牌/成本、执行计数(windowHours) |
GET /audit-events | 管理 | 审计跟踪 |
其他用户拥有的记录返回404,因此无法探测id。
示例工作流
{
"name": "Research brief",
"inputSchema": { "type": "object", "required": ["topic"], "properties": { "topic": { "type": "string" } } },
"steps": [
{ "id": "search", "capability": "web.search", "input": { "query": "{{input.topic}}", "count": 5 } },
{
"id": "summary", "capability": "llm.generate", "dependsOn": ["search"],
"input": { "prompt": "Summarize {{input.topic}} using only these results and cite URLs: {{steps.search.output.results}}" },
"verification": {
"criteria": [{ "type": "urls_in_step", "path": "text", "sourceStep": "search" }],
"required": false
}
}
]
}
供应商
| 提供者 | 功能 | 时启用 | 笔记 |
|---|---|---|---|
| Anthropic(官方SDK) | llm.generate | ANTHROPIC_API_KEY | 默认的 claude-opus-5-5,明确 effort,服务器端拒绝回退;拒绝表面上是 refused. 根据公布的每个令牌费率估算的成本。 |
| 谷歌双子座 | llm.generate | GEMINI_API_KEY (可选 GEMINI_MODEL、默认 gemini-3.6-flash; GEMINI_MAX_OUTPUT_TOKENS(默认为8192) | 提供免费层。主要从 aistudio.google.com/apikey,发送到 x-goog-api-key 头(从不在url中)。在免费层,谷歌可以使用提示和输出来改进其产品。没有估算成本,因为API不报告键的层。安全块表面为 refused。 |
| 胜者人工智能 | text.embed, text.rerank, web.extract | JINA_API_KEY (读者也可通过 JINA_READER_KEYLESS=true) | 形状的 api.jina.ai/openapi.json;读者通过 r.jina.ai JSON模式。 |
| Tavily | web.search | TAVILY_API_KEY | 主要搜索: POST https://api.tavily.com/search 与 Authorization: Bearer, search_depth: basic (每次呼叫1学分)。主要从 app.tavily.com. a429荣誉 Retry-After. HTTP 432/433(已达到计划或现收现付限制)显示为 quota_exceeded未重试,退到Exa。空结果列表是有效的输出(使用 min_items 需要结果的验证)。管理活动探针使用 GET /usage, tily将其限制在每10分钟10个请求。 |
| 穰 | web.search | EXA_API_KEY | 回退搜索;报告 costDollars。 |
| GitHub休息 | github.repo | GITHUB_TOKEN | 只读的。 |
| 模拟 | 所有 | ENABLE_MOCK_PROVIDERS=true | Dev /测试;生产中被拒绝。 |
自由层限制改变。在依赖每个提供商之前,请检查它们的仪表板。集成时:Jina为新密钥提供免费令牌,而Reader在没有密钥的情况下允许20 RPM。Exa为新账户提供免费积分。tily开发键限制为每分钟100个请求;在app.tavily.com上查看你的每月信用额度。
安全总结
- 严格的环境验证。在生产中,它需要
AUTH_TOKENS并拒绝开发绕过、模拟提供程序和通配符CORS。 - 只配置API令牌的SHA-256哈希值,并且在恒定时间内比较它们。
- 角色映射到显式权限;非管理员用户的范围仅限于他们拥有的记录。每个用户允许列出每种功能(
USER_ALLOWED_CAPABILITIES). - 只有json的请求体有大小限制,并且在每个路由上进行验证。
- 每个凭证的速率限制,对执行开始和探测有更严格的限制。
- SSRF防护装置
web.extract:- 端口80/443上只有http, URL中没有凭据
- 内部主机名被阻止
- 解析DNS,拒绝私有地址、环回地址、链路本地地址、元数据地址和映射地址
- 可选allowlist和denylist
- 秘密从日志、持久化错误和审计细节中编校:配置的键值、通用键格式、承载头和私钥形状的十六进制。日志从不包含请求主体或授权标头。
- 不存在区块链或财务功能,并且GitHub适配器是只读的。
网页前端(web/)
登陆页面(/)和控制台(/console/)都是用Vite构建的普通TypeScript。通过vitest, Vite已经在工具链中了,所以没有添加UI框架。fasttify为构建提供服务 web/dist 基于与API相同的来源,具有严格的CSP,对散列资产进行不可变缓存,并且对静态文件没有速率限制。
| 命令 | 目的 |
|---|---|
npm run build | 构建服务器(dist/)和前端(web/dist/) |
npm run dev + npm run dev:web | 后端:3000,Vite开发服务器:5173代理 /api |
npm run test:e2e | 后 npm run build:实时浏览器检查安装Chrome/Edge (CHROME_PATH 覆盖)在320-1440px |
- 登录页面: 英雄的身份牌读大众
/health和/health/ready端点。仪表板预览使用样例值,并以这种方式进行标记。 - 控制台: 使用个人API令牌登录。它被保存在
sessionStorage仅针对当前选项卡,而提供程序键永远不会到达浏览器。控制台包括:- 概述: 实时提供程序运行状况、功能和活动探测(仅限管理员)
- 工作流程: 模板、服务器端验证和准备
- 执行: 实时轮询、取消和验证证据
- 指标: 管理员只
- 模拟数据: mock提供程序生成的任何东西都被标记为mock。
前端集成(定制客户端)
- 为前端的用户生成令牌
npm run token:create -- <user> user并将打印的条目添加到AUTH_TOKENS. 将前端原点添加到CORS_ORIGINS。 - 发送
Authorization: Bearer <token>如果可以的话,从服务器端代理或BFF发送,因此令牌永远不会到达浏览器。对于本地开发,直接调用API是可以的。 - 呼叫
GET /api/v1/providers构建功能选择器。每个功能包括inputSchema和outputSchema(JSON模式),available和allowedForCaller。 - 呼叫
POST /api/v1/workflows/validate当用户编辑工作流时。显示errors[].path和message,并进行渲染stages作为执行计划。 - 呼叫
POST /api/v1/workflows/:id/executions开始跑步:开始跑步民意调查GET /api/v1/executions/:id直到status是succeeded,failed,cancelled或interrupted. 目前还没有推送频道。 - 的步骤
verificationId,显示验证assessment.summary一字不差,永远不要把模型输出说成是“验证过的”。徽章结果如下isMock是真的。