使用说明
推测系统设计师
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/<use_case>/ 在磁盘上。
资源
服务器公开了两个任何客户端都可以直接读取的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 < len(future_ids),然后继续finalize当所有的未来都耗尽时。
未来的ID通过以下方式从服务器动态获取 list_futures_scope 在图构建时,主机永远不会硬编码未来的名称。
国家(host/state.py)
class DesignState(TypedDict):
problem_statement: str
run_evaluation: bool # True = simulate futures, False = generate + draw only
architecture_id: Optional[str]
architecture_text: Optional[str]
critiques: List[dict] # [{id, future, summary, risks, ...}]
tradeoffs: List[dict] # [{critique_id, selected, ...}]
final_architecture: Optional[str]
current_future_index: int # loop counter through futures
future_ids: List[str]
initial_diagram_url: Optional[str]
final_diagram_url: Optional[str]
采样和采集处理人员(host/handlers.py)
主机向MCP注册两个回调 ClientSession:
sampling_handler --每当SSD服务器调用时调用 ctx.session.create_message。将消息转发到配置的OpenAI模型并返回结果。这就是服务器在不需要自己的API密钥的情况下将所有LLM工作委托给客户端的方式。
elicitation_handler --每当SSD服务器调用时调用 ctx.elicit (目前在 propose_tradeoff_tool).行为取决于 ELICITATION_MODE:
| 模式 | 行为 |
|---|---|
llm (默认) |
LLM读取A/B/C选项并自动选择最实用的选项 |
human |
LangGraph interrupt() 暂停图形;人类用以下方式恢复它 Command(resume="B") |
Excalidraw集成
主机连接到Excalidraw MCP服务器 https://mcp.excalidraw.com/mcp (可通过 EXCALIDRAW_MCP_URL).对于每个绘图节点,它:
- 调用LLM(gpt-4o-mini)将架构散文转换为有效的Excalidraw元素JSON数组(服务为矩形,数据流为箭头)。
- 调用Excalidraw
create_drawing具有这些元素的工具。 - 将返回的可共享URL存储在
DesignState.
每次完整运行都会生成两个图表——初始和最终——这两个URL都会在会话结束时打印出来。
设置
先决条件
- Python 3.11+
- 克劳德桌面版 或任何MCP兼容客户端
- OpenAI API密钥(用于LangGraph主机)
安装
git clone https://github.com/yourname/speculative-system-designer
cd speculative-system-designer
python -m venv .venv
# Windows
.venv\Scripts\activate
# Unix / macOS
source .venv/bin/activate
pip install -r requirements.txt
配置
cp .env.example .env
# Edit .env — set OPENAI_API_KEY at minimum
关键环境变量:
| 变量 | 默认值 | 描述 |
|---|---|---|
OPENAI_API_KEY |
-- | LangGraph主机需要 |
SERVER_PYTHON |
.venv/Scripts/python.exe (赢)/ .venv/bin/python (Unix) |
通往venv Python的路径 |
SERVER_MODULE |
server.mcp_server |
MCP服务器的Python模块路径 |
ELICITATION_MODE |
llm |
llm 对于完全代理, human 每次权衡时都要停下来 |
LLM_MODEL |
gpt-4o-mini |
用于采样和启发的OpenAI模型 |
EXCALIDRAW_MCP_URL |
https://mcp.excalidraw.com/mcp |
排除MCP服务器URL |
跑步
选项A——LangGraph主机(完全代理运行)
# Full run: generate → evaluate all futures → finalize → draw both diagrams
python -m host.run
# Skip evaluation: generate + draw initial diagram only
python -m host.run --no-eval
运行结束输出:
========================================
DESIGN SESSION COMPLETE
========================================
Architecture ID : <uuid>
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分支,它提供了一个与克劳德兼容的服务器,用自包含的工具逻辑替换采样和启发式: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 在每一个批评都得到解决之前,拒绝产生最终的架构。