Ciff:从零打造一个简化版 Dify — AI Agent 开发与运行平台
灿若繁星先生 Lv6

Ciff(Code It For Future)是我独立开发的一个 AI Agent 开发与运行平台,定位为简化版 Dify,面向 20-50 人小团队。支持 Agent 智能助手、知识库 RAG、工作流编排、多模型供应商适配,并提供 Web 界面和 REST API 两种交互方式。

项目从 2026 年 4 月 13 日启动,到 4 月 25 日完成全部 6 个阶段,历时约两周。后端 Java、前端 Vue 3,借助 AI 编码工具(Claude、Kimi、GLM)完成开发。本文将全面介绍项目的设计思路、技术架构和开发历程。

一、项目背景

2026 年 4 月,离职后准备找工作的间隙,我决定做一个 AI Agent 开发平台来练手。初衷很直接:

  1. 熟悉 AI Agent 开发流程:2026 年 Agent 应用开发已成为后端 JD 高频关键词,纸上谈兵不如动手做
  2. 补全技术栈:多年 Java 后端经验,但前端生疏,借此机会用 AI 辅助补齐前端短板
  3. 实践 VibeCoding:验证「思路清晰 + AI 执行」的协作模式能否真正提效

对比了 Dify 和 FastGPT 后,选择以 Dify 为蓝本做简化版。核心约束是:

  • 不做可视化拖拽,工作流用 JSON 配置
  • 只支持 API 类型工具(MCP 协议留扩展口但不实现)
  • 知识库只支持 TXT 文档 + 固定长度分块
  • 单机 Docker Compose 部署,不搞分布式

二、功能一览

功能 说明
Agent 智能助手 工具调用 + 多轮对话,核心形态
Chatbot 对话应用 基础对话,最高频入口
Workflow 工作流 JSON 配置,线性步骤 + 条件分支
模型供应商适配 统一接口,OpenAI / Claude / 本地模型
知识库 RAG TXT 文档,固定长度分块,PGVector 向量检索
自定义工具 / API Agent 可调用外部 HTTP 接口
Web + API 发布 对话界面 + REST API 双入口
认证鉴权 Sa-Token + JWT + GitHub OAuth

三、技术栈

后端:JDK 17 / Spring Boot 3.3.6 / MyBatis-Plus 3.5.9 / MySQL 8.x / PostgreSQL (PGVector) / Redis 7.x (Redisson) / Resilience4j / Sa-Token + JWT / Flyway / WebClient + Reactor Netty

前端:Vue 3.5 / TypeScript / Element Plus / Vite / Markdown-it / Mermaid (流程图可视化)

基础设施:Nginx (反向代理 + SSE 透传) / Docker Compose

四、项目结构

项目采用 Maven 多模块架构,8 个子模块按业务领域拆分,依赖关系清晰:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ciff/
├── ciff-common/ # 公共模块(工具类、异常、DTO、配置)
├── ciff-provider/ # 模型供应商管理 + LLM 客户端适配
├── ciff-mcp/ # MCP 工具管理与调用
├── ciff-knowledge/ # 知识库 + RAG(文档分块、向量化、检索)
├── ciff-agent/ # Agent 管理与编排
├── ciff-workflow/ # 工作流编排与执行引擎
├── ciff-chat/ # 对话引擎(顶层编排,SSE 流式)
├── ciff-app/ # Spring Boot 启动模块(Controller / Service / Mapper)
├── ciff-web/ # Vue 3 前端应用
├── deploy/ # Docker 部署配置
├── docs/ # 架构文档与设计规范
├── rules/ # 编码规范(9 篇)
├── plan/ # 各阶段执行计划
└── 开发日记/ # 每日开发记录

模块依赖关系ciff-chat 是顶层编排模块,依赖其他所有业务模块;各业务模块通过 Facade 层对外暴露接口,避免循环依赖。

五、产品展示

供应商与模型管理

平台支持 OpenAI、Claude、Gemini、Ollama 等多家模型供应商,统一适配层屏蔽不同 API 的差异。

image image

工具管理

支持 API 类型工具,通过 JSON Schema 定义入参出参,Agent 对话时可自动调用。

image

Agent 管理

Agent 是平台的核心实体,绑定模型、工具和知识库,通过 System Prompt 定义角色行为。

image

知识库与 RAG

支持上传 TXT 文档,自动分块 → 向量化 → 存入 PGVector。对话时可开启 RAG 模式,先检索知识库再生成回答。

image image image

对话引擎

对话页面支持 SSE 流式输出(打字机效果)+ Markdown 渲染 + 工具调用展示。可选择不同 Agent 进行对话。

image image

工作流引擎

工作流通过 JSON 定义,支持 llmtoolconditionknowledge_retrieval 四种步骤类型,线性执行 + 条件分支。前端提供 JSON 编辑器和 Mermaid 流程图两种可视化方式。

image image image

六、架构设计

整体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
┌─────────────────────────────────────────────────────────────────┐
│ Docker Compose - Single Node │
│ │
│ ┌──────────────┐ ┌──────────────────────────────────────┐ │
│ │ Nginx │ │ ciff-server │ │
│ │ :80/443 │ │ Spring Boot 3.3 :8080 │ │
│ │ │ │ JVM -Xmx512m │ │
│ │ /api/** ────┼────►│ │ │
│ │ /sse/** ────┼────►│ ┌──────────────────────────────┐ │ │
│ │ /** ────┼──┐ │ │ Business Modules │ │ │
│ └──────────────┘ │ │ │ │ │ │
│ ▲ │ │ │ Chat Engine (SSE+Streaming) │ │ │
│ │ │ │ │ Agent (Config+Prompt) │ │ │
│ ┌────┴───────┐ │ │ │ Workflow (JSON Engine) │ │ │
│ │ ciff-web │ │ │ │ Knowledge(RAG Pipeline) │ │ │
│ │ Vue3+TS │◄──┘ │ │ MCP (Tool Execution) │ │ │
│ │ ElementPlus│ │ │ Provider (LLM Adapter) │ │ │
│ └────────────┘ │ └──────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────┐ ┌───────────────┐ │ │
│ │ │ Resilience │ │ Thread │ │ │
│ │ │ Breaker │ │ Pools │ │ │
│ │ │ Bulkhead │ │ Tomcat: 200 │ │ │
│ │ │ Retry │ │ LLM: 10/30 │ │ │
│ │ │ Timeout(4) │ │ Biz: 5/15 │ │ │
│ │ └────────────┘ └───────────────┘ │ │
│ └──────────────┬───────────────────────┘ │
│ │ │
│ ┌─────────────┬───────────┼──────────┐ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────┐ ┌─────────┐ ┌────────┐ ┌──────────┐ │
│ │ MySQL 8 │ │Redis 7 │ │PGVector│ │ LLM APIs │ │
│ │ :3306 │ │ :6379 │ │ :5432 │ │ OpenAI/ │ │
│ │ 14 tables│ │Cache │ │1536dim │ │ Claude/ │ │
│ │ │ │Session │ │HNSW │ │ Ollama │ │
│ └──────────┘ └─────────┘ └────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

关键设计决策

1. 四级超时策略

LLM 调用的延迟不可预测,设计了四级超时保护:

级别 超时时间 说明
TCP 连接 30s 建立 TCP 连接的超时
首个 Token 15s 等待模型返回第一个 token
Token 间隔 180s 两个 token 之间的最大间隔
整体请求 配置化 单次请求的最大时长

2. Resilience4j 熔断保护

每个 Provider 维护独立的熔断器(Circuit Breaker)和舱壁(Bulkhead),某个模型供应商不可用时自动熔断,不影响其他供应商的调用。

3. 线程池隔离

  • Tomcat 线程池(200):处理 HTTP 请求
  • LLM 线程池(核心 10 / 最大 30):隔离 LLM 调用,避免慢请求占满 Tomcat
  • Biz 线程池(核心 5 / 最大 15):处理异步业务(缓存刷新、文档处理等)

4. SSE 流式输出

对话接口使用 Server-Sent Events 实现流式输出,前端逐 token 渲染实现打字机效果。Nginx 配置 proxy_buffering off 确保 SSE 消息实时透传。

5. 双数据源

MySQL 存储业务数据(14 张表),PGVector 专门存储向量 embedding(1 张表)。通过 Spring Boot 多数据源配置实现读写隔离。

七、数据库设计

ER 关系图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
┌─────────┐     ┌──────────┐     ┌──────────┐
│ t_user │ │t_provider│ │ t_model │
│─────────│ │──────────│ │──────────│
│ id PK │ │ id PK │◄────│provider_id│
│username │ │ name │ │ name │
│password │ │ type │ │max_tokens│
│ role │ │api_base │ │default_ │
│github_id│ │api_key │ │ params │
└────┬────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ ┌────┴─────┐ │
│ │t_provider│ │
│ │ _health │ │
│ │──────────│ │
│ │status │ │
│ │failures │ │
│ │latency │ │
│ └──────────┘ │
│ │
▼ ▼
┌──────────┐ ┌───────────┐ ┌──────────┐
│ t_agent │◄────│ t_agent_ │ │ t_tool │
│──────────│ │ knowledge │ │──────────│
│ id PK │ │───────────│ │ id PK │
│ user_id │ │agent_id FK│ │ name │
│ model_id │ │knowledge_ │ │ type │
│workflow_id│ │ id FK │ │ endpoint │
│ type │ └───────────┘ │param_ │
│system_ │ │ schema │
│ prompt │ ┌───────────┐ └────┬─────┘
│model_ │ │ t_agent_ │ │
│ params │ │ tool │ │
│fallback_ │ │───────────│ │
│ model_id │ │agent_id FK│◄──────┤
└──┬───┬───┘ │ tool_id FK│ │
│ │ └───────────┘ │
│ │ │
│ ▼ │
│ ┌────────────┐ │
│ │t_workflow │ │
│ │────────────│ │
│ │ id PK │ │
│ │ user_id FK │ │
│ │definition │ │ (JSON)
│ └────────────┘ │
│ │
▼ │
┌──────────────┐ │
│t_conversation│ │
│──────────────│ │
│ id PK │ │
│ user_id FK │ │
│ agent_id FK │ │
│ title │ │
└──────┬───────┘ │
│ │
▼ │
┌──────────────┐ │
│t_chat_message│ │
│──────────────│ │
│ id PK │ │
│conversation_ │ │
│ id FK │ │
│ role │ │
│ content │ │
│ token_usage │ │
│ tool_call_id │ │
└──────────────┘ │

┌────────────────────────────────────┤
│ Knowledge Module │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ t_knowledge │ │t_knowledge_ │ │
│ │─────────────│ │ document │ │
│ │ id PK │ │─────────────│ │
│ │ user_id FK │ │ id PK │ │
│ │ chunk_size │ │knowledge_id │ │
│ │ embedding_ │ │ file_name │ │
│ │ model │ │ chunk_count │ │
│ └──────┬──────┘ │ status │ │
│ │ └──────┬──────┘ │
│ │ │ │
│ │ ┌──────┴──────┐ │
│ │ │t_knowledge_ │ │
│ │ │ chunk │ │
│ │ │ (PGVector) │ │
│ │ │─────────────│ │
│ │ │ id PK │ │
│ │ │ content │ │
│ │ │ embedding │ │
│ │ │ VECTOR(1536)│ │
│ │ │ chunk_index │ │
│ │ └─────────────┘ │
│ │ │
└─────────┼─────────────────────────┘

┌─────────┼─────────────────────────┐
│ ▼ │
│ ┌───────────┐ │
│ │ t_api_key │ │
│ │───────────│ │
│ │ id PK │ │
│ │ user_id FK│ │
│ │ agent_id │ │
│ │ key_hash │ (SHA256) │
│ │key_prefix │ │
│ │permissions│ │
│ │expires_at │ │
│ └───────────┘ │
└───────────────────────────────────┘

核心表设计说明

MySQL(14 张表)

表名 用途 要点
t_user 用户账号 支持 GitHub OAuth 自动注册
t_provider 模型供应商 API Key AES 加密存储
t_model 模型配置 绑定供应商,含默认参数
t_provider_health 供应商健康 连续失败次数、延迟、探活时间
t_tool 工具定义 JSON Schema 描述入参出参
t_agent Agent 配置 类型(chatbot/agent/workflow)、System Prompt
t_agent_tool Agent-Tool 绑定 中间表,多对多
t_agent_knowledge Agent-知识库绑定 中间表,多对多
t_workflow 工作流定义 JSON 存储步骤和条件
t_knowledge 知识库 分块大小、Embedding 模型配置
t_knowledge_document 文档元数据 状态机(uploading → processing → ready/failed)
t_conversation 对话会话 绑定 Agent 和用户
t_chat_message 消息记录 Append-only,含 token 用量和延迟
t_api_key API 密钥 SHA256 哈希存储,支持过期和撤销

PGVector(1 张表)

表名 用途 要点
t_knowledge_chunk 向量分块 embedding VECTOR(1536) + HNSW 索引(余弦距离)

数据隔离策略

  • 全局资源:Provider、Model、Tool — 所有用户可见
  • 用户资源:Agent、Knowledge、Workflow、Conversation、ApiKey — 用户隔离

八、开发历程

项目分为 6 个阶段(Phase 0 ~ Phase 6),通过 plan/ 目录管理每个阶段的执行计划,开发日记/ 目录记录每日进展。

Phase 0-1:基础框架 + 供应商管理(4.13 ~ 4.17)

Day 1 搭建 Maven 多模块工程骨架,完成统一响应 Result<T>、异常处理、MyBatis-Plus 配置等基础组件。Day 2 搭建前端设计系统,实现 CiffTable、CiffFormDialog 等公共组件。

Phase 1 完成供应商 CRUD、模型管理、LLM HTTP 客户端(WebClient + Reactor Netty 同步/SSE 流式)、Claude 适配器、Resilience4j 熔断保护和四级超时策略。

期间还做了 AI 编码工具对比:用 Kimi、GLM-5.1 分别做任务规划,感受两个模型在项目规划能力上的差异。

Phase 2:Agent + MCP 工具(4.17 ~ 4.18)

完成工具 CRUD(API 类型)、Agent CRUD、Agent-Tool 绑定关系、Agent 聚合控制器。关键设计是通过 Facade 层实现跨模块调用,避免循环依赖。

Phase 3:知识库 + RAG(4.17 ~ 4.19)

这是技术难度最高的阶段。核心工作:

  1. 双数据源配置:MySQL + PostgreSQL/PGVector,通过 Spring Boot 多数据源管理
  2. 文档处理流水线:上传 → 固定长度分块 → Embedding 生成 → PGVector 存储
  3. 向量检索:HNSW 索引 + 余弦相似度 + Top-K 检索
  4. Agent-知识库绑定:对话时开启 RAG 模式,先检索再生成

分块策略选了最简单的固定长度,误差控制在 10% 以内,V1 阶段够用。

Phase 4:对话引擎(4.19 ~ 4.21)

对话引擎是整个平台的顶层编排模块,串联 Agent、Provider、Knowledge、MCP:

  • 非流式对话接口(测试用)
  • SSE 流式对话(打字机效果)
  • 工具调用(单轮)
  • RAG 增强(知识检索 + 上下文注入)
  • 前端 Chat 页面(会话列表、SSE 集成、Markdown 渲染)

Phase 5:工作流引擎(4.22 ~ 4.24)

工作流通过 JSON 定义,支持 4 种步骤类型:

1
2
3
4
5
6
7
8
{
"steps": [
{ "id": "step1", "type": "llm", "modelId": 1, "prompt": "分析 ${inputs.question}" },
{ "id": "step2", "type": "tool", "toolId": 1, "params": { "query": "${step1.output.text}" } },
{ "id": "step3", "type": "condition", "condition": "${step2.output.score} > 0.8", "trueNext": "step4", "falseNext": "step5" },
{ "id": "step4", "type": "knowledge_retrieval", "knowledgeId": 1, "query": "${inputs.question}" }
]
}

支持变量插值 ${stepId.output.xxx}、DFS 循环检测、异步执行 + Redis 状态追踪、超时自动标记失败。前端提供 JSON 编辑器和 Mermaid 流程图双视图。

这个阶段补了 38 个单元测试,全部通过。

Phase 6:认证 + API 发布 + 部署(4.25)

最后一个阶段:

  • 认证:Sa-Token + JWT 登录鉴权 + GitHub OAuth 自动注册 + Token Redis 持久化
  • API Key 管理:生成 / 列表 / 撤销,SHA256 哈希存储
  • 外部对话接口/api/v1/external/chat,API Key 认证
  • Docker 部署:docker-compose + Nginx 反向代理 + SSE 透传
  • 全局 UI 打磨:空状态、错误提示、响应式侧边栏

踩过的坑

Token 消耗:智谱 Coding Plan 周额度 7 天消耗 229.46M,不够用。又买了 Kimi Moderato 版(99/月),两天就耗了一半。最终结论:AI 编码是 token 密集型工作,预算要留够。

前端术语沟通:AI 对前端术语的理解有偏差。比如「label 右对齐」,AI 理解成文字右对齐而非 label 元素整体靠右。最终通过 justify-content: flex-end 解决。

序列化陷阱:外部 API 返回下划线格式(tool_calls),但生成的代码用驼峰序列化(toolCalls),导致请求失败。Jackson 的 @JsonNaming 不会级联到内部类,每个嵌套类都需要单独加注解。

Apple Silicon 兼容:Docker 镜像在 M 系列芯片上 Alpine 基础镜像平台不兼容,改用标准 Debian 镜像。

九、部署方案

Docker Compose 一键部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# docker-compose.yml
services:
ciff-app:
build:
context: ..
dockerfile: deploy/Dockerfile
ports:
- "8080:8080"
environment:
- MYSQL_HOST=mysql
- REDIS_HOST=redis
- PGVECTOR_HOST=pgvector
- SA_TOKEN_JWT_SECRET_KEY=${SECRET}
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:8080/actuator/health"]
interval: 30s

nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ../ciff-web/dist:/usr/share/nginx/html:ro
depends_on:
- ciff-app

Nginx 关键配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# SSE 流式透传 — 核心配置
location /sse/ {
proxy_pass http://ciff-app:8080;
proxy_buffering off; # 禁用缓冲,SSE 消息实时推送
proxy_cache off;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
}

# API 反向代理
location /api/ {
proxy_pass http://ciff-app:8080;
}

# 静态资源(1 个月缓存)
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
expires 30d;
}

部署步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 构建前端
cd ciff-web && npm run build

# 2. 构建后端
cd ciff-app && mvn clean package -DskipTests

# 3. 创建外部网络
docker network create ciff-network

# 4. 启动 MySQL / Redis / PGVector(外部管理或独立 compose)

# 5. 启动应用
cd deploy && docker-compose up -d

# 6. 验证健康
curl http://localhost/api/v1/health

十、总结

Ciff V1 从 0 到 1 跑通了 AI Agent 平台的核心链路:模型适配 → Agent 编排 → 知识库 RAG → 工作流引擎 → 对话引擎 → API 发布

项目亮点

  • 两周交付:6 个阶段,借助 AI 编码工具显著提效
  • 工程规范:9 篇编码规范 + Flyway 数据库版本管理 + 完善的单元测试
  • 生产就绪:熔断保护、四级超时、线程池隔离、SSE 流式输出
  • 全栈实践:后端 Java + 前端 Vue 3,AI 辅助补齐前端短板

不足与展望

  • 权限模块缺失,上线前需做大量功能限制
  • 知识库只支持 TXT,需扩展 PDF / Word / Markdown
  • 工作流是 JSON 配置而非可视化拖拽,使用门槛较高
  • 未实现 MCP 协议,只支持 API 类型工具

这个项目最大的收获不是代码本身,而是验证了「清晰的架构设计 + AI 执行」这套 VibeCoding 工作流的可行性。当你能把需求拆解到足够细的粒度,AI 编码工具的输出质量会远超预期。