增加技术验证证据筛选并区分材料与独立测试

This commit is contained in:
2026-10-30 05:41:35 +08:00
parent 3e0cbf9831
commit cfb9273f88
7 changed files with 130 additions and 21 deletions
+4 -2
View File
@@ -98,7 +98,7 @@ AUTH_CONFIG_FILE=/private/tmp/tabularium-auth/config.json AUTH_COOKIE_SECURE=fal
同一版本内容变化会拒绝导入,避免静默读取旧数据。回退代码时继续读取对应旧版本。
所有修改都经过工作分支和 PR,部署仍等待人工合并主线。
当前资料包含 1,006 条公司/产品记录、10 类业务及待分类组、1,067 条证据记录和 129 条经营观测。
当前资料包含 1,006 条公司/产品记录、10 类业务及待分类组、1,069 条证据记录和 129 条经营观测。
其中保留 62 家个案资料、928 条目录基础资料,并新增 16 个平台产品/业务样本;目录项不等于完成财务和技术核验。
补充ThinkWheel与中国NaiveAI;MiroMind成立年未核,公开研发年份单列。保留灵初智能成立年排除、深度原理日期待核和Naive近名实体复核记录。
它是定向试样,收入披露不足且统计时点不统一,不代表全部 AI 公司。
@@ -133,7 +133,9 @@ MRR×12仅作为显示单位转换,不改写为公司ARR;公司ARR、估算
其中121条原待分类记录已按YC官方业务介绍人工复核主营业务,分类理由保存在公司字段与对应证据摘要中;
仍有3条因简介不足保留待分类。人工复核分类不代表独立验证技术水平、客户效果或财务数据。
AI 关键词可能出现在业务背景中,收入、买家和交付状态仍须逐家深查,不能把目录项的未知收入当作未披露。
界面支持按资料深度、记录团队规模筛选;证据列表每页 30 条,避免一次渲染全部来源。
技术专项评审用独立字段记录层级、日期、摘要与证据:未评审、已读技术自述、公开代码/模型材料可访问、有独立测试记录。
当前仅5条完成该字段的专项记录,其中2条确认材料可访问;没有独立测试记录。缺字段不表示无技术能力,代码或模型卡可读不证明性能。
界面支持按资料深度、记录团队规模和技术验证证据筛选;证据列表每页 30 条,避免一次渲染全部来源。
`pnpm test:research` 在远程 Runner 上验证同库持久化、重复读取、版本导入与回退、
登录限流保留、引用完整性及收入排除规则。镜像冒烟检查会验证研究页面与 API 的认证、
+1 -1
View File
@@ -187,7 +187,7 @@ async function main() {
const research = await researchResponse.json();
assert.equal(research.companies.length, 1006);
assert.equal(research.categories.length, 11);
assert.equal(research.evidence.length, 1067);
assert.equal(research.evidence.length, 1069);
assert.equal(research.observations.length, 129);
assert.equal((await request("/api/research/ai-companies", { method: "POST", headers })).status, 405);
const researchPage = await request("/docs/other-research/ai-company-research/overview", { headers });
+12 -8
View File
@@ -1,7 +1,7 @@
import { useEffect, useMemo, useRef, useState } from "react";
import IncomeDistribution from "./IncomeDistribution";
import {
categorySummary, evidenceUrl, type ResearchEvidence, type ResearchSnapshot,
categorySummary, evidenceUrl, technicalReviewLabels, type ResearchEvidence, type ResearchSnapshot,
} from "@/lib/ai-research";
const pageSize = 12;
@@ -41,6 +41,7 @@ export default function AICompanyResearch() {
const [sourcePage, setSourcePage] = useState(0);
const [depth, setDepth] = useState("");
const [teamBand, setTeamBand] = useState("");
const [technicalLevel, setTechnicalLevel] = useState("");
const [selected, setSelected] = useState<string | null>(null);
const [tab, setTab] = useState<"companies" | "revenue" | "sources" | "reviews">("companies");
const companySection = useRef<HTMLElement>(null);
@@ -63,9 +64,9 @@ export default function AICompanyResearch() {
const depthMatch = !depth || (depth === "profile" ? c.profileOnly : !c.profileOnly);
const teamMatch = !teamBand || (teamBand === "unknown" ? c.team === null || (!!c.teamBound && c.team < 51)
: c.team !== null && (teamBand === "xl" ? c.team >= 51 : !c.teamBound && (teamBand === "small" ? c.team <= 5 : teamBand === "medium" ? c.team >= 6 && c.team <= 20 : c.team >= 21 && c.team <= 50)));
return depthMatch && teamMatch && (!category || c.cat === category) && yearMatch && (!income || c.revenueStatus === income)
return (!technicalLevel || (c.technicalReview?.level ?? "unreviewed") === technicalLevel) && depthMatch && teamMatch && (!category || c.cat === category) && yearMatch && (!income || c.revenueStatus === income)
&& search.includes(query.trim().toLocaleLowerCase());
}) ?? [], [data, query, category, cohort, income, depth, teamBand]);
}) ?? [], [data, query, category, cohort, income, depth, teamBand, technicalLevel]);
if (error) return <div className="research-message" role="alert">研究数据库暂时不可用。<button onClick={() => { setError(false); setAttempt((n) => n + 1); }}>重新读取</button></div>;
if (!data) return <p className="research-message" role="status">正在读取研究数据库…</p>;
const categories = new Map(data.categories.map((c) => [c.id, c]));
@@ -76,6 +77,7 @@ export default function AICompanyResearch() {
const selectedObservations = data.observations.filter((o) => o.company === selected);
const selectedEvidenceIds = new Set([
...(selectedCompany?.evidenceIds ?? []), ...(selectedCompany?.pricingEvidence ? [selectedCompany.pricingEvidence] : []),
...(selectedCompany?.technicalReview?.evidenceIds ?? []),
...selectedObservations.map((o) => o.evidence),
]);
const selectedEvidence = data.evidence.filter((e) => e.companyId === selected || selectedEvidenceIds.has(e.id));
@@ -118,14 +120,16 @@ export default function AICompanyResearch() {
<label>成立或发布<select value={cohort} onChange={(e) => { setCohort(e.target.value); setPage(0); }}><option value="">全部组别</option><option value="2025">2025 年成立</option><option value="2026">2026 年成立</option><option value="other">公开发布与转型对照</option></select></label>
<label>资料深度<select value={depth} onChange={(e) => { setDepth(e.target.value); setPage(0); }}><option value="">全部资料</option><option value="case">原有个案研究</option><option value="profile">目录基础资料</option></select></label>
<label>记录团队规模<select value={teamBand} onChange={(e) => { setTeamBand(e.target.value); setPage(0); }}><option value="">全部规模</option><option value="small">1—5 人</option><option value="medium">6—20 人</option><option value="large">21—50 人</option><option value="xl">至少 51 人</option><option value="unknown">未披露或分组不确定</option></select></label>
<label>技术验证证据<select value={technicalLevel} onChange={(e) => { setTechnicalLevel(e.target.value); setPage(0); }}><option value="">全部验证层级</option>{Object.entries(technicalReviewLabels).map(([value, label]) => <option key={value} value={value}>{label}</option>)}</select></label>
<label>收入资料<select value={income} onChange={(e) => { setIncome(e.target.value); setPage(0); }}><option value="">全部状态</option><option value="self">找到公司披露</option><option value="estimate">仅第三方估算</option><option value="platform">仅支付平台记录</option><option value="unknown">暂无可用数字</option></select></label>
</div>
<div className="research-result-count" role="status">{filtered.length} 条匹配记录<button onClick={() => { setCategory(""); setQuery(""); setCohort(""); setIncome(""); setDepth(""); setTeamBand(""); setPage(0); }}>清除筛选</button></div>
<div className="research-table-wrap"><table><caption className="sr-only">公司/产品分类与经营资料。选择名称查看指标和原始证据。</caption><thead><tr><th>公司或产品 / 地区</th><th>业务与买家</th><th>成立 / 发布组别</th><th>团队规模</th><th>商业成熟度</th><th>收入资料</th></tr></thead><tbody>
<p className="research-muted">技术验证层级记录我们查到了什么,不是能力强弱评分。技术专项评审与业务分类复核分别记录。</p>
<div className="research-result-count" role="status">{filtered.length} 条匹配记录<button onClick={() => { setCategory(""); setQuery(""); setCohort(""); setIncome(""); setDepth(""); setTeamBand(""); setTechnicalLevel(""); setPage(0); }}>清除筛选</button></div>
<div className="research-table-wrap"><table><caption className="sr-only">公司/产品分类与经营资料。选择名称查看指标和原始证据。</caption><thead><tr><th>公司或产品 / 地区</th><th>业务与买家</th><th>成立 / 发布组别</th><th>团队规模</th><th>交付与技术验证证据</th><th>收入资料</th></tr></thead><tbody>
{filtered.slice(page * pageSize, (page + 1) * pageSize).map((c) => <tr key={c.id} data-selected={selected === c.id || undefined}>
<td><button className="research-company-name" aria-expanded={selected === c.id} aria-controls="research-company-detail" onClick={() => setSelected(c.id)}>{c.name}</button><small>{c.location ?? "地区未注明"}</small></td>
<td><span className="research-tag">{categories.get(c.cat)?.name}</span>{c.profileOnly && <small>规则初分 · 待复核</small>}<p>{c.business}</p><small>买家:{c.buyer}</small></td>
<td>{c.cohort}</td><td>{c.team !== null && c.teamBound}{c.team ?? "未披露"}{c.team !== null && " 人"}</td><td>{c.stage}</td><td>{c.profileOnly ? "收入尚未逐一核验" : revenueLabels[c.revenueStatus]}</td>
<td><span className="research-tag">{categories.get(c.cat)?.name}</span>{c.profileOnly && <small>目录基础资料 · 分类依据见明细</small>}<p>{c.business}</p><small>买家:{c.buyer}</small></td>
<td>{c.cohort}</td><td>{c.team !== null && c.teamBound}{c.team ?? "未披露"}{c.team !== null && " 人"}</td><td>{c.stage}<small>{technicalReviewLabels[c.technicalReview?.level ?? "unreviewed"]}</small></td><td>{c.profileOnly ? "收入尚未逐一核验" : revenueLabels[c.revenueStatus]}</td>
</tr>)}
</tbody></table></div>
{!filtered.length && <p className="research-message">没有匹配公司,请调整筛选条件。</p>}
@@ -149,7 +153,7 @@ export default function AICompanyResearch() {
{selectedCompany && <section id="research-company-detail" ref={detailSection} tabIndex={-1} className="research-detail research-section" aria-label={`${selectedCompany.name}资料明细`}>
<div className="research-detail-heading"><h2>{selectedCompany.name}</h2><button onClick={() => setSelected(null)} aria-label="关闭公司明细">关闭</button></div>
<p><External url={selectedCompany.url}>资料入口</External> {selectedCompany.extraUrl && <External url={selectedCompany.extraUrl}>额外核验入口</External>}</p>
<dl className="research-company-facts"><div><dt>主营业务</dt><dd>{selectedCompany.business}</dd></div><div><dt>样本组别</dt><dd>{selectedCompany.cohort}</dd></div><div><dt>买家</dt><dd>{selectedCompany.buyer}</dd></div><div><dt>收费/交付模式</dt><dd>{selectedCompany.businessModel ?? "待进一步核验"}</dd></div><div><dt>收费方式</dt><dd>{selectedCompany.price}</dd></div><div><dt>交付证据</dt><dd>{selectedCompany.delivery}</dd></div><div><dt>团队口径</dt><dd>{selectedCompany.teamBasis ?? "本轮未核验人数"}</dd></div><div><dt>分类依据</dt><dd>{selectedCompany.classificationBasis ?? "原有个案分类;随研究复核"}</dd></div><div><dt>技术水平</dt><dd>尚未独立测试,不据融资或收入作能力排名。</dd></div><div><dt>观测主体</dt><dd>{selectedCompany.entityBasis ?? "公司或经营品牌,法律主体未逐一核验"}</dd></div><div><dt>身份与限制</dt><dd>{selectedCompany.notes}</dd></div></dl>
<dl className="research-company-facts"><div><dt>主营业务</dt><dd>{selectedCompany.business}</dd></div><div><dt>样本组别</dt><dd>{selectedCompany.cohort}</dd></div><div><dt>买家</dt><dd>{selectedCompany.buyer}</dd></div><div><dt>收费/交付模式</dt><dd>{selectedCompany.businessModel ?? "待进一步核验"}</dd></div><div><dt>收费方式</dt><dd>{selectedCompany.price}</dd></div><div><dt>交付证据</dt><dd>{selectedCompany.delivery}</dd></div><div><dt>团队口径</dt><dd>{selectedCompany.teamBasis ?? "本轮未核验人数"}</dd></div><div><dt>分类依据</dt><dd>{selectedCompany.classificationBasis ?? "原有个案分类;随研究复核"}</dd></div><div><dt>技术验证证据</dt><dd>{technicalReviewLabels[selectedCompany.technicalReview?.level ?? "unreviewed"]}<p>{selectedCompany.technicalReview?.summary ?? "本轮未形成专项技术评审记录;不表示公司没有技术能力。"}</p>{selectedCompany.technicalReview && <small>评审日期:{selectedCompany.technicalReview.reviewedAt}</small>}{selectedCompany.technicalReview?.testScope && <p>测试范围:{selectedCompany.technicalReview.testScope}</p>}<p>材料可访问仅确认可查看,不证明性能、可靠性或商业交付质量;不同任务不作统一能力排名。</p></dd></div><div><dt>观测主体</dt><dd>{selectedCompany.entityBasis ?? "公司或经营品牌,法律主体未逐一核验"}</dd></div><div><dt>身份与限制</dt><dd>{selectedCompany.notes}</dd></div></dl>
<h3>经营指标与排除记录</h3>
{selectedObservations.length ? <div className="research-table-wrap"><table><thead><tr><th>指标 / 归属</th><th>原始数值</th><th>数据时点</th><th>用途及限制</th><th>证据</th></tr></thead><tbody>{selectedObservations.map((o) => <tr key={o.id}><td>{o.kind}<small>{o.scope}</small></td><td>{o.bound !== "=" && o.bound}{money(o.value,o.unit)}</td><td>{o.timing}</td><td>{useLabels[o.selection] ?? o.selection}<small>{o.reason}</small></td><td><a href={`#detail-evidence-${o.evidence}`}>{o.evidence}</a></td></tr>)}</tbody></table></div> : <p>本轮尚未找到可记录的量化经营指标。收入保持未知。</p>}
<h3>原始证据</h3><ul className="research-evidence-list">{selectedEvidence.map((e) => <Evidence key={e.id} prefix="detail-evidence" item={e} />)}</ul>
+19
View File
@@ -1,4 +1,10 @@
export type ResearchCategory = { id: string; name: string; description: string };
export const technicalReviewLabels = {
unreviewed: "技术证据尚未专项评审",
description: "已读技术自述",
"public-artifact": "公开代码/模型材料可访问",
"independent-test": "有独立测试记录",
};
export type ResearchCompany = {
id: string; name: string; url: string; cat: string; year: number | null;
team: number | null; teamBound?: ">" | "≥"; location: string | null; business: string; buyer: string;
@@ -8,6 +14,10 @@ export type ResearchCompany = {
extraUrl?: string; pricingEvidence?: string;
businessModel?: string; entityBasis?: string;
profileOnly?: boolean; classificationBasis?: string; directoryTags?: string[];
technicalReview?: {
level: "description" | "public-artifact" | "independent-test";
reviewedAt: string; summary: string; evidenceIds: string[]; testScope?: string;
};
};
export type ResearchObservation = {
id: string; company: string; name: string; kind: string; value: number;
@@ -59,6 +69,15 @@ export function validateResearch(snapshot: ResearchSnapshot) {
unique(snapshot.observations.map((o) => o.id), "observations");
unique(snapshot.reviews.map((r) => r.id), "reviews");
for (const c of snapshot.companies) {
const technical = c.technicalReview;
if (technical && (!["description", "public-artifact", "independent-test"].includes(technical.level) ||
!/^\d{4}-\d{2}-\d{2}$/.test(technical.reviewedAt) ||
Number.isNaN(Date.parse(technical.reviewedAt)) ||
new Date(technical.reviewedAt).toISOString().slice(0, 10) !== technical.reviewedAt ||
technical.reviewedAt > snapshot.asOf || !technical.summary.trim() ||
!technical.evidenceIds.length || technical.evidenceIds.some((id) => !evidence.has(id)) ||
(technical.level === "independent-test" && !technical.testScope?.trim())))
throw new Error(`Invalid technical review: ${c.id}`);
if (c.profileOnly && (c.revenueStatus !== "unknown" || !c.classificationBasis))
throw new Error(`Invalid profile-only research record: ${c.id}`);
if (!categories.has(c.cat) || !evidenceUrl(c.url) ||
+21 -1
View File
@@ -24,7 +24,7 @@ test("research revisions share the authentication database and preserve replay l
assert.equal(consumeCode(config, "invalid"), "limited");
const initial = readResearchSnapshot();
assert.equal(initial.companies.length, 1006);
assert.equal(initial.evidence.length, 1067);
assert.equal(initial.evidence.length, 1069);
assert.equal(initial.observations.length, 129);
assert.deepEqual(readResearchSnapshot(), initial, "reopening does not duplicate records");
const db = openSiteDatabase();
@@ -73,6 +73,26 @@ test("disclosure counts exclude client income, biographies, targets and duplicat
assert.ok(snapshot.observations.filter((o) => o.selection === "signal").every((o) => o.scope === "公司整体"));
});
test("technical reviews keep accessible materials separate from tested capabilities", () => {
const snapshot = structuredClone(seed) as ResearchSnapshot;
assert.equal(snapshot.companies.filter((c) => c.technicalReview).length, 5);
assert.equal(snapshot.companies.filter((c) => c.technicalReview?.level === "public-artifact").length, 2);
assert.equal(snapshot.companies.filter((c) => c.technicalReview?.level === "independent-test").length, 0);
const company = snapshot.companies.find((c) => c.id === "AI061")!;
const review = company.technicalReview!;
review.evidenceIds = ["missing-technical-evidence"];
assert.throws(() => validateResearch(snapshot), /technical review/);
review.evidenceIds = ["TECH-NAIVE-HF"];
review.reviewedAt = "2026-02-30";
assert.throws(() => validateResearch(snapshot), /technical review/);
review.reviewedAt = "2099-01-01";
assert.throws(() => validateResearch(snapshot), /technical review/);
review.reviewedAt = snapshot.asOf;
review.level = "independent-test";
assert.throws(() => validateResearch(snapshot), /technical review/,
"an accessible artifact cannot become a test record without a stated test scope");
});
// Retractions and forecasts must stay visible for review without becoming revenue signals.
test("retracted ARR and projected margins remain excluded", () => {
const snapshot = seed as ResearchSnapshot;
+72 -8
View File
@@ -1,5 +1,5 @@
{
"version": "2026-10-30-global-1",
"version": "2026-10-30-technical-1",
"asOf": "2026-10-30",
"scope": "全球公开资料研究:保留首批个案,并扩展核查2025—2026八个YC批次候选池;成立年份以官方公司页面字段为依据。",
"limitations": [
@@ -142,7 +142,15 @@
"evidenceIds": [
"E003"
],
"revenueStatus": "unknown"
"revenueStatus": "unknown",
"technicalReview": {
"level": "description",
"reviewedAt": "2026-10-30",
"summary": "已读取YC关于自主公司运行时的描述;未测试真实任务成功率、授权控制或人工接管。",
"evidenceIds": [
"E003"
]
}
},
{
"id": "AI004",
@@ -28195,7 +28203,15 @@
"revenueStatus": "self",
"businessModel": "订阅与用量额度",
"entityBasis": "公司品牌;注册实体及地区登记尚未核验",
"classificationBasis": "公司定位为小企业综合经营平台,主类C05;营销和内容生成为次级功能。"
"classificationBasis": "公司定位为小企业综合经营平台,主类C05;营销和内容生成为次级功能。",
"technicalReview": {
"level": "description",
"reviewedAt": "2026-10-30",
"summary": "已读取综合Agent产品说明;未独立执行网站、营销及运营任务,不将产品宣传当作测试结果。",
"evidenceIds": [
"RUN-ANN"
]
}
},
{
"id": "AI060",
@@ -28220,7 +28236,15 @@
"revenueStatus": "unknown",
"businessModel": "具身平台与硬件;收入结构待核",
"entityBasis": "品牌与注册实体对应关系见证据限制;未独立核验登记原件",
"classificationBasis": "按官方主营描述人工归类;不构成技术水平排名。"
"classificationBasis": "按官方主营描述人工归类;不构成技术水平排名。",
"technicalReview": {
"level": "description",
"reviewedAt": "2026-10-30",
"summary": "已读取主动交互具身平台介绍;未独立测试感知、决策或硬件可靠性。",
"evidenceIds": [
"CN-TW"
]
}
},
{
"id": "AI061",
@@ -28241,12 +28265,21 @@
"sourceType": "公司官方资料;部分成立信息为二手工商资料",
"evidenceIds": [
"CN-NAIVE-MODEL",
"CN-NAIVE-REG"
"CN-NAIVE-REG",
"TECH-NAIVE-HF"
],
"revenueStatus": "unknown",
"businessModel": "开放权重与API;实际付费收入未知",
"entityBasis": "品牌与注册实体对应关系见证据限制;未独立核验登记原件",
"classificationBasis": "按官方主营描述人工归类;不构成技术水平排名。"
"classificationBasis": "按官方主营描述人工归类;不构成技术水平排名。",
"technicalReview": {
"level": "public-artifact",
"reviewedAt": "2026-10-30",
"summary": "已打开官方链接指向的模型卡,查看架构、部署和许可证说明;未下载权重或运行推理,基准结果仍为开发方自报。",
"evidenceIds": [
"TECH-NAIVE-HF"
]
}
},
{
"id": "AI062",
@@ -28267,12 +28300,21 @@
"sourceType": "公司官方资料;部分成立信息为二手工商资料",
"evidenceIds": [
"CN-MIRO-2025",
"CN-MIRO-PRICE"
"CN-MIRO-PRICE",
"TECH-MIRO-CODE"
],
"revenueStatus": "unknown",
"businessModel": "订阅与一次性额度包",
"entityBasis": "品牌与注册实体对应关系见证据限制;未独立核验登记原件",
"classificationBasis": "按官方主营描述人工归类;不构成技术水平排名。"
"classificationBasis": "按官方主营描述人工归类;不构成技术水平排名。",
"technicalReview": {
"level": "public-artifact",
"reviewedAt": "2026-10-30",
"summary": "已打开官方MiroFlow仓库,查看源码目录、配置及部署说明;未运行Agent或复现榜单,代码存在不代表端到端任务可靠。",
"evidenceIds": [
"TECH-MIRO-CODE"
]
}
}
],
"observations": [
@@ -42170,6 +42212,28 @@
"kind": "公司自述",
"summary": "官方介绍材料化学AI业务,但可读取正文未列成立日期。",
"limitation": "本轮未取得直接成立日期证据,暂不新增为2025—2026成立公司;2025备案号不能替代成立年。"
},
{
"id": "TECH-NAIVE-HF",
"companyId": "AI061",
"title": "NaiveAI官方模型卡与部署资料",
"url": "https://huggingface.co/NaiveAI/Naive-N0.5-Flash",
"publishedAt": null,
"accessedAt": "2026-10-30",
"kind": "公开模型材料",
"summary": "可访问模型卡、架构表、部署示例及许可证说明;官方站点指向该组织。",
"limitation": "仅检查公开材料可访问,未下载模型、运行代码或复测基准;不把下载次数或组织成员数当作客户数或员工数。"
},
{
"id": "TECH-MIRO-CODE",
"companyId": "AI062",
"title": "MiroFlow官方公开代码仓库",
"url": "https://github.com/MiroMindAI/MiroFlow",
"publishedAt": null,
"accessedAt": "2026-10-30",
"kind": "公开代码材料",
"summary": "公开仓库包含源码目录、配置、启动脚本和部署说明,官方站点可到达所属组织。",
"limitation": "未执行代码或复现性能;榜单成绩为维护方声明,star和fork不代表客户或收入。"
}
],
"reviews": [
+1 -1
View File
@@ -1,5 +1,5 @@
{
"sourceVersion": "2026-10-30-global-1",
"sourceVersion": "2026-10-30-technical-1",
"asOf": "2026-10-30",
"metric": "platform-mrr-usd",
"sampleUnit": "product-or-business",