关于 Iris
Iris 是用于评估和监控 AI Agent 运行情况的本地优先 MCP 服务。它将轨迹存入 SQLite,并结合可编辑的确定性规则、可选 LLM 评审、仪表盘和 HTTP 接收功能。
主要功能
- 01记录带层级跨度的轨迹
- 0226 条确定性评估规则
- 03可选且设有成本上限的 LLM 评审
- 04SQLite 存储与仪表盘
- 05通过 HTTP 接收轨迹
- 06CI 评估门禁
适用场景
- 01检查 Agent 失败及其依据
- 02检测输出中的 PII 或提示词注入
- 03在 CI 中比较重复运行
- 04无需模型调用工具即可收集轨迹
MCP 相关
MCP 客户端可连接 Iris,并使用其工具和能力资源来记录、评估运行情况。除非使用 HTTP 接收或独立捕获集成,否则需由 Agent 调用 Iris 工具来捕获轨迹。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
9收录的工具列表 · 更新于 3 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_name | string | 是 | Agent 名称 — 用于在 get_traces 中筛选(例如,"customer-support-bot") |
| framework | string | 否 | Agent 框架标识符(例如,langchain、autogen、custom) |
| input | string | 否 | Agent 输入文本——生成此输出的用户提示词或上游输入 |
| output | string | 否 | Agent 输出文本——Agent 生成的内容(传递给 evaluate_output 进行评分) |
| tool_calls | array | 否 | 执行期间发起的工具调用(每次调用的延迟、错误、输入/输出) |
| latency_ms | number | 否 | 总执行时间(毫秒,端到端 Agent 延迟) |
| token_usage | object | 否 | Token 用量明细(提示词/补全/总计——用于成本分析) |
| token_usage.prompt_tokens | number | 否 | |
| token_usage.completion_tokens | number | 否 | |
| token_usage.total_tokens | number | 否 | |
| cost_usd | number | 否 | USD 总成本——提供时会覆盖按 span 汇总的成本(视为权威值) |
| metadata | object | 否 | 不透明的键值标签(例如 {requestId, userId, env})——可在仪表板中查询,但不能通过 get_traces 筛选 |
| spans | array | 否 | 详细的执行 span(包含计时、属性和事件的分层 span 树) |
| timestamp | string | 否 | Trace 时间戳(ISO 8601);省略时默认为 now() |
详细介绍
Iris — 别再凭感觉发布 Agent
Iris 会对每次 Agent 运行的质量、安全性和成本进行评分——在你的机器上完成,无需 SDK 或账户。 大多数 Agent 项目通过运行几个印象中的提示词,再凭感觉查看输出,来检查质量。Iris 用可审计的数字取代这种做法:Agent 的运行记录会写入你磁盘上的 SQLite 数据库,内置的 26 条规则会以确定性方式为其评分,涵盖个人身份信息(PII)、提示词注入、幻觉标记、成本阈值以及 Agent 自身的工具调用;这一切免费完成,不调用大模型。对于语义问题,还可启用 LLM 评判器,并设置严格的单次评估成本上限。每条规则都可检查和编辑,因为无法审计的评判器,不过是披着数字外衣的凭感觉判断。采用 MIT 许可,无遥测。除非你启用以下任一功能,否则数据不会离开你的机器:OpenTelemetry 端点(IRIS_OTEL_ENDPOINT),它会将追踪数据导出到你指定的收集器;使用你自己的密钥启用 LLM 评判器,它会将待评判文本发送给相应服务提供商,且其引用检查会获取输出中引用的页面;或者启用 Webhook,它会向你设置的地址发送 ID、判定结果和规则名称,绝不会发送文本。
需要 Node.js 22.13 或更高版本。 使用 node --version 检查。

演示数据库由 scripts/demo-media.mts 录制;源文件为 demo.mp4。静态截图:dashboard-overview.png。
60 秒内在屏幕上看到一次失败
无需连接 Agent,无需配置——只需一条命令:
npx @iris-eval/mcp-server --demo
这会填充一个演示数据库——其中有五个小型 Agent、两周的运行记录,以及引擎自己的所有判定——并在 http://localhost:6920 上展示基于该数据库的仪表板(首次运行时会自动打开浏览器)。仪表板会显示在失败页面:失败项按严重程度和时间从高到低排列,每张卡片都标明规则及其证据。值得点进去看看:安全规则捕获到的 PII 泄漏;摘要器照办了论坛帖子中的隐藏指令;源文档中从未提及的数字;对同一组十二个问题的两次运行进行区间对比(运行记录);一条已部署的自定义规则和一条已暂停的规则,以及它们的审计记录;还有一次失败的 LLM 评判分数及其理由。
演示数据存放在独立的数据库中(Iris 主目录下的 demo.db——macOS/Linux 上为 ~/.iris,Windows 上为 %USERPROFILE%\.iris),绝不会与真实追踪记录混在一起。只需一条命令即可删除所有演示数据:
npx @iris-eval/mcp-server --demo-clear
接入你自己的 Agent
首先,验证安装可在这台机器上正常运行——它离线运行,不会打开你的任何文件:
npx @iris-eval/mcp-server --self-test # exit 0 = healthy
然后,将 Iris 添加到你的 MCP 客户端。一条命令即可写入客户端自己的配置文件,保留其中的其他所有服务,并锁定到你运行的版本:
npx -y @iris-eval/mcp-server install claude-code
支持的客户端:claude-code、claude-desktop、cursor、windsurf、continue、vscode、cline、zed、codex、gemini。install --list 会显示这台机器上检测到的客户端、每个客户端运行的 Iris 版本,以及它读取的文件;运行 install <client> --uninstall 即可移除 Iris。所有客户端共用一个数据库,因此升级后请一次性迁移它们:运行 install --upgrade(参见更新)。重启客户端以加载配置。
Claude Desktop:一键安装。 从 0.20.0 版开始,每个发行版都会附带 iris-eval.mcpb,这是一个 MCP Bundle:下载最新版本,打开后,Claude Desktop 会显示安装对话框。其中没有任何必填项——用于 LLM 评审器的 Anthropic 或 OpenAI 密钥为可选项,且仪表板开关默认关闭。该 Bundle 包含 npm 软件包及其依赖,因此无需安装其他内容:当 Claude Desktop 自带的 Node 版本为 22.13 或更高版本时,它会使用该 Node 运行 Iris(Claude Desktop 1.1.6679 自带 24.13 版);Iris 使用 Node 内置的 SQLite 存储跟踪记录,路径与其他所有安装共用的 ~/.iris 相同。发行说明介绍了如何验证其签名和构建证明。
Iris 可在任何 MCP 客户端中运行,文中提到的每个客户端都有一行记录,说明实际经过了哪些验证。每次 CI 运行都会验证:Claude Code、Gemini CLI——真实客户端会根据安装程序写入的配置启动 Iris,并报告连接成功(claude mcp list、gemini mcp list);Linux、macOS 和 Windows 均已验证。Claude Code 的捕获插件钩子也会通过真实脚本进行测试。根据各客户端自己的 MCP 文档声明支持——安装程序会写入客户端文档中规定的配置格式,并针对该格式测试写入器;Iris 团队无人实际观察过这些客户端成功连接:Claude Desktop、Cursor、Devin Desktop(Windsurf)、Continue、VS Code、Cline、Zed、OpenAI Codex CLI。每一行都附有来源及查阅日期:https://iris-eval.com/clients。也可以手动配置,只需粘贴下面这段,包含仪表板配置:
{
"mcpServers": {
"iris-eval": {
"command": "npx",
"args": ["-y", "@iris-eval/mcp-server", "--dashboard"]
}
}
}
客户端连接时会列出 Iris 的十二个工具,仪表板运行于 **http://localhost:6920**。现在,把这句话发给你的 Agent:
将刚才的任务记录到 Iris,并评估输出。
跟踪记录及其评分会显示在仪表板上。若希望 MCP 服务以无头模式运行,请从参数中移除 --dashboard——你随时可以通过 npx @iris-eval/mcp-server --dashboard 打开同一个仪表板。
有一点最好一开始就了解: MCP 工具只有在模型决定调用时才会运行。Iris 不会拦截你的 Agent,因此只有当 Agent 请求 Iris 记录跟踪时,跟踪记录才会被记录——这可能是因为你告诉了它,也可能是因为你的代码直接调用了这些工具。让 Agent“将此记录到 Iris 并进行评估”,它就会执行。如果你希望捕获过程不依赖模型是否选择调用,POST /api/v1/traces 就能做到这一点——你的代码通过普通 HTTP 发送跟踪记录,不涉及模型(参见 docs/http-ingest.md)。路线图中的 CLI 和主机钩子将是同一端点的轻量客户端。
通过 HTTP 采集(不经过模型)
采集端点位于仪表板端口,默认是 6920,而不是 MCP 传输端口,并且仅在仪表板运行时存在。传入 --dashboard(或设置 IRIS_DASHBOARD=true);单独使用 --transport http 不会启动仪表板,向传输端口发送请求会返回 404。仪表板启动后,任何能发送 HTTP 请求的程序都可以记录一条跟踪记录,并可选择在同一请求中运行确定性评估。同一端口上的 GET /api/v1/capabilities 会说明此服务能评判什么、每条规则需要什么、评判器状态及启用步骤,以及相关限制——这与 MCP 资源 iris://capabilities 提供的是同一个对象,因此 HTTP 调用方能获得 MCP 客户端在初始化时得到的框架信息:
curl -s -X POST "http://127.0.0.1:6920/api/v1/traces" \
-H "Content-Type: application/json" \
-d '{
"agent_name": "support-bot",
"input": "What is the refund policy?",
"output": "Refunds are available within 30 days of purchase.",
"evaluate": true,
"eval_type": "safety"
}'
成功时返回 201,其中包含已存储的 trace_id 和评估结果(在 --demo 模式下,端点会以 403 拒绝写入,因此演示数据不会与您的数据混在一起)。端点接受与 log_trace 工具相同的请求正文,并使用与仪表板其余部分相同的中间件栈:默认绑定到环回地址并启用 DNS 重绑定防护;设置密钥后还会启用 Bearer 身份验证。**关于此端点,有两点需要明确:**除非 Iris 启动时指定了 --api-key(或设置了 IRIS_API_KEY),否则它接受未经身份验证的写入;默认情况下,环回地址绑定会将访问限制在您的机器上,因此绑定到环回地址以外的地址之前,请先设置密钥。它存储的内容为原文——input 和 output 会按发送时的内容原样写入 iris.db,包括之后会被 no_pii 标记的文本。完整的接口约定、字段说明和错误语义见docs/http-ingest.md。
采集 Claude Code 的每一轮对话(可选)
/plugin marketplace add iris-eval/mcp-server
/plugin install iris-eval-capture@iris-eval
另一个需要单独安装的插件:它的钩子会记录每一轮对话的提示词、工具调用(包括失败的调用及 Agent 收到的错误)和最终回答,并以提示词为键,因此中断轮次的调用和后台子 Agent 的调用也会发送给 iris-eval ingest,以分离方式运行,并在存储的评估文本中对关键跨度进行脱敏——这类采集不依赖模型自行决定是否调用工具。以 API 错误结束的轮次会被存储,但不会进行评判;如果模型也记录了该轮次,两条跟踪记录都会保留,且插件记录的名称会采用模型记录的名称。它绝不打印内容、阻塞流程或向任何地方发送任何内容。单独安装 iris-eval 不会改变您的轮次处理流程。限制和卸载说明见claude-plugin-capture/README.md。
Python
pip install iris-eval
from iris_eval import IrisClient
iris = IrisClient() # IRIS_URL, or the running dashboard's runtime.json
iris.evaluate_output("…", input="…", agent_name="support-bot")["verdict"] # {"state": "pass", "basis": "clean", "by": []}
这是一个基于服务器 HTTP API 的轻量客户端,适用于服务器 0.16.0 及更高版本,拥有独立的版本号:iris_eval.__version__ 和 PyPI 页面会显示客户端版本,该版本号与服务器版本不同。它提供 log_trace()、evaluate_output()、get_traces()、get_trace()、health()、capabilities(),支持同步和异步调用、带类型的答案,以及服务器自身对拒绝请求的说明;此外还提供一个 pytest 插件,其中包含 iris fixture 和 assert_iris(output, expect="pass"),可对判定状态进行断言。见packages/python/README.md。
记录每次 OpenAI 和 Anthropic 调用
from iris_eval import wrap_openai
client = wrap_openai(OpenAI(), agent_name="support-bot") # every call: a GenAI span to POST /v1/traces, scored
import { wrapOpenAI } from '@iris-eval/sdk';
const openai = wrapOpenAI(new OpenAI(), { agentName: 'support-bot' });
只需包装一次提供方客户端,每次模型调用都会成为一个 OpenTelemetry GenAI span,发送到 OTLP 接口,并连同输入、输出、令牌用量和工具调用一起存储和评分:这种采集方式不依赖模型调用工具。Python 客户端使用 wrap_openai / wrap_anthropic;Vercel AI SDK 使用 @iris-eval/sdk 中的 wrapOpenAI、wrapAnthropic 和 irisMiddleware。这两者尚未发布(下一个 iris-eval 版本将在 PyPI 发布;@iris-eval/sdk 在首次 npm 发布前需从源码构建)。支持流式调用、SDK 的流式辅助函数和工具调用,不会更改原始客户端;即使 Iris 宕机,也不会导致调用失败 — packages/sdk/README.md、packages/python/README.md。
为每次 LangChain 和 LangGraph 运行评分
from iris_eval.langchain import IrisCallbackHandler
graph.invoke(inputs, config={"callbacks": [IrisCallbackHandler(agent_name="support-bot")]})
每次顶层运行都会成为一条 trace(运行本身、模型调用、工具调用和图节点均作为 GenAI span),其中包含输入、输出、工具调用、令牌用量和判定结果。Python 在客户端中运行(pip install iris-eval,版本 0.2.0 及更高);JavaScript 使用 @iris-eval/langchain(位于此仓库中,尚未发布到 npm)。两者都已在 CI 中通过真实 LangGraph 应用和脚本化模型验证;LangSmith 自带的 OpenTelemetry 导出也以相同方式通过验证 — docs/otel-recipes.md。
CI 门禁,无需服务端
npx -y @iris-eval/mcp-server ingest --file traces.ndjson --evaluate --fail-on any
any 会在判定结果不是通过时让作业失败:包括失败,以及未经检查的判定结果。也可以指定一个依据(例如 --fail-on detector_veto),这样只有符合该依据时作业才会失败。
或者使用 GitHub Action(0.16.0):它会在你指定的判定结果出现时让作业失败,将回执写入作业摘要,并以一条拉取请求评论发布,后续更新原评论:使用 uses: iris-eval/mcp-server/.github/actions/[email protected],并设置 traces: traces.ndjson — docs/ci-gate.md。
第四个入口(0.15.0):仪表板端口上的 POST /v1/traces 接收 OpenTelemetry 埋点已生成的 OTLP/HTTP JSON 或 protobuf(Python SDK 的导出器仅支持 protobuf,因此这也是 Python 的入口),每条 OTLP trace 都会转换为包含其 spans 的 Iris trace — docs/otel-integration.md;docs/otel-recipes.md 为每个框架提供一份配方(Pydantic AI、Google ADK、通过 LangSmith 使用 LangGraph、CrewAI、Python 和 JavaScript 中的 OpenAI Agents SDK、LlamaIndex、AutoGen、Microsoft Agent Framework、Semantic Kernel、Vercel AI SDK 和 Mastra),且每份配方都已通过 fixture 验证。ingest 从标准输入或文件读取一条 JSON trace(或每行一条的 NDJSON),存储该 trace,严格按照 evaluate_output 运行时使用的规则对其进行评估,针对每条 trace 输出一行包含判定结果及其依据的 JSON;当判定结果符合 --fail-on 时,退出码为 1。--dataset <id|label> 会将门禁限制为数据集中的 case key(POST /api/v1/datasets 可将一次运行的 case key 加入数据集),因此作业只会因你选择的案例而失败。完整配方、退出码和八种依据见 docs/ci-gate.md。
将规则编写为代码
config.json 中的 eval.plugins 会加载你编写的规则——一个默认导出 { name, kind, mechanism, version, needs, evaluate(ctx) } 的 ES 模块,并通过文件的 sha256 值固定;因此,固定后文件若有更改,启动时会拒绝运行。已加载的插件会像内置规则一样触发,并显示在 list_rules 的 plugins 下。契约、哈希计算方法以及插件可以返回的内容:docs/plugins.md。
在你自己的进程中使用引擎
评估引擎可以导入——无需服务、数据库或模型:
import { EvalEngine, defaultConfig } from '@iris-eval/mcp-server/engine';
const engine = new EvalEngine(defaultConfig.eval.defaultThreshold, defaultConfig.eval.ruleThresholds, defaultConfig.eval);
const result = await engine.evaluateAll({ output: answer, input: prompt, toolCalls, costUsd });
result.verdict.state; // 'pass' | 'fail' | 'unknown', with result.verdict.basis and result.interpretations
与服务运行的引擎、规则和组合器完全相同;builtInRules()、createCustomRule()、compose() 和已发布准确率的读取器也与引擎一同导出。
用于 HTTP 路由的类型化客户端
import { createClient } from '@iris-eval/mcp-server/client';
const iris = createClient({ baseUrl: 'http://127.0.0.1:6920', apiKey: process.env.IRIS_API_KEY });
const { trace_id, evaluation } = await iris.logTrace({ agent_name: 'support-bot', input, output, tool_calls, evaluate: true });
evaluation?.verdict?.state; // the same object evaluate_output returns
所有入口都使用同一个请求体:log_trace 和 iris-eval ingest 接受的正是它。遇到拒绝时,会抛出 IrisClientError,其中包含服务自身的错误说明和状态。每次构建都会从打包的 tarball 检查这两个子路径。
验证安装
npx @iris-eval/mcp-server --self-test # offline diagnostic; exit 0 = healthy, 1 = a check failed
npx @iris-eval/mcp-server --version # prints the bare version, e.g. 1.2.3
--self-test 会先在 Iris 主目录不存在时创建该目录,并检查其是否可写(如果不可写,则以退出码 1 退出并指出路径);报告数据库搜索索引的状态(完整、后台构建目前已索引的追踪数量,或此 SQLite 不支持 FTS5);读取数据库架构(如果此版本或固定使用旧版的 MCP 客户端无法打开数据库,则以退出码 1 退出并提供修复方法);然后在隔离的临时主目录中运行检查,包括存储往返测试、检测由安全规则拦截的预置 SSN 和预置注入内容、仪表板启动以及 DNS 重绑定防护。它只会读取你的真实数据库,不会修改它。Iris 写入的所有内容都存放在一个目录中,即你的 Iris 主目录:默认路径为 ~/.iris(Windows 上为 %USERPROFILE%\.iris),也可以是 IRIS_HOME 指定的路径。iris.db、config.json、custom-rules.json、audit.log、preferences.json 和演示文件都存放在此处;将 IRIS_HOME 指向临时目录,即可在不触碰真实数据的情况下试用 Iris。
按工具配置
| 客户端 | 状态 | 含义 | 参考 |
|---|---|---|---|
| Claude Code | 已验证 | 每次 CI 运行时都会通过测试驱动真实客户端 | 2026-09-25 |
| Claude Desktop | 声称支持 | 安装程序会写入客户端文档规定的配置格式,并且会针对该格式测试写入程序;Iris 团队无人亲眼确认过连接 | 2026-09-25 |
| Cursor | 声称支持 | 安装程序会写入客户端文档规定的配置格式,并且会针对该格式测试写入程序;Iris 团队无人亲眼确认过连接 | 2026-09-25 |
| Devin Desktop (Windsurf) | 声称支持 | 安装程序会写入客户端文档规定的配置格式,并且会针对该格式测试写入程序;Iris 团队无人亲眼确认过连接 | 2026-09-25 |
| Continue | 声称支持 | 安装程序会写入客户端文档规定的配置格式,并且会针对该格式测试写入程序;Iris 团队无人亲眼确认过连接 | 2026-09-25 |
| VS Code | 声称支持 | 安装程序会写入客户端文档规定的配置格式,并且会针对该格式测试写入程序;Iris 团队无人亲眼确认过连接 | 2026-09-25 |
| Cline | 声称支持 | 安装程序会写入客户端文档规定的配置格式,并且会针对该格式测试写入程序;Iris 团队无人亲眼确认过连接 | 2026-09-25 |
| Zed | 声称支持 | 安装程序会写入客户端文档规定的配置格式,并且会针对该格式测试写入程序;Iris 团队无人亲眼确认过连接 | 2026-09-25 |
| OpenAI Codex CLI | 声称支持 | 安装程序会写入客户端文档规定的配置格式,并且会针对该格式测试写入程序;Iris 团队无人亲眼确认过连接 | 2026-09-25 |
| Gemini CLI | 已验证 | 每次 CI 运行时都会通过测试驱动真实客户端 | 2026-09-25 |
每一行的检查内容详见:iris-eval.com/clients。没有对应条目的客户端不会被称为受支持。
运行 npx -y @iris-eval/mcp-server install <client> 即可自动写入以上配置。也可以按客户端手动配置:
Claude Desktop
编辑 MCP 配置文件:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
添加上面的 JSON 配置,然后重启 Claude Desktop。
Claude Code
claude mcp add --transport stdio iris-eval -- npx -y @iris-eval/mcp-server
然后重启会话(运行 /clear 或重新启动),以加载工具。
Windows 注意: 不要使用
cmd /c包装命令——这会导致路径解析问题。可直接运行npx命令。
Cursor
将上面的 JSON 配置添加到 ~/.cursor/mcp.json(适用于每个项目)或工作区中的 .cursor/mcp.json,并在 iris-eval 条目中添加 "type": "stdio"——Cursor 文档将其标记为必填项。
Devin Desktop (Windsurf)
将上面的 JSON 配置添加到 mcp_config.json:macOS 和 Linux 上为 ~/.config/devin/mcp_config.json,Windows 上为 %APPDATA%\devin\mcp_config.json。
Continue
将上面的 JSON 配置另存为独立文件,放在 Continue 的 mcpServers 文件夹中:每个工作区对应 ~/.continue/mcpServers/iris-eval.json,或在某个工作区中放在 .continue/mcpServers/iris-eval.json。
VS Code(原生 MCP)
将以下内容添加到工作区中的 .vscode/mcp.json(注意:VS Code 使用 servers,而非 mcpServers):
{
"servers": {
"iris-eval": {
"command": "npx",
"args": ["-y", "@iris-eval/mcp-server"]
}
}
}
Cline
打开 Cline 的 MCP Servers 面板 → 配置 MCP Servers,然后将上面的 mcpServers JSON 配置添加到 cline_mcp_settings.json(路径为 ~/.cline/data/settings/cline_mcp_settings.json,由 VS Code、JetBrains 和 CLI 中的 Cline 共用)。
Zed
将以下内容添加到 Zed 的 settings.json:
{
"context_servers": {
"iris-eval": {
"command": "npx",
"args": ["-y", "@iris-eval/mcp-server"],
"env": {}
}
}
}
OpenAI Codex CLI
添加到 ~/.codex/config.toml:
[mcp_servers.iris-eval]
command = "npx"
args = ["-y", "@iris-eval/mcp-server"]
Gemini CLI
将上面的 mcpServers JSON 配置添加到 ~/.gemini/settings.json。Gemini CLI 仅在其信任的文件夹中连接 MCP 服务:如果 gemini mcp list 显示 iris-eval 为 Disabled,请在该文件夹中运行 /permissions。
其他支持 MCP 的客户端
Iris 是标准的 stdio MCP 服务——只需运行一条 npx @iris-eval/mcp-server 命令,无需 SDK,也无需更改代码。如果你的客户端支持 MCP,就支持 Iris。客户端配置格式可能会变化;如有疑问,请查阅客户端的 MCP 文档,并将其指向该命令。
其他安装方式
# Global install (recommended for persistent data and faster startup)
npm install -g @iris-eval/mcp-server
iris-eval --dashboard
# Docker — two servers, two ports: 3000 = MCP HTTP transport,
# 6920 = dashboard (which also serves the POST /api/v1/traces ingest endpoint).
# The image binds 0.0.0.0 inside the container, so a key is required (see Production).
# The volume is the Iris home: the database, deployed rules and audit log persist in it.
docker run -p 3000:3000 -p 6920:6920 -v iris-data:/data \
-e IRIS_API_KEY="$(openssl rand -hex 32)" ghcr.io/iris-eval/mcp-server
提示: 全局安装(
npm install -g)会将追踪记录持久存储在~/.iris/iris.db。使用npx时,追踪记录也会持久保存在同一位置,但由于需要解析软件包,启动速度较慢。
你将获得什么
| Trace 日志记录 | 分层 Span 树,包含每次工具调用的延迟、Token 用量和美元成本。数据存储在 SQLite 中,可即时查询。 |
| 输出评估 | 内置 26 条规则,涵盖 4 个类别:完整性、相关性、安全性、成本。PII 检测(21 种模式:SSN、信用卡、电话、电子邮件、IBAN、DOB、MRN、IP、API 密钥、护照,以及 AWS/Slack/SendGrid/GitHub/Google/npm/DigitalOcean Token、URL 中的凭据、名称中含有 secret 的赋值、PEM 私钥块和助记词;出生日期、病历号、护照和助记词仅在其标签旁出现时才会触发,这是有意设计的),提示词注入检测(38 种模式,包含短语和结构),占位输出检测、幻觉检测(25 种基于上下文的编造/矛盾信号——传入 input,即可根据 Agent 的源材料进行核验),以及六条轨迹规则,用于读取 Agent 已执行的操作:未确认的失败工具调用;重复调用(按调用、按重复序列,或在你传入 tools 后按目标识别);工具自身的 JSON Schema 拒绝其参数、且 Agent 未重试的调用;答案中引用的文件、目录或 URL,但 Agent 读取的内容中均未出现;来自 TOOL RESULT 的指令随后被后续调用遵循;以及工具调用次数超过步骤预算的任务。轨迹可通过 tool_calls 或 OpenTelemetry TOOL Span 传入。使用 Zod Schema 添加自定义规则。 |
| LLM-as-Judge | 可选的语义评分,通过 Anthropic 或 OpenAI 实现——使用你自己的 API 密钥。提供七种模板。设置 IRIS_RELEVANCE_JUDGE_MODEL 后,answers_the_ask 会调用 relevance 评审器,并将离题的回答判定为失败;未设置时,该规则会通过词汇层面分析问题并提供建议。每次评估设有硬性成本上限(IRIS_LLM_JUDGE_MAX_COST_USD_PER_EVAL,默认值为 $0.25),结果中会披露每次评估的定价。 |
| 成本可见性 | 汇总所有 Agent 在任意时间范围内的成本。设置预算阈值。Agent 超支时会收到标记。对于提供了 Token 数量和模型、但未提供成本的轨迹(大多数 OpenTelemetry 和框架轨迹),系统会按模型的标价计算,并在所有展示位置标注为估算值;config.json 中的 pricing.models 可为内置表中没有的模型定价——文档。 |
| Web 仪表板 | 实时暗色模式界面,优先显示失败项,并按最差到最新排序——提供轨迹可视化、可搜索所有轨迹文本的全文搜索、评估结果、成本明细,以及可搜索你自己的规则、轨迹和评估的命令面板(⌘K)。 |
| 本地优先 | 所有数据都存储在你磁盘上的 SQLite 中。可自托管的服务无需账户或注册,也不会发送遥测数据。只有在你选择启用时才会发生出站 HTTP 通信:使用你自己的 LLM 评审密钥、获取引用内容、配置 OTel 导出器,或设置 Webhook。 |
接下来将如何发展:能力图谱——列出可向 Iris 提出的关于各个主题的所有问题,以及它已具备和欠缺的能力——以及三条路线。
有数据支撑,而非空口宣称
每条内置规则都公布了精确率、召回率和 F1 值及其 95% 置信区间,这些数据是在标注语料上测得的。语料位于此仓库(proof/corpus/)中,可通过一条命令 — npm run proof — 离线重新生成,无需密钥,也不涉及模型。这些数字分属两种不同类型,页面不会将它们混为一谈:有些规则是根据模型阅读故障本身后给出的标签来测量的,这衡量的是检测能力;其余规则则根据各自文档中独立应用的定义进行核验,这表明代码实现了相应公式,但并不能说明该公式是否能捕获故障。proof/RESULTS.md 和验证页面会标明每条规则的类型。CI 会在每个拉取请求上重新运行测量,并在提交的数字与代码生成的结果不一致时失败,因此规则变更时,其对应数字也必须随之改变。数据见 iris-eval.com/proof 和 proof/RESULTS.md;语料的制作方式、它不代表什么,以及如何解读置信区间,见 docs/proof.md。该语料为合成语料,并由模型标注 — 人工盲标尚未完成,页面对此有明确说明;运行 node proof/blind-sample.mjs 可抽取可复现的样本,最终结果将以此为准。
MCP 工具
Iris 注册了十二种工具,任何兼容 MCP 的 Agent 都可以调用 — 覆盖追踪和规则生命周期、跨运行比较、LLM-as-judge 以及语义引用核验:
log_trace— 记录一次 Agent 执行,包括 spans、工具调用、令牌用量和成本;传入evaluate: true可在同一次调用中对其评分evaluate_output— 根据完整性、相关性、安全性和成本规则评估输出质量(启发式、确定性、免费)get_traces— 查询已存储的追踪记录,支持筛选、分页和时间范围;也可通过q查找 Agent 说过某内容的运行记录:对输入、输出、工具调用值和元数据进行全文搜索,按相关性排序,并标出匹配词list_rules— 列出已部署的自定义评估规则(只读)deploy_rule— 注册新的自定义评估规则,使其在该类别的每次evaluate_output调用中生效delete_rule— 移除已部署的自定义规则(破坏性操作、幂等)delete_trace— 按 ID 移除单条已存储的追踪记录(破坏性操作,限定租户)evaluate_with_llm_judge— 通过 LLM 进行语义评估(Anthropic 或 OpenAI)。提供七种模板:准确性、帮助性、安全性、正确性、忠实度、任务完成度、相关性。设有成本上限,并公开每次评估的定价。请自备 API 密钥(IRIS_ANTHROPIC_API_KEY或IRIS_OPENAI_API_KEY)— Iris 不会代理或中继 LLM 调用。verify_citations— 从输出中提取引用(编号引用、作者-年份格式、URL、DOI),通过受 SSRF 防护且设有域名白名单的解析器获取来源,并使用 LLM judge 检查每个来源是否确实支持相应引用主张。默认不启用出站 HTTP。与evaluate_with_llm_judge有相同的 BYOK 要求。compare_runs— 某项更改是否让 Agent 表现变差?比较两次运行中存储的评估:如果两次运行共享案例键,则进行配对精确检验;否则计算差值区间;如果无法判断,则如实给出“无法判断”以及所需的案例数;也可能得出“在误差范围内等效”。每条规则都有自己的单侧检验,并统一进行校正(Benjamini–Hochberg),避免二十条规则凭空制造出回归compare_traces— Agent 对同一问题的回答有多可靠?显示各案例的通过率及其区间,优先列出不稳定案例,并提供考虑重复次数的总体通过率evaluate_runs— 根据当前规则重新评估某次运行中的所有追踪记录,并生成新的运行记录,避免将规则变更误判为 Agent 变更 启用 LLM 评审器(可选;确定性规则始终不需要它)
- 从 Anthropic 或 OpenAI 获取 API 密钥。
- 将它设置在运行 Iris 的进程环境中,而不只是设置在你的 shell 中。Claude Code、Claude Desktop、Cursor 和大多数 MCP 客户端:在 MCP 配置中 iris-eval 条目的 "env" 块里设置——"iris-eval": { "command": "npx", "args": ["-y", "@iris-eval/mcp-server"], "env": { "IRIS_ANTHROPIC_API_KEY": "sk-ant-..." } }(如果使用 OpenAI 密钥,则设置 IRIS_OPENAI_API_KEY)。Docker:在 run 命令中添加 -e IRIS_ANTHROPIC_API_KEY=...。HTTP 或 CI:启动 iris-eval 前先导出该变量。
- 重启 MCP 会话。运行中的进程无法看到它启动后才设置的变量。
- 在客户端内确认:读取 iris://capabilities——其中的 judge.enabled 必须为 true。除非客户端配置中列出了该密钥,否则在 shell 中导出的密钥不会传递给客户端启动的进程。在机器上,
npx @iris-eval/mcp-server --self-test会打印该 shell 的评审器状态;运行中的仪表板则会通过 GET /api/v1/health 报告 judge.enabled。 - 支出保护:每次调用的费用上限由 IRIS_LLM_JUDGE_MAX_COST_USD_PER_EVAL 控制(默认为 0.25 USD);若最坏情况下的费用会超出上限,则会在产生任何支出前拒绝调用。Iris 使用你的密钥发起的每次评审器调用,都会计入同一个每日预算 IRIS_LLM_JUDGE_DAILY_BUDGET_USD(每天 UTC 时间默认为 1 USD);超出预算后,直到 UTC 午夜前都不会再发起调用。Iris 会使用你的密钥直接调用服务提供商,不会代理该密钥。
- 可选:将 IRIS_RELEVANCE_JUDGE_MODEL 设为一个有定价的模型 ID(例如 claude-haiku-4-5),这样 answers_the_ask 就会让评审器判断每个答案是否回应了对应的提问,并将偏题的答案判定为失败。每次包含输入的评估会发起一次评审器调用,使用你的密钥并受上述费用上限约束;仅设置密钥不会启用此功能。每次调用都会将该输入和输出发送给模型服务提供商,并先替换个人数据和凭据的 no_pii 标志(IRIS_RELEVANCE_JUDGE_REDACT=off 则会原样发送)。调用费用计入该每日预算,并且每个请求最多发起 IRIS_RELEVANCE_JUDGE_MAX_CALLS_PER_REQUEST 次调用(默认为 20);超过调用上限或预算后,answers_the_ask 会通过词面匹配读取提问并说明原因。
配置 IRIS_OTEL_ENDPOINT 后,log_trace 调用还会尽力向任意 OpenTelemetry 收集器(Jaeger、Grafana Tempo、Datadog OTLP、Honeycomb 等)导出 OTLP/HTTP JSON。请参阅 docs/otel-integration.md。
如何判定 passed
evaluate_output 会同时返回 score 和 passed 标志——它们回答的是不同的问题:
score(0..1)是已运行规则的加权平均值——反映质量的连续指标。passed是是否发布的判定,完全不会参考分数。组合器会根据每条规则所作声明的类型来处理:你配置的策略会作为门槛;关键检测器会否决;已被要求执行但无法给出答案的关键检查会将判定设为未知(passed: false),而非无问题;其余检测器会合并为一个输出不合格的概率,并根据你在eval.falsePassCost中指定的损失比进行权衡(默认为 1,因此阈值为 0.5)。verdict.basis会标明作出判定的层,verdict.by会列出相关规则,verdict.also会列出所有后续同样会作出该判定的层;interpretations[]会说明失败的规则为何没有决定判定,以及哪些设置可以改变这一点,并指出任何未作出判断的问题,以及完成判断所需的输入。
真正的安全违规会直接判定失败。默认情况下,no_pii、no_injection_patterns 和 no_blocklist_words 是关键规则:其中任何一条失败,评估结果都会报告 passed: false,无论其他规则得分多高;响应会在 critical_failures 中列出违规项。泄露的 SSN 不能被平均掉。哪些内置规则属于关键规则由部署设置决定(eval.criticalRules / eval.nonCriticalRules);每条规则结果都会携带生效的 critical 标志和 criticalSource,list_rules 会报告此服务采用的规则清单。以 severity: "high" 或 "critical" 部署的自定义规则也会以相同方式直接判定失败;low / medium 严重程度只影响分数。有一点需要了解,并且所有界面上的说法都一致:已跳过的关键规则(缺少上下文、定义有误,或正则表达式在沙箱预算耗尽时被终止)并未对输出作出判断,因此不会否决——所有此类规则都会列在 critical_skipped 中。必须采用失败即关闭策略的门禁会将非空的 critical_skipped 视为未知,而非无问题,并且可以将 rule_results 中任何 budgetExceeded 跳过项也作同样处理。
对于 CI 门禁:如果省略 eval_type,所有评估包都会运行——完整性、相关性、安全性、成本以及任何自定义规则;响应会使用 eval_type: "all",并附带说明默认配置已运行的 note,以及按评估包划分的 categories 映射。每一行都会带有 state:根据该评估包所包含规则得出的判定,因此不会将未参与评估的规则误读为导致评估失败的规则。没有可评估内容的评估包(例如成本评估没有 cost_usd,或相关性评估没有 input)会报告 state: "unknown",并在该包中设为 passed: null——这表示未评估,而非失败,且绝不会计入总判定。响应始终会回显实际运行的 eval_type,因此门禁可以验证覆盖范围;根据 passed 作出判定,只有在需要缩小运行范围时才指定评估包。
编写自定义规则
添加规则有两种方式。内联规则随一次 evaluate_output 调用一并传入(custom_rules,每次最多 10 条);它们会与所选 eval_type 规则集一同触发,也可以单独使用 eval_type: "custom"。已部署规则通过 deploy_rule 注册一次,保存在 Iris 主目录下的 custom-rules.json 中,并会在之后每次 evaluate_output 的 evalType 与之匹配时触发。两种方式的定义格式相同:
| 字段 | 必填 | 说明 |
|---|---|---|
name | 是 | 1–80 个字符;在结果中显示为 ruleName |
type | 是 | 以下类型之一:regex_match · regex_no_match · min_length · max_length · contains_keywords · excludes_keywords · json_schema · cost_threshold |
config | 是 | 对应类型所需的键:两种正则类型使用 pattern(以及可选的 flags);min_length / max_length(字符数);两种关键词类型使用 keywords(以及可选的 threshold,范围为 0–1,默认值 1 表示必须全部出现);json_schema 使用 {};cost_threshold 使用以美元计的 max_cost |
weight | 否 | 在评分中的权重;默认值为 1 |
deploy_rule 会将定义与 name、可选的 description、evalType(completeness · relevance · safety · cost · custom)和 severity 一并包装。严重级别表示失败的含义:low / medium 只会降低评分;high / critical 会直接判定评估失败——passed: false,并将规则列入 critical_failures,不受加权评分影响。跳过的规则(缺少 cost_usd 的 cost_threshold 规则,或在 100 ms 沙盒时限内被终止的正则规则)并未对输出作出判定,因此会列入 critical_skipped。部署一条关键规则,禁止 Agent 输出任何内部主机名:
{
"name": "no_internal_hostnames",
"description": "Output must not mention internal hostnames.",
"evalType": "safety",
"severity": "critical",
"definition": {
"name": "no_internal_hostnames",
"type": "regex_no_match",
"config": { "pattern": "\\b[a-z0-9-]+\\.internal\\.example\\b", "flags": "i" }
}
}
响应中包含已持久化的规则——请保留 id,以便调用 delete_rule:
{ "rule": { "id": "rule-588823d0", "name": "no_internal_hostnames", "evalType": "safety", "severity": "critical", "enabled": true, "version": 1, "definition": { "…": "…" } } }
从下一次 eval_type: "safety" 的 evaluate_output 开始,凡是提及 db-primary.internal.example 的输出都会返回 passed: false,并附带 critical_failures: ["no_internal_hostnames"]——即便五条内置安全规则全部通过,且加权评分为 0.895。正则表达式模式在部署时必须通过 ReDoS 检查,并始终在有硬性 100 ms 时限的沙盒工作进程中运行。list_rules 会显示已部署的规则;仪表板的规则编辑器可根据你点击的失败项生成相同格式的规则定义。完整参考、各类型的评分说明和示例请见:docs/custom-rules.md。
完整的工具架构和配置:iris-eval.com
可自托管且免费
Iris 可在你的机器或自有基础设施上运行。开源核心免费且采用 MIT 许可证,没有使用限制,也无需账户。托管存储、团队共享历史记录和告警功能目前仍在考虑中,尚未开始开发;如果这些功能对你有帮助,请发送邮件至 [email protected] 告诉我们。
我们坚持两项承诺:今天免费的功能绝不会转为付费功能,并且在取得合规认证之前,绝不声称已获得该认证。
示例
- Claude Desktop 配置 — 适用于 stdio 和 HTTP 模式的 MCP 配置
- TypeScript — MCP SDK 客户端 — 连接并调用工具
- HTTP 传输(TS + Python) — 用于 REST 风格集成的完整客户端代码
- 一个逐次评分的 LangGraph Agent(Python) — 在图的回调中使用
IrisCallbackHandler;CI 使用脚本化模型运行同一张图 - 通过 OpenTelemetry 接入的 CrewAI 团队(Python) — OpenInference 插桩直接连接 Iris 的 OTLP 接口,并将 CrewAI 配方作为脚本运行
- 通过 OpenTelemetry 接入的 OpenAI Agents SDK Agent(Python) 和 (JavaScript),以及 一个 LlamaIndex Agent — 每个配方都作为脚本,由 CI 对接真实的 Iris 服务运行
社区
- GitHub Issues — Bug 报告和功能请求
- GitHub Discussions — 问题和想法
- 贡献指南 — 如何参与贡献
- HTTP 接收 — 通过
POST /api/v1/traces确定性地捕获追踪数据 - 功能一览 — 可以向 Iris 提出的所有问题,以及它无法回答的内容
- 版本策略 — 每个版本号代表什么,以及发布 1.0 前必须满足的条件
配置与安全
### CLI Arguments| Flag | Default | Description |
|---|---|---|
--transport | stdio | 传输类型:stdio 或 http |
--port | 3000 | HTTP 传输端口 |
--db-path | ~/.iris/iris.db | SQLite 数据库路径 |
--config | ~/.iris/config.json | 配置文件路径 |
--api-key | — | 用于 HTTP 身份验证的 API 密钥(适用于传输和仪表板,包括 POST /api/v1/traces) |
--dashboard | false | 启用 Web 仪表板。也是启动 POST /api/v1/traces 数据接收端点的唯一方式;它不会随 --transport http 隐式启动 |
--dashboard-port | 6920 | 仪表板端口 |
--dashboard-host | 127.0.0.1 | 仪表板绑定地址。默认仅绑定环回地址——除非设置了 --api-key,否则仪表板无需身份验证,因此绑定到环回地址以外的地址会暴露完整的追踪记录历史 |
--demo | false | 初始化演示数据库(与真实追踪记录分开),并使用该数据库为仪表板提供数据 |
--demo-clear | false | 删除演示数据库并退出 |
--self-test | false | 在隔离的临时主目录中运行离线安装诊断,然后退出(0 = 正常,1 = 检查失败)。它还会以只读方式读取已配置的数据库;如果此版本或固定版本的 MCP 客户端无法打开数据库,则检查失败 |
--purge | false | 删除已配置数据库中的所有追踪记录、跨度和评估,压缩文件并截断预写日志,以免已删除文本继续留在磁盘上,然后退出。已部署的规则、审计日志和偏好设置会保留。此操作不可撤销。请先停止所有正在运行的 Iris 服务端——文件会原地压缩。不能与 --demo、--demo-clear 或 --self-test 一起使用 |
--version | — | 将纯版本号(例如 1.2.3)打印到标准输出并退出,退出码为 0。不会读取 Iris 主目录下的任何内容 |
三个命令会接收各自的参数并退出:iris-eval ingest 从文件或标准输入加载追踪记录(一个无需服务端的 CI 门禁);iris-eval export traces|evaluations --format csv|jsonl 将已存储的数据按仪表板列表的筛选方式筛选后,写入标准输出或 --out 指定的位置(不使用仪表板进行导出);iris-eval install <client> 将 Iris 写入 MCP 客户端的配置中——--uninstall 会移除它,--list 会显示在此机器上找到的客户端及各客户端运行的 Iris 版本,--upgrade 会将所有运行 Iris 的客户端升级到此版本(接入你自己的 Agent、更新)。这些命令都不会启动服务端。
Iris 启动时会验证 config.json。 如果文件中包含 Iris 不会读取的键——例如拼写错误的 eval.critcalRules、其他工具的键——或类型不正确的值,Iris 将拒绝启动,并用一句话说明完整键名、最可能的正确键名或所需类型。文件中的内容不会被静默忽略。
Environment Variables
--help 中记录的所有变量。当命令行标志和环境变量同时设置时,命令行标志优先。
| 变量 | 描述 |
|---|---|
IRIS_TRANSPORT | 传输类型(stdio 或 http) |
IRIS_HOST | HTTP 传输绑定地址(默认 127.0.0.1) |
IRIS_PORT | HTTP 传输端口(1-65535,默认 3000) |
IRIS_HOME | 存放所有用户级文件的目录:config.json、iris.db、custom-rules.json、audit.log、preferences.json(默认 ~/.iris) |
IRIS_DB_PATH | SQLite 数据库路径(仅对数据库覆盖 IRIS_HOME) |
IRIS_SQLITE_DRIVER | 用于存放数据库的 SQLite 驱动:native(better-sqlite3,默认)或 node(Node 内置的 node:sqlite,Node 22.13+)。未设置时使用 native;若原生模块无法加载(或其构建版本会在此 Node 版本上中止),Iris 会警告一次并回退到内置驱动 |
IRIS_SEARCH_BUDGET_MS | 一次轨迹搜索(q)最多读取多长时间,之后会返回目前找到的匹配项,并将 search.complete 设为 false,单位为毫秒(50 到 60000,默认 1000)。搜索在读取期间会阻塞其他请求,因此这也是其他请求最长的等待时间。也可在 config.json 中通过 storage.searchBudgetMs 设置 |
IRIS_SEARCH_INDEX | on(默认)或 off。off 不会保留轨迹的全文索引:写入时只存储轨迹,不做其他处理;轨迹搜索(q)会在 IRIS_SEARCH_BUDGET_MS 时限内直接读取轨迹,按时间从新到旧排列,因此在存储量较大时可能只返回部分匹配项(search.complete: false)。关闭时会删除数据库中保留的索引;重新开启后会在后台构建新索引。也可在 config.json 中通过 storage.searchIndex 设置 |
IRIS_LOG_LEVEL | 日志级别:debug、info、warn、error |
IRIS_DASHBOARD | 设为 true/1/yes/on 可启用 Web 控制面板;设为 false/0/no/off 可禁用(也会覆盖 config.json 中的 dashboard.enabled) |
IRIS_DASHBOARD_PORT | 控制面板端口(1-65535,默认 6920) |
IRIS_WEBHOOK_URL | 在某个时刻触发的 Webhook 的接收地址 — 与 config.json 中的 notify.webhook 合并(文档) |
IRIS_WEBHOOK_SECRET | Webhook 的签名密钥(任意字符串,或 whsec_ + base64);iris 格式要求必须设置该密钥才能运行 |
IRIS_DASHBOARD_HOST | 控制面板绑定地址(默认 127.0.0.1) |
IRIS_API_KEY | HTTP 身份验证用的 API 密钥。若要将 HTTP 传输或控制面板绑定到 loopback 之外的地址(0.0.0.0、局域网地址、容器),则必须设置;否则服务将拒绝启动 |
IRIS_API_KEY_FILE | 文件路径,文件经去除首尾空白后的内容即为 API 密钥 — 这是 Docker 和 Kubernetes 挂载的机密文件模式,可避免密钥留在环境变量块中。设置此项或 IRIS_API_KEY,不要同时设置 |
IRIS_ALLOW_UNAUTHENTICATED | 设为 1,即可有意在 loopback 之外绑定且不使用密钥(解除拒绝限制;此时网络边界由你负责) |
IRIS_ALLOWED_ORIGINS | 以逗号分隔的来源允许列表。控制面板:CORS 标头(支持通配模式,例如 http://localhost:*)。HTTP 传输:用于 DNS 重绑定保护的精确匹配 Origin 允许列表(忽略通配模式;始终允许服务自身的 loopback 来源) |
IRIS_MAX_INPUT_ELEMENTS | 输入在验证前最多可包含的数组元素和对象键数量(整数,默认 100000):适用于所有传输上的工具调用参数,以及控制面板端口上的请求正文(API 的 JSON 正文和 POST /v1/traces 的 JSON 或 protobuf)。超出限制的工具调用会返回错误结果;正文则返回 413。该默认值适用于所有经测量的合法 JSON 输入,即使输入占满整个 1 MB 请求也适用。POST /v1/traces 上的 protobuf 正文有其自身上限:每 security.requestSizeLimit 5 字节对应一个元素(1 MB 时为 209,716),若此设置更高则使用此设置,因此批次大小未超过请求大小限制的导出器不会因元素过密而被拒绝(测得的最密集真实导出器正文每 7.5 字节包含一个元素);提高采集器的 requestSizeLimit 以支持更大批次时,此上限也会随之提高。也可在 config.json 中通过 security.maxInputElements 设置 |
IRIS_MAX_TOOL_INPUT_ELEMENTS | IRIS_MAX_INPUT_ELEMENTS 在 0.21.0 之前的名称;新名称未设置时会读取此项(config.json 中的 security.maxToolInputElements 也同样适用) |
IRIS_NO_AUTO_LAUNCH | 设为 1 可禁用首次运行时自动启动控制面板 |
IRIS_ANTHROPIC_API_KEY | 使用 provider=anthropic 时,evaluate_with_llm_judge 和 verify_citations 必须设置 |
IRIS_OPENAI_API_KEY | 使用 provider=openai 时,evaluate_with_llm_judge 和 verify_citations 必须设置 |
IRIS_LLM_JUDGE_MAX_COST_USD_PER_EVAL | 每次 LLM judge 调用的硬性费用上限(默认 0.25)。max_cost_usd 可针对单次调用降低上限,但不能提高 |
IRIS_RELEVANCE_JUDGE_MODEL | 定价模型的 judge 模型 ID(例如 claude-haiku-4-5)。设置后,并提供对应提供商的密钥时,answers_the_ask 会在每次包含输入的评估中请求此 LLM judge,并根据其相关性判定进行门控 — 每次评估调用一次 judge,费用不超过上述上限以及下面的两个限制。每次此类评估的输入和输出都会通过你的密钥发送给该模型的提供商(Anthropic 或 OpenAI),发送前会先替换 no_pii 标记的个人数据和凭据。未设置(默认)时,answers_the_ask 会从字面上分析用户请求并给出建议,不会调用 judge,也不会发送任何内容(文档) |
IRIS_LLM_JUDGE_DAILY_BUDGET_USD | 每个租户在每个 UTC 日内所有 judge 调用的总费用上限(默认 1):相关性 judge、evaluate_with_llm_judge 和 verify_citations 共用此额度。额度保存在数据库中,因此重启不会重置。只有在最坏情况下的费用不超过剩余额度时才会发起调用;超出后,answers_the_ask 会从字面上分析用户请求(judge.withheld: "daily_budget"),judge 工具则会返回 IRIS_BUDGET_EXCEEDED(引文为 daily_budget_reached)。设为 0 可停止所有调用 |
IRIS_RELEVANCE_JUDGE_DAILY_BUDGET_USD | IRIS_LLM_JUDGE_DAILY_BUDGET_USD 在 0.21.0 之前的名称,当时仅限制相关性 judge。新名称未设置时会读取此项,此时它会限制所有 judge 调用;服务会在启动时说明这一点 |
IRIS_RELEVANCE_JUDGE_MAX_CALLS_PER_REQUEST | 单次请求可发起的相关性 judge 调用次数(默认 20):OTLP 批次或 evaluate_runs 重新评分时,会对前 20 条轨迹调用 judge,其余轨迹按字面分析,并设置 judge.withheld: "request_cap" |
IRIS_RELEVANCE_JUDGE_REDACT | on(默认):发送到相关性 judge 之前,输入和输出中每个跨度的 no_pii 标记(个人数据和凭据)都会替换为 [REDACTED:<kind>#<n>] 标记。off 则按原样发送 |
IRIS_CITATION_ALLOW_FETCH | 设为 1 可允许 verify_citations 发起出站 HTTP 请求(默认关闭)。工具参数可针对单次调用关闭此项,但不能开启 |
IRIS_CITATION_DOMAINS | verify_citations 的主机名允许列表,以逗号分隔(后缀匹配)。工具参数可缩小范围,但不能添加条目 |
IRIS_CITATION_MAX_COST_USD_TOTAL | 单次 verify_citations 调用在 judge 调用上的最高总费用(默认 1)。max_cost_usd_total 可针对单次调用降低上限,但不能提高 |
IRIS_OTEL_ENDPOINT | 启用尽力而为的 OTLP/HTTP JSON 轨迹导出,并将其发送到此采集器 URL |
IRIS_OTEL_SERVICE_NAME | OTel 导出的 service.name 资源属性(默认 iris-eval) |
IRIS_OTEL_HEADERS | OTel 导出的逗号分隔 k=v 标头(例如 authorization=Bearer abc) |
IRIS_OTEL_TIMEOUT_MS | 每次导出的超时时间(默认 15000) |
RATE_LIMIT_SALT | 仅用于网站 — 为 iris-eval.com Playground 速率限制所保留的地址哈希添加盐值;服务不会读取此项 |
安全
使用 HTTP 传输时,Iris 包含:
- 使用时间安全比较的 API 密钥身份验证(API 客户端使用 Bearer;浏览器通过
?key=登录仪表盘) - 默认仅允许 localhost 的 CORS
- 按客户端地址和分钟进行速率限制:仪表盘 API(
security.rateLimit.api)每分钟 600 个请求,MCP 端点(security.rateLimit.mcp)每分钟 20 个请求,均在config.json中设置;超出限制的 MCP 请求会收到一条 JSON-RPC 错误,其中会指出触发限制的键 - Helmet 安全标头
- 所有路由均使用 Zod 验证输入
- 自定义 eval 规则使用可防止 ReDoS 的正则表达式
- 每种传输均限制请求大小为 1 MB(
security.requestSizeLimit):HTTP 返回413,stdio 返回 JSON-RPC 错误并保持会话打开 - 每个输入中的数组元素和对象键最多限制为 100,000 个,并在验证前计数(
security.maxInputElements):所有传输方式下,工具调用的参数超出限制时都会返回错误结果;仪表盘或 OTLP 请求正文超出限制时则返回413 - 验证错误最多返回 20 条问题及总数(
issueCount),因此无论请求有多错误,响应都能保持精简
# Production deployment
iris-eval --transport http --port 3000 --api-key "$(openssl rand -hex 32)" --dashboard
设置密钥后,API 客户端——MCP 客户端、捕获 SDK、POST /api/v1/traces——需发送 Authorization: Bearer <key>。要在浏览器中打开仪表盘,只需在任意仪表盘 URL 后附加一次密钥,例如 http://localhost:6920/?key=<api key>:Iris 会将其兑换为 HttpOnly、SameSite=Lax 会话 Cookie,然后重定向到同一页面,并从地址栏中移除密钥。未使用会话打开的页面会显示登录表单,该表单会执行相同的兑换操作。密钥绝不会存储在浏览器中,会话仅存在于服务器进程中(同时最多有 256 个有效会话;如果登录时所有会话都有效,则会拒绝登录,而不会淘汰现有会话)。
生产环境
使用多个密钥,并无缝轮换。 config.json 中的 security.apiKeys 可保存任意数量的其他密钥,每个密钥都包含 id,并且必须且只能包含以下两项之一:keyFile(文件内容去除首尾空白后即为密钥)或 keyHash(密钥的 sha256 十六进制值,因此配置文件中不含密钥——printf %s "$KEY" | openssl dgst -sha256);还可选填 expiresAt(ISO 8601 时间,密钥到达该时刻后即停止匹配)。轮换密钥的步骤:添加新密钥,迁移客户端,然后移除旧密钥。config.json 和密钥文件中的密钥无需重启即可生效(0.20.0):每个请求到达时,服务器都会检查 config.json 或其中指定的密钥文件是否已更改;如果已更改,就会在响应前重新读取密钥。从 security.apiKeys 中移除密钥或删除其密钥文件后,该密钥会在下一个请求时被撤销:该请求会被拒绝,所有使用该密钥打开的浏览器会话也会退出登录。如果无法读取 config.json(例如文件只写入了一部分),系统会默认拒绝访问;在修复文件之前,只接受来自 IRIS_API_KEY 或 --api-key 的密钥。IRIS_API_KEY 或 --api-key 中的密钥本身,以及是否启用身份验证,仍需重启后才会更改。每个密钥在被移除或过期之前都可用于身份验证,无论是通过 Bearer 方式还是浏览器登录;启动日志会列出密钥 ID。设置 security.rateLimit.mcpKeyBy: "apiKey" 后,MCP 端点的每分钟请求配额将按密钥而非客户端地址计算,因此,共用一个地址的多个 Agent 各自拥有独立的每分钟配额。
多个客户端。 --transport http 可在同一地址上为任意数量的 MCP 客户端提供服务:每次 initialize 都会通过同一个引擎和存储开启独立会话,同时最多支持 256 个会话。使用 stdio 时,每个客户端都会启动自己的服务端进程,这些进程共享 Iris 主目录:通过其中一个进程部署、关闭或删除的规则会在 20 ms 内应用到其他进程,通过其中一个进程写入的标签会立即被其他进程读取,而两个进程同时更改规则时,双方的更改都不会丢失。
当 HTTP 传输或仪表板绑定到回环地址以外的位置(0.0.0.0、局域网地址、容器)且未配置 API 密钥时,Iris 会拒绝启动,并用一句话说明需要设置 IRIS_API_KEY。这也包括直接运行镜像的 docker run,因为容器内会绑定到 0.0.0.0,容器端口发布后无法通过回环地址访问。不配置密钥时,回环地址仍可正常使用(HTTP 传输会发出警告):在这种情况下,机器边界就是访问控制措施。
# The image: pass a key
docker run -p 3000:3000 -p 6920:6920 -v iris-data:/data \
-e IRIS_API_KEY="$(openssl rand -hex 32)" ghcr.io/iris-eval/mcp-server
# Compose: the file requires the variable and refuses before the container starts
IRIS_API_KEY="$(openssl rand -hex 32)" docker compose up
# A network you have already fenced some other way: run open, on purpose
IRIS_ALLOW_UNAUTHENTICATED=1 iris-eval --transport http --dashboard
在已配置密钥的服务上,以下端点按设计公开:传输层的 GET /health 和仪表板的 GET /api/v1/health 无需密钥,也不受任何速率限制;它们以统一格式返回状态、版本、运行时间、SQLite 驱动程序、用于存储、已部署规则文件和迁移(已应用数与已知数对比)的 checks、搜索索引的状态(search:就绪,或显示构建进度占跟踪总数的比例),以及是否配置了裁判密钥——绝不会返回密钥、跟踪内容或跟踪数量。只有所有检查均通过时,status 才为 ok;否则为 degraded,并返回 HTTP 503。Docker 镜像自带的 HEALTHCHECK 会读取此状态。其他所有内容都需要 Authorization: Bearer <key> 或浏览器会话。每台服务器都会执行数据保留:在启动时(服务器开始响应后)以及每隔 retention.sweepIntervalHours,分步删除超过 retention.days(默认值为 30)的跟踪和评估记录,确保请求不会长时间等待;--self-test 会显示此安装的策略,iris://capabilities 和 GET /api/v1/capabilities 则会在 retention 字段中提供该策略。
Webhook 会在特定时刻触发(0.16.0):config.json 中的 notify.webhook(或 IRIS_WEBHOOK_URL 和 IRIS_WEBHOOK_SECRET)用于指定接收端;当判定失败、关键检测否决结果、成本异常、规则失败率变化,或案例首次出现正反两种回答时,Iris 会发送一条签名消息,其中包含 ID、判定结果、规则和数值,但绝不包含 Agent 的文本。消息同时采用 Standard Webhooks 和 GitHub 的签名方式,失败时会通过退避策略重试,并按 Agent 和规则设置冷却时间,且不会阻碍评估;内置 Slack 和 Discord 消息正文。docs/webhooks.md。
磁盘上的数据
Everything Iris 存储的内容都位于你的 Iris 主目录下(~/.iris 或 IRIS_HOME)。iris.db 会逐字保存每条跟踪记录的 input 和 output——包括之后被 no_pii 标记的任何文本;除非你要求,否则检测不会进行脱敏:在 config.json 中设置 storage.redact: "critical_spans",即可将每次评估的输出中被关键检测器标记的片段替换为 [REDACTED:<pattern>] 后存储(默认关闭;证据偏移量仍对应调用方看到的文本)。storage.synchronous 用于设置写入何时到达磁盘:normal(默认值)会在每次检查点同步预写日志,因此 Iris 崩溃不会丢失任何内容,文件也不会损坏;但断电或操作系统崩溃可能会撤销自上次同步以来的写入;full 会在每次提交时同步,即使发生上述两种情况也能保留写入,每次写入约多花 1.5 ms。启动时,以及之后每隔 retention.sweepIntervalHours 小时(默认值为 24,设为 0 可禁用定时清理),系统会删除早于 retention.days 天(在 config.json 中设置,默认值为 30,设为 0 可禁用)的跟踪记录和评估,并检查点预写日志。有一项内容会在保留期限过后继续保留:固定的基线运行及其评估,因为之后的每次运行都要与之比较。每次清理若保留了其中部分内容,都会在日志中说明,并列出运行记录和数量;取消固定该运行后,下次清理就会将其删除。数据集用例和标签不是跟踪记录或评估,因此不会被清理。删除跟踪记录(通过 delete_trace 或清理操作)时,会擦除与之关联的所有评估文本(输出、预期文本和规则消息),并写入 erased_at;判定结果、评分和证据偏移量则会保留。每次删除都会在返回前检查点预写日志,因此已删除的文本不会残留在 iris.db 或 iris.db-wal 中可供读取(如果此时有搜索正在读取文件,或另一个进程正在读取或写入文件,删除操作会直接返回而不等待,待其完成后,文本就会从文件中消失)。若要立即删除所有内容,请停止服务并运行 --purge:该命令会删除所有已存储的跟踪记录、片段和评估,压缩数据库并截断预写日志,从磁盘中彻底移除文本,同时保留已部署的规则、审计日志和偏好设置。发布版本对现有 iris.db 执行迁移前,会在其旁边复制一份文件(iris.db.<from>-to-<to>.<time>.bak,仅所有者可访问,保留最新的三份;降级):副本会保留原样的跟踪记录,因此保留清理会删除早于 retention.days 天的记录,而 --purge 会将其全部删除。服务会先响应客户端,然后在独立线程上进行复制和迁移:期间到达的工具调用、资源读取和 HTTP 请求会等待,最长各等 30 秒,之后将收到拒绝响应,其中会说明服务正在执行的操作(IRIS_STORAGE_ERROR,可重试;HTTP 503,带有 Retry-After)。整个升级期间,健康检查都会正常响应,并说明正在进行的升级。从 0.19.0 版本开始,以 100,000 条跟踪记录进行测试(每条都是一个 Agent 循环),复制和迁移耗时约 6 秒。iris-eval ingest、--purge 和 --self-test 仍会先执行升级,再进行其他操作。
Iris 不会加密静态数据。iris.db 及其预写日志文件均以仅所有者可访问的权限(模式 600)创建,Iris 主目录则以模式 700 创建(Windows 上由文件 ACL 控制)。数据库不会存储大模型提供商密钥:IRIS_ANTHROPIC_API_KEY 和 IRIS_OPENAI_API_KEY 从环境中读取,绝不会写入磁盘。数据库确实会逐字存储跟踪记录的输入和输出,因此请将 Iris 主目录放在加密磁盘或卷上(FileVault、BitLocker、LUKS,或用于 Docker 镜像 /data 挂载的加密云卷)。
一次导出——包括仪表板「跟踪记录」和「评估」页面上的 导出 按钮、GET /api/v1/traces/export 和 /api/v1/evaluations/export,或 iris-eval export——都会原样携带这些存储的文本,与仪表板中显示的内容一致:跟踪记录的输入和输出不做改动,评估输出则应用了 storage.redact。请像对待导出文件所源自的数据库一样对待它。
故障排查
第一步:运行自检
npx @iris-eval/mcp-server --self-test
它会在隔离的临时主目录中检查存储、确定性评估和仪表板,并逐步输出判定结果——失败输出会指出出错步骤。退出代码 0 表示安装正常。
Iris 无法启动 / ERR_MODULE_NOT_FOUND
你可能缓存了较旧的版本。清除 npx 缓存后重试:
npx --yes @iris-eval/mcp-server@latest
或者全局安装,以彻底避免缓存问题:
npm install -g @iris-eval/mcp-server@latest
npm install --ignore-scripts 导致 SQLite 绑定出错
Iris 使用 better-sqlite3 存储跟踪记录,这是一个原生模块,会在安装脚本中获取或编译其绑定。如果跳过了该脚本——例如在命令行中使用了 --ignore-scripts、在 .npmrc 中设置了 ignore-scripts=true(公司机器上很常见),或使用的注册表镜像移除了 postinstall——启动就会失败,并输出一长串「Could not locate the bindings file」错误,其中列出了尝试过的十几个路径。重新构建该模块即可:
npm rebuild better-sqlite3
# for a global install:
npm rebuild -g better-sqlite3
Claude Code 中未显示工具
MCP 工具只会在会话启动时加载。添加 iris-eval 后,使用 /clear 重启会话,或重新启动终端。
检查版本
npx @iris-eval/mcp-server --version
启动日志的第一行也会显示版本(Starting Iris MCP server vX.Y.Z),--self-test 也会在摘要中显示版本。对于全局安装,npm ls -g @iris-eval/mcp-server 会显示已安装的版本。
更新
一台机器上的所有 MCP 客户端共用一个数据库 ~/.iris/iris.db,而 install 会将每个客户端固定到写入其配置的版本。当某个版本更改数据库架构时,该版本的第一个打开文件的进程会升级数据库;此后,仍固定在较旧版本的客户端将无法启动。因此,请在升级前或升级后立即一次性更新所有客户端:
npx -y @iris-eval/mcp-server@latest install --upgrade
此命令会查找这台机器上运行 Iris 的所有客户端配置,并将每个客户端固定到该版本(保留你添加到配置项中的内容,例如 --dashboard 或 env 块);不会改动固定到较新版本的配置项,也不会改动运行 npm 包以外程序的配置项,并会列出所做的更改。请重启它列出的客户端。install --list 会显示每个客户端运行的 Iris 版本。
有两种安装方式不在这些文件中:打开较新的 Claude Desktop 扩展包时,Claude Desktop 扩展(iris-eval.mcpb)会更新;Claude Code 插件则使用 claude plugin marketplace update iris-eval,然后运行 claude plugin update iris-eval@iris-eval(捕获插件还需运行 claude plugin update iris-eval-capture@iris-eval)来更新。
从 0.19.x 升级到 0.20.0。 0.20.0 会向数据库添加搜索索引等内容(迁移 015 及更高版本)。任何 0.20.0 进程打开 ~/.iris/iris.db 后(Claude Desktop 扩展、npx iris-eval,或未指定版本的 npx @iris-eval/mcp-server),固定在 0.19.x 的客户端就会停止运行,并显示 This database was migrated by a newer Iris (…) — migration(s) 015-trace-search, … are unknown to v0.19.0. Upgrade Iris, …。此消息来自 0.19.x,无法更改;请运行上面的命令进行修复。升级前,0.20.0 会在文件旁创建副本,因此也可以回退(见下文)。
升级数据库的启动过程会在 stderr 中输出所做的更改:创建的副本、哪些较旧版本将无法再打开该文件,以及这台机器上固定到这些版本的客户端,并附上相应命令。--self-test 会读取数据库但不会更改它,并会在你启动任何程序前输出相同的信息。
对于全局安装,运行 npm update -g @iris-eval/mcp-server,然后运行 iris-eval install --upgrade。
升级数据库的版本会先在数据库旁边创建副本:文件名为 iris.db.<from>-to-<to>.<time>.bak,位于你的 Iris 主目录中(<from> 是上次更改该文件架构的版本,<to> 是执行升级的版本;启动日志会打印确切路径)。要回退:
- 停止所有 MCP 客户端以及任何使用该数据库的其他 Iris 进程。
- 保留升级后的文件,以备你之后再次使用:将
iris.db重命名为iris.db.upgraded,并删除iris.db-wal和iris.db-shm(如果存在)。 - 将备份复制为
iris.db:cp ~/.iris/iris.db.0.19.0-to-0.20.0.<time>.bak ~/.iris/iris.db。 - 将每个客户端都固定回旧版本:对每个客户端分别运行
npx -y @iris-eval/[email protected] install <client>(install --upgrade不会将客户端回退到旧版本)。
升级后存储的跟踪记录位于 iris.db.upgraded 中,不在备份里。如果没有创建副本(启动日志会说明原因,例如磁盘已满),旧版本将无法打开升级后的文件;此时只能运行 install --upgrade。
存储驱动
在没有预构建 better-sqlite3 的平台上,安装仍会成功。 better-sqlite3 是可选依赖:如果 npm 既无法下载适用于你的 Node 和平台的预构建二进制文件,也无法编译(编译需要 Python 和 C++ 工具链——Windows 上需要 Visual Studio 的 C++ 构建工具),npm 会打印构建错误、跳过该模块,然后完成安装。随后 Iris 会使用 Node 内置的 SQLite 运行,并说明这一情况:启动时会在 stderr 上打印一行,说明原因;--self-test 会显示 driver node: better-sqlite3 is not installed …。要重新启用原生驱动,请在有预构建二进制文件或工具链的环境中安装它(在项目中运行 npm install better-sqlite3;如果是全局安装,可在工具链可用后通过 npm install -g @iris-eval/mcp-server 重新安装 Iris)。每次变更时,CI 都会强制原生构建失败来安装打包后的服务,并要求安装完成,且自检能使用内置驱动存储和读取跟踪记录。
Iris 将所有内容保存在一个 SQLite 文件中,并通过 better-sqlite3 打开——这是一个为你的 Node 和平台下载或编译的原生扩展。如果该模块无法加载,Iris 会回退到 Node 内置的 SQLite(node:sqlite,Node 22.13 或更高版本),并在 stderr 上输出一条警告;因此,缺少预构建二进制文件只会让启动变慢,不会导致无法启动。对于使用 Node 24.19 或更高版本的头文件在本机编译、并在 Node 24.19 至 24.21 上运行的 better-sqlite3,Iris 也会在加载该模块前执行相同的回退:在这些版本中,这样的二进制文件会在首次释放语句时导致整个进程中止(Assertion failed: (env) != nullptr,nodejs/node#65446)。Node 24.22.0 包含了修复,Iris 会在该版本中加载此二进制文件。npm rebuild better-sqlite3 会用预构建二进制文件替换它,该文件在所有版本上都可安全使用。IRIS_SQLITE_DRIVER=node 会按需选择内置驱动,native 则禁止回退。内置驱动在关闭扩展加载和 trusted_schema 的情况下打开;加载时,Node 会在 stderr 上打印自己的 ExperimentalWarning: SQLite is an experimental feature 行,Iris 不会将其隐藏。--self-test 和 GET /health 会指出正在使用的驱动;证明页面上的所有数字都是在原生驱动上测得的,测试套件也会在 CI 中使用两种驱动运行。
Node.js 版本
Iris 要求 Node.js 22.13 或更高版本。Node 20 已于 2026-04-30 结束生命周期,不受支持;Node 18 已于 2025 年 4 月结束生命周期。
最低版本定为 22.13,而不是 22.0,是因为 22.13.0 是首个内置 node:sqlite 的版本。因此,这是所有受支持的 Iris 安装首次都拥有第二种存储驱动的版本:当原生 better-sqlite3 扩展无法加载时,Iris 会回退到 Node 内置的 SQLite,而不是启动失败。在低于 22.13 的版本中——以及 Node 20 的整个生命周期内——始终只有一种驱动,缺少预构建二进制文件就会导致无法启动。
node --version # Must be v22.13.0 or newer
Windows:不需要 cmd /c
Claude Code 的 /doctor 可能会建议使用 cmd /c 包装 npx。这并非必需,还会导致路径解析问题。请直接使用 npx:
# Correct
claude mcp add --transport stdio iris-eval -- npx -y @iris-eval/mcp-server
# Wrong (causes /c to be parsed as a path)
claude mcp add --transport stdio iris-eval -- cmd /c "npx -y @iris-eval/mcp-server"
如果 Iris 对你有帮助,欢迎考虑给仓库点个 Star——这能帮助其他人找到它。
采用 MIT 许可。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。