项目精华精炼器
何时使用
用户说这些话时,直接触发本 skill:
- "做一个更好的版本" / "做一个更好的 XX 系统"
- "取其精华去其糟粕" / "升级一下我的 XX"
- "项目太臃肿了,精简一下"
- "参考 XX 项目重做自己的" / "仿造 XX 做一版"
- "整合 XX 和 YY 的优点"
- "我之前做过 XX,现在想重构"
- "帮我把这个老项目现代化"
核心信念
更少的步骤、更轻的依赖、更清晰的架构 = 更好的项目。
- 借鉴"经过验证的模式",不是抄袭代码
- 每一步都问:"这真的需要吗?能更简单吗?"
- 能省则省:CPU torch 替代 CUDA、本地模型替代联网、SQLite 起步
工作流
Phase 0: 明确起点与目标
- 读取上游分析报告(analyzer 产物,含 ✅/🔧/❌ 标记)
- 明确项目目标、技术约束(成本/时间/部署环境)
- 建立「借鉴清单」:
- ✅ 直接复用(架构模式、协议、认证)
- 🔧 升级替换(embedding、检索、存储)
- ❌ 坚决不要(缺陷、过度工程)
Phase 1: 保留精华(骨架不动)
从分析报告提取,原样保留:
- 分层架构(routes / services / models / schemas / core / utils)
- 流式响应协议(SSE:references / answer / done / error)
- 权限模型(JWT + 角色 + 知识库隔离)
- API 路由结构与请求/响应 Schema
Phase 2: 升级短板(一次只换一个变量)
2.1 Embedding 升级:哈希 → BGE 真实语义
Before(无语义哈希):
# ❌ 差方案:MD5 哈希,完全没有语义能力
import hashlib
def embed_text(text: str) -> list[float]:
h = hashlib.md5(text.encode()).digest()
return [b / 255.0 for b in h] # 16维,语义为0
After(BGE 真实语义):
# ✅ 好方案:ModelScope 下载,本地 CPU 运行
from modelscope import snapshot_download
from transformers import AutoTokenizer, AutoModel
import torch
MODEL_PATH = snapshot_download("BAAI/bge-small-zh-v1.5")
class EmbeddingService:
def __init__(self, model_path: str = MODEL_PATH):
self.tokenizer = AutoTokenizer.from_pretrained(model_path)
self.model = AutoModel.from_pretrained(model_path)
self.model.eval()
def embed(self, text: str) -> list[float]:
with torch.no_grad():
inputs = self.tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
outputs = self.model(**inputs)
embedding = outputs.last_hidden_state[:, 0].numpy()[0]
return embedding.tolist()
> 💡 注意:snapshot_download 走 ModelScope 国内源,不被墙。torch 装 CPU 版:pip install torch==2.1.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
2.2 bcrypt 依赖修复(Before/After)
Before(bcrypt 5.x 崩溃):
# ❌ requirements.txt —— passlib 1.7.4 与 bcrypt 5.x 不兼容
bcrypt==5.0.0
passlib==1.7.4
运行报错:AttributeError: module 'bcrypt' has no attribute '__about__'
After(锁死安全版本):
# ✅ requirements.txt
bcrypt==4.1.3
passlib==1.7.4
2.3 SSE 流式 + 数据库(MissingGreenlet 修复)
Before(SSE 生成器内操作 db → 崩溃):
# ❌ MissingGreenlet: session 已在生成器关闭后被访问
@router.post("/stream")
async def stream_chat(request: ChatRequest, db: Session = Depends(get_db)):
async def generate():
# 这里 db 已经被 FastAPI 关闭了
result = await llm.astream(prompt) # 流式生成
db.add(Message(content=result)) # 💥 MissingGreenlet
await db.commit()
yield f"data: {result}\n\n"
return StreamingResponse(generate(), media_type="text/event-stream")
After(db 操作拆分到生成器外):
# ✅ 拆分:setup 阶段用 db,generate 阶段纯流式
@router.post("/stream")
async def stream_chat(request: ChatRequest, db: Session = Depends(get_db)):
session_id = request.session_id or str(uuid.uuid4())
user_msg = db.query(Message).filter_by(session_id=session_id).first()
if not user_msg:
user_msg = Message(session_id=session_id, role="user", content=request.message)
db.add(user_msg)
db.commit()
async def generate():
try:
async for chunk in llm.astream(request.message):
yield f"data: {chunk}\n\n"
finally:
# 流式结束后,独立 session 持久化
finally_db = next(db_session_factories)
ai_msg = Message(session_id=session_id, role="assistant", content="...")
finally_db.add(ai_msg)
finally_db.commit()
return StreamingResponse(generate(), media_type="text/event-stream")
2.4 存储升级(SQLite → 预留接口)
# ✅ 起步用 SQLite,后期一键切换 PostgreSQL
from settings import settings
DATABASE_URL = settings.database_url or "sqlite:///./data.db"
# 改一行 .env 即可切换:DATABASE_URL=postgresql://user:pass@host/db
Phase 3: 删除冗余
主动砍掉这几类:
- ❌ 顺带装的 nvidia 包(GB 级,CPU 运行不需要):删掉,换 torch==2.x+cpu
- ❌ 工厂模式/过度抽象层(能用函数就不建类)
- ❌ Kubernetes/Qdrant/Elasticsearch(小项目用不上)
- ❌ 硬编码路径(如 D:\modelscope_cache):统一读 os.getenv()
Phase 4: 避坑清单(12 条)
| # | 坑 | 解法 |
|---|---|---|
| 1 | pydantic extra=forbid 崩溃 |
.env 变量必须在 Settings 类里全声明 |
| 2 | create_all 没跑就插数据 |
脚本先 Base.metadata.create_all(bind=engine) |
| 3 | ssh 长脚本被 SIGKILL | 短命令分步执行 + nohup/setsid |
| 4 | Windows 路径在 Linux 崩 | 所有路径读环境变量,不硬编码 |
| 5 | bcrypt 5.x + passlib 不兼容 | 锁 bcrypt==4.1.3 |
| 6 | SSE + DB MissingGreenlet | db 操作拆到生成器外(见 Phase 2.3) |
| 7 | nginx 软链接断链 | 软链名必须与目标文件名完全一致 |
| 8 | 前后端混在一个项目 | 前端单独 build,通过 nginx 或 CDN 托管 |
| 9 | PowerShell && 报错 |
用分号隔开,或分开两行执行 |
| 10 | HuggingFace 下载被墙 | 用 modelscope.snapshot_download |
| 11 | torch 默认装 CUDA | 指定 torch==2.x+cpu |
| 12 | Kimi 模型 temperature 限制 | kimi-k2.6 强制 temperature=1.0 |
Phase 5: 分步验证
每完成一个模块就测,不要全做完了再测:
# 1. 单端点(注意 FastAPI 重定向到 /api/x/)
curl -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}' \
-L # -L 跟随重定向
# 2. 端到端(用 httpx,follow_redirects=True)
# 登录 → 建知识库 → 上传文档 → 对话 → 检查 references 字段
# 3. 部署验证
curl https://your-domain.com/api/health # 公网访问
Phase 6: 输出交付物
- 项目代码(独立目录,与参考项目隔离)
- 对比文档
your-pro-vs-friend.md(✅ 改进点 / ❌ 删除点 / 📊 性能对比) deploy.sh+README.md+ARCHITECTURE.md
常见问题 FAQ
Q:没有 analyzer 的分析报告,能直接用这个 skill 吗?
A:可以,但效果打折扣。最佳流程是先 analyzer 分析 → 再 refiner 精炼。如果没有报告,跳过 Phase 0,直接进入 Phase 1,参照自己对该项目的理解建立借鉴清单。
Q:Phase 2 一次只换一个变量,怎么判断先换哪个?
A:按影响/成本比排序:Embedding(检索准确率提升最明显)> 存储(数据量到 10GB+ 再换)> 上下文压缩(对话轮次多时才有价值)。建议 Embedding 最先做。
Q:遇到坑没在清单里怎么办?
A:① 先看 analyzer 报告的"缺陷清单"是否有线索;② 看 Phase 4 按类目排查(数据库/部署/依赖/网络);③ 独立 session 跑 python -c "import 模块; help(模块)" 定位问题;④ 上网搜错误信息 + site:stackoverflow.com。
Q:Phase 3 删除冗余,会不会砍掉有用的功能?
A:Phase 3 删除的是"依赖和架构冗余",不是功能。功能在 Phase 1 保留的骨架里。如果不确定某个依赖是否需要,看它是否影响 Phase 6 的任意一个交付物。
红线
- 不污染参考项目(独立目录)
- 升级控制变更面(别一次换 5 个东西同时调试)
- 每个"精简"决策都要能解释理由
- 功能不退化(精简 ≠ 砍功能)
- 生成文件用 UTF-8,不硬编码 Windows 绝对路径
与 open-source-logic-analyzer 的衔接
先 analyzer 分析 → 再 refiner 精炼。若用户下一步是"做一个更好的版本",自动衔接触发本 skill。