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
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ InsideGov 是一个面向政企互动研究与政策演示的多智能体实验
2. 政策兑现与承诺可信度;
3. 产业补贴、企业进入与产能演化。

核心世界现已继续演化到**城市模仿、企业退出与政府救助**:竞争城市会观察成功项目并自主选择模仿策略,需求冲击后困难企业进入跨部门救助会商,可严格比较市场退出、无条件救助和附条件救助的就业—财政—效率权衡。见 [docs/DYNAMIC_COMPETITION.md](docs/DYNAMIC_COMPETITION.md)。

另有一个**独立的人才对接场景**(需求 ↔ 政策 ↔ 能力映射),把「沟通协商机制」建成可运行的 2x2 反事实实验:语言模式(官话/人话)× 中介平台(关/开)。见 [docs/TALENT_SCENARIO.md](docs/TALENT_SCENARIO.md)。

再有一个**政企协商机制实验室**(双边协商与共同问题建构):企业带着私有信息(真实需求 ≠ 第一轮表达)、政府带着有限认知进入协商,七种协商机制(自由/政策匹配/澄清优先/复述确认/约束先行/多方案/分阶段承诺)在完全相同的初始世界上反事实对比,产出帕累托前沿与 H1-H7 假设检验。见 [docs/NEGOTIATION_LAB.md](docs/NEGOTIATION_LAB.md)。

低质项目识别已升级为**无答案标签的证据型尽调实验**:政府 Agent 自主选择调查顺序和最终处置,只能观察企业材料与独立核验证据;隐藏的多维项目状态仅用于生成证据和事后结算。前端可比较五种程序、多 seed、误签/误伤和阈值曲线。见 [docs/DUE_DILIGENCE_RESEARCH.md](docs/DUE_DILIGENCE_RESEARCH.md)。

## 设计原则

- **LLM 负责想,模拟器负责算**:认知策略可替换,财政、土地、合同、项目、产能等状态由确定性规则更新。
Expand All @@ -20,7 +24,7 @@ InsideGov 是一个面向政企互动研究与政策演示的多智能体实验
- **实验必须可复现**:种子、配置、干预和事件日志构成完整实验记录。
- **seed 生成世界,不是只生成噪声**:种子同时决定城市财政/产业禀赋、企业私有偏好、部门底线、信用先验和项目扰动。
- **三个场景共用一个世界**:招商、履约和产业演化不是三套脚本,而是一条因果链。
- **组织而非个人是行动者**:不同部门拥有不同动作集并自主决定行动或等待;议程、会前沟通、联盟、风险政策、程序转换和行动顺序可以变化,财政、合同与生产边界仍保持刚性。
- **组织而非个人是行动者**:不同部门拥有不同标准行为模板、职责能力、信息权限和禁止事项;Agent 可提出目录外行动并自主决定行动或等待,财政、合同与生产边界仍保持刚性。

## 快速开始

Expand Down Expand Up @@ -89,7 +93,7 @@ tests/ 可复现性和关键约束测试
## 当前能力

- 三座异质城市、招商局—财政局—市领导—企业董事会可执行 Agent 与供应商网络;
- **组织行动内核**:招商、财政、司法审查、园区、市领导、企业与产业基金拥有角色专属动作集;各部门自主争夺有限注意力,正式程序可启动、暂停、退回、恢复或重新议程化,企业可在任意轮次接受、还价、退出、等待或选址;前端完整回放模型选择、规则门与行动后复盘。
- **组织行动内核**:招商、财政、司法审查、园区、市领导、企业与产业基金拥有角色专属标准行为模板,但模板不是行动上限;开放行动经能力、禁止事项和执行原语编译后进入世界。各部门自主争夺有限注意力,正式程序可启动、暂停、退回、恢复或重新议程化。
- **组织自主学习闭环**:组织保存跨季度计划树并比较候选策略;可提出目录外行动并接受职责和资源审查;上级政策、会议、竞争、财政、舆情和领导更替形成内生机会窗口;否决、协调和企业回应持续更新信任、策略偏好、组织惯例与可迁移教训。
- Agent 私有观察、记忆检索、结构化行动、事后复盘与财政否决协调;
- 多维招商政策包和企业异质偏好;
Expand All @@ -104,11 +108,11 @@ tests/ 可复现性和关键约束测试
- **P2 产品闭环**:历史季度复制、自然语言干预人工确认、证据约束的 Agent 访谈、HTML/JSON 自动报告、文本材料参数抽取与溯源。
- **真实案例校准**:合肥—蔚来公开参数卡,本级财政与三类联合产业基金分账结算,以及15%—35%财政空间敏感性。
- **人才对接场景**:企业需求向量化表达、政策工具包与语言模式(官话/人话)设计、人才解读协商(理解度/信任/覆盖度)、可选平台翻译撮合、合同分期兑现与知识外溢,以及 2x2 反事实矩阵。
- **政企协商机制实验室**:企业双层需求(真实/表达错位)与政府信念分离、七种协商机制严格反事实对比、理解差距与语义/激励对齐度量、低质项目签约前识别(veto)、分阶段履约结算,以及多种子矩阵(均值/方差 + 帕累托前沿 + H1-H7 假设检验 + 语言探针)。
- **政企协商机制实验室**:企业双层需求与政府信念分离、七种协商机制反事实对比;低质项目模块不读取答案标签,由资金/技术/市场/治理/交付证据驱动多 Agent 自主尽调,并报告假阳性、假阴性、Brier、阈值敏感性和分阶段承诺结果。

## 产品文档

完整的产品定位、目标用户、端到端流程、世界与 Agent 设计、功能需求、验收标准和版本路线见 [docs/PRODUCT.md](docs/PRODUCT.md)。组织行动的现实依据与编码边界见 [docs/research/organizational-behavior-evidence.md](docs/research/organizational-behavior-evidence.md),计划、开放行动、机会窗口和学习机制见 [docs/research/organization-autonomy-upgrade.md](docs/research/organization-autonomy-upgrade.md)。系统实现另见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md),研究机制与结论边界见 [docs/MODEL.md](docs/MODEL.md)。人才对接场景的设计理念、机制量化与实验结果见 [docs/TALENT_SCENARIO.md](docs/TALENT_SCENARIO.md);政企协商机制实验室的七机制设计、对齐度量、H1-H7 假设与帕累托结论见 [docs/NEGOTIATION_LAB.md](docs/NEGOTIATION_LAB.md)。
完整的产品定位、目标用户、端到端流程、世界与 Agent 设计、功能需求、验收标准和版本路线见 [docs/PRODUCT.md](docs/PRODUCT.md)。组织行动的现实依据与编码边界见 [docs/research/organizational-behavior-evidence.md](docs/research/organizational-behavior-evidence.md),计划、开放行动、机会窗口和学习机制见 [docs/research/organization-autonomy-upgrade.md](docs/research/organization-autonomy-upgrade.md)。系统实现另见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md),研究机制与结论边界见 [docs/MODEL.md](docs/MODEL.md)。人才对接场景见 [docs/TALENT_SCENARIO.md](docs/TALENT_SCENARIO.md);政企协商机制见 [docs/NEGOTIATION_LAB.md](docs/NEGOTIATION_LAB.md);无答案标签尽调研究见 [docs/DUE_DILIGENCE_RESEARCH.md](docs/DUE_DILIGENCE_RESEARCH.md)。

## 研究边界

Expand Down
109 changes: 109 additions & 0 deletions apps/web/app/components/DueDiligenceLab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"use client";

import { useEffect, useMemo, useState } from "react";
import {
api,
type DueDiligenceCase,
type DueDiligenceReport,
} from "../lib/api";

const decisions: Record<string, string> = {
approve: "正式推进",
conditional_pilot: "可逆试点",
defer: "补证后再议",
reject: "签约前否决",
};
const dimensionNames: Record<string, string> = {
financing: "资金闭合",
technology: "技术成熟",
market: "市场验证",
governance: "治理信用",
execution: "交付能力",
};

export function DueDiligenceLab() {
const [report, setReport] = useState<DueDiligenceReport | null>(null);
const [program, setProgram] = useState("adaptive_staged");
const [seed, setSeed] = useState(3);
const [firmId, setFirmId] = useState("firm_risky");
const [busy, setBusy] = useState(true);
const [error, setError] = useState("");

useEffect(() => {
let alive = true;
void api.dueDiligence()
.then((value) => {
if (!alive) return;
setReport(value);
setSeed(value.configuration.seeds[0]);
})
.catch((caught) => alive && setError(String(caught)))
.finally(() => alive && setBusy(false));
return () => { alive = false; };
}, []);

const run = useMemo(
() => report?.program_runs.find((item) => item.program === program && item.seed === seed),
[program, report, seed],
);
const current = useMemo(
() => run?.cases.find((item) => item.firm_id === firmId) ?? run?.cases[0],
[firmId, run],
);
const contrast = useMemo(() => {
if (!report) return [];
return ["light_screen", "independent_verification", "adaptive_staged"].map((id) => {
const row = report.program_runs.find((item) => item.program === id && item.seed === seed);
return row?.cases.find((item) => item.firm_id === "firm_risky");
}).filter((item): item is DueDiligenceCase => Boolean(item));
}, [report, seed]);

if (busy) return <section className="diligence-lab loading"><small>EVIDENCE-GATED DUE DILIGENCE</small><h2>正在运行 5 种程序 × 8 个随机种子…</h2></section>;
if (error || !report) return <section className="diligence-lab"><h2>研究矩阵未完成</h2><p className="error-text">{error}</p></section>;

return (
<section className="diligence-lab">
<header>
<div><small>UNKNOWN FIRM · NO ORACLE LABEL</small><h2>面对没有答案标签的新企业,什么程序更有识别力?</h2><p>{report.research_question}</p></div>
<span>Agent 看不到真实质量 ✓</span>
</header>

<div className="diligence-programs">
{report.program_summary.map((item) => (
<button key={item.id} className={program === item.id ? "active" : ""} onClick={() => setProgram(item.id)}>
<small>{item.runs} SEEDS</small><strong>{item.name}</strong><p>{item.description}</p>
<div><span>识别失败项目 <b>{pct(item.metrics.recall.mean)}</b></span><span>不误伤好项目 <b>{pct(item.metrics.specificity.mean)}</b></span><span>尽调成本 <b>{item.metrics.diligence_cost.mean.toFixed(1)}</b></span></div>
</button>
))}
</div>

<section className="diligence-contrast">
<header><h3>同一企业、同一 Seed,只改变证据程序</h3><select value={seed} onChange={(event) => setSeed(Number(event.target.value))}>{report.configuration.seeds.map((value) => <option key={value}>{value}</option>)}</select></header>
<div>
{contrast.map((item) => <article key={item.program}><small>{report.program_summary.find((row) => row.id === item.program)?.name}</small><strong className={item.decision}>{decisions[item.decision]}</strong><p>事前风险 {pct(item.estimated_failure_probability)} · 不确定性 {pct(item.uncertainty)}</p><span>{item.evidence.length} 项证据 / {item.elapsed_days} 天 / 成本 {item.diligence_cost.toFixed(2)}</span></article>)}
</div>
</section>

<div className="diligence-detail">
<section>
<header><h3>证据链</h3><select value={current?.firm_id ?? ""} onChange={(event) => setFirmId(event.target.value)}>{run?.cases.map((item) => <option value={item.firm_id} key={item.firm_id}>{item.firm_name}</option>)}</select></header>
{current?.evidence.map((item) => (
<article key={item.id}>
<i>R{item.round}</i><div><small>{item.requested_by} · {dimensionNames[item.dimension]}</small><strong>{item.source_type}</strong><p>{item.summary}</p></div><span>可靠度<b>{pct(item.reliability)}</b></span><span>材料主张<b>{pct(item.claim_value)}</b></span><span>核验观察<b>{pct(item.observed_quality)}</b></span>
</article>
))}
</section>
<section>
<h3>Agent 自主性与规则边界</h3>
<ol>{current?.agent_turns.map((turn, index) => <li key={index}><i>{index + 1}</i><span><b>{String(turn.actor_id)}</b>{String(turn.action_id)}<small>{String(turn.rationale ?? "基于风险与信息价值选择下一步")}</small></span></li>)}</ol>
{current && <div className="diligence-verdict"><small>FINAL DECISION</small><strong>{decisions[current.decision]}</strong><p>规则引擎没有替 Agent 判断项目好坏;只负责证据可见性、权限、硬红线和事后结算。</p><code>decision_used_hidden_label = {String(current.decision_used_hidden_label)}</code></div>}
</section>
</div>

<section className="threshold-panel"><h3>阈值不是答案:它改变误放与误伤的取舍</h3><div>{report.threshold_curve.map((item) => <article key={item.threshold}><b>{item.threshold.toFixed(2)}</b><span className="catch" style={{height: `${item.true_positive_rate * 100}%`}} /><span className="hurt" style={{height: `${item.false_positive_rate * 100}%`}} /><small>抓到 {pct(item.true_positive_rate)}<br/>误伤 {pct(item.false_positive_rate)}</small></article>)}</div></section>
<p className="boundary">{report.interpretation_boundary}</p>
</section>
);
}

function pct(value: number) { return `${(value * 100).toFixed(0)}%`; }
195 changes: 195 additions & 0 deletions apps/web/app/components/DynamicCompetitionLab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
"use client";

import { useEffect, useMemo, useState } from "react";
import {
api,
type DynamicCompetitionReport,
type DynamicRescueDecision,
} from "../lib/api";

const decisionNames: Record<string, string> = {
market_exit: "市场退出",
unconditional_rescue: "无条件救助",
conditional_rescue: "附条件救助",
unconditional: "无条件救助",
conditional: "附条件救助",
reject_rescue: "拒绝救助",
aggressive_imitation: "激进模仿",
targeted_imitation: "定向模仿",
blocked: "被硬约束拦截",
};

export function DynamicCompetitionLab() {
const [report, setReport] = useState<DynamicCompetitionReport | null>(null);
const [selected, setSelected] = useState("conditional");
const [busy, setBusy] = useState(true);
const [error, setError] = useState("");

async function run() {
setBusy(true);
setError("");
try {
setReport(await api.dynamicCompetition());
} catch (caught) {
setError(String(caught));
} finally {
setBusy(false);
}
}

useEffect(() => {
let alive = true;
void api
.dynamicCompetition()
.then((result) => {
if (alive) setReport(result);
})
.catch((caught) => {
if (alive) setError(String(caught));
})
.finally(() => {
if (alive) setBusy(false);
});
return () => {
alive = false;
};
}, []);

const current = useMemo(
() => report?.variants.find((item) => item.id === selected),
[report, selected],
);
const sampleRescue = current?.rescue_decisions.find(
(item) => item.decision !== "reject_rescue",
) ?? current?.rescue_decisions[0];

if (busy)
return (
<section className="dynamic-lab loading">
<small>DYNAMIC COMPETITION · SAME SEED</small>
<h2>正在运行三种退出与救助制度…</h2>
<p>共同随机种子、共同城市模仿、共同 Q10 需求冲击,仅改变救助制度。</p>
</section>
);
if (error || !report)
return (
<section className="dynamic-lab">
<small>DYNAMIC COMPETITION</small>
<h2>实验未完成</h2>
<p className="error-text">{error}</p>
<button onClick={() => void run()}>重新运行</button>
</section>
);

return (
<section className="dynamic-lab">
<header>
<div>
<small>DYNAMIC COMPETITION · SEED {report.seed}</small>
<h2>城市模仿 → 产能过剩 → 企业退出与政府救助</h2>
<p>
Q10 需求下降 {Math.abs(report.common_conditions.demand_shock * 100).toFixed(0)}%,
三个世界只改变救助制度。
</p>
</div>
<span>24 季度真实规则推演</span>
</header>

<ol className="causal-ribbon">
{report.causal_chain.map((item, index) => (
<li key={item}>
<i>{index + 1}</i>
<span>{item}</span>
</li>
))}
</ol>

<div className="policy-cards">
{report.variants.map((variant) => (
<button
key={variant.id}
className={variant.id === selected ? "active" : ""}
onClick={() => setSelected(variant.id)}
>
<small>制度 {variant.id === "market_exit" ? "A" : variant.id === "unconditional" ? "B" : "C"}</small>
<h3>{variant.name}</h3>
<p>{variant.description}</p>
<div>
<span>
就业 <b>{variant.final.total_employment.toLocaleString()}</b>
</span>
<span>
退出 <b>{variant.final.exited_firms}</b>
</span>
<span>
僵尸 <b>{variant.final.zombie_firms}</b>
</span>
<span>
救助 <b>{variant.final.rescue_spending.toFixed(1)}亿</b>
</span>
</div>
</button>
))}
</div>

{current && (
<div className="dynamic-evidence">
<section>
<h3>{current.name}:季度演化</h3>
<div className="trajectory-chart">
{current.trajectory.map((item) => (
<div key={item.quarter} title={`Q${item.quarter} 利用率 ${(item.utilization * 100).toFixed(0)}%`}>
<i style={{ height: `${Math.max(3, item.utilization * 100)}%` }} />
<span>Q{item.quarter}</span>
</div>
))}
</div>
<div className="evolution-stats">
<span>模仿项目 <b>{current.final.imitation_projects}</b></span>
<span>模仿产能 <b>{current.final.imitation_capacity.toFixed(1)}</b></span>
<span>期末总产能 <b>{current.final.capacity.toFixed(1)}</b></span>
<span>期末利用率 <b>{(current.final.utilization * 100).toFixed(1)}%</b></span>
</div>
</section>
<section>
<h3>一次真实救助会商</h3>
{sampleRescue ? <RescueMeeting decision={sampleRescue} /> : <p>该制度下没有发生救助会商。</p>}
</section>
</div>
)}

<div className="dynamic-conclusion">
{Object.entries(report.headline_comparison).map(([key, value]) => (
<p key={key}><b>{decisionNames[key] ?? key}</b>{value}</p>
))}
</div>
<p className="boundary">{report.interpretation_boundary}</p>
</section>
);
}

function RescueMeeting({ decision }: { decision: DynamicRescueDecision }) {
return (
<div className="rescue-meeting">
<div className="meeting-limit">
<span>企业申请 <b>{decision.requested_amount.toFixed(1)} 亿</b></span>
<span>财政硬上限 <b>{decision.finance_limit.toFixed(1)} 亿</b></span>
<span>最终执行 <b>{decision.approved_amount.toFixed(1)} 亿</b></span>
</div>
{decision.turns.map((turn, index) => (
<article key={`${turn.actor_id}-${index}`}>
<small>{turn.actor_id}</small>
<strong>{decisionNames[turn.act ?? ""] ?? turn.act}</strong>
<p>{turn.summary}</p>
</article>
))}
<div className="rule-settlement">
<small>RULE ENGINE SETTLEMENT</small>
<p>
产能 {decision.capacity_before.toFixed(1)} → {decision.capacity_after.toFixed(1)};
岗位 {decision.jobs_before} → {decision.jobs_after}。
</p>
</div>
</div>
);
}
Loading
Loading