内容
# Vayl
[](https://pypi.org/project/vayl-mcp/)





[](https://vayl.gitbook.io/vayl-docs)
**适用于 AI 代理的持久记忆**。大多数记忆层都会 *累积* —— 保存每个事实,然后在之后将过时的事实提供给代理。Vayl **协调**:新值取代旧值,“我们放弃了 X”实际上移除了 X,并且您可以询问以前的事实。
```
"We use Redux." → 已记住
"Actually we moved off Redux to Zustand." → Redux 已退休,Zustand 激活
"What do we use?" → "Zustand" (不是 "Redux, Zustand")
"What did we use first?" → "Redux" (历史记录)
```
**为什么它与众不同,四行代码解释:**
- ✅ **协调,不会堆积** —— 新值 *取代* 旧值; *相同槽不变性* 意味着一个事物永远不会有两个有效的同时值,因此 "当前是什么" 总是明确的。
- 🗑️ **移除是一等公民** —— “我们放弃了 X” 实际上 *撤销* X。图内存存储无法建模这一点;累加存储永远不会这样做。
- 🕰️ **历史记录和审计** —— 询问当前或过去的事实;每个更改都记录在防篡改、签名审计链上。
- 🔌 **本地,廉价,可扩展** —— 两个核心依赖,默认 SQLite(无需运行服务器),每个事实约 2 个 LLM 调用,并且它支持 MCP,因此任何代理客户端都可以连接。
📚 **完整文档:** [vayl.gitbook.io/vayl-docs](https://vayl.gitbook.io/vayl-docs) — 指南,MCP 工具参考,教程和部署。
新用户?跳转到 [快速入门](#快速入门-2分钟) · [为什么选择 Vayl?](#为什么选择Vayl) · [工作原理](#工作原理) · [贡献](#贡献).
---
## 快速入门(~2 分钟)
```bash
pip install vayl-mcp # 从 PyPI
```
在您的 MCP 客户端(Claude Desktop / Cursor)配置中添加:
```json
{
"mcpServers": {
"vayl": {
"command": "vayl-mcp",
"env": {
"LLM_PROVIDER": "openai",
"OPENAI_API_KEY": "sk-…",
"OPENAI_MODEL": "gpt-5-mini",
"EMBED_BASE_URL": "https://api.openai.com/v1",
"EMBED_MODEL": "text-embedding-3-small",
"VAYL_DB": "/absolute/path/vayl.db"
}
}
}
}
```
`gpt-5-mini` 是默认模型,在现实世界的测试套件中得分 **0% 无效**。任何兼容 OpenAI 的端点都可以使用 — 如果数据驻留很重要,请将 `OPENAI_BASE_URL` 指向您自己的托管或欧盟区域部署。
重启客户端 — 您的代理现在具有以下工具。`VAYL_DB` 是记忆存储的位置。
---
## 代理可用的工具
Vayl 提供了 **30 多种 MCP 工具**,按其功能分组:
| 组 | 工具 | 用于 |
|---|---|---|
| **记忆** | `remember` · `recall` · `history` · `update_memory` · `forget` · `list_memories` | 存储、查询、纠正和穿越事实的时间旅行 |
| **安全** | `check_before_act` · `safe_recall` · `pending_changes` · `confirm_change` / `reject_change` | 不可逆操作的门控;高风险写入的人工审批 |
| **责任** | `record_decision` · `explain_decision` · `attest` · `audit_log` · `verify_audit` | 签名、不可篡改的代理所信和所做的记录 |
| **合规(GDPR)** | `delete` · `delete_all` · `export_memory` · `purge_expired` · `verify_receipt` | 删除、DSAR 导出、保留 — 附带签名收据 |
| **团队 / 管理** | `create_principal` · `set_reconcile_policy` · `license_status` · `stats` · `health` | 多租户范围、冲突策略、运维 |
<details>
<summary><b>📖 完整工具参考</b> — 点击展开所有 30 多种工具的详细行为</summary>
| 工具 | 功能 |
|------|--------------|
| `remember(text, source=…)` | 存储事实。自动取代、撤销或拆分一条消息中的多个事实。`source` 记录谁/什么断言了它(信念来源 + 源感知协调在共享空间中)。 |
| `recall(question, explain=True)` | 从记忆中回答 — 当前值、历史或多步。 **"我不知道"** 而不是猜测。与 `explain=True` 一起返回 **来源** — 使用的精确事实,每个事实都有谁断言了它、置信度和它取代了什么。`include_history=True` 检索已退休的事实,以回答关于过去的问题;`critical_categories=…` 强制将命名类别放入答案中,无论排名如何。 |
| `recall_related(question)` | 通过实体图进行深度/关系/多步问题(“谁拥有 Bob 工作的公司?”)。当图未启用时,退回到 `recall`。 |
| `pending_changes()` | 提议更改 **确认所需** 槽尚未应用 — 人工审批队列。显示旧值 → 新值以及触发它的句子。 |
| `confirm_change(memory_id, decided_by)` | 批准提议的更改,应用它。记录 **谁** 做了决定。 |
| `reject_change(memory_id, decided_by)` | 丢弃提议的更改。当前值保持不变;提议作为历史记录保留。 |
| `history(subject)` | 主题的更改日志 — 它持有的每个值,从旧到新,都有状态。累加存储无法提供的 “我们首先使用 vs 现在” 视图。 |
| `get_memory(memory_id)` | 一个记忆的构造细节(值、状态、范围、置信度、元数据)。`list_memories` 以 `#id` 显示 ID。 |
| `update_memory(memory_id, new_value)` | 通过 ID 修正记忆 — **审计保留**:旧值退休到历史,新值变为活动值。 |
| `forget(text)` | 撤销事实(“我们放弃了 Sentry”,“Alice 离开了”)— 已退休但 **保留在历史中** 进行审计。保证删除。 |
| `record_decision(action_summary, question)` | 记录绑定到 **代理咨询的确切事实** 的决定 — 不变、签名的决策快照。回答“为什么代理执行 X?”即使这些事实后来改变。 |
| `explain_decision(decision_id)` | 重建过去的决定:动作加上 **当时** 持有的信念,并验证签名收据以确保记录未被篡改。 |
| `check_before_act(subject, …)` | **不可逆动作前的安全门控**。返回安全或阻止并提供原因 — 有争议(标记)的值、置信度低于阈值、过时事实或刚刚更改的事实。调整策略(`min_confidence`、`max_staleness_days`,…)。 |
| `safe_recall(question, …)` | 像 `recall` 一样,但仅当答案背后的每个当前事实 **安全可执行** 时才回答;否则它保留答案并返回原因。在操作路径上使用。 |
| `delete(subject)` | **永久擦除** 主题,包括历史 — 被遗忘的权利/GDPR。不同于 `forget`,不保留任何内容;擦除的值也会从决策快照中 **编辑和审计**。发出 **签名擦除收据**。 |
| `delete_all()` | 永久擦除 **所有** 用户的记忆(账户删除/GDPR),同时编辑他们的决策快照。发出签名擦除收据。 |
| `export_memory()` | **DSAR 完整导出** (GDPR 第 15/20 条):语句(活动 + 历史)加上主题的决策快照、审计条目和收据 — 机器可读的 JSON。 |
| `purge_expired(days, …)` | 保留(第 5(1)(e) 条):硬删除超过 N 天的记录;标志将审计日志/决策/收据扩展到 **签名保留锚点**。 |
| `audit_log()` | 责任链 — 谁做了什么,何时;详细加密存储;从不被擦除。 |
| `attest(subject)` | 发布 **签名、第三方可验证的证明** 当前值 — “截至目前,X 是值”,锚定到防篡改审计头。证明已知和何时。 |
| `verify_receipt(receipt_id)` | 验证签名擦除收据或证明 — 重新计算正文并检查 Ed25519 签名。有效/无效。 |
| `verify_audit()` | 验证 **防篡改审计链** 端到端 — 报告完整或中断的确切行(编辑/重新排序/截断)。 |
| `export_public_key()` | Vayl 的 Ed25519 公钥 — 任何人都可以验证收据、证明和审计链 **无需私钥或数据库**。 |
| `set_reconcile_policy(mode, authority)` | 配置 **共享空间** 如何解决不同贡献者之间的冲突:`RECENCY` (更新者获胜),`AUTHORITY` (更高级别的来源获胜;较低级别的被标记,但不被覆盖),或 `REVIEW` (每个跨源冲突都被标记)。 |
| `get_reconcile_policy()` | 显示空间的协调策略。 |
| `create_principal(name, role, scopes)` | *(管理员)* 创建用户/代理并颁发其 API 密钥(显示一次)。角色:管理员、成员、代理、查看者、审计员。`scopes` 限制其到命名 `user_id` — **多租户必填**,请参见下文。 |
| `list_principals()` | *(管理员)* 列出部署的管理员和角色(从不显示密钥)。 |
| `revoke_principal(id, erase)` | *(管理员)* 禁用管理员 — 其 API 密钥立即停止工作。`erase=True` 硬删除记录(团队成员的第 17 条)。 |
| `license_status()` | 显示版本(社区或许可)、已用/允许的席位、到期日和解锁的功能。 |
| `list_memories()` | 当前活动事实(每个都有其 `#id`)以及已取代/撤销的历史块。 |
| `stats()` | 设备上的 KPI — 每个工具的调用次数、平均延迟、错误(包括最近的错误详细信息),以及协调动作的分布(SUPERSEDE / RETRACT / FLAG / SKIP …)。无数据离开设备。 |
| `health()` | 诊断设置 — 检查数据库、嵌入器、LLM 和图(如果启用)是否可达。如果某些内容无法正常工作,请先运行此命令。 |
</details>
`forget` 与 `delete`:**forget** 退休一个事实,但将其保留在历史中(可审计的“我们曾经使用 X”);**delete** 硬删除它以满足合规性。两者都很重要 — 一个用于正确性,一个用于隐私。
**安全注解:** 每个工具都带有 MCP 注解,因此客户端可以安全地自动运行读取(`recall`、`list_memories`、`history`,…)并在 **不可逆擦除前确认** (`delete`、`delete_all`、`purge_expired` 被标记为破坏性)。
**身份验证和 RBAC(适用于团队部署):** 每个管理员都使用 API 密钥(`vayl_sk_…`)进行身份验证;每个管理员都有一个角色(管理员/成员/代理/查看者/审计员),授予一组能力(读取/写入/删除/验证/管理员),并且每个工具都检查其所需的能力(关闭失败)。本地 `stdio` 使用被视为受信任的本地管理员;远程服务器从调用者的 Bearer 凭证中解析调用者 — 既可以是 **API 密钥** (`vayl_sk_…`),也可以是使用企业许可证的 **OIDC SSO** ID 令牌(来自 IdP 的 JWT,验证其 JWKS 并将声明映射到角色)。每个部署都是一个组织(隔离边界),具有 `tenant_id` 缝隙以适应未来的托管版本。
**租户范围 — 在多租户部署前请阅读此内容。** `user_id` 来自 *调用者*。角色回答“此密钥是否可以读取?”;它们 **不** 回答“属于谁的内存”。给予管理员 `scopes` 以限制其范围:
```python
create_principal("support-bot", role="agent", scopes="cust_5521,cust_7788")
```
没有范围的管理员是 **无限制的** — 适用于单租户,错误的多租户部署会持有多个客户的数据。管理员无论如何都是无限制的(组织控制意味着可以访问每个空间,包括删除它)。拒绝记录到审计日志中,并且故意不回显请求的 `user_id`,因此范围调用者无法从错误文本中列出空间。
**运行方式:**
```bash
vayl-mcp # stdio 传输 — 一个本地用户(Claude Desktop / Cursor / Claude Code)
vayl-server # 流式 HTTP 传输 — 共享的团队端点,需要身份验证
# pip install ".[server]" · VAYL_HOST=0.0.0.0 VAYL_PORT=8080 vayl-server
```
`vayl-server` 需要每个请求的 `Authorization: Bearer vayl_sk_…` (密钥无效/失效 → 401);`/healthz`、`/.readyz` 和 `/metrics` (Prometheus)对探针/爬虫开放。引导第一个管理员通过 stdio(`vayl-mcp` → `create_principal("you", role="admin")`),然后发放密钥。**在您的反向代理/入口处终止 TLS** — 不要直接暴露服务器。
**部署(自托管,Docker):**
```bash
docker compose up -d --build
# 引导第一个管理员(一次性)— 复制打印的密钥:
docker compose run --rm vayl python -c "from vayl.api import mcp_server as s; print(s.create_principal('admin', role='admin'))"
curl localhost:8080/healthz # 存活 · curl localhost:8080/metrics → Prometheus
```
容器以非 root 用户运行;SQLite 数据库 + 加密/签名密钥保留在 `vayl-data` 卷(`/data`)中。在 `docker-compose.yml` 中通过 env 配置 LLM 和(可选)许可证。
**密钥托管(KMS):** 默认情况下,加密/签名密钥是自动生成的文件,位于数据旁边(`VAYL_KMS=file`)。对于生产环境,设置 `VAYL_KMS=vault` (+ `VAULT_ADDR`、`VAULT_TOKEN`)以使用 **HashiCorp Vault Transit 信封加密** — 主密钥从不离开 Vault,只有包装的密钥块位于磁盘上,并在启动时解包到内存中。如果 Vault 不可访问,Vayl **失败关闭** (不会启动)而不是运行未加密。自我托管的 Vault 在您的基础设施上保留托管。
**存储后端:** 默认情况下为 SQLite(单个节点,无需设置)。对于共享/扩展部署,设置 `VAYL_DATABASE_URL=postgresql://…` (`pip install ".[postgres]"` 或 `docker compose --profile postgres up`),Vayl 改为在 **Postgres** 上运行 — 在 Postgres 17 上验证端到端(所有模块、加密、防篡改审计)。**多个 `vayl-server` 进程可以共享一个 Postgres**:相同空间的写入通过 `pg_advisory_xact_lock` 序列化(不同空间并行运行),因此扩展到更多进程/节点是安全的。(每个节点在一个进程内的吞吐量仍然是一个写入一次。)
**版本和许可:** 社区版(默认)免费、开源,限制在少数管理员。
企业许可证(签名、**离线验证**的 blob — 无许可证服务器,在断电情况下可用)提高了席位限制,并解锁了企业功能。设置 `VAYL_LICENSE=<blob 或路径>`。供应商使用 `mint_license.py` 铸造许可证(一次 `keygen`,然后为每个客户铸造 `mint`),并通过 `VAYL_VENDOR_PUBKEY` 嵌入其公钥。缺少/过期/篡改的许可证 **软降级为社区版** — 它永远不会使正在运行的部署瘫痪。您可以用 `license_status` 工具检查当前状态。
**范围:** 每个工具都采用可选的 `agent_id` / `run_id` — 每个 `(user_id, agent_id, run_id)` 组合都是其自己的隔离内存空间(留空则为单个空间)。`remember` 还采用可选的 `metadata` 来标记事实。
---
## 使用更好的模型(可选)
更大的模型更好地处理主观边缘(参见 [质量诚实](#质量诚实))。使用相同的配置,交换 `env`:
```jsonc
// OpenAI
"env": {"LLM_PROVIDER": "openai", "OPENAI_API_KEY": "sk-...", "OPENAI_MODEL": "gpt-5-mini"}
// Anthropic
"env": {"LLM_PROVIDER": "anthropic", "ANTHROPIC_API_KEY": "sk-ant-..."}
// Groq · Gemini · OpenRouter — 都有免费层
"env": {"LLM_PROVIDER": "groq", "GROQ_API_KEY": "gsk_...", "GROQ_MODEL": "llama-3.3-70b-versatile"}
"env": {"LLM_PROVIDER": "openai", "OPENAI_API_KEY": "AIza...",
"OPENAI_BASE_URL": "https://generativelanguage.googleapis.com/v1beta/openai", "OPENAI_MODEL": "gemini-2.0-flash"}
"env": {"LLM_PROVIDER": "openai", "OPENAI_API_KEY": "sk-or-...",
"OPENAI_BASE_URL": "https://openrouter.ai/api/v1", "OPENAI_MODEL": "meta-llama/llama-3.3-70b-instruct:free"}
```
---
**正在构建某些内容?** 请参阅 [文档站点](https://vayl.gitbook.io/vayl-docs) — 已有实例和教程(编码助手、支持/CRM、个人助理、多代理、电子商务、DevOps、医疗保健、辅导…)以及每个实例的确切范围和工具调用。
## 为什么选择 Vayl?
两种常见的代理内存类型都留下了相同的空白,Vayl 弥补了这一空白:
- **累加内存** 将每个事实相加,并通过相似性检索 — 因此更改的值仍可能排名靠前,而模型则需要猜测哪一个是当前的。
- **时间图内存** 协调得很好,但很重量级(图数据库和每个事实的许多 LLM 调用),而大多数设计 **无法建模删除** — “我们放弃了 X”使 X 仍然有效。
Vayl 在累加存储成本的情况下协调,不使用图数据库,并将 **删除视为一等公民**:新值取代旧值,撤销实际上删除,历史记录保留。
**关于范围的诚实:** 在一个简单的矛盾之处(“切换到 X → Y”),一个强大的模型在大多数设计中都能很好地协调 — Vayl 在准确性方面并不超越同类。它的优势在于 **正确的遗忘、低成本、按构造的当前真相以及免费和本地运行**。
## 基准测试
所有数字均可在 `benchmarks/` 中复现 — 自己运行脚本。这些是 Vayl 在此存储库中的测试套件的自身结果(单次运行,作者编写 — 强复现信号,非第三方审计)。
| 指标 | Vayl | 含义 |
|--------|------|---------------|
| **撤回** ("we dropped X"),gpt-4o,10 个案例 | **10 / 10** 已移除 | 移除是首要的,不是重新排序的事实 |
| **静默错误** (自信地返回虚假的当前值)— 255 次对抗性试验,Haiku | **0.4%** | 标记/降级而不是猜测 |
| **杂乱的真实输入** — 30 个案例,12 个领域 × 14 种噪声类型,gpt-4o | **0%** 静默错误 | 拼写错误,俚语,表情符号,多事实消息 |
| **每个事实的成本** | **~2 LLM 调用,无图数据库** | 有界的 top-k 检索,无全图扫描 |
```bash
python benchmarks/evaluations/eval_reconcile.py # 完整的 LLM 流水线与标记集对比 → 静默错误率
python benchmarks/evaluations/eval_adversarial.py # + rep_eval.py 用于 5 次重复平均值
python benchmarks/evaluations/messy_eval.py # 杂乱的真实用户套件
python benchmarks/stress/stress_test.py # 持久性和并发性(离线)
```
`eval_reconcile.py` 是您部署模型时要运行的脚本 — 它会锻炼真正的提取器 + 调和器 + 召回并报告 **静默错误率** (信任指标)。请先设置您的 LLM 环境(参见文件头)。对于 gpt-5-mini 的生产检查,只需一条命令:
```bash
export OPENAI_API_KEY=sk-...
bash benchmarks/scripts/run_eval_gpt4omini.sh # 通过 OpenAI 进行 LLM + 嵌入;无需运行其他内容
```
### 质量(诚实)
`benchmarks/` 中测试套件的静默错误率 — 信任指标:
| 模型 | 杂乱的现实世界(30 个案例) | 对抗性 |
|-------|-----------------------------|-------------|
| **gpt-5-mini** (默认) | **0%** | — |
| gpt-4o-mini | **0%** | — |
| gpt-4o / Claude Haiku | ~0% | **0.4%** (255 次试验) |
调和质量不再像以前那样与模型强度直接相关:**相同槽位不变性** 允许多多一个活动值用于每个 `(subject, scope)` ,因此即使是更便宜的模型也不能留下两个矛盾的值 — gpt-4o-mini 在杂乱的套件上达到 0%,原因就是如此。这些是单次运行、作者编写的测试套件 — 强复现信号,非第三方审计。
---
## 工作原理
每条消息通过一次 LLM 调用转换为事实(或多个事实),然后与存储的内容进行调和:新值 **取代** 旧值,移除 **撤销**,模糊的值被 **标记** 而不是猜测,取代/撤销的事实保留在历史中。状态保存在 SQLite 中(每个 `user_id`)。`recall` 使用 **混合检索** — 它融合了语义(嵌入)和词汇(关键词)排名,以便在嵌入排名较低时,精确术语匹配仍然可以显示出来 — 并且只拉取前 k 个相关事实,因此 LLM 上下文保持小而快速 **无论内存增长多大** (并且如果嵌入不可用,召回仍然有效)。
```mermaid
flowchart TD
A["Agent 消息<br/>'we moved off Redux to Zustand'"] -->|1 LLM 调用| B[提取事实(s)]
B --> C{"与存储的调和<br/>(相同槽位不变性)"}
C -->|新值| D[SUPERSEDE<br/>旧 → 历史]
C -->|移除| E[RETRACT<br/>墓碑 → 历史]
C -->|模糊| F[FLAG<br/>等待人工]
C -->|已知| G[DEDUP]
D --> H[("SQLite / Postgres<br/>事件源语句")]
E --> H
F --> H
G --> H
H --> I[["审计哈希链<br/>签名 · 防篡改"]]
Q["recall(问题)"] --> R[混合检索<br/>语义 + 词汇]
H -. 活动工作集 .-> R
R --> S[前 k 个相关事实]
S -->|1 LLM 调用| T["回答 'Zustand'<br/>或 '我不知道'"]
```
写入路径(顶部)调和并记录;读取路径(底部)仅拉取活动的 top-k,因此已退休的值不能作为当前值返回,并且上下文保持有界。所有不涉及 LLM 调用的部分都是确定性的本地代码 — 这就是为什么信任属性是结构性的,而不是依赖于提示的原因。
## 可选:大规模图查询
对于深度关系问题(“谁拥有 Bob 工作的公司?”):
```bash
pip install ".[graph]" # + 运行 Neo4j
```
然后在服务器 `env` 中启用它:`"VAYL_GRAPH": "1"` (可选 `NEO4J_URI` / `NEO4J_USER` / `NEO4J_PASSWORD`)。事实镜像到 Neo4j 为实体图;每个边在写入时嵌入并由本机 **Neo4j 矢量索引** 排名,因此 `recall_related` 工具在 **高学位中心** 保持 **50–230 毫秒** — 无全图扫描。如果未启用图(或 Neo4j 关闭),`recall_related` 优雅地回退到槽召回。边按 `(user_id, agent_id, run_id)` 命名,因此 `delete` / `delete_all` **也清除图** — 抹除保持完整。(图 *检索* 仍在 MVP 中单租户;每用户查询隔离是一个托管阶段的项目。)
---
## 扩展和历史
Vayl 是事件源 — 取代 / 撤销 / 更新 **保留** 旧行(这就是审计轨迹),因此历史随时间增长。为了保持长期、历史繁重的用户快速,热路径与历史分离:
- **`load` 读取仅活动工作集** (ACTIVE + FLAGGED),从不读取完整历史 → 成本为 **O(活动事实)** ,而非 O(总历史)。活动保持小(数十 - 数百)无论历史累积多少。
- **`save` 是增量的** — 插入新行,更新少量更改的行 — **从不重写**。
- **向量保留在磁盘上,直到需要排名时。** `load` 仅获取事实是否有嵌入;向量本身在排名时被加载,并且在召回上限以下的空间中从不加载。存储为二进制 float32 而不是 JSON(~6 KB vs ~15 KB 每 1536 维向量)。
- **查询嵌入被缓存** (FIFO,`VAYL_QUERY_CACHE`,默认 512)。代理循环不断询问相同的问题,每个问题否则都是网络往返。
历史存储在磁盘上并延迟读取。`recall(include_history = True)` 按需加载历史;关闭时 **从不加载** ,这使得“已取代的值不能作为当前值返回”成为结构属性,而不是要求模型尊重的东西。
## 到达过去
`recall` 默认从 **当前** 事实中回答。已退休的事实不仅被过滤出答案 — 它们从不被加载,因此已取代的值不能作为当前值返回,无论模型如何表现。
对于关于过去的问题,请选择:
```python
recall("我们之前使用什么监控?", include_history=True)
```
已退休的事实然后会带有 `(历史)` 标签与当前值一起出现。 当您想要一个槽的完整时间线而不是答案时,请使用 `history(subject)`。
## 关键事实:绕过排名的类别
召回是语义 top-k,这是有概率的。对于大多数记忆,排名低的事实是质量问题。对于某些事实,这是安全问题 — 未被召回的过敏不是排名低的,它是 **不可见的** ,而答案自信且不完整。安全门无法捕获这一点; `safe_recall` 和 `check_before_act` 仅判断他们所接收的事实,因此检索丢失对他们来说也是不可见的。
类别为关键的事实会完全跳过排名:
```bash
VAYL_CRITICAL_CATEGORIES=过敏,活跃药物
```
```python
remember("患者对青霉素过敏", metadata={"category": "过敏"})
recall("总结图表", critical_categories="过敏") # 每次调用覆盖
```
如果关键集超过上下文预算(`VAYL_CRITICAL_BUDGET`,默认 200),则读取 **引发** 而不是截断 — 静默丢弃始终包含集的尾部将重新创建该机制旨在防止的精确丢失。除非配置,否则不启用。
## 声明的槽(可选)
Vayl 通常允许提取器命名自己的槽,这适用于开放式内存。成本以真实的对话数据衡量:**在 638 个事实中,有 626 个不同的主题** — 大约 2% 的重用。由于相同槽不变性通过确切的主题相等性解决竞争,因此描述性每个语句命名意味着关于某事物的两个陈述并排存在,未调和。
对于具有已知字段的领域,请声明它们(`VAYL_SLOT_SCHEMA=/path/slots.json`):
```json
{"slots": [
{"name": "active_medication", "category": "critical", "verbatim": true, "confirm": true,
"description": "患者当前服用的药物,包括剂量和频率",
"aliases": ["药物", "当前药物", "处方药物"]}
]}
```
| 属性 | 效果 |
|---|---|
| `aliases` | 每个列出的拼写都折叠到规范名称上,因此后续语句落在同一个槽中并调和。匹配是 **确定性的** — 仅限大小写和分隔符。`penicillin_reaction` 不匹配 `allergy` ;推断两个不同命名的槽是“相同”的就是一个调和存储静默销毁一个事实的方式。 |
| `category` | 标记槽中的每个事实,提供上述关键事实通道。一次声明而不是每次写入。 |
| `verbatim` | 值按原样存储。规范化设计上是有损的(`"500mg twice daily"` → `"twice_daily"` 丢失剂量)— 对于 `favourite_colour` 来说很好,但对于处方来说是不可接受的。 |
| `confirm` | 参见下文。 |
格式错误或缺失的模式 **引发** 而不是回退到空:开放失败意味着如果未规范关键槽,关键槽被规范化。默认情况下为空 — 声明无内容,行为不变。完整示例: [`examples/clinical-slots.json`](examples/clinical-slots.json)。
## 高风险写入的人工审批
调和由阅读对话文本的 LLM 驱动。对于大多数槽,错误写入是可恢复的 — 它再次被取代,历史显示发生了什么。对于某些槽,**写入本身就是危险的** :“我们可能应该停止华法林”出现在句子中并不是停止的命令。
声明为“`confirm`:true”的槽不应用替换或删除。它记录一个 **提议** ,保留当前值并等待:
```
pending_changes() → #42 REMOVE active_medication: '华法林 5mg 每日' → …
表示:“停止华法林”
confirm_change(42, decided_by="dr_smith") → 应用它
reject_change(42, decided_by="dr_smith") → 丢弃它;当前值不变
```
对 gated 槽的第一次写入不会被阻止(此时无损失)。提案一旦其将要取代的值已更改就无法确认 — 该决定是在不再成立的状态下做出的。被拒绝的提案作为历史记录保留:有人提出停止药物的事实本身值得审计。而待处理提案被标记为 **FLAGGED** 而不是 ACTIVE,因此它永远不会在召回中作为当前值出现。
## 事件与状态
事实带有 `kind` 。 **状态** 持续到有东西取代它(我们使用 Postgres)并保持相同的槽不变性 — 每个槽恰好有一个活动值,这使得“哪一个是当前值?”这个问题可以回答。 **事件** 发生在某个时间点(`ran a charity race` , `the customer called`)并且是豁免的:两个比赛是两个比赛,而不是更正,因此它们共存并且永远不会相互取代。
豁免在引擎中强制执行,而不是信任提取器 — 标记为 `SUPERSEDE` 的事件被降级,因此混乱的模型或后来的状态事实共享主题都无法删除已发生事件的记录。未标记的事实默认为状态。
## 状态
**MVP → 硬化** 。本地单节点服务器;内存默认保存在 SQLite 中,或使用 Postgres 进行多写入器扩展。最近的工作使单个进程真正并发:旧的进程全局锁被 **移除** 以支持 **每个空间的锁定 + 线程本地连接 + 每个空间的 ID 分配** ,因此不同内存空间的操作现在可以并行运行,而相同空间的写入仍然序列化,篡改证据的审计链保持完整(它序列化自己的追加)。通过 **475 个离线单元测试** ,并发性/延迟负载套件和完整性测试下的 **无损坏** 验证 — 即使在 **自由线程 Python 3.14t** (禁用 GIL)下,移除锁最终让额外线程增加吞吐量。热路径分离保持每个操作的成本与活动集绑定,历史增长;持久性验证到 20k 个事实。托管,多租户,计费构建是下一个阶段。深入了解: [`benchmarks/load/FINDINGS.md`](benchmarks/load/FINDINGS.md) (并发性), [`research/reports/memory-reconciliation-report.md`](research/reports/memory-reconciliation-report.md) (设计)。
## 贡献
欢迎贡献 — 错误报告,文档,测试,新的 LLM 提供商和调和边缘情况等。Vayl 故意保持小(两个核心依赖),其单元测试完全离线,因此开发循环快速且无需 API 密钥。
**设置**
```bash
git clone https://github.com/vayl-dev/vayl && cd vayl
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,server,postgres]" # 可编辑安装 + 开发和可选额外功能
```
**运行 CI 运行的检查** (离线,确定性 — 无 LLM,无网络):
```bash
pytest # 475 个单元测试
ruff check . # 静态代码检查
```
**自由线程 Python(可选)** 。Vayl 在自由线程的 CPython 3.14t 上验证(禁用 GIL) — 并发工作在此实际发挥作用。要体验它:
```bash
python3.14t -m venv .venv-ft && source .venv-ft/bin/activate
pip install -e ".[dev,postgres]"
python -c "import sys; print('GIL enabled:', sys._is_gil_enabled())" # → False
pytest tests/test_db.py tests/test_accountability.py
```
**负载和完整性基准** (离线,确定性 — 存根嵌入):
```bash
python -m benchmarks.load.concurrency # 锁定策略:吞吐量 + 尾部延迟
python -m benchmarks.load.integrity # 真实的写入路径;断言无损坏,退出时返回非零
```
参见 [`benchmarks/load/FINDINGS.md`](benchmarks/load/FINDINGS.md) 以了解它们衡量的是什么以及结果。
**项目布局**
```
src/vayl/
memory/ 调和引擎 — 取代 / 撤销 / 标记 / 去重,相同槽不变性
storage/ SQLite + Postgres 后端,每个空间的锁定,事件源语句
security/ 静止加密,Ed25519 签名,篡改证据审计哈希链
auth/ 主体,角色/能力,API 密钥,OIDC SSO
licensing/ 离线验证的企业许可证,签名收据
clinical/ FHIR 导入,声明的槽,退药调和
telemetry/ 设备上的指标
api/ MCP 服务器(mcp_server),HTTP 服务器(server)
tests/ 离线单元测试 — CI 运行
benchmarks/ LLM 评估 + 负载/完整性测试 — 手动运行
```
**指南**
- 保持 **核心依赖为两个** (`mcp` , `cryptography` );任何较重的依赖都放在可选的额外功能后面。
- 单元测试保持 **离线和确定性** — 依赖 LLM 的检查属于 `benchmarks/` 。
- 新行为需要测试,并且必须通过 `ruff check .` 。
- 审计哈希链是安全保证: `security/audit.py` 下的更改需要并发测试(参见 `tests/test_accountability.py` )。
- 对于任何重要的内容,请先开启一个问题,以便我们在进行大规模 PR 之前统一意见。
通过贡献,您同意您的贡献根据 Apache-2.0 许可。
## 安全
本地/本地使用的默认安全设置:**加密静止数据,默认加密并关闭** — 如果加密(或审计签名)开启但不可用,Vayl 拒绝启动而不是静默运行明文;只有明确的 `VAYL_ENCRYPT=off` / `VAYL_SIGN=off` 关闭它们。加密范围涵盖可能包含个人数据的所有内容:语句内容 + 嵌入,审计详细信息,**决策快照,收据有效载体,工具错误消息和主体名称** (标识符和结构保持明文以便查询;自动密钥,`VAYL_KEY` 密码短语,或 `VAYL_KMS=vault`)。 **无遥测 — 唯一的出站调用是您配置的 LLM/嵌入端点** (该选择,而不是 Vayl,设置您的数据驻地姿态:将其指向自托管或欧盟区域端点,如果您需要一个)。 **CSRF 加强,参数化 SQL**。威胁模型,共享责任矩阵和确切的加密范围: [`SECURITY.md`](SECURITY.md)。欧盟部署(GDPR + AI 法案映射,控制器责任,DPO 检查清单): [`COMPLIANCE.md`](COMPLIANCE.md)。未经第三方审计,不保证合规性 — 运行您自己的审查,DPO 签署和渗透测试,然后再上线。
**高风险部署** (临床,金融,法律) — 配置和诚实的 Vayl 功能清单:请参阅 [文档网站](https://vayl.gitbook.io/vayl-docs)。
Apache-2.0 许可。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器