关于 Design Pattern MCP
Design Pattern MCP 是面向 AI 编码 Agent 的设计模式选择与设计生成服务。它基于自然语言需求检索模式,并可生成实现方案、评估质量属性;内置模式含多种语言变体。
主要功能
- 01按系统描述分析设计需求
- 02检索并选择设计模式
- 03生成参与者与实现步骤
- 04按八项质量属性评估设计
- 05异步提交并查询设计任务
- 06浏览和读取模式目录
适用场景
- 01为新系统选择合适的设计模式
- 02让编码 Agent 获取模式决策和理由
- 03评估现有设计的质量属性
MCP 相关
作为 MCP 服务向客户端提供模式分析、设计生成、设计评估、目录查询及异步任务工具,并提供工作流提示词和变体资源。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm -e DESIGN_PATTERN_GENERATOR_API_KEY -e DESIGN_PATTERN_GENERATOR_PROVIDER -e DESIGN_PATTERN_GENERATOR_MODEL -e DESIGN_PATTERN_GENERATOR_BASE_URL -e DESIGN_PATTERN_PATTERN_DIRECTORY -e DESIGN_PATTERN_EMBEDDER_BASE_URL -e DESIGN_PATTERN_EMBEDDER_API_KEY -e DESIGN_PATTERN_RERANKER_BASE_URL docker.io/olkowa/design-pattern-mcp:1.0.1DESIGN_PATTERN_GENERATOR_API_KEY密钥API key for the generator LLM (LiteLLM provider).DESIGN_PATTERN_GENERATOR_PROVIDERLiteLLM provider prefix, e.g. openai, anthropic, openrouter.DESIGN_PATTERN_GENERATOR_MODELModel name, combined as <provider>/<model> via LiteLLM.DESIGN_PATTERN_GENERATOR_BASE_URLOpenAI-compatible base URL (LiteLLM api_base).DESIGN_PATTERN_PATTERN_DIRECTORYDirectory with the catalog records (*.json main records plus per-language files). The image bakes the 438-record catalogue at /app/pattern.DESIGN_PATTERN_EMBEDDER_BASE_URLRequired: the dense retrieval leg embeds the 438-record catalogue during startup warmup and the server refuses to start when the endpoint is unreachable. TEI embeddings endpoint: on the Docker host use http://host.docker.internal:8080 (add --add-host=host.docker.internal:host-gateway on Linux); on a shared Docker network use http://design-pattern-tei-embed:8080.DESIGN_PATTERN_EMBEDDER_API_KEY密钥API key for the embedder. Keyless TEI accepts any non-empty value; use 'not-needed' there.DESIGN_PATTERN_RERANKER_BASE_URLRequired: retrieval runs a mandatory TEI cross-encoder rerank whenever the fused pool has more than one candidate. On the Docker host use http://host.docker.internal:8080; on a shared Docker network use http://design-pattern-tei-rerank:8080.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
design-pattern-mcp
一个 MCP(Model Context Protocol)服务,为 AI 编码 Agent 提供设计模式选择方面的专业能力。给定对系统的自由文本描述,它会分析问题、检索匹配的设计模式(来自 486 个内置模式,每种模式均以 13 种具体语言和一种通用绑定实现),生成具体设计,其中包含主要模式、辅助模式、参与者、交互、实现步骤和语言绑定变体,并依据八个设计级质量维度(可测试性、可扩展性、可维护性、简洁性、性能、可靠性、安全性、可伸缩性)进行评估。其流水线(分析 → 权重 ∥ 检索 → 生成 → 评估 → 有界优化)及线协议契约详见 docs/implementation-guide.md。
目录
- ⚡ 快速开始
- 通过 AI Agent 安装
- 🔌 连接你的 Agent
- 🧑🏫 面向 AI Agent 的 SKILL
- 🧪 使用工具
- 🛠️ 工具一览
- 💬 提示词
- 📖 模式目录
- 与 architecture-pattern-mcp 协同工作
- 安装替代方案
- 配置
- 结构化推理
- 长时间运行的工具与超时
- 基准测试与验证
- 故障排除
- 构建与开发
- 发布
- systemd 服务(Linux)
- 许可证
⚡ 快速开始
# 1. Clone
git clone https://git.odvlh.xyz/olli/design-pattern-mcp.git && cd design-pattern-mcp
# 2. Add your API key
export MINIMAXAI_API_KEY=your_key_here
# 3. Build + start (three images: MCP server + TEI embed + TEI rerank)
make docker-build-all
make docker-up
# 4. Demo (analyze-only: fast. For the full design: make client-design)
make client-analyze
make client-async # full design via the background job trio
服务通过 streamable-http 在 http://localhost:8822/mcp 上启动(开发用 compose 将主机端口 8822 映射到容器端口 8050)。make client-analyze / make client-design 的目标地址为 http://localhost:8822/mcp;可通过 CLIENT_URL 覆盖。
通过 AI Agent 安装
如需让 AI 编码 Agent 使用预构建镜像安装 Docker 栈(无需本地构建),请将其指向 INSTALL.md — 这是供 Agent 使用的非交互式安装手册(包含可复制粘贴的命令、预期输出和验证探测)。
Clone this repo, then follow INSTALL.md Path A (compose stack, port 8822).
My generator API key is: sk-...
在 INSTALL.md 中选择一种方案:
- 方案 A — Compose 栈(
:8822):用于评估 / 开发,无需 sudo。使用自有 TEI sidecar 副本。 - 方案 B — systemd 栈(
:8812):持久运行的主机服务,开机时启动,需要sudo。使用共享的pattern-tei-infraTEI 栈。
Agent 需要克隆的仓库、已加入用户组的 Docker ≥ v2.20、约 8 GB 可用空间、一个 MINIMAXAI_API_KEY(或按 INSTALL.md#switching-the-generator-llm 使用其他提供方),以及 linux/amd64 环境(在 arm64 上会通过 make docker-build-all 在本地构建)。开发栈和 systemd 栈可在默认主机端口上同时运行(8822 与 8812)。安装完成后,请继续阅读下方的连接你的 Agent。
🔌 连接你的 Agent
Claude Code
# Install (one-time)
uv pip install -e .
# Run as stdio subprocess — pass the API key via env
claude mcp add design-patterns \
-e GENERATOR_PROVIDER=minimax \
-e GENERATOR_MODEL=minimax/MiniMax-M2.7 \
-e GENERATOR_BASE_URL=https://api.minimax.io/v1 \
-e GENERATOR_API_KEY=your_key \
-- design-pattern-mcp --transport stdio
Stdio 模式需要能够访问 TEI 端点(默认地址为 http://design-pattern-tei-embed:8080,重排器 URL 来自 config/config.json)——可以运行 compose 堆栈的 sidecar,也可以将 EMBEDDER_BASE_URL / RERANKER_BASE_URL 设置为你自己的地址。若要进行无需网络的冒烟测试,请使用 --offline 启动(采用确定性的纯词法检索,而非 TEI)。
OpenCode
OpenCode 使用 HTTP 传输。先启动服务端,然后配置 opencode:
# Terminal 1: start the server
make docker-up
# or locally:
uv run design-pattern-mcp --transport streamable-http --port 8050
# Terminal 2: add to ~/.config/opencode/opencode.json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"design-patterns": {
"type": "remote",
"url": "http://localhost:8822/mcp"
}
}
}
**注意:**生成器 API 密钥由服务端从其配置文件(
config/config.json,{env:...}占位符)读取,而不是从 opencode 的环境中读取。
Codex CLI
# Install (one-time)
uv pip install -e .
添加到 ~/.codex/config.toml:
[mcp_servers.design-patterns]
command = "design-pattern-mcp"
args = ["--transport", "stdio"]
[mcp_servers.design-patterns.env]
GENERATOR_PROVIDER = "minimax"
GENERATOR_MODEL = "minimax/MiniMax-M2.7"
GENERATOR_BASE_URL = "https://api.minimax.io/v1"
GENERATOR_API_KEY = "your_key"
或通过 CLI:
codex mcp add design-patterns \
-e GENERATOR_API_KEY=your_key \
-- design-pattern-mcp --transport stdio
🧑🏫 面向 AI Agent 的 SKILL
skills/design-pattern-mcp/ 中的 SKILL 是为 Oh My Pi (OMP) 编写的。在 OMP 中,此服务的工具可通过 xd://mcp__design_pattern_local_* 设备访问,且每个 MCP 请求都有截止时间(OMP_MCP_TIMEOUT_MS → 每服务的 timeout → 30 s)。它会教 Agent 如何选择符合截止时间的入口点、如何读取结果,以及完整的工作流配方。
skills/design-pattern-mcp/
├── SKILL.md # OMP constraints, entry-point decision guide, device quick reference
└── references/
├── tools.md # 9 tool signatures, arg shapes, status codes
└── workflows.md # workflow recipes, result interpretation, troubleshooting
为 OMP 安装——将技能目录复制到 OMP 的用户技能根目录:
cp -r skills/design-pattern-mcp ~/.omp/agent/skills/
随后,技能会告诉 Agent:
- 哪个入口点符合 OMP 的 30 s 截止时间:默认使用异步任务三件套;
select_design_patterns(完整流水线)需要 2–10 min - 哪条路径适合该任务:Agent 自行编写代码时使用
analyze_design(提供决策和理由,不生成设计);调用方需要设计时使用完整流水线 job_id是唯一持久句柄——请持久化保存(SQLite 任务存储,JOBS_DB)- 如何编写
description(组件名称 + 职责 + 约束),并将language、paradigm、focus作为单独的结构化参数传入 - 如何解读
overview.score(0–100)、findings[]和各部分的选择结果
references/ 中的工具 schema 与客户端无关;只有截止时间和设备相关指南是 OMP 专用的。
🧪 使用工具
所有流水线工具都将自由文本 description 作为主要参数。下面的示例展示了确切的工具调用形式,因此你可以在任何 MCP 客户端或 API 使用方中使用。
设计你的第一个选择
在 Claude Code(或任何 MCP 客户端)中,粘贴以下自然语言指令:
We're building the ingestion path of an IoT platform: a Kafka consumer
service parses 10k events/sec, an enricher adds geolocation from Redis,
and a writer persists to InfluxDB and S3. Components: consumer, parser,
enricher, sink. Constraints: at-least-once delivery, backpressure, Python.
你的 Agent 会在内部调用 select_design_patterns。服务端会返回 DesignResult:overview(主要模式、辅助模式、原则、分数、推理过程)、applications[](参与者、交互、实施步骤、代码草图、包含绑定变体的语言绑定)、interactions[]、quality_attributes、test_strategy 和 risks[]。
**自行编写代码的编码 Agent 应改为调用 analyze_design。**它会运行相同的 ANALYZE 阶段(§1–§8:需求修复 → 决策部分 → 按部分检索 → 质量权重 → 选择),并在此处停止:你将获得修复后的需求、决策部分以及每个部分的一个选择结果(主要模式、辅助模式、排序后的备选方案、否决记录、权重),但不会生成应用方案,也不会进行评估。选择结果只指出每个部分要应用 什么,而不是 如何 应用——参与者、实施步骤、代码草图和语言绑定由 generate_design 根据该数据生成(也可以按模式通过 get_design_pattern 读取);应根据应用方案实施,切勿仅凭模式名称实施。select_design_patterns 仍是设计路径——它会继续执行 GENERATE → EVALUATE,而完整运行的大部分耗时都花在这两个阶段。无论选择哪种方式,决策都来自相同的 ANALYZE 阶段——每次调用都会重新进行 LLM 分析,因此两种路径都不具幂等性;如果之后需要生成具体的应用方案,请将 analyze_design 的载荷传递给 generate_design。
也可以直接从你的 Agent 调用工具:
Call select_design_patterns with:
description: "IoT ingestion path: Kafka consumer (10k events/sec) → JSON parse
→ Redis geo-enrich → InfluxDB + S3 sink. At-least-once, backpressure."
language: "python"
Call list_design_patterns() # all 486 patterns
Call list_design_patterns(category="workflow-messaging")
Call get_design_pattern(name="process-manager")
异步作业模式:submit_design_job + get_design_status
适用于请求超时较短的客户端。submit_design_job 会立即返回 job_id;轮询 get_design_status,直到作业完成:
# Step 1: start the job
Call submit_design_job with:
description: "IoT ingestion path: Kafka consumer → parse → Redis enrich → InfluxDB + S3"
# Step 2: poll every 10-30 seconds
Call get_design_status with:
job_id: "<job_id from step 1>"
# → status is "pending" | "running" | "completed" | "failed" | "cancelled"
# When status is "completed", the full DesignResult is in result
# When status is "failed", the error is in error
在 Python 中(通过 MCP 客户端 API——参见 examples/design_pattern_client_async.py):
import asyncio
from fastmcp import Client
SERVER = "http://localhost:8822/mcp"
POLL_EVERY = 15 # seconds
async def main():
client = Client(SERVER)
async with client:
# Step 1: start the job
result = await client.call_tool(
"submit_design_job",
{"description": "IoT ingestion: Kafka → parse → enrich → sink"},
)
job_id = result.data["job_id"]
print(f"Job started: {job_id}")
# Step 2: poll until terminal state
while True:
await asyncio.sleep(POLL_EVERY)
status = await client.call_tool("get_design_status", {"job_id": job_id})
data = status.data
print(f" status={data['status']}")
if data["status"] in ("completed", "failed", "cancelled"):
break
print(data.get("result", data)) # full DesignResult when completed
asyncio.run(main())
完整的可运行示例请参见 examples/design_pattern_client_async.py。运行方式:
make docker-up # Terminal 1
make client-async # Terminal 2
🛠️ 工具一览
| 工具 | 描述 |
|---|---|
select_design_patterns | 面向需要生成设计的调用方的设计路径:完整流水线(分析 → 权重 ∥ 检索 → 生成 → 评估 → 优化,最多尝试 LOOP_MAX_TRIES 次),返回包含参与者、实现步骤、绑定变体、质量属性、测试策略和风险的应用。运行时间较长(2–10 分钟)。采用心跳保护。 |
analyze_design | 面向自行编写代码的编码 Agent 的仅分析路径:需求 → 简述、决策部分及各部分的选择(主要、辅助、备选、否决项、权重),不生成设计。编码前,使用相同的负载调用 generate_design,以获取参与者、步骤和语言绑定。运行时间较长(约 1 分钟)。 |
generate_design | 根据先前 analyze_design 的负载或明确指定的模式生成应用。运行时间较长(约 1 分钟)。 |
evaluate_design | 根据八个质量维度对现有设计 JSON 评分。运行时间较长(约 30 秒)。 |
submit_design_job | 启动后台选择作业并立即返回 job_id(由 SQLite 提供支持,服务重启后仍可保留)。适用于请求超时较短的客户端。 |
get_design_status | 轮询作业状态;作业完成后返回完整的 DesignResult。 |
cancel_design | 取消正在运行的作业(尽力而为;在下一个流水线阶段边界生效)。 |
list_design_patterns | 列出模式及其名称/类别/描述;可按 category 和/或 language 筛选。 |
get_design_pattern | 获取单个名称对应的完整模式 JSON(可由加载器解析,并支持别名)。 |
描述、语言、范式、重点都是结构化参数——应将它们作为单独的工具参数传入,而不是嵌入描述文本。变体选择基于信号:不存在覆盖整个目录的 variant_id 枚举(变体 ID 按(模式,语言)分别设置,并会随目录编辑而变化;有效 ID 可通过 design-patterns://variants 资源和以往运行的 bound_variant 发现)。
💬 提示词
此服务还提供四个由用户调用的工作流提示词(MCP 客户端中的斜杠命令)。与工具不同,大模型不会自主调用提示词——用户选择一个提示词并填写其参数。每个提示词都封装了一套经过测试的工具编排方案,且都不会硬编码模式名称(类别指导会引用实时目录自身的结构)。
| 提示词 | 参数 | 功能 |
|---|---|---|
/select_design_patterns_workflow | description*、language、architecture_style、paradigm、focus | 引导式端到端选择工作流 |
/explore_pattern_catalog | category | 通过 design-patterns://variants 资源和决策探查,发现实时目录内容 |
/evaluate_my_design | description* | 将目录中的选择结果与现有设计进行比较 |
/compare_pattern_candidates | decision*、candidates* | 针对一个决策并列比较两个或三个指定模式;每个候选模式运行一次选择 |
* = 必填参数
仅支持工具的客户端
仅支持工具协议(不支持原生 prompts/list 或 prompts/get)的客户端,可通过生成的 list_prompts 和 get_prompt 工具访问全部四个工作流提示词。这些工具会经过服务的中间件链路,其处理方式与原生提示词调用完全相同。
📖 模式目录
通过 MCP 工具(推荐 — 适用于所有客户端)
list_design_patterns() # all 486 patterns
list_design_patterns(category="concurrency-resilience") # filter by category
get_design_pattern(name="acceptor-connector") # full pattern JSON
有效的 category 值(九个决策组,即目录第 1–9 节):domain-modeling、construction-composition、behavior-selection、interaction-boundary、workflow-messaging、persistence-state、concurrency-resilience、security-verification、specialized-components。
通过 MCP 资源
mcp_read_resource(uri="design-patterns://variants")
按语言、再按模式汇总每种模式的语言实现,提供 language/paradigm/focus 提示和变体选择所需的实现词汇。
每条模式记录包含的内容
加载器(src/patterns/loader.py)会合并 14 个文件中的记录:一个通用条目,以及每种具体语言各一个(13 种语言:C、C++、C#、Go、Java、JavaScript、Kotlin、Python、Ruby、Rust、Scala、Swift、TypeScript)。每个语言绑定都包含 support(native/idiomatic/discouraged)、idiom_name、implementation_notes、可选的 code_sketch、pitfalls,以及在支持级别为 discouraged 时必需的 alternative。语言绑定可以声明 variants[](按语言划分的实现,目录中共有 4,953 个)及 default_variant。每条记录还会声明 related_patterns(仅限加载器可解析的规范名称)和 related_patterns_notes(对每个关联的区别说明)。
别名解析已内置:加载器会将 993 个原始别名条目 → 经过大小写折叠后得到 978 个不同别名 → 建立 966 个索引别名映射,并丢弃有歧义的别名(别名本身是另一条记录的名称,或被两条记录同时声明;ambiguous_aliases 健康状态字段会列出这些别名),因此 get_design_pattern(name="circuit-breaker") 及其别名拼写都会解析到同一条记录。当目录中的枚举与 docs/pattern-schema.json 不一致时,启动会立即失败(assert_enums_in_sync)。
与 architecture-pattern-mcp 协同使用
此服务回答的是 如何编写每个单元(设计模式);其姊妹项目 architecture-pattern-mcp 回答的是 要构建哪种系统形态(架构)。架构模式用于构建整个系统的结构——包括其组件、组件之间的关系,以及 API/数据/事件契约(例如 event-driven、pipe-and-filter、microservices,选自姊妹项目的 40 种模式目录)。设计模式则用于解决单个组件内部反复出现的实现问题——包括参与者、交互方式和绑定语言的代码(例如 observer、process-manager、circuit-breaker,选自上方的 486 种模式目录,每种模式均提供 13 种语言绑定)。
建议顺序——先宏观,后微观:
1. Call the sibling's design_architecture with:
requirements: "ETL pipeline for IoT: Kafka → JSON → Redis geo-enrich → InfluxDB + S3"
domain: "data-processing"
# → components (Kafka source, parser filter, enricher, sinks),
# relationships, API contracts, data models, event contracts
# Keep design_architecture for callers that want the design: the sibling's LLM
# generates the architecture artifact (5–10 min) rather than only advising.
2. For each component with non-trivial internal logic, ask for the pattern decisions:
- coding agent writes the code itself → analyze_design with:
description: "<component name + responsibilities + constraints from step 1>"
language: "python"
# → decision parts + per-part selection with rationale; no generated applications
- caller wants the generated pattern design → select_design_patterns with:
description: "<component name + responsibilities + constraints from step 1>"
language: "python"
# → primary + supporting patterns, participants, implementation_steps,
# code_sketch with the bound language variant
两个模式调用都会运行相同的 ANALYZE 阶段(§1–§8);analyze_design 会在此处停止,不进入生成和评估阶段。每次调用都会单独运行 LLM 分析(这两个工具都不是幂等的),因此针对相同描述的两次调用可能会选出不同模式。
IoT 示例中的具体衔接方式:姊妹项目会生成 pipe-and-filter 架构(Kafka 源 → JSON 解析器过滤器 → 地理位置增强器 → InfluxDB/S3 接收端,契约中包含至少一次投递和背压)。接下来,地理位置增强器的缓存未命中路径就交由此服务处理:当你的 Agent 编写增强器时,analyze_design 会返回 circuit-breaker + retry 决策及其理由;而当调用方需要生成的设计时,select_design_patterns 还会进一步生成具体应用内容(参与者、implementation_steps、Python code_sketch)。这些内部细节会被架构层面的组件契约引用,但不会在架构层实现。
对于 analyze_design 路径,在编写增强器代码前,先对返回的载荷运行 generate_design(或运行 get_design_pattern("circuit-breaker"),查看 Python 绑定和注意事项)。
同时运行两个服务(开发主机端口为 :8060(架构)、:8822(设计模式);systemd 端口为 :8050/:8812)。两者彼此独立——探索时先运行哪个都可以——但先处理架构有助于稳定组件边界,再由此服务补充内部实现。
安装替代方案
Docker(手动)
# Build the three images (dense vector cache generated and baked in)
make docker-build-all
# Run with your API key
MINIMAXAI_API_KEY=your_key make docker-up
首先,docker-build-all 会通过 TEI 嵌入侧车生成 FAISS 稠密缓存(data/dense-cache/),并将其打包进服务镜像;make docker-build-nocache 则会跳过此步骤(首次启动时会嵌入语料,约需 19 分钟)。
两个 compose 堆栈都使用命名 Docker 卷(design-pattern-mcp-dev-dense-cache/design-pattern-mcp-systemd-dense-cache,挂载于 /app/data/dense-cache)保存该缓存目录。新卷会从镜像内置的快照初始化,之后所有回写都会保存到卷中——因此,重启或重新创建容器时会重新加载向量,而不是重新嵌入整个模式目录(只需嵌入一次探测文本,而非约 486 份文档)。docker compose down 会保留该卷。
这会带来两项运维后果:镜像重建后,已有卷会遮蔽较新的内置快照,此时指纹检查会强制重新嵌入一次——因此,在目录或模型发生变化后重建镜像时,请先删除该卷(docker volume rm design-pattern-mcp-dev-dense-cache),再让 up -d 重新初始化它。此外,DENSE_CACHE_MODE=off 会完全禁用缓存——compose 堆栈会将主机上的值传入容器,而主机上运行的服务会直接读取该值。详情请参阅:稠密嵌入缓存。
本地开发(uv)
前置条件: Python 3.12+、uv
# Install
make install
# Configure — the shipped wire config with {env:...} placeholders
export GENERATOR_PROVIDER=minimax
export GENERATOR_MODEL=minimax/MiniMax-M2.7
export GENERATOR_BASE_URL=https://api.minimax.io/v1
export GENERATOR_API_KEY=your_key
# Dense cache (host runs): the image default /app/data/dense-cache does not exist
# on a workstation, so point it at the gitignored repo snapshot. `make embed-cache`
# fills it; otherwise the first start embeds once and writes it (later starts warm).
export DENSE_CACHE_PATH="$PWD/data/dense-cache"
# Run the server
uv run design-pattern-mcp --transport stdio # for Claude Code / Codex
uv run design-pattern-mcp --transport streamable-http --port 8050 # HTTP
uv run design-pattern-mcp --health # offline readiness check (config + catalog + index)
也可使用已安装的控制台脚本(运行 make install 后):design-pattern-mcp。
要获得完整的检索质量,需要 TEI sidecar:启动时会融合一个稠密检索通路(通过 embed sidecar 使用 Qwen3-Embedding-0.6B)和一个 BM25 检索通路(service_healthy 顺序保证 embedder 在服务接受请求前启动;如果索引预热失败,服务将拒绝启动)。Reranker sidecar(gte-reranker-modernbert-base)会对候选模式重新排序。如果 sidecar 无法访问,请使用 --offline 启动(仅进行确定性的词法检索,不访问网络)。
TEI 启动: 两个 sidecar 镜像(
docker/Dockerfile.tei-embed、docker/Dockerfile.tei-rerank)都包含自行构建的 TEI router(v1.9.4 + 尚未合并的 PR #884),默认设置WARMUP_TOKENS=0——这会跳过合成预热过程(实测 embed 约需 131 s,rerank 约需 78 s),让容器仅在权重加载后就绪。可通过TEI_WARMUP_TOKENS/TEI_RERANK_WARMUP_TOKENS分别覆盖各 sidecar 的设置(正整数 N 会改为使用 N 个 token 预热;大于MAX_BATCH_TOKENS的值会在启动时被拒绝)。
GPU TEI: 随附的 sidecar 是 CPU 镜像(cpu-1.9)。在配备 TEI-CUDA 支持的 NVIDIA GPU(计算能力 ≥ 7.5)及 NVIDIA 容器工具包的主机上,make docker-build-tei-embed-gpu 会基于 CUDA 镜像(ghcr.io/huggingface/text-embeddings-inference:cuda-1.9)构建 embed sidecar;make docker-up-gpu 则会使用 docker/docker-compose.gpu.yml 启动开发栈(分配 1 个 GPU)。此覆盖配置仅适用于 embed sidecar——reranker 仍使用 CPU。在 GPU 上,可将 WARMUP_TOKENS 设为较小的正数,以便在启动时预热内核,而不是跳过预热过程。
已运行的 embedder/reranker: 如果 embedding 和 reranking 端点已在其他服务器上运行,可通过 EMBEDDER_BASE_URL(OpenAI 兼容路由,http://<host>:8080/v1)和 RERANKER_BASE_URL(http://<host>:8080)将 MCP 服务指向这些端点,并跳过 sidecar。embedder 会通过 LiteLLM 转发 EMBEDDER_MODEL(默认值为 openai//data/embedding-model)和 EMBEDDER_API_KEY——提供商/模型语法见 LiteLLM embedding 文档。reranker 不是 LiteLLM 客户端——它会直接向 {base_url}/rerank 发送 POST 请求(src/patterns/safe_tei_rerank.py),因此 RERANKER_MODEL(默认值为 Alibaba-NLP/gte-reranker-modernbert-base)是 TEI 模型名称,而不是 LiteLLM 模型字符串。使用不同模型的远程 embedder 会导致预构建的稠密缓存失效——请运行 make embed-cache 重新生成缓存,确保向量与所用模型一致。
配置
config.json
服务会读取相对于仓库路径的 config/config.json(可通过 --config-path 或 CONFIG_PATH 覆盖)。每个值都是 {env:VAR:-default} 占位符,会在加载时展开,因此随附文件也可作为环境变量参考。架构(节选):
{
"logging_level": "{env:LOGGING_LEVEL:-INFO}",
"transport": "{env:TRANSPORT:-stdio}",
"host": "{env:HOST:-0.0.0.0}",
"port": "{env:PORT:-8050}",
"generator": {
"provider": "{env:GENERATOR_PROVIDER:-openai}",
"config": {
"model": "{env:GENERATOR_MODEL:-gpt-4o-mini}",
"base_url": "{env:GENERATOR_BASE_URL:-https://api.openai.com/v1}",
"api_key": "{env:GENERATOR_API_KEY:-}",
"temperature": "{env:GENERATOR_TEMPERATURE:-0.1}",
"timeout_seconds": "{env:GENERATOR_TIMEOUT_SECONDS:-120.0}",
"generation_batch_size": "{env:GENERATOR_GENERATION_BATCH_SIZE:-5}",
"generation_max_concurrency": "{env:GENERATION_MAX_CONCURRENCY:-2}",
"circuit_breaker_failure_threshold": "{env:GENERATOR_CIRCUIT_BREAKER_FAILURE_THRESHOLD:-3}",
"circuit_breaker_cooldown_seconds": "{env:GENERATOR_CIRCUIT_BREAKER_COOLDOWN_SECONDS:-60.0}"
}
},
"embedder": { "provider": "{env:EMBEDDER_PROVIDER:-tei}", "config": { "base_url": "{env:EMBEDDER_BASE_URL:-http://design-pattern-tei-embed:8080}" } },
"retrieval": { "bm25_weight": "{env:RETRIEVAL_BM25_WEIGHT:-0.3}", "dense_weight": "{env:RETRIEVAL_DENSE_WEIGHT:-0.7}", "max_concurrent_parts": "{env:RETRIEVAL_MAX_CONCURRENT_PARTS:-1}" },
"reranker": { "rerank_text": "{env:RERANK_TEXT:-full}", "rerank_top_n": "{env:RERANK_TOP_N:-10}" },
"reasoning": { "enabled": "{env:REASONING_ENABLED:-true}", "per_phase": { "analyze": { "enabled": "{env:REASONING_ANALYZE_ENABLED:-true}" }, "weights": { "enabled": "{env:REASONING_WEIGHTS_ENABLED:-true}" }, "generate": { "enabled": "{env:REASONING_GENERATE_ENABLED:-true}" }, "evaluate": { "enabled": "{env:REASONING_EVALUATE_ENABLED:-true}" } }, "max_injected_chars": "{env:REASONING_MAX_INJECTED_CHARS:-}" },
"validation": { "max_retries": "{env:VALIDATION_MAX_RETRIES:-1}", "retry_on_fail": "{env:VALIDATION_RETRY_ON_FAIL:-true}", "truncation_rerun": "{env:VALIDATION_TRUNCATION_RERUN:-repair}", "truncation_boost_max_completion_tokens": "{env:VALIDATION_TRUNCATION_BOOST_TOKENS:-65536}" },
"prompts": { "stable_prefix_first": "{env:PROMPT_STABLE_PREFIX:-false}" },
"loop": { "max_tries": "{env:LOOP_MAX_TRIES:-2}", "min_quality_score": "{env:LOOP_MIN_QUALITY_SCORE:-50}" },
"tasks": { "deadline_seconds": "{env:TASKS_DEADLINE_SECONDS:-1800.0}", "heartbeat_enabled": "{env:TASKS_HEARTBEAT_ENABLED:-true}", "heartbeat_interval_seconds": "{env:TASKS_HEARTBEAT_INTERVAL_SECONDS:-30}" },
"jobs": { "db_path": "{env:JOBS_DB:-~/.config/design-pattern-mcp/jobs.db}" }
}
生成器大模型(LlamaIndex LiteLLM)
生成器大模型通过 LlamaIndex LiteLLM 集成访问。所有提供商设置都遵循 LiteLLM 的模型语法:<provider>/<model>。开发栈和基准测试框架默认使用 MiniMax:
| 配置 / 环境变量 | 开发栈默认值 |
|---|---|
GENERATOR_PROVIDER | minimax |
GENERATOR_MODEL | minimax/MiniMax-M2.7 |
GENERATOR_BASE_URL | https://api.minimax.io/v1 |
GENERATOR_API_KEY | 回退使用 MINIMAXAI_API_KEY(主机环境变量或 .env) |
提供商列表及语法:LiteLLM 提供商文档。temperature、top_p、top_k、timeout_seconds 和熔断器参数会映射到相应的 LiteLLM/提供商参数。
稠密 embedding 缓存(重启后仍持久的检索)
默认启用。 随附配置解析为 dense_cache.mode=auto,且 write=true(Pydantic 默认值和 config/config.json 均如此;两个 compose 栈也都显式设置了相同值),服务路径始终会传入缓存——容器栈完全不需要配置缓存,主机运行只需导出一个变量(DENSE_CACHE_PATH,见“本地开发”部分)。只有在明确要求时才会关闭缓存:设置 DENSE_CACHE_MODE=off(用于 compose 栈或主机运行),在配置文件中设置 mode=off,使用 --offline/--health 路径(确定性嵌入器,不使用缓存),以及使用基准测试的 compose 覆盖配置(测量冷启动)。
tests/unit/test_config.py::test_dense_cache_defaults 和 ::test_shipped_config_validates 固定了这些默认值。
密集检索环节使用一个 FAISS 索引,涵盖目录中的所有记录(486 条记录 → 启动时生成 486 个 document_text 嵌入)。该矩阵会与清单一起快照到磁盘,并在下次启动时重新加载,因此重启只需生成一个探测嵌入,而不必遍历整个语料库(在 CPU TEI 旁路容器上约需 19 分钟)。
- 路径:
DENSE_CACHE_PATH(镜像中的路径为/app/data/dense-cache)。 内容:manifest.json(提交标记)+vectors.npy。没有可用清单的目录会被视为普通未命中;mode=require会将未命中转为启动错误(ERR_008),mode=off则完全跳过读取缓存。 - 写入时机:冷启动嵌入完成后立即写入(
DENSE_CACHE_WRITE=true),因此即使之后的启动步骤失败,快照也已写入磁盘。先写入向量文件,再写入清单,因此如果写入中途崩溃,下次启动会将该目录视为未命中。 - 容器会将其持久化到命名卷
(
design-pattern-mcp-dev-dense-cache/design-pattern-mcp-systemd-dense-cache)——因此快照在容器重建、执行docker compose down和升级镜像后仍会保留。新建卷会从镜像内置的快照初始化;现有卷则保留自身内容,因此在更改目录或模型后重新构建时,应在执行up -d前删除该卷(docker volume rm <name>),否则校验会强制重新嵌入一次(约 19 分钟)并重写该卷。要获得真正的冷缓存,需要使用不含缓存的镜像(make docker-build-nocache)或设置mode=off——DENSE_CACHE_MODE=off同时适用于 compose 堆栈和主机运行。 - 自动重建:嵌入函数发生变化(模型字符串、文本指令、声明的
model_repo/onnx_variant)、目录发生变化(记录数量、名称顺序或任何document_text——corpus_sha256)、vectors.npy缺失/形状无效/范数异常,或探测向量显示相同配置背后的模型已变更(重新部署的 TEI sidecar;余弦值低于dense_cache.probe_min_cosine)时,都会触发重建。每次此类变更只会重新嵌入一次:重建的快照会以新身份写回,因此下次重启时会再次命中(由较早版本构建的缓存,例如缺少EMBED_MODEL_REPO时构建的缓存,也会因相同原因未命中一次)。每项决策都会记录所用目录(compose 堆栈中的 JSON 日志字段):Dense cache hit(path、records、mode)、Dense cache miss(reason、path)、Dense cache written(path、records、dimension)、Dense cache disabled(mode、path)、Dense cache write failed(path、reason)——此外,检索器还会记录摘要Dense vectors resolved(source=cache-hit/embedded/disabled,documents)。 - 尽力写入:缓存目录为只读或已满时,会记录
Dense cache write failed,运行会继续使用刚刚嵌入的向量——缓存写入不会导致启动失败。 - 单写入者:不使用锁。每个缓存目录只运行一个服务进程(dev 和 systemd 堆栈使用不同的卷;基准测试工具会固定使用自己的临时
data/dense-cache,并在缓存为冷缓存时拒绝运行,除非设置mode=off)。 - 主机运行(
uv run python -m src.main)必须将DENSE_CACHE_PATH指向可写目录——随附的默认路径/app/data/dense-cache仅存在于镜像中。可以执行export DENSE_CACHE_PATH=$PWD/data/dense-cache以复用make embed-cache生成的快照,也可以将其指向~/.config/design-pattern-mcp/dense-cache,让首次启动时写入缓存。
关键环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
GENERATOR_API_KEY | (必填) | Generator API 密钥(开发环境技术栈会回退使用 MINIMAXAI_API_KEY) |
GENERATOR_PROVIDER | openai | LiteLLM 提供商前缀(minimax、openai、anthropic、openrouter……) |
GENERATOR_MODEL | gpt-4o-mini | 模型名称;minimax/MiniMax-M2.7 是经过测量的开发环境技术栈默认值 |
GENERATOR_BASE_URL | https://api.openai.com/v1 | API 基础 URL(LiteLLM api_base) |
GENERATOR_TEMPERATURE | 0.1 | 采样温度 |
GENERATOR_TIMEOUT_SECONDS | 120.0 | 限制向模型发送的单个 HTTP 请求 |
GENERATOR_GENERATION_BATCH_SIZE | 5 | 每个 GENERATE 批次的模式数 |
GENERATION_MAX_CONCURRENCY | 2 | 同时进行的 GENERATE 批次数 |
GENERATOR_CIRCUIT_BREAKER_FAILURE_THRESHOLD | 3 | 连续发生符合条件的提供商故障(超时、连接故障、408/429/5xx、空响应)后打开熔断器;身份验证/配置类 4xx 永不计入 |
GENERATOR_CIRCUIT_BREAKER_COOLDOWN_SECONDS | 60.0 | 打开状态的持续时间,之后进行一次半开探测 |
EMBEDDER_BASE_URL | http://design-pattern-tei-embed:8080 | TEI 嵌入器端点 |
EMBEDDER_BATCH_SIZE | 16 | 嵌入批次大小 |
DENSE_CACHE_PATH | /app/data/dense-cache(镜像中) | Dense 端快照目录(manifest.json + vectors.npy):重启时重新加载,无需调用语料嵌入;嵌入器/语料/探测发生任何变化时重建。主机运行时会将其覆盖为可写路径 |
DENSE_CACHE_MODE | auto | auto(未命中时重新嵌入)、require(未命中时在启动时引发 ERR_008)、off(永不读取或写入缓存) |
DENSE_CACHE_WRITE | true | 持久化重新嵌入的语料(尽力而为——写入失败会记录 Dense cache write failed,运行仍会保留其向量) |
RETRIEVAL_DENSE_WEIGHT | 0.7 | Dense 端融合权重(与 ..._BM25_WEIGHT 之和必须为 1.0 ± 1e-3——经配置验证;某一端低于 0.05 时会记录警告) |
RETRIEVAL_BM25_WEIGHT | 0.3 | BM25 端融合权重 |
RETRIEVAL_MAX_CONCURRENT_PARTS | 1 | 每个部分的重排并发数。上限设为 2 会让 CPU TEI 重排 sidecar 的 16,384-token 缓冲区达到饱和(HTTP 429)——除非已对 sidecar 进行测量,否则保持为 1 |
RERANK_TEXT | full | 重排输入文本:full(排序结果如文档所述)或 capsule(快 3.6–4.3 倍,但排序结果会有差异——需显式启用) |
RERANK_TOP_N | 10 | 重排前 N 项 |
REASONING_ENABLED | true | 预 LLM 推理层的总开关 |
REASONING_{ANALYZE,WEIGHTS,GENERATE,EVALUATE}_ENABLED | true | 各推理阶段的开关;禁用的阶段会提供空推理上下文(不调用生成器、不启动子进程、不写入缓存条目),而启用阶段发生故障时会静默降级为提示词内的脚手架 |
REASONING_{...}_THOUGHTS | 2 / 阶段 | 每个阶段的主调用前所需推理步骤数 |
REASONING_STEP_TIMEOUT_SECONDS | 20 | 每次思考的工具调用超时 |
REASONING_MAX_TOTAL_STEPS | 8 | 每个阶段的推理步骤数上限 |
REASONING_MAX_INJECTED_CHARS | 不限 | 注入下游提示词的渲染后推理轨迹上限;超出部分会被截断,并标记为 (reasoning trace truncated at N chars)(经测量的延迟调节项) |
REASONING_FAIL_FAST | false | 推理工具不可达时令服务启动失败(默认:降级并记录 ERROR) |
REASONING_QUIET_STDERR | true | 禁止推理子进程向 stderr 输出;设为 false 可调试启动失败问题 |
VALIDATION_MAX_RETRIES | 1 | 结构化输出修复额度(仅适用于模型输出失败——提供商错误会重新抛出,绝不会触发修复提示词) |
VALIDATION_TRUNCATION_RERUN | repair | 截断处理:repair(默认)、boost(优先处理截断:使用 boost 上限重新运行一次,不会在相同上限下进行修复)、off |
VALIDATION_TRUNCATION_BOOST_TOKENS | 65536 | boost 重运行的 max_completion_tokens(MiniMax M2.x 推荐上限) |
PROMPT_STABLE_PREFIX | false | 调整每个操作的用户提示词顺序,使静态响应契约块置前、每次运行的负载置后(MiniMax 被动提示词缓存优化;两种顺序的内容完全相同) |
LOOP_MAX_TRIES | 2 | 设计循环 GENERATE→EVALUATE 尝试次数 |
LOOP_MIN_QUALITY_SCORE | 50.0 | 提前停止的分数阈值 |
TASKS_DEADLINE_SECONDS | 1800.0 | 单次工具调用/作业的全程运行时限;到期时运行会以 ERR_014 结束(管道内部的 llama-index Workflow 超时为该值的 0.9 倍) |
TASKS_HEARTBEAT_INTERVAL_SECONDS | 30 | 进度通知间隔(应低于客户端的空闲超时时间) |
JOBS_DB | ~/.config/design-pattern-mcp/jobs.db | 异步作业三件套使用的 SQLite 路径 |
PATTERN_DIRECTORY | pattern/ | 模式文件目录 |
LOGGING_LEVEL | INFO | DEBUG 会捕获每次推理思考和每一步的工具响应 |
TRANSPORT | stdio | stdio 或 streamable-http |
PORT | 8050 | HTTP 绑定端口(容器中为 8050;开发主机为 8822,systemd 主机为 8812) |
CONFIG_PATH | config/config.json | 配置文件路径 |
CLI 参数
| 参数 | 说明 |
|---|---|
--config-path | 配置文件路径(默认:config/config.json) |
--transport {stdio,streamable-http} | 覆盖传输模式 |
--host | 覆盖 HTTP 绑定主机(默认:0.0.0.0) |
--port | 覆盖 HTTP 端口(默认:8050) |
--health | 运行离线就绪检查(配置 + 目录 + 索引)并退出 |
--offline | 使用确定性离线嵌入器/重排器提供服务(无网络;仅词法检索) |
结构化推理
在每次提示词 LLM 阶段调用(analyze / weights / generate / evaluate)之前,服务都会运行一个有界的 ThoughtGenerator 循环:每个步骤由生成器 LLM 编写(每个步骤调用一次补全),并提交到 shannonthinking 和/或 code-reasoning MCP 服务——这些结构化思考草稿本会验证、编号并记录每个步骤。生成的轨迹会作为 <reasoning_context> 块注入阶段提示词。约定:每条思路 = 1 次 LLM 补全 + 1 次 MCP 工具调用,上限由 REASONING_MAX_TOTAL_STEPS 控制(默认值为 8)。每个阶段的工具列表默认包含两个草稿本;启动健康检查会准确检查已启用阶段所用的工具。
主要特性:
- 内置于 Docker — 镜像会将两个 npm 包打包进镜像;运行时通过
node直接调用它们(无需网络,也不使用 npx)。 - 在 Docker 外自动回退到 npx — 找不到内置入口时,会回退到
npx -y <pkg>(首次调用时下载)。 - 单次调用静默降级 — 已启用阶段中的任何进程启动、超时或工具故障都会记录 WARNING,阶段随后会使用降级版的提示词内思考框架继续运行;推理中断不会导致工具调用失败。通过
REASONING_<PHASE>_ENABLED=false禁用的阶段则会提供空的推理上下文——不会调用生成器、启动子进程或创建缓存条目。 - 启动时报错,运行时软失败 — 生命周期健康检查会报告推理健康状态;设置
REASONING_FAIL_FAST=true可在工具故障时改为使启动失败。 - 轨迹缓存 — analyze 和 generate 轨迹会针对每个设计请求计算一次,并在设计循环的多次尝试中复用;weights 和 evaluate 轨迹则每次运行都会重新计算。
REASONING_MAX_INJECTED_CHARS— 可限制注入下游的渲染轨迹长度(默认不设上限),以减少每次下游调用的输入令牌数。
延迟说明:完整运行会串行启动子进程,调用 8 次推理工具(每个阶段 2 次);推理占端到端延迟的比重较大。在 Stage-0 基准测试中测得(MiniMax-M2.7 生成器,18 个场景,NO_WARMUP=1):发布配置(所有阶段均启用)的 p50 为 288.4 s/p95 为 439.6 s;设置 REASONING_WEIGHTS_ENABLED=false REASONING_EVALUATE_ENABLED=false 后,p50 为 253.0 s/p95 为 413.9 s——两组均通过质量门槛(总体命中率 ≥ 基线 Wilson 下限)。各阶段开关是文档所述的选择性启用方式。
长时间运行的工具与超时
select_design_patterns 会运行多阶段流水线,每次调用需要数分钟。这由工作负载本身决定:生成器 LLM 会处理融合后的检索候选项、各部分的质量权重以及所有前序阶段的完整输出,并逐个令牌生成大型且严格遵循结构的 JSON 文档。设计循环会重复执行 generate → evaluate,最多达到 LOOP_MAX_TRIES 次。如果调用方只需要模式决策(由 Agent 自行编写代码),analyze_design 只运行 §1–§8,并跳过 generate → evaluate 循环。
四种边界共同约束一次运行——不要混淆
| 边界 | 默认值 | 范围 |
|---|---|---|
GENERATOR_TIMEOUT_SECONDS | 120 s | 向模型发起的一次 HTTP 请求 |
VALIDATION_MAX_RETRIES | 1 | 修复时只会重新发起一次调用,且仅针对模型输出失败 |
LOOP_MAX_TRIES | 2 | GENERATE→EVALUATE 设计循环的尝试次数 |
TASKS_DEADLINE_SECONDS | 1800 s | 整次运行;超时会取消正在进行的工作,并以 ERR_014 结束 |
心跳防护(默认启用)
每个长时间运行的工具都会由并行协程每 30 秒发送一次 progress 通知(TASKS_HEARTBEAT_INTERVAL_SECONDS)。只要接收到任何数据就会重置空闲计时器的客户端,可以在整个流水线运行期间保持连接。
TS-SDK 客户端(Claude Desktop、Cursor)不会因
progress通知而重置超时计时器。
异步作业三件套(适用于超时受限的客户端)
submit_design_job(description, language?, ...) → job_id
get_design_status(job_id) → {status, result | error}
cancel_design(job_id) → best-effort, next stage boundary
submit_design_job 会在毫秒内返回 job_id;流水线会在后台任务中运行,存储使用 SQLite(JOBS_DB)。每隔 10–30 秒轮询一次。这是适用于 TS-SDK 客户端的有效解决方案。
使用直接 HTTP 客户端绕过客户端超时
examples/design_pattern_client.py 是一个直接的 Python HTTP 客户端,没有客户端空闲超时——发起一个阻塞请求,无论耗时多久都会返回完整响应:
make docker-up # Terminal 1
make client-analyze # Terminal 2: analyze_design only
make client-design # Terminal 2: select_design_patterns, the long synchronous call
两个目标都会调用同一个直接客户端(--tool analyze|design)。make client-design 展示了服务端能够正确完成长时间请求——超时问题完全出在客户端。封装了 MCP 的宿主应用(Claude Desktop、Cursor 等)会施加自己的超时限制;在这些应用中请使用异步作业三件套。
基准测试与验证
本仓库将选择质量和流水线延迟视为可测量的属性,而非口头宣称。
基准测试工具(tests/benchmark/、make benchmark-*):包含 18 个场景并划分留出集;make benchmark-live NO_WARMUP=1 OUT=benchmarks/<arm> RUN_ID=<id> 会在进程内运行完整流水线,并按阶段归因(reasoning.、llm.、rerank、embed——调用次数、延迟、字符/秒、修复计数)。make benchmark-analyze 使用相同的评估环节,在 ANALYZE 阶段后停止(选择命中率/召回率/得分,不进行生成);make benchmark-compare 接受一组 analyze/live 配对结果,并报告命中率的 Newcombe 区间、选择指标的变化和命中翻转情况。由于端到端差异来自跳过的生成工作,p95 延迟规则会标记为不适用。make benchmark-selfcheck 会逐项人工核验每个指标公式。make benchmark-compare A=<baseline> B=<candidate> 会应用预先登记的规则——当且仅当候选版本的留出集端到端 p95 改善 ≥ 15 %,且候选版本整体可接受主选项命中率 ≥ 基线整体 Wilson 下界时,候选版本才算胜出——并在候选版本落败时以状态码 1 退出。端到端模式测量的是已部署的 compose 堆栈,不能与进程内模式进行比较。
参考组(已提交至 benchmarks/,均使用 MiniMax-M2.7,包含 18 个场景):
| 组 | 配置 | 端到端 p50 | 端到端 p95 |
|---|---|---|---|
base-fresh | 已发布的默认值(所有阶段均启用推理) | 288.4 s | 439.6 s |
cfg-reasoning | + 关闭 weights/evaluate 推理(通过环境变量选择启用) | 253.0 s | 413.9 s |
code-levers | + L5 截断增强、L6 跟踪上限、L7 稳定前缀(默认均关闭,由环境变量控制) | 223.6 s | 379.2 s |
analyze-20261025 | 仅 analyze 组(不执行 generate/evaluate),配置/环境变量与 live-20261025 相同 | 161.5 s | 378.1 s |
live-20261025 | 完整流水线,NO_WARMUP=1,已发布的默认值 | 273.0 s | 474.6 s |
analyze 与 live 配对结果(2026-10-25,基线 live-20261025,候选版本 analyze-20261025)
测得整体可接受主选项命中数分别为 12/18 和 12/18(Newcombe Δ +0.000 [−0.286, +0.286]);
留出集分别为 4/6 和 5/6(Δ −0.167 [−0.558, +0.295]),recall@5 分别为 0.378 和 0.367。两个场景向每个方向翻转(总计 4 个),因此相同的总数对应的是不同场景;所有划分的区间都包含零。没有依据这些结果更改默认值。
代码开关以可选环境配置的形式提供,不作为默认设置:累计收益声明通过了针对 base-fresh 的预注册门槛(留出集 p95 从 410.9 → 297.5 s = 27.6 % ≥ 15 %,整体 13/18 ≥ 下限),但与 cfg-reasoning 的归因比较记录到候选方案表现较差(+12.5 % < 15 %),因此默认行为保持不变。启用方法:
export VALIDATION_TRUNCATION_RERUN=boost
export REASONING_MAX_INJECTED_CHARS=4000
export PROMPT_STABLE_PREFIX=true
验证层(本仓库的差异化之处):
| 层级 | 内容 | 位置 |
|---|---|---|
| L1 | 单元测试(收集到 418 项,362 个测试函数) | tests/unit/ |
| L2 | Hypothesis 属性预言机(test_config_properties.py 中有 15 项;共收集到 115 项验证测试) | tests/verification/ |
| L3 | 规范花园(FG-* 变异体) | verify/,make verify-fizz-garden |
| L4 | FizzBee 穷举模型(8 个规范:作业运行器/协议、流水线控制、设计循环、推理重试、TEI 回退、检索融合、稠密缓存) | verify/fizz/,make verify-fizz |
| — | 变异测试门槛(mutmut 基线;新增存活变异体会导致 make test-mutations 失败,在 nightly CI 中仅作提示) | verify/mutmut-baseline.json,make test-mutations |
| — | 台账(门控)+ 交叉一致性(提示性自然语言文档分流) | make verify-ledger,make verify-cross-consistency |
每个 .fizz 断言都必须至少杀死一个规范花园变异体,或附带 # spec-explains: 说明;修改决策模块时,必须在同一个 PR 中更新对应的 .fizz 模型。规范性流水线规范见 docs/implementation-guide.md(按 § 编号,并镜像翻译为德语,见 docs/selektions-process-de.md);工具结果的线协议契约见 docs/result-schema.json,目录记录契约见 docs/pattern-schema.json。
故障排查
服务已启动,但看不到工具
- 检查 Agent 的 MCP 连接:Claude Code 使用
/mcp,OpenCode 使用opencode mcp list,Codex 使用codex mcp list - 确认服务进程已启动且目录已加载:
uv run design-pattern-mcp --health会输出OK和 JSON 就绪报告(记录、语言文件、别名) - 确认 TEI 嵌入器运行正常:
docker compose -f docker/docker-compose.yml ps(健康检查的嵌入模型启动等待期为 120 s)
“Connection refused”或启动失败
嵌入器无法访问时,索引预热会拒绝启动服务(快速失败,而不是让你的第一个请求失败):
docker compose -f docker/docker-compose.yml logs design-pattern-tei-embed
本地非 Docker 运行必须将 EMBEDDER_BASE_URL / RERANKER_BASE_URL 指向可访问的端点,或使用 --offline 启动。
大模型提供商错误(502 / 401)
- 确认已设置
GENERATOR_API_KEY(或开发栈中的MINIMAXAI_API_KEY),且密钥未过期 - 确认
GENERATOR_BASE_URL与你的提供商端点一致 - 连续发生 3 次传输失败后,熔断器会打开 60 s(
..._CIRCUIT_BREAKER_*参数);身份验证/配置类 4xx 错误不会计入其中
ERR_012 / ERR_014
ERR_012= 已取消(通过cancel_design取消,或客户端断开连接后在下一阶段边界处取消)ERR_014= 超出整次运行的截止时间(TASKS_DEADLINE_SECONDS);对于确实需要更长时间的运行,请显式调高该值——正在执行的提供商协程会被取消,部分结果会被丢弃
重排器出现 429
一次完整的全文重排请求就已耗尽 CPU TEI sidecar 的 16,384-token 推理缓冲区(MAX_BATCH_TOKENS=8192 会影响请求打包;测量日期为 2026-09-25):retrieve_many 中并发数为 2 的分支会直接失败,并返回 HTTP 429“Model is overloaded”。请保持 RETRIEVAL_MAX_CONCURRENT_PARTS=1(随附默认值);服务端不变量也会确保 dense_top_k + bm25_top_k ≤ sidecar 的 MAX_CLIENT_BATCH_SIZE(48)。
模式 JSON 文件未加载
- 每种模式的记录由 14 个文件合并而成(
pattern/<name>.generic.json+ 13 个<language>.json);通用文件为必需文件 - 使用
./scripts/verify-patterns.sh验证目录(两个 schema,以及 ajv 无法表达的变体检查;映射一致性和孤立项由启动门禁强制检查) - 如果枚举与 schema 不一致,启动会失败(
assert_enums_in_sync)——这是有意为之;应修复数据,而不是防护机制
构建与开发
常用 make 目标:
| 目标 | 描述 |
|---|---|
make install | 将依赖同步到 .venv |
make install-mcps | 全局安装推理 MCP 服务(本地开发使用;Docker 镜像中已包含) |
make check-lint | ruff |
make check-static-typing | 对 src/ 执行 mypy --strict(60 个文件) |
make check-deadcode / make check-depcheck | vulture / deptry |
make check-all | 全部四项检查门禁 |
make test-unit | 单元测试(tests/unit/) |
make test-oracles | 可执行预言机:L1 canary、L2 属性预言机、spec-garden 回放(test_fizz_garden);test_fizz_traces/test_fizz_conformance 已规划,尚未落地 |
make test-mutations | 针对 verify/mutmut-baseline.json 执行 mutmut 递进检查 |
make verify-fizz | 穷举式 FizzBee 模型检查(L4) |
make verify-ledger / make verify-cross-consistency | Ledger + NL 文档一致性门禁 |
make benchmark-live / make benchmark-analyze / make benchmark-compare / make benchmark-selfcheck | 基准测试工具(见上文) |
make embed-cache | 强制重新生成 data/dense-cache/ |
make docker-build-all | 构建全部三个镜像(内置 dense cache) |
make docker-up / make docker-down | 启动 / 停止开发栈 |
make client-analyze / make client-design / make client-async | 针对 http://localhost:8822/mcp 的演示客户端 |
make docker-publish | 将服务镜像推送至 Docker Hub + GHCR(并添加 git 标签) |
开发工作流:
make install # first-time setup
uv run pre-commit install # First-time setup: commit hook
make check-all # before pushing
make test-unit # behavioral oracle
uv run mypy --strict # src/ is strict; must exit 0
make docker-up # after image-affecting changes
基本规则(来自 AGENTS.md):src/ 使用 mypy 严格模式;schema 变更时,必须在同一提交中更新 docs/implementation-guide.md、schema 和代码;流水线控制流变更时,必须在同一 PR 中更新对应的 FizzBee 模型。
发布
全部三个镜像都会同时发布到两个注册表:
| 镜像 | Docker Hub | GHCR | 标签 |
|---|---|---|---|
| MCP 服务 | olkowa/design-pattern-mcp | ghcr.io/olk/design-pattern-mcp | $(DOCKER_TAG)、latest |
| TEI 嵌入器 | olkowa/pattern-tei-embed | ghcr.io/olk/pattern-tei-embed | $(DOCKER_TAG)、latest |
| TEI 重排序器 | olkowa/pattern-tei-rerank | ghcr.io/olk/pattern-tei-rerank | $(DOCKER_TAG)、latest |
全部三个镜像共用同一个 $(DOCKER_TAG)(取自 pyproject.toml 的版本)。TEI 镜像也由同级的 architecture-pattern-mcp 服务共用,且很少变更——blob 去重使未变更的 TEI 镜像重新打标签的成本很低;sidecar 未变更时,也可以跳过推送 TEI 镜像。
带宽说明: TEI 嵌入器镜像约为 5 GB(内置 ONNX fp32 权重)。首次推送到每个注册表时,需上传约 5 GB。后续推送采用增量方式——只传输变更的层。
前置条件
Docker Hub——本地已完成身份验证(docker login)。
GitHub Container Registry——需要具备 write:packages 权限范围的 classic PAT。无需担心 2FA——PAT 可绕过它。登录后令牌会被丢弃;凭据会保留在 ~/.docker/config.json 中,直到你登出。
发布(一次性设置 + 每个会话)
# 1. Login to GHCR (interactive — paste token at the password prompt)
docker login ghcr.io -u olk
# 2. Build and push all three images (MCP + TEI embedder + TEI reranker)
# The umbrella target builds the MCP image, tags it, pushes it, creates the git tag,
# then builds and pushes each TEI image in sequence.
make docker-publish-all
# 3. Logout from GHCR immediately after publishing
# (removes the ghcr.io credential from ~/.docker/config.json)
docker logout ghcr.io
后续发布时重复步骤 1–3。如果 PAT 已过期,请通过上方链接生成新的 PAT。make docker-publish 只推送 MCP 服务镜像(并添加 v$(DOCKER_TAG) git 标签,以及通过 mcp-publisher 发布的 MCP Registry 条目);make docker-publish-tei 只推送两个 TEI sidecar。
首次推送——将软件包设为公开(仅 GHCR)
GHCR 软件包默认是私有的。首次运行 make docker-publish-all 后,将这三个软件包都设为公开:
| 软件包 | 设置 URL |
|---|---|
| MCP 服务 | https://github.com/users/olk/packages/container/design-pattern-mcp/settings |
| TEI 嵌入器 | https://github.com/users/olk/packages/container/pattern-tei-embed/settings |
| TEI 重排器 | https://github.com/users/olk/packages/container/pattern-tei-rerank/settings |
将每个软件包设为公开并保存。
部分失败后的恢复
如果推送中途失败(例如,尚未配置 GHCR 身份验证),Docker Hub 层已经上传。修复身份验证后,重新运行 make docker-publish-all 是安全的——每个注册表都会报告已上传层的缓存命中,并完成剩余推送。若要针对性重试,可使用 make docker-publish-tei 推送 TEI sidecar。
systemd 服务(Linux)
该服务可在任何基于 systemd 的 Linux 主机上作为 systemd 服务运行。它会在启动时自动启动 Docker Compose 堆栈。完整的 Agent 运维手册安装说明见:INSTALL.md(路径 B)。systemd/ 包含:
| 文件 | 用途 |
|---|---|
systemd/design-pattern-mcp.service | systemd 单元 |
systemd/docker-compose.yml | 生产环境 compose 变体(无 build:、绝对路径、容器端口 8050) |
systemd/README.md | 完整运维手册:安装、验证、故障排除 |
生产环境 compose 是开发文件的部署变体:仅使用预构建镜像、采用绝对路径,可部署到 /etc/design-pattern-mcp/。TEI sidecar 位于共享的 pattern-tei-infra 堆栈中;此堆栈会加入其 Docker 网络,并通过 DNS 名称访问这些 sidecar。只有在共享 TEI 基础设施处于 active (running) 状态后,才能启动该服务。
# Fast path (see INSTALL.md Path B for the full verified sequence):
VERSION=$(grep -m 1 '^version' pyproject.toml | sed -E 's/.*"([^"]+)".*/\1/')
docker pull "olkowa/design-pattern-mcp:${VERSION}"
docker tag "olkowa/design-pattern-mcp:${VERSION}" design-pattern-mcp:latest
sudo install -d /etc/design-pattern-mcp/config
sudo install -m 644 systemd/docker-compose.yml /etc/design-pattern-mcp/
sudo install -m 644 config/config.json /etc/design-pattern-mcp/config/
sudo install -m 644 systemd/design-pattern-mcp.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now design-pattern-mcp
日常操作:使用 systemctl start|stop|restart design-pattern-mcp,通过 journalctl -u design-pattern-mcp -f 查看日志。详情请见 systemd/README.md。
许可证
MIT License。版权所有 (c) 2026 Oliver Kowalke。请参阅源文件中的许可证声明。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。