Ragflow是目前团队化部署大模型+RAG的优质方案,不过其仍不适合直接部署使用,本文将从实际使用的角度,对其进行二次开发。
1. Ragflow 存在问题
Ragflow 开源仓库地址:https://github.com/infiniflow/ragflowRagflow 当前版本: v0.17.0
Ragflow 目前主要存在以下问题:
- 登录页开放注册
当前版本,在login页面,用户可直接进行注册,在小规模私有化部署中,开放注册接口,易对用户产生困扰,甚至存在被频繁调用攻击的风险。 - 知识库共享问题
当前版本,团队成员进行知识库共享需要知识库的创建者邀请其它成员进团队,发出邀请时,需要其它成员点击接受才行,较为繁琐。 - 模型设置问题
当前版本,所有团队成员的模型设置是独立的,如需共用同一套模型配置,需要每个用户单独进行设置,不利于团队化协作。 - 可视化管理
当前版本,未存在超级管理员的后台界面,无法直观的对用户账户进行可视化管理。
本文将围绕以上四点问题,对Ragflow进行二次开发解决。
2. 开源协议
Ragflow采用的是较为宽松的Apache License 2.0
,这意味着其允许进行二次开发和商用,且修改后可无需开源。
3. 技术栈分析
3.1 容器组件分析
通过docker启动该服务时,docker-compose-base.yml
包含了部分基础配置参数,可以看到整个服务共包含5个容器组件:
services:
es01:
container_name: ragflow-es-01
infinity:
container_name: ragflow-infinity
mysql:
container_name: ragflow-mysql
minio:
container_name: ragflow-minio
redis:
container_name: ragflow-redis
各组件功能如下:
- Elasticsearch
主要用作文档引擎,负责存储和检索文本及向量数据,作为系统的知识库存储后端,用以支持向量存储和相似度搜索 - ragflow-infinity
前端系统,包含基本的界面显示、数据交互、路由跳转等功能 - MySQL
关系型数据库,存储系统的结构化数据,包括管理用户账户、权限等基础信息、存储知识库的元数据信息等 - MinIO
对象存储服务,用于存储原始文档及文档切片图像信息 - Redis
内存数据库, 采用Valkey版本,缓存大模型的响应结果,处理异步任务,临时保存对话上下文
3.2 前后端框架分析
该系统前端框架使用React+Typescript,代码统一在web
文件夹。 后端框架使用Flask+Python,代码分好几部分,具体内容如下:
- agent:对应前端agent相关模块功能
- agentic_reasoning:对应前端搜索相关模块功能
- api:核心后端程序,用来与前端进行数据对接,并提供后端服务和其它各组件连接及数据交互功能
- deepdoc:提供文件ocr等解析相关功能
- graphrag:知识图谱相关功能
- rag:主要用以和大模型相关接口进行交互
- sdk:拓展型功能,用来提供系统的外部调用,目前不太完善,可忽略。
3.3 前后端可视化分析
3.3.1 前端可视化分析
前端代码全部集成在web文件夹下,因此可直接在web路径下直接启动查看。
先安装依赖:
yarn instsall
依赖安装完成,生成node_modules
再启动:
yarn start
访问http://localhost:9222
即可进入登录界面。
考虑到登录需要和后端交互,密码验证通过后,才能进入主界面。如需直接进入主界面预览,可修改web/src/utils/request.ts
: 这里的逻辑是:本来响应结果为504,弹出error,这里直接改成成功响应。
request.interceptors.response.use(async (response: Response, options) => {
if (response?.status === 413 || response?.status === 504) {
// 原代码
// message.error(RetcodeMessage[response?.status as ResultCode]);
// 修改
return new Response(JSON.stringify({
code: 0,
message: 'Success',
data: {}
}));
}
3.3.2 mysql可视化分析
下面再可视化查看其它组件信息,在docker/.env
文件中,包含了各组件的密码。
启动docker服务,首先查看mysql数据库信息。
使用DBeaver
进行连接,连接参数如下,默认用户名为root
,密码为infini_rag_flow
。
可以看到,该数据库中包含多张数据表,后续在解决实际问题时,会进一步分析。
3.3.3 Elasticsearch可视化分析
Elasticsearch需要通过Kibana
进行可视化分析。虽然.env
文件中写了Kibana的初始用户名和密码,但实际服务中,并未启用Kibana。看到仓库中有人提出过该问题,具体可参考该pr:https://github.com/infiniflow/ragflow/pull/548/files
3.3.4 MinIO可视化分析
MinIO自带了可视化管理的控制台,访问http://localhost:9001/
即可进入,默认用户名为rag_flow
,默认密码为infini_rag_flow
。
进入可看见,里面的容器包含了原始上传的pdf文件和切块分页的图像数据。
3.3.5 Redis可视化分析
使用Rdis insight
连接Redis数据库,默认地址为127.0.0.1:6379
,默认用户名为default
,默认密码为infini_rag_flow
。
进入可看见,里面存储了一些缓存数据。
4. 问题解决方法
分析完了,开始解决开头提到的一些具体问题。
4.1 关闭注册通道
关闭注册通道,可直接将前端界面上的注册元素注释掉。
具体方法是修改src\pages\login\index.tsx
文件,注释掉以下内容:
{ <div>
{title === 'login' && (
<div>
{t('signInTip')}
<Button type="link" onClick={changeTitle}>
{t('signUp')}
</Button>
</div>
)}
{title === 'register' && (
<div>
{t('signUpTip')}
<Button type="link" onClick={changeTitle}>
{t('login')}
</Button>
</div>
)}
</div>}
这样修改,用户直接通过浏览器访问/register
也是无法注册的,因为注册功能并不是一个单独界面,而是在login中,post到后端进行处理。
4.2 后台添加用户
阻止用户自己注册之后,管理员还需要为用户进行注册。可直接采用修改数据库内容的方式进行实现。
连接mysql数据库,用户信息存储在user
表中。该表包含以下字段,核心字段是email
和password
。
email比较容易理解,存储的就是登陆明文邮箱,但密码是哈希字符串,为了防止被人攻击泄露,不能存储明文密码,因此,要解决注册问题,首先需要理清楚密码的加密逻辑。
通过仔细阅读源代码,我理清楚了注册阶段,密码的加密存储过程: 首先,前端获取到用户原始输入密码后,先进行Base64编码,防止特殊字符造成解析失败,编码后利用公钥进行RSA加密; 后端接收到加密密码后,利用私钥进行RSA解密,最后通过Hash处理,存储到数据库。
❝
A[原始密码] --> B[前端Base64编码] --> C[RSA加密] --> D[后端RSA解密] --> E[hash存储]
为了让这个过程更容易理解,我写了个python代码,模拟了该过程,其中,私钥数据可以在文件中找到,路径为 conf/private.pem
:
import base64
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_v1_5
from werkzeug.security import generate_password_hash, check_password_hash
def rsa_decrypt(encrypted_password: str, private_key_path: str, passphrase: str) -> str:
# 从文件中读取私钥
with open(private_key_path, "r") as key_file:
private_key = key_file.read()
# 导入私钥
rsa_key = RSA.importKey(private_key, passphrase=passphrase)
cipher = PKCS1_v1_5.new(rsa_key)
# Base64 解码加密数据
encrypted_data = base64.b64decode(encrypted_password)
# 解密数据
decrypted_data = cipher.decrypt(encrypted_data, "Fail to decrypt password!")
# Base64 解码解密后的数据
return base64.b64decode(decrypted_data).decode('utf-8')
def rsa_psw(password: str) -> str:
# 公钥
pub_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB
-----END PUBLIC KEY-----"""
# 导入公钥
rsa_key = RSA.import_key(pub_key)
cipher = PKCS1_v1_5.new(rsa_key)
# 对密码进行 Base64 编码
password_base64 = base64.b64encode(password.encode('utf-8'))
# 使用 RSA 公钥加密
encrypted_data = cipher.encrypt(password_base64)
# 对加密结果进行 Base64 编码并返回
return base64.b64encode(encrypted_data).decode('utf-8')
if __name__ == "__main__":
# 注册流程
# 1. 获取到用户输入的密码
password = "test"
# 2. Base64编码
base64_password = base64.b64encode(password.encode()).decode()
print("Base64:", base64_password)
# 3. RSA加密
encrypted_password = rsa_psw(base64_password)
print("RSA:", encrypted_password)
# 4. 后端解密(使用private.pem)
private_key_path = "private.pem"# 私钥文件路径
passphrase = "Welcome"# 私钥的密码
decrypted_password = rsa_decrypt(encrypted_password, private_key_path, passphrase)
print("Decrypted:", decrypted_password)
# 5. 生成最终的hash
hash_password = generate_password_hash(decrypted_password)
print("hash_password:", hash_password)
# 登录验证匹配过程
is_match = check_password_hash(hash_password, decrypted_password)
print(is_match)
理解完这一过程之后,就可以通过python脚本插入一条用户数据:
import mysql.connector
import uuid
import base64
from datetime import datetime
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_v1_5
from werkzeug.security import generate_password_hash
# 数据库连接配置
db_config = {
"host": "localhost", # 数据库地址
"port": 5455, # 数据库端口
"user": "root", # 数据库用户名
"password": "infini_rag_flow", # 数据库密码
"database": "rag_flow", # 数据库名称
}
# 生成随机的 UUID 作为 id
def generate_uuid():
return str(uuid.uuid4()).replace("-", "")
def rsa_decrypt(encrypted_password: str, private_key_path: str, passphrase: str) -> str:
# 从文件中读取私钥
with open(private_key_path, "r") as key_file:
private_key = key_file.read()
# 导入私钥
rsa_key = RSA.importKey(private_key, passphrase=passphrase)
cipher = PKCS1_v1_5.new(rsa_key)
# Base64 解码加密数据
encrypted_data = base64.b64decode(encrypted_password)
# 解密数据
decrypted_data = cipher.decrypt(encrypted_data, "Fail to decrypt password!")
# Base64 解码解密后的数据
return base64.b64decode(decrypted_data).decode('utf-8')
def encrypt_password(raw_password: str) -> str:
# 1. Base64编码
base64_password = base64.b64encode(raw_password.encode()).decode()
# 2. RSA加密
encrypted_password = rsa_psw(base64_password)
# 3. 后端解密(使用private.pem)
private_key_path = "private.pem"# 私钥文件路径
passphrase = "Welcome"# 私钥的密码
decrypted_password = rsa_decrypt(encrypted_password, private_key_path, passphrase)
# 4. 生成最终的hash
return generate_password_hash(decrypted_password)
# RSA 加密密码
def rsa_psw(password: str) -> str:
pub_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB
-----END PUBLIC KEY-----"""
rsa_key = RSA.import_key(pub_key)
cipher = PKCS1_v1_5.new(rsa_key)
encrypted_data = cipher.encrypt(base64.b64encode(password.encode()))
return base64.b64encode(encrypted_data).decode()
if __name__ == '__main__':
# 插入数据的 SQL 语句
insert_query = """
INSERT INTO user (
id, create_time, create_date, update_time, update_date, access_token,
nickname, password, email, avatar, language, color_schema, timezone,
last_login_time, is_authenticated, is_active, is_anonymous, login_channel,
status, is_superuser
) VALUES (
%s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s
)
"""
# 对密码进行加密
raw_password = "123"# 原始密码
hash_encrypted = encrypt_password(raw_password)
# 学号
student_id = "123456789"
student_email = student_id + "@xidian.cn"
# 数据值
data = (
generate_uuid(), # id
1741361741738, # create_time
datetime.strptime("2025-03-07 23:35:41", "%Y-%m-%d %H:%M:%S"), # create_date
1741416354403, # update_time
datetime.strptime("2025-03-08 14:45:54", "%Y-%m-%d %H:%M:%S"), # update_date
None, # access_token (默认为空)
student_id, # nickname
hash_encrypted, # password
student_email, # email
None, # avatar (默认为空)
"Chinese", # language
"Bright", # color_schema
"UTC+8 Asia/Shanghai", # timezone
datetime.strptime("2025-03-07 23:35:41", "%Y-%m-%d %H:%M:%S"), # last_login_time
1, # is_authenticated
1, # is_active
0, # is_anonymous
"password", # login_channel
1, # status
0, # is_superuser
)
# 连接数据库并插入数据
try:
# 建立数据库连接
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor()
# 执行插入操作
cursor.execute(insert_query, data)
conn.commit() # 提交事务
print("数据插入成功!")
except mysql.connector.Error as err:
print(f"数据库错误: {err}")
finally:
# 关闭连接
if conn.is_connected():
cursor.close()
conn.close()
print("数据库连接已关闭。")
注:这段代码实际上,可以再进行优化,获取到密码后,未涉及前后端传输,可以直接将base64编码的密码进行哈希操作,而省略RSA步骤。
4.3 设置用户模型
添加完用户,下面要设置用户模型配置。 模型配置涉及到tenant
和tenant_llm
两张数据表。
tenant记录了每个用户的模型配置,对应前端界面如下:
tenant_llm记录了每个模型的基本信息,对应前端界面如下:
具体插入操作,后续将和用户团队表格一块进行更新。
4.4 设置用户团队
用户团队涉及的表格名为user_tenant
。 设置用户团队需要插入两条记录,首先,用户自己注册时,单独有一个自己的团队,这样方便用户可以后续再组小团体。其次,需要将新用户的团队记录插入到老用户底下。
结合4.2、4.3、4.4的内容,一次性批量插入用户的脚本如下:
import mysql.connector
import uuid
import base64
import json
from datetime import datetime
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_v1_5
from werkzeug.security import generate_password_hash
# 数据库连接配置
db_config = {
"host": "localhost",
"port": 5455,
"user": "root",
"password": "infini_rag_flow",
"database": "rag_flow",
}
# 生成随机的 UUID 作为 id
def generate_uuid():
return str(uuid.uuid4()).replace("-", "")
# RSA 加密密码
def rsa_psw(password: str) -> str:
pub_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB
-----END PUBLIC KEY-----"""
rsa_key = RSA.import_key(pub_key)
cipher = PKCS1_v1_5.new(rsa_key)
encrypted_data = cipher.encrypt(base64.b64encode(password.encode()))
return base64.b64encode(encrypted_data).decode()
# 加密密码
def encrypt_password(raw_password: str) -> str:
base64_password = base64.b64encode(raw_password.encode()).decode()
encrypted_password = rsa_psw(base64_password)
return generate_password_hash(base64_password)
# 处理批量注册
def batch_register_students():
# 从 JSON 文件加载学生数据
with open("add.json", "r", encoding="utf-8") as json_file:
student_groups = json.load(json_file)
try:
# 建立数据库连接
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor()
for group in student_groups:
tenant_id = group["tenant_id"]
student_ids = group["student_id"]
for student_id in student_ids:
# 生成 ID 和时间戳
user_id = generate_uuid()
create_time = 1741361741738
create_date = datetime.strptime("2025-03-07 23:35:41", "%Y-%m-%d %H:%M:%S")
update_time = 1741416354403
update_date = datetime.strptime("2025-03-08 14:45:54", "%Y-%m-%d %H:%M:%S")
# 用户信息
student_email = student_id + "@xidian.cn"
raw_password = student_id
hash_encrypted = encrypt_password(raw_password)
# 插入 user 表数据
user_insert_query = """
INSERT INTO user (
id, create_time, create_date, update_time, update_date, access_token,
nickname, password, email, avatar, language, color_schema, timezone,
last_login_time, is_authenticated, is_active, is_anonymous, login_channel,
status, is_superuser
) VALUES (
%s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s
)
"""
user_data = (
user_id, create_time, create_date, update_time, update_date, None,
student_id, hash_encrypted, student_email, None, "Chinese", "Bright", "UTC+8 Asia/Shanghai",
create_date, 1, 1, 0, "password",
1, 0
)
cursor.execute(user_insert_query, user_data)
# 插入 tenant 表数据
tenant_insert_query = """
INSERT INTO tenant (
id, create_time, create_date, update_time, update_date, name,
public_key, llm_id, embd_id, asr_id, img2txt_id, rerank_id, tts_id,
parser_ids, credit, status
) VALUES (
%s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s,
%s, %s, %s
)
"""
tenant_data = (
user_id, create_time, create_date, update_time, update_date, student_id + "'s Kingdom",
None, "deepseek-r1:1.5b@Ollama", "BAAI/bge-large-zh-v1.5@BAAI", "", "", "", None,
"naive:General,qa:Q&A,resume:Resume,manual:Manual,table:Table,paper:Paper,book:Book,laws:Laws,presentation:Presentation,picture:Picture,one:One,audio:Audio,email:Email,tag:Tag",
512, 1
)
cursor.execute(tenant_insert_query, tenant_data)
# 插入 user_tenant 表的第一条记录
user_tenant_insert_query = """
INSERT INTO user_tenant (
id, create_time, create_date, update_time, update_date, user_id,
tenant_id, role, invited_by, status
) VALUES (
%s, %s, %s, %s, %s, %s,
%s, %s, %s, %s
)
"""
user_tenant_data_owner = (
generate_uuid(), create_time, create_date, update_time, update_date, user_id,
user_id, "owner", user_id, 1
)
cursor.execute(user_tenant_insert_query, user_tenant_data_owner)
# 插入 user_tenant 表的第二条记录
user_tenant_data_normal = (
generate_uuid(), create_time, create_date, update_time, update_date, user_id,
tenant_id, "normal", tenant_id, 1
)
cursor.execute(user_tenant_insert_query, user_tenant_data_normal)
# 插入 tenant_llm 表数据
tenant_llm_insert_query = """
INSERT INTO tenant_llm (
create_time, create_date, update_time, update_date, tenant_id,
llm_factory, model_type, llm_name, api_key, api_base, max_tokens, used_tokens
) VALUES (
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s
)
"""
tenant_llm_data = (
create_time, create_date, update_time, update_date, user_id,
"Ollama", "chat", "deepseek-r1:1.5b", "xxxxxxxxxxxxxxx", "http://10.195.140.47:11434", 88888888, 0
)
cursor.execute(tenant_llm_insert_query, tenant_llm_data)
# 提交事务
conn.commit()
print("批量用户数据添加成功!")
except mysql.connector.Error as err:
print(f"数据库错误: {err}")
finally:
if conn.is_connected():
cursor.close()
conn.close()
print("数据库连接已关闭。")
if __name__ == '__main__':
batch_register_students()
这里我将需要插入的用户写在json文件中,格式示例如下:
[
{
"student_id": [
"123456789",
"1234567810"
],
"tenant_id": "d3de1596fb6911efa0f40242ac120006"
}
]
student_id
为需要插入的用户学号,密码默认和id一致,tenant_id
对应的是团队队长的id
,即需要共享数据库用户的id。代码中,模型名用的是deepseek-r1:1.5b
型号的测试模型,如需配置其它型号,对应修改名称即可。
4.5 其它外观修改
主要问题已全部解决。最后,再进行一些外观上的调整。
1.移除Agent菜单
Agent目前比较复杂,也不实用。可以考虑移除。 具体修改方式是修改web\src\layouts\components\header\index.tsx
,注释以下内容:
{ path: '/flow', name: t('flow'), icon: GraphIcon },
2.移除登陆页描述
登录页描述有点花哨,进行移除。 修改web\src\pages\login\right-panel.tsx
,注释以下内容:
<Text className={classNames(styles.pink, styles.loginDescription)}>
{t('description')}
</Text>
<Flex align="center" gap={16}>
<Avatars></Avatars>
<Flex vertical>
<Space>
<Rate disabled defaultValue={5} />
<span
className={classNames(styles.white, styles.loginRateNumber)}
>
5.0
</span>
</Space>
<span className={classNames(styles.pink, styles.loginRateReviews)}>
{t('review')}
</span>
</Flex>
</Flex> }
3.修改用户默认头像
用户默认头像文件位置在web\src\layouts\components\user\index.tsx
,可将https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png
修改为其它图像超链接,例如:https://picx.zhimg.com/v2-aaf12b68b54b8812e6b449e7368d30cf_l.jpg
。
5. 前端修改文件上传
对于前端修改的文件,在docker里修改也不会生效。
观察docker/nginx/ragflow.conf
文件,不难发现,网页默认的访问的root路径为 /ragflow/web/dist
。
因此,需要让前端修改内容生效,需要先将web文件进行打包。
进入到web路径下,执行
npm run build
执行完之后,会在同级目录下生成dist
文件。
进入到容器,删除容器中已有的/ragflow/web/dist
文件
docker exec -it ragflow-server /bin/sh
rm -rf /ragflow/web/dist
将本地新生成的打包文件上传到容器:
docker cp dist ragflow-server:/ragflow/web/
重启容器,即可看到修改效果。
总结
作为高度团队协作的产品,ragflow用到的技术挺多。在此之前,我没接触过react和typescript,起初看起来有些费力。不过技术是相通的,慢慢看就能逐渐理解。这个系统代码规范性做的很好,即使拿来学习,也是一份不错的材料。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。