关于 Coalent
Coalent 为 AI Agent 和检索增强应用提供上下文复用与新鲜度管理。它跨查询缓存基于来源的事实,并在来源变化时仅使相关缓存单元失效;可叠加在现有检索器之上。
主要功能
- 01跨查询语义缓存
- 02基于来源的原子事实
- 03来源变更时定向失效
- 04跨单元证据召回
- 05读取失败后修复缓存
- 06可接入自有检索器和模型
适用场景
- 01在 Agent 查询间复用来源事实
- 02文档修改后更新缓存答案
- 03回答涉及多份文档的问题
- 04读取失败后修复上下文
MCP 相关
coalent-mcp 服务通过七种工具向 MCP 客户端提供缓存上下文与新鲜度操作。支持 stdio 和 HTTP 模式,也可让多个客户端共享一个缓存。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx coalentOPENAI_API_KEY密钥Required in folder (--watch) mode for semantic embeddings (text-embedding-3-small) and build synthesis (gpt-4o-mini); the server exits loudly without it rather than degrade. Factory (--cache-factory) mode brings its own models and needs no key.COALENT_MCP_TOKEN密钥Optional shared secret for --transport http: when set, every HTTP request must carry Authorization: Bearer <token>. Unused over stdio.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
为 AI Agent 和 RAG 提供实时、来源失效时同步失效的上下文。
只需构建一次理解,即可随处复用,并自动保持最新。
📖 文档 · coalent.ai · 💬 Discord
快速入门 · v0.7 新功能 · 门控阶梯 · 使用自己的技术栈 · MCP · LangChain · 基准测试 · 命令行
你的 Agent 每次调用都会重新读取相同的来源;一旦来源发生变化,所有缓存的答案就会悄无声息地出错。
Coalent 只构建一次理解,根据查询的含义进行缓存,并在底层来源发生变化时精准失效。正确性不输于重新读取所有内容,成本却低得多,而且永不过时。
为什么选择 Coalent
每个上下文层都不得不在三件事之间取舍。Coalent 旨在同时兼顾这三点:
- 🧠 提取式理解,而非分块。 它会缓存由你的大模型提取的、与查询无关且基于来源的原子声明,保留每个数字和事实,让一个缓存单元能够回答之后许多不同的问题。每个单元都会保留原始证据,因此如果命中内容无法充分覆盖查询,就会回退到检索,而不是给出依据不足的回答。
- ♻️ 在查询、Agent 和文档之间复用。 语义缓存以查询的含义为键:再次提问,或由另一个 Agent 提问,都能命中热缓存。跨单元召回会汇集多个单元中的声明,以回答证据分散在不同文档中的多跳问题,无需额外调用大模型。
- 🌿 来源可溯,内容常新。 每个单元都会记住它使用的确切来源。某个来源变更时,只有实际用到它的单元会变为过时,精准、自动且延迟更新。
Coalent 位于检索之上——可接入任意检索器(向量数据库、混合搜索、GraphRAG、工具、API)。它是新鲜度与复用层,而不是又一个检索器——刻意与 GraphRAG 预先构建整张图的开销相反:它轻量、单元彼此独立,仅在查询实际需要时延迟构建,并通过标记单个单元为过时来刷新(无需改造整张图)。
v0.7 新增——自愈版本。失败链路如下:读取失败时, 你的 Agent 会调用
repair(read_id),缓存会重新提取构建时遗漏的内容—— 并永久修复。在同一套冻结的 605 道题测试装置上测得:0.826,相比 v0.6 最强配置的 0.774,在相同的约 983 个 token 服务预算下提升 5.3 个百分点, 最终拒答减少 69%。此外,默认读取路径切换为 pool:使用语义嵌入器时,读取路径会自动设为"pool"(read_path="unit"仍是字节级不变的备用选项)。参见 v0.7 新增内容。v0.6.1 新增——MCP 服务:
coalent-mcp让你只需一行配置,即可将缓存接入 Claude Code、Cursor 或任何 MCP 客户端(在 Claude Code / Cursor 中使用);而langchain-coalent则让你现有的 LangChain 技术栈成为缓存的底层支撑。两者都只增加功能,不做破坏性更改。v0.6——pool 读取路径(
read_path="pool"):每次读取都会提供按 token 预算打包、全局排序的新鲜声明池。在同一基准测试中测得:981 个上下文 token 时准确率为 0.731——比朴素方案测得的最高准确率(top-12:1,729 个 token 时为 0.731)少约 43% 的 token。此外,还有默认关闭的行为层,在同一数据存储上测得拒答减少 33%、准确率提升 3.1 个百分点。
安装
pip install coalent # the core has zero required dependencies
快速开始
可直接运行——StubSynthesizer 无需 API 密钥,让你十秒内体验完整流程:
from coalent import SemanticCache, InMemoryRetriever, StubSynthesizer
# 1. Any retriever — a vector DB, a tool, an API. (In-memory here for the demo.)
retriever = InMemoryRetriever()
retriever.add("confluence:hr", "Leave policy: 21 days of annual leave per year.")
# 2. Build the cache. Swap StubSynthesizer for a real LLM below.
cache = SemanticCache(retriever, StubSynthesizer())
# 3. Ask. The first call builds understanding and caches it; the next is a warm hit.
result = cache.get("what is our leave policy?")
print(result.context["understanding"])
print(result.cache_hit) # False (cold) -> True on the next call
# 4. A source changed? Only the units that used it go stale — surgically.
cache.source_changed("confluence:hr", text="Leave policy: now 25 days.")
# the next matching read rebuilds just that one unit, lazily
(上面的免密钥演示运行的是经典的 unit 读取路径——由于没有可用的语义嵌入器,缓存会明确提示这一点并回退。接入真正的嵌入器,如下所示,默认路径就会变为经测试验证的 pool 读取路径。)
接入真正的模型——任何文本输入、文本输出的大模型都可以。在 v0.4 中,合成器默认构建提取式理解(与查询无关、保留每项事实的原子声明),缓存也会自动启用跨单元召回:
from coalent import SemanticCache, LLMSynthesizer, OpenAIProvider, OpenAIEmbedder
cache = SemanticCache(
retriever,
LLMSynthesizer(OpenAIProvider(), model="gpt-4o-mini"), # extract=True by default (v0.4)
embedder=OpenAIEmbedder(), # match queries by MEANING (recommended for real use)
)
# ^ with a semantic embedder, v0.7 resolves the default read path to "pool" — the
# measured path. Pass read_path="unit" for the pre-0.7 unit default, byte-identical.
# Multi-hop across documents? recall is already on; raise its trigger to bridge units:
# SemanticCache(retriever, synth, embedder=..., recall_threshold=0.7)
pool 读取路径——自 v0.7 起的默认路径(只要有可用的语义嵌入器):每次读取都会提供按预算打包、全局排序的新鲜声明池,而不是一个路由到的单元。只需接入归属信息:提供一个 3 行的 pool_header 可调用对象,根据你自己的语料元数据,将每个单元映射为 [title | source | date]。这是经测试验证的最佳路径——在一个包含 605 道新闻问题的基准测试(严格评分)中,使用内置基础标题时准确率为 0.68,使用此可调用对象时为 0.73;两者使用相同的数据存储和查询:
DOC_META = { # your corpus metadata, keyed by artifact id
"docs:azure-refresh": {"title": "Azure region refresh", "source": "CloudWire", "date": "2026-05-02"},
}
def pool_header(unit) -> str: # the [title | source | date] golden path — 3 lines
meta = DOC_META.get(unit.evidence[0].artifact_id if unit.evidence else "")
return f"[{meta['title']} | {meta['source']} | {meta['date']}]" if meta else f"[source: {unit.id}]"
cache = SemanticCache(retriever, synthesizer, embedder=OpenAIEmbedder(),
pool_header=pool_header) # pool is the resolved default (v0.7)
result = cache.get("which regions got the refresh?")
result.context["pool"] # the packed, attributed claim payload — hand it to your answer model
可运行的免 API 密钥演示(包括拒答循环):examples/pool_read_path.py。
在 Claude Code / Cursor 中使用(MCP)
coalent-mcp 可向任何 MCP 客户端提供来自 Coalent 缓存的新鲜、带来源归属的信息 ——
源内容一有变化,相关信息就会立即失效。只需一行,即可将其接入 Claude Code:
pip install "coalent[mcp,openai]"
claude mcp add coalent -- coalent-mcp --cache-factory my_cache:build
(对于 Cursor / Claude Desktop / 任何 MCP 客户端:在其 MCP 配置中注册相同的 coalent-mcp ... 命令。)
使用自己的缓存(--cache-factory module:function)——主要模式。 工厂会返回一个完整构建好的 SemanticCache:你的向量数据库、嵌入模型、大模型,以及所有参数。服务端只负责添加协议连接层——经测量,这层带来的质量损失为零:工厂模式逐字节复现了该库自身的基准测试结果(基于从我们的 n=605 新闻基准中抽取的 100 个问题进行验证,得分为 0.710;置信区间完全一致,100/100 次提供服务,98/100 个答案负载与库的运行结果逐字节一致)。
# my_cache.py — importable from the directory you launch in
from coalent import (SemanticCache, LLMSynthesizer, OpenAIProvider,
OpenAIEmbedder, SQLiteCognitionStore)
def build() -> SemanticCache:
return SemanticCache(
my_vector_retriever, # YOUR vector DB / retriever
LLMSynthesizer(OpenAIProvider()), # YOUR synthesis model
embedder=OpenAIEmbedder(), # YOUR embedder
read_path="pool",
residual_spans=True, query_keys=True, # the behavioral stack, opt-in as ever
pool_header=my_metadata_header, # [title | source | date] — the measured golden path
store=SQLiteCognitionStore("kb.db"), # persistence is yours too
)
这里的新鲜度由信号驱动:文档发生变化时,你的摄取流水线会调用 source_changed 工具,受影响的信息会立即失效。(在工厂模式下同时添加 --watch DIR,也能在文件编辑时触发该工具——但只负责失效;它不会将内容摄取到你的索引中,且只有当你的 artifact ID 与相对于监视目录的路径相同时才会匹配。)
零配置文件夹模式(--watch DIR)——演示入口。 指定一个文档文件夹,即可获得推荐的 v0.6 部署方案(池化路径、残留片段、查询键、SQLite 持久化、自动添加 [path | modified date] 来源归属),完全无需编写代码:
claude mcp add coalent --env OPENAI_API_KEY=$OPENAI_API_KEY -- coalent-mcp --watch ./docs
每次读取前都会重新扫描监视中的文件(mtime + 内容哈希),然后才提供结果——保存文件后不会得到过时答案;未更改的文件夹重启后会保持完全预热状态。**真实数据:**在同一组 100 个问题的验证测试中,文件夹模式得分为 0.46,而工厂构建的缓存为 0.71(拒答数分别为 40 次和 18 次)——这是通用段落分块器和按需构建键孔的实测代价。你可以用它在一分钟内体验新鲜度循环;要达到生产级质量,请使用自己的技术栈。有一点需要说明:关于刚添加文件的问题,在读取触发该文件的首次构建之前,预热缓存可能会如实拒答——这是拒答,而不是过时或错误的答案。
供多个 Agent 共用一个缓存(--transport http)。
COALENT_MCP_TOKEN=<secret> coalent-mcp --cache-factory my_cache:build --transport http --port 8765
一个长期运行的进程,多个并发 MCP 客户端,共用一个缓存——效果共享叠加,不会发生存储竞态(已验证:两个并发客户端的 20 次读取结果全部与顺序执行的参考结果一致,且没有重复构建)。设置 COALENT_MCP_TOKEN 后,每个请求都必须携带 Authorization: Bearer <token>——请绑定到 localhost 或可信网络。由此可知,stdio 模式下每次启动都会创建独立进程,因此切勿让两个应用指向同一个 --store 路径——HTTP 模式才是共享缓存的解决方案。
七种工具: get_context(query, budget?) → 带来源归属、按预算打包的负载 + 一个 read_id · report_refusal(read_id) / report_success(read_id) → 通过 MCP 实现的行为修复循环 · source_changed(artifact_id, text?) → 自带新鲜度信号源(会通过哈希检测内容未变的情况并跳过) · list_sources() · cache_stats() · refresh()。
LangChain
langchain-coalent 让 Coalent 成为原生支持 LangChain 的新鲜度/复用层——优先使用自有资源:你现有的 VectorStore(或检索器)、嵌入模型和聊天模型都会直接成为缓存的底层组件,无需更改。
pip install langchain-coalent
from langchain_coalent import create_coalent_cache, CoalentRetriever
cache = create_coalent_cache(my_vectorstore, llm=my_chat_model, embeddings=my_embeddings)
retriever = CoalentRetriever(cache=cache) # drop-in LangChain BaseRetriever
docs = retriever.invoke("what is our leave policy?")
docs[0].page_content # the served, attributed context payload
docs[0].metadata["read_id"] # -> cache.report_refusal() / report_success()
docs[0].metadata["cache_hit"] # True == served with zero LLM spend
cache.source_changed("policy.md", text=new_text) # surgical, provenance-keyed invalidation
create_coalent_cache 会传入 Coalent 的所有参数;软件包还附带一个可运行的 LangGraph 风格示例,展示拒答→修复循环。仅依赖 coalent>=0.6 和 langchain-core>=0.3。
v0.7 新增内容
具备自愈能力的版本——也是默认读取路径切换的版本。读取操作现在会附带自身的不确定性,缓存则新增了一条由 Agent 驱动的显式失败链——每一级仅在读取失败时触发,因此读取成功时不会产生任何额外开销。有一项有意做出的默认设置变更(即下文第一项中的读取路径);所有新的 v0.7 参数默认关闭,在启用前不会产生任何字节级影响(由测试保证)。详情请参阅变更日志。
在与 v0.5 以来每个基准版本相同的冻结新闻测试环境中测得(609 篇文章、605 个留出问题、严格评分 + 锁定的裁定规则):完整的 v0.7 组合得分为 0.826,而最强的 v0.6 配置为 0.774——在约 983 个 token 的服务预算完全相同的情况下提升 5.3 个百分点,最终拒答数减少 69%(61 → 19)。仅在失败时触发修复,准确率与始终启用修复相当,而提取调用量仅为其 14%。
Agent 式循环——由你的评估器做出判断,缓存负责自我修复:
cache = SemanticCache(
retriever, synthesizer, embedder=OpenAIEmbedder(),
# read_path resolves to "pool" automatically under a semantic embedder (v0.7 default)
gap_detector=True, # observe-only: the read reports its own holes
repair_extractor=my_extractor, # BYO callable(span, region, existing_claims) -> [claims]
)
r = cache.get(
question,
subs=planner_subquestions, # your planner's decomposition (optional)
constraints={"dates": ["2023-10-05"], "sources": ["TechCrunch"]}, # intent metadata (optional)
)
answer = my_answerer(r.context["pool"]) # your model, your prompt
r.sources # artifact ids behind the payload
r.max_source_age_s # freshness age of this serve
r.gaps # [{probe, span, source, kind}, ...] — the read's own doubt
if failed(answer): # the failure chain — each rung ONLY on failure
cache.repair(r.read_id) # re-extract what the build missed — PERMANENT
r2 = cache.get(question, subs=planner_subquestions) # repaired claims now compete
answer = my_answerer(r2.context["pool"])
if failed(answer):
r3 = cache.reprobe(r2.read_id) # entity-probe re-rank of the same pool
answer = my_answerer(r3.context["pool"]) if r3 else answer
if failed(answer):
r4 = cache.serve_unserved(r2.read_id) # last rung: force-pack admitted-but-unserved claims
answer = my_answerer(r4.context["pool"]) if r4 else answer
- 不兼容变更 — 默认读取路径现在解析为 pool。
read_path现在默认为None,并且只要有语义嵌入器可用,就会解析为"pool"— 即所有配置了密钥的真实部署(设置了OPENAI_API_KEY,或使用任何非词法的embedder=)。Pool 是经过测量的路径(所有已发布的 v0.6/v0.7 数值;0.7 测试套件证据已满足切换标准 — 上文为 0.826 对 0.774)。无需密钥的零配置部署会回退到 unit 路径,并发出醒目警告,指出两种解决办法(设置OPENAI_API_KEY/ 传入embedder=)。退出选项: 显式设置read_path="unit"仍受到完整支持,且与 0.7 之前的默认行为逐字节一致;显式指定的read_path始终优先于解析结果。 - 读取结果会附带自身的来源信息和不确定性。
Result.sources(实际支撑载荷的 artifact ID)、Result.max_source_age_s(服务时的时效年龄),以及在启用gap_detector=True时的Result.probes/probe_coverage/gaps:针对每个探针,判断是否有原始证据句子得分高于所有主张(extraction_hole→ 修复空间),或没有内容命中该探针(corpus_hole→ 路由到工具)。按设计仅供观察:启用与关闭时的服务结果逐字节一致(已固定验证),句子层级在服务时成本为 $0(惰性加载,仅嵌入一次并缓存)。 repair(read_id)— 注入器。 消耗读取结果已存入的缺口和约束候选项,以及读取结果附近的桥接候选项;每个候选项调用一次锚定文本片段的 BYO 抽取器(库本身仍不会调用 LLM);机械式拒绝近似重复项和缺陷项(缺少先行词的代词主语、截断的文本片段);然后对存储进行仅追加、带来源标记、永久的改进 — 成本仅支付一次,此后每次读取都会受益。返回RepairReport。serve_unserved(read_id)和reprobe(read_id, hint=None)— 拒绝后的补救步骤。 强制纳入在筛选竞争中落败的主张,或使用从已服务内容中提取的实体探针,对未改变的池重新排序。只进行嵌入 — 不调用 LLM、不检索、不修改存储 — 并且按约定仅在拒绝时运行,因此不会撤销正确读取的回答。get()中的subs=/constraints=。 规划器将读取分解结果(subs=["...", ...]— 优先于decompose=可调用对象)和元数据意图(constraints={"dates": ..., "sources": ..., "entities": ...}— 键之间为 AND,同一键内为 OR;未命中时回退为 OR,而不是让读取结果无内容)。约束仅用于馈送:它们会存储修复候选项,但绝不影响池评分或服务结果(已固定验证)。- 摄取元数据弥补了 v0.6 的归因缺口。
retriever.add(..., meta={"title": ..., "source": ..., "date": ...})会将元数据放在 unit 上(source_meta),默认 pool 标题会呈现经过测量的[title | source | date]最佳路径,无需pool_header可调用对象。未附元数据的摄取与 0.6 逐字节一致。 - 切换状态: 在 0.6.0 中预先登记的 pool 路径默认值切换,已在 0.7.0 中实施 — 依据 0.7 测试套件证据及维护者的明确决定(2026-09-15),替代了从未按指定方式运行的三个预先登记门槛。此设计为条件式(按约定,pool 需要语义嵌入器,因此词法回退仍使用 unit,并会发出醒目警告)。这不是弃用:
read_path="unit"仍受到完整支持,不会发出DeprecationWarning;而serve="pool"(v0.5 预览版)在 0.7 中保持不变。
v0.6 有哪些新变化
以池为先的版本。以下所有 n=605 的数据均来自同一套冻结测试配置——包含 609 篇文章的新闻语料库、605 个留出问题、gpt-4.1-mini 答题器和严格评分——与测量 v0.5 数据时使用的配置相同。完整详情请见 CHANGELOG 和 UPGRADE-0.5-to-0.6.md。
read_path="pool"——以声明池为先的读取路径(在 0.6 中需选择启用;使用语义嵌入器时,自 v0.7 起为解析后的默认值)。通过按预算打包全局新鲜声明池来回答问题;单元仍是所有权、新鲜度和来源信息的骨架。测得结果:严格准确率为 0.731 @ 平均上下文 981 个 token——与朴素 top-9(0.711 @ 1,311)相当,token 数减少约 25%;与朴素方法测得的最佳点(top-12:0.731 @ 1,729)相比,token 数减少约 43%。结论是用更少 token 达到同等水平(置信区间有重叠),而非准确率胜出。黄金声明在服务顺序中的排名:池顺序下 p50/p75/p90 = 1/6/15。- 默认提供来源归属信息,并测量了标题梯度。 现在,
pool_header=None会渲染内置的逐来源标题。相同存储、相同查询:不透明 ID 0.641 → 发布默认值 0.678 → 你的[title | source | date]元数据回调 0.731。差距受限于单元元数据(媒体和日期信息在你的语料库中,而不在单元上)——请接入该回调(见上方快速入门)。 - 行为链路(默认全部关闭):跨度 → 回退 → 修复 → 键。
residual_spans=True会将提取器遗漏的承载事实的句子作为第 2 层跨度保存在单元上(绝不进入池)。当你的答题器拒答时,report_refusal(read_id)会返回带来源归属信息的重试载荷;report_success(read_id)会确认救援成功,并且在启用query_keys=True时,生成持久的备用键;带有有损标记的单元会在下次重建时以仅追加的方式自我修复。通过完整链路测得:拒答次数 91 → 61(−33%),最终准确率提升 3.1 个百分点,token 数增加 2.1%(同一存储对比),没有新增错误答案;键命中类别在改写后的再次查询中,首次回答成功率从 0% → 61%。 - 自适应服务门控(
serve_gate=None)——根据池自身的噪声上限进行调整;显式指定浮点数则会禁用自适应(便于复现基准测试)。通过 $0 回放门控后才发布:两组各有 605/605 个服务决策完全一致,构建次数为零,LLM 调用次数为零。 - 加固与管线完善: 每个载荷接口都带有来源归属信息(池载荷和升级原始数据);不同所有者之间的近重复声明会保留为佐证;新增 14 个可观测性事件(
pool_served、residual_fallback、key_confirmed,……);新增reranker钩子(仅影响服务顺序——绝不会导致错误服务);新增claim_index自带池存储;修复了 v0.5 池预览中一个陈旧服务问题。 - 已弃用:
serve="pool"(v0.5 预览功能)——在 0.6 和 0.7 中仍可原样使用;请迁移到read_path="pool"。在 0.6 中,默认读取路径仍为"unit"(与 v0.5 行为完全一致),并受五项预先注册的切换门控约束——v0.7 已切换(见 v0.7 有哪些新变化)。
坦诚说明限制(实测结果,而非假设)
- 拒答回退能处理约 20% 的自然拒答(载荷中包含答案原文时为 33%)——实验室问题中 68% 的数据只适用于载荷按设计包含答案的情况。这是对提取尾部问题的净改善,并非第二个检索器。
- 在主题相近且密集的语料库中,查询键可能在同组文章之间冲突(观察到 3/605 次读取;答案仍然正确)。此类情况下请将
key_floor提高到默认值 0.85 以上。键会将拒答后的往返流程转为首次直接作答;对于多样化的改写,不会提升最终准确率。 - 发布默认标题只能归属单元已知的信息——从 0.678 到 0.731 的差距受限于单元元数据;目前可通过
pool_header回调消除这一差距;可选的摄取时元数据字段计划在 v0.7 中推出。
v0.5 有哪些新内容
池化版本——我们在真实新闻数据(MultiHopRAG,609 篇文章、第三方问题)上进行了为期一个月的预注册基准测试,结果催生了以下可选择启用的功能:
preset="multi_hop"— 一个参数即可启用跨单元召回和第 2 跳桥接,并采用经过校准的阈值。显式传入的 kwargs 始终优先。- 来源扩展(
widen_chunks=24)— 由未命中触发的构建会读取主导来源中最多 N 个数据块,而不只是检索到的狭窄片段。端到端效果:重建次数从 460 降至 31,热缓存轮次的准确率从持续下降转为不断提升。摄取时绝不会触发。 - 来源溯源准入(
provenance_admission=True)— 精确文本包含探测可避免重复理解:覆盖充分的读取直接返回,无需构建;覆盖不足时则在原处扩展并重建。 - 自适应命中门控(
adaptive_hit=True)— 随着缓存增长,自动校准评分膨胀问题(已证实,固定阈值会在规模扩大时接受所有内容)。 - 池化服务预览(
serve="pool"、serve_budget=600、pool_header=...)— 提供按 Token 预算排序的全局新鲜声明池,而非单个路由单元(实验性功能;v0.6 中由read_path="pool"取代——该预览在 0.6 和 0.7 中仍可原样使用)。 留出集 n=605:准确率为 0.699,而单元服务为 0.579(z=6.66);在 Token 数仅为朴素方法 k9 方案(当时测得的最佳结果)的 0.79 倍时,统计上与其持平;95% 的空值诚实性。 来源发生变化时,过期单元的声明会立即从池中屏蔽。 fast="auto"— 存在 numpy 时启用由 numpy 加速的读取路径(pip install "coalent[fast]");结果与纯 Python 核心严格保持等价。- 可观测性(
on_event=...)— 结构化的新鲜度事件:构建、重建、准入复用、阻止过期读取、召回和桥接活动。 - 已弃用:
select_floor(已由池化服务取代)。
完整数据和方法见 基准测试 部分和 CHANGELOG。
v0.4 有哪些新内容
两项此前仅供选择启用的预览功能现已成为默认功能,因为它们在 Coalent 面向的结构化/高复用语料上表现严格更好,而在其他场景中则免费或处于休眠状态。两项功能都可通过一行配置恢复到 v0.3 的精确行为(extract=False、cross_unit_recall=False)。
- 🎯 抽取式理解(
extract=True,默认启用)。 合成器不再生成问题导向的散文式摘要,而是提取一组与查询无关、以来源为依据的原子声明。同一单元现在可以回答之后提出的许多不同问题,而且不会遗漏任何数字——我们的测试中,散文式摘要会悄然丢失来源中约 40% 的数字。 - 🔗 跨单元声明召回(
cross_unit_recall=True,默认启用)。 当一个单元对查询的覆盖不足时,缓存会汇总所有新鲜单元中的逐条声明记忆(MaxSim),并呈现桥接事实——无需额外调用 LLM,即可回答朴素检索从结构上无法回答的多跳问题(证据所在文档与问题并不相似)。单跳场景下处于休眠状态/不产生额外开销;使用非语义嵌入器时会自动关闭。结果通过result.recalled提供。 - 🛡️ 精确度与服务选项(可选启用,默认关闭):
hit_margin(拒绝有歧义的平分结果)、select_floor(按语义提供原子声明,减少 Token)、residual_floor(找回抽取器遗漏的数字片段)。何时使用各选项,请参见门控阶梯。
从 v0.3 升级?请参阅 UPGRADE-0.3-to-0.4.md — 仅增量升级,有一项行为变化(理解结果现为声明,而非散文)。
工作原理
query ──► embed ──► semantic cache
│ hit & fresh? ──► serve cached understanding (no retrieval, no LLM)
│ miss / stale? ─┐
▼ ▼
your Retriever ──► your Synthesizer ──► Cognition unit
(vector/tool/API) (LLM or passthrough) { understanding
▲ + raw evidence
│ + provenance }
source changed ────────────┘ dirties ONLY the units that used that source
- 嵌入查询,并查找含义相似的现有单元。
- 命中且未过期 → 返回缓存的理解结果(不检索,也不调用 LLM)。
- 未命中或已过期 → 检索、综合理解结果、保留原始证据、记录溯源信息(使用的确切来源),并将结果缓存。
- 来源发生变化 →
source_changed(id)只会标记溯源信息包含该id的单元;这些单元会在下次读取时延迟重建。
通过比较内容哈希跳过未变化的内容,因此无操作的更改不会产生任何成本。
读取路径——层层关卡
这是单元读取路径(read_path="unit"——与 v0.5 的行为完全一致;也是无密钥回退路径,以及绕过 v0.7 池默认值、保持字节级一致的入口)。在语义嵌入器下,v0.6 的池路径——自 v0.7 起解析后的默认路径——会以全局声明池打包取代单元路由,并使这些单元路由参数失效;池路径自身的参数见 UPGRADE-0.5-to-0.6.md。
Coalent 根据单元了解什么来匹配——嵌入的是它的理解,而不是查询中的词语——因此,*"有多少天年假?"会命中你的休假单元,而"交换政策"*则不会。每次 get(query) 都会依次通过一组固定的关卡。默认值采用纯余弦相似度——无需额外模型,也没有重型依赖——且每个关卡都是可调参数。触发顺序如下:
| # | 关卡 | 默认值 | 触发条件 → 结果 |
|---|---|---|---|
| 1 | hit_threshold——匹配 | 自动(OpenAI 约为 0.33) | 最佳单元的混合得分(0.7·topic + 0.3·seed)低于此值 → 未命中 → 检索并合成一个新单元 |
| 2 | hit_margin——精度保护 | 0.0(关闭) | 最佳单元领先第二名的幅度小于此边际 → 存在歧义 → 改为构建查询自身的单元 |
| 3 | 新鲜度 | 来源信息 / TTL | 匹配的单元已变脏或过期 → 重新生成 |
| 4 | 覆盖度——能否回答? | 每条声明的最大余弦相似度 | 匹配的单元对当前查询的覆盖程度(一条完美匹配的声明即视为已覆盖) |
| 5 | cross_unit_recall | 开启(v0.4) | 覆盖度 < recall_threshold → 汇集所有新鲜单元中的最佳声明(MaxSim),可能提高覆盖度。休眠时零开销,不调用 LLM |
| 6 | coverage_scorer(S2) | None(关闭) | 处于模糊区间 [coverage_floor, coverage_ceiling) 时 → 通过交叉编码器 / NLI / LLM 蕴含检查覆盖余弦相似度 |
| 7 | coverage_floor——RAG 下限 | 自动(约为 0.28) | 覆盖度仍低于此值 → 升级:追加新鲜的原始检索结果(不调用 LLM),让覆盖不足的命中回退到检索,而非错误作答 |
| 8 | select_floor——提供结果 | None(按词法裁剪) | 按含义提供单元中的原子内容(每条声明的余弦相似度 ≥ 下限),而不是按关键词裁剪——只保留与查询相关的事实,减少 token |
此外还有一个构建时参数——residual_floor:将提取器丢弃的含数字源文本片段(最佳每条声明的余弦相似度 < 下限)保留为额外原子。仅使用嵌入。
其他钩子:route_by_claim(通过大型单元的声明进行晚期交互路由)、relevance_gate(在合成前接入自有重排器)、depth(合成完整度与成本)、calibrate_thresholds() / suggest_thresholds()。
不同工作负载该用哪个参数——默认设置针对结构化、单跳复用进行了调优;数据特征不同时可使用以下参数:
| 使用…… | 适用场景 |
|---|---|
recall_threshold ≈ 0.7 | 多跳 / 跨文档问题——让召回功能弥合覆盖不完整的读取结果(实现完整的多跳收益) |
hit_margin > 0 | 矛盾多 / 冲突多的语料库,其中相近的得分难以区分(代价是重建次数增加——干净的数据应保持关闭) |
select_floor | 大型单元中的同义改写较多的查询(查询与声明没有共同词语时,按关键词裁剪会漏掉内容) |
residual_floor | 提取器可能会漏掉数字的杂乱真实文本(低成本保险) |
coverage_scorer(S2) | 高风险歧义场景,错误提供结果的代价较高(每次边界读取会增加一次判定调用) |
stats() 会报告 hit_rate、escalation_rate 和当前生效的阈值,让你准确了解——并调优——缓存的行为。
自带技术栈
Coalent 只拥有一个精简契约,其余一切都交由你的工具处理。
检索器——从一行代码到完全掌控,任你选择:
| 你已有…… | 使用 |
|---|---|
| Qdrant / Chroma / pgvector | 内置适配器(自带客户端) |
| 其他向量数据库 | 扩展 BaseVectorRetriever |
| 现有搜索函数 | FunctionRetriever |
| 多个需要融合的数据源 | CompositeRetriever |
| 其他任何情况 | 实现 Retriever(一个方法) |
from coalent import QdrantRetriever
retriever = QdrantRetriever(client=my_client, collection="docs", embed=my_embed)
合成器——将证据转化为理解:
LLMSynthesizer— 通过你的 LLM(OpenAI、Anthropic 或任何提供商)生成结构化、基于引文的理解。instruction和fields由你负责;来源 / 严格 JSON / 引文封装由 Coalent 负责,因此无论你要求什么,都能记录溯源信息。JSONPassthroughSynthesizer— 适用于已有结构化工具 / API JSON:将其直接作为理解缓存,不调用 LLM。
嵌入——缓存如何按含义匹配查询。安装 coalent[openai] 并设置 OPENAI_API_KEY 后,缓存会自动使用 OpenAI 嵌入;否则会发出警告并回退到词汇匹配器。你可以随时覆盖:
from coalent import SemanticCache, OpenAIEmbedder, FunctionEmbedder
cache = SemanticCache(retriever, synthesizer, embedder=OpenAIEmbedder("text-embedding-3-large"))
# or a local model: embedder=FunctionEmbedder(lambda t: my_model.encode(t).tolist())
使用真正的嵌入器进行语义匹配——无密钥时回退的
HashingEmbedder按关键词重叠匹配,而不是按含义匹配,因此措辞不同但相似的查询可能无法命中缓存。
存储——持久可靠,重启后仍可使用(启动时会重建失效关系图):
from coalent import SemanticCache, SQLiteCognitionStore # stdlib, no server
from coalent import RedisCognitionStore # shared across processes / hosts
cache = SemanticCache(retriever, synthesizer, store=SQLiteCognitionStore("coalent.db"))
任意 Agent 框架——读取 API 只需调用一次,因此可以随处接入。内置了用于图节点和 MCP 工具的辅助工具:
from coalent import make_cognition_node, build_mcp_tools
node = make_cognition_node(cache) # a graph node: state -> { context: fresh understanding }
tools = build_mcp_tools(cache) # expose the cache as an MCP tool
关于完整的独立 MCP 服务(新鲜度循环、七种工具、HTTP 传输),请参阅下一节;关于 LangChain,请参阅 langchain-coalent。
基准测试
真实场景:v0.7 失败链(n=605)
以下所有基准均使用相同的固定测试环境(609 篇真实新闻文章、605 个留出问题、gpt-4.1-mini 答题器、严格评分 + 锁定的仲裁规则),衡量完整的 v0.7 组合,相较于我们自己的最强 v0.6 配置——而非简单基线:
| 方案 | 准确率 | 上下文 token 数 | 最终拒答数 |
|---|---|---|---|
| 最强 v0.6 配置(元数据头 + 行为栈) | 0.774 | ~983 | 61 |
| v0.7 组合(subs + gap detector + constraints → 失败链) | 0.826 | ~983 | 19(−69%) |
- 在服务预算完全相同的情况下提升 5.3 个百分点——服务 token 数零增加;该链只会在读取失败时增加成本(仅在失败时触发修复,准确率与始终启用相当,但提取调用仅为其 14%)。
- 57 次修复的来源: 首轮处理 37 次(分解 + 元数据服务),
repair()10 次,serve_unserved()6 次,reprobe()4 次——每一环都有其价值。 - 每一环只会在读取失败时触发——拒答绝不是正确答案,因此后续环节无从造成破坏。修复是永久性的:相关声明会连同各自的溯源信息一起加入存储,因此所有用户今后每次读取都会受益。
真实场景:池读取路径(v0.6,n=605)
与下方 v0.5 数据使用同一套测试配置——609 篇真实新闻文章、605 个冻结的留出问题、gpt-4.1-mini 答题器、严格评分——并以 naive 自身的 token 扩展曲线作为公平性对照;现在测试范围扩展到了 naive 实测的最佳点:
| 组别 | 准确率 | 上下文 token 数 |
|---|---|---|
| naive top-9 | 0.711 | 1,311 |
| naive top-12(实测最佳) | 0.731 | 1,729 |
Coalent read_path="pool"(默认设置 + 元数据标题) | 0.731 | 981 |
- 结论是准确率持平、token 更少,而非准确率胜出。 每组两两比较的置信区间均有重叠。准确率为 0.731、上下文 token 数为 981,准确率与 naive top-9 相同,所需上下文 token 少约 25%;与 naive 实测最佳点相比,所需上下文 token 少约 43%(仅为其预算的 57%)——此外还能实现仅靠检索无法做到的事(新鲜度、来源归属和行为累积)。
- 服务排序: 在池中的顺序里,黄金声明的排名为 p50/p75/p90 = 1/6/15(仅统计包含该声明的查询),使用默认余弦排序——没有重排器。
- 标题效果经实测,而非凭感觉: 不透明 ID 为 0.641 → 默认配置为 0.678 → 你提供的
[title | source | date]可调用项为 0.731。上表中的 0.731 使用了元数据可调用项;请配置pool_header(参见快速入门)。 - 行为机制栈(可选启用):最终准确率 +3.1 个百分点,token 数 +2.1%,拒答率 −33%,没有新增的错误答案——同一存储、同一总体,完整运行 report_refusal/report_success 循环后的对比。
- 在这套测试配置中,v0.5 的基准点为 0.699 @ ~1,036:v0.6 重写版保持了这一水平(置信区间有重叠),同时修复了过时内容服务漏洞,并默认启用来源归属。
真实场景:MultiHopRAG(v0.5,预注册)
609 篇真实新闻文章、第三方黄金问题,由 gpt-4.1-mini 作答并采用精确匹配评分——该语料对分块检索极为有利(问题根据文章句子生成),因此被选作对抗性测试。我们采用多数基准测试忽略的公平性对照:在相同数据流上测试 naive 自身的 token 扩展曲线(k4:0.58 @ 590 tok · k6:0.64 @ 882 · k9:0.71 @ 1311,n=605 个留出样本)。
- 池服务(
serve="pool",缓存已预热):0.699 @ ~1,036 个 token——优于 naive k6(配对 McNemar z=3.22),并且在统计上与 naive k9 组持平;当时这是其实测最佳点,而本方案所需 token 仅为其 0.79 倍(z=0.60)。我们并未声称在此胜过这条曲线;结论是以更少 token 达到同等水平,并实现仅靠检索无法做到的事(新鲜度、来源归属和复用累积)。 - 如实处理无答案情况(n=100 个不可回答问题):池的拒答率为 95%,naive 为 85–88%。
- 构建层(冷启动、即时构建):拓宽后的单元读取其来源中位数为 23 个分块,而 keyhole 构建为 2 个;重建变动次数从 460 → 31;热运行准确率从衰减(−0.03)转为累积提升(+0.04)。
- 错误归因率为 2–6%;跨单元召回在约 90% 的读取中触发(已完整埋点)。
更早的基准测试历史——v0.4 结构化场景研究,以及基准透明度说明(我们的发现、修复内容及方法)——请参阅文档。
CLI
安装 Coalent 后即可使用 coalent 命令——它是面向认知缓存的 redis-cli(底层使用 SQLite 存储):
$ coalent ls
STATUS HITS AGE SRC ID QUERY
fresh 6 2m 2 cog:c95a9d2897e0af what is our leave policy?
dirty 1 12m 1 cog:7f1a0b9c3d2e4f remote work rules
$ coalent show cog:c95a9d2897e0af # understanding + provenance + raw evidence
$ coalent invalidate confluence:98231 # fire a change event
$ coalent stats
文档
📚 完整文档:coalent.ai/docs——概念、来源归属与新鲜度、检索器、合成器、持久化、实用示例(向量搜索、MCP 与工具、Agent),以及完整的 get() / 数据模型参考。
安装选项
pip install coalent # core, zero required deps
pip install "coalent[openai]" # OpenAI provider (also: anthropic)
pip install "coalent[qdrant]" # vector adapters (also: chroma, pgvector)
pip install "coalent[redis]" # distributed store
pip install "coalent[dev]" # tests + lint + types
参与贡献
欢迎提交问题和 PR。推送前请运行检查门禁:
pip install -e ".[dev]"
pytest && ruff check src && mypy src
请注意一项 CI 实际情况:项目将 mypy 的分析目标固定为 3.10
(pyproject.toml 中的 python_version),但如果安装了 numpy >= 2.5(Python 3.12+ 上 [fast] 额外依赖会解析到此版本),其存根会使用 3.10 分析目标无法解析的语法。在这种情况下,请运行 mypy src --python-version 3.12,使其与当前解释器版本一致——这与 CI 矩阵的做法完全相同。
状态与许可
Alpha — API 可能会在 1.0 版之前发生变化。类型标注完整(mypy --strict),已通过 lint 检查和测试。
基于 Apache-2.0 许可。
值得信赖的上下文,而不只是廉价。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。