关于 Provenza
Provenza 为 AI Agent 操作提供治理层,包含 Python SDK 和网关,可检查权限与策略并生成签名审计记录。它面向需要审批工具访问和追踪调用的团队;MCP 客户端可经网关访问管理员批准的工具。
主要功能
- 01根据 Agent 权限和策略检查调用
- 02生成可验证的签名审计记录
- 03将调用暂挂以等待人工审批
- 04将独立模式调用限制在允许的工具内
- 05治理 HTTP 请求和 AI 提供商请求
- 06通过 MCP 暴露获批工具
适用场景
- 01要求 Agent 执行敏感操作前先经审核
- 02追踪并验证 Agent 工具调用记录
- 03将 Agent 限定为使用组织批准的工具
- 04通过策略检查的访问路径连接 MCP 客户端
MCP 相关
MCP 客户端可连接 Provenza 网关,访问经管理员批准、调用经过策略检查并记录的工具。`provenza-mcp` 命令通过 stdio 为启动本地进程的客户端提供网关。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx provenzaPROVENZA_URLYour self-hosted Provenza, e.g. https://provenza.example.comPROVENZA_TOKEN密钥One-time enrollment token (Agents -> New agent); needed on the first start onlyPROVENZA_AGENT_NAMEAgent name on this machine (default: mcp)本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Provenza Python SDK
为 AI Agent 提供受治理、已签名且可审计的操作。
pip install provenza
from provenza import Agent
agent = Agent("support-bot") # reads PROVENZA_URL and PROVENZA_TOKEN
@agent.tool(name="zendesk.read")
def open_tickets(status: str = "open") -> list:
return zendesk.tickets.list(status=status)
open_tickets() # checked by Provenza, run, recorded and signed
调用时会发生什么
在连接模式下(设置了 PROVENZA_URL 时):
-
SDK 会向服务端询问是否可以执行此次调用。服务端会检查 Agent 的权限、组织策略、工具注册表、 行为以及断路器。
-
只有在调用获准后,工具才会运行。否则:
- 被拒绝的调用会引发
ProvenzaDenied(一个PermissionError); - 需要人工决策的调用会引发
ProvenzaApprovalRequired。
两种情况下,尝试都会被记录。有人批准暂缓的调用后,完全相同 的调用只需重新执行一次:它会在一小时内运行。
try: send_report(to="[email protected]") except ProvenzaApprovalRequired as held: if agent.wait_for_approval(held.action_id)["decision"] == "approved": send_report(to="[email protected]") # the same call: runs once - 被拒绝的调用会引发
-
结果会被记录,并使用 Agent 自己的密钥签名。Provenza 会将其封存到已签名的审计日志中,因此你可以下载一份可离线验证的回执。
SDK 会默认拒绝:如果无法连接服务端,工具不会运行,SDK 会引发
ProvenzaUnavailable。传入 fail_open=True,即可继续运行,并在本地将调用记录为未经检查。
在独立模式下(没有服务端),调用会运行,并写入本地的
签名哈希链审计日志。你可以通过 allowed_tools=[...] 限制可用工具。
本地审计日志只会写入参数和结果的哈希值, 不会写入内容。
获取身份
管理员会在 Provenza 中签发一次性注册令牌(Agents → New agent)。Agent 的密钥会在其自己的机器上生成,只有公钥会发送到服务端:
export PROVENZA_URL=https://provenza.example.com
export PROVENZA_TOKEN=pvz_enr_... # used once; afterwards the identity file is enough
身份信息存储在 ~/.config/provenza/agents/<name>.json(权限模式
600)中。如果其他用户可以读取该文件,SDK 会拒绝加载。tools/provenza_sign.py enroll 写入的也是同一个
文件,因此你可以使用 CLI 注册,然后使用 SDK 运行。
若要改为将私钥存储在操作系统钥匙串中:
pip install 'provenza[keychain]'
Agent("bot", key_store="keychain")
若要使用后量子混合密钥(Ed25519 + ML-DSA-65):
pip install 'provenza[pq]'
Agent("bot", hybrid=True)
agent.fingerprint 是你提供给签名验证者的值。
更多信息
with agent.chain(chain_id): # act inside a delegation chain
open_tickets()
agent.delegate(other_agent_id, "summarize Q3", ["read"], tools=["db.read"], expires_in=600)
chk = agent.check("files.delete", {"path": p}) # ask for something that is not a decorated tool
if chk.allowed:
delete(p)
agent.record(chk, {"deleted": True})
异步工具的用法相同(async def)。如果调用已运行,但无法在服务端记录(服务端在调用期间宕机),SDK 不会
隐藏结果。它会在本地审计日志中写入一条已签名的缺口记录,并发出 ProvenzaWarning。
延迟
受治理的调用耗时包括 SDK 自身的处理时间,以及与服务端的往返通信。 SDK 自身的处理时间是在进程内服务端(无网络)上于 2026-10-11 测得的:
record_mode | 工具返回前的往返次数 | 中位数 | p95 |
|---|---|---|---|
"sync"(默认) | 2(检查、记录) | 0.42 ms | 0.59 ms |
"background" | 1(检查) | 0.15 ms | 0.33 ms |
再加上网络往返时间(×2 或 ×1)以及服务端的检查时间。
使用 "sync" 时,工具返回时记录已写入服务端。
使用 "background" 时,记录会按顺序从队列发送。发送失败时,
会在本地审计日志中生成一条已签名的缺口记录,与同步模式相同。
flush() / aflush() 会等待队列处理完毕,close() 和解释器
退出时也会清空队列。进程若被强制终止,尚在队列中的内容会丢失;这就是不等待的代价。
拒绝调用总会在引发异常前记录,因此
ProvenzaDenied.action_id 会立即可用。
自动插桩
import provenza.auto
provenza.auto.instrument(agent) # httpx (sync and async) and requests
此进程直接向 AI 提供商发出的每个调用都会成为受治理的操作。这涵盖 OpenAI、Anthropic、Mistral、Cohere 和 Gemini SDK、LangChain 的提供商客户端,以及普通 HTTP。提供商主机来自组织的 AI 域名目录(影子 AI),由服务端决定每次调用的处理方式:
| 目录状态 | 结果 |
|---|---|
blocked | 拒绝;事件为 ai_domain_blocked |
allowed | 发送,除非策略拒绝 |
| 未分类 | 发送,除非策略拒绝;记录下来供管理员分类 |
记录的内容仅包括主机、方法、不含查询字符串的路径,以及请求正文的哈希和大小。提示词绝不会被记录。
拒绝会以 HTTP 响应返回:状态码 403,包含 OpenAI 风格的错误正文,并带有 x-should-retry: false。因此,提供商 SDK 会按常规抛出权限错误,其中包含 Provenza 给出的原因,且不会重试。如果无法连接服务端,则不会发送任何内容(503)。其他主机以及对 Provenza 本身的调用不受影响。provenza.auto.uninstrument() 会恢复所有设置。
出站代理
在 Provenza 出站代理(仓库中的 proxy/)后,Agent 有两种选择:发送自己的密钥,由代理检查每个请求;或者自行检查请求,再发送检查 ID:
body = json.dumps({"amount": 1000}).encode()
chk = agent.proxy_check("stripe", "POST", "/v1/charges", body=body)
if chk.allowed:
httpx.post("http://127.0.0.1:8443/stripe/v1/charges", content=body, headers=chk.proxy_headers)
path、query 和 body 必须与实际上网发送的内容完全一致。代理会拒绝任何与该检查不符的请求。代理会记录请求,因此不会将其传递给 record()。
agent.proxy_request_headers(...)(参数相同)会返回要发送的请求头:检查对应的请求头,或者在无法连接 Provenza 时,仅返回 Agent 的签名请求。强制执行已签名策略的代理会在本地紧急停用开关将其停止之前,仅依据已签名的发布版本进行判断;Provenza 恢复后会记录请求(docs/pvp/edge.md,第 6 节)。
通过 Provenza 使用 HTTP 工具
在 Provenza 中注册的 API(网关 → HTTP 工具)通过 Provenza 调用,Agent 无需持有其密钥。调用会在一个步骤中完成检查、执行和记录。需要凭据的工具通过持有该凭据的出站代理调用;Provenza 本身不持有任何凭据:
agent = Agent("billing-bot", egress_proxy="http://127.0.0.1:8443") # or PROVENZA_EGRESS_PROXY
out = agent.invoke("crm.set_tier", {"id": "42", "tier": "gold"})
out["status"], out["body"] # a refusal raises ProvenzaDenied
agent.http_tools() # names, descriptions and input schemas, for the model
通过代理发送时,HTTP 请求本身也会签名(X-Provenza-Request),强制模式下的代理要求每个路径都必须带有此签名。对于无法签名的客户端(标准 OpenAI 或 MCP 客户端),请使用此身份文件在 Agent 旁运行代理的 Agent 模式(provenza-proxy -agent-identity …,proxy/README.md)。
出站自检
此主机是否仅能通过 Provenza 出站代理访问外部?请在启动时运行检查,然后定期检查。启用要求限制出站流量后,Agent 仅在最近一次检查结果为 confined 时运行。请参阅 docs/egress-lockdown.md。
agent.egress_selfcheck() # {"verdict": "confined" | "open", ...}
工具许可凭证
允许的检查会附带由组织签名的许可凭证。Agent 会随调用一并发送:
chk = agent.check("crm.update", payload)
if chk.allowed:
httpx.post("https://crm-tool.internal/update", json=payload, headers=chk.permit_headers)
agent.record(chk, output)
工具在未获得针对该调用的有效许可凭证时不会执行任何操作:
from provenza.permit import PermitMiddleware, PermitVerifier, require_permit
verifier = PermitVerifier("https://provenza.example.com", org="acme")
app = PermitMiddleware(app, verifier, tool="crm.update") # any ASGI app: FastAPI, Starlette, ...
@require_permit(verifier, "db.read") # or a plain function
def read(query: str): ...
验证器会根据组织的 JWKS 检查签名、工具、JSON 正文的哈希(必须与已检查的输入一致)以及有效期。随后,它会向 Provenza兑付许可凭证,确保该凭证在该工具的所有副本间只能使用一次。如果无法连接 Provenza,则会拒绝。
集成
每个框架都是可选的附加项。SDK 不依赖任何框架:crewai
和 autogen-core 甚至需要彼此冲突的 protobuf 版本。
通过 Provenza 的网关。 调用会经过服务端,由服务端持有提供方凭据,因此无论 Agent 的 代码如何操作,都会强制执行策略。
# pip install 'provenza[openai]'
from provenza.integrations.openai import gateway_client, wrap
client = gateway_client(agent) # an openai.OpenAI pointed at the LLM gateway, with the agent's key
client = wrap(existing_client, agent) # or a copy of one you already configured
# MCP: no extra needed. Point any MCP client at Provenza instead of at the servers.
from provenza.integrations.mcp import connection, client_config
conn = connection(agent) # {"url": ".../api/v1/mcp", "headers": {...}}
启动本地命令的 MCP 客户端(Claude Desktop、Claude Code、
Cursor、Windsurf、Gemini CLI、VS Code):provenza-mcp 是通过 stdio 提供的同一网关。
在 Provenza 中创建注册令牌(Agents → New agent),然后添加:
{"mcpServers": {"provenza": {
"command": "uvx", "args": ["provenza-mcp"],
"env": {"PROVENZA_URL": "https://provenza.example.com", "PROVENZA_TOKEN": "pvz_enr_..."}}}}
首次启动时,Agent 会注册:它的密钥会在该机器上生成,之后即可移除令牌。客户端只能看到管理员批准的工具,而且每次调用都会根据组织的策略进行检查并记录。设置 PROVENZA_AGENT_NAME,即可在一台机器上运行多个 Agent。
通过 LLM 网关,服务端会检查允许使用的模型和提供方、策略及限制;它不会存储提示词或回答。组织已路由的所有模型(OpenAI、Anthropic、Azure、……)都可通过这一个兼容 OpenAI 的端点访问。目前尚不支持流式传输。
工具包装器。 这些包装器会保留工具的名称、描述和参数架构,因此模型看到的内容不会有差异:
# pip install 'provenza[langchain]'
from provenza.integrations.langchain import govern_tools
tools = govern_tools(agent, [search, read_file]) # a refusal reaches the model as the tool's answer
# pip install 'provenza[autogen]'
from provenza.integrations.autogen import govern_tools
tools = govern_tools(agent, [FunctionTool(lookup, description="...")])
# pip install 'provenza[crewai]'
from provenza.integrations.crewai import govern_tools
researcher = crewai.Agent(role="...", goal="...", tools=govern_tools(agent, [search_tool]))
prefix= 会为策略所见的名称添加命名空间,因此 prefix="kb." 会将
search 转换为 kb.search。对于其他情况,可使用
agent.call(name, arguments, lambda: ...) 或 await agent.acall(...) 管控单次调用。
这是什么,以及它不是什么
SDK 在 Agent 的进程内运行。它是 Agent 的如实记录和协作式强制执行机制:同一进程中的代码可以绕过它调用工具,也可以读取 Agent 的密钥。不依赖 Agent 代码的强制执行由 Provenza 的网关(LLM、MCP、A2A)提供,网关持有凭据,因此 Agent 永远不会持有这些凭据。
使用 provenza.audit_log.verify(path, public_key) 验证本地审计日志。
使用 tools/provenza_audit.py receipt 验证服务端回执。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。