Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 6 additions & 19 deletions crates/utopia-server/src/api/auth_routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use serde_json::json;
use utopia_core::models::User;
use utopia_core::AppError;

use super::kbs::{install_packs, DEFAULT_PACK};
use crate::auth::{self, AuthUser};
use crate::error::ApiResult;
use crate::state::AppState;
Expand Down Expand Up @@ -66,7 +65,7 @@ pub async fn register(
let utopia_store::accounts::Registered {
user,
workspace,
general_kb,
general_kb: _,
} = utopia_store::accounts::register(
&state.pool,
req.email.trim(),
Expand All @@ -77,23 +76,11 @@ pub async fn register(
)
.await?;

/* 首个用户那个 General 库也要有词汇表(#322)。
建库对话框里 schema.org 是预勾选的默认(0008、0009),而这条路径绕过了
对话框——于是每个部署的第一个库、也就是新用户落地的那个,本体是空的:
没有 domain/range,抽取的方向就定不住,事实退化成 related_to,
而这正是 0008 装包要解决的事。

**装不上不能挡住注册。** 事务已经提交,账号已经存在;这一步失败只是
回到今天的行为(库空着,日后手动装包再重跑类型消解即可,见 0009),
让人注册不进来则是把小事变成大事 */
if let Some(kb_id) = general_kb {
if install_packs(&state, kb_id, user.id, &[DEFAULT_PACK.to_string()])
.await
.is_err()
{
tracing::warn!(kb_id = %kb_id, pack = DEFAULT_PACK, "默认库的冷启动本体包没装上");
}
}
// 首个用户那个 General 库**空着起步**(#580)。从前这里给它装 schema.org(#322),
// 理由是没有 domain/range 事实就没方向;量过之后(#580 的对照):包给的是实体
// 类型,不是谓词——装了包八成事实照样没谓词;空库靠 bootstrap 从文档里长出
// 十几个类、上百条关系,都是语料自己的说法,每块的提示词从 18k tokens 回到 2k。
// 包还在建库对话框里,要的人一键装

let token = auth::issue_token(&state, user.id)?;
let secure = auth::behind_tls(&headers, state.cookie_secure);
Expand Down
7 changes: 3 additions & 4 deletions crates/utopia-server/src/api/kbs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,9 @@ pub async fn audit_log(
/// **一个包失败不回滚已装的**:本体是加法,装了一半的库仍然可用,
/// 而回滚要撤已经建好的类——那正是 0008 决定不做导入撤销的理由。
/// 失败信息里带上是哪个包,让人知道从哪补。
/// 建库对话框里预勾选的那个包(0008、0009)。注册时建出来的 General 库
/// 绕过了对话框,所以它在那条路径上也要用同一个默认(#322)。
pub(super) const DEFAULT_PACK: &str = "schema-org";

///
/// 没有默认包(#580):注册建的 General 库与对话框里新建的库都空着起步,
/// 装哪个包是建库的人选的。
pub(super) async fn install_packs(
state: &AppState,
kb_id: Uuid,
Expand Down
22 changes: 19 additions & 3 deletions docs/decisions/0008-ontology-packs-as-cold-start.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# 0008 · Ontology packs as the cold start

- **Status**: Built · five packs embedded in the binary (gzip, 1.7 MB → 316 KB),
multi-select at KB creation with schema.org checked by default, 22-row static alignment
table · a new KB seeds no relations: the packs are the whole ontology · the three open
questions stay open; the Chinese-label one got worse (2026-09-02 check)
multi-select at KB creation, 22-row static alignment table · a new KB seeds no
relations: the packs are the whole ontology · **no pack is checked by default and the
base made at registration installs none** (2026-09-10, #580) · the three open questions
stay open; the Chinese-label one got worse (2026-09-02 check)
- **Written**: 2026-08-30 · condensed into English 2026-09-03
- **Related**: [0001](0001-ontology-import-and-governance.md) criteria and IRI/key split;
[0006](0006-ontology-scale-and-the-prompt.md) prompt budget;
Expand Down Expand Up @@ -99,6 +100,21 @@ order status).
of packs is not prompt length but **choice**: many names are generic with narrow meanings
(`affectedBy` is a medical test, `competitor` a sports event), and picking by name or
vector will hit them.
- 2026-09-10 (#580): **the default is no pack.** schema.org was pre-selected in the creation
dialog and installed into the base made at registration (#322), on the argument that a
base without domain and range has no direction for its facts. Measured on the ai-timeline
corpus (15 documents, DeepSeek-V3.2, `auto_extend_ontology` on, bootstrap allowed to
finish): the pack gives types, not predicates. With schema.org, 98% of entities were typed
at 90% accuracy against the Wikidata sheet, but 81% of facts still had no predicate before
bootstrap and 65% after, and 342 facts were dropped for `domain_mismatch`; the empty base
ended with 13 classes and 175 relations in the corpus's own words, 80% of entities typed
at 77%, and *more* entity-to-entity edges with a predicate (56% against 49%). Each
extraction call carried ~2k tokens instead of ~18k (the retrieved slice of a 915-class
pack; the full list would be ~105k and never goes in). The pack costs ten points of type
accuracy to leave out and buys an ontology the base grows itself; that is the trade this
record now makes by default, and a base that wants schema.org's vocabulary picks it in
the dialog. Still unmeasured: how many edges in the empty base run backwards, which the
direction check cannot catch without signatures.

## Open questions

Expand Down
2 changes: 1 addition & 1 deletion docs/decisions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The test for writing one: if someone (including us) looks at a piece of code in
| 0005 | [The alert center](0005-alert-center.md) | Built · five alert kinds live, search and paging in the panel · `document.no_text_layer` still unwired |
| 0006 | [Ontology scale and the extraction prompt](0006-ontology-scale-and-the-prompt.md) | Built · the character budget (24,000) and per-chunk retrieval live, values untested · answer keys still hand-filled |
| 0007 | [Counting decides what becomes a relation](0007-who-decides-what-becomes-a-relation.md) | Built · adoption decided by counting (`MIN_DOCS = 2`, `MIN_SIGNALS = 3`), proposals persist (#112) · narrative verbs and `_by` folding still open |
| 0008 | [Ontology packs as the cold start](0008-ontology-packs-as-cold-start.md) | Built · five packs embedded, multi-select at creation, schema.org by default · three open questions stay open; Chinese labels got worse |
| 0008 | [Ontology packs as the cold start](0008-ontology-packs-as-cold-start.md) | Built · five packs embedded, multi-select at creation · no pack by default and none at registration (#580, measured) · three open questions stay open; Chinese labels got worse |
| 0009 | [An undecided type stays empty](0009-no-type-is-a-type.md) | Implemented · `type_id` nullable, builtin classes gone · kin classes go to Review (#226), declared `disjointWith` keeps them apart (0016 B3) · `metric` / `dimension` builtin on demand (#231) · `metric` / `dimension` to retire under 0036 |
| 0010 | [An unnamed relation stays empty](0010-no-relation-is-no-relation.md) | Implemented · `predicate_id` nullable, `related_to` gone, wording recovered by `fact_surface_predicate` · follow-ups done with 0011 |
| 0011 | [A mapping is configuration](0011-a-mapping-is-not-a-fact.md) | Implemented (#126 / #140 / #148) · Review flow and revision history rebuilt · the evidence chain not built · revised in part by 0036: the concept becomes an attribute or a rule, the table becomes a rendered one |
Expand Down
2 changes: 1 addition & 1 deletion docs/pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ flowchart TB

**The dotted line back into extraction is the loop.** Extraction uses the ontology; when it meets a term the ontology lacks, it records the original wording; proposals flow back into the ontology; the next batch of documents is extracted with it. See [0003](decisions/0003-ontology-growth-loop.md).

**Where the ontology comes from.** A new database seeds nothing. The starting point is an optional prebuilt pack (schema.org checked by default; also W3C Org, PROV-O, FOAF, IOF Core), a user-imported OWL file, or nothing at all. An empty ontology still extracts; an entity without a type simply has no type. See [0008](decisions/0008-ontology-packs-as-cold-start.md) and [0009](decisions/0009-no-type-is-a-type.md).
**Where the ontology comes from.** A new database seeds nothing. The starting point is an optional prebuilt pack (schema.org, W3C Org, PROV-O, FOAF, IOF Core; none is checked by default), a user-imported OWL file, or nothing at all, which is the default. An empty ontology still extracts; an entity without a type simply has no type. See [0008](decisions/0008-ontology-packs-as-cold-start.md) and [0009](decisions/0009-no-type-is-a-type.md).

**The bottom two boxes are the ontology's axioms at work.** The consistency check never writes `facts`; it only surfaces contradictions (the Review page's two tiers, violations and defects). Materialized inference is off by default; when on, derived facts go to a separate table, show as gold on the graph, and never close any asserted fact. See section 4.

Expand Down
4 changes: 0 additions & 4 deletions web/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1243,10 +1243,6 @@ export type AlertGroup = {
lines: { name?: string; error?: string; job?: string }[];
};

/** 新库默认装的本体包。建库对话框和自动建出的第一个库都从这里取,
* 两处只能有一个答案:README 承诺的是「默认 schema.org」,不是「默认没有词表」 */
export const DEFAULT_ONTOLOGY_PACKS = ["schema-org"];

export const api = {
health: () =>
request<{ status: string; name: string; version: string }>(
Expand Down
2 changes: 1 addition & 1 deletion web/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ export const en = {
packsLabel: "Bundled ontologies",
packsHint: "Optional, and more can be imported later.",
packsPick: "Search packs…",
packsNone: "None — start from the ten seed relations",
packsNone: "None — the ontology grows out of the documents",
packsCount: (c: number, p: number) => `${c} classes · ${p} properties`,
name: "Name",
description: "Description",
Expand Down
2 changes: 1 addition & 1 deletion web/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ export const zh: Strings = {
packsLabel: "预置本体",
packsHint: "可不选,之后还能再导入。",
packsPick: "搜索本体包…",
packsNone: "不选 —— 从十个种子关系起步",
packsNone: "不选 —— 本体从文档里长出来",
packsCount: (c: number, p: number) => `${c} 个类 · ${p} 个属性`,
name: "名称",
description: "描述",
Expand Down
10 changes: 5 additions & 5 deletions web/src/kb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCallback, useSyncExternalStore } from "react";
import { useLocation, useNavigate, useParams } from "@tanstack/react-router";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { STREAM_KEYS } from "./queryDefaults";
import { api, DEFAULT_ONTOLOGY_PACKS, type Kb, type Workspace } from "./api";
import { api, type Kb, type Workspace } from "./api";
import { kbStore, wsStore } from "./wsStore";

/** 当前路径里的知识库 id。**页面都在 /kb/$kbId 之下,所以直接从路径取**——
Expand Down Expand Up @@ -55,13 +55,13 @@ export function useKb(): {
if (existing.length > 0) return existing;
// 空工作区自动创建 General——建库现在是管理员动作,非管理员会 403:
// 静默等管理员来创建(现实中首个用户即管理员,General 总在)。
// **带默认本体包。** 从前这里不传 packs,于是一个新部署的第一个库一个类
// 都没有,第一批文档抽出来全是未分类实体;建库对话框里那个"默认 schema.org"
// 只对第二个库起效。第一个库恰恰是大多数人唯一会用的那个
// **空着起步,不带包**(#580)。从前这里装 schema.org,怕第一批文档抽出来全是
// 未分类实体;量过之后(#580 的对照)本体从文档里自己长出来,装包换来的类型
// 准确率十个点,代价是每块提示词 18k 对 2k。要包的人在建库对话框里选
try {
const created = await api.createKb(ws!.id, {
name: "General",
ontology_packs: DEFAULT_ONTOLOGY_PACKS,
ontology_packs: [],
});
queryClient.invalidateQueries({ queryKey: ["kbs", ws!.id] });
return [created];
Expand Down
10 changes: 5 additions & 5 deletions web/src/pages/MyKbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useNavigate, useSearch } from "@tanstack/react-router";
import { Lock, Plus, Search } from "lucide-react";
import { api, DEFAULT_ONTOLOGY_PACKS, type MyKb } from "../api";
import { api, type MyKb } from "../api";
import { S } from "../i18n";
import { useKb } from "../kb";
import {
Expand Down Expand Up @@ -192,10 +192,10 @@ function NewKbModal({
const [name, setName] = useState("");
const [desc, setDesc] = useState("");
const [restricted, setRestricted] = useState(true);
// schema.org 默认勾选,可反选(0009)。删掉内置类之后不选任何包的库是真的空,
// 而空库仍然能用——但绝大多数人要的是一个已经能认出人、组织、产品的起点
// 一秒装完(0008 的批量插入),所以默认装得起
const [packs, setPacks] = useState<string[]>([...DEFAULT_ONTOLOGY_PACKS]);
// 默认一个包都不勾(#580)。从前预勾 schema.org,理由是"要一个已经能认出人、
// 组织、产品的起点";量过之后本体从文档里长出来的是库自己的十几个类,而 915 个
// 类的包让每块抽取提示词从 2k 涨到 18k tokens。要包的人在这里勾
const [packs, setPacks] = useState<string[]>([]);

const available = useQuery({
queryKey: ["ontologyPacks"],
Expand Down
Loading