一句话摘要
展示 2026 年 AI 工程的极简交付形态
一句提示词。一个按钮。一套完全部署的系统。这就是 2026 年的 AI 工程
2026 年 7 月 15 日 · 13 分钟阅读 · 查看原文 ↗ Claude Obsidian AI MCP
六个月前,这需要一名后端开发者、一名 DevOps 工程师和两周时间。今天,只需要一句话和 Claude。下面是完整架构——以及为什么大多数开发者还没想明白它。
大多数人对 AI 的心智模型是错的。
他们以为:AI 助手 → 你提问 → 它回答。
2026 年的现实是:AI 智能体 → 你描述一个系统 → 它构建、部署,并把那个系统连接到你的环境。不是原型。不是代码片段。而是一个正在运行的应用。
这种转变有一个名字:Generative App Architecture(生成式应用架构)。而让它变得实用的技术栈——Claude + Obsidian + MCP——现在任何人都可以使用。
Generative App Architecture 到底是什么意思
传统软件开发:
Generative App Architecture:
AI 智能体不只是写代码。它在底层运行一个完整的 Agentic Loop:
第 1 步——拆解
你的抽象请求("为一家洗车店构建 CRM")被拆成几十个原子子任务:数据库 schema、UI 组件、通知逻辑、认证流程、API 端点。
第 2 步——通过 MCP 映射环境
通过 Model Context Protocol,智能体立刻理解自己能访问哪些工具——你的文件系统、已安装的软件包、正在运行的服务、可用的 API。
第 3 步——迭代构建和测试
智能体写代码、执行代码、捕获编译错误、修复并重新测试——全程自主——直到系统正确运行。
第 4 步——上下文部署
输出不是一个装着代码的文本文件。它是一个真实运行的本地环境,或一个可部署的 Web 应用。
这就是"辅助开发者的工具"和"彻底替代构建流水线的系统"之间的区别。
以 Claude 为引擎——三种部署模式
模式 1——Claude Artifacts
这是从提示词到运行中应用最快的路径。当你让 Claude 构建某样东西时,它会打开一个交互式 Artifact 窗口,在你眼前实时组装、编译并运行一个完整的 Web 应用——React、HTML、JavaScript。
你可以直接在 Artifact 内与运行中的应用交互,用自然语言继续迭代,并导出完整压缩包。
一条能生成生产就绪财务追踪器的提示词:
Claude Artifacts 返回的东西——一个完整 React 应用:
这就是完整应用。一句提示词。立即在 Claude Artifacts 内运行。
模式 2——Claude + MCP(本地部署)
MCP(Model Context Protocol)让 Claude 直接访问你的本地环境。一句提示词不只是生成代码——它会在硬盘上创建文件夹、通过终端安装软件包、写配置文件,并启动本地服务器。
把 Claude 连接到你文件系统的 MCP 配置:
启用这份配置之后,只需一句提示词:
Claude 会通过 MCP 端到端执行它——创建目录结构、安装 Node 软件包、编写数据库 schema、构建 UI、启动服务器。你会拿到一个 localhost URL。
模式 3——带公司标准的 Claude Projects
只需把你的 UI kit、design tokens、API 文档和安全要求上传到一个 Claude Project 一次。此后 Claude 生成的每个系统都会自动遵循你的内部技术栈。
以 Obsidian 为 AI 指挥中心
2026 年的 Obsidian 不是一款笔记应用。它是一个个人 AI 指挥中心,你积累的知识库会成为你构建的每个系统的上下文层。
架构上的转变:你的 vault 不再是存储。它是 RAG(Retrieval Augmented Generation,检索增强生成)——一个 AI 智能体在执行任何任务之前都会查询的活知识库。
让它运转起来的插件栈:
Smart Connections——索引你的整个 vault,并在每次 AI 调用时把相关笔记作为上下文发送。
Obsidian Copilot——把你的 vault 直接连接到 Claude API。只需配置一次:
以 Canvas 作为系统设计界面
一句提示词创建一个交互式 Obsidian Canvas,Claude 会把完整系统架构铺成相连的区块。
Claude 以结构化文件形式生成的 Canvas 输出:
用于完全隐私的本地 LLM
对于敏感数据——客户系统、财务工具、内部工具——你让请求经过本地模型,而不是云端 API。零数据离开你的机器。
配置 Obsidian Copilot 使用本地端点:
本地模型 + MCP bridge:
把一个 .md 文件放进你的 Obsidian AI-Inbox,其中写入构建提示词。bridge 会捡起它,通过本地模型路由,在线下 Projects 文件夹中构建系统,并把完成说明写回 Obsidian。
完整工作流——从想法到已部署系统
从第 1 步到应用运行的总时间:4-12 分钟。
这次转变代表什么
这并不是一种更快写代码的方法。
这是想法与执行之间一种不同的关系。
过去,能构建什么受技术能力和时间限制。非开发者造不了 CRM。独立开发者无法同时构建五套系统。团队无法用几小时而不是几周交付。
这三个约束全都消失了。
新的约束是思考的清晰度——能否准确描述你需要什么、理解什么算好输出,并对其进行迭代。这是一项思维技能,而不是技术技能。
Generative App Architecture 不会让开发者过时。它会让"以系统方式思考——在构建之前推理自己需要什么"的能力,变得比编写实现代码的能力有价值得多。
技术栈已经在这里。模型能力已经足够。唯一剩下的变量,是你正在用它构建,还是只在读别人如何构建。
本文描述截至 2026 年 7 月 Claude、Obsidian 和 MCP 已提供的能力。具体插件版本和 API 配置可能变化。实施前请务必核对最新文档。
感谢阅读。
提示词
{
"provider": "ollama",
"model": "qwen2.5-coder:32b",
"baseUrl": "http://localhost:11434",
"systemPrompt": "You are a local system builder with access to the user's filesystem via MCP. Build complete working systems. Never send data to external APIs without explicit instruction."
}
{
"provider": "anthropic",
"model": "claude-opus-4-8",
"apiKey": "YOUR_ANTHROPIC_KEY",
"systemPrompt": "You are a system architect. You have access to the user's complete knowledge vault as context. When building systems, reference their existing notes, past decisions, and established patterns. Output: working code + Obsidian file structure.",
"contextWindow": {
"includeActiveFile": true,
"includeLinkedFiles": true,
"includeRecentFiles": 10,
"includeFolders": ["Projects", "Systems", "Decisions"]
}
}
import { useState, useEffect } from "react";
const CATEGORIES = ["Housing","Food","Transport","Entertainment","Health","Savings","Income","Crypto","Other"];
const CRYPTO_IDS = { BTC: "bitcoin", ETH: "ethereum", SOL: "solana" };
export default function FinanceTracker() {
const [transactions, setTransactions] = useState(() =>
JSON.parse(localStorage.getItem("transactions") || "[]")
);
const [cryptoPrices, setCryptoPrices] = useState({});
const [form, setForm] = useState({ description:"", amount:"", category:"Other", type:"expense", date:"" });
const [darkMode, setDarkMode] = useState(false);
useEffect(() => {
const ids = Object.values(CRYPTO_IDS).join(",");
fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${ids}&vs_currencies=usd`)
.then(r => r.json())
.then(data => {
const prices = {};
Object.entries(CRYPTO_IDS).forEach(([sym, id]) => { prices[sym] = data[id]?.usd || 0; });
setCryptoPrices(prices);
});
}, []);
useEffect(() => {
localStorage.setItem("transactions", JSON.stringify(transactions));
}, [transactions]);
const addTransaction = () => {
if (!form.description || !form.amount) return;
setTransactions(prev => [...prev, { ...form, id: Date.now(), amount: parseFloat(form.amount),
date: form.date || new Date().toISOString().split("T")[0] }]);
setForm({ description:"", amount:"", category:"Other", type:"expense", date:"" });
};
const totalIncome = transactions.filter(t => t.type === "income").reduce((s,t) => s + t.amount, 0);
const totalExpenses = transactions.filter(t => t.type === "expense").reduce((s,t) => s + t.amount, 0);
const netWorth = totalIncome - totalExpenses;
const bg = darkMode ? "#0D0D0D" : "#F9F7F4";
const card = darkMode ? "#1A1A1A" : "#FFF";
const text = darkMode ? "#F0F0F0" : "#1A1A1A";
const border = darkMode ? "#2A2A2A" : "#E8E8E8";
return (
<div style={{ minHeight:"100vh", background:bg, color:text, fontFamily:"system-ui, sans-serif" }}>
<div style={{ maxWidth:800, margin:"0 auto", padding:"24px 16px" }}>
<div style={{ display:"flex", justifyContent:"space-between", marginBottom:24 }}>
<div>
<h1 style={{ fontSize:22, fontWeight:500, margin:0 }}>Finance Tracker</h1>
<p style={{ margin:"4px 0 0", fontSize:13, color: netWorth >= 0 ? "#22C55E" : "#EF4444" }}>
Net worth: ${netWorth.toFixed(2)}
</p>
</div>
<button onClick={() => setDarkMode(!darkMode)}
style={{ padding:"8px 14px", borderRadius:8, border:`1px solid ${border}`,
background:card, color:text, cursor:"pointer" }}>
{darkMode ? "Light" : "Dark"}
</button>
</div>
<div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:10, marginBottom:20 }}>
{[["Income","#22C55E",totalIncome],["Expenses","#EF4444",totalExpenses]].map(([label,color,val]) => (
<div key={label} style={{ background:card, border:`1px solid ${border}`, borderRadius:10, padding:14 }}>
<p style={{ margin:0, fontSize:12, color:"#999" }}>{label}</p>
<p style={{ margin:"4px 0 0", fontSize:20, fontWeight:600, color }}>${val.toFixed(2)}</p>
</div>
))}
</div>
<div style={{ background:card, border:`1px solid ${border}`, borderRadius:10, padding:16, marginBottom:20 }}>
<div style={{ display:"grid", gridTemplateColumns:"2fr 1fr 1fr", gap:8, marginBottom:8 }}>
<input placeholder="Description" value={form.description}
onChange={e => setForm(p => ({ ...p, description: e.target.value }))}
style={{ padding:"9px 12px", borderRadius:8, border:`1px solid ${border}`, background:bg, color:text, fontSize:13 }} />
<input type="number" placeholder="Amount" value={form.amount}
onChange={e => setForm(p => ({ ...p, amount: e.target.value }))}
style={{ padding:"9px 12px", borderRadius:8, border:`1px solid ${border}`, background:bg, color:text, fontSize:13 }} />
<select value={form.type} onChange={e => setForm(p => ({ ...p, type: e.target.value }))}
style={{ padding:"9px 12px", borderRadius:8, border:`1px solid ${border}`, background:bg, color:text, fontSize:13 }}>
<option value="expense">Expense</option>
<option value="income">Income</option>
</select>
</div>
<div style={{ display:"grid", gridTemplateColumns:"2fr auto", gap:8 }}>
<select value={form.category} onChange={e => setForm(p => ({ ...p, category: e.target.value }))}
style={{ padding:"9px 12px", borderRadius:8, border:`1px solid ${border}`, background:bg, color:text, fontSize:13 }}>
{CATEGORIES.map(c => <option key={c}>{c}</option>)}
</select>
<button onClick={addTransaction}
style={{ padding:"9px 20px", borderRadius:8, background:"#E8692A",
color:"#fff", border:"none", cursor:"pointer", fontSize:13, fontWeight:500 }}>
Add
</button>
</div>
</div>
<div style={{ background:card, border:`1px solid ${border}`, borderRadius:10, overflow:"hidden" }}>
<p style={{ margin:0, padding:"12px 16px", borderBottom:`1px solid ${border}`, fontSize:13, fontWeight:500 }}>
Transactions ({transactions.length})
</p>
{transactions.slice().reverse().map(t => (
<div key={t.id} style={{ display:"flex", justifyContent:"space-between",
alignItems:"center", padding:"11px 16px", borderBottom:`1px solid ${border}` }}>
<div>
<p style={{ margin:0, fontSize:13, fontWeight:500 }}>{t.description}</p>
<p style={{ margin:"2px 0 0", fontSize:12, color:"#999" }}>{t.category} · {t.date}</p>
</div>
<span style={{ fontSize:14, fontWeight:600, color: t.type==="income" ? "#22C55E" : "#EF4444" }}>
{t.type==="income" ? "+" : "-"}${t.amount.toFixed(2)}
</span>
</div>
))}
</div>
</div>
</div>
);
}
# 安装 Ollama
curl -fsSL https://ollama.com/install.sh | sh
# 拉取一个具备编码能力的模型
ollama pull llama3.2:latest
# 或者使用更强的代码生成模型:
ollama pull qwen2.5-coder:32b
# 启动本地服务器
ollama serve
# 运行于 localhost:11434
Obsidian Canvas 中的提示词:
"为我的 Twitter 账号设计一条内容生产流水线。
我每周发布 3 篇文章,管理 5 笔赞助合作。
参考我现有的内容日历笔记。"
输出:带有相连区块的 Canvas:
→ 内容创意(从你的兴趣笔记中提取)
→ 初稿 → 审核 → 发布工作流
→ 带截止日期提醒的赞助追踪
→ 分析仪表盘
→ 自动交叉发布逻辑
→ 每个区块都链接到对应的 Obsidian 文件
import requests
import json
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
OBSIDIAN_INBOX = Path.home() / "Obsidian" / "AI-Inbox"
PROJECTS_DIR = Path.home() / "Projects"
OLLAMA_URL = "http://localhost:11434/api/generate"
class InboxWatcher(FileSystemEventHandler):
"""
监视 Obsidian AI-Inbox 文件夹。
当新的 .md 文件出现时,把它视为构建提示词。
传给本地 LLM -> 通过 MCP 执行 -> 报告结果。
"""
def on_created(self, event):
if not event.src_path.endswith(".md"):
return
prompt_file = Path(event.src_path)
prompt = prompt_file.read_text()
print(f"[Inbox] New prompt: {prompt_file.name}")
self.execute_prompt(prompt, prompt_file.stem)
def execute_prompt(self, prompt: str, project_name: str):
response = requests.post(OLLAMA_URL, json={
"model": "qwen2.5-coder:32b",
"prompt": f"""You are a system builder.
Build this system: {prompt}
Output a JSON plan with:
{{
"project_dir": "relative path",
"files": [
{{"path": "relative path", "content": "file content"}}
],
"commands": ["npm install", "etc"],
"summary": "what was built"
}}""",
"stream": False
})
plan = json.loads(response.json()["response"])
project_dir = PROJECTS_DIR / plan["project_dir"]
project_dir.mkdir(parents=True, exist_ok=True)
for file_spec in plan["files"]:
file_path = project_dir / file_spec["path"]
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_text(file_spec["content"])
print(f"[Build] Created: {file_path}")
result_file = OBSIDIAN_INBOX / f"{project_name}-result.md"
result_file.write_text(
f"# Build Complete: {project_name}\n\n"
f"**Location:** {project_dir}\n\n"
f"**Summary:** {plan['summary']}\n\n"
f"**Files created:** {len(plan['files'])}\n"
)
print(f"[Done] Result written to Obsidian")
if __name__ == "__main__":
OBSIDIAN_INBOX.mkdir(exist_ok=True)
observer = Observer()
observer.schedule(InboxWatcher(), str(OBSIDIAN_INBOX), recursive=False)
observer.start()
print(f"[Watching] {OBSIDIAN_INBOX}")
try:
while True:
import time; time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/yourname/Projects"
]
},
"terminal": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-terminal"]
},
"sqlite": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-sqlite",
"--db-path",
"/Users/yourname/Projects/app.db"
]
}
}
}
{
"nodes": [
{
"id": "ideation",
"type": "file",
"file": "Systems/ContentPipeline/Ideation.md",
"x": 0, "y": 0, "width": 300, "height": 200,
"color": "1"
},
{
"id": "draft",
"type": "file",
"file": "Systems/ContentPipeline/Draft.md",
"x": 400, "y": 0, "width": 300, "height": 200,
"color": "3"
},
{
"id": "sponsors",
"type": "file",
"file": "Systems/ContentPipeline/SponsorTracker.md",
"x": 0, "y": 300, "width": 300, "height": 200,
"color": "5"
},
{
"id": "analytics",
"type": "file",
"file": "Systems/ContentPipeline/Analytics.md",
"x": 400, "y": 300, "width": 300, "height": 200,
"color": "6"
}
],
"edges": [
{
"id": "e1",
"fromNode": "ideation", "fromSide": "right",
"toNode": "draft", "toSide": "left",
"label": "approved concepts"
},
{
"id": "e2",
"fromNode": "sponsors", "fromSide": "right",
"toNode": "draft", "toSide": "bottom",
"label": "brief integration"
}
]
}
为一家洗车企业构建并部署一套本地 CRM 系统。
在 ~/Projects/carwash-crm/ 创建项目。
自动安装依赖。
设置 SQLite 数据库,包含以下表:
- customers(name、phone、email、vehicle、notes)
- visits(customer_id、date、service、price、status)
- staff(name、role、schedule)
构建一个 Web 界面,包含:
- 客户搜索和客户资料
- 每位客户的到访历史
- 按月收入仪表盘
- 短信提醒系统(Twilio 集成,仅配置)
完成后启动开发服务器。
就绪后报告 localhost URL。
想法 → 规格 → 设计 → 前端开发 → 后端开发
→ 数据库搭建 → API 集成 → 测试 → 部署
耗时:数周。成本:数千美元。
第 1 步——输入
在 Obsidian 的 AI 面板中写下:
"带加密货币集成和月度预算追踪的
个人财务系统。"
按下 Generate。
第 2 步——上下文组装
Smart Connections 插件扫描你的 vault。
找到你过去 3 个月的财务笔记。
把它们作为上下文发送到 Claude API。
第 3 步——架构
Claude(Opus 4.8)设计系统:
- 数据库 schema
- UI 组件
- API 集成
- 文件结构
第 4 步——构建
Claude(Sonnet 5)通过 MCP 实现:
- 创建 ~/Projects/finance-tracker/
- 安装依赖
- 写入所有文件
- 在 localhost:3000 启动开发服务器
第 5 步——结果
Obsidian 创建文件夹:Finance System/
内部包含:互相链接的仪表盘、配置笔记,
以及运行中应用的直接链接。
想法 → 提示词 → 运行中的系统
耗时:几分钟。成本:API tokens。
[Claude Project 上下文包含:]
- design-system.md(颜色、排版、间距)
- api-standards.md(认证模式、错误码)
- security-policy.md(输入验证规则、CORS 配置)
- stack.md(React 18、TypeScript、Tailwind、Prisma、PostgreSQL)
提示词:
"构建一个带邮箱验证的客户入驻表单,
提交后自动写入 Notion 数据库。"
输出:完全匹配你的技术栈的生产就绪代码
——不需要清理。
构建一个个人财务追踪应用,包含:
功能:
- 交易日志(按分类添加/编辑/删除)
- 带进度条的月度预算视图
- 从 CoinGecko API 拉取实时价格的加密资产组合追踪器
- 实时更新的净资产计算
- 深色模式开关
技术要求:
- 单文件 React 应用
- 只使用 useState + useEffect(不使用外部状态管理)
- 使用 Tailwind CSS 做样式
- 从公开 API 拉取数据(无需认证)
- 移动端响应式
数据:
- 跨会话持久化到 localStorage
- 把交易导出为 CSV
输出:可直接部署的完整自包含应用。
标签:# X # Claude # Obsidian # AI # MCP # Crypto # Design # Sonnet # Rag
相关文章 Every Morning I Re-Taught Claude My Whole Life. It Forgot by Lunch. One File Ended the Loop
人们花了多年寻找完美的提示词。答案从来不在于你怎么问,而在于你开口之前,AI 已经对你知道些什么。Claude AI Obsidian MCP
原文参考:https://maxed.wiki/posts/one-prompt-one-button-a-fully-deployed-system-this-is-what-ai-engineering-looks-like-in-2026/ (Maxed.wiki,本页为站内中文整理)