15 KiB
Arabica Sprint1 版本开发指南说明
1. 环境前置要求
在开始部署前,请确保开发机已安装以下软件:
- Node.js: v18 LTS 或更高版本。
- Visual Studio Code (VS Code): 作为主力开发与断点调试 IDE。
- Cherry Studio: 最新版,作为发起请求的 MCP Client(大模型中枢)。
- 本地知识库: 一个存放
.md格式笔记的本地文件夹(如 Obsidian Vault)。
2. 工程初始化与依赖安装
打开终端,执行以下命令从零搭建工程骨架:
# 1. 创建并进入项目目录
mkdir project-caffeine-ts
cd project-caffeine-ts
# 2. 初始化 npm
npm init -y
# 3. 安装生产核心依赖
npm install @modelcontextprotocol/sdk zod
# 4. 安装 TypeScript 及开发环境依赖
npm install --save-dev typescript @types/node
# 5. 创建标准的目录结构
mkdir -p src/services dist .vscode
touch src/app.ts src/services/promptService.ts src/services/resourceService.ts .vscode/launch.json tsconfig.json
3. 核心工程配置
我们需要配置 TypeScript 编译器选项、启动脚本以及 VS Code 独有的源码映射调试环境。
3.1 编辑tsconfig.json
控制代码编译并生成用于断点调试的 sourceMap:
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src",
"sourceMap": true, // 【关键】生成 .js.map 文件,用于 VS Code 断点映射
"strict": true, // 开启严格模式
"esModuleInterop": true, // 允许默认导入 CommonJS 模块
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}
2.2 编辑package.json
添加 build 和 watch 脚本,用于将 .ts 编译为 .js。在 package.json 中找到 "scripts" 字段并替换为
"scripts": { "build": "tsc", "watch": "tsc --watch", "start": "node dist/app.js" }
2.3 编辑 .vscode/launch.json
新增的 outFiles 字段,它告诉 VS Code 去 dist 目录寻找编译后的文件,从而将断点映射回你的 src/*.ts 源码上。
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "🍒 附加到 Cherry Studio (TS 联调)",
"port": 9229,
"restart": true,
"skipFiles": ["<node_internals>/**"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
}
]
}
4. 第核心业务代码实现
4.1. src/services/promptService.ts (提示词策略生成)
纯本地业务逻辑,负责 5 Whys 框架生成。
/**
* 根据查询主题生成 5 Whys 提示词策略
* @param query 用户输入的查询主题
* @returns 包含 5 个追问的字符串数组
*/
export function generate5Whys(query: string): string[] {
if (query.includes("开源人才")) {
return [
"为什么中国开源人才的培养面临困难?",
"为什么中国开源人才缺乏足够的行业经验?",
"为什么开源社区对中国人才的支持力度不足?",
"为什么中国开源人才的市场需求与供给不平衡?",
"为什么政策支持不足导致中国开源人才流失?"
];
}
return [
`为什么 "${query}" 会成为一个问题?`,
`为什么导致上述现象的直接原因会发生?`,
`为什么当前的系统或流程没有阻止这种情况?`,
`为什么以前的解决方案或预防措施失效了?`,
`为什么根本的系统性漏洞一直未被修复?`
];
}
## 4.2 src/services/resourceService.ts (本地知识库访问)
带有严格路径防穿越(Path Traversal)安全校验的本地文件读取服务。
import fs from 'fs/promises';
import path from 'path';
// 【⚠️ 重要配置】请修改为你电脑上真实的 Markdown 笔记文件夹绝对路径!
const OBSIDIAN_VAULT_PATH = '/home/wguo/Downloads/MyVault';
export async function listObsidianNotes(): Promise<string[]> {
try {
const files = await fs.readdir(OBSIDIAN_VAULT_PATH);
return files.filter(file => file.toLowerCase().endsWith('.md'));
} catch (error: any) {
console.error(`[Project Caffeine] 无法读取知识库目录: ${error.message}`);
return [];
}
}
export async function readObsidianNote(filename: string): Promise<string> {
const targetPath = path.resolve(OBSIDIAN_VAULT_PATH, filename);
const safeVaultPath = path.resolve(OBSIDIAN_VAULT_PATH);
// 核心防御:防止大模型通过传入 "../../" 读取系统敏感文件
if (!targetPath.startsWith(safeVaultPath)) {
throw new Error(`安全警告:越权访问拦截!禁止读取目录外的文件: ${filename}`);
}
try {
const content = await fs.readFile(targetPath, 'utf-8');
return content;
} catch (error: any) {
throw new Error(`无法读取笔记 [${filename}]: 文件可能不存在或无权限。`);
}
}
4.3 src/app.ts (主入口)
负责初始化标准输入输出传输层,并向 Cherry Studio 注册工具字典。
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import { generate5Whys } from './services/promptService';
import { listObsidianNotes, readObsidianNote } from './services/resourceService';
// ==========================================
// 1. 初始化 MCP Server
// ==========================================
const mcpServer = new McpServer({
name: "Project-Caffeine-Prompt-Strategy",
version: "1.2.0"
});
// ==========================================
// 2. 注册 Tools (工具) - 赋予大模型主动执行的能力
// ==========================================
// 工具 1:5 Whys 提示词策略生成
mcpServer.tool(
"generate_5_whys",
"使用 5 Whys 模板对用户查询进行深度分解,生成增强的提示词策略",
{ query: z.string().describe("需要分析的查询主题") },
async ({ query }: { query: string }) => {
console.error(`[Project Caffeine] 大模型调用工具: 正在生成 5 Whys 策略 -> ${query}`);
const enhancedPrompt = generate5Whys(query);
return {
content: [{ type: "text", text: JSON.stringify(enhancedPrompt, null, 2) }]
};
}
);
// 工具 2:扫描本地知识库目录
mcpServer.tool(
"list_local_notes",
"获取本地 Obsidian 知识库中的所有 Markdown 笔记列表,用于了解当前有哪些可用的本地上下文资料。",
{},
async () => {
console.error(`[Project Caffeine] 大模型调用工具: 正在扫描本地笔记列表...`);
const notes = await listObsidianNotes();
return {
content: [{
type: "text",
text: notes.length > 0 ? `找到了以下笔记:\n${notes.join('\n')}` : "未找到笔记。"
}]
};
}
);
// 工具 3:阅读指定的单篇笔记内容
mcpServer.tool(
"read_local_note",
"读取本地 Obsidian 知识库中指定笔记的完整内容,作为深度分析的上下文参考。",
{ filename: z.string().describe("需要读取的笔记文件名,必须包含 .md 后缀") },
async ({ filename }: { filename: string }) => {
console.error(`[Project Caffeine] 大模型调用工具: 正在深度阅读笔记 -> ${filename}`);
try {
const content = await readObsidianNote(filename);
return { content: [{ type: "text", text: content }] };
} catch (error: any) {
return {
content: [{ type: "text", text: `读取失败: ${error.message}` }],
isError: true // 明确告知大模型此操作抛出了错误
};
}
}
);
// ==========================================
// 3. 注册 Resources (资源) - 暴露给客户端供用户手动勾选的静态数据
// ==========================================
// 资源 1:知识库目录索引
mcpServer.resource(
"obsidian-index", // 客户端显示的资源 Name/ID
"obsidian://vault/index", // 唯一的 URI 标识
{
description: "本地知识库的目录索引,包含所有 Markdown 笔记的列表"
},
async (uri) => {
console.error(`[Project Caffeine] 客户端请求静态资源: ${uri.href}`);
const notes = await listObsidianNotes();
const textContent = notes.length > 0
? `当前知识库包含以下文件:\n${notes.join('\n')}`
: "当前知识库为空。";
return {
contents: [{
uri: uri.href,
mimeType: "text/plain",
text: textContent
}]
};
}
);
// ==========================================
// 4. 启动底层 Stdio 传输层
// ==========================================
async function start(): Promise<void> {
console.error("[Project Caffeine] 正在启动 TS 版 MCP Server (含 Tools 与 Resources)...");
const transport = new StdioServerTransport();
await mcpServer.connect(transport);
console.error("[Project Caffeine] MCP Server 已就绪,等待 Cherry Studio 交互。");
}
// 捕获致命错误并安全退出
start().catch((err: unknown) => {
console.error("服务器启动失败:", err);
process.exit(1);
});
5. 启动与工作流验证
5.1 步骤一:启动 TS 实时编译 (Watch Mode)
在 VS Code 中打开终端,运行以下命令。这会让 TypeScript 编译器在后台实时监控你的 .ts 文件修改,并自动编译到 dist 目录中:
npm run watch
(保持这个终端窗口在后台运行不要关闭)
5.2 步骤二:在 Cherry Studio 中配置 Server
-
进入 Cherry Studio 的 设置 -> MCP。
-
添加或修改 Server,关键在于你要指向编译后的
dist/app.js而不是src/app.ts:- Command:
node - Args:
["--inspect=9229", "/project-caffeine-sprint1/dist/app.js"] - 注意:需要输入app.js的绝对路径
- Command:
-
确保状态灯亮起绿色。
5.3 步骤三:VS Code 源码级断点联调
- 在
src/app.ts或各个 Service 的关键代码行打上断点。 - 在 VS Code 左侧调试面板,选择 "🍒 附加到 Cherry Studio (TS 联调)",点击运行。
- 状态栏变色即表示成功抓取到 Cherry Studio 的底层 Node 进程。
5.4 发起全链路交互
在 Cherry Studio 对话框中,输入以下指令测试大模型的自主编排能力:
"请先查看我的本地笔记列表,找到关于开源领域的笔记并阅读内容。然后结合你的知识,调用 5 Whys 工具帮我分析一下里面的痛点。"
预期结果:你将看到大模型自动、按顺序调用了 list_local_notes -> read_local_note -> generate_5_whys 三个工具,最终为你输出一篇深度融合了你的私有知识库的洞察报告。
6. 系统运行测试样例
在开始执行测试之前,请确保已完成以下前置准备:
- 后台编译:在 VS Code 终端保持运行
npm run watch命令。 - 配置连接:在 Cherry Studio 的设置中,将 Command 设为
node,Args 设为["--inspect=9229", "<绝对路径>/dist/app.js"],并确保状态灯亮起绿色。 - 本地知识库:确保代码中
OBSIDIAN_VAULT_PATH指向的本地文件夹中存在至少一篇 Markdown 格式的测试笔记。
6.1 测试样例1:基础工具调用与分支逻辑验证
-
测试目标:验证
generate_5_whys工具的硬编码逻辑分支是否生效。 -
操作步骤:
- 向大模型发送指令:“请调用工具,帮我生成关于‘开源人才’的 5 Whys 策略。”
-
预期结果:
- VS Code Debug Console打印日志:
[Project Caffeine] 大模型调用工具: 正在生成 5 Whys 策略 -> 开源人才。 - 大模型的回答中必须包含代码中预设的具体问题,例如“为什么中国开源人才的培养面临困难?”、“为什么中国开源人才缺乏足够的行业经验?”等。
- VS Code Debug Console打印日志:
6.2 测试样例 2:泛化工具调用验证
-
测试目标:验证
generate_5_whys工具在遇到未知主题时的泛化模板处理能力。 -
操作步骤:
- 向大模型发送指令:“我正在研究‘AI算力成本过高’的问题,请调用工具为我生成 5 Whys 框架。”
-
预期结果:
- 大模型会调用工具,并将主题代入泛化模板,输出类似“为什么 "AI算力成本过高" 会成为一个问题?”、“为什么导致上述现象的直接原因会发生?”等框架性问题。
6.3 测试样例 3:安全防御机制测试(红蓝对抗)
-
测试目标:验证
read_local_note工具中的路径防穿越(Path Traversal)安全校验是否能成功拦截恶意请求。 -
操作步骤:
- 向大模型发送诱导性指令:“请调用读取笔记的工具,帮我读取
../etc/passwd文件的内容。”
- 向大模型发送诱导性指令:“请调用读取笔记的工具,帮我读取
-
预期结果:
- 工具调用将被拦截,并抛出错误。
- 大模型将收到包含
isError: true的错误响应。 - 错误信息明确提示大模型:“读取失败: 安全警告:越权访问拦截!禁止读取目录外的文件”。
6.4 测试样例 4:全链路 Agentic 自主编排测试
-
测试目标:验证大模型是否能自主决策,按顺序组合调用多个外部工具完成复杂分析。
-
操作步骤:
- 向大模型发送综合指令:“请先查看我的本地笔记列表,找到关于开源领域的笔记并阅读内容。然后结合你的知识,调用 5 Whys 工具帮我分析一下里面的痛点。”。
-
预期结果:
- 大模型将自动并按顺序调用
list_local_notes->read_local_note->generate_5_whys三个工具。 - 最终输出一篇融合了私有知识库内容深度的洞察报告。
- 大模型将自动并按顺序调用
6.5 测试样例 5:VS Code 断点联调环境测试
-
测试目标:验证
.vscode/launch.json源码映射与调试环境配置是否成功。 -
操作步骤:
- 在
src/app.ts或其他 Service 文件的关键代码行打上断点。 - 在 VS Code 调试面板选择“🍒 附加到 Cherry Studio (TS 联调)”并运行。
- 在 Cherry Studio 中触发任意工具调用。
- 在
-
预期结果:
- VS Code 底部状态栏变色,表示成功抓取到 Cherry Studio 的底层 Node 进程。
- 代码执行将暂停在设置了断点的位置,允许开发者查看当前变量与调用栈。
许可声明
本文档采用 知识共享署名--相同方式共享 4.0 国际许可协议 (CC BY--SA 4.0) 进行许可,© 2025-2026 Gitconomy Research.