【FDE系列】阶段2:Day 47:对接企业系统 — 飞书 / 钉钉 API

📚前言

📒FDE系列内容总纲:

【大纲】FDE 前沿部署工程师学习系列教程-CSDN博客

🚄前置课程列表:

见文档结尾附录。


🚀阶段2·Day 47:对接企业系统 --- 飞书 / 钉钉 API

FDE 学习系列教程 · 第二阶段 · 第 6 周 · Day 2 预计时长:3 小时 | 难度:★★★★☆ | 前置知识:Day 46 认证、FastAPI、环境变量
📌 一句话目标:掌握用 httpx 调用外部 HTTP 接口,理解企业开放平台"app_id/secret 换 token"的通用模式,实现带 token 缓存、自动刷新、失败重试的飞书/钉钉客户端。


🧑‍🤝‍🧑 开场:FDE 的核心战场------让系统之间对话

昨天你是"守门人",给自己的 API 加锁。今天换到"访客"视角------带着凭证去调用别人的 API

FDE 在客户现场最高频的一类需求就是对接企业 IM(即时通讯)。随便举几个真实场景:

  • 设备温度超标,自动往飞书运维群里发告警消息

  • 工单创建成功,通知到钉钉对应负责人

  • 每天早上 9 点,读取值班表,把今日待办推送给值班同事

  • 新员工入职,从企业通讯录拉取部门信息同步到你的系统

这些事的本质都一样:你的程序,按照对方平台规定的方式,调用它的 HTTP 接口。

今天的内容确实硬核,一次消化不了正常,先跟着把流程跑通,理解"换 token → 带 token 调接口"这条主线,细节以后对着平台文档查就行。

复制代码
今天的能力地图:

  通用 HTTP 客户端 httpx        ------ 任何第三方 API 都用它调
        │
        ▼
  企业平台通用认证模式          ------ app_id + app_secret 换 access_token
        │
        ▼
  飞书实战 / 钉钉对比           ------ 发消息、读群列表
        │
        ▼
  工程化:token 缓存、自动刷新、失败重试

💡 关于练习环境:真实调用飞书/钉钉需要你有企业管理员权限去"创建应用"。考虑到不是每个人都有现成企业环境,今天我会同时给你两套方案:

  • 方案 A(推荐先做) :本地搭一个模拟服务器(Mock),零门槛练通整个调用流程

  • 方案 B(有条件再做):注册飞书自建应用,真实调用

两套方案的客户端代码几乎一样------这正是抽象的价值:先在 Mock 上把逻辑写对,接真实平台只改地址和凭证。


📖 一、认识 httpx:现代 Python HTTP 客户端

调外部 API,本质就是用 Python 发 HTTP 请求。你可能听过 requests(老牌经典),今天用 httpx ------它接口和 requests 几乎一样,但原生支持异步(async),和你的 FastAPI 是绝配(FastAPI 的异步接口里绝不能用阻塞的 requests)。

安装

复制代码
pip install httpx

三种最常用请求

复制代码
import httpx

# ① GET 请求(查数据)
resp = httpx.get("https://httpbin.org/get")
print(resp.status_code)     # 200
print(resp.json())          # 响应体转成字典

# ② POST 发 JSON(提交数据)------ 调企业 API 90% 是 POST
resp = httpx.post(
    "https://httpbin.org/post",
    json={"name": "注塑机A1", "temperature": 91},   # 自动序列化为 JSON、设好 Content-Type
)
print(resp.json()["json"])  # 对方收到的数据

# ③ 带请求头(认证 token 就放这)
resp = httpx.get(
    "https://httpbin.org/get",
    headers={"Authorization": "Bearer xxx-token"},
)

响应对象的常用属性

复制代码
resp = httpx.get("https://httpbin.org/get")

resp.status_code     # HTTP 状态码:200/401/404...
resp.text            # 响应体(字符串)
resp.json()          # 响应体解析成 dict/list(对方返回 JSON 时)
resp.headers         # 响应头
resp.raise_for_status()   # 如果状态码是 4xx/5xx,直接抛异常

为什么企业 API 推荐用 Client(连接复用)

复制代码
# 零散调用:每次请求都新建一次连接(慢)
httpx.get(url1)
httpx.post(url2, ...)

# 用 Client:复用底层连接池(快),还能统一配置 base_url、请求头
with httpx.Client(base_url="https://open.feishu.cn", timeout=10) as client:
    client.post("/open-apis/auth/...")   # URL 只写路径部分
    client.get("/open-apis/im/v1/chats")
# with 结束自动关闭连接

# 异步版(FastAPI 里用):AsyncClient + await
async with httpx.AsyncClient() as client:
    r = await client.get(url)

📌 FDE 记住:写脚本一次性调用httpx.get/post 简单;封装成客户端类、要多次调用 (比如今天的飞书客户端)用 Client/AsyncClient,配 base_url 和统一超时。


📖 二、企业开放平台的通用认证模式

飞书、钉钉、企业微信,以及绝大多数开放平台,认证流程惊人地一致。先把这个"万能套路"搞懂,看任何平台文档都不慌:

复制代码
┌────────────────────────────────────────────────────────────┐
│          对接企业 API 的四步通用流程                         │
└────────────────────────────────────────────────────────────┘

  第 1 步:注册应用,拿到一对"身份证"
     app_id(公开标识)+ app_secret(私密密钥)

  第 2 步:用身份证换"临时通行证"
     POST 认证接口 {app_id, app_secret}
        → 返回 access_token(有效期通常 2 小时)

  第 3 步:带着 token 调业务接口
     GET/POST 业务接口
     Header: Authorization: Bearer <access_token>
        → 发消息、读通讯录、建日程......

  第 4 步:token 过期前自动重新获取
     缓存 token,快过期时再换新的(别每次调用都换)

  为什么这么绕?不直接用 app_secret 调接口?

  · app_secret 是长期密钥,泄露危害大,要尽量少在网络上传
  · access_token 是短期令牌,2 小时过期,即使泄露损失可控
  · token 可以携带权限范围(scope),平台能精细化授权

  和昨天学的 JWT 思想一脉相承:长期密钥换短期令牌。

各家平台的术语对照(看着不同,本质一样)

平台 应用凭证 换取的令牌 认证接口(大致)
飞书 app_id + app_secret tenant_access_token /open-apis/auth/v3/tenant_access_token/internal
钉钉 appkey + appsecret access_token /gettoken
企业微信 corpid + corpsecret access_token /gettoken

🖥️ 三、方案 A:搭一个 Mock 服务器(零门槛先练通)

在没有真实企业凭证时,我们用 FastAPI 本地模拟一个"飞书风格"的开放平台。它实现两个接口:换 token、发消息。你写的客户端去调它,流程和调真实飞书完全一致。

新建 mock_platform.py

复制代码
"""
模拟企业开放平台(飞书风格)
运行:uvicorn mock_platform:app --port 9000
"""
from fastapi import FastAPI, Header, HTTPException
import time

app = FastAPI(title="模拟企业开放平台")

# 模拟平台后台存储
APP_ID = "cli_mock_123"
APP_SECRET = "mock_secret_456"
VALID_TOKENS: dict[str, float] = {}   # token -> 过期时间戳
MESSAGES = []                          # 收到的消息记录


@app.post("/auth/v3/tenant_access_token/internal")
def get_token(body: dict):
    """第 2 步:app_id + secret 换 access_token"""
    if body.get("app_id") != APP_ID or body.get("app_secret") != APP_SECRET:
        # 飞书真实接口错误时 code 非 0
        return {"code": 99991663, "msg": "app_id or app_secret invalid"}

    token = f"t-mock-{int(time.time() * 1000)}"
    # 真实飞书有效期 7200 秒;这里设短一点(30秒)方便测试自动刷新
    VALID_TOKENS[token] = time.time() + 30
    return {"code": 0, "msg": "ok", "tenant_access_token": token, "expire": 30}


def _check_token(authorization: str):
    """统一校验 token"""
    if not authorization or not authorization.startswith("Bearer "):
        raise HTTPException(401, "missing token")
    token = authorization.split("Bearer ", 1)[1]
    expire = VALID_TOKENS.get(token)
    if not expire:
        raise HTTPException(401, "invalid token")
    if time.time() > expire:
        VALID_TOKENS.pop(token, None)
        raise HTTPException(401, "token expired")
    return token


@app.post("/im/v1/messages")
def send_message(body: dict, authorization: str = Header(None)):
    """第 3 步:带 token 发消息"""
    _check_token(authorization)
    MESSAGES.append(body)
    return {"code": 0, "msg": "ok", "data": {"message_id": f"om_{len(MESSAGES)}"}}


@app.get("/im/v1/chats")
def list_chats(authorization: str = Header(None)):
    """读群列表"""
    _check_token(authorization)
    return {"code": 0, "data": {"items": [
        {"chat_id": "oc_ops_001", "name": "设备运维群"},
        {"chat_id": "oc_safe_002", "name": "安全生产群"},
    ]}}


@app.get("/debug/messages")
def debug_messages():
    """调试用:查看平台收到了哪些消息"""
    return {"count": len(MESSAGES), "messages": MESSAGES}

启动它(占用 9000 端口):

复制代码
uvicorn mock_platform:app --port 9000

新开终端,先手动验证一下这个模拟平台:

复制代码
# 换 token
curl -X POST http://localhost:9000/auth/v3/tenant_access_token/internal \
  -H "Content-Type: application/json" \
  -d '{"app_id":"cli_mock_123","app_secret":"mock_secret_456"}'
# {"code":0,"tenant_access_token":"t-mock-...","expire":30}

# 错误凭证
curl -X POST http://localhost:9000/auth/v3/tenant_access_token/internal \
  -H "Content-Type: application/json" \
  -d '{"app_id":"wrong","app_secret":"wrong"}'
# {"code":99991663,"msg":"app_id or app_secret invalid"}

Mock 就绪。接下来写真正有价值的东西------一个能复用的企业 IM 客户端类。


🖥️ 四、封装企业 IM 客户端(核心代码)

新建 im_client.py。这个类的设计思路适用于飞书、钉钉乃至任何类似平台:

复制代码
"""
企业 IM 客户端:token 缓存 + 自动刷新 + 失败重试
"""
import time
import logging
import httpx

logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s [%(levelname)s] %(message)s")
log = logging.getLogger(__name__)


class IMWikiClient:
    """对接企业开放平台的通用客户端(默认指向本地 Mock,可改 base_url 接真实飞书)"""

    def __init__(self, app_id: str, app_secret: str,
                 base_url: str = "http://localhost:9000",
                 timeout: float = 10.0):
        self.app_id = app_id
        self.app_secret = app_secret
        self.base_url = base_url.rstrip("/")
        # token 缓存:None 表示还没获取
        self._token: str | None = None
        self._token_expire_at: float = 0
        # 连接池客户端,统一超时
        self._http = httpx.Client(base_url=self.base_url, timeout=timeout)

    # ---------- token 管理 ----------
    def _fetch_token(self) -> str:
        """真正去请求新 token"""
        log.info("正在获取新的 access_token ...")
        resp = self._http.post(
            "/auth/v3/tenant_access_token/internal",
            json={"app_id": self.app_id, "app_secret": self.app_secret},
        )
        data = resp.json()
        if data.get("code") != 0:
            raise RuntimeError(f"获取 token 失败:{data}")

        self._token = data["tenant_access_token"]
        # 提前 5 分钟(Mock 环境提前 25 秒)认为过期,留足余量
        buffer = 300 if data.get("expire", 0) > 100 else 25
        self._token_expire_at = time.time() + data["expire"] - buffer
        log.info("获取成功,token 将在 %.0f 秒后刷新", data["expire"] - buffer)
        return self._token

    def _get_token(self) -> str:
        """获取可用 token:缓存有效就用缓存,否则换新的"""
        if self._token and time.time() < self._token_expire_at:
            return self._token
        return self._fetch_token()

    # ---------- 带重试的请求 ----------
    def _request(self, method: str, path: str, **kwargs):
        """统一请求:token 失效自动重取并重试一次,网络错误重试 3 次"""
        max_network_retry = 3
        for attempt in range(1, max_network_retry + 1):
            token = self._get_token()
            headers = kwargs.pop("headers", {})
            headers["Authorization"] = f"Bearer {token}"
            try:
                resp = self._http.request(method, path, headers=headers, **kwargs)
            except httpx.HTTPError as e:
                log.warning("网络异常(第 %s 次):%s", attempt, e)
                if attempt == max_network_retry:
                    raise
                time.sleep(1 * attempt)
                continue

            # token 失效(过期/被吊销):清缓存重新获取,重试一次
            if resp.status_code == 401:
                log.warning("token 已失效,清除缓存后重试")
                self._token = None
                continue

            resp.raise_for_status()
            data = resp.json()
            # 业务层错误码(飞书风格:code=0 才算成功)
            if isinstance(data, dict) and data.get("code", 0) != 0:
                raise RuntimeError(f"接口返回错误:{data}")
            return data

        raise RuntimeError("请求重试次数耗尽")

    # ---------- 业务方法 ----------
    def send_text(self, chat_id: str, text: str) -> dict:
        """发送文本消息到群聊"""
        import json
        return self._request(
            "POST", "/im/v1/messages",
            params={"receive_id_type": "chat_id"},
            json={
                "receive_id": chat_id,
                "msg_type": "text",
                "content": json.dumps({"text": text}, ensure_ascii=False),
            },
        )

    def list_chats(self) -> list:
        """获取群列表"""
        data = self._request("GET", "/im/v1/chats")
        return data.get("data", {}).get("items", [])

    def close(self):
        self._http.close()

    # 支持 with 语法
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()

跑通完整调用

新建 run_demo.py

复制代码
from im_client import IMWikiClient

# 用 Mock 的凭证;接真实飞书时换成真实 app_id/secret 和官方 base_url
with IMWikiClient(
    app_id="cli_mock_123",
    app_secret="mock_secret_456",
    base_url="http://localhost:9000",
) as client:

    # ① 读群列表
    chats = client.list_chats()
    print("群列表:", chats)

    # ② 发告警消息
    result = client.send_text(
        chat_id="oc_ops_001",
        text="🚨【设备告警】注塑机A3 温度 91℃,超过阈值 80℃,请立即处理!",
    )
    print("发送结果:", result)

    # ③ 再发一条(这次应该复用缓存的 token,不会重新获取)
    client.send_text("oc_ops_001", "📋 巡检任务已完成,共检查 5 台设备")

python run_demo.py

观察日志,重点看 token 行为:

复制代码
[INFO] 正在获取新的 access_token ...
[INFO] 获取成功,token 将在 5 秒后刷新
群列表: [{'chat_id': 'oc_ops_001', 'name': '设备运维群'}, ...]
发送结果: {'code': 0, 'data': {'message_id': 'om_1'}}
(第 3 次调用没有再打印"获取新 token"------缓存命中 ✅)

到 Mock 的调试接口确认消息真的发出去了:

复制代码
curl http://localhost:9000/debug/messages
# {"count":2,"messages":[{"receive_id":"oc_ops_001",...}]}

验证自动刷新

Mock 的 token 有效期设成了 30 秒(提前 25 秒刷新)。在 run_demo.py 里加一段等待,观察过期后自动换新:

复制代码
import time
print("等待 token 过期 ...")
time.sleep(8)
client.send_text("oc_ops_001", "⏰ 这条消息用的是刷新后的新 token")
# 日志会再次出现"正在获取新的 access_token ..."

📖 五、Token 管理的四个工程要点

这个客户端类是今天的精华,把背后的设计原则讲透:

复制代码
┌────────────────────────────────────────────────────────────┐
│  ① 缓存(Cache)                                            │
│     不要每次调业务接口都先换 token。换 token 有网络开销,    │
│     平台也有调用频率限制。拿到后存起来,有效期内反复用。    │
│                                                            │
│  ② 提前刷新(Refresh ahead)                                │
│     别等 token 真正过期那一刻才换------万一正好卡在请求中途     │
│     过期就失败了。提前几分钟(如 5 分钟)就认为该换了。     │
│                                                            │
│  ③ 失效兜底(Invalidate & retry)                           │
│     即使提前刷新,token 仍可能被管理员手动吊销。调用业务    │
│     接口收到 401 时:清空缓存 → 强制换新 → 重试一次。       │
│                                                            │
│  ④ 网络重试(Retry with backoff)                           │
│     网络抖动、平台瞬时不可用很常见。失败后等 1s、2s、3s     │
│     递增重试(退避),不要无限重试,超过次数就报错让人处理。 │
└────────────────────────────────────────────────────────────┘

  token 生命周期时间轴:

  换取 token
     │
     │◄──────────── 缓存有效,直接用 ───────────►│
     │                                          │
     │                              提前刷新点   │ 真正过期
     │                                  │        │
  ───●──────────────────────────────────●────────●──────────► 时间
     0                              expire-300   expire
                                    ↑
                            代码在这里就主动换新 token
                            (保证任何请求用的 token 都不会过期)

💡 多线程/多协程高并发时还有个细节:可能 10 个请求同时发现 token 过期,一起去换新 token("惊群效应")。生产环境会加锁,让第一个请求去换,其余等待复用。学习阶段知道有这回事即可。


📖 六、钉钉 API 的差异(对照学习)

把客户端的认证部分换成钉钉,你会发现几乎就是换了个 URL 和字段名。钉钉获取 token:

复制代码
GET https://oapi.dingtalk.com/gettoken?appkey=xxx&appsecret=xxx

返回:
{
  "errcode": 0,
  "access_token": "xxxx",
  "expires_in": 7200
}

差异对照:

环节 飞书 钉钉
凭证字段 app_id / app_secret appkey / appsecret
换 token POST + JSON body GET + query 参数
成功判断 code == 0 errcode == 0
token 字段 tenant_access_token access_token
有效期字段 expire expires_in
带 token 调接口 请求头 Authorization: Bearer URL 参数 ?access_token=xxx

一个钉钉版 token 获取方法长这样(体会"换汤不换药"):

复制代码
def _fetch_dingtalk_token(self):
    resp = self._http.get(
        "https://oapi.dingtalk.com/gettoken",
        params={"appkey": self.app_id, "appsecret": self.app_secret},
    )
    data = resp.json()
    if data.get("errcode") != 0:
        raise RuntimeError(f"钉钉 token 获取失败:{data}")
    self._token = data["access_token"]
    self._token_expire_at = time.time() + data["expires_in"] - 300
    return self._token

📌 学对接的正确姿势 :不要背每个平台的接口!而是掌握通用模式 + 学会查官方文档。任何平台先找三样东西:①认证 (怎么拿 token)②接口地址和参数错误码表 。飞书开放平台 open.feishu.cn、钉钉开放平台 open.dingtalk.com 都有在线 API 调试台,可以填参数直接试。


🖥️ 七、方案 B:接真实飞书(有条件再做)

如果你有飞书企业版(或免费注册一个测试企业),可以真实调用:

准备步骤

  1. 打开飞书开放平台 飞书开放平台 ,用企业账号登录

  2. 进入"开发者后台" → 创建企业自建应用,填名字描述

  3. 在"凭证与基础信息"里拿到 App IDApp Secret

  4. 进入"权限管理",开通权限:

    • im:message:send_as_bot(以应用身份发消息)

    • im:chat:readonly(读群列表)

  5. 发布版本并由企业管理员审核通过(自建应用通常秒过)

  6. 把机器人加进一个群(群设置 → 群机器人 → 添加你的应用机器人),拿到 chat_id

改两行代码切到真实平台

复制代码
import os

with IMWikiClient(
    app_id=os.getenv("FEISHU_APP_ID"),        # cli_xxxxxxxx
    app_secret=os.getenv("FEISHU_APP_SECRET"),
    base_url="https://open.feishu.cn",         # ← 换成官方地址
) as client:
    chats = client.list_chats()
    print(chats)
    # client.send_text("oc_真实群id", "来自 FDE 的真实告警")

export FEISHU_APP_ID="cli_xxxxxxxx"
export FEISHU_APP_SECRET="xxxxxxxxxx"
python run_demo.py

⚠️ 凭证安全:app_secret 等同于应用密码,绝不能写死代码、提交 Git、发到群里。一律走环境变量或配置中心(呼应第 4 周 .env、第 5 周 12-Factor)。

真实平台常见错误码速查(飞书)

code 含义 排查方向
0 成功 ---
99991663 token 无效 app_id/secret 错或 token 失效
99991664 token 过期 刷新 token
99991668 权限不足 去权限管理加 scope 并发版
230002 群/会话不存在 chat_id 错、机器人没进群
99991400 请求过于频繁 触发限流,降低频率/加退避

📝 本课小结

知识点 一句话记住
httpx Python 发 HTTP;Client 复用连接,AsyncClient 配 FastAPI
常用方法 resp.status_code / .json() / .raise_for_status()
企业认证通用模式 app_id+secret → 换 access_token → 带 token 调接口
token 有效期 通常 2 小时,短期令牌降低泄露风险
缓存 token 有效期内复用,别每次都换
提前刷新 到期前 5 分钟主动换新
401 兜底 清缓存重新换 token 并重试
网络重试 失败退避重试 3 次,超限报错
飞书 POST 换 token,code==0,Bearer 头
钉钉 GET 换 token,errcode==0,query 带 token
凭证管理 app_secret 走环境变量,绝不进 Git
学习方法 掌握通用模式 + 查文档,不背接口

🧠 核心认知 :对接第三方系统,你写的代码里最值钱的不是"怎么发请求"(httpx 几行就够),而是周边的工程可靠性------token 的缓存与刷新、失败的重试、错误码的处理、凭证的保护。这些做好了,你的对接程序才能在客户现场长期稳定运行,而不是三天两头因为 token 过期、网络抖动报警。


📋 课后练习

练习 1:跑通 Mock 全流程(约 30 分钟)

  1. 启动 mock_platform,用 curl 手动换一次 token、带 token 调一次群列表

  2. 运行 run_demo.py,确认消息发到了 /debug/messages

  3. 故意把 app_secret 写错,观察客户端抛出的错误信息

  4. 在两次调用之间 sleep 8 秒,从日志确认 token 自动刷新

练习 2:给客户端加新能力(约 30 分钟)

  1. 在 Mock 平台加一个 GET /contact/v3/users 接口(带 token 校验,返回模拟的员工列表)

  2. 在 IMWikiClient 里加 list_users() 方法调用它

  3. 写代码调用并打印员工名单,体会"新增一个平台能力 = Mock 加接口 + 客户端加方法"的扩展模式

练习 3:理解重试(约 20 分钟)

  1. 把客户端的 base_url 改成一个不存在的地址(如 http://localhost:9999),运行观察重试 3 次的日志和等待时间

  2. 思考:为什么重试间隔要"递增"(1s、2s、3s)而不是固定 1 秒?(提示:平台过载时,大量客户端同时固定间隔重试会造成"重试风暴")

  3. 查一查"指数退避(exponential backoff)"是什么意思


🔭 下节预告

今天是你的程序主动 调别人(发消息、读数据)------"拉"模式。明天学反过来的场景:别人有事情发生时,主动通知你的程序 ------"推"模式,也就是 Webhook

比如:飞书群里有人 @机器人 提了一个工单,飞书服务器会立刻 POST 一个请求到你的接口;你要接收、验证它真的是飞书发来的(防伪造)、处理、还要防止同一条事件被重复处理(幂等)。这是事件驱动自动化的核心,也是 FDE 集成项目的关键一环。明天见!


🌍附录:前置课程列表

阶段一:认知启蒙(AI 认知与 FDE 角色)
AI 认知

【FDE系列】阶段1Day 1:AI 层级关系 --- 四个嵌套的圈-CSDN博客

【FDE系列】阶段1Day 2:AI 三阶段发展史 --- 会认 → 会判断 → 会创造-CSDN博客

【FDE系列】阶段1Day 3:符号 AI vs 机器学习 --- 两条路线的本质区别-CSDN博客

【FDE系列】阶段1Day 4:Transformer 的历史意义 --- 2017 年的分水岭-CSDN博客

【FDE系列】阶段1Day 5:本周复习与自测 --- 检验你的 AI 认知地基-CSDN博客

【FDE系列】阶段1Day 6:Transformer 架构 --- 一张图纸盖出千千万万栋楼-CSDN博客

【FDE系列】阶段1Day 7:LLM 本质 --- 文字接龙机器-CSDN博客

【FDE系列】阶段1Day 8:Token --- 模型眼中的最小单位-CSDN博客

【FDE系列】阶段1Day 9:AI 幻觉 --- 为什么会一本正经地胡说八道-CSDN博客

【FDE系列】阶段1Day 10:上下文窗口 --- 模型的记忆力上限 + 本周复习-CSDN博客

【FDE系列】阶段1Day 11:Prompt --- 给模型立规矩-CSDN博客

【FDE系列】阶段1Day 12:Memory --- 让模型记住上下文

【FDE系列】阶段1Day 13:RAG --- 给模型配图书管理员-CSDN博客

【FDE系列】阶段1Day 14:Tool Use --- 让模型动手操作-CSDN博客

【FDE系列】阶段1Day 15:MCP --- 统一的工具接口标准 + 第三周复习-CSDN博客

FDE 基础概念

【FDE系列】阶段1Day 16:什么是 FDE --- 把 AI 变成客户结果的人-CSDN博客

【FDE系列】阶段1Day 17:FDE vs 传统实施 --- 三大本质区别-CSDN博客

【FDE系列】阶段1Day 18:FDE 三重身份 + C6 胜任力模型-CSDN博客

【FDE系列】阶段1Day 19:七阶段行动路径 + 行业经验的价值-CSDN博客

【FDE系列】阶段1Day 20:阶段总结与产出物 --- 第一阶段收官-CSDN博客


阶段二:技术地基(Python + FastAPI + SQL + Docker + API 集成)
Python基础

【FDE系列】阶段2:Day 21:Python 环境搭建 --- 写出你的第一行代码-CSDN博客

【FDE系列】阶段2:Day 22:变量、数据类型、条件判断 --- Python 的"记忆"和"判断"-CSDN博客

【FDE系列】阶段2:Day 23:循环与函数 --- 让代码跑 100 遍、把逻辑打包复用-CSDN博客

【FDE系列】阶段2:Day 24:数据结构 --- 列表、字典、集合、元组-CSDN博客

【FDE系列】阶段2:Day 25:文件读写与 JSON --- 让程序连通外部数据(第一周收官)-CSDN博客

【FDE系列】阶段2:Day 26:模块化编程 --- 把代码拆成"抽屉柜"-CSDN博客

【FDE系列】阶段2:Day 27:异常处理与日志 --- 让程序"摔不烂、查得到"-CSDN博客

FastAPI入门到进阶

【FDE系列】阶段2:Day 28:FastAPI 入门 --- 把你的函数变成 API 服务-CSDN博客

【FDE系列】阶段2:Day 29:FastAPI 进阶 --- Pydantic 模型与完整 CRUD 实战-CSDN博客

【FDE系列】阶段2:Day 30:生产代码规范 --- 测试、类型注解、配置管理(第二周收官)-CSDN博客

SQL基础

【FDE系列】阶段2:Day 31:SQL 基础 --- 增删改查一把梭-CSDN博客

【FDE系列】阶段2:Day 32:多表查询 --- JOIN 与聚合-CSDN博客

【FDE系列】阶段2:Day 33:进阶查询 --- 窗口函数与 CTE-CSDN博客

【FDE系列】阶段2:Day 34:数据清洗 --- 把脏数据捋干净-CSDN博客

【FDE系列】阶段2:Day 35:Python + SQL --- 工单接入 MySQL + 本周收官-CSDN博客

Linux基础

【FDE系列】阶段2:Day 36:Linux 入门与文件操作 --- 扔掉鼠标的第一天-CSDN博客

【FDE系列】阶段2:Day 37:权限、进程与文本三剑客-CSDN博客

【FDE系列】阶段2:Day 38:Shell 脚本 --- 把命令串起来自动跑-CSDN博客

【FDE系列】阶段2:Day 39:Linux 综合实战 --- 让服务无人值守-CSDN博客

【FDE系列】阶段2:Day 40:Shell 进阶 --- 生产级脚本与本周收官-CSDN博客

Docker

【FDE系列】阶段2:Day 41:Docker 入门 --- 把环境装进盒子-CSDN博客

【FDE系列】阶段2:Day 42:Dockerfile 实战 --- 把你的应用打包成镜像-CSDN博客

【FDE系列】阶段2:Day 43:Docker Compose --- 多容器一键编排-CSDN博客

相关推荐
Dawson Zhu1 小时前
从 Jev 说起:快慢模型如何协同调度,以及这背后需要解决什么问题
人工智能·语言模型·架构·aigc·agi
小陈phd1 小时前
深入理解agent学习笔记(一)——现代agent介绍
人工智能·算法
渡我白衣1 小时前
HttpRequest与HttpResponse的实现
服务器·数据结构·c++·人工智能·tcp/ip·机器学习·caffe
AAIshangyanxiu1 小时前
智能气候前沿:AI Agent结合机器学习与深度学习在全球气候变化驱动因素预测中的应用
人工智能·agent·气候变化·智能气候前沿
User_芊芊君子1 小时前
让 Claude Code 换上国产大脑:蓝耘元生代上 GLM-5.2 / DeepSeek / Qwen 模型横评实测
人工智能·ai·大模型
东坡肘子1 小时前
一场关于 SwiftUI 动画的“原生”之争 -- 肘子的 Swift 周报 #154
人工智能·swiftui·swift
编程一生1 小时前
DevOps从持续开发到持续部署
人工智能
byte轻骑兵1 小时前
【LE Audio】PBP精讲[5]: 广播音频的身份标识与元数据交互法则
人工智能·音视频·le audio·低功耗蓝牙音频
智慧物业老杨3 小时前
物业日常巡查的数智化重构:从“打卡式巡检“到“闭环式风控“
android·java·人工智能·系统架构·rxjava