EvoCode 开发指导(详细版)¶
MVP 边界 · 技术架构 · 数据库 · API 契约 · 分析流水线 · AI 设计 · 开发顺序 · 部署与测试 版本:v1.3 · 2026-08-10
使用说明(灵活性声明):本文档是纲要与基线,不是死规定。版本号、表结构、接口字段、阶段划分均为建议基线;遇到文档未覆盖或与实际冲突的情况,具体情况具体分析——先记录决策(AD),再合理偏离,最后回填本文档。例如:P3 与 P4 顺序可互换、DDL 字段可按实现增删、Sonar 可换成自研规则引擎(如时间不允许)。唯一硬约束:三端验证命令通过 + 验收用例(AC)通过。
目录¶
- MVP(v0.1)定义
- 环境准备与工具链
- 关键技术决策(AD)
- 仓库结构与模块说明
- 数据库设计(完整 DDL)
- API 设计(详细契约)
- 分析流水线设计
- AI 报告生成设计
- RAG 与 AI 医生设计(P6)
- 健康分算法(评分模型)
- 开发顺序(任务清单与里程碑)
- 部署方案
- 测试策略
- 性能预算
- 风险与对策
1. MVP(v0.1)定义¶
1.1 做(In Scope)¶
| 项 | 说明 |
|---|---|
| 创建项目 | zip 上传 + GitHub 公开仓库 clone(--depth 1) |
| 项目档案 | 语言占比、框架识别、LOC、文件数、最近分析时间 |
| 扫描 | 文件树、忽略规则、语言识别、技术栈识别、LOC 统计 |
| 分析任务 | 异步 + DB 状态机 + 前端轮询(2s) |
| AI 报告 | 健康分、技术栈、项目地图摘要、风险清单、分阶段建议;无 Key 自动降级规则版 |
| 页面 | 项目列表、创建、详情 Overview、报告页 |
1.2 不做(Out of Scope)¶
- 登录/多用户/权限(v1.0 前不做)
- SonarQube 集成、调用图、Git 历史、AI 聊天、技术债、文档生成、Dashboard
- 增量分析(每次全量)
- Redis 队列(DB 状态机够用)
- LangChain / LlamaIndex
1.3 验收标准¶
见 01-需求分析.md §13 的 12 条验收用例(AC-1 ~ AC-12),全过才算完成。
2. 环境准备与工具链¶
2.1 版本基线(锁死,避免"在我机器上能跑")¶
| 组件 | 版本 | 说明 |
|---|---|---|
| JDK | 17(21 亦可) | Spring Boot 3 要求 ≥17 |
| Spring Boot | 3.3.x(≥3.3) | 稳定线 |
| MyBatis Plus | 3.5.7+ | 用 mybatis-plus-spring-boot3-starter |
| PostgreSQL | 16.x | 含 pgvector 0.7+(镜像 pgvector/pgvector:pg16) |
| Redis | 7.x | P1 起预装,P6+ 才真正使用 |
| Node | 20 LTS 或 22 LTS | Vite 5/6 要求 |
| Vue | 3.4+ | |
| Vite | 5.x 或 6.x | |
| Python | 3.11 | |
| FastAPI | 0.115+ | |
| SonarQube | 10.x 社区版 | docker 镜像 sonarqube:lts-community |
| Docker Desktop | 最新 | 本地基础设施 |
| tree-sitter | 0.22+ + 语言包 | tree-sitter-language-pack(bundle 常见语言) |
2.2 国内网络准备¶
- npm 镜像:
npm config set registry https://registry.npmmirror.com - Maven 镜像:
settings.xml配阿里云https://maven.aliyun.com/repository/public - pip 镜像:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple - Docker 镜像加速:配置 registry-mirrors(阿里云/腾讯云)
- GitHub clone 慢:提供代理配置项(
GIT_PROXY),失败引导 zip 上传
2.3 本地启动脚本规划¶
scripts/
├── start-dev.ps1 / start-dev.sh # 一键:docker compose up → analyzer → backend → frontend
├── stop-dev.ps1 / stop-dev.sh
├── init-db.ps1 / init-db.sh # 首次建库建表(执行 V*.sql)
├── smoke.ps1 # 三端健康检查(analyzer/backend/frontend)
├── check-contract.ps1 # 契约字段比对(analyzer schemas ↔ 后端 DTO,见 08 §4)
└── security-smoke.ps1 # 安全冒烟(恶意 zip/越权读取,见 08 §6 与 03 §7.4)
3. 关键技术决策(AD)¶
| # | 决策 | 选择 | 理由 |
|---|---|---|---|
| AD-1 | 整体形态 | 单体:Spring Boot + Python Analyzer + Vue | 可控、可答辩、演示简单 |
| AD-2 | 分析器独立服务 | Python FastAPI(仅 127.0.0.1) | tree-sitter 生态在 Python 最好;职责清晰;契约化协作 |
| AD-3 | LLM 框架 | 不引 LangChain,自写 LLMClient 接口 |
国内 DeepSeek 现实;RAG 手写(pgvector 检索 + prompt 拼装)足够,论文好讲 |
| AD-4 | 向量库 | PostgreSQL + pgvector | 少一个中间件,事务一致 |
| AD-5 | 异步任务 | DB 状态机 + Spring @Async + 轮询 | 单机足够;Redis 队列留扩展点 |
| AD-6 | 代码存储 | 磁盘 data/projects/{id}/,DB 只存元数据 |
避免大对象入库;备份=打包目录 |
| AD-7 | Git 分析 | analyzer 内 git log --numstat(subprocess,参数白名单) |
git 必然已装;零依赖;输出稳定 |
| AD-8 | SonarQube | docker 社区版 + sonar-scanner | 免费;不可用时质量维度 N/A 不影响其他 |
| AD-9 | 报告降级 | 无 LLM Key/失败 → 规则版报告(标注来源) | 演示/答辩永不卡死(核心设计) |
| AD-10 | 多语言 | 完整:Java/Python/TS/JS/Go/Vue;其余按文本统计 | 覆盖常见场景,其余降级 |
| AD-11 | 安全 | 静态只读;路径三重校验;绝不执行被分析代码 | 防恶意 zip/Shell 注入 |
| AD-12 | 隐私 | 默认发结构化摘要给 LLM;全文发送为显式开关 | 合规 + 省 token |
| AD-13 | 报告存储 | v0.1 报告 JSON 直接存 analysis.report_json;v1 数据量大再拆表 |
避免过度设计 |
| AD-14 | 健康分 | 规则基础分 + LLM 修正(±10,必须给理由) | 可解释、可复现(见 §10) |
| AD-15 | 数据库迁移 | SQL 文件版本化 V{version}__desc.sql 手写 |
可控、可评审;不引 Flyway 自动执行(v0.1 由脚本执行) |
| AD-16 | 流式 | 聊天用 SSE(后端 → 前端) | 简单可靠,避免 WebSocket 复杂性 |
4. 仓库结构与模块说明¶
evocode/
├── backend/ # Spring Boot 3
│ ├── src/main/java/com/evocode/
│ │ ├── EvocodeApplication.java
│ │ ├── config/ # AsyncConfig、CorsConfig、MybatisConfig、EvocodeProperties
│ │ ├── controller/ # ProjectController、AnalysisController、ReportController…
│ │ ├── service/
│ │ │ ├── project/ # ProjectService、UploadService(解压/克隆/校验)
│ │ │ ├── analysis/ # AnalysisService(状态机)、AnalyzerClient(HTTP 调 analyzer)
│ │ │ └── chat/ # ChatService(会话持久化 + SSE 透传,P6)
│ │ ├── mapper/ entity/ dto/ enums/
│ │ └── common/ # Result、BusinessException、ErrorCode、GlobalExceptionHandler
│ └── src/main/resources/
│ ├── application.yml / application-dev.yml / application-prod.yml
│ └── db/migration/V001__init.sql …
├── analyzer/ # Python FastAPI(内部服务)
│ ├── app/
│ │ ├── main.py # 路由注册:/analyze/v1/*
│ │ ├── schemas.py # 契约模型(唯一事实来源)
│ │ ├── config.py # 环境配置(pydantic-settings)
│ │ ├── scanners/ # filescanner.py、langdetect.py、stackdetect.py、loc.py
│ │ ├── parsers/ # base.py、java_parser.py、python_parser.py…(tree-sitter)
│ │ ├── quality/ # sonar_client.py、normalizer.py
│ │ ├── git_analyzer/ # git_stats.py、hotspot.py
│ │ ├── ai/ # llm_client.py、report_generator.py、rag.py、prompts.py
│ │ └── utils/ # path.py(安全封装)、ignore.py、limits.py
│ └── tests/ + tests/fixtures/ # 小样本工程
├── frontend/ # Vue3 + TS + Vite
│ └── src/
│ ├── api/ project.ts、analysis.ts、report.ts …
│ ├── views/ project-list/ project-detail/(overview/report/…)
│ ├── components/ base/ charts/ feature/
│ ├── stores/ project.ts
│ ├── utils/ request.ts、format.ts、markdown.ts
│ └── types/ api.ts(与后端 DTO 对应)
├── docker-compose.yml
├── scripts/ # start-dev / stop-dev / init-db
├── samples/ # 演示用示例项目 zip(小体积入库)
├── docs/ # 本目录 + decisions/ + devlog/ + screenshots/ + api/
└── .env.example .gitignore README.md
命名约定:后端模块目录 = 业务域(project/analysis/quality/arch/evolution/ai/debt/doc);analyzer 目录 = 技术职责。两者映射关系写进 README。
5. 数据库设计(完整 DDL)¶
约定:UTF-8;主键 bigint;时间
TIMESTAMPTZ;逻辑删除deleted;灵活结构jsonb;枚举存varchar(20)。 v0.1 只建:project / analysis / file_node / knowledge_chunk(预留),其余随阶段加。
V001__init.sql(v0.1)¶
-- 项目
CREATE TABLE project (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
source_type VARCHAR(10) NOT NULL, -- ZIP / GIT
repo_url VARCHAR(500),
storage_path VARCHAR(500) NOT NULL, -- data/projects/{id}/
status VARCHAR(20) NOT NULL DEFAULT 'CREATED', -- CREATED/ANALYZING/READY/FAILED
lang_stats JSONB, -- {"Java":60,"Python":30,...}
framework_tags VARCHAR(100)[], -- {Vue,Electron}
loc_total BIGINT,
file_count INT,
ignored_count INT, -- 被忽略文件数(报告说明)
last_analyzed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
deleted SMALLINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_project_deleted ON project(deleted);
CREATE INDEX idx_project_status ON project(status);
-- 分析任务(状态机)
CREATE TABLE analysis (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
type VARCHAR(20) NOT NULL DEFAULT 'FULL', -- FULL/QUALITY/ARCH/EVOLUTION
status VARCHAR(20) NOT NULL DEFAULT 'PENDING', -- PENDING/RUNNING/SUCCEEDED/FAILED/CANCELLED
progress INT NOT NULL DEFAULT 0, -- 0-100
stage VARCHAR(30), -- SCAN/REPORT/…
error_code VARCHAR(10),
error_message TEXT,
report_json JSONB, -- AI 报告(AD-13)
report_source VARCHAR(10), -- LLM / RULES(报告来源,05 第三轮 C-1)
prompt_version VARCHAR(20), -- 生成报告所用 prompt 版本(C-1)
analyzer_version VARCHAR(20),
regenerated_at TIMESTAMPTZ, -- 最近一次重新生成时间(C-2)
started_at TIMESTAMPTZ,
finished_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
deleted SMALLINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_analysis_project ON analysis(project_id, id DESC);
-- 文件快照(项目地图)
CREATE TABLE file_node (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
analysis_id BIGINT NOT NULL REFERENCES analysis(id),
path TEXT NOT NULL,
language VARCHAR(30),
loc INT,
size_bytes INT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_file_node ON file_node(project_id, analysis_id);
V002__quality.sql(P3:质量)¶
CREATE TABLE dependency (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
analysis_id BIGINT NOT NULL REFERENCES analysis(id),
ecosystem VARCHAR(20) NOT NULL, -- maven/npm/pip/go
name VARCHAR(200) NOT NULL,
version VARCHAR(50),
latest_version VARCHAR(50),
risk_level VARCHAR(10) NOT NULL DEFAULT 'LOW', -- LOW/MEDIUM/HIGH
risk_reason TEXT,
suggestion TEXT,
is_eol BOOLEAN NOT NULL DEFAULT FALSE,
ai_advice JSONB, -- {impact,steps,risks,estimate}(HIGH 依赖 AI 建议)
ai_status VARCHAR(10) NOT NULL DEFAULT 'NONE', -- NONE/PENDING/DONE/FAILED
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_dep_project ON dependency(project_id, analysis_id);
CREATE TABLE quality_issue (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
analysis_id BIGINT NOT NULL REFERENCES analysis(id),
source VARCHAR(20) NOT NULL, -- SONAR
severity VARCHAR(10) NOT NULL, -- BLOCKER/CRITICAL/MAJOR/MINOR/INFO
kind VARCHAR(20) NOT NULL, -- BUG/VULNERABILITY/SMELL
rule_key VARCHAR(100),
file_path TEXT,
line INT,
message TEXT,
ai_explanation TEXT,
ai_suggestion TEXT,
ai_status VARCHAR(10) NOT NULL DEFAULT 'PENDING', -- PENDING/DONE/FAILED
status VARCHAR(10) NOT NULL DEFAULT 'OPEN', -- OPEN/IGNORED/FIXED
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_qissue_project ON quality_issue(project_id, analysis_id);
V003__architecture.sql(P4:架构)¶
CREATE TABLE architecture_node (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
analysis_id BIGINT NOT NULL REFERENCES analysis(id),
node_key VARCHAR(300) NOT NULL, -- 如 com.evocode.service.UserService
name VARCHAR(200) NOT NULL,
node_type VARCHAR(20) NOT NULL, -- CONTROLLER/SERVICE/REPOSITORY/ENTITY/UTIL/MODULE/OTHER
file_path TEXT,
metrics JSONB, -- 入度/出度/依赖数
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_arch_node ON architecture_node(project_id, analysis_id);
CREATE TABLE architecture_edge (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
analysis_id BIGINT NOT NULL REFERENCES analysis(id),
source_node_id BIGINT NOT NULL REFERENCES architecture_node(id),
target_node_id BIGINT NOT NULL REFERENCES architecture_node(id),
relation VARCHAR(20) NOT NULL, -- CALL/IMPORT/DEPEND
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_arch_edge ON architecture_edge(source_node_id, target_node_id);
CREATE TABLE arch_violation (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
analysis_id BIGINT NOT NULL REFERENCES analysis(id),
violation_type VARCHAR(40) NOT NULL, -- LAYER_VIOLATION/CYCLE/GOD_CLASS/…
description TEXT,
source_node_id BIGINT REFERENCES architecture_node(id),
target_node_id BIGINT REFERENCES architecture_node(id),
severity VARCHAR(10) NOT NULL,
suggestion TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_arch_violation ON arch_violation(project_id, analysis_id);
V004__evolution.sql(P5:演化)¶
CREATE TABLE commit_stat (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
commit_hash VARCHAR(40) NOT NULL,
author_name VARCHAR(100),
author_email VARCHAR(200),
committed_at TIMESTAMPTZ NOT NULL,
lines_added INT NOT NULL DEFAULT 0,
lines_removed INT NOT NULL DEFAULT 0,
files_changed INT NOT NULL DEFAULT 0,
message TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_commit_project ON commit_stat(project_id, committed_at DESC);
CREATE TABLE file_change_stat (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
file_path TEXT NOT NULL,
commit_count INT NOT NULL DEFAULT 0,
lines_added_total BIGINT NOT NULL DEFAULT 0,
lines_removed_total BIGINT NOT NULL DEFAULT 0,
first_changed_at TIMESTAMPTZ,
last_changed_at TIMESTAMPTZ,
risk_score NUMERIC(5,2), -- 规则热度分(热点判定)
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (project_id, file_path)
);
CREATE INDEX idx_fchange_project ON file_change_stat(project_id, commit_count DESC);
V005__debt_doc.sql(P7:技术债 + 文档)¶
CREATE TABLE tech_debt (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
source VARCHAR(30) NOT NULL, -- ARCH/QUALITY/DEPEND/EVOLUTION/AI_DOCTOR/MANUAL
title VARCHAR(200) NOT NULL,
level VARCHAR(10) NOT NULL, -- HIGH/MEDIUM/LOW
description TEXT,
suggestion TEXT,
status VARCHAR(10) NOT NULL DEFAULT 'OPEN', -- OPEN/DOING/DONE/WONTFIX
ref_analysis_id BIGINT REFERENCES analysis(id),
resolve_note TEXT, -- DONE 时填验证说明
wonfix_reason TEXT, -- WONTFIX 必填
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
resolved_at TIMESTAMPTZ
);
CREATE INDEX idx_debt_project ON tech_debt(project_id, status);
CREATE TABLE generated_doc (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
doc_type VARCHAR(20) NOT NULL, -- README/ARCH/API
title VARCHAR(200) NOT NULL,
content TEXT NOT NULL,
version INT NOT NULL DEFAULT 1,
edited BOOLEAN NOT NULL DEFAULT FALSE, -- 是否人工修改过
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_doc_project ON generated_doc(project_id);
V006__chat_rag.sql(P6:聊天 + RAG)¶
CREATE TABLE chat_session (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
title VARCHAR(200) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_chat_session ON chat_session(project_id, created_at DESC);
CREATE TABLE chat_message (
id BIGSERIAL PRIMARY KEY,
session_id BIGINT NOT NULL REFERENCES chat_session(id),
role VARCHAR(10) NOT NULL, -- USER/ASSISTANT
content TEXT NOT NULL,
citations JSONB, -- [{file,line,excerpt}]
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_chat_msg ON chat_message(session_id, id);
-- 需要先 CREATE EXTENSION vector;
CREATE TABLE knowledge_chunk (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
analysis_id BIGINT NOT NULL REFERENCES analysis(id), -- 随分析全量重建(先删后插)
file_path TEXT NOT NULL,
chunk_index INT NOT NULL,
content TEXT NOT NULL,
meta JSONB, -- {symbol, lang}
embedding VECTOR(1024), -- 维数随模型:bge-m3=1024 / 3-small=1536
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_chunk_project ON knowledge_chunk(project_id, analysis_id);
CREATE INDEX idx_chunk_embedding ON knowledge_chunk
USING hnsw (embedding vector_cosine_ops);
V007__hotspot.sql(P5:热点落库)¶
01 FR-8.2 的热点判定(规则 + AI 结论)必须落库,否则每次查询重复计算/重复调 LLM。
CREATE TABLE hotspot (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL REFERENCES project(id),
analysis_id BIGINT NOT NULL REFERENCES analysis(id),
module VARCHAR(200) NOT NULL,
risk_level VARCHAR(10) NOT NULL, -- HIGH/MEDIUM
evidence JSONB, -- ["变更45次","新增12000行",...]
ai_conclusion TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_hotspot ON hotspot(project_id, analysis_id);
5.1 数据生命周期¶
- 删除项目(级联顺序):chat_message → chat_session → knowledge_chunk → hotspot → tech_debt → generated_doc → commit_stat/file_change_stat → arch_violation/edge/node → quality_issue/dependency → file_node → analysis → project → 删磁盘目录
- 建议用后端事务逐表删除 + 最后删目录(目录删除失败记日志,不阻塞业务)
6. API 设计(详细契约)¶
6.1 对外 REST(/api/v1,后端 knife4j 出文档)¶
统一响应:{"code":0,"message":"ok","data":…}
POST /api/v1/projects — 创建项目¶
// Content-Type: multipart/form-data(zip 方式)
{ "name": "Chatez", "file": <zip> }
// 或 JSON(GitHub 方式)
{ "name": "Chatez", "repoUrl": "https://github.com/xxx/chatez", "cloneDepth": 1 }
// 201 响应
{ "code": 0, "message": "ok",
"data": { "id": 1, "name": "Chatez", "sourceType": "ZIP", "status": "CREATED",
"storagePath": "data/projects/1", "langStats": null, "locTotal": 0 } }
// 错误:1002 参数非法 / 2003 zip 非法(含原因 code 细分:2003-1 路径穿越等)
GET /api/v1/projects?page=1&size=10&keyword=&language=&sort=lastAnalyzedAt&order=desc¶
{ "code": 0, "data": { "total": 5, "items": [
{ "id": 1, "name": "Chatez", "langStats": {"Java":60,"JavaScript":40},
"frameworkTags": ["Vue","Electron"], "locTotal": 20431, "fileCount": 412,
"healthScore": 82, "lastAnalyzedAt": "2026-08-10T10:00:00Z" } ] } }
POST /api/v1/projects/{id}/analyses¶
// req: { "type": "FULL" }
// 202 响应: { "code": 0, "data": { "id": 10, "projectId": 1, "status": "PENDING", "progress": 0 } }
// 2002 已有运行中任务
GET /api/v1/analyses/{id}(轮询)¶
{ "code": 0, "data": { "id": 10, "status": "RUNNING", "progress": 45,
"stage": "SCAN", "errorMessage": null } }
GET /api/v1/analyses/{id}/report¶
{ "code": 0, "data": { "analysisId": 10, "generatedAt": "...", "source": "LLM", // 或 RULES
"report": { "healthScore": 82, "level": "GOOD", "summary": "…",
"techStack": { "languages": {"Java":60,...}, "frameworks": ["Vue","Electron"] },
"dimensions": [ { "key":"quality","score":76,"stars":4,"summary":"…" } ],
"risks": [ { "level":"HIGH","title":"User 模块耦合度过高","detail":"…",
"suggestion":"拆分 UserService","references":[{"file":"...","line":12}] } ],
"recommendations": [ { "phase":"第一阶段","items":["拆分 UserService"] } ] } } }
其余(P1+ 数据接口,字段以 analyzer schemas 为准)¶
GET /projects/{id}/files?page=&size=&language=&keyword=
GET /projects/{id}/files/content?path= → 文件内容(白名单+≤2MB+只读;FR-6.3 预览用)
GET /projects/{id}/dependencies?riskLevel=
GET /projects/{id}/quality-issues?severity=&kind=&status=
GET /projects/{id}/architecture → {nodes, edges, violations}
GET /projects/{id}/tech-debts?status=
GET /projects/{id}/evolution?range=30d → {trend, topFiles, authors, hotspots}
GET /projects/{id}/chats → 会话列表
POST /projects/{id}/chats → 建会话
GET /chats/{id}/messages
POST /chats/{id}/messages → SSE 流式(text/event-stream)
DELETE /chats/{id} → 删除会话(级联删消息)
POST /analyses/{id}/report/regenerate → 重新生成报告(不重扫,仅重跑 LLM 步骤)
GET /projects/{id}/docs?type=README
POST /projects/{id}/docs/{id}/edit
6.2 Analyzer 内部 API(仅 127.0.0.1,/analyze/v1)¶
POST /analyze/v1/scan¶
// req: { "projectId": 1, "codeDir": "data/projects/1" }
// resp:
{ "languages": {"Java":60,"JavaScript":40,"OTHER":5}, "locTotal": 20431,
"fileCount": 412, "ignoredCount": 88,
"frameworks": ["Vue","Electron","Spring Boot"], "hasBackend": true, "hasFrontend": true,
"dbHint": ["MySQL"], "files": [ {"path":"src/main/java/...", "language":"Java","loc":180,"sizeBytes":4210} ] }
POST /analyze/v1/report¶
// req: { "projectId":1, "scan": {...}, "quality": {...} | null, "arch": {...} | null,
// "evolution": {...} | null, "historyReports": [...摘要...] }
// resp: { "source":"LLM"|"RULES", "report": { …同对外 report 结构… } }
P3+:/analyze/v1/quality → {metrics, issues}¶
P3+:/analyze/v1/explain → 单条 issue 解释(信号量限流 4)¶
P4+:/analyze/v1/architecture → {nodes, edges, violations}¶
P5+:/analyze/v1/evolution → {commits, trend, topFiles, authors, hotspots}¶
P6+:/analyze/v1/chat(SSE 生成端)→ 事件流 delta / citations / done / error¶
P6+:/analyze/v1/rag/index(切片入库)、/analyze/v1/rag/search(检索 topK)¶
analyzer 统一错误契约(非 200 时返回):
{ "error": { "code": "SCAN_TIMEOUT | LLM_FAILED | LLM_NO_KEY | SONAR_UNAVAILABLE | GIT_FAILED | INTERNAL",
"message": "人类可读原因" } }
backend AnalyzerClient 映射规则:
- LLM_NO_KEY / LLM_FAILED → 走降级路径(规则版),不视为失败
- SCAN_TIMEOUT → 错误码 3xxx + 保留已得部分结果
- SONAR_UNAVAILABLE → 质量维度标记 N/A,其余正常
- 其余 → ErrorCode 3001(分析器错误),message 透传
6.3 状态码约定¶
- HTTP:POST 创建 201 / 任务创建 202 / 正常 200 / 业务错误 400 / 鉴权 401(预留)/ 未找到 404 / 服务器错误 500
- 业务 code:以 docs/06-API契约.md §2.2 为唯一来源(开发规范 §2.4 仅列分段原则;改动只改 06,三端同步引用)
7. 分析流水线设计¶
7.1 状态机¶
PENDING → RUNNING → SUCCEEDED
↓ ↓
FAILED (终态,可重新发起新任务)
↓
CANCELLED(项目被删除时)
补充转移(regenerate):SUCCEEDED → RUNNING(stage=REPORT) → SUCCEEDED,复用原 analysis 行,非新任务;SUCCEEDED → RUNNING(stage=REPORT) 期间再次触发返回 2008。
7.2 阶段与超时(FULL 分析)¶
| 序号 | 阶段 | 步骤 | 超时 | 降级策略 |
|---|---|---|---|---|
| 1 | SCAN | 调 /analyze/scan → 写 file_node + 更新 project 档案 |
10 分钟 | 超时保留部分结果,报告标注"部分扫描" |
| 2 | REPORT | 组装摘要 → 调 LLM → 解析 JSON | 120s | 重试 2 次 → 规则版报告(source=RULES) |
| 3 | 收尾 | 写 report_json、更新 status、last_analyzed_at | — | — |
7.3 幂等与并发¶
- 同一项目仅一个 RUNNING 任务(发起时
SELECT … FOR UPDATE校验) - 重发请求返回 2002,不重复创建
- 任务失败后重新发起 = 新任务;历史任务只读保留
- 结果幂等重建:每阶段落库前先清理该 analysis_id 的旧数据(先删后插);后端超时重试不会堆积
- analyzer 端点无副作用状态:重试由后端控制,幂等由"按 analysis_id 重建"保证
7.4 进度上报¶
- analyzer 侧报阶段(
progress映射:SCAN 0-70 / REPORT 70-100) - 后端轮询接口幂等返回当前状态
8. AI 报告生成设计¶
8.1 LLM 客户端(仅 analyzer 侧)¶
铁律(05 审查 P0-1A):LLM 出口唯一在 analyzer,backend 永不直连 LLM(backend 仅经
AnalyzerClient透传,chat 场景见 §9.4)。
# analyzer/app/ai/llm_client.py(骨架见 03 附录 A.6)
class LLMClient:
def chat(self, system: str, user: str) -> str: ... # 同步
def chat_json(self, system: str, user: str) -> dict: ... # 强制 JSON 输出
- 实现:
OpenAiCompatibleClient(baseUrl/apiKey/model 来自 analyzer 配置,兼容 DeepSeek/OpenAI/Ollama) - 降级:无 Key / 调用失败 → 规则版报告(source=RULES,§8.3),不把 LLM 错误透出为业务失败
8.2 报告 Prompt(草稿,v0.1 够用)¶
【系统提示】
你是资深软件架构师与代码质量顾问。你将收到一个项目的分析摘要(JSON)。
请输出一份 JSON 健康报告,严格遵循结构:
{ healthScore, level, summary, dimensions[], risks[], recommendations[] }
规则:
1. healthScore 为 0-100 整数,先按规则得分(质量40% 结构30% 依赖15% 规模15%),可在 ±10 内修正,并在 summary 中说明理由;
2. risks 每条必须引用真实文件路径,禁止编造;
3. recommendations 分三个阶段,每条具体可执行(如"拆分 X 类为 A/B/C");
4. 只输出 JSON,不要额外文字。
【用户内容】
项目:Chatez
语言:Java 60% / JavaScript 40%
框架:Vue / Electron / Spring Boot
LOC:20431,文件 412
依赖风险:[Spring Boot 2.5 EOL - HIGH]
结构摘要:backend 为主;存在超长方法 23 处;User 相关文件 31 个,耦合度最高
历史:近 3 次健康分 85 → 83 → 82(下行)
8.3 规则版报告(降级,source=RULES)¶
无 Key / 调用失败时,按模板生成:
- 健康分 = 规则分(见 §10,无 AI 修正)
- 风险清单 = 规则命中(超长方法 Top10、大文件、依赖 EOL、未知语言占比)
- 建议 = 模板化("优先处理 HIGH 级依赖升级"等),明确标注"规则版",前端展示横幅
8.4 报告生成并发¶
- 报告生成为异步步骤,不影响页面其他数据;页面展示"AI 分析中…"占位,完成即更新
9. RAG 与 AI 医生设计(P6)¶
9.1 知识块切片¶
| 项 | 规则 |
|---|---|
| 单元 | 函数/类/模块(tree-sitter 提取) |
| 大小 | ≤800 token/块;超长函数按 400 token 窗口滑切(overlap 50) |
| 元数据 | file_path、language、symbol、chunk_index |
| 索引时机 | 分析成功时顺带索引(或手动触发) |
9.2 检索¶
- 向量:
embedding <=> :q LIMIT 8(cosine,HNSW 索引) - 关键词:文件路径/符号名 LIKE 匹配(低成本兜底)
- 合并:向量 top8 + 关键词 top4 去重 → topK=8
- 上下文注入顺序:系统摘要(项目档案+最近报告摘要)→ 用户问题 → 知识块列表(带路径标记)
9.3 引用与防幻觉¶
- Prompt 强约束:只有检索到的文件可引用,格式
[path:line] - 回答后校验:引用路径必须存在于知识块集合,否则剔除
- 兜底回复:"该问题超出当前分析范围,建议发起一次新分析"
9.4 流式协议(SSE)¶
POST /chats/{id}/messages → 200 text/event-stream
data: {"type":"delta","content":"…"}
data: {"type":"citations","items":[{"file":"…","line":12,"excerpt":"…"}]}
data: {"type":"done"}
10. 健康分算法(评分模型)¶
10.1 总公式(v0.1 可用;P3+ 随维度扩展)¶
健康分 = 规则基础分 + LLM 修正(±10, 需理由)
规则基础分 = 40×质量分 + 30×结构分 + 15×依赖分 + 15×规模分
| 子分 | 规则(v0.1 近似实现,P3 起替换为真实指标) |
|---|---|
| 质量分 | 超长方法/超大文件占比、未分类文件占比、平均文件 LOC 健康度;P3 起 = Sonar 指标映射(bug 数、异味密度、重复率、覆盖率) |
| 结构分 | 目录层级合理性、backend/frontend 清晰度、命名一致性(后缀混乱扣分) |
| 依赖分 | EOL 依赖:HIGH 扣 30%/个(封顶)、大版本落后 MEDIUM 扣 15%;无依赖清单 = 50 基线 |
| 规模分 | LOC<1000 降分(规模不足不利于评估)→ 起评 60;万行级为满分基线 |
10.2 等级映射¶
90-100 优秀(EXCELLENT) ★★★★★
75-89 良好(GOOD) ★★★★
60-74 一般(FAIR) ★★★
<60 差(POOR) ★★
10.3 输出要求¶
- 报告 JSON 含
healthScore+level;规则版含scoreDetail(各子分) - LLM 修正必须在其 summary 里给出理由(保证可解释、可评审)
11. 开发顺序(任务清单与里程碑)¶
工时为人日估算(单人)。每阶段独立 feature 分支 + PR + 冒烟 + devlog。
| 阶段 | 任务 | 工时 | 交付/演示点 | 里程碑 |
|---|---|---|---|---|
| P0 | ① 三端骨架(Spring Boot hello + FastAPI hello + Vue hello)② docker-compose(pg/pgvector/redis/sonar)③ scripts 启动脚本 ④ .env.example、.gitignore、README | 2-3d | start-dev 一键起全套 |
基础可运行 |
| P1 | ① project 表 + CRUD ② zip 上传/解压/路径校验(UploadService)③ GitHub clone ④ analyzer scanners(忽略/语言/技术栈/LOC)⑤ /analyze/scan 联调 ⑥ 列表/详情/上传页 | 6-8d | 上传 Chatez → 档案(语言占比/框架/LOC) | 档案闭环 |
| P2 | ① analysis 状态机 + @Async + 轮询 ② analyzer llm_client + 规则版降级 ③ /analyze/report + prompt ④ 报告页 ⑤ 12 条 AC 验收 | 5-7d | v0.1 完整闭环:上传→进度→体检报告 | v0.1 MVP |
| P3 | ① sonar-scanner 封装 + 指标归一 ② quality_issue 表 + 指标页 ③ AI 逐条解释(异步 + 重试) | 5-7d | 质量页:指标 + AI 解释抽屉 | v0.2 |
| P4 | ① tree-sitter 解析(Java 先行)② 节点/边/违规检测 ③ ECharts 关系图 ④ 违规侧栏 | 6-9d | 架构图可交互、违规标红 | v0.3 |
| P5 | ① git log 统计入库 ② 趋势/Top 文件/作者 ③ 热点规则 + AI 风险中心判断 | 4-6d | 演化页三图 + 风险中心 | v0.4 |
| P6 | ① 切片 + embedding + pgvector 入库 ② 会话/消息表 + SSE ③ 检索 + prompt + 引用卡片 ④ Monaco 预览 | 6-8d | AI 医生问答带引用 | v0.5 |
| P7 | ① tech_debt 闭环(含复查复发)② 文档生成三类 ③ Dashboard ④ 全面美化 + 深浅色 | 6-8d | v1.0 完整产品 | v1.0 |
合计 ≈ 40-55 人日(2-3 个月课余节奏),留 2 周缓冲写论文。
关键路径:P1 → P2 是主线(验收 AC-1~AC-12 全在前半程),P3-P7 按可并行顺序做即可(质量/架构/演化无强依赖,顺序自选)。
12. 部署方案¶
12.1 开发环境(docker-compose.yml 骨架)¶
services:
postgres:
image: pgvector/pgvector:pg16
environment: [POSTGRES_DB=evocode, POSTGRES_USER=evocode, POSTGRES_PASSWORD=${POSTGRES_PASSWORD}]
ports: ["127.0.0.1:5432:5432"]
volumes: [pgdata:/var/lib/postgresql/data]
redis:
image: redis:7-alpine
ports: ["127.0.0.1:6379:6379"]
sonarqube:
image: sonarqube:lts-community
ports: ["127.0.0.1:9000:9000"]
environment: [SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true]
volumes: { pgdata: {} }
12.2 生产/答辩部署(单机脚本)¶
scripts/deploy.sh:
1. docker compose up -d postgres redis sonarqube
2. 安装 Python 依赖,systemd 托管 analyzer(127.0.0.1:8091)
3. java -jar backend/target/*.jar(--spring.profiles.active=prod,环境变量注入密钥)
4. nginx 反代:/api → backend:8080,/ → frontend dist 静态文件
12.3 数据备份¶
- 每晚
pg_dump+ 打包data/目录;保留 7 份滚动 - 迁移清单:SQL 文件按序执行(
scripts/init-db.sh维护版本记录表schema_version)
13. 测试策略¶
| 层次 | 内容 | 工具 | 时机 |
|---|---|---|---|
| 单元 | backend service/工具;analyzer 忽略/识别/解析;frontend 工具 | JUnit5/Mockito、pytest、vitest | 每 PR |
| 契约 | analyzer schemas ↔ 后端 DTO 字段比对脚本(可选自动化,先人工) | 手动 + grep | 契约变更 |
| 集成 | 三端本地全链路:上传→分析→报告;轮询竞态 | 手工冒烟 + curl 脚本 | 每阶段 |
| 降级 | 无 Key 报告、Sonar 关停、扫描超时 | 手工 + 配置开关 | 每阶段 |
| 安全 | AC-8 恶意 zip、路径校验、删除残留 | 手工脚本 scripts/security-smoke.ps1 |
每阶段 |
| 性能 | 2 万行项目计时(AC-5);并发 3 任务 | 手工 + 日志时间戳 | P2 验收 |
回归清单(每阶段跑):AC-1/3/5/6/7/8/9(高频冒烟 7 项)。
14. 性能预算¶
| 环节 | 预算 | 备注 |
|---|---|---|
| zip 上传 + 解压(200MB) | ≤30s | 流式解压 |
| 档案快扫(语言/LOC) | ≤30s(2 万行) | 与全量扫描分离 |
| 全量扫描 SCAN | ≤10min | 含 file_node 落库 |
| LLM 报告 | ≤120s | 60s 超时 ×2 重试 |
| 列表/详情接口 | ≤1s | 分页 + 索引 |
| 报告页渲染 | ≤2s | 数据量 <5MB |
| 轮询 | 2s 间隔 | 幂等 |
| 架构图(>3000 节点) | 加载 ≤3s | 前端抽样/聚合降级 |
15. 风险与对策¶
| # | 风险 | 概率 | 影响 | 对策 |
|---|---|---|---|---|
| R1 | LLM 不稳定/超时 | 高 | 高 | 超时重试 + 规则版降级(AD-9);报告生成独立于扫描 |
| R2 | 无 Key / 断网演示 | 中 | 高 | 规则版报告兜底;AC-12 离线验收 |
| R3 | 大项目扫描慢 | 中 | 中 | 忽略规则 + 文件上限 5 万 + 2MB 单文件跳过 + 超时留部分结果 |
| R4 | 恶意 zip | 低 | 高 | 路径三重校验 + 体积/数量上限 + AC-8 回归 |
| R5 | tree-sitter 覆盖不全 | 中 | 中 | 语言优先级 + 未知语言文本统计,流程不中断 |
| R6 | MyBatis Plus 与 jsonb/数组兼容 | 中 | 中 | JacksonTypeHandler + autoResultMap;先建表后写实体;P1 前置验证 |
| R7 | 国内访问 GitHub/Sonar 慢 | 中 | 中 | 镜像配置 + zip 兜底 + Sonar 降级 N/A |
| R8 | 范围蔓延 | 高 | 高 | 严格按 1.2 节砍需求;新想法进 backlog 文档 |
| R9 | 单人进度拖期 | 中 | 高 | P1/P2 优先(v0.1 主线),P3+ 按剩余时间裁剪(质量/架构/演化可单挑展示) |
| R10 | 论文与代码脱节 | 中 | 中 | devlog 周记 + AD 留档 + screenshots,论文素材随开发同步沉淀 |
附录 A:端口与环境约定¶
| 服务 | 端口 | 绑定 | 用途 |
|---|---|---|---|
| frontend(Vite dev) | 5173 | 127.0.0.1 | 开发服务器,/api 代理到 8080 |
| backend | 8080 | 本地 | REST API(/api/v1) |
| analyzer | 8091 | 仅 127.0.0.1 | 内部分析服务(/analyze/v1) |
| PostgreSQL | 5432 | 127.0.0.1 | 主库 + pgvector |
| Redis | 6379 | 127.0.0.1 | 缓存/队列预留(P6+) |
| SonarQube | 9000 | 127.0.0.1 | 质量扫描(P3 起) |
| 健康检查 | backend /actuator/health · analyzer /health |
— | 启动脚本探活 |
.env.example 全文(密钥一律不入库):
POSTGRES_DB=evocode
POSTGRES_USER=evocode
POSTGRES_PASSWORD=change-me
DATA_ROOT=./data
LLM_BASE_URL=https://api.deepseek.com
LLM_API_KEY=
LLM_MODEL=deepseek-chat
LLM_TIMEOUT_SECONDS=60
LLM_MAX_RETRIES=2
EMBEDDING_MODEL=bge-m3
EMBEDDING_BASE_URL=http://127.0.0.1:11434 # Ollama;或硅基流动等 OpenAI 兼容端点
GIT_PROXY= # 可选:http://127.0.0.1:7890(GitHub 加速)
附录 B:默认规则表¶
B.1 语言识别后缀映射(完整版)¶
| 语言 | 后缀 | 备注 |
|---|---|---|
| Java | .java | 完整解析 |
| Python | .py .pyi | 完整解析 |
| TypeScript | .ts .tsx | 完整解析 |
| JavaScript | .js .jsx .mjs .cjs | 完整解析 |
| Go | .go | 完整解析 |
| Vue | .vue | 完整解析(模板+脚本) |
| Kotlin | .kt .kts | 基础 |
| C/C++ | .c .h .cpp .hpp .cc .cxx | 基础 |
| C# | .cs | 基础 |
| Rust | .rs | 基础 |
| Ruby | .rb | 基础 |
| PHP | .php | 基础 |
| Swift | .swift | 基础 |
| Groovy/Gradle | .groovy .gradle | 基础 |
| SQL | .sql | 基础 |
| Shell | .sh .bash .zsh | 基础 |
| HTML | .html .htm .vue(模板部分) | 基础 |
| CSS | .css .scss .less .styl | 基础 |
| XML | .xml .xsd .pom(当 pom.xml) | 基础 |
| YAML | .yml .yaml | 基础 |
| JSON | .json | 基础 |
| Markdown | .md | 基础 |
| Dockerfile | Dockerfile(文件名匹配) |
基础 |
| 其他/未知 | 其余 | OTHER(按文本行统计) |
实现建议:后缀 → 语言用一张
dict/配置表;Dockerfile、Makefile 等按文件名匹配;.vue的 script 部分可复用 JS 解析器。
B.2 默认忽略规则清单¶
| 类别 | 规则 | 说明 |
|---|---|---|
| 目录(直接剪枝,不遍历) | node_modules .git dist build target __pycache__ venv .venv .idea .vscode .next out coverage .gradle .mvn .cache logs temp tmp vendor |
os.walk 时剪枝,性能关键 |
| 文件(跳过统计) | *.lock package-lock.json yarn.lock pnpm-lock.yaml poetry.lock *.min.js *.min.css *.map |
lock 文件不算 LOC |
| 二进制/资源(忽略计数) | *.png *.jpg *.jpeg *.gif *.ico *.woff* *.ttf *.pdf *.zip *.class *.jar *.pyc *.so *.dll *.exe |
文本统计时直接跳过 |
| 隐藏文件 | 除 .github .gitignore .env.example 白名单外全部忽略 |
防源码泄密误计 |
| 自定义 | 项目根 .evocodeignore(gitignore 语法,支持 ! 取反) |
用户可覆盖 |
B.3 EOL / 版本风险规则表(内置,可配置化)¶
判定逻辑:解析
major.minor→ 命中区间 → 输出风险与建议文案;规则存配置文件(v1 可改 DB)。表中日期为 EOL 参考,实现时以官方公告为准,规则应做成可更新文件。
| 生态 | 组件 | 风险版本 | 风险 | 建议文案 |
|---|---|---|---|---|
| Java 生态 | Spring Boot | 2.x 全系 | HIGH | 2.x 已于 2023-11 结束 OSS 支持 → 升级 3.x(3.5+) |
| Java 生态 | Spring Boot | 3.0~3.2 | MEDIUM | 已过 OSS 支持期,仅安全维护 → 升级 3.5+ |
| Java 生态 | Spring Framework | 5.x | MEDIUM | 维护尾声 → 随 Boot 3 升级到 6.x |
| Java 生态 | log4j | 1.x | HIGH | 2015 年 EOL + 严重漏洞(CVE-2021-44228)→ 必须升级 |
| Java 生态 | Java | 8 / 11 | MEDIUM | Oracle 免费更新已停止 → 转 OpenJDK 发行版(Temurin)或升 17/21 |
| Node 生态 | Node.js | 14、16 | HIGH | 已 EOL(2023-04 / 2023-09)→ 升 20/22 |
| Node 生态 | Node.js | 18 | MEDIUM | 2025-04 EOL → 升 22 |
| Node 生态 | Node.js | 20 | LOW→MEDIUM | 2026-04 EOL → 升 22(当前) |
| Node 生态 | Electron | 落后最新大版本 ≥3 | HIGH | 仅最新 3 个大版本受支持 → 升级 |
| Node 生态 | Vue | 2.x | HIGH | 2023-12-31 EOL → 升 Vue 3 |
| Node 生态 | Angular | 落后最新大版本 ≥3 | HIGH | 每个大版本仅支持 12 个月 → 升最新 |
| Node 生态 | webpack | 4.x | MEDIUM | 维护结束 → 升 5.x |
| Python | Python | 3.8 / 3.9 | HIGH | 已 EOL(2024-10 / 2025-10)→ 升 3.11+ |
| Python | Python | 3.10 | MEDIUM | 2026-10 EOL → 升 3.11+ |
| Python | Flask | 1.x | MEDIUM | 维护结束 → 升 2.x/3.x |
| Python | Django | 3.2 及更早 | MEDIUM | 仅 LTS 受支持 → 升 LTS |
| Go | Go | 落后最新大版本 ≥2 | MEDIUM | 官方只维护最近 2 个大版本 → 升级 |
| 数据库 | PostgreSQL | 11、12、13 | MEDIUM | 已 EOL(2023-11 / 2024-11 / 2025-11)→ 升 16+ |
| 数据库 | MySQL | 5.7 | HIGH | 2023-10-21 EOL → 升 8.0 |
| 缓存 | Redis | 5.x | HIGH | 2022-04 EOL → 升 7.x |
| 其他 | 大版本落后 | 当前 major < 最新 major - 1 | LOW | 建议跟进大版本节奏 |
说明:以上为"内置示例规则",实际实现建议单独文件
rules/eol.json(生态→组件→版本区间→风险),可追加、可关闭,不影响主流程。
附录 C:配置文件示例¶
C.1 backend application.yml(骨架)¶
server:
port: 8080
spring:
application:
name: evocode-backend
datasource:
url: jdbc:postgresql://127.0.0.1:5432/evocode
username: ${POSTGRES_USER:evocode}
password: ${POSTGRES_PASSWORD:}
hikari:
maximum-pool-size: 10
jackson:
default-property-inclusion: non_null
time-zone: Asia/Shanghai
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
evocode:
data-root: ${DATA_ROOT:./data}
analyzer:
base-url: http://127.0.0.1:8091
timeout-seconds: 30
llm:
base-url: ${LLM_BASE_URL:https://api.deepseek.com}
api-key: ${LLM_API_KEY:}
model: ${LLM_MODEL:deepseek-chat}
timeout-seconds: ${LLM_TIMEOUT_SECONDS:60}
max-retries: ${LLM_MAX_RETRIES:2}
analysis:
max-upload-mb: 200
max-extract-mb: 500
max-file-count: 50000
max-file-size-bytes: 2097152
scan-timeout-seconds: 600
max-concurrent: 3
management:
endpoints:
web:
exposure:
include: health,info
C.2 analyzer config.py(pydantic-settings)¶
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_port: int = 8091
data_root: str = "./data"
llm_base_url: str = "https://api.deepseek.com"
llm_api_key: str = ""
llm_model: str = "deepseek-chat"
llm_timeout_seconds: int = 60
llm_max_retries: int = 2
embedding_model: str = "bge-m3"
embedding_base_url: str = "http://127.0.0.1:11434"
scan_max_files: int = 50000
scan_max_file_bytes: int = 2 * 1024 * 1024
scan_timeout_seconds: int = 600
sonar_base_url: str = "http://127.0.0.1:9000"
sonar_token: str = ""
# 允许从前端/后端传入的覆盖配置(仅白名单字段)
class Config:
env_prefix = ""
case_sensitive = False
C.3 docker-compose.yml(完整版)¶
services:
postgres:
image: pgvector/pgvector:pg16
container_name: evocode-postgres
environment:
POSTGRES_DB: ${POSTGRES_DB:-evocode}
POSTGRES_USER: ${POSTGRES_USER:-evocode}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-change-me}
ports:
- "127.0.0.1:5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-evocode}"]
interval: 5s
retries: 10
redis:
image: redis:7-alpine
container_name: evocode-redis
ports:
- "127.0.0.1:6379:6379"
sonarqube:
image: sonarqube:lts-community
container_name: evocode-sonar
environment:
SONAR_ES_BOOTSTRAP_CHECKS_DISABLE: "true"
ports:
- "127.0.0.1:9000:9000"
volumes:
pgdata: {}
C.4 scripts/start-dev.ps1 逻辑要点¶
1. docker compose up -d(等待 postgres healthy,pg_isready 轮询 ≤60s)
2. 执行 db/migration/V*.sql(幂等:schema_version 表记录已执行版本)
3. 起 analyzer:uvicorn app.main:app --port 8091(探活 /health)
4. 起 backend:mvn spring-boot:run(探活 /actuator/health)
5. 起 frontend:npm run dev(提示访问 http://localhost:5173)
任一步失败 → 打印排查指引(见附录 G)并停止
附录 D:Prompt 完整集¶
所有 prompt 放
analyzer/app/ai/prompts.py,统一管理;输出均要求"只输出 JSON / 纯文本"避免污染解析。
D.1 体检报告(主报告,v0.1 核心)¶
【SYSTEM】
你是资深软件架构师与代码质量顾问。你将收到一个项目的分析摘要(JSON)。
输出一份 JSON 健康报告,严格遵循以下结构,不得输出任何额外文字:
{
"healthScore": 0-100整数,
"level": "EXCELLENT|GOOD|FAIR|POOR",
"summary": "总体评价,2-4 句话,若调整了规则基础分必须说明理由",
"techStack": {"languages": {"Java": 61.2}, "frameworks": ["Spring Boot"]},
"dimensions": [ {"key":"quality|structure|dependency|scale","score":0,"stars":1-5,"summary":"..."} ],
"risks": [ {"level":"HIGH|MEDIUM|LOW","title":"...","detail":"...","suggestion":"...",
"references":[{"file":"相对路径","line":行号}] } ],
"recommendations": [ {"phase":"第一阶段","items":["具体可执行建议1","..."]} ]
}
规则:
1. risks 每条必须引用摘要中真实存在的文件路径,禁止编造;
2. recommendations 分三个阶段,每项具体可执行(如"将 UserService 拆为 UserQueryService / UserCommandService");
3. healthScore 以摘要中的规则基础分(scoreBase)为基准,可在 ±10 内修正;
4. dimensions 必须恰好 4 项(quality/structure/dependency/scale),stars 与 score 对应(0-20→1 星,每 20 分一星);
5. 全部用中文输出。
analysis_summary_json 由后端/analyzer 组装,v0.1 至少包含:项目名、语言占比、框架、LOC、文件数、被忽略数、依赖风险摘要、结构摘要(目录层级统计、最大文件 Top10)、历史健康分序列。
D.2 质量 issue 解释(P3,逐条)¶
【SYSTEM】
你是代码质量专家。下面是静态扫描工具(SonarQube)发现的 1 条 issue 及所在文件的代码上下文。
输出 JSON:{ "explanation": "为什么这是问题,结合上下文具体说", "suggestion": "怎么改,具体到类/方法/代码块", "codeExample": "关键改法示意(可省略)" }
要求:解释必须结合给出的上下文,不要背诵规则原文;如果上下文不足,明确说明。
【USER】
rule: {rule_key}
severity: {severity}
message: {message}
文件: {file_path}:{line}
上下文片段:
{file_snippet}
D.3 依赖升级建议(P3)¶
【SYSTEM】
你是依赖与生态专家。请针对以下依赖风险给出升级建议,输出 JSON:
{ "impact": "升级影响面分析", "steps": ["升级步骤1", ...], "risks": ["兼容性风险...", ...], "estimate": "工作量估算" }
【USER】
{依赖信息:name/current/latest/risk_reason}
D.4 架构违规解释(P4)¶
【SYSTEM】
你是软件架构专家。以下是一条架构违规检测结果(调用链为真实解析结果)。
输出 JSON:{ "why": "为什么违反分层/架构原则", "impact": "对可维护性的影响", "fix": "重构建议(具体到移动/提取到哪一层)" }
【USER】
violationType: {type} source: {source} target: {target} 相关调用链: {chain}
D.5 风险中心判断(P5,演化)¶
【SYSTEM】
你是软件演化分析师。根据以下统计,判断哪些模块正在成为"风险中心",输出 JSON:
{ "hotspots": [ {"module":"...","evidence":["变更45次","新增12000行","耦合度最高"],"riskLevel":"HIGH|MEDIUM"} ] }
要求:每条结论必须引用统计数字作为证据,不得凭空判断。
【USER】
{file_change_stats 摘要 + 依赖关系摘要}
D.6 AI 医生系统提示词(P6)¶
【SYSTEM】
你是 EvoCode 的软件医生助手,服务于项目 {project_name}({language} / {framework} / {loc} 行)。
背景资料(仅以下内容可信):
1. 项目摘要:{project_summary}
2. 最近分析报告摘要:{latest_report_summary}
3. 检索到的代码片段(每条带 [path:line] 标记):
{knowledge_chunks}
4. 会话历史:{history}
规则:
1. 只能引用"背景资料"中出现过的文件,引用格式必须为 [path:line],禁止编造;
2. 涉及代码分析时先给出结论再给证据(引用);
3. 无法从背景资料回答时,明确说"当前分析范围无法确认",可建议用户发起一次新分析;
4. 不执行代码、不改写代码文件、不输出机密信息;
5. 用中文,简洁、结构化(列表/小标题)。
历史截断策略(chat 端点内部实现):保留最近 6 轮原始消息,更早内容由上一轮 AI 回答滚动摘要;超 token 预算时优先丢弃最旧完整轮次。
D.7 文档生成(P7)¶
README: 【SYSTEM】你是技术文档专家。基于项目分析结果生成 README(markdown),包含:项目简介、技术栈、目录结构、快速开始、运行要求。只输出 markdown,不加围栏。输出 JSON { "title": "...", "content": "markdown字符串" }
架构文档: 【SYSTEM】基于架构分析结果生成架构说明文档:模块划分、分层说明、核心调用流程、部署方式(含 ASCII 图)。
API 文档: 【SYSTEM】基于控制器/路由解析结果生成 API 文档:每个端点的方法、路径、入参、出参、用途说明(表格形式)。
附录 E:样例输出(演示素材,可直接用于答辩)¶
E.1 项目档案(创建后)¶
{
"id": 1, "name": "Chatez", "sourceType": "GIT",
"langStats": { "Java": 61.2, "JavaScript": 30.4, "HTML": 4.1, "OTHER": 4.3 },
"frameworkTags": ["Spring Boot", "Vue", "Electron", "MySQL"],
"locTotal": 20431, "fileCount": 412, "ignoredCount": 88,
"lastAnalyzedAt": "2026-08-10T10:00:00Z"
}
E.2 AI 报告(LLM 版)¶
{
"healthScore": 82, "level": "GOOD",
"summary": "项目整体结构清晰,前后端分层明确,但 User 模块耦合度过高且依赖存在 EOL 风险,导致健康分较上次下降 3 分(85→82),建议优先处理。",
"techStack": {
"languages": { "Java": 61.2, "JavaScript": 30.4, "HTML": 4.1, "OTHER": 4.3 },
"frameworks": ["Spring Boot", "Vue", "Electron", "MySQL"]
},
"dimensions": [
{ "key": "quality", "score": 76, "stars": 4, "summary": "超长方法 23 处,主要集中在 UserService 与 MailService" },
{ "key": "structure", "score": 88, "stars": 4, "summary": "目录分层规范,backend/frontend 边界清晰" },
{ "key": "dependency", "score": 55, "stars": 3, "summary": "Spring Boot 2.5 已 EOL" },
{ "key": "scale", "score": 90, "stars": 5, "summary": "2 万行规模,评估充分" }
],
"risks": [
{ "level": "HIGH", "title": "Spring Boot 2.5 已停止官方支持",
"detail": "依赖文件 pom.xml 中 parent 版本为 2.5.14,官方 OSS 支持已于 2023-11 结束",
"suggestion": "升级至 Spring Boot 3.5,注意 javax→jakarta 包名迁移",
"references": [{ "file": "pom.xml", "line": 3 }] },
{ "level": "HIGH", "title": "User 模块耦合度过高",
"detail": "UserService 同时承担认证、数据库操作与邮件发送,方法数 31 个,入度出度均为全项目最高",
"suggestion": "拆分 UserAuthService / UserQueryService / UserMailService",
"references": [{ "file": "src/main/java/com/chatez/service/UserService.java", "line": 1 }] }
],
"recommendations": [
{ "phase": "第一阶段", "items": ["升级 Spring Boot 到 3.x", "拆分 UserService"] },
{ "phase": "第二阶段", "items": ["为 User 模块补充单元测试", "拆分 MailService"] },
{ "phase": "第三阶段", "items": ["引入分层校验,防止新违规"] }
]
}
E.3 规则版报告(降级,source=RULES)¶
source 不放在 report 内部:由 data 层返回
source=RULES(与 LLM 版同构,见 06 §3.7 / 07 §5.2)。
{
"healthScore": 74, "level": "FAIR",
"summary": "规则版报告(未连接 LLM)。基于静态规则评估:存在 EOL 依赖 1 项、超长方法 23 处、重复率未检测(Sonar 未启用)。",
"techStack": {
"languages": { "Java": 61.2, "JavaScript": 30.4 },
"frameworks": ["Spring Boot", "Vue"]
},
"dimensions": [
{ "key": "quality", "score": 70, "stars": 4, "summary": "规则检测:超长方法 23 处" },
{ "key": "structure", "score": 80, "stars": 4, "summary": "规则检测:分层目录正常" },
{ "key": "dependency", "score": 55, "stars": 3, "summary": "规则命中:Spring Boot 2.5 EOL" },
{ "key": "scale", "score": 90, "stars": 5, "summary": "2 万行规模,评估充分" }
],
"risks": [
{ "level": "HIGH", "title": "Spring Boot 2.5 EOL(规则命中)",
"detail": "pom.xml 使用 2.5.14,OSS 支持已于 2023-11 结束",
"suggestion": "升级至 3.x,配置 LLM Key 后可生成详细升级建议",
"references": [{ "file": "pom.xml", "line": 3 }] },
{ "level": "MEDIUM", "title": "检测到 23 个超长方法(>100 行)",
"detail": "规则统计(未做 AI 上下文解释)",
"suggestion": "优先拆分 UserService",
"references": [{ "file": "src/main/java/com/chatez/service/UserService.java", "line": 88 }] }
],
"recommendations": [
{ "phase": "第一阶段", "items": ["配置 LLM Key 后重新生成 AI 报告", "优先升级 EOL 依赖"] }
]
}
E.4 AI 医生对话样例¶
用户:为什么这个项目维护困难?
助手:根据最近一次分析(健康分 82,较上期 -3),主要原因有三个:
1. **依赖风险**:pom.xml:3 使用 Spring Boot 2.5,官方支持已结束,升级成本会随时间递增;
2. **耦合集中**:UserService.java:1 同时承担认证/DB/邮件,31 个方法、出入度全项目最高,改动牵一发动全身;
3. **测试缺口**:最近 90 天 commit 中该模块变更 45 次,但测试文件新增仅 2 个(见 evolution 统计)。
建议:先拆分 UserService(第一阶段),再补测试(第二阶段),最后升级依赖。需要我给出拆分方案吗?
附录 F:LLM 选型与成本估算¶
价格为示例估算(2026-08 参考价),以各厂商官网实时报价为准;金额极小,不作为开发重点。
| 用途 | 首选 | 备选 | 说明 |
|---|---|---|---|
| 报告/解释/医生 | DeepSeek deepseek-chat |
OpenAI gpt-4o-mini、Ollama 本地(qwen2.5-32b) |
OpenAI 兼容协议,一套代码全兼容 |
| Embedding | bge-m3(1024 维,Ollama/硅基流动) |
text-embedding-3-small(1536 维) |
本地跑免费,数据不出内网 |
| 成本估算 | 一次报告:输入 ~4k + 输出 ~2k token ≈ 0.02 元 | 每日 10 次分析 ≈ 0.2 元 | 可忽略 |
选型建议:
- 开发期用 DeepSeek(便宜、国内直连快);答辩演示可选 Ollama 本地模型(完全离线,配合规则版兜底万无一失)
- embedding 维数一旦定下写进 knowledge_chunk.embedding 表定义,中途换模型需重建向量列
- LLM 输出一律走 chat_json(强制 JSON),温度 0.3(报告)/ 0.7(医生对话)
附录 G:健康检查与排查手册¶
G.1 三端探活¶
curl http://127.0.0.1:8091/health # analyzer: {"status":"ok"}
curl http://127.0.0.1:8080/actuator/health # backend
curl http://localhost:5173 # frontend 页面
docker ps # postgres/redis/sonarqube 状态
G.2 常见故障排查¶
| 症状 | 排查步骤 | 常见原因 |
|---|---|---|
| 前端起不来 | npm install 后看报错 |
镜像源未配置 / Node 版本低 |
| 后端起不来 | 看日志前 30 行;mvn -q compile |
数据库没起 / 密码不对 / 端口占用 |
| analyzer 连不上 | 后端日志 3001 错误;curl /health | analyzer 未启动 / 端口被占 |
| 上传卡住 | 看后端日志(解压阶段) | zip 超大 / 磁盘不足(检查 DATA_ROOT 空间) |
| 轮询卡在 RUNNING | 看任务日志 grep "analysisId" |
LLM 超时重试中 / 扫描超时 |
| 报告一直不出现 | 检查 LLM_KEY 是否配置 | 降级规则版应出,若也没有 → 看 REPORT 阶段日志 |
| 中文乱码 | 确认三端文件 UTF-8;前端 <meta charset> |
Windows 下 PowerShell 重定向编码 |
| 端口占用 | netstat -ano | findstr :8080 |
旧进程未关(taskkill /PID) |
| Sonar 指标为空 | 看 sonarqube 日志;等首次扫描完成 | 容器刚启动需要 1-2 分钟预热 |
G.3 日志定位约定¶
- 任务相关日志统一前缀
[analysisId=12]:grep "analysisId=12" logs/*.log - 阶段标记:
[stage=SCAN]/[stage=REPORT] - 降级/重试必须打 WARN 并带原因(
[fallback=RULES] reason=LLM_TIMEOUT)