mcpField
F
factstore

factstore

本地服务
github.com记忆与知识04 周前更新

关于 factstore

factstore 是基于 Postgres 的事实存储,面向需要从公司系统和文档中记录可追溯信息的 AI Agent、开发者及团队。属性需预先注册,写入事实时关联证据,并保留不可变历史,以查询特定日期已知的信息。

主要功能

  1. 01注册并限定类型的属性
  2. 02关联证据的事实事务
  3. 03支持指定时点读取的不可变历史
  4. 04MCP 服务与 Python SDK
  5. 05用于目录编制和文档摄取的技能

适用场景

  1. 01将发票和邮件摄取为事实
  2. 02为业务系统导出数据编制目录
  3. 03按日期查询事实及其历史

MCP 相关

MCP 服务通过 stdio 将 factstore 操作作为工具提供,并从环境中读取凭据。MCP 客户端可通过它访问事实;只有提供单独凭据时才会开放删除工具。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"victor-eu-factstore": {
"command": "uvx",
"args": [
"factstore"
],
"env": {
"FACTSTORE_DSN": "<FACTSTORE_DSN>",
"FACTSTORE_EXCISE_DSN": "<FACTSTORE_EXCISE_DSN>"
}
}
}
}
启动命令uvx factstore
需要配置的环境变量
FACTSTORE_DSN密钥The credential `factstore actor STORE NAME` prints: a Postgres connection string for one actor of one store.
FACTSTORE_EXCISE_DSN密钥An excision credential (`factstore actor --excise`). Only then is the excise tool listed.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypifactstore0.1.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

factstore

一个构建在 Postgres 上、供 AI Agent 使用的事实存储。Agent 将公司系统和文档中的内容记录为事实:针对已注册属性的一个值,记录在一笔事务中,并标注记录者和来源文档。内容不会被覆盖,因此存储可以说明自己在任意日期知道什么。Agent 通过 MCP 服务使用它,脚本则通过 Python SDK 使用它。

它还提供技能,即 Agent 遵循的操作流程:一个从公司的系统导出数据中建立索引,另一个将文档和邮件读取为事实。设计文档说明了这样构建的原因,评测则说明了测试内容。采用 MIT 许可证。

安装

你需要 Postgres 16 或更高版本、Python 3.12 或更高版本,以及用于运行 uvx 的 uv。

1. Postgres。 使用你自己的实例,或启动一个实例。factstore init 需要超级用户权限。

docker run -d --name factstore-pg -e POSTGRES_PASSWORD=postgres -p 5432:5432 --shm-size=1g postgres:17

2. 创建存储,并为 Agent 准备凭据。

pip install factstore
export FACTSTORE_ADMIN_DSN=postgresql://postgres:postgres@localhost:5432/postgres
factstore init acme                        # a store, with factstore-core
factstore install acme factstore-skills    # the vocabulary the skills use
factstore actor acme "ingest agent"        # prints the actor's ID and its credential

3. 连接 Agent。 在 Claude Code 中,插件会提供 MCP 服务和技能,并提示你输入凭据:

/plugin marketplace add Victor-EU/factstore
/plugin install factstore@factstore

其他 MCP 客户端则在其环境变量中设置凭据并运行服务:

{"mcpServers": {"factstore": {"command": "uvx", "args": ["factstore", "mcp"],
                              "env": {"FACTSTORE_DSN": "<credential>"}}}}

factstore skills DIR 会将技能复制到 Agent 的技能目录,例如 .claude/skills。目录和导入技能也会通过 SDK 批量写入数据,因此 Agent 的 shell 环境需要运行 pip install factstore。

4. 提出请求。 “将 ./invoices 中的发票读入存储”会使用 factstore-ingest。“为我们的 Shopify 和 QuickBooks 导出数据建立索引”会使用 factstore-catalogue。“存储中有哪些类型的事物?”会使用 factstore-ontology。

此版本包含的内容

factstore install STORE NAME 会按名称安装软件包及其依赖项。

软件包内容
factstore-core证据、文档、置信度、货币、合并和领域时间。由 init 安装。
factstore-skills目录、知识本体和导入技能,以及知识本体技能用于命名形状的词汇表。
factstore-ecom-ops在中国生产并在线销售的品牌的供应侧内容:供应商、SKU、采购订单、检验、货运和海关申报。其技能会读取供应商的 PDF、聊天记录和运输邮件。
factstore-ecom-indexShopify、Amazon、3PL 和 QuickBooks 所有内容的 ID 和关联键,供目录技能使用。

个人数据。 服务的规则会将人名、地址和电话号码排除在存储之外:这些信息会保留在文档中。若企业希望某个属性能够存储这些信息,则需通过 core/personal 允许。

开发

git clone https://github.com/Victor-EU/factstore && cd factstore
docker compose up -d                      # Postgres 17 on port 54329
cd factstore
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest

每项测试都会创建一个存储,并在测试结束后将其删除。从源码检出目录运行时,factstore install 也可以接受软件包目录作为参数,例如 ../packages/ecom-ops。packages/介绍了软件包格式和各个软件包。新版本的软件包会注册其新增内容。它还可以将某个属性设为多值或身份属性,这是内核允许的两种变更。factstore-skills/存放各项技能。

import factstore

store = factstore.connect("<credential from above>")
store.register_attribute({"ident": "shopify/order_id", "type": "string", "cardinality": "one",
                          "unique": "identity", "doc": "Shopify's ID for an order."})
store.transact([{"e": ["shopify/order_id", "1234"], "a": "shopify/order_id", "v": "1234"}])
store.query('select e, v from "shopify/order_id"').rows

设置 FACTSTORE_DSN 后,也可以在模块级别进行相同调用:factstore.transact([...])、factstore.query(...)。

MCP 服务

factstore mcp 或 factstore-mcp 会通过 stdio 将调用作为工具提供。凭据来自其环境变量(FACTSTORE_DSN),绝不会来自工具调用。只有当 FACTSTORE_EXCISE_DSN 中也设置了删除凭据时,才会列出 excise。tools.py 中的工具描述就是模型读取的文档。

目录结构

文件内容
schema.sql表、角色,以及用于在 Postgres 中强制执行不变量的触发器
kernel.py写入锁、模式加载、分配,以及将变更应用到日志和派生表
store.py调用:transact、query、stats、register_attribute、search_attributes、excise
read.py安全执行一次读取:只读、回滚、单条语句、超时、行数上限
stats.py属性用法、签名和引用连接情况
server.pyMCP 服务
admin.py、cli.py创建存储、参与者和凭据;安装软件包
packages.py软件包清单、某个版本携带的软件包,以及安装顺序
tools.pyMCP 工具描述和输入架构:面向模型的文档
fs.py内核自身的 fs/ 属性
tests/reference.py参考读取器:对日志进行朴素折叠,用作测试判定依据

如何强制执行不变量

这些不变量由 Postgres 强制执行,而不只是由 Python 代码实现:

  • 不可变性。 写入者对 fact 表拥有 INSERT 和 SELECT 权限,除此之外没有其他权限。触发器会拒绝 UPDATE、TRUNCATE,以及任何不在 fs_excise() 中执行的 DELETE,即使操作者是所有者也不例外。
  • 由内核标明写入者。 凭据是 Postgres 登录。触发器会根据 session_user 设置每个事务的参与者,并从时钟获取事务时间,覆盖客户端发送的任何值。只有内核自身的函数可以写入 fs/actor、fs/at 和 fs/excised_* 事实。
  • 已注册且类型正确的属性。 每个事实都必须指定一个已注册属性(由外键强制执行),并且触发器会检查其值是否符合该属性的类型。另一个触发器会阻止属性向错误方向变更(更改类型、从多值改为单值、移除唯一性)。
  • 提交顺序。 每次写入都会为每个存储获取一个咨询锁,并在锁内分配事务 ID。触发器会再次获取同一个锁,并拒绝任何不晚于最新 ID 的 ID。

读取

query 接受一条针对视图的 SQL 语句(设计中的开放问题 1,由探索性验证决定):

  • 每个属性都是一个以属性命名的视图:模式 current 中的 "po/status"(e, v, tx),以及涵盖整个日志的 history."po/status"(e, v, tx, op)。
  • 注册时会通过触发器创建视图。
  • 执行某个时间点的读取时,会设置 factstore.as_of,所有视图都会遵循该设置。

由于 SQL 由模型编写,read.py 会通过单独的连接运行它:

  • 在始终会回滚的 READ ONLY 事务中运行,从而撤销语句所做的任何更改,包括 SET ROLE;
  • 使用预备语句,因此 Postgres 会拒绝第二条语句;
  • 设置 10 s 超时和 1,000 行上限。

咨询锁不会随回滚而释放,因此每次读取后都会释放锁;查询绝不会持有写入锁。

stats 会按签名对实体分组,即每个实体所携带属性的精确集合,并统计签名之间的引用数量。这种共现形式正是本体技能所需的:一种事物通常对应一个签名,或几个仅在可选属性上有所差异的签名。

构建过程中的决策

  • 在写入时强制执行基数为 1。 断言新值时,会将旧值的显式撤回写入日志。无论某个属性当时的基数是多少,当前状态都是对断言和撤回进行简单折叠得到的。
  • 丢弃冗余写入。 断言实体已有的值,或撤回实体没有的值,都会报告为未更改,且不会记入日志。不产生任何更改的调用不会写入事务,因此重新运行数据导入不会改动日志。
  • 查找仅通过断言创建实体。 查找没有结果时,如果调用断言了某些内容,就会创建该实体。如果查找仅用于撤回,则调用会报错。
  • 字符串相等性使用哈希索引。 字符串值上的 btree 会索引其前 200 个字符,因此长值不会导致插入失败。相等性判断通过针对整个值的哈希索引完成,任何视图上的 v = 'x' 都可以使用该索引。
  • 内核测试在裸存储上运行。 init 会安装 factstore-core,但内核测试会传入 core=False:内核不认识任何名称,因此其测试不应依赖 core 中的名称。这些包各自有自己的测试。
  • 近似匹配阈值。 这些阈值是基于样本属性进行的初步校准,设定在 store.py 顶部;tests/test_register.py 记录了用于验证的案例。M5 中的“已注册属性”指标用于调整这些阈值。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。