推测系统设计师
MCP服务器+LangGraph主机,通过结构化期货、显式权衡和Excalidraw图生成、压力测试和管理软件架构。
______________________________________________________________________
这是什么?
大多数架构审查过程发生得太晚,也太礼貌了。团队提出一个系统,对其进行简短辩论,然后附带未解决的假设。
推测系统设计师 翻转它。它强制执行通常跳过的三件事:
- 不可谈判的限制 宣布 *之前* 设计开始--称为 *根*
- 悲观的未来 模拟的 *反对* 提出后的设计
- 明确的权衡 接受 *有记录的* 在最终架构发布之前
该项目分为两个独立的层:
| 层 | 它做什么 |
|---|---|
服务器 (server/mcp_server.py) | FastMCP服务器——公开工具、执行治理规则、管理状态 |
主机/客户端 (host/) | LangGraph编排——通过有状态图驱动服务器工具,连接Excalidraw |
______________________________________________________________________
仓库的规划
SpeculativeSystemDesigner/
├── server/
│ ├── mcp_server.py ← FastMCP entry point (all tools registered here)
│ ├── architectures.py ← Architecture model + submit_architecture factory
│ ├── critiques.py ← Critique model, CRITIQUE_STORE, helpers
│ ├── futures.py ← Loads futures.json
│ ├── roots.py ← Loads roots.json + prompt formatter
│ ├── store.py ← In-memory REVIEW_STORE
│ ├── declare_tradeoff.py ← Records an accepted tradeoff
│ ├── TOOLS.md ← Tool-by-tool API reference
│ ├── CONCEPTS.md ← Deep-dive into Roots, Futures, Tradeoffs
│ └── data/
│ ├── roots.json ← Non-negotiable architectural constraints
│ └── futures.json ← Pessimistic stress-test scenarios
│
├── host/
│ ├── __init__.py
│ ├── state.py ← DesignState TypedDict
│ ├── handlers.py ← MCP sampling + elicitation callbacks
│ ├── nodes.py ← LangGraph node implementations
│ ├── graph.py ← Graph definition and routing logic
│ └── run.py ← CLI entry point
│
├── graph_flow.svg ← Visual graph flow reference
├── requirements.txt
├── pyproject.toml
├── .env.example
└── README.md______________________________________________________________________
关键概念
| 概念 | 描述 |
|---|---|
| 根 | 不可协商的架构约束(例如“必须可由≤6名工程师操作”)。违规行为会阻止审批。定义于 server/data/roots.json. |
| 未来 | 系统可能不得不在一种悲观的情况下运行——扩展10倍、安全滥用、监管变化。定义于 server/data/futures.json. |
| 批评 | 通过根据架构模拟未来而产生的结构化LLM反馈。包含a summary 最多3个 risks. |
| 权衡 | 一种明确的、人类接受的妥协,可以解决批评。必须在发布最终架构之前声明。 |
______________________________________________________________________
架构概述
┌─────────────────────────────────────────────────────────────┐
│ LangGraph Host (host/) │
│ run.py ──► graph.py ──► nodes.py │
│ │ │
│ handlers.py (sampling + elicitation callbacks) │
└─────────────┬───────────────────┬──────────────────────────┘
│ MCP stdio │ MCP streamable-http
▼ ▼
┌─────────────────────┐ ┌────────────────────────┐
│ SSD MCP Server │ │ Excalidraw MCP Server │
│ (mcp_server.py) │ │ mcp.excalidraw.com/mcp │
│ │ └────────────────────────┘
│ Tools │
│ ├─ generate_arch │
│ ├─ simulate_future │ Resources
│ ├─ propose_trdoff │ ├─ roots://governance
│ ├─ finalize_arch │ └─ roots://futures
│ ├─ evaluate_arch │
│ ├─ list_roots │ State (in-memory)
│ ├─ list_futures │ └─ REVIEW_STORE + CRITIQUE_STORE
│ └─ write_arch │
│ │
│ data/ │
│ ├─ server/data/ │
│ │ ├─ roots.json │
│ │ └─ futures.json │
└─────────────────────┘权限属于服务器。 治理提示——根约束、未来场景、批评结构——都存在于MCP服务器内部。客户无法绕过它们。
创意属于客户。 客户端LLM处理开放式综合:提出架构、生成权衡选项、编写最终文档。
______________________________________________________________________
服务器功能
工具
服务器通过MCP公开了八个工具。五个是异步的(它们使用MCP采样将LLM调用委托回客户端),三个是同步实用程序。
generate_architecture_tool *(异步)*
生成满足中定义的所有根的系统架构 server/data/roots.json。它使用 MCP采样 将提示(包括所有根约束)逐字发送到客户端LLM。结果立即保存到 REVIEW_STORE 和a architecture_id 对于所有后续的工具调用,都会返回。
simulate_future_tool *(异步)*
根据单个未来场景对保存的架构进行压力测试。发送未来的 review_prompt +通过采样将架构文本发送到客户端LLM,并期望得到JSON响应 summary (字符串)和 risks (列表)。创建并存储 Critique 对象。
propose_tradeoff_tool *(异步)*
解决批评的两阶段工具:
- 采样阶段 --LLM生成三个权衡选项(A、B、C),每个选项都有一个
statement,sacrifice,以及benefit. - 激发阶段 --选项通过以下方式呈现给人类 MCP激发人挑一个;服务器通过以下方式记录它
declare_tradeoff并标志着批评已解决。
服务器从不让LLM选择权衡——这个决定总是属于人类的。
evaluate_architecture_tool *(async--编排器)*
全面评估运行的高级入口点。内部通话 simulate_future_tool 为了每一个未来 server/data/futures.json那么 propose_tradeoff_tool 对于每一个由此产生的批评,那么 finalize_architecture_tool用户在未来每次都会被提示(通过Elicitation)选择一个折衷方案。
finalize_architecture_tool *(异步)*
生成包含所有公认折衷的最终治理架构。如果没有声明折衷,则拒绝执行——服务器强制要求必须首先解决每个Critique。使用采样将合成委托给客户端LLM。
list_roots_scope *(同步)*
返回当前从加载的根约束ID的列表 server/data/roots.json.
list_futures_scope *(同步)*
返回当前加载的未来ID列表 server/data/futures.json。由LangGraph主机在启动时使用,无需硬编码即可动态发现期货。
write_architecture *(同步)*
将架构文档(初始、模拟未来或最终)写入 architectures// 在磁盘上。
资源
服务器公开了两个任何客户端都可以直接读取的MCP资源:
| URI | 内容 |
|---|---|
roots://governance | 满 server/data/roots.json |
roots://futures | 满 server/data/futures.json |
状态管理
所有运行时状态都存在于两个内存字典中(在服务器重新启动时重置):
| 店铺 | 位置 | 内容 |
|---|---|---|
REVIEW_STORE | store.py | 每个架构一个条目:初始文本、权衡列表、评论ID列表、最终文本 |
CRITIQUE_STORE | critiques.py | 每个条目一个 Critique 对象,由UUID键控 |
默认根目录(server/data/roots.json)
| 根ID | 约束 |
|---|---|
team_constraints | 系统必须可由≤6名工程师构建和操作。 |
cost_discipline | 基线基础设施成本必须是可预测的,并且对非技术利益相关者来说是可防御的。 |
operational_observability | 系统必须可诊断,而无需生产中的专用仪器。 |
reversibility | 重大的架构决策必须是可逆的,无需完全重写。 |
默认期货(server/data/futures.json)
| 未来ID | 假设 | 立场 |
|---|---|---|
scaling | 流量和数据量在18个月内增长了10倍 | 悲观 |
security_abuse | 攻击者有能力;内部配置错误将会发生 | 对抗性 |
regulatory_compliance | 新的合规要求在系统发货后很快就会到来 | 字面意思 |
______________________________________________________________________
主机/客户端(LangGraph)
这 host/ 包是一个LangGraph编排层,它通过有状态的有向图驱动SSD服务器工具。它同时维护两个MCP会话——一个到SSD服务器(stdio),一个到Excalidraw MCP服务器(HTTP)。
图形流
generate
└─► draw_initial (Excalidraw)
│
├─[--no-eval / run_evaluation=False]──────────────► END
│
└─[run_evaluation=True]──► simulate_future ◄────────┐
│ │
▼ │ (more futures)
propose_tradeoff │
│ │
├─[more futures]─────┘
│
└─[all done]──► finalize
│
▼
draw_final (Excalidraw)
│
▼
END看 graph_flow.svg 对于视觉版本。
节点(host/nodes.py)
| 节点 | MCP工具调用 | 它的作用 |
|---|---|---|
generate | generate_architecture_tool | 生成初始架构;种子 architecture_id, architecture_text,重置循环索引 |
draw_initial | 退场 create_drawing | 转换架构文本→ 排除JSON元素(通过LLM)→ 创建图表,存储 initial_diagram_url |
simulate_future | simulate_future_tool | 模拟列表中的下一个未来;在状态后附加批评,递增 current_future_index |
propose_tradeoff | propose_tradeoff_tool | 为最新的《评论》提出选项;启发式回调解决了选择问题;附加到 tradeoffs |
finalize | finalize_architecture_tool | 从初始设计+所有声明的权衡中综合出最终架构 |
draw_final | 退场 create_drawing | 与 draw_initial 但对于最终的架构;商店 final_diagram_url |
路由逻辑(host/graph.py)
两条条件边控制流:
- 之后
draw_initial:分支上state["run_evaluation"]--前往simulate_future如果True,否则立即结束。受控于--no-evalCLI标志。 - 之后
propose_tradeoff:循环回到simulate_future当 `current_future_index
Initial diagram : https://excalidraw.com/#... Futures simulated : 3 Tradeoffs declared : 3 Final diagram : https://excalidraw.com/#...
FINAL ARCHITECTURE: ...
### 选项B——克劳德桌面
> ⚠️ **Claude桌面兼容性**
>
> 此分支上的服务器(`main`)用途 **MCP采样** 和 **MCP激发** --Claude Desktop目前不支持的两个功能。将此服务器直接连接到Claude Desktop将导致工具调用静默失败或挂起。
>
> **要在Claude Desktop中使用推测系统设计器,请切换到 `simple-server` 分支**,它提供了一个与克劳德兼容的服务器,用自包含的工具逻辑替换采样和启发式:
>
> ```bash
> git checkout simple-server
> ```
>
> 然后按照该分支的README中的设置和Claude Desktop配置说明进行操作。
### 选项C-HTTP传输
In server/mcp_server.py, change the last line to:
mcp.run(transport="streamable-http")
python -m server.mcp_server
Server available at http://127.0.0.1:8000/mcp
______________________________________________________________________
## 人在循环模式
集 `ELICITATION_MODE=human` 在 `.env`。图表在每个时间点暂停 `propose_tradeoff_tool` 通过LangGraph调用 `interrupt()`.
要继续,请执行以下操作:
from langgraph.types import Command
config = {"configurable": {"thread_id": "design-session-1"}}
First ainvoke pauses at the first tradeoff
result = await graph.ainvoke(initial_state, config=config)
Resume with the human's choice
result = await graph.ainvoke(Command(resume="B"), config=config)
______________________________________________________________________
## 定制根和未来
这两个文件都是纯JSON格式,不需要更改代码。
### 添加根(`server/data/roots.json`)
{ "your_root_id": { "id": "your_root_id", "statement": "The constraint in plain language.", "rationale": "Why this constraint exists.", "violation_examples": [ "concrete example of something that breaks this rule" ] } }
根被注入到下一个 `generate_architecture_tool` 自动呼叫。
### 添加未来(`server/data/futures.json`)
{ "your_future_id": { "id": "your_future_id", "description": "What this scenario assumes.", "assumption": "The specific pessimistic assumption.", "stance": "pessimistic | adversarial | literal", "review_prompt": "You are a [role]. Given this architecture... Respond ONLY in JSON: {\"summary\": \"...\", \"risks\": [\"...\"]}" } }
这 `review_prompt` **必须** 指示模型返回JSON `summary` (字符串)和 `risks` (字符串数组)。LangGraph主机通过以下方式自动获取新的期货 `list_futures_scope` 在图形构建时。
______________________________________________________________________
## 交换LLM(主机)
主机默认使用OpenAI。要切换到Anthropic,请编辑 `host/handlers.py` 和 `host/nodes.py`:
from anthropic import AsyncAnthropic _client = AsyncAnthropic() # reads ANTHROPIC_API_KEY
In sampling_handler, replace the openai call with:
response = await _client.messages.create( model="claude-sonnet-4-5", max_tokens=800, messages=messages, ) text = response.content[0].text
______________________________________________________________________
## 使用的MCP功能
|功能|位置|
|---|---|
| **工具** |全部 `@mcp.tool()` 功能在 `mcp_server.py` |
| **资源** | `roots://governance`, `roots://futures` |
| **采样** (`ctx.session.create_message`)|架构生成、未来模拟、权衡选项生成、最终确定——服务器将LLM调用委托给客户端|
| **引出** (`ctx.elicit`) | `propose_tradeoff_tool` --服务器向人类呈现选择并等待选择|
______________________________________________________________________
## 设计原则
**权限属于服务器。** MCP服务器内的治理提示是实时的。客户不能跳过或更改它们。
**创意属于客户。** 客户端LLM处理开放式合成。
**权衡是一流的产物。** 每一个被接受的权衡都是坚持的 `REVIEW_STORE` 并逐字传递到定稿提示中。没有什么是被默默抛弃的。
**服务器可以拒绝。** `finalize_architecture_tool` 在每一个批评都得到解决之前,拒绝产生最终的架构。
______________________________________________________________________