字数 5943,阅读大约需 30 分钟
AI 应用架构演进实践 · 第 06 集 · X-ray 三部曲(上)
学习 Skill 最省力的办法,不是先背完概念,而是亲手走一遍完整流程。今天我们就从一个空文件夹开始,做出能被 Agent 调用的 Skill。
先看一个真实场景:接手陌生项目时,最费劲的往往不是写代码,而是在几十层目录里“考古”——它到底做什么?入口在哪?哪些文件值得先读?
为了把流程讲清楚,我选择制作一个叫 X-ray 的示例 Skill:进入陌生仓库后调用它,AI 会先扫描,再挑关键文件阅读,最后生成一份能直接打开的 xray-report.html。
X-ray 只是这次的练习题,不是标准答案。 你真正要学的是“描述任务 → 准备脚本和资料 → 安装 → 调用 → 验收”这套方法。以后完全可以把它换成代码审查、周报整理、接口文档生成,或者任何你经常重复做的事情。
上集主线:制作 X-ray、装进 Agent 工具,并在真实仓库完成第一次本地闭环
这一集会稍长,因为我们要一次完成两个动作:从零制作 X-ray,再把它装进 CodeBuddy,在一个真实仓库里跑通第一次闭环。
- • 预计用时:35~50 分钟;代码可以直接复制,不要求提前会 Python。
- • 成功标志:测试目录里出现
xray-data.json和xray-report.html,浏览器能打开一份完整的项目分析报告。 - • 今天不做:不发布 GitHub,不碰 ClawHub,也不运行测试仓库自己的项目代码。
不用先把 Skill 的所有原理背下来。今天的目标很朴素:像小时候抄一遍代码那样,把一条完整路线亲手走通,手感自然就来了。
1. 先准备四样东西
第一次出现的工具,我们都只讲“马上要用的那一点”。已经装好的,直接跳过。
- • CodeBuddy IDE:一款带 Agent 能力的 AI 编程工具,本集用它加载并调用 X-ray。Windows 10+、macOS 和 Linux 都有安装包;官方安装说明在这里。能打开一个本地文件夹并进入 Agent 对话,就算准备完成。
- • Python 3:负责运行扫描脚本。在 PowerShell 执行
python --version,能看到Python 3.x就不用重装;如果系统只认py --version,后文把python换成py即可。 - • Git:负责把测试项目克隆到电脑。执行
git --version,能看到版本号就算过关。 - • VS Code:选装。素材里的部分代码编辑画面来自 VS Code 风格界面;只用 CodeBuddy 也能完成,不需要为了截图长得一样再装一套编辑器。
公司电脑如果没有安装权限,先走组织的软件准入流程。教程可以等等,安全制度不适合“曲线救国”。
2. Skill 到底是什么?
说白了,Skill 是一套保存下来的做事方法。
普通提示词像临时口头交代;Skill 更像 Agent 的“岗位说明书 + 工具箱”。下次换一个项目,不用重新解释几十行要求,只要调用 /x-ray,Agent 就知道应该先扫描、再读证据、最后交付报告。
OpenAI 的可复用 Skill 教程采用同样的分层思路。不同工具的安装目录和唤起方式会不同,但下面四个位置很好记:
一个 Skill 的四个位置:说明、脚本、分析规则与报告模板
- •
SKILL.md:告诉 Agent 什么时候上班、按什么流程工作、哪些事不能做。 - •
scripts/:负责数文件、识别配置等确定性工作。 - •
references/:保存分析规则,帮助 Agent 区分事实和推断。 - •
assets/:保存最终报告会用到的模板。
X-ray 的设计原则只有一句:代码负责测量,Agent 负责理解,HTML 负责展示。
3. 搭好 X-ray 的空骨架
这一步的作用,是先把 Skill 的四类内容分开放好,后面不会把说明、脚本和模板搅成一锅粥。
先在桌面创建一个名为 x-ray 的文件夹。你可以使用 CodeBuddy、VS Code,或者直接用系统文件管理器。
先创建一个名为 x-ray 的空文件夹
接着在里面创建三个文件夹和一个文件:
x-ray/
├─ assets/
├─ references/
├─ scripts/
└─ SKILL.md
也可以在 PowerShell 中一次完成:
New-Item -ItemType Directory -Force x-ray
Set-Location x-ray
New-Item -ItemType Directory -Force assets, references, scripts
New-Item -ItemType File -Force SKILL.md
X-ray 的基础目录包含 assets、references、scripts 和 SKILL.md
看到什么算成功: 左侧文件树和截图一样,有三个文件夹与 SKILL.md。现在它还不会干活,但办公室已经装修完了。
4. 写 SKILL.md:先把岗位说清楚
SKILL.md 相当于写给 Agent 的岗位说明书:它决定什么时候触发、按什么顺序工作,以及哪些事绝对不能做。
把下面内容完整写入 SKILL.md:
---
name: x-ray
description: 扫描一个陌生的软件项目,快速识别项目类型、技术栈、架构、文件结构、复杂度和学习价值。适用于用户希望快速理解、探索、检查或接手一个陌生代码库的场景。
---
# X-ray
分析当前软件项目,并生成一份简洁、可视化的项目概览。
## 目标
调用 X-ray 时:
1. 扫描项目目录结构。
2. 判断项目是什么类型。
3. 识别主要技术栈。
4. 找出重要目录和关键文件。
5. 推断项目的整体架构。
6. 评估项目复杂度。
7. 判断这个项目适合哪些方向的人学习。
8. 推荐理解项目的阅读顺序。
9. 最终生成一个独立可打开的 HTML 可视化报告。
## 安全规则
默认把项目视为只读。
不要:
- 修改项目源代码;
- 安装依赖;
- 执行项目代码;
- 读取或暴露 `.env` 等文件中的敏感信息;
- 执行未知脚本。
优先使用静态分析和文件读取来理解项目。
在 SKILL.md 中写清目标、流程与只读安全边界
这里最重要的不是写得多,而是三件事写清楚:什么时候触发、要交付什么、不能碰什么。 陌生仓库里的脚本不能因为名字亲切就随手运行,.env 也不是项目欢迎词。
5. 写第一个脚本:先学会“数”
大模型擅长理解,但不适合自己数几百个文件。这一步先把“数文件、认语言”交给脚本,得到可靠的一手数据。
在 scripts 目录创建 scan_repo.py。
在 scripts 目录创建 scan_repo.py
接下来正文直接提供可复制的完整代码。为了不让同一段内容占两遍版面,长代码截图不再重复插入;我们只保留创建文件、执行命令和结果验收的真实画面。清晰代码原图仍完整保存在本集图片集中。
复制下面的完整代码:
from pathlib import Path
from collections import Counter
import json
import sys
IGNORE_DIRS = {
".git",
"node_modules",
".next",
"dist",
"build",
"__pycache__",
".venv",
"venv",
}
LANGUAGE_MAP = {
".py": "Python",
".js": "JavaScript",
".jsx": "JavaScript",
".ts": "TypeScript",
".tsx": "TypeScript",
".java": "Java",
".go": "Go",
".rs": "Rust",
".rb": "Ruby",
".php": "PHP",
".cs": "C#",
".cpp": "C++",
".c": "C",
".swift": "Swift",
".kt": "Kotlin",
".vue": "Vue",
".svelte": "Svelte",
}
def should_ignore(path: Path) -> bool:
return any(part in IGNORE_DIRS for part in path.parts)
def scan_repository(root: Path):
files = []
directories = set()
languages = Counter()
for path in root.rglob("*"):
if should_ignore(path.relative_to(root)):
continue
if path.is_dir():
directories.add(str(path.relative_to(root)))
continue
files.append(str(path.relative_to(root)))
language = LANGUAGE_MAP.get(path.suffix.lower())
if language:
languages[language] += 1
return {
"project_name": root.name,
"total_files": len(files),
"total_directories": len(directories),
"languages": dict(languages.most_common()),
"files": files,
}
def main():
target = Path(sys.argv[1] if len(sys.argv) > 1 else ".").resolve()
result = scan_repository(target)
print(json.dumps(
result,
ensure_ascii=False,
indent=2
))
if __name__ == "__main__":
main()
它做的事情很朴素:跳过缓存和依赖目录,统计文件、目录、语言,再把文件清单交出来。模型不用自己掰手指数 174 个文件,我们已经给它配了计算器。
在 x-ray 根目录打开终端,执行和截图一致的命令:
python scripts/scan_repo.py .
运行 python scripts/scan_repo.py . 测试扫描脚本
正常情况下,终端会返回一段 JSON:
扫描脚本返回项目名、文件数、目录数与语言
第一次小成功: 能看到 project_name、total_files、total_directories、languages 和 files,说明扫描脚本已经能工作。
6. 再写一个脚本:识别技术栈
只知道“有多少文件”还不够。这个脚本会根据真实配置文件识别技术栈,让 Agent 有证据再下结论。
继续在 scripts 中创建 detect_stack.py。
继续创建用于识别技术栈的 detect_stack.py
复制下面代码:
from pathlib import Path
import json
import sys
def load_json(path: Path):
try:
return json.loads(path.read_text(encoding="utf-8"))
except Exception:
return {}
def detect_stack(root: Path):
detected = []
package_json = root / "package.json"
if package_json.exists():
detected.append("Node.js")
data = load_json(package_json)
dependencies = {}
dependencies.update(data.get("dependencies", {}))
dependencies.update(data.get("devDependencies", {}))
rules = {
"react": "React",
"next": "Next.js",
"vue": "Vue",
"nuxt": "Nuxt",
"svelte": "Svelte",
"@sveltejs/kit": "SvelteKit",
"express": "Express",
"fastify": "Fastify",
"nestjs": "NestJS",
"@nestjs/core": "NestJS",
"tailwindcss": "Tailwind CSS",
"prisma": "Prisma",
"@prisma/client": "Prisma",
"drizzle-orm": "Drizzle ORM",
"typescript": "TypeScript",
"vite": "Vite",
"webpack": "Webpack",
"vitest": "Vitest",
"jest": "Jest",
}
for package_name, technology in rules.items():
if package_name in dependencies:
detected.append(technology)
if (root / "requirements.txt").exists():
detected.append("Python")
if (root / "pyproject.toml").exists():
detected.append("Python")
if (root / "Cargo.toml").exists():
detected.append("Rust")
if (root / "go.mod").exists():
detected.append("Go")
if (root / "pom.xml").exists():
detected.append("Java / Maven")
if (root / "build.gradle").exists():
detected.append("Java / Gradle")
if (root / "Dockerfile").exists():
detected.append("Docker")
if (root / "docker-compose.yml").exists():
detected.append("Docker Compose")
if (root / "docker-compose.yaml").exists():
detected.append("Docker Compose")
detected = list(dict.fromkeys(detected))
return {
"technology_stack": detected
}
def main():
target = Path(
sys.argv[1] if len(sys.argv) > 1 else "."
).resolve()
result = detect_stack(target)
print(json.dumps(
result,
ensure_ascii=False,
indent=2
))
if __name__ == "__main__":
main()
还是在同一个终端执行:
python scripts/detect_stack.py .
运行 python scripts/detect_stack.py . 查看识别结果
截图里得到的是空数组 [],这不是翻车。我们此刻扫描的是 X-ray 自己,它的根目录没有 package.json、requirements.txt 等证据。没证据就不乱猜,恰恰是好习惯。
7. 把两份结果装进同一个 JSON
前两个脚本各管一摊,这一步给它们加一个统一入口。以后 Agent 只需要运行一次,就能拿到一份结构化数据。
创建 scripts/run_xray.py。
创建 run_xray.py 统一生成扫描数据
写入:
from pathlib import Path
import json
import sys
from scan_repo import scan_repository
from detect_stack import detect_stack
def run_xray(root: Path):
repo_data = scan_repository(root)
stack_data = detect_stack(root)
result = {
"project": repo_data,
"stack": stack_data,
}
return result
def main():
target = Path(
sys.argv[1] if len(sys.argv) > 1 else "."
).resolve()
result = run_xray(target)
output_path = Path("xray-data.json")
output_path.write_text(
json.dumps(
result,
ensure_ascii=False,
indent=2
),
encoding="utf-8"
)
print(f"X-ray 扫描完成")
print(f"目标项目:{target}")
print(f"数据文件:{output_path.resolve()}")
if __name__ == "__main__":
main()
这个文件是统一入口。它把仓库扫描和技术栈识别合并,写成 xray-data.json,方便 Agent 后面继续处理。
8. 补三件事:关键文件、复杂度、HTML 模板
做到这里,最小骨架已经有了:说明书、扫描脚本、技术栈识别和统一入口。代码看累了可以先歇一下;你不需要立刻理解每一行 Python,只要知道每个文件负责什么、运行后应该看到什么。
接下来三个组件会把“能扫描”升级成“能给出有用报告”:找关键文件、计算可解释的复杂度、把结果放进 HTML。它们不是为了把 Python 课偷偷塞进来,而是给 Agent 提供可复用的测量工具。
先创建 scripts/find_key_files.py:
创建 find_key_files.py 寻找高信息密度文件
from pathlib import Path
import json
import sys
KEY_FILE_RULES = {
"README.md": "项目说明",
"README": "项目说明",
"package.json": "Node.js 项目配置",
"pyproject.toml": "Python 项目配置",
"requirements.txt": "Python 依赖",
"Cargo.toml": "Rust 项目配置",
"go.mod": "Go 项目配置",
"Dockerfile": "容器部署",
"docker-compose.yml": "容器编排",
"docker-compose.yaml": "容器编排",
"next.config.js": "Next.js 配置",
"next.config.mjs": "Next.js 配置",
"next.config.ts": "Next.js 配置",
"vite.config.js": "Vite 配置",
"vite.config.ts": "Vite 配置",
"tsconfig.json": "TypeScript 配置",
"prisma/schema.prisma": "数据库模型",
"src/main.py": "Python 程序入口",
"main.py": "Python 程序入口",
"src/main.ts": "程序入口",
"src/main.tsx": "前端程序入口",
"src/app/layout.tsx": "Next.js 根布局",
"src/app/page.tsx": "Next.js 首页",
"middleware.ts": "中间件",
"middleware.js": "中间件",
}
def find_key_files(root: Path):
key_files = []
for relative_path, reason in KEY_FILE_RULES.items():
path = root / relative_path
if path.exists():
key_files.append({
"path": relative_path,
"reason": reason
})
return {
"key_files": key_files
}
def main():
target = Path(
sys.argv[1] if len(sys.argv) > 1 else "."
).resolve()
result = find_key_files(target)
print(json.dumps(
result,
ensure_ascii=False,
indent=2
))
if __name__ == "__main__":
main()
它不让模型凭感觉选文件,而是先把 README、配置、入口等高信息密度位置列出来。
再创建 scripts/complexity.py:
创建 complexity.py 计算可解释的复杂度分数
from pathlib import Path
import json
import sys
def clamp(value, min_value=0, max_value=10):
return max(min_value, min(max_value, value))
def calculate_complexity(project_data, stack_data, key_files_data):
total_files = project_data.get("total_files", 0)
total_directories = project_data.get("total_directories", 0)
languages = project_data.get("languages", {})
technology_stack = stack_data.get("technology_stack", [])
key_files = key_files_data.get("key_files", [])
size_score = min(total_files / 100, 10)
structure_score = min(total_directories / 20, 10)
stack_score = min(len(technology_stack) * 1.2, 10)
language_score = min(len(languages) * 2, 10)
key_file_score = min(len(key_files), 10)
final_score = (
size_score * 0.30
+ structure_score * 0.20
+ stack_score * 0.25
+ language_score * 0.10
+ key_file_score * 0.15
)
final_score = round(clamp(final_score), 1)
if final_score < 3:
level = "简单"
elif final_score < 5:
level = "中等"
elif final_score < 7:
level = "偏复杂"
elif final_score < 9:
level = "复杂"
else:
level = "非常复杂"
return {
"score": final_score,
"level": level,
"dimensions": {
"size": round(size_score, 1),
"structure": round(structure_score, 1),
"technology_stack": round(stack_score, 1),
"languages": round(language_score, 1),
"key_files": round(key_file_score, 1),
}
}
def main():
if len(sys.argv) < 2:
print("用法:python complexity.py xray-data.json")
return
data_path = Path(sys.argv[1])
data = json.loads(data_path.read_text(encoding="utf-8"))
result = calculate_complexity(
data["project"],
data["stack"],
data["key_files"]
)
print(json.dumps(
result,
ensure_ascii=False,
indent=2
))
if __name__ == "__main__":
main()
复杂度不是圣旨,只是一个可以解释、可以调整的起点。至少它比“模型看了一眼,觉得大概 8 分”靠谱。
然后在 assets/report-template.html 写一个最小模板:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>X-ray Report</title>
</head>
<body>
<h1>🔍 X-ray</h1>
<h2>{{PROJECT_NAME}}</h2>
<p>文件数量:{{TOTAL_FILES}}</p>
<p>目录数量:{{TOTAL_DIRECTORIES}}</p>
<p>复杂度:{{COMPLEXITY_SCORE}} / 10</p>
<h2>技术栈</h2>
<div>{{TECH_STACK}}</div>
<h2>关键文件</h2>
<div>{{KEY_FILES}}</div>
</body>
</html>
文件树里应该能看到模板:
在 assets 中创建 report-template.html
最后创建 scripts/render_report.py:
创建 render_report.py 把结构化数据填入模板
from pathlib import Path
import json
import sys
def render_report(data_path):
data = json.loads(
Path(data_path).read_text(encoding="utf-8")
)
script_dir = Path(__file__).resolve().parent
template_path = (
script_dir.parent
/ "assets"
/ "report-template.html"
)
template = template_path.read_text(
encoding="utf-8"
)
project = data["project"]
stack = data["stack"]
complexity = data["complexity"]
key_files = data["key_files"]
tech_stack = ", ".join(
stack["technology_stack"]
)
key_files_text = ""
for item in key_files["key_files"]:
key_files_text += (
f"<p>{item['path']} —— "
f"{item['reason']}</p>"
)
template = template.replace(
"{{PROJECT_NAME}}",
project["project_name"]
)
template = template.replace(
"{{TOTAL_FILES}}",
str(project["total_files"])
)
template = template.replace(
"{{TOTAL_DIRECTORIES}}",
str(project["total_directories"])
)
template = template.replace(
"{{COMPLEXITY_SCORE}}",
str(complexity["score"])
)
template = template.replace(
"{{TECH_STACK}}",
tech_stack
)
template = template.replace(
"{{KEY_FILES}}",
key_files_text
)
output_path = (
Path(data_path).parent
/ "xray-report.html"
)
output_path.write_text(
template,
encoding="utf-8"
)
print("✅ X-ray 报告生成完成")
print(output_path)
if __name__ == "__main__":
render_report(sys.argv[1])
先别单独运行它。当前 run_xray.py 只稳定生成 project 和 stack,而这个渲染脚本还需要 key_files 与 complexity。本集的真实路线是让 Agent 调用这些组件、抽样阅读源码,再生成完整 HTML;后面再把它们串成纯脚本流水线也来得及。
9. 先运行统一扫描入口
在交给 Agent 之前,我们先自己运行一次统一入口。这样如果出错,也能立刻判断是脚本问题,还是后面的 Agent 调用问题。
在 x-ray 根目录执行:
python scripts\run_xray.py .
运行 python scripts/run_xray.py . 生成 xray-data.json
你应该看到“X-ray 扫描完成”、目标项目路径和数据文件路径,同时目录里多出 xray-data.json。
xray-data.json 保存脚本采集的一手事实
到这里,事实采集层已经成功。 JSON 只负责记录它真正看见的东西,不负责把未知内容脑补成“高并发微服务宇宙”。
10. 给 Agent 一份分析规则
扫描数据只告诉 Agent“看到了什么”,分析规则负责告诉它“应该怎样解释、什么时候必须承认不确定”。
在 references 中创建 analysis-rules.md。
在 references 中创建 analysis-rules.md
写入:
# X-ray 分析规则
先读取 `xray-data.json`,所有分析必须基于扫描得到的一手数据。
## project.total_files
用于判断项目规模。
## project.total_directories
用于判断项目的目录规模和组织程度。
## project.languages
用于判断项目主要使用的开发语言。
## project.files
用于了解项目文件结构,并选择值得进一步阅读的关键文件。
## stack.technology_stack
用于判断已经识别出的技术栈。
## 分析原则
在 `xray-data.json` 的基础上,再读取 README、配置文件和关键源码,
进一步判断:
- 这个项目是什么
- 项目类型
- 整体架构
- 关键模块
- 项目复杂度
- 适合哪些方向的人学习
- 推荐阅读顺序
所有结论必须优先依据 `xray-data.json` 和实际源码。
`references` 只负责说明“如何解释扫描数据”,不能覆盖或虚构项目的一手事实。
如果证据不足,明确标记为“不确定”,不要猜测。
这份文件的作用不是给答案,而是约束“怎么得出答案”。事实来自项目,规则只负责提醒 Agent 别演得太投入。
11. 把完整流程补回 SKILL.md
前面的文件各自已经能工作,但 Agent 还不知道如何把它们串起来。这一步把完整工作流补回 SKILL.md,相当于给它一张执行路线图。
在 SKILL.md 的安全规则后继续加入:
## 工作流程
当用户调用 X-ray 分析一个陌生项目时:
1. 使用 `scripts/run_xray.py` 扫描当前项目。
2. 读取扫描生成的 `xray-data.json`。
3. 读取 `references/analysis-rules.md`,按照其中的规则解释扫描数据。
4. 根据 `xray-data.json` 中的文件列表,进一步读取:
- README
- 项目配置文件
- 程序入口
- 关键源码文件
5. 基于真实扫描数据和实际源码,分析:
- 这个项目是什么
- 项目类型
- 技术栈
- 文件结构
- 整体架构
- 项目复杂度
- 适合哪些方向的人学习
- 推荐阅读顺序
6. 最终生成一个 HTML 可视化项目报告。
## 原则
- 优先依据 `xray-data.json` 和实际源码。
- `references` 只提供分析方法,不提供项目事实。
- 不确定的信息明确标记为“不确定”。
- 不修改被分析项目的源代码。
- 不安装依赖。
- 不执行未知项目代码。
现在 X-ray 的“大脑说明书、测量工具、判断规则和报告模板”都齐了。下一步不再盯着代码看,我们直接把它扔进真实项目。
12. 选一个你想分析的项目
这里不要求你必须使用 AIFriends:
- • 手边有自己的项目:直接使用自己的公开项目,或确认允许发送给当前模型服务的项目。
- • 暂时没有合适项目:跟着截图使用 AIFriends,最容易对照结果。
不要把未经允许的公司仓库或敏感代码交给云端 Agent。示例可以换,数据边界不能靠勇气突破。
为了和截图一致,可以在桌面新建 测试X-ray 文件夹。
新建测试X-ray文件夹,准备第一次真实项目验收
进入这个文件夹,执行和截图一致的 SSH 命令:
git clone git@github.com:ppshux/AIFriends.git
克隆本集使用的公开测试仓库;截图采用 Git SSH 地址
如果你还没有配置 GitHub SSH Key,就用官方仓库页面提供的 HTTPS 地址:
git clone https://github.com/ppshuX/AIFriends.git
本次截图使用 AIFriends,只是为了演示和方便对照;它不是教程的主角,也不是制作 Skill 的必选项。
两条命令只选一条。看到 Cloning into 'AIFriends'... 并顺利结束,就说明测试项目已经准备好;我们不会安装它的依赖,也不会运行它。
13. 用 CodeBuddy 打开测试目录
这一步是让 CodeBuddy 把测试项目当作当前工作目录。这样调用 /x-ray 时,它才知道应该分析谁。
用 CodeBuddy 打开刚刚的 测试X-ray 文件夹。
用 CodeBuddy 打开测试文件夹
第一次使用 CodeBuddy 的读者,回看第 1 节的官方下载入口即可。这里的成功标志只有一个:左侧能看到刚克隆的项目文件,右侧能打开 Agent 对话。
14. 把本地 X-ray 装进 CodeBuddy
Skill 文件夹做好了,不等于 Agent 已经能看见它。这一步要把 X-ray 放进 CodeBuddy 实际读取的 Skill 目录。
我们直接把真实实验里的话原样交给 Agent:
把我桌面的 x-ray 放到你的skill目录里
让 CodeBuddy 把桌面的 x-ray 放入本地 Skill 目录
CodeBuddy 会找到桌面的 x-ray,再把它放进自己的 Skill 目录。Windows 上通常是:
C:\Users\你的用户名.codebuddy\skills\x-ray
CodeBuddy 完成本地 Skill 安装并提示重启
截图里的关键信息是安装目录与“需要重启/重载”。完成后彻底重启 CodeBuddy,让它重新扫描 Skills。
15. 调用 /x-ray,生成第一份真实报告
前面都是搭积木,现在才是真正的验收:如果 /x-ray 能被识别,并为测试项目生成报告,这个 Skill 才算跑通。
重启后,在对话框输入 /x。列表中能看到 /x-ray,说明安装成功。
重启后输入斜杠加 x,列表中已经出现 x-ray
选中它,直接回车,不需要额外写提示词:
/x-ray
直接执行 /x-ray,不需要另外编写提示词
CodeBuddy 会先运行扫描脚本,再读取 JSON、分析规则、README、配置与必要源码。
CodeBuddy 开始运行 X-ray 并读取扫描脚本
等它完成后,测试目录里应该出现:
xray-data.json
xray-report.html
打开 HTML,先看项目结构与关键技术组件:
第一份报告展示项目结构与技术组件
继续往下看复杂度、适合的学习方向和推荐阅读顺序:
第一份报告给出复杂度、适合方向和推荐阅读顺序
最后回到报告顶部,确认项目名和关键指标:
第一份 X-ray 项目解剖报告已经生成
上集通关: 第一份真实项目报告已经生成。此时我们不只是“写了几个文件”,而是亲手完成了“制作 Skill → 本地安装 → 真实项目调用 → 交付 HTML”的第一次闭环。
16. 三个最容易卡住的地方
- • 终端提示找不到 Python:依次试
python --version与py --version,后续统一使用能显示 Python 3 的那个命令。 - • 重启后没有
/x-ray:检查%USERPROFILE%.codebuddy\skills\x-ray\SKILL.md是否存在,并确认SKILL.md文件名没有变成SKILL.md.txt。 - • 只有 JSON,没有 HTML:JSON 只代表脚本层完成;继续等待 Agent 抽样阅读与生成报告,并查看对话里是否出现文件读取或写入失败。
还有一个容易被忽略的边界:X-ray 不修改目标项目源码,但会在测试目录写出 xray-data.json 和 xray-report.html。如果用于公司仓库,还要先确认代码能否发送给当前模型服务,并检查报告里是否包含内部路径、仓库名或业务信息。
17. 收尾:我们真正做成了什么
上集通关:X-ray 已在真实仓库中生成第一份项目解剖报告
这一集完成了三件事:
- 用
SKILL.md + scripts + references + assets做出 X-ray; - 把它装进 CodeBuddy,并成功唤起
/x-ray; - 用一个真实仓库生成第一份可以打开的项目解剖报告。
这时的 X-ray 已经不是一段聊天记录,而是一套可以重复调用的工作方法。
更重要的是,你已经掌握了制作 Skill 的通用套路。哪怕你对“项目 X 光机”没有兴趣,也可以保留目录结构和验证方法,把里面的任务换成自己的需求。
下一集只增加一个变量:把它发布到 GitHub,再从仓库重新安装,并换一个项目验证“换个地方还能用”。
上集已完成“制作 Skill + 本地真实调用”。清晰原图、完整代码和后续两集持续更新在:AI 应用架构演进实践。下一集把 X-ray 发布到 GitHub。
