前端转 AI 第 27 天:Docker 容器化 - 从"我电脑能跑"到"到处能跑"
昨日回顾
Day 26 给 API 加了可观测性,日志、指标、追踪全有了。
但还有个尴尬的问题——部署到服务器时,环境对不上:
- 本地 Python 3.11,服务器是 3.9
- 本地有 SQLite,服务器没有
- 本地能跑的依赖,服务器装不上
"我电脑上能跑"是工程师最不想听到的话。
今天用 Docker 解决这个问题。
Day 27 目标:写出生产级 Dockerfile,用 docker-compose 一键起服务。
一、Docker 解决什么问题
一句话:Docker 把"应用 + 环境"打包成一个"集装箱",到哪都能跑。
1. 没有 Docker 的世界
部署一个 Python 服务,要经历:
- ssh 到服务器
- 装 Python 3.11
- 装 pip / venv
- 拉代码
- 装依赖
- 配环境变量
- 配 systemd 开机自启
- 装 Nginx 反代
- ...
每一步都可能翻车,尤其换服务器、多机部署时。
2. Docker 的世界
docker run -d -p 8000:8000 ai-assistant:latest
一行命令。 环境、依赖、配置全在镜像里。
3. 前端视角:Docker 是"前端构建"的升级版
前端熟悉的东西:
npm install+npm run build→ 产出dist/dist/扔到 CDN / Nginx,谁都能访问
Docker 做的:
docker build→ 产出镜像- 镜像
docker run,谁都能跑
| 前端 | Docker |
|---|---|
package.json | Dockerfile |
npm install | docker build |
dist/ | image |
npm start | docker run |
npx serve dist | docker compose up |
| Node 版本管理 | 基础镜像(python:3.11-slim) |
思路完全一样:把源码"编译"成可部署产物。
区别: 前端产物是静态文件(HTML / JS / CSS),Docker 产物是完整运行环境(含 OS、依赖、Python)。更重,但更通用。
二、Docker 核心概念
三个关键词:镜像、容器、仓库。
| 概念 | 类比 | 说明 |
|---|---|---|
| 镜像(Image) | 类的定义 | 只读模板,包含应用和环境 |
| 容器(Container) | 类的实例 | 镜像运行起来的进程 |
| 仓库(Registry) | npm / PyPI | 存镜像的地方(Docker Hub / 私有) |
关系:
Dockerfile → build → Image → run → Container
↑
│ push / pull
↓
Registry
前端类比:
- Dockerfile ≈
package.json - Image ≈
dist/ - Container ≈
node dist/index.js - Registry ≈ npm registry
三、写第一个 Dockerfile
1. 最简单的 Dockerfile
在 ai-assistant-api-v6/ 下创建 Dockerfile:
# 基础镜像
FROM python:3.11-slim
# 工作目录
WORKDIR /app
# 复制依赖文件
COPY pyproject.toml ./
# 安装依赖
RUN pip install --no-cache-dir -e .
# 复制源码
COPY src/ ./src/
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["uvicorn", "ai_assistant.api.app:app", "--host", "0.0.0.0", "--port", "8000"]
逐行拆解:
| 指令 | 作用 |
|---|---|
FROM | 基础镜像 |
WORKDIR | 设置工作目录 |
COPY | 复制文件进镜像 |
RUN | 构建时执行命令 |
EXPOSE | 声明端口(文档作用) |
CMD | 容器启动时的默认命令 |
2. 构建镜像
docker build -t ai-assistant:v0.3.0 .
参数:
-t ai-assistant:v0.3.0:给镜像打标签.:构建上下文(当前目录)
输出:
[+] Building 45.2s (10/10) FINISHED
=> [1/5] FROM python:3.11-slim
=> [2/5] WORKDIR /app
=> [3/5] COPY pyproject.toml ./
=> [4/5] RUN pip install --no-cache-dir -e .
=> [5/5] COPY src/ ./src/
=> exporting to image
=> => naming to docker.io/library/ai-assistant:v0.3.0
3. 运行容器
docker run -d \
-p 8000:8000 \
--name ai-api \
-e API_KEY=test-secret \
-e ENV=prod \
ai-assistant:v0.3.0
参数:
-d:后台运行-p 8000:8000:端口映射(宿主 8000 → 容器 8000)--name ai-api:容器名字-e KEY=VALUE:环境变量
访问:
curl http://localhost:8000/health/live
# {"status": "alive"}
4. 常用命令
# 查看运行中的容器
docker ps
# 查看所有容器
docker ps -a
# 查看日志
docker logs -f ai-api
# 进容器(调试)
docker exec -it ai-api bash
# 停止
docker stop ai-api
# 删除
docker rm ai-api
# 删除镜像
docker rmi ai-assistant:v0.3.0
# 查看所有镜像
docker images
四、优化:多阶段构建
上面那个 Dockerfile 有几个问题:
- 镜像太大:把源码、缓存、编译工具都带上了
- 每次改代码都要重装依赖:
COPY src/变化导致缓存失效 - 构建慢:pip 每次都从网络拉
解决:多阶段构建。
1. 什么是多阶段构建
思路:用两个阶段——构建阶段装依赖,运行阶段只拿产物。
# ============ Stage 1: 构建 ============
FROM python:3.11-slim AS builder
WORKDIR /app
# 装编译工具
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
# 装依赖到虚拟环境
COPY pyproject.toml ./
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir -e .
# ============ Stage 2: 运行 ============
FROM python:3.11-slim
WORKDIR /app
# 只复制虚拟环境(不带编译工具)
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# 复制源码
COPY src/ ./src/
EXPOSE 8000
CMD ["uvicorn", "ai_assistant.api.app:app", "--host", "0.0.0.0", "--port", "8000"]
关键点:
- Stage 1 装了
gcc编译工具 - Stage 2 只复制虚拟环境——
gcc不进来 - 最终镜像小了一半以上
前端类比: 相当于 webpack 的 splitChunks + tree shaking——只把"运行时需要的东西"打进最终产物。
2. 利用缓存:先复制依赖文件
Docker 构建是分层的——每条指令一层,只要输入没变就复用缓存。
问题:
COPY . . # ← 改一个文件,后面全部重建
RUN pip install
优化:
COPY pyproject.toml ./ # ← 依赖文件单独复制
RUN pip install ... # ← 只有依赖变了才重建
COPY src/ ./src/ # ← 源码变化时只重建这一层
改代码时,只有最后 COPY src/ 那一层重建,依赖层用缓存。 构建从 40 秒降到 3 秒。
这是 Docker 构建优化的核心技巧。
3. 用 .dockerignore
问题: docker build . 会把当前目录所有文件打包给 Docker daemon。
包括 data/ / logs/ / __pycache__/ / .venv/ / .git/——几百 MB 白白传过去。
创建 .dockerignore:
# 版本控制
.git
.gitignore
# Python
__pycache__/
*.py[cod]
*.egg-info/
.venv/
venv/
.pytest_cache/
.coverage
htmlcov/
# 项目数据
data/
logs/
*.db
# 开发文件
.vscode/
.idea/
.env
.env.local
# 构建产物
dist/
build/
# 文档
README.md
docs/
# 测试
tests/
# 其他
.DS_Store
Makefile
效果: 构建上下文从 200MB 降到 5MB,构建速度提升明显。
4. 用非 root 用户运行
默认容器用 root 运行——安全隐患。
生产环境应该用非 root:
FROM python:3.11-slim
# 创建非 root 用户
RUN groupadd -r app && useradd -r -g app app
WORKDIR /app
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY --chown=app:app src/ ./src/
USER app
EXPOSE 8000
CMD ["uvicorn", "ai_assistant.api.app:app", "--host", "0.0.0.0", "--port", "8000"]
关键:
USER app切到非 root 用户COPY --chown=app:app复制时改属主
好处: 即使容器被攻破,攻击者也只是普通用户,不能对宿主机造成大破坏。
5. 完整生产级 Dockerfile
# ============ Stage 1: 构建 ============
FROM python:3.11-slim AS builder
WORKDIR /app
# 编译工具(仅构建时需要)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
# 装依赖
COPY pyproject.toml README.md ./
RUN python -m venv /opt/venv && \
/opt/venv/bin/pip install --no-cache-dir --upgrade pip && \
/opt/venv/bin/pip install --no-cache-dir -e .
# ============ Stage 2: 运行 ============
FROM python:3.11-slim
# 时区(可选)
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 非 root 用户
RUN groupadd -r app && useradd -r -g app app
WORKDIR /app
# 复制虚拟环境
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH" \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
# 复制源码
COPY --chown=app:app src/ ./src/
USER app
EXPOSE 8000
# 健康检查(Docker 自身)
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health/live')" || exit 1
CMD ["uvicorn", "ai_assistant.api.app:app", \
"--host", "0.0.0.0", \
"--port", "8000", \
"--workers", "2", \
"--log-config", "/dev/null"]
新增要点:
1. 环境变量
PYTHONUNBUFFERED=1:日志实时输出(不然 Python 会缓冲,看不到日志)PYTHONDONTWRITEBYTECODE=1:不生成.pyc文件
2. HEALTHCHECK
Docker 自身也支持健康检查——容器不健康会显示 unhealthy。
3. --workers 2
Uvicorn 多进程——利用多核 CPU。一般设 CPU 核数 × 2 + 1。
五、Docker Compose:一键起多个服务
实际项目往往需要多个容器:
- API 服务
- PostgreSQL 数据库
- Redis 缓存
- Nginx 反向代理
一个个 docker run 太麻烦。
docker-compose 用一个 YAML 文件定义所有服务,一键起停。
1. 编写 docker-compose.yml
version: "3.9"
services:
# ============ API 服务 ============
api:
build:
context: .
dockerfile: Dockerfile
image: ai-assistant:latest
container_name: ai-api
ports:
- "8000:8000"
environment:
- ENV=prod
- DATABASE_URL=postgresql://postgres:secret@db:5432/assistant
- REDIS_URL=redis://redis:6379/0
- API_KEY=${API_KEY:-dev-key}
- LOG_LEVEL=INFO
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
volumes:
# 开发时挂载源码,改代码自动生效
- ./src:/app/src:ro
- ./logs:/app/logs
restart: unless-stopped
# ============ PostgreSQL ============
db:
image: postgres:16-alpine
container_name: ai-db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
POSTGRES_DB: assistant
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
# ============ Redis ============
redis:
image: redis:7-alpine
container_name: ai-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
# ============ Nginx(可选,反代)============
nginx:
image: nginx:alpine
container_name: ai-nginx
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- api
restart: unless-stopped
volumes:
postgres_data:
redis_data:
2. 启动
docker compose up -d
输出:
[+] Running 5/5
✔ Network ai-assistant_default Created
✔ Container ai-db Healthy
✔ Container ai-redis Healthy
✔ Container ai-api Started
✔ Container ai-nginx Started
一次把 API + DB + Redis + Nginx 全起了。
3. 常用命令
# 启动
docker compose up -d
# 停止
docker compose down
# 停止 + 删除数据卷
docker compose down -v
# 看日志
docker compose logs -f api
# 重启某个服务
docker compose restart api
# 查看状态
docker compose ps
# 进容器
docker compose exec api bash
# 重建(改了 Dockerfile 时)
docker compose up -d --build
4. 环境变量管理
docker-compose.yml 里用 ${VAR} 引用环境变量,从 .env 文件读:
创建 .env:
API_KEY=prod-secret-key-change-me
POSTGRES_PASSWORD=strong-password
ENV=prod
docker-compose.yml 里:
environment:
- API_KEY=${API_KEY}
- ENV=${ENV}
.env 一定要加到 .gitignore,用 .env.example 提示别人。
5. 服务间网络
Compose 自动创建 network,服务名即主机名:
# API 里访问数据库
DATABASE_URL = "postgresql://postgres:secret@db:5432/assistant"
# ↑ 服务名
db 是服务名——Compose 自动 DNS 解析到对应容器的 IP。
这就是为什么不用写 IP。 前端类比:类似 Kubernetes 的 Service,或者前端的 import alias。
六、实战:把 Day 26 项目 Docker 化
1. 项目准备
cp -r day26/ai-assistant-api-v6 day27/ai-assistant-api-docker
cd day27/ai-assistant-api-docker
2. 创建 Dockerfile
复制上面的完整版 Dockerfile。
3. 创建 .dockerignore
复制上面的内容。
4. 创建 docker-compose.yml(开发版)
简化版——SQLite 存本地,不引入 PostgreSQL:
services:
api:
build:
context: .
dockerfile: Dockerfile
image: ai-assistant:dev
container_name: ai-api
ports:
- "8000:8000"
environment:
- ENV=dev
- API_KEY=${API_KEY:-dev-key}
- LOG_LEVEL=INFO
- AUTO_CREATE_TABLES=true
- DATABASE_URL=sqlite:///data/assistant.db
volumes:
# 开发时挂载源码(热更新)
- ./src:/app/src
- ./data:/app/data
- ./logs:/app/logs
command: uvicorn ai_assistant.api.app:app --host 0.0.0.0 --port 8000 --reload
restart: unless-stopped
关键点:
- 挂载
./src:改代码容器里立刻生效(配合--reload) - 挂载
./data:数据库持久化到宿主机 --reload:开发模式
注意: 生产环境不要挂载源码、不要
--reload。
5. 创建 .env
API_KEY=dev-key-change-me
6. 启动
docker compose up -d --build
第一次构建大约 1 分钟。
看日志:
docker compose logs -f api
输出:
ai-api | INFO: Uvicorn running on http://0.0.0.0:8000
ai-api | INFO: Started reloader process
ai-api | {"event": "API 启动", "level": "info", ...}
7. 测试
curl http://localhost:8000/health/live
# {"status": "alive"}
curl -H "X-API-Key: dev-key-change-me" \
http://localhost:8000/api/sessions
# {"sessions": [], "total": 0}
完美运行!
8. 热更新测试
修改 routes.py 里的某个字符串,保存。
看日志:
ai-api | INFO: Detected file changes
ai-api | INFO: Reloading...
ai-api | INFO: Application startup complete.
自动重启。 前端同学会觉得很熟悉——这就是 Docker 里的"HMR"。
9. 生产版 Compose
创建 docker-compose.prod.yml:
services:
api:
build:
context: .
dockerfile: Dockerfile
image: ai-assistant:prod
container_name: ai-api
ports:
- "8000:8000"
environment:
- ENV=prod
- API_KEY=${API_KEY}
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/assistant
- LOG_LEVEL=INFO
- AUTO_CREATE_TABLES=false
depends_on:
db:
condition: service_healthy
restart: always
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 128M
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: assistant
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
restart: always
volumes:
postgres_data:
关键差异:
- 不挂载源码:镜像里是固化的代码
--reload去掉:生产模式- PostgreSQL 替代 SQLite
restart: always:崩溃自动重启- 资源限制:
cpus/memory
启动:
docker compose -f docker-compose.prod.yml up -d
日常用 docker-compose.yml,上线用 docker-compose.prod.yml。
10. 多环境配置管理
最终项目结构:
ai-assistant/
├── Dockerfile
├── .dockerignore
├── docker-compose.yml # 开发
├── docker-compose.prod.yml # 生产
├── .env # 本地配置(不进 git)
├── .env.example # 配置模板(进 git)
├── pyproject.toml
├── src/
├── tests/
└── scripts/
从开发到生产:
# 开发
docker compose up -d
# 生产
docker compose -f docker-compose.prod.yml up -d
七、镜像优化:从 1.2GB 到 150MB
1. 看看当前镜像有多大
docker images ai-assistant
输出:
REPOSITORY TAG SIZE
ai-assistant latest 1.2GB
1.2GB 太大了——部署慢、占用磁盘。目标 150MB 以下。
2. 优化策略
| 优化 | 效果 |
|---|---|
python:3.11-slim 而不是 python:3.11 | -600MB |
| 多阶段构建 | -200MB |
pip install --no-cache-dir | -50MB |
apt-get clean | -30MB |
alpine 基础镜像 | -80MB(可选) |
3. slim vs alpine
基础镜像选择:
| 镜像 | 大小 | 优点 | 缺点 |
|---|---|---|---|
python:3.11 | ~1GB | 完整 | 太大 |
python:3.11-slim | ~150MB | 够用 | 无编译工具 |
python:3.11-alpine | ~50MB | 最小 | 编译问题多 |
推荐 slim——平衡最好。Alpine 的 musl libc 会让某些包编译失败(比如 PyTorch),踩坑成本高。
4. 完整的优化版 Dockerfile
# ============ Stage 1: 构建 ============
FROM python:3.11-slim AS builder
WORKDIR /app
# 编译工具(仅构建时用,之后会丢弃)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
python3-dev \
&& rm -rf /var/lib/apt/lists/*
# 装依赖到虚拟环境
COPY pyproject.toml README.md ./
RUN python -m venv /opt/venv && \
/opt/venv/bin/pip install --no-cache-dir --upgrade pip && \
/opt/venv/bin/pip install --no-cache-dir -e .
# ============ Stage 2: 运行 ============
FROM python:3.11-slim
# 非 root
RUN groupadd -r app && useradd -r -g app app
WORKDIR /app
# 复制虚拟环境(不含编译工具)
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH" \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1
# 复制源码
COPY --chown=app:app src/ ./src/
USER app
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health/live')"
CMD ["uvicorn", "ai_assistant.api.app:app", \
"--host", "0.0.0.0", \
"--port", "8000", \
"--workers", "2"]
结果:
docker images ai-assistant
# REPOSITORY TAG SIZE
# ai-assistant latest 148MB
从 1.2GB 降到 148MB,8 倍!
八、前端视角:Docker vs 前端构建
| 概念 | 前端 | Docker |
|---|---|---|
| 配置 | package.json | Dockerfile |
| 依赖锁 | package-lock.json | pyproject.toml 版本固定 |
| 构建 | npm run build | docker build |
| 产物 | dist/ | image |
| 运行 | npx serve dist | docker run |
| 依赖忽略 | .npmignore | .dockerignore |
| 多环境 | .env.production | docker-compose.prod.yml |
| 本地起服务 | npm run dev | docker compose up |
前端部署 vs Docker 部署:
前端传统流程:
npm install
npm run build
scp -r dist/ server:/var/www/
Docker 流程:
docker build -t myapp .
docker push registry/myapp
# 服务器上
docker pull registry/myapp
docker run -d myapp
核心思想一样:构建产物 + 部署产物。
Docker 更强大的地方: 前端 dist/ 只包含静态文件,Docker 镜像包含整个运行环境——能跑任何东西,不只是网页。
九、今日踩坑
COPY . .没配.dockerignore→ 传输几百 MB,构建超慢COPY . .在pip install之前 → 改一个文件就重装依赖- 忘了
PYTHONUNBUFFERED=1→ 日志卡住看不到 - 用 root 用户运行 → 安全隐患
pip install -e .没加--no-cache-dir→ 镜像大- 多阶段构建忘了
COPY --from=builder→ 运行阶段找不到包 docker-compose服务名和 URL 不一致 →db和postgres搞混,连接不上- 忘写
depends_on→ API 先启动但数据库还没好,报连接错误 depends_on只保证启动顺序,不保证健康 → 用condition: service_healthy- 生产挂载源码 → 改代码就重启,甚至覆盖镜像
--reload上了生产 → 性能差 + 安全隐患- 端口映射写反 →
8000:8000对,8000:8080意思是"宿主 8000 → 容器 8080" - 镜像没打 tag → 全是
latest,回滚找不到旧版本 - 时区问题 → 日志时间对不上,加
TZ=Asia/Shanghai
十、今日总结
今天核心收获三件事:
- Docker 是"应用 + 环境"的打包:从"我电脑能跑"到"到处能跑"
- 多阶段构建是必备技巧:构建阶段装编译工具,运行阶段只拿虚拟环境,镜像能瘦 5-8 倍
docker-compose让多容器编排变成"一份 YAML":API + DB + Redis + Nginx 一键起
一个感受:
Docker 是"前端构建思维"的后端版本。
- 前端:源码 →
npm run build→dist/- 后端:源码 →
docker build→ image- 前端:
dist/扔 CDN- 后端:image 扔任意服务器
核心思想完全一样:把"运行环境"也打包进产物,让部署变得确定。
"在我电脑上能跑"的终结者。
你的项目现在能"一键部署"到任何服务器了。 只要装了 Docker,docker compose up -d 就能跑。Day 30 部署到真实服务器时,直接复用这套。
Day 27 完成度:✅
明日预告(Day 28)
- CI/CD:GitHub Actions 自动化
- 自动化测试、构建、发布
- 从 push 到部署的全自动流程
- 分环境部署(staging / production)
- 给项目加一套完整的 CI/CD
最后
如果这篇对你有用,点个赞让我知道有人在看。
评论区互动: 你之前用过 Docker 吗?遇到过什么坑?欢迎分享。
有问题评论区见,我们第 28 天继续。
100 天,第 28 天见。
附录:系列目录
已完成 ✅
| 阶段 | 天数 | 内容 |
|---|---|---|
| 基础 | 01-06 | Python 语法、数据结构、函数、OOP、文件、依赖管理 |
| 网络 | 07-08 | HTTP 请求、异步 asyncio |
| 工程 | 09-12 | 正则、装饰器、Pydantic、日志调试 |
| 项目 | 13-14 | 命令行 AI 助手 v1 / v2 |
| 复盘 | 15 | 阶段总结 |
| 数据 | 16-17 | SQLite 数据库、SQLAlchemy ORM |
| 测试 | 18-19 | pytest 入门 + 进阶 |
| 工具 | 20 | CLI 打包与发布 |
| 后端 | 21-27 | FastAPI 全链路 + Docker 化 ✅ |
进行中 🎯
| 阶段 | 天数 | 内容 |
|---|---|---|
| 后端 | 28-30 | CI/CD + 部署上线 |
| 大模型 | 31-45 | LLM API + Prompt 工程 |
| RAG | 46-60 | 向量数据库 + 知识库 |
| Agent | 61-80 | LangChain / 工具调用 |
| 实战 | 81-100 | 完整 Agent 项目上线 |
100 天后见,一起从切图仔变成 Agent 开发者。