diff --git a/CHANGELOG.md b/CHANGELOG.md index 9528b02..af9e937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ 本项目遵循语义化版本。日期使用北京时间。 +## [0.6.0] - 2026-08-13 + +### Added + +- 组织作为行动者的自主行为内核,为招商、财政、园区、市领导、司法审查、企业和产业基金配置角色专属行动空间; +- 正式科层、非正式政治动力学与混合三种过程模式,以及议程启动、暂停、退回、恢复和重新议程化; +- 跨季度计划树、目录外候选行动审查、内生政治机会窗口和长期组织学习; +- 企业自主接受、还价、等待、选址与退出决策,以及模型建议、规则修正和最终执行的审计轨迹; +- 合肥—蔚来组织行为校准与留出集验证,支持多模式、多随机种子比较和可复现报告。 + +### Changed + +- Web 回放界面增加组织计划、行动时机、程序状态、机会窗口、权限检查、组织学习和复盘展示; +- API、序列化结构和 TypeScript 类型扩展到组织行为与校准结果; +- 项目、Web 和健康检查版本升级到 0.6.0。 + +### Fixed + +- 阻止正式科层模式泄漏仅适用于非正式过程的候选行动; +- 将财政、法律、合同和生产约束保留为确定性硬边界,避免自主行动越权修改世界状态。 + ## [0.5.0] - 2026-08-13 ### Added diff --git a/README.md b/README.md index b2c19d0..0543742 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ InsideGov 是一个面向政企互动研究与政策演示的多智能体实验 - **实验必须可复现**:种子、配置、干预和事件日志构成完整实验记录。 - **seed 生成世界,不是只生成噪声**:种子同时决定城市财政/产业禀赋、企业私有偏好、部门底线、信用先验和项目扰动。 - **三个场景共用一个世界**:招商、履约和产业演化不是三套脚本,而是一条因果链。 +- **组织而非个人是行动者**:不同部门拥有不同动作集并自主决定行动或等待;议程、会前沟通、联盟、风险政策、程序转换和行动顺序可以变化,财政、合同与生产边界仍保持刚性。 ## 快速开始 @@ -32,6 +33,14 @@ uv run insidegov demo --seed 42 --fiscal-multiplier 0.5 uv run insidegov case-hefei-nio --seed 42 --quarters 16 uv run insidegov case-hefei-nio-sensitivity --seed 42 --quarters 16 uv run insidegov run --quarters 16 +# 同一 seed 比较正式科层、非正式动力学与混合过程 +uv run insidegov organization-compare --seed 42 --quarters 16 + +# 真实案例的组织行为校准:校准集选模式,后续节点做留出验证 +uv run insidegov case-hefei-nio-org-calibration \ + --seeds 11,23,42,57,89 \ + --modes formal,informal,hybrid \ + --quarters 16 uv run insidegov compare uv run insidegov matrix --no-llm # 只跑确定性 + Flash(检查点保存在 .insidegov) @@ -80,6 +89,8 @@ tests/ 可复现性和关键约束测试 ## 当前能力 - 三座异质城市、招商局—财政局—市领导—企业董事会可执行 Agent 与供应商网络; +- **组织行动内核**:招商、财政、司法审查、园区、市领导、企业与产业基金拥有角色专属动作集;各部门自主争夺有限注意力,正式程序可启动、暂停、退回、恢复或重新议程化,企业可在任意轮次接受、还价、退出、等待或选址;前端完整回放模型选择、规则门与行动后复盘。 +- **组织自主学习闭环**:组织保存跨季度计划树并比较候选策略;可提出目录外行动并接受职责和资源审查;上级政策、会议、竞争、财政、舆情和领导更替形成内生机会窗口;否决、协调和企业回应持续更新信任、策略偏好、组织惯例与可迁移教训。 - Agent 私有观察、记忆检索、结构化行动、事后复盘与财政否决协调; - 多维招商政策包和企业异质偏好; - 有条件承诺、财政支付、延期与信誉更新; @@ -97,7 +108,7 @@ tests/ 可复现性和关键约束测试 ## 产品文档 -完整的产品定位、目标用户、端到端流程、世界与 Agent 设计、功能需求、验收标准和版本路线见 [docs/PRODUCT.md](docs/PRODUCT.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);政企协商机制实验室的七机制设计、对齐度量、H1-H7 假设与帕累托结论见 [docs/NEGOTIATION_LAB.md](docs/NEGOTIATION_LAB.md)。 ## 研究边界 diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 364f015..ceb47ad 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -21,5 +21,7 @@ button:disabled { opacity:.5;cursor:not-allowed!important; } .trace-drawer{margin:0 12px 12px;max-width:1696px;margin-inline:auto}.trace-grid{padding:9px}.trace-grid article{display:grid;grid-template-columns:35px 135px 1.1fr 1.4fr 85px 120px;align-items:center;gap:12px;padding:11px;border-bottom:1px solid #203438}.trace-grid small{font-size:8px;color:var(--muted)}.trace-grid h3{font-size:11px;margin:3px 0}.trace-grid p{font-size:9px;margin:3px 0;color:#9fb0b4}.trace-no{font:11px var(--font-geist-mono);color:#496167}.trace-grid>article>strong{font:13px var(--font-geist-mono);color:var(--amber)}.trace-grid button{border:0;background:transparent;color:var(--cyan);font-size:9px;cursor:pointer}footer{height:42px;border-top:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;padding:0 22px;color:#50676c;font-size:9px;max-width:1720px;margin:auto} .empty-chart{width:100%;align-self:center;text-align:center;color:var(--muted);font-size:11px}.modal-backdrop{position:fixed;inset:0;z-index:60;background:#020708d9;display:grid;place-items:center;padding:40px}.modal{width:min(760px,90vw);max-height:82vh;padding:28px;position:relative;overflow:auto}.modal>small{color:var(--cyan);font:9px var(--font-geist-mono);letter-spacing:.16em}.modal>h2{margin:7px 0 18px}.modal>p{color:#a0b0b4;line-height:1.7;font-size:12px}.modal-close{position:absolute;right:14px;top:12px;background:transparent;border:0;color:var(--muted);font-size:24px;cursor:pointer}.modal-list{display:grid;gap:8px}.modal-list article{background:#0a1517;border:1px solid var(--line);padding:13px;display:grid;grid-template-columns:repeat(3,1fr);gap:7px}.modal-list article strong,.modal-list article p{grid-column:1/-1}.modal-list article span{font-size:10px;color:#a7b9bd}.modal-list article p{font-size:10px;color:var(--muted);margin:3px 0}.settings{display:grid;grid-template-columns:1fr 1fr;gap:12px}.settings label{display:grid;gap:7px;color:var(--muted);font-size:10px}.settings select{background:#0b181a;border:1px solid var(--line);color:var(--ink);padding:10px}.hint{color:var(--amber)!important;font-size:10px!important} .tranches{grid-column:1/-1;display:flex;flex-wrap:wrap;gap:5px;border-top:1px dashed var(--line);padding-top:8px}.tranches i{font-style:normal;font-size:8px;color:var(--cyan);background:#102427;padding:5px 7px} +.org-state{display:grid;grid-template-columns:repeat(4,1fr);gap:7px;margin:12px 0}.org-state span{display:grid;gap:5px;background:#091517;border:1px solid var(--line);padding:10px;color:var(--muted);font-size:9px}.org-state b{color:var(--amber);font:13px var(--font-geist-mono)}.org-timeline{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.org-timeline article{border:1px solid var(--line);background:#0a1618;padding:12px;display:grid;gap:7px;margin:0}.org-timeline article.formal{border-top:2px solid var(--cyan)}.org-timeline article.informal{border-top:2px solid var(--amber)}.org-timeline article.control{border-top:2px solid var(--red)}.org-timeline header{display:flex;align-items:center;gap:7px}.org-timeline time{color:var(--cyan);font:9px var(--font-geist-mono)}.org-timeline header strong{font-size:9px}.org-timeline header em{margin-left:auto;font-style:normal;color:var(--muted);font-size:8px}.org-timeline h3{font-size:12px;margin:2px 0}.org-timeline p{font-size:9px;color:var(--muted);margin:0;line-height:1.6}.org-timeline>article>small{font-size:8px;color:#71878c}.org-timeline footer{height:auto;border:0;padding:7px 0 0;display:flex;justify-content:flex-start;gap:5px;flex-wrap:wrap}.org-timeline footer a{color:var(--cyan);font-size:8px;text-decoration:none;border-bottom:1px dotted var(--cyan)} .demo-banner{max-width:1696px;margin:12px auto 0;border:1px solid #4f6653;background:linear-gradient(100deg,#142723,#0d1c1d 70%);min-height:72px;padding:12px 18px;display:grid;grid-template-columns:1fr auto auto;align-items:center;gap:10px}.demo-banner div{display:grid;gap:3px}.demo-banner small{color:var(--green);font:9px var(--font-geist-mono);letter-spacing:.14em}.demo-banner strong{font-size:14px}.demo-banner span{color:var(--muted);font-size:9px}.demo-banner button{border:1px solid #42675a;background:#112723;color:var(--green);padding:10px 15px;cursor:pointer}.demo-banner button:first-of-type{background:var(--green);color:#07130f;font-weight:700}.modal-wide{width:min(1160px,94vw);max-height:90vh}.demo-intro{max-width:720px;margin:auto}.demo-intro ul{padding-left:20px;color:#9eb0b5;font-size:11px;line-height:1.8}.error-text{color:#ff9f97!important}.demo-replay{display:grid;gap:15px}.replay-proof{display:flex;align-items:center;gap:8px;flex-wrap:wrap;border:1px solid #31554b;background:#0b1b18;padding:10px}.replay-proof span{color:var(--green);font-weight:700;font-size:10px}.replay-proof code{font-size:9px;color:#9fc0b4;background:#071310;padding:5px 7px}.replay-proof b{margin-left:auto;color:var(--muted);font-size:9px}.replay-steps{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(6,1fr);gap:5px}.replay-steps button{width:100%;height:58px;border:1px solid var(--line);background:#0a1618;color:#71868b;display:flex;align-items:center;text-align:left;gap:7px;padding:8px;cursor:pointer}.replay-steps i{width:24px;height:24px;border:1px solid #34494e;border-radius:50%;display:grid;place-items:center;font-style:normal;font-size:9px}.replay-steps span{font-size:9px}.replay-steps small{display:block;margin-top:3px;color:#52676c}.replay-steps .active button{border-color:var(--cyan);color:var(--cyan);background:#11272a}.replay-steps .done button{border-color:#31544a;color:var(--green)}.replay-stage{min-height:440px;background:#091416;border:1px solid var(--line);padding:18px;overflow:auto}.replay-stage>header{border-bottom:1px solid var(--line);margin-bottom:14px}.replay-stage>header small{font:9px var(--font-geist-mono);color:var(--cyan)}.replay-stage>header h3{margin:5px 0 13px}.replay-grid,.fund-grid,.comparison-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:9px}.replay-grid article,.fund-grid article,.comparison-cards article{border:1px solid var(--line);background:#0d1c1f;padding:14px}.replay-grid strong,.fund-grid strong,.comparison-cards strong{display:block;color:var(--amber);font-size:14px}.replay-grid p,.fund-grid p{color:var(--muted);font-size:10px;line-height:1.7}.evidence-stack{display:grid;gap:8px}.evidence-stack p{margin:0;border-left:2px solid var(--cyan);background:#0d1b1e;padding:12px;font-size:11px;line-height:1.7;color:#9eafb3}.offer-chain{display:grid;grid-template-columns:1fr 24px 1fr 24px 1fr;gap:7px;align-items:center}.offer-chain>article{border:1px solid var(--line);background:#0d1d20;padding:12px;display:grid;gap:6px}.offer-chain>article small{color:var(--cyan)}.offer-chain>article strong{font-size:12px}.offer-chain>article span{font-size:9px;color:var(--muted)}.offer-chain>b{text-align:center;color:var(--amber)}.private-audit{margin-top:14px}.private-audit h4,.sensitivity-table h4{font-size:11px;color:var(--amber)}.private-audit details{border:1px solid var(--line);background:#0b181a;margin:6px 0;padding:10px}.private-audit summary{cursor:pointer;color:#a8babd;font-size:10px}.private-audit p{font-size:9px;color:var(--muted);overflow-wrap:anywhere}.fund-grid article small{display:block;color:var(--cyan);font-size:8px;margin-bottom:7px}.fund-grid article strong{color:var(--ink);font-size:11px}.promise-table{margin-top:12px;border:1px solid var(--line)}.promise-table>div{display:grid;grid-template-columns:1fr 100px 1.4fr 80px;gap:8px;padding:8px 11px;border-bottom:1px solid var(--line);align-items:center;font-size:9px}.promise-table b{color:var(--amber)}.promise-table em{font-style:normal;text-transform:uppercase;color:var(--green)}.event-chain{display:grid;grid-template-columns:repeat(2,1fr);gap:7px}.event-chain article{display:grid;grid-template-columns:35px 1fr;gap:9px;border:1px solid var(--line);background:#0c191b;padding:10px}.event-chain time{color:var(--cyan);font:9px var(--font-geist-mono)}.event-chain strong{font-size:10px}.event-chain p{font-size:9px;color:var(--muted);margin:5px 0 0}.comparison-cards article small{display:block;color:var(--muted);font-size:9px}.comparison-cards article strong{font:19px var(--font-geist-mono);margin:8px 0}.comparison-cards article span{font-size:10px}.comparison-cards .warning{border-color:#6d5932}.boundary{border-left:2px solid var(--amber);background:#1c1911;color:#b8aa8e;padding:11px;font-size:10px;line-height:1.7}.sensitivity-table{margin-top:14px}.table-row{display:grid;grid-template-columns:repeat(5,1fr);border-bottom:1px solid var(--line);padding:7px 10px;font-size:9px}.table-row.header{color:var(--muted);background:#102024}.table-row b{color:var(--amber)}.replay-actions{display:flex;gap:7px;height:auto;padding:0;border:0;max-width:none}.replay-actions button,.replay-actions a{border:1px solid #355056;background:#0c191b;color:#9fb2b6;padding:9px 12px;cursor:pointer;font-size:9px}.replay-actions .primary{margin-left:auto;background:var(--cyan);border-color:var(--cyan);color:#071315;font-weight:700}.report-center{display:grid;gap:12px}.report-center>small{color:var(--cyan);font:9px var(--font-geist-mono);letter-spacing:.16em}.report-center>h2{margin:0}.report-toolbar{display:grid;grid-template-columns:1fr auto auto;gap:10px;align-items:end}.report-toolbar label{display:grid;gap:5px;color:var(--muted);font-size:9px}.report-toolbar select{background:#091517;border:1px solid var(--line);color:var(--ink);padding:9px}.report-toolbar div{display:flex}.report-toolbar button,.report-toolbar a{border:1px solid var(--line);background:#0b181a;color:var(--muted);padding:9px 11px;font-size:9px;cursor:pointer}.report-toolbar button.active{border-color:var(--cyan);color:var(--cyan)}.report-meta{display:flex;gap:10px;color:var(--muted);font-size:9px}.report-meta span{border:1px solid var(--line);padding:5px 8px}.summary-chart{display:grid;gap:6px}.summary-chart button{display:grid;grid-template-columns:150px 1fr 105px 120px;align-items:center;gap:10px;border:1px solid var(--line);background:#0b181a;color:var(--ink);padding:9px;text-align:left;cursor:pointer}.summary-chart button>span{font-size:10px}.summary-chart i{height:8px;background:#172b2f}.summary-chart i b{display:block;height:100%;background:linear-gradient(90deg,var(--cyan),var(--green))}.summary-chart strong{font:10px var(--font-geist-mono);color:var(--amber)}.summary-chart small{font-size:8px;color:var(--muted)}.seed-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:6px;max-height:180px;overflow:auto}.seed-grid button{display:grid;text-align:left;gap:3px;border:1px solid var(--line);background:#0b181a;color:var(--ink);padding:8px;cursor:pointer}.seed-grid button.active{border-color:var(--cyan);background:#11272a}.seed-grid button.failed{border-color:#693b37}.seed-grid strong{font-size:10px}.seed-grid span,.seed-grid small{font-size:8px;color:var(--muted)}.run-detail{border:1px solid #38565b;background:#0c1a1d;padding:12px}.run-detail header{display:flex;justify-content:space-between;align-items:center}.run-detail h3{margin:4px 0}.run-detail header span{font-size:9px;color:var(--green)}.run-detail>div{display:grid;grid-template-columns:repeat(6,1fr);gap:6px}.run-detail p{display:grid;color:var(--muted);font-size:8px;background:#091416;padding:7px}.run-detail p strong{color:var(--ink);font-size:11px;margin-top:4px}.run-detail>button{border:1px solid var(--cyan);background:#11282a;color:var(--cyan);padding:9px 12px;cursor:pointer}.failure-panel{border-top:1px solid var(--line);padding-top:10px}.failure-panel h3{font-size:11px}.failure-panel article{display:grid;grid-template-columns:180px 100px 1fr;gap:8px;font-size:9px;color:var(--muted);padding:7px;background:#191313;border-bottom:1px solid #472a27}.failure-panel article p{margin:0}.failure-panel>p{color:var(--muted);font-size:9px} @media(max-width:1250px){.dashboard-grid{grid-template-columns:270px 1fr 310px}.metric-card strong{font-size:20px}} +.org-plan-grid,.learning-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin:8px 0 16px}.org-plan-grid>article,.learning-grid>article,.opportunity-strip>article{border:1px solid var(--line);background:#091517;padding:11px;display:grid;gap:6px}.org-plan-grid header{display:flex;justify-content:space-between}.org-plan-grid header em{font-style:normal;color:var(--green);font-size:8px}.org-plan-grid p,.learning-grid p,.opportunity-strip p{font-size:9px;color:var(--muted);margin:2px 0;line-height:1.5}.org-plan-grid small,.learning-grid small,.opportunity-strip small,.opportunity-strip span{font-size:8px;color:#71878c}.org-plan-grid summary{cursor:pointer;color:var(--cyan);font-size:9px;margin:6px 0}.plan-nodes{display:grid;gap:4px}.plan-nodes p{border-left:2px solid #43585d;padding:5px 7px;background:#0c1b1d}.plan-nodes p.completed{border-color:var(--green)}.plan-nodes p.deviated{border-color:var(--red)}.plan-nodes p.pending{border-color:var(--amber)}.opportunity-strip{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin:8px 0 16px}.opportunity-strip article.active{border-top:2px solid var(--amber)}.learning-grid strong,.opportunity-strip strong{font-size:10px}.learning-grid span{font-size:8px;color:var(--amber)}.compact-list{margin-bottom:16px}.compact-list article{grid-template-columns:1fr 1fr}.org-timeline article.initiative{border-top:2px solid #9b8ad7}.org-timeline article.procedure{border-top:2px solid var(--green)} diff --git a/apps/web/app/lib/api.ts b/apps/web/app/lib/api.ts index ffc99ba..ca75922 100644 --- a/apps/web/app/lib/api.ts +++ b/apps/web/app/lib/api.ts @@ -1,4 +1,5 @@ -export const API_BASE = process.env.NEXT_PUBLIC_INSIDEGOV_API_URL ?? "http://localhost:8000"; +export const API_BASE = + process.env.NEXT_PUBLIC_INSIDEGOV_API_URL ?? "http://localhost:8000"; export type Offer = { subsidy: number; @@ -41,59 +42,232 @@ export type World = { quarter: number; phase: string; cities: Record; - firms: Record; - investment_funds: Record; + firms: Record< + string, + { id: string; name: string; operating: boolean; location: string | null } + >; + investment_funds: Record< + string, + { + id: string; + name: string; + city_id: string; + source_level: string; + available_capital: number; + committed_capital: number; + risk_tolerance: number; + due_diligence_threshold: number; + } + >; promises: Array<{ - id: string; item: string; amount: number; due_quarter: number; condition: string; - status: string; paid_amount: number; delayed_quarters: number; + id: string; + item: string; + amount: number; + due_quarter: number; + condition: string; + status: string; + paid_amount: number; + delayed_quarters: number; funding_source_id: string | null; }>; - events: Array<{ quarter: number; kind: string; title: string; detail: string; severity: string }>; + events: Array<{ + quarter: number; + kind: string; + title: string; + detail: string; + severity: string; + }>; traces: Array<{ - id: string; actor_id: string; action: string; evidence: string[]; - constraints: string[]; outcome: string; expected_effects: Record; + id: string; + actor_id: string; + action: string; + evidence: string[]; + constraints: string[]; + outcome: string; + expected_effects: Record; }>; action_audits: AgentActionAudit[]; + organization_actions: OrganizationAction[]; + organization_plans: OrganizationPlan[]; + open_action_proposals: OpenActionProposal[]; + opportunity_windows: OpportunityWindow[]; + organization_learning: Record; + organization_processes: Record< + string, + { + city_id: string; + agenda_priority: number; + procedural_completeness: number; + coalition_support: number; + legitimacy: number; + risk_posture: string; + finance_preconsulted: boolean; + legal_reviewed: boolean; + collective_deliberated: boolean; + pilot_authorized: boolean; + relationships: Record; + action_sequence: string[]; + formal_status: string; + return_count: number; + last_transition: string; + last_transition_quarter: number; + paused_reason: string | null; + attention_budget: number; + } + >; history: Metric[]; negotiations: Array<{ - id: string; quarter: number; city_id: string; proposal_cost: number; - finance_limit: number; finance_approved: boolean; concerns: string[]; - resolution: string; final_cost: number; policy_mode: string; - proposer_id: string; reviewer_id: string; coordinator_id: string; + id: string; + quarter: number; + city_id: string; + proposal_cost: number; + finance_limit: number; + finance_approved: boolean; + concerns: string[]; + resolution: string; + final_cost: number; + policy_mode: string; + proposer_id: string; + reviewer_id: string; + coordinator_id: string; proposal_tools: Record; finance_tool_limits: Record; final_tools: Record; - payment_schedule: Array<{ item: string; amount: number; due_offset: number; condition: string; funding_source_id?: string | null }>; - turns: Array<{ actor_id: string; act: string; summary: string; amount?: number; approved?: boolean }>; + payment_schedule: Array<{ + item: string; + amount: number; + due_offset: number; + condition: string; + funding_source_id?: string | null; + }>; + turns: Array<{ + actor_id: string; + act: string; + summary: string; + amount?: number; + approved?: boolean; + }>; }>; external_negotiations: Array<{ - id: string; quarter: number; city_id: string; firm_id: string; protocol: string; - stated_need: string; government_questions: string[]; + id: string; + quarter: number; + city_id: string; + firm_id: string; + protocol: string; + stated_need: string; + government_questions: string[]; disclosed_components: Record; - belief_before: Record; belief_after: Record; - belief_confidence: number; internal_negotiation_id: string; + belief_before: Record; + belief_after: Record; + belief_confidence: number; + internal_negotiation_id: string; government_offer: Record; enterprise_response: "accept" | "counter" | "terminate"; - counter_terms: Record; enterprise_rationale: string; - utility: number; minimum_utility: number; outcome: string; + counter_terms: Record; + enterprise_rationale: string; + utility: number; + minimum_utility: number; + outcome: string; }>; selected_city_id: string | null; + recruitment_status: string; + negotiation_round_limit: number; parent_id: string | null; policy_mode: string; + process_mode: "formal" | "informal" | "hybrid"; model_name: string | null; }; +export type OrganizationAction = { + id: string; + quarter: number; + city_id: string; + actor_id: string; + actor_role: string; + action_id: string; + action_name: string; + arena: "formal" | "informal" | "control" | "initiative" | "procedure"; + process_mode: string; + candidates: string[]; + observations: Record; + rationale: string; + effects: Record; + required: boolean; + authorized: boolean; + blocked_reason: string | null; + evidence_ids: string[]; + selection_provider: string; + selection_rationale: string; + fallback: boolean; + stage: string; + sequence: number; + decision: string; + target_actor_id: string | null; + urgency: number; + reflection: string; + plan_id: string | null; + plan_node_id: string | null; + deviation_reason: string | null; + open_action_proposal_id: string | null; +}; + +export type OrganizationPlan = { + id: string; + city_id: string; + actor_id: string; + created_quarter: number; + horizon_quarter: number; + objective: string; + alternatives: Array<{ + id: string; name: string; approach: string; benefits: string[]; risks: string[]; score: number; + }>; + selected_strategy: string; + nodes: Array<{ + id: string; title: string; action_id: string; earliest_quarter: number; latest_quarter: number; + preconditions: string[]; on_success: string | null; on_failure: string | null; + expected_effects: Record; status: string; executed_quarter: number | null; + actual_action_id: string | null; deviation_reason: string | null; + }>; + assumptions: string[]; + rationale: string; + status: string; + provider: string; + fallback: boolean; + review_history: Array>; +}; + +export type OpenActionProposal = { + id: string; quarter: number; city_id: string; actor_id: string; title: string; + mechanism: string; target_actor_id: string | null; + requested_effects: Record; resource_request: Record; + rationale: string; status: string; validation_reason: string; + executed_effects: Record; provider: string; fallback: boolean; +}; + +export type OpportunityWindow = { + id: string; city_id: string | null; kind: string; title: string; + start_quarter: number; end_quarter: number; magnitude: number; + source: string; trigger: string; effects: Record; + observed_by: string[]; status: string; applied: boolean; +}; + +export type OrganizationLearning = { + agent_id: string; trust_by_actor: Record; + firm_type_beliefs: Record; action_attempts: Record; + action_successes: Record; strategy_preferences: Record; + routines: Record; veto_count: number; successful_coordination_count: number; + leadership_generation: number; inherited_memory_ratio: number; + lessons: string[]; transferable_lessons: string[]; +}; + export type AgentActionAudit = { id: string; quarter: number; agent_id: string; action_type: string; observation: Record; - private_context_used: { redacted?: boolean; fields_used?: string[] } | Record; + private_context_used: + { redacted?: boolean; fields_used?: string[] } | Record; retrieved_memories: string[]; llm_suggestion: Record; rule_adjustment: Record; @@ -111,52 +285,105 @@ export type Capability = { llm_available: boolean; models: string[]; default_model: string; + process_modes?: string[]; }; export type InterventionPlan = { - id: string; source_text: string; effective_quarter: number; status: string; - assumptions: string[]; promise_priority: string | null; - changes: Array<{ target: string; operation: string; value: number; description: string }>; + id: string; + source_text: string; + effective_quarter: number; + status: string; + assumptions: string[]; + promise_priority: string | null; + changes: Array<{ + target: string; + operation: string; + value: number; + description: string; + }>; }; export type Interview = { - quarter: number; agent_id: string; question: string; answer: string; + quarter: number; + agent_id: string; + question: string; + answer: string; evidence_audit_ids: string[]; knowledge_labels: { - known_at_the_time: string[]; private_fields_used: string[]; - unknown_at_the_time: string[]; hindsight: string[]; hypothetical: boolean; + known_at_the_time: string[]; + private_fields_used: string[]; + unknown_at_the_time: string[]; + hindsight: string[]; + hypothetical: boolean; }; }; export type CandidateWorld = { - id: string; filename: string; content_excerpt: string; entities: string[]; - relations: Array<{ source: string; target: string; relation: string; evidence: string }>; - missing_fields: string[]; status: string; + id: string; + filename: string; + content_excerpt: string; + entities: string[]; + relations: Array<{ + source: string; + target: string; + relation: string; + evidence: string; + }>; + missing_fields: string[]; + status: string; parameters: Array<{ - id: string; target: string; evidence: string; suggested_value: number; - final_value: number | null; confidence: number; provenance: string; status: string; + id: string; + target: string; + evidence: string; + suggested_value: number; + final_value: number | null; + confidence: number; + provenance: string; + status: string; }>; }; export type DemoBundle = { - schema_version: string; generated_at: string; source: string; seed: number; + schema_version: string; + generated_at: string; + source: string; + seed: number; common_ancestor_quarter: number; - intervention: { quarter: number; target: string; operation: string; value: number }; - baseline: World; branch: World; - comparison: { baseline_world_id: string; branch_world_id: string; delta: Record }; + intervention: { + quarter: number; + target: string; + operation: string; + value: number; + }; + baseline: World; + branch: World; + comparison: { + baseline_world_id: string; + branch_world_id: string; + delta: Record; + }; replay: { - selected_city_id: string; selected_city_name: string; + selected_city_id: string; + selected_city_name: string; external_negotiation: World["external_negotiations"][number]; internal_negotiation: World["negotiations"][number]; synthetic_private_audit: Array<{ - audit_id: string; agent_id: string; private_context_used: Record; - observation: Record; llm_suggestion: Record; - rule_adjustment: Record; executed_action: Record; - rationale: string; reflection: string; provider: string; fallback: boolean; + audit_id: string; + agent_id: string; + private_context_used: Record; + observation: Record; + llm_suggestion: Record; + rule_adjustment: Record; + executed_action: Record; + rationale: string; + reflection: string; + provider: string; + fallback: boolean; }>; funding_partners: Array; promises: World["promises"]; - baseline_outcome: Metric; branch_outcome: Metric; + baseline_outcome: Metric; + branch_outcome: Metric; causal_events: World["events"]; steps: Array<{ id: string; title: string; quarter: number }>; }; @@ -164,46 +391,85 @@ export type DemoBundle = { }; export type HefeiSensitivity = { - case: string; seed: number; quarters: number; historical_equity_investment: number; - budget_shares: number[]; interpretation_boundary: string; + case: string; + seed: number; + quarters: number; + historical_equity_investment: number; + budget_shares: number[]; + interpretation_boundary: string; runs: Array<{ - available_budget_share: number; joint_investment: boolean; selected_city: string | null; - municipal_equity: number; external_equity: number; total_equity_support: number; - historical_equity_gap: number; fulfilled_promises: number; cluster_size: number; - total_employment: number; average_credibility: number; + available_budget_share: number; + joint_investment: boolean; + selected_city: string | null; + municipal_equity: number; + external_equity: number; + total_equity_support: number; + historical_equity_gap: number; + fulfilled_promises: number; + cluster_size: number; + total_employment: number; + average_credibility: number; }>; }; export type ExperimentReportSummary = { - id: string; title: string; kind: string; source: string; generated_at: string | null; - strategy_runs: number; ablation_runs: number; failures: number; + id: string; + title: string; + kind: string; + source: string; + generated_at: string | null; + strategy_runs: number; + ablation_runs: number; + failures: number; drilldown_available: boolean; }; export type ExperimentRun = { - strategy: string; seed: number; successful: boolean; selected_city: string | null; - fulfilled_promises: number; total_employment: number; total_committed_expenditure: number; - average_credibility: number; cluster_size: number; utilization: number; - world_id: string; world_available: boolean; + strategy: string; + seed: number; + successful: boolean; + selected_city: string | null; + fulfilled_promises: number; + total_employment: number; + total_committed_expenditure: number; + average_credibility: number; + cluster_size: number; + utilization: number; + world_id: string; + world_available: boolean; }; export type ExperimentMetricSummary = { - id?: string; name?: string; strategy?: string; mechanism?: string; - attempted?: number; successful?: number; success_rate?: number; + id?: string; + name?: string; + strategy?: string; + mechanism?: string; + attempted?: number; + successful?: number; + success_rate?: number; metrics?: Record; - employment?: number; employment_variance?: number; cluster?: number; - fulfilled_promises?: number; seeds?: number; + employment?: number; + employment_variance?: number; + cluster?: number; + fulfilled_promises?: number; + seeds?: number; }; export type ExperimentReport = { - report_id: string; generated_at?: string; + report_id: string; + generated_at?: string; report_summary: Partial; configuration: Record; strategy_summary: ExperimentMetricSummary[]; ablation_summary: ExperimentMetricSummary[]; strategy_runs: ExperimentRun[]; ablation_runs: ExperimentRun[]; - failure_cases: Array<{ strategy: string; seed: number; stage: string; reason: string }>; + failure_cases: Array<{ + strategy: string; + seed: number; + stage: string; + reason: string; + }>; llm_quality?: Array>; }; @@ -212,66 +478,127 @@ async function request(path: string, init?: RequestInit): Promise { ...init, headers: { "Content-Type": "application/json", ...init?.headers }, }); - if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); + if (!response.ok) + throw new Error(`${response.status} ${await response.text()}`); return response.json() as Promise; } export const api = { capabilities: () => request("/capabilities"), - listWorlds: () => request>>("/worlds"), + listWorlds: () => + request< + Array< + Pick + > + >("/worlds"), getWorld: (id: string) => request(`/worlds/${id}`), - createWorld: (seed: number, policyMode: string, modelName: string) => request("/worlds", { - method: "POST", - body: JSON.stringify({ seed, policy_mode: policyMode, model_name: policyMode === "llm" ? modelName : null }), - }), - step: (id: string, quarters = 1) => request(`/worlds/${id}/step`, { - method: "POST", body: JSON.stringify({ quarters }), - }), - branch: (id: string) => request(`/worlds/${id}/branches`, { method: "POST" }), - branchFromHistory: (id: string, quarter: number) => request(`/worlds/${id}/branches/from-history`, { - method: "POST", body: JSON.stringify({ quarter }), - }), - createPair: (id: string, quarter: number) => request<{ baseline: World; branch: World; quarter: number }>(`/worlds/${id}/counterfactual-pairs`, { - method: "POST", body: JSON.stringify({ quarter }), - }), - syncPair: (baselineId: string, branchId: string, quarters: number) => request<{ baseline: World; branch: World; comparison: { delta: Record } }>("/experiments/sync-worlds", { - method: "POST", body: JSON.stringify({ baseline_world_id: baselineId, branch_world_id: branchId, quarters }), - }), - intervene: (id: string, kind: string, target: string, value: number) => request<{ accepted: boolean }>(`/worlds/${id}/interventions`, { - method: "POST", body: JSON.stringify({ kind, target, value }), - }), + createWorld: ( + seed: number, + policyMode: string, + modelName: string, + processMode = "hybrid", + ) => + request("/worlds", { + method: "POST", + body: JSON.stringify({ + seed, + policy_mode: policyMode, + model_name: policyMode === "llm" ? modelName : null, + process_mode: processMode, + }), + }), + step: (id: string, quarters = 1) => + request(`/worlds/${id}/step`, { + method: "POST", + body: JSON.stringify({ quarters }), + }), + branch: (id: string) => + request(`/worlds/${id}/branches`, { method: "POST" }), + branchFromHistory: (id: string, quarter: number) => + request(`/worlds/${id}/branches/from-history`, { + method: "POST", + body: JSON.stringify({ quarter }), + }), + createPair: (id: string, quarter: number) => + request<{ baseline: World; branch: World; quarter: number }>( + `/worlds/${id}/counterfactual-pairs`, + { + method: "POST", + body: JSON.stringify({ quarter }), + }, + ), + syncPair: (baselineId: string, branchId: string, quarters: number) => + request<{ + baseline: World; + branch: World; + comparison: { delta: Record }; + }>("/experiments/sync-worlds", { + method: "POST", + body: JSON.stringify({ + baseline_world_id: baselineId, + branch_world_id: branchId, + quarters, + }), + }), + intervene: (id: string, kind: string, target: string, value: number) => + request<{ accepted: boolean }>(`/worlds/${id}/interventions`, { + method: "POST", + body: JSON.stringify({ kind, target, value }), + }), audits: (id: string, quarter?: number, agentId?: string) => { const params = new URLSearchParams(); if (quarter != null) params.set("quarter", String(quarter)); if (agentId) params.set("agent_id", agentId); - return request(`/worlds/${id}/audits?${params.toString()}`); + return request( + `/worlds/${id}/audits?${params.toString()}`, + ); }, - draftIntervention: (id: string, text: string) => request(`/worlds/${id}/intervention-plans`, { - method: "POST", body: JSON.stringify({ text }), - }), - confirmIntervention: (id: string, planId: string) => request<{ accepted: boolean; plan: InterventionPlan }>(`/worlds/${id}/intervention-plans/confirm`, { - method: "POST", body: JSON.stringify({ plan_id: planId }), - }), - interview: (id: string, agentId: string, quarter: number, question: string) => request(`/worlds/${id}/interviews`, { - method: "POST", body: JSON.stringify({ agent_id: agentId, quarter, question }), - }), - createCandidate: (filename: string, content: string) => request("/materials/candidates", { - method: "POST", body: JSON.stringify({ filename, content }), - }), - confirmCandidate: (id: string, candidate: CandidateWorld) => request(`/materials/candidates/${id}/confirm`, { - method: "POST", body: JSON.stringify({ - parameters: candidate.parameters.map((item) => ({ - parameter_id: item.id, final_value: item.final_value ?? item.suggested_value, - provenance: item.provenance, - })), - seed: 42, + draftIntervention: (id: string, text: string) => + request(`/worlds/${id}/intervention-plans`, { + method: "POST", + body: JSON.stringify({ text }), + }), + confirmIntervention: (id: string, planId: string) => + request<{ accepted: boolean; plan: InterventionPlan }>( + `/worlds/${id}/intervention-plans/confirm`, + { + method: "POST", + body: JSON.stringify({ plan_id: planId }), + }, + ), + interview: (id: string, agentId: string, quarter: number, question: string) => + request(`/worlds/${id}/interviews`, { + method: "POST", + body: JSON.stringify({ agent_id: agentId, quarter, question }), + }), + createCandidate: (filename: string, content: string) => + request("/materials/candidates", { + method: "POST", + body: JSON.stringify({ filename, content }), + }), + confirmCandidate: (id: string, candidate: CandidateWorld) => + request(`/materials/candidates/${id}/confirm`, { + method: "POST", + body: JSON.stringify({ + parameters: candidate.parameters.map((item) => ({ + parameter_id: item.id, + final_value: item.final_value ?? item.suggested_value, + provenance: item.provenance, + })), + seed: 42, + }), + }), + createDemo: (seed = 42, fiscalMultiplier = 0.5) => + request("/demos/hefei-nio", { + method: "POST", + body: JSON.stringify({ seed, fiscal_multiplier: fiscalMultiplier }), }), - }), - createDemo: (seed = 42, fiscalMultiplier = 0.5) => request("/demos/hefei-nio", { - method: "POST", body: JSON.stringify({ seed, fiscal_multiplier: fiscalMultiplier }), - }), - hefeiSensitivity: (seed = 42) => request(`/cases/hefei-nio/sensitivity?seed=${seed}`), - listExperimentReports: () => request("/experiment-reports"), - getExperimentReport: (id: string) => request(`/experiment-reports/${id}`), - getExperimentWorld: (reportId: string, worldId: string) => request(`/experiment-reports/${reportId}/worlds/${worldId}`), + hefeiSensitivity: (seed = 42) => + request(`/cases/hefei-nio/sensitivity?seed=${seed}`), + listExperimentReports: () => + request("/experiment-reports"), + getExperimentReport: (id: string) => + request(`/experiment-reports/${id}`), + getExperimentWorld: (reportId: string, worldId: string) => + request(`/experiment-reports/${reportId}/worlds/${worldId}`), }; diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 287de13..cf4d16d 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -3,39 +3,90 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { DemoReplay } from "./components/DemoReplay"; import { ReportCenter } from "./components/ReportCenter"; -import { API_BASE, api, type AgentActionAudit, type CandidateWorld, type Capability, type InterventionPlan, type Interview, type Metric, type World } from "./lib/api"; +import { + API_BASE, + api, + type AgentActionAudit, + type CandidateWorld, + type Capability, + type InterventionPlan, + type Interview, + type Metric, + type OrganizationAction, + type World, +} from "./lib/api"; const phaseNames: Record = { - recruitment: "招商竞争", delivery: "承诺履约", industrialization: "产业演化", + recruitment: "招商竞争", + delivery: "承诺履约", + industrialization: "产业演化", }; const tones = ["amber", "cyan", "violet"]; export default function Home() { const [world, setWorld] = useState(null); const [capability, setCapability] = useState(null); - const [worldList, setWorldList] = useState>([]); + const [worldList, setWorldList] = useState< + Array<{ + id: string; + name: string; + quarter: number; + parent_id: string | null; + policy_mode: string; + }> + >([]); const [busy, setBusy] = useState(false); const [playing, setPlaying] = useState(false); const [error, setError] = useState(""); const [policyMode, setPolicyMode] = useState("deterministic"); const [model, setModel] = useState("deepseek-v4-flash"); - const [detail, setDetail] = useState<"demo" | "reports" | "negotiations" | "unified" | "trace" | "audit" | "counterfactual" | "interview" | "material" | "about" | null>(null); + const [processMode, setProcessMode] = useState("hybrid"); + const [detail, setDetail] = useState< + | "demo" + | "reports" + | "negotiations" + | "unified" + | "organization" + | "trace" + | "audit" + | "counterfactual" + | "interview" + | "material" + | "about" + | null + >(null); const remember = useCallback((next: World) => { setWorld(next); window.localStorage.setItem("insidegov_world_id", next.id); }, []); - const refreshList = useCallback(async () => setWorldList(await api.listWorlds()), []); + const refreshList = useCallback( + async () => setWorldList(await api.listWorlds()), + [], + ); - const create = useCallback(async (mode = policyMode, selectedModel = model) => { - setBusy(true); setError(""); - try { - remember(await api.createWorld(42, mode, selectedModel)); - await refreshList(); - } catch (caught) { setError(`无法连接模拟 API:${String(caught)}`); } - finally { setBusy(false); } - }, [model, policyMode, refreshList, remember]); + const create = useCallback( + async ( + mode = policyMode, + selectedModel = model, + selectedProcess = processMode, + ) => { + setBusy(true); + setError(""); + try { + remember( + await api.createWorld(42, mode, selectedModel, selectedProcess), + ); + await refreshList(); + } catch (caught) { + setError(`无法连接模拟 API:${String(caught)}`); + } finally { + setBusy(false); + } + }, + [model, policyMode, processMode, refreshList, remember], + ); useEffect(() => { let alive = true; @@ -43,152 +94,1476 @@ export default function Home() { try { const caps = await api.capabilities(); if (!alive) return; - setCapability(caps); setModel(caps.default_model); + setCapability(caps); + setModel(caps.default_model); const id = window.localStorage.getItem("insidegov_world_id"); if (id) { - try { remember(await api.getWorld(id)); } - catch { await create("deterministic", caps.default_model); } + try { + remember(await api.getWorld(id)); + } catch { + await create("deterministic", caps.default_model); + } } else await create("deterministic", caps.default_model); await refreshList(); - } catch (caught) { if (alive) setError(`后端未就绪:${String(caught)}`); } + } catch (caught) { + if (alive) setError(`后端未就绪:${String(caught)}`); + } })(); - return () => { alive = false; }; + return () => { + alive = false; + }; }, [create, refreshList, remember]); async function step() { if (!world || busy) return; - setBusy(true); setError(""); + setBusy(true); + setError(""); try { const next = await api.step(world.id); remember(next); if (next.quarter >= 16) setPlaying(false); + } catch (caught) { + setError(String(caught)); + } finally { + setBusy(false); } - catch (caught) { setError(String(caught)); } - finally { setBusy(false); } } useEffect(() => { if (!playing || !world || world.quarter >= 16) return; const timer = window.setTimeout(() => void step(), 700); return () => window.clearTimeout(timer); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [playing, world?.quarter]); const latest = world?.history.at(-1); const cities = world ? Object.values(world.cities) : []; - const selected = world?.selected_city_id ? world.cities[world.selected_city_id] : null; + const selected = world?.selected_city_id + ? world.cities[world.selected_city_id] + : null; const phase = world ? phaseNames[world.phase] : "准备中"; - const operating = world ? Object.values(world.firms).filter((firm) => firm.operating).length : 0; + const operating = world + ? Object.values(world.firms).filter((firm) => firm.operating).length + : 0; const history = useMemo(() => world?.history ?? [], [world]); - if (!world) return
内

InsideGov

{error || "正在恢复世界…"}

{error && }
; + if (!world) + return ( +
+ 内 +

InsideGov

+

{error || "正在恢复世界…"}

+ {error && } +
+ ); return (
-
内
InsideGov政企互动推演场
-
{world.name}{world.id}
- +
+ 内 +
+ InsideGov + 政企互动推演场 +
+
+
+ + {world.name} + {world.id} +
+
{error &&
{error}
}
-
COMPETITION DEMO · RULE-GENERATED从真实案例到反事实结果,六步验证博弈不是预写剧情合肥—蔚来公开参数卡 · 联合产业基金 · Agent 私有信息 · 跨期履约
- +
+ COMPETITION DEMO · RULE-GENERATED + 从真实案例到反事实结果,六步验证博弈不是预写剧情 + + 合肥—蔚来公开参数卡 · 联合产业基金 · Agent 私有信息 · 跨期履约 + +
+
-
模拟时钟2030 Q{((world.quarter || 1) - 1) % 4 + 1}第 {world.quarter} / 16 季度
-
{["招商竞争", "承诺履约", "产业演化"].map((item, index) =>
{index + 1}{item}
)}
-
+
+ 模拟时钟 + 2030 Q{(((world.quarter || 1) - 1) % 4) + 1} + 第 {world.quarter} / 16 季度 +
+
+ {["招商竞争", "承诺履约", "产业演化"].map((item, index) => ( +
+ {index + 1} + {item} +
+ ))} +
+
+ + + +
- - - - + + + +
-
WORLD STATE

产业世界态势

● 政府◆ 龙头· 链企
-
-
{cities[0]?.name[0]}{cities[0]?.name}{world.selected_city_id === cities[0]?.id ? "项目落地" : "竞争城市"}
-
{cities[2]?.name[0]}{cities[2]?.name}{world.selected_city_id === cities[2]?.id ? "项目落地" : "竞争城市"}
-
◆星澜显示{selected ? `${selected.name} · ${world.firms.firm_nova.operating ? "已投产" : "建设中"}` : "正在选址"}{Array.from({ length: Math.min(10, Math.max(0, operating - 1)) }, (_, i) => )}
-
{phase}{world.policy_mode === "llm" ? `${world.model_name} 负责认知,规则引擎负责状态更新` : "确定性异质认知层 · 可复现基线"}
+
+
+
+ WORLD STATE +

产业世界态势

+
+
+ ● 政府 + ◆ 龙头 + · 链企 +
+
+
+
+
+
+
+ {cities[0]?.name[0]} + {cities[0]?.name} + + {world.selected_city_id === cities[0]?.id + ? "项目落地" + : "竞争城市"} + +
+
+ {cities[2]?.name[0]} + {cities[2]?.name} + + {world.selected_city_id === cities[2]?.id + ? "项目落地" + : "竞争城市"} + +
+
+
+ + + +
+ ◆ + 星澜显示 + + {selected + ? `${selected.name} · ${world.firms.firm_nova.operating ? "已投产" : "建设中"}` + : "正在选址"} + + {Array.from( + { length: Math.min(10, Math.max(0, operating - 1)) }, + (_, i) => ( + + ), + )} +
+
+ {phase} + + {world.policy_mode === "llm" + ? `${world.model_name} 负责认知,规则引擎负责状态更新` + : "确定性异质认知层 · 可复现基线"} + +
-
EVOLUTION

产能与需求演化

安全阈值 68%
{history.length ? history.map((point) => ) :
推进世界后生成季度数据
}
市场需求形成产能数据源:世界状态日志
+
+
+
+ EVOLUTION +

产能与需求演化

+
+ 安全阈值 68% +
+
+ {history.length ? ( + history.map((point) => ( + + )) + ) : ( +
推进世界后生成季度数据
+ )} +
+
+ + + 市场需求 + + + + 形成产能 + + 数据源:世界状态日志 +
+
-
DECISION TRACE

可追溯决策链

{world.traces.slice(-4).reverse().map((trace, index) =>
0{index + 1}
{trace.actor_id}

{trace.action}

调用证据

{trace.evidence.join(" · ")}

内部权衡

{trace.constraints.join(" · ")}

{Object.values(trace.expected_effects)[0]?.toFixed?.(1) ?? "—"}
)}
+
+
+
+ DECISION TRACE +

可追溯决策链

+
+ +
+
+ {world.traces + .slice(-4) + .reverse() + .map((trace, index) => ( +
+ 0{index + 1} +
+ {trace.actor_id} +

{trace.action}

+
+
+ 调用证据 +

{trace.evidence.join(" · ")}

+
+
+ 内部权衡 +

{trace.constraints.join(" · ")}

+
+ + {Object.values(trace.expected_effects)[0]?.toFixed?.(1) ?? + "—"} + + +
+ ))} +
+
-
InsideGov v0.5 · 政策实验与机制探索,不构成现实政策预测{world.policy_mode === "llm" ? world.model_name : "确定性基线"} · Seed {world.seed} · 自动持久化
+
+ InsideGov v0.6 · 组织行动与政策实验,不构成现实政策预测 + + {world.process_mode} 过程 ·{" "} + {world.policy_mode === "llm" ? world.model_name : "确定性基线"} · Seed{" "} + {world.seed} + +
- {detail &&
{detail === "demo" ? : detail === "reports" ? : detail === "about" ? : detail === "negotiations" ? : detail === "unified" ? : detail === "audit" ? : detail === "counterfactual" ? : detail === "interview" ? : detail === "material" ? : }
} + {detail && ( +
+
+ + {detail === "demo" ? ( + + ) : detail === "reports" ? ( + + ) : detail === "about" ? ( + + ) : detail === "negotiations" ? ( + + ) : detail === "unified" ? ( + + ) : detail === "organization" ? ( + + ) : detail === "audit" ? ( + + ) : detail === "counterfactual" ? ( + + ) : detail === "interview" ? ( + + ) : detail === "material" ? ( + + ) : ( + + )} +
+
+ )}
); } -function fiscalCost(offer: { subsidy: number; equity: number; credit_support: number }) { return offer.subsidy + offer.equity + offer.credit_support * .08; } -function averageCredibility(cities: Array<{ objective_credibility: number }>) { return cities.length ? cities.reduce((sum, city) => sum + city.objective_credibility, 0) / cities.length : 0; } -function Metric({ label, value, delta, tone }: { label: string; value: string; delta: string; tone: string }) { return
{label}{value}{delta}
; } -function ChartBar({ point }: { point: Metric }) { return ; } +function fiscalCost(offer: { + subsidy: number; + equity: number; + credit_support: number; +}) { + return offer.subsidy + offer.equity + offer.credit_support * 0.08; +} +function averageCredibility(cities: Array<{ objective_credibility: number }>) { + return cities.length + ? cities.reduce((sum, city) => sum + city.objective_credibility, 0) / + cities.length + : 0; +} +function Metric({ + label, + value, + delta, + tone, +}: { + label: string; + value: string; + delta: string; + tone: string; +}) { + return ( +
+ {label} + {value} + {delta} +
+ ); +} +function ChartBar({ point }: { point: Metric }) { + return ( + + ); +} -function About({ capability, mode, model, setMode, setModel, create }: { capability: Capability | null; mode: string; model: string; setMode: (v: string) => void; setModel: (v: string) => void; create: (mode?: string, model?: string) => Promise }) { - return <>RESEARCH DESIGN

双层模拟与模式设置

大模型只负责私有观察下的理解、提案、否决、协调、选址与复盘;财政扣减、项目进度、履约和产能均由确定性引擎更新。

{capability?.llm_available ? "服务端已识别 DeepSeek 凭据。" : "服务端未识别 DEEPSEEK_API_KEY,LLM 选项已锁定。"}

; +function About({ + capability, + mode, + model, + processMode, + setMode, + setModel, + setProcessMode, + create, +}: { + capability: Capability | null; + mode: string; + model: string; + processMode: string; + setMode: (v: string) => void; + setModel: (v: string) => void; + setProcessMode: (v: string) => void; + create: ( + mode?: string, + model?: string, + processMode?: string, + ) => Promise; +}) { + return ( + <> + RESEARCH DESIGN +

认知层、组织过程与刚性结算

+

+ 组织 Agent + 从各自专属动作集中选择议程、会前沟通、联盟、审查与协调行动;财政扣减、合同、项目进度、履约和产能仍由确定性引擎更新。 +

+
+ + + +
+

+ 非正式行动可以改变议程、信任、工具组合和行动顺序,但不能越过财政与合同硬约束。 +

+ + + ); +} +const organizationEvidence: Record = { + formal_procedure: { + label: "重大行政决策程序暂行条例", + url: "https://www.moj.gov.cn/pub/sfbgw/zcjd/201905/t20190516_390231.html", + }, + fund_independence: { + label: "政府投资基金高质量发展指导意见", + url: "https://www.mee.gov.cn/zcwj/gwywj/202501/t20250108_1100235.shtml", + }, + informal_networks: { + label: "中国科层权力网络中的政策试验研究", + url: "https://www.cambridge.org/core/journals/china-quarterly/article/policy-experimentation-within-bureaucratic-power-networks-the-policebusiness-cooperation-scheme-in-urban-china/6E7E2E1536065118E626082F0CFF58E8", + }, + attention: { + label: "中国科层体系中的注意力竞争", + url: "https://link.springer.com/article/10.1186/s40711-018-0071-z", + }, +}; +function OrganizationTimeline({ world }: { world: World }) { + const [cityId, setCityId] = useState( + world.selected_city_id ?? Object.keys(world.cities)[0], + ); + const actions = (world.organization_actions ?? []).filter( + (item) => item.city_id === cityId, + ); + const plans = (world.organization_plans ?? []).filter( + (item) => item.city_id === cityId, + ); + const windows = (world.opportunity_windows ?? []).filter( + (item) => item.city_id === cityId || item.city_id === null, + ); + const openActions = (world.open_action_proposals ?? []).filter( + (item) => item.city_id === cityId, + ); + const learning = Object.values(world.organization_learning ?? {}).filter( + (item) => item.agent_id.startsWith(`${cityId}_`), + ); + const process = world.organization_processes?.[cityId]; + const arenaName = (item: OrganizationAction) => + item.arena === "formal" + ? "正式" + : item.arena === "informal" + ? "非正式" + : item.arena === "initiative" + ? "行动发起" + : item.arena === "procedure" + ? "程序转换" + : "硬约束"; + return ( + <> + ORGANIZATION AS ACTOR · EVIDENCE-GROUNDED +

谁发起、程序何时转换、企业何时回应

+

+ 当前世界使用 {world.process_mode}{" "} + 模式。各部门先独立决定行动或等待,再竞争有限注意力;领导可实时启动、暂停、退回或重新议程化正式程序。 +

+
+ +
+ {process && ( +
+ + 程序状态 {process.formal_status} + + + 最近转换 {process.last_transition} + + + 议程优先度 {(process.agenda_priority * 100).toFixed(0)}% + + + 程序完整度{" "} + {(process.procedural_completeness * 100).toFixed(0)}% + + + 联盟支持 {(process.coalition_support * 100).toFixed(0)}% + + + 注意力名额 {process.attention_budget} + +
+ )} +

多步计划:原计划 → 实际行动 → 偏离

+
+ {plans.map((plan) => ( +
+
+ {plan.actor_id} + {plan.status} +
+

+ {plan.objective} +

+ + 已选策略:{plan.selected_strategy} · Q{plan.created_quarter}—Q + {plan.horizon_quarter} · {plan.provider} + +
+ 比较过的策略树 + {plan.alternatives.map((option) => ( +

+ {option.id === plan.selected_strategy ? "✓ " : ""} + {option.name}({(option.score * 100).toFixed(0)}%): + {option.approach};风险 {option.risks.join("、") || "未标注"} +

+ ))} +
+
+ {plan.nodes.map((node) => ( +

+ {node.id} · {node.status} Q{node.earliest_quarter}—Q + {node.latest_quarter}:计划 {node.action_id} + {node.actual_action_id ? ` → 实际 ${node.actual_action_id}` : ""} + {node.deviation_reason ? `;偏离:${node.deviation_reason}` : ""} +

+ ))} +
+
+ ))} + {!plans.length &&

推进一轮后将生成可检验的跨季度计划。

} +
+

内生机会窗口

+
+ {windows.map((window) => ( +
+ Q{window.start_quarter} · {window.title} + {window.kind} · 强度 {(window.magnitude * 100).toFixed(0)}% +

{window.trigger}

+ {brief(window.effects)} +
+ ))} + {!windows.length &&

尚未出现政策、竞争、财政或注意力窗口。

} +
+

目录外行动与权限审查

+
+ {openActions.map((proposal) => ( +
+ Q{proposal.quarter} · {proposal.actor_id} · {proposal.title} + {proposal.mechanism} · {proposal.status} +

{proposal.rationale}

+ 申请效果:{brief(proposal.requested_effects)} + 规则执行:{brief(proposal.executed_effects)} · {proposal.validation_reason} +
+ ))} + {!openActions.length &&

Agent 尚未提出目录外组织手段。

} +
+

组织学习与制度记忆

+
+ {learning.map((item) => ( +
+ {item.agent_id} + 财政否决 {item.veto_count} 次 · 协调成功 {item.successful_coordination_count} 次 · 第 {item.leadership_generation} 任 + 稳定信任:{brief(item.trust_by_actor)} + 组织惯例:{Object.keys(item.routines).join("、") || "尚未形成"} +

{item.transferable_lessons.at(-1) ?? item.lessons.at(-1) ?? "尚无可迁移经验"}

+
+ ))} +
+
+ {actions.map((item) => ( +
+
+ + {item.actor_id} + + {arenaName(item)} + {item.required ? " · 必经" : ""} + +
+

{item.action_name}

+

{item.rationale}

+ + 决策:{item.decision} · 紧迫度 {(item.urgency * 100).toFixed(0)}% + {item.target_actor_id ? ` · 目标 ${item.target_actor_id}` : ""} + + + 当时可选:{item.candidates.join("、") || "唯一法定动作"} + + + 选择者:{item.selection_provider} + {item.fallback ? "(已降级)" : ""} · {item.selection_rationale} + + 状态影响:{brief(item.effects)} + {item.plan_id && ( + + 计划证据:{item.plan_id}/{item.plan_node_id} + {item.deviation_reason ? ` · 偏离 ${item.deviation_reason}` : " · 按计划"} + + )} + {item.reflection && ( +

+ 行动后复盘: + {item.reflection} +

+ )} + {item.blocked_reason && ( +

未执行:{item.blocked_reason}

+ )} + +
+ ))} + {!actions.length && ( +

推进一个招商季度后将出现自主组织行动。

+ )} +
+

+ 刚性边界只保留财政、合法性、合同和生产结算;发起主体、行动时点、程序转换和企业谈判时点均由组织 + Agent 决定。 +

+ + ); +} +function Negotiations({ world }: { world: World }) { + return ( + <> + INTERNAL GOVERNANCE +

招商局提案 → 产业基金审核 → 财政局否决 → 市领导协调

+
+ {world.negotiations + .slice() + .reverse() + .map((item) => ( +
+ + Q{item.quarter} · {world.cities[item.city_id].name} + + + 招商局:现金 {money(item.proposal_tools?.subsidy)},本级股权{" "} + {money(item.proposal_tools?.equity)} + + + 联合基金:独立审核 {money(item.proposal_tools?.external_equity)} + + + 财政局:现金上限 {money(item.finance_tool_limits?.subsidy)} + ,本级股权上限 {money(item.finance_tool_limits?.equity)} + + + 最终:现金 {money(item.final_tools?.subsidy)} + 总股权{" "} + {money(item.final_tools?.total_equity_support)} + +

{item.concerns.join(";")}

+
+ {item.payment_schedule?.map((row, index) => ( + + Q+{row.due_offset} · {toolName(row.item)}{" "} + {row.amount.toFixed(2)} 亿 · {row.condition} + + ))} +
+
+ ))} +
+ + ); +} +function UnifiedNegotiations({ world }: { world: World }) { + const internal = Object.fromEntries( + world.negotiations.map((item) => [item.id, item]), + ); + return ( + <> + NESTED NEGOTIATION +

企业表达 → 政府澄清 → 内部会商 → 企业回应

+
+ {world.external_negotiations + .slice() + .reverse() + .map((item) => { + const meeting = internal[item.internal_negotiation_id]; + return ( +
+ + Q{item.quarter} · {world.cities[item.city_id].name} ·{" "} + {item.enterprise_response} + +

+ 企业表面诉求: + {item.stated_need} +

+

+ 政府追问: + {item.government_questions.join("、") || "沿用已有理解"} +

+

+ 理解更新: + {brief(item.belief_before)} → {brief(item.belief_after)}(信心{" "} + {(item.belief_confidence * 100).toFixed(0)}%) +

+ {meeting && ( + <> + + 招商局:现金 {money(meeting.proposal_tools.subsidy)} + +本级股权 {money(meeting.proposal_tools.equity)} + + + 联合基金:{money(meeting.proposal_tools.external_equity)} + + + 财政硬上限:现金{" "} + {money(meeting.finance_tool_limits.subsidy)}+本级股权{" "} + {money(meeting.finance_tool_limits.equity)} + + + 领导协调:现金 {money(meeting.final_tools.subsidy)} + +总股权 {money(meeting.final_tools.total_equity_support)} + + + )} +

+ 企业回应: + {item.enterprise_rationale} +

+ {Object.keys(item.counter_terms).length > 0 && ( +

+ 还价条件: + {brief(item.counter_terms)} +

+ )} + + 效用 {item.utility.toFixed(1)} / 最低门槛{" "} + {item.minimum_utility.toFixed(1)} · 内部记录{" "} + {item.internal_negotiation_id} + +
+ ); + })} +
+ + ); +} +function Traces({ world }: { world: World }) { + return ( + <> + AUDIT LOG +

完整决策链

+
+ {world.traces + .slice() + .reverse() + .map((trace) => ( +
+ + {trace.actor_id} · {trace.action} + +

{trace.evidence.join(";")}

+ {trace.outcome} +
+ ))} +
+ + ); } -function Negotiations({ world }: { world: World }) { return <>INTERNAL GOVERNANCE

招商局提案 → 产业基金审核 → 财政局否决 → 市领导协调

{world.negotiations.slice().reverse().map((item) =>
Q{item.quarter} · {world.cities[item.city_id].name}招商局:现金 {money(item.proposal_tools?.subsidy)},本级股权 {money(item.proposal_tools?.equity)}联合基金:独立审核 {money(item.proposal_tools?.external_equity)}财政局:现金上限 {money(item.finance_tool_limits?.subsidy)},本级股权上限 {money(item.finance_tool_limits?.equity)}最终:现金 {money(item.final_tools?.subsidy)} + 总股权 {money(item.final_tools?.total_equity_support)}

{item.concerns.join(";")}

{item.payment_schedule?.map((row, index) => Q+{row.due_offset} · {toolName(row.item)} {row.amount.toFixed(2)} 亿 · {row.condition})}
)}
; } -function UnifiedNegotiations({ world }: { world: World }) { const internal = Object.fromEntries(world.negotiations.map((item) => [item.id, item])); return <>NESTED NEGOTIATION

企业表达 → 政府澄清 → 内部会商 → 企业回应

{world.external_negotiations.slice().reverse().map((item) => { const meeting = internal[item.internal_negotiation_id]; return
Q{item.quarter} · {world.cities[item.city_id].name} · {item.enterprise_response}

企业表面诉求:{item.stated_need}

政府追问:{item.government_questions.join("、") || "沿用已有理解"}

理解更新:{brief(item.belief_before)} → {brief(item.belief_after)}(信心 {(item.belief_confidence * 100).toFixed(0)}%)

{meeting && <>招商局:现金 {money(meeting.proposal_tools.subsidy)}+本级股权 {money(meeting.proposal_tools.equity)}联合基金:{money(meeting.proposal_tools.external_equity)}财政硬上限:现金 {money(meeting.finance_tool_limits.subsidy)}+本级股权 {money(meeting.finance_tool_limits.equity)}领导协调:现金 {money(meeting.final_tools.subsidy)}+总股权 {money(meeting.final_tools.total_equity_support)}}

企业回应:{item.enterprise_rationale}

{Object.keys(item.counter_terms).length > 0 &&

还价条件:{brief(item.counter_terms)}

}效用 {item.utility.toFixed(1)} / 最低门槛 {item.minimum_utility.toFixed(1)} · 内部记录 {item.internal_negotiation_id}
; })}
; } -function Traces({ world }: { world: World }) { return <>AUDIT LOG

完整决策链

{world.traces.slice().reverse().map((trace) =>
{trace.actor_id} · {trace.action}

{trace.evidence.join(";")}

{trace.outcome}
)}
; } function AuditTimeline({ world }: { world: World }) { const [quarter, setQuarter] = useState("all"); const [agent, setAgent] = useState("all"); const audits = world.action_audits ?? []; - const quarters = Array.from(new Set(audits.map((item) => item.quarter))).sort((a, b) => a - b); - const agents = Array.from(new Set(audits.map((item) => item.agent_id))).sort(); - const visible = audits.filter((item) => (quarter === "all" || item.quarter === quarter) && (agent === "all" || item.agent_id === agent)); - return <>P2.1 · AUDITABLE AGENTS

观察 → 建议 → 规则修正 → 执行 → 复盘

{visible.slice().reverse().map((item) => )}{!visible.length &&

当前筛选条件下暂无认知行动。

}
; -} -function AuditCard({ item }: { item: AgentActionAudit }) { const fields = item.private_context_used.fields_used; const fieldNames = Array.isArray(fields) ? fields.join("、") : ""; return
Q{item.quarter} · {item.agent_id} · {item.action_type}{item.provider}{item.fallback ? " · 已降级" : " · 原生执行"}

当时观察:{brief(item.observation)}

私有信息:{"redacted" in item.private_context_used ? `默认脱敏;使用字段 ${fieldNames}` : "已记录"}

模型/策略建议:{brief(item.llm_suggestion)}

规则修正:{brief(item.rule_adjustment)}

最终执行:{brief(item.executed_action)}

理由:{item.rationale}

复盘:{item.reflection}

诊断记录 {item.diagnostics.length} 条 · {item.outcome}
; } -function brief(value: unknown) { const text = JSON.stringify(value); return text.length > 240 ? `${text.slice(0, 237)}…` : text; } -function CounterfactualLab({ world, remember, refreshList }: { world: World; remember: (world: World) => void; refreshList: () => Promise }) { + const quarters = Array.from(new Set(audits.map((item) => item.quarter))).sort( + (a, b) => a - b, + ); + const agents = Array.from( + new Set(audits.map((item) => item.agent_id)), + ).sort(); + const visible = audits.filter( + (item) => + (quarter === "all" || item.quarter === quarter) && + (agent === "all" || item.agent_id === agent), + ); + return ( + <> + P2.1 · AUDITABLE AGENTS +

观察 → 建议 → 规则修正 → 执行 → 复盘

+
+ + +
+
+ {visible + .slice() + .reverse() + .map((item) => ( + + ))} + {!visible.length && ( +

当前筛选条件下暂无认知行动。

+ )} +
+ + ); +} +function AuditCard({ item }: { item: AgentActionAudit }) { + const fields = item.private_context_used.fields_used; + const fieldNames = Array.isArray(fields) ? fields.join("、") : ""; + return ( +
+ + Q{item.quarter} · {item.agent_id} · {item.action_type} + + + {item.provider} + {item.fallback ? " · 已降级" : " · 原生执行"} + +

+ 当时观察: + {brief(item.observation)} +

+

+ 私有信息: + {"redacted" in item.private_context_used + ? `默认脱敏;使用字段 ${fieldNames}` + : "已记录"} +

+

+ 模型/策略建议: + {brief(item.llm_suggestion)} +

+

+ 规则修正: + {brief(item.rule_adjustment)} +

+

+ 最终执行: + {brief(item.executed_action)} +

+

+ 理由: + {item.rationale} +

+

+ 复盘: + {item.reflection} +

+ + 诊断记录 {item.diagnostics.length} 条 · {item.outcome} + +
+ ); +} +function brief(value: unknown) { + const text = JSON.stringify(value); + return text.length > 240 ? `${text.slice(0, 237)}…` : text; +} +function CounterfactualLab({ + world, + remember, + refreshList, +}: { + world: World; + remember: (world: World) => void; + refreshList: () => Promise; +}) { const [quarter, setQuarter] = useState(Math.max(0, world.quarter - 1)); - const [text, setText] = useState("临江市财政收入下降30%,但上级提供5亿元专项资金,优先保障设备采购后的第二期补贴。"); + const [text, setText] = useState( + "临江市财政收入下降30%,但上级提供5亿元专项资金,优先保障设备采购后的第二期补贴。", + ); const [baseline, setBaseline] = useState(null); const [branch, setBranch] = useState(null); const [plan, setPlan] = useState(null); const [delta, setDelta] = useState | null>(null); const [message, setMessage] = useState(""); - async function makeBranch() { const result = await api.createPair(world.id, quarter); setBaseline(result.baseline); setBranch(result.branch); setPlan(null); setDelta(null); setMessage(`已从Q${quarter}生成基线/干预副本,两者随机状态完全一致。`); await refreshList(); } - async function parse() { if (!branch) return; setPlan(await api.draftIntervention(branch.id, text)); } - async function confirm() { if (!branch || !plan) return; const result = await api.confirmIntervention(branch.id, plan.id); setPlan(result.plan); setMessage(`干预已确认,将从Q${plan.effective_quarter}按规则执行。`); } - async function sync() { if (!baseline || !branch) return; const result = await api.syncPair(baseline.id, branch.id, 1); setBaseline(result.baseline); setBranch(result.branch); setDelta(result.comparison.delta); remember(result.branch); } - return <>P2.2 · COUNTERFACTUAL

历史节点复制与干预确认