【学习笔记】OpenAI 兼容 API 服务化:从协议到 LLM Gateway(19/35)

前面几篇我们反复提到 OpenAI 兼容 API------vLLM、SGLang、Ollama、TGI、lmdeploy 全都支持它。

为什么这件事这么重要?

举个真实的工程场景:你的团队同时在用:

  • 闭源 API:Claude、GPT-5、Gemini、DeepSeek API

  • 自部署 vLLM:内网 Qwen3-32B

  • 自部署 SGLang:Agent 业务用

  • 本地 Ollama:开发同学的笔记本

  • 未来还要接:阿里通义、Moonshot、智谱

如果每家都用自己的 SDK,你的客户端代码要维护 N 套 。但如果它们都"长得像" OpenAI API,你的客户端只用 openai 库就够了------只需要改 base_url 和 model 参数。

这就是 OpenAI 兼容 API 的事实标准价值。

这一篇我们要彻底搞清这个事:

  1. OpenAI API 协议的关键设计

  2. 各家推理框架的"兼容程度"和差异点

  3. 怎么自己实现一个 OpenAI 兼容代理

  4. 怎么设计 LLM Gateway 统一多模型 + 计费 + 限流 + 缓存

读完本文你将能:

  • 写出符合 OpenAI 协议的兼容 API 实现

  • 选对 LLM Gateway 工具(LiteLLM / Portkey / 自研)

  • 在生产环境跑一个多模型路由 + 容灾 + 计费 + 缓存的网关

我们开始。


一、OpenAI 兼容 API:怎么变成「事实标准」的

1.1 历史回顾

  • 2020.06 :GPT-3 API 发布,OpenAI 定义了 /v1/completions 接口

  • 2023.03 :ChatGPT API 上线,/v1/chat/completions 接口诞生

  • 2023.06:vLLM 第一次提供「OpenAI 兼容 API」选项

  • 2023 下半年:所有主流推理框架跟进

  • 2024:Anthropic、Google 等"对手"也开始提供 OpenAI 兼容接口(兼容层)

  • 2026:OpenAI 兼容已是行业默认

为什么是 OpenAI 的协议被「奉为标准」?

  1. 先发优势:GPT-3 / ChatGPT 火得早,大家先按它对接

  2. 设计合理:协议简洁清晰,支持流式 / Tool Use / 多模态扩展

  3. 生态压力 :openai Python SDK 太流行,所有客户端都用它

  4. 社区共识:大家都不想发明新协议

1.2 OpenAI 兼容 API 给工程师带来什么

好处 真实价值
客户端 SDK 通用 openai 库一统天下
切换模型零成本 改 base_url 即可
工具生态共享 LangChain / LlamaIndex 等开箱即用
测试方便 curl / Postman 直接测
AB 测试简单 多个 URL 随便切

二、OpenAI API 协议详解

2.1 核心端点

OpenAI API 主要有这几个端点:

复制代码
GET  /v1/models                     ── 列出可用模型
POST /v1/chat/completions           ── 对话补全(主力接口)
POST /v1/completions                ── 文本补全(旧版,少用)
POST /v1/embeddings                 ── 文本向量化
POST /v1/audio/transcriptions       ── 音频转文字
POST /v1/audio/speech               ── 文字转语音
POST /v1/images/generations         ── 图像生成
POST /v1/files                      ── 文件上传
POST /v1/batches                    ── 批处理(异步)

大模型推理服务最关键的是chat completions 和 embeddings。

2.2 /v1/chat/completions 详解

完整请求体:

复制代码
{
  "model":"qwen3-32b",
"messages":[
    {"role":"system","content":"你是助手"},
    {"role":"user","content":"解释 KV Cache"}
],
"temperature":0.7,
"top_p":0.9,
"max_tokens":500,
"n":1,
"stream":false,
"stop":["\n\n"],
"presence_penalty":0,
"frequency_penalty":0,
"logit_bias":{},
"user":"user-id-123",

"tools":[...],
"tool_choice":"auto",

"response_format":{"type":"json_object"},
"seed":42,

"logprobs":false,
"top_logprobs":null
}

完整响应:

复制代码
{
  "id":"chatcmpl-xxx",
"object":"chat.completion",
"created":1735000000,
"model":"qwen3-32b",
"choices":[{
    "index":0,
    "message":{
      "role":"assistant",
      "content":"KV Cache 是..."
    },
    "finish_reason":"stop"
}],
"usage":{
    "prompt_tokens":25,
    "completion_tokens":150,
    "total_tokens":175
}
}

2.3 流式输出(SSE)

stream: true 时返回 Server-Sent Events:

复制代码
data: {"id":"chatcmpl-xxx","choices":[{"delta":{"role":"assistant","content":""},"index":0}]}

data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"KV"},"index":0}]}

data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":" Cache"},"index":0}]}

data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":" 是..."},"index":0}]}

data: {"id":"chatcmpl-xxx","choices":[{"finish_reason":"stop","delta":{},"index":0}]}

data: [DONE]

两个关键点:

  1. 每行以 data: 开头,空行分隔

  2. 最后以 data: [DONE] 结束

Python 客户端处理:

复制代码
from openai import OpenAI

client = OpenAI(base_url="...", api_key="...")
stream = client.chat.completions.create(
    model="qwen3-32b",
    messages=[{"role": "user", "content": "hi"}],
    stream=True,
)

for chunk in stream:
    delta = chunk.choices[0].delta.content or ""
    print(delta, end="", flush=True)

2.4 Function Calling / Tool Use

让模型能调用外部工具,这是 Agent 的基石:

复制代码
{
  "model":"qwen3-32b",
"messages":[{"role":"user","content":"上海今天天气怎么样?"}],
"tools":[{
    "type":"function",
    "function":{
      "name":"get_weather",
      "description":"查询某个城市的天气",
      "parameters":{
        "type":"object",
        "properties":{
          "city":{"type":"string"}
        },
        "required":["city"]
      }
    }
}],
"tool_choice":"auto"
}

响应:

复制代码
{
  "choices":[{
    "message":{
      "role":"assistant",
      "tool_calls":[{
        "id":"call_xxx",
        "type":"function",
        "function":{
          "name":"get_weather",
          "arguments":"{\"city\":\"上海\"}"
        }
      }]
    },
    "finish_reason":"tool_calls"
}]
}

第二轮,把工具调用结果传回去:

复制代码
{
  "messages":[
    {"role":"user","content":"上海今天天气怎么样?"},
    {"role":"assistant","tool_calls":[...]},
    {"role":"tool","tool_call_id":"call_xxx","content":"晴,25°C"}
]
}

👉 详见 系列第 27 篇:Function Calling / Tool Use 实战。

2.5 多模态(Vision)

OpenAI 在 Vision 接入上也定义了事实标准:

复制代码
{
  "model":"qwen-vl-max",
"messages":[{
    "role":"user",
    "content":[
      {"type":"text","text":"这张图里有什么?"},
      {"type":"image_url","image_url":{"url":"https://..."}}
    ]
}]
}

也支持 base64:

复制代码
{"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw..."}}

2.6 JSON Mode

强制 JSON 输出:

复制代码
{
  "model": "qwen3-32b",
  "messages": [{"role": "user", "content": "..."}],
  "response_format": {"type": "json_object"}
}

更强的 Structured Outputs(JSON Schema 严格约束):

复制代码
{
  "response_format":{
    "type":"json_schema",
    "json_schema":{
      "name":"person",
      "schema":{
        "type":"object",
        "properties":{
          "name":{"type":"string"},
          "age":{"type":"number"}
        },
        "required":["name","age"]
      },
      "strict":true
    }
}
}

框架兼容性:

  • vLLM:支持,靠 outlines / xgrammar 后端

  • SGLang:最强支持(regex 100% 保证)

  • Ollama:基本支持

  • TGI:部分支持


三、各家框架的「兼容程度」与差异

3.1 兼容性对比

特性 vLLM SGLang TGI Ollama lmdeploy
/v1/chat/completions ✅ ✅ ✅ ✅ ✅
/v1/completions ✅ ✅ ✅ ✅ ✅
/v1/embeddings ✅ ✅ △ ✅ △
/v1/models ✅ ✅ △ ✅ ✅
流式输出 ✅ ✅ ✅ ✅ ✅
Function Calling ✅ ✅ △ ✅ △
Vision (多模态) ✅ ✅ △ ✅ ✅
JSON Mode ✅ ✅ ⭐ △ ✅ △
Logprobs ✅ ✅ ✅ ✅ △
seed (可重复性) ✅ ✅ △ ✅ △

3.2 框架扩展参数(注意非标准)

每家框架都加了"自己的扩展参数",用了就不兼容 OpenAI 标准了。

vLLM 扩展:

复制代码
{
  "guided_json":{...},                    // JSON Schema 约束
"guided_regex":"^\\d{4}-\\d{2}$",       // 正则约束
"guided_choice":["A","B","C"],        // 选项约束
"use_beam_search":true,                 // beam search
"best_of":4,
"repetition_penalty":1.05,
"lora_request":{"lora_name":"..."}     // 多 LoRA 路由
}

SGLang 扩展:

复制代码
{
  "regex": "...",
  "json_schema": {...},
  "ignore_eos": true,
  "skip_special_tokens": false
}

生产建议 :只用标准参数。需要扩展能力时,再做包装。这样切换框架成本低。

3.3 一个真实「兼容性陷阱」

很多人以为"OpenAI 兼容"就是 100% 兼容。事实上是 70-90% 兼容。

我们曾遇到的坑:

  • vLLM 早期版本不支持 seed 参数 → 测试用例随机失败

  • TGI 的 streaming chunk 格式略有差异 → 客户端 SDK 解析失败

  • Ollama 的 tool_calls 字段早期被放错位置 → Agent 框架不识别

  • 某框架对 temperature=0 处理与 OpenAI 不一致 → 输出不稳定

对策:

  1. 用 openai 官方 Python SDK 测试,能跑通才能上线

  2. 关键参数(temperature / max_tokens / stream)必须做端到端测试

  3. 灰度上线,监控成功率


四、自己实现一个 OpenAI 兼容代理

4.1 场景

很多团队会自己写一个 OpenAI 兼容代理,原因:

  • 后端可能是 vLLM、SGLang、本地小模型混合

  • 需要统一鉴权、限流、计费、监控

  • 想做 fallback、缓存等高级特性

下面是一个生产可用的极简实现(基于 FastAPI)。

4.2 完整代码(约 200 行)

复制代码
"""
OpenAI 兼容代理 - 基础版
依赖:pip install fastapi uvicorn httpx
"""
import os
import json
import time
import asyncio
from typing importAny, AsyncGenerator
from fastapi import FastAPI, Request, HTTPException, Header
from fastapi.responses import StreamingResponse, JSONResponse
import httpx

app = FastAPI(title="LLM Proxy")

# 模型 → 后端映射
ROUTING = {
    "qwen3-32b":   {"url": "http://vllm-qwen:8000",       "real_model": "Qwen/Qwen3-32B-Instruct"},
    "qwen3-7b":    {"url": "http://ollama:11434",         "real_model": "qwen3:7b-instruct"},
    "deepseek-r1": {"url": "http://sglang-r1:30000",      "real_model": "deepseek-r1"},
    "claude-fall": {"url": "https://api.anthropic.com",   "real_model": "claude-sonnet-4-6", "type": "anthropic"},
}

# 鉴权
VALID_KEYS = set(os.getenv("API_KEYS", "sk-test").split(","))


defauth(authorization: str | None):
    ifnot authorization ornot authorization.startswith("Bearer "):
        raise HTTPException(401, "Missing API key")
    key = authorization[7:]
    if key notin VALID_KEYS:
        raise HTTPException(403, "Invalid API key")
    return key


@app.get("/v1/models")
asyncdeflist_models(authorization: str = Header(None)):
    auth(authorization)
    return {
        "object": "list",
        "data": [
            {"id": name, "object": "model", "owned_by": "internal"}
            for name in ROUTING
        ]
    }


@app.post("/v1/chat/completions")
asyncdefchat_completions(req: Request, authorization: str = Header(None)):
    user_key = auth(authorization)
    body = await req.json()
    model = body.get("model")
    
    if model notin ROUTING:
        raise HTTPException(404, f"Model '{model}' not found")
    
    route = ROUTING[model]
    backend_body = {**body, "model": route["real_model"]}
    
    stream = body.get("stream", False)
    
    # 记录请求
    start_time = time.time()
    request_id = f"req-{int(start_time*1000)}"
    
    if stream:
        return StreamingResponse(
            forward_stream(route, backend_body, request_id, user_key),
            media_type="text/event-stream",
            headers={"X-Request-ID": request_id}
        )
    else:
        returnawait forward_sync(route, backend_body, request_id, user_key)


asyncdefforward_sync(route, body, req_id, user_key):
    """同步转发"""
    timeout = httpx.Timeout(300.0, connect=10.0)
    asyncwith httpx.AsyncClient(timeout=timeout) as client:
        try:
            resp = await client.post(
                f"{route['url']}/v1/chat/completions",
                json=body,
                headers={"Content-Type": "application/json"},
            )
            resp.raise_for_status()
            data = resp.json()
            # 记日志(计费、监控)
            log_usage(req_id, user_key, body["model"], data.get("usage", {}))
            return JSONResponse(content=data)
        except httpx.HTTPStatusError as e:
            raise HTTPException(e.response.status_code, e.response.text)
        except Exception as e:
            raise HTTPException(500, str(e))


asyncdefforward_stream(route, body, req_id, user_key) -> AsyncGenerator[bytes, None]:
    """流式转发"""
    timeout = httpx.Timeout(600.0, connect=10.0)
    total_tokens = 0
    
    asyncwith httpx.AsyncClient(timeout=timeout) as client:
        asyncwith client.stream(
            "POST",
            f"{route['url']}/v1/chat/completions",
            json=body,
            headers={"Content-Type": "application/json"},
        ) as resp:
            if resp.status_code != 200:
                err = await resp.aread()
                yieldf'data: {{"error": "{err.decode()}"}}\n\n'.encode()
                return
            
            asyncfor line in resp.aiter_lines():
                ifnot line:
                    continue
                yieldf"{line}\n\n".encode()
                # 统计 token(简化版,实际要解析 chunk)
                total_tokens += 1
    
    log_usage(req_id, user_key, body["model"], {"completion_tokens": total_tokens})


deflog_usage(req_id, user, model, usage):
    """记录使用量(用于计费、监控)"""
    print(f"[USAGE] {req_id} user={user} model={model} usage={usage}")
    # 生产环境写 Redis / Kafka / 数据库


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

4.3 启动 + 测试

复制代码
# 启动
API_KEYS=sk-prod-1,sk-prod-2 python proxy.py

# 测试
curl http://localhost:8000/v1/chat/completions \
  -H "Authorization: Bearer sk-prod-1" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3-32b",
    "messages": [{"role":"user","content":"hi"}],
    "stream": true
  }'

这个代理已经具备:

  • 模型路由

  • API key 鉴权

  • 流式 / 非流式转发

  • 使用量记录

  • 超时控制

后面我们扩展它加更多特性。


五、LLM Gateway:完整的生产架构

5.1 为什么要 Gateway

随着业务变复杂,简单代理不够用。需要一个完整的 LLM Gateway:

复制代码
                      ┌─────────────┐
应用 1 (聊天) ──┐    │             │   ┌── vLLM (Qwen)
应用 2 (Agent) ─┼──→│ LLM Gateway │ ─┼── SGLang (DeepSeek)
应用 3 (RAG)  ──┤    │             │   ├── Claude API
应用 4 (Code) ──┘    │             │   └── GPT-5 API
                      └─────────────┘
                       ↑↑↑↑↑↑↑↑↑↑↑↑↑
                       鉴权 / 限流 / 计费
                       缓存 / 监控 / 容灾
                       Fallback / 路由 / 灰度

Gateway 的核心职责:

  1. 统一接口:所有应用看到的都是 OpenAI 标准

  2. 多后端路由:按模型名 / 优先级 / 业务路由

  3. 鉴权:API Key 管理 + 配额

  4. 限流:QPS / TPM 限制

  5. 计费:token 使用 → 成本核算

  6. 缓存:相同请求复用结果

  7. 监控:延迟、错误率、成本可视化

  8. 容灾:上游失败自动 fallback

  9. 审计:日志、合规审查

5.2 主流 Gateway 方案对比

方案 类型 优势 适合
LiteLLM 开源 Python 100+ 模型支持,社区活跃 首选 ⭐
Portkey 商业 SaaS 监控强、可视化好 企业
Kong AI Gateway 商业 / 开源 与 Kong API Gateway 集成 已用 Kong
Cloudflare AI Gateway 云服务 全球节点 / 边缘缓存 全球应用
自研 自己写 完全可控 大厂

5.3 LiteLLM 实战

LiteLLM 是 2024 年开源社区最火的 Gateway,它把 100+ 模型 API 都映射到 OpenAI 协议。

部署 LiteLLM Proxy:

复制代码
pip install litellm[proxy]

# 创建配置文件 config.yaml
cat > config.yaml << 'EOF'
model_list:
  - model_name: qwen3-32b
    litellm_params:
      model: openai/Qwen/Qwen3-32B-Instruct
      api_base: http://vllm-qwen:8000/v1
      api_key: sk-dummy

  - model_name: claude
    litellm_params:
      model: anthropic/claude-sonnet-4-6
      api_key: os.environ/ANTHROPIC_API_KEY

  - model_name: gpt-5
    litellm_params:
      model: openai/gpt-5
      api_key: os.environ/OPENAI_API_KEY

litellm_settings:
  drop_params: true
  num_retries: 3
  request_timeout: 600
  cache: true
  cache_params:
    type: redis
    host: redis
    port: 6379

router_settings:
  routing_strategy: usage-based-routing-v2  # 按用量负载
  fallbacks:
    - "gpt-5": ["claude", "qwen3-32b"]      # GPT-5 失败 → fallback
EOF

# 启动
litellm --config config.yaml --port 4000

测试:

复制代码
from openai import OpenAI

client = OpenAI(base_url="http://localhost:4000/v1", api_key="sk-...")

# 用任意模型名
for model in ["qwen3-32b", "claude", "gpt-5"]:
    r = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": "hi"}],
    )
    print(f"{model}: {r.choices[0].message.content}")

LiteLLM 内置特性:

  • ✅ 自动 fallback / retry

  • ✅ Redis 缓存

  • ✅ 按 API Key 限流和计费

  • ✅ Prometheus metrics

  • ✅ Admin UI(用户管理、用量查看)

  • ✅ Slack / 邮件告警

5.4 关键设计点

5.4.1 模型映射策略
复制代码
# 简单别名
"smart-fast": ["qwen3-32b"]

# 按优先级
"smart-best": ["claude-opus-4-7", "gpt-5", "qwen3-32b"]   
# 第一个失败 → 第二个 → ...

# 按权重负载
"smart":
-model:"qwen3-32b"
    weight:70
-model:"claude"
    weight: 30
5.4.2 Fallback 策略
复制代码
fallbacks = {
    "gpt-5": ["claude-sonnet-4-6", "qwen3-32b"],
    "claude-opus-4-7": ["gpt-5", "qwen3-32b"],
}

典型场景:

  • API rate limit 触发 → 切到自部署

  • 上游返回 5xx → 立即重试到备用

  • 超时 → 触发 fallback

5.4.3 缓存策略
复制代码
cache:
  type: redis
  ttl: 3600
  key_strategy: hash_of_prompt   # 完全相同的 prompt 复用
  excluded_models: ["claude-opus-4-7"]   # 关键模型不缓存

注意:

  • • 缓存对 temperature > 0 的请求风险大(用户期望随机性)

  • • 推荐只缓存 temperature=0 或带 cache=true 标记的请求

5.4.4 限流策略
复制代码
rate_limits:
  -api_key:"sk-team-a"
    rpm:1000    # 每分钟请求
    tpm:200000# 每分钟 token

-api_key:"sk-team-b"
    rpm:500
    tpm: 100000
5.4.5 计费
复制代码
# LiteLLM 内置成本计算
model_costs = {
    "qwen3-32b": {"input": 0.0004, "output": 0.0012},   # per 1K tokens
    "claude-sonnet-4-6": {"input": 0.003, "output": 0.015},
    "gpt-5": {"input": 0.010, "output": 0.040},
}

# 每次请求记账
def calculate_cost(usage, model):
    return (
        usage["prompt_tokens"] * model_costs[model]["input"] +
        usage["completion_tokens"] * model_costs[model]["output"]
    ) / 1000

5.5 监控面板

LiteLLM 内置 Admin UI,能看到:

  • 各模型 QPS / 错误率 / 延迟

  • 各 API Key 用量 / 成本

  • Fallback 触发次数

  • 缓存命中率

也可以接 Prometheus + Grafana 做更专业的监控。


六、生产环境的几个真实问题

6.1 长尾延迟治理

问题:99% 请求快,1% 请求 30s+。

对策:

  • 设置严格超时(30-60 秒)

  • 长 prompt 走专门通道(不和短 prompt 混 batch)

  • 慢请求自动降级到小模型

6.2 成本失控

问题:业务突然爆量,月账单暴涨。

对策:

  • API Key 配额硬上限(超额拒绝)

  • 实时成本监控 + 阈值告警

  • 大流量场景用便宜模型 + 缓存

  • 关键 / 极致需求才用顶级模型

6.3 多模型质量不一致

问题:业务有时用 GPT-5、有时 fallback 到 Qwen3-32B,效果差异大。

对策:

  • 同等级模型才互相 fallback

  • prompt 适当冗余以适配多家模型

  • 通过 A/B 测试确认 fallback 模型质量

6.4 流式输出的代理坑

问题:流式输出经过代理后断了 / 慢了。

对策:

复制代码
# Nginx
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 600s;
add_header X-Accel-Buffering no;

# FastAPI 代理
return StreamingResponse(
    generator(),
    media_type="text/event-stream",
    headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}
)

七、扩展话题与下一篇预告

7.1 OpenAI 协议之外

虽然 OpenAI 兼容是事实标准,但有些场景需要别的协议:

  • MCP(Model Context Protocol):Anthropic 推出的 AI 工具协议,2024 年快速崛起

  • Anthropic Messages API:略有差异,但好处是支持更丰富的 system prompt

  • gRPC:内部高性能调用用,不走 HTTP

7.2 LLM Gateway 的未来

2026 年 LLM Gateway 的趋势:

  • 智能路由:根据 prompt 内容自动选最佳模型

  • Prompt 优化:网关层自动改写 prompt 节省 token

  • 语义缓存:相似 prompt 也能命中(不只是完全相同)

  • 观测增强:tracing 完整链路(OpenTelemetry 集成)

7.3 下一篇预告

  • 第 20 篇:分布式推理 - TP / PP / EP 并行策略详解 ------ 部署服务化篇的收官。我们已经多次提到 TP / PP / EP,这一篇会把它们彻底讲透。包括什么场景用什么、混合策略怎么设计、跨机部署的网络要求。

下一篇结束之后,部署服务化篇(第 16-20 篇)正式收官。


八、结语:API 兼容性是 AI 工程化的基石

读完本文你应该明白:

  • OpenAI 兼容 API 是事实标准------一套客户端代码,N 个后端

  • 协议细节多------流式、Tool Use、JSON Mode、Vision 都有规范

  • 各框架"70-90% 兼容"------关键参数务必端到端测试

  • LLM Gateway 是生产必备------多模型、限流、计费、监控、容灾的统一入口

  • LiteLLM 是 2026 年开源首选------100+ 模型支持,生态最广

  • 长尾、成本、质量、流式四大生产问题都有套路化的对策

参考文献:

OpenAI 兼容 API 服务化:从协议到 LLM Gateway

相关推荐
JWASX1 小时前
Java 转 go 学习 - 类型转换
学习·golang
Ztt6666666661 小时前
壶口敞开以后,器身反而更显从容
笔记·其他·百度·微信公众平台·微信开放平台
小淮AI3 小时前
在职医生备考主治医师,我的资料搭配思路
学习
知产xiao_xin3 小时前
了解商号权
经验分享·笔记·知识产权
进击的横打3 小时前
【人工智能】卡片笔记法:从原子化思考到知识网络构建
笔记
AOI小白新手上路4 小时前
韦东山《ARM 架构与编程》基于I.MX6ULL 3-1 硬件知识_LED 原理图 · 学习笔记
arm开发·学习·架构
传奇开心果编程5 小时前
【现代声明式UI学与练】第1课 从命令式UI到声明式UI
学习·flutter·ui·swiftui·react·android jetpack
东方芷兰5 小时前
Agent 技术摘要 06 —— Harness、原生视觉、Jev、mmproj、dsh
人工智能·笔记·python·ai·langchain·ai编程
贾斯汀frank5 小时前
飞牛OS部署Memos私有笔记:Docker安装
笔记
老王爱玩车6 小时前
工具函数——清空输入缓冲区
c语言·开发语言·学习