跳转至

EvoCode 开发指导(详细版)

MVP 边界 · 技术架构 · 数据库 · API 契约 · 分析流水线 · AI 设计 · 开发顺序 · 部署与测试 版本:v1.3 · 2026-08-10

使用说明(灵活性声明):本文档是纲要与基线,不是死规定。版本号、表结构、接口字段、阶段划分均为建议基线;遇到文档未覆盖或与实际冲突的情况,具体情况具体分析——先记录决策(AD),再合理偏离,最后回填本文档。例如:P3 与 P4 顺序可互换、DDL 字段可按实现增删、Sonar 可换成自研规则引擎(如时间不允许)。唯一硬约束:三端验证命令通过 + 验收用例(AC)通过。


目录

  1. MVP(v0.1)定义
  2. 环境准备与工具链
  3. 关键技术决策(AD)
  4. 仓库结构与模块说明
  5. 数据库设计(完整 DDL)
  6. API 设计(详细契约)
  7. 分析流水线设计
  8. AI 报告生成设计
  9. RAG 与 AI 医生设计(P6)
  10. 健康分算法(评分模型)
  11. 开发顺序(任务清单与里程碑)
  12. 部署方案
  13. 测试策略
  14. 性能预算
  15. 风险与对策

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