手把手教你实现用AI大模型做代码审查

背景

我头脑中有个理念,能用AI大模型做的事情,能自动化的事情,就不要让人做。这一类事情,人与AI大模型相比,几无优势。AI大模型不知疲倦,会强化学习,会越来越聪明,质量不断提高。随着技术的进步,代码审查已经进入可以自动化事情之列,一直都很想学习一下如何用AI大模型做代码审查,经常说就是没有行动。这次下定决心,要排除各种借口和理由,告别拖延症,现在我们进入正题。

AI大模型审查的流程是:

  1. 代码编写者在Gitlab UI界面发起合并代码申请
  2. 触发配置好的gitlab webhook事件
  3. webhook将代码合并信息推送给Jenkins
  4. 在Jenkins上运行AI代码审查主流程逻辑,将文件发送给大模型,将审查结果写入到Gitlab 合并请求评论区

实现步骤

第一步 制作大模型镜像

在代码审查方面, 下面四个大模型表现相对较好

1. GPT-OSS 系列

推荐指数:⭐⭐⭐⭐⭐

模型 :gpt-oss:20B(开源大模型)
部署 :Docker 容器,本地部署,支持 Ollama 或 vLLM
优势 :中文代码理解能力强,多语言支持(Python、Java、JS/TS 等常见语言),上下文处理能力大(20B 模型约 32k token 上下文)
特点:适合代码审查、生成、优化任务,尤其在大型项目中能提供详细分析

2. CodeLlama 系列

推荐指数:⭐⭐⭐⭐

  • 模型:CodeLlama-7B/13B/34B-Instruct
  • 部署:支持Docker,可用Ollama或vLLM部署
  • 优势:Meta开源,专门针对代码优化,支持多种编程语言
  • Python调用:通过OpenAI兼容API或直接调用

3. DeepSeek Coder

推荐指数:⭐⭐⭐⭐

  • 模型:DeepSeek-Coder-6.7B/33B-Instruct
  • 部署:Docker + vLLM/Ollama
  • 优势:代码能力强,中文支持好,推理速度快
  • 特点:在代码理解和生成方面表现优异

4. StarCoder2

推荐指数:⭐⭐⭐

  • 模型:StarCoder2-7B/15B
  • 部署:Docker + Transformers/vLLM
  • 优势:BigCode项目,训练数据质量高
  • 特点:支持80+编程语言

刚开始选择大模型选择的是Meta的 CodeLlama-7B-Instruct,因为它的占用资源比较低,后面发现上下文长度偏低,不够用,如下图所示

决定升级模型参数到CodeLlama-13B-Instruct,CodeLlama-13B-Instruct模型支持的输入长度是4096 token,比CodeLlama-7B-Instruct模型多了一倍, 满足大多数场景。查了一下CodeLlama-13B-Instruct模型的硬件要求:至少32GB RAM,推荐RTX 4090或A100,公司的GPU是A100, 符合这一条,跑起来之后发现不支持中文,于是只能无奈放弃。

接着将大模型又换成DeepSeek-Coder-6.7B-Instruct, 发现同样的文件, 评审质量不如CodeLlama系列, 最后换成gpt-oss:20b,发现响应速度比CodeLlama和DeepSeek-Coder都要快一些, 而且代码评审质量也很高。

到底该使用Docker镜像部署大模型好,还是使用裸机部署大模型好,这个要看场景

对于大多数场景,推荐从Docker开始:

  1. 快速验证可行性
  2. 建立标准化流程
  3. 积累运维经验
  4. 根据性能瓶颈决定是否迁移到裸机

性能关键场景才考虑裸机:

  1. 已经明确性能瓶颈在容器层
  2. 有足够的运维能力
  3. 对性能的要求超过了便捷性
  4. 资源充足,追求极致优化

AI代码审核,相比性能,部署便捷性,环境一致性,扩展性可能才是更应关注的方面。所以这里选择了Docker镜像部署方案。

写一个制作大模型镜像的Jenkins任务,流程是:

  1. 设置环境变量和凭证。
  2. 进入大模型镜像 Dockerfile 所在目录
  3. 检查 远程 Docker Registry 是否已有该镜像, 如果存在,跳过构建和推送, 如果不存在,构建镜像、推送到 远程 Docker Registry、更新 Kubernetes deployment
js 复制代码
pipeline {
  agent { label 'jenkins-runner-1' }

  environment {
    REGISTRY_HOST = 'reg.xxx.com:9088'
    MODEL_NAME = 'gpt-oss-20b'
    CONTAINER_NAME = 'codellama-review'
  }

    stage('制作大模型服务镜像') {
      steps {
        script {
          try {
            withCredentials([
              usernamePassword(
                  credentialsId: 'REGISTRY',
                  usernameVariable: 'REGISTRY_USERNAME',
                  passwordVariable: 'REGISTRY_PASSWORD'
              )
            ]) {
              env.IMAGE_TAG_MODEL = "base-images/ai-code-review:${env.MODEL_NAME}"
              dir("common-tools/ai-code-review/${env.MODEL_NAME}") {
                def imageModelExists = sh(
                  script: "curl -s -o /dev/null -w \"%{http_code}\" -u $REGISTRY_USERNAME:$REGISTRY_PASSWORD https://$REGISTRY_HOST/v2/${IMAGE_TAG_MODEL.split(':')[0]}/manifests/${IMAGE_TAG_MODEL.split(':')[1]}",
                  returnStdout: true
                ).trim() == '200'
                // 如果镜像不存在,则构建并推送,并启动容器,运行镜像服务
                if (!imageModelExists) {
                  sh """
                    set -e
                    echo "\$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" -u "\$REGISTRY_USERNAME" --password-stdin
                    docker build -f Dockerfile -t $REGISTRY_HOST/$IMAGE_TAG_MODEL .
                    docker push $REGISTRY_HOST/$IMAGE_TAG_MODEL

                    kubectl  --kubeconfig=/etc/deploy/kubegpu set image deployment/$CONTAINER_NAME $CONTAINER_NAME=$REGISTRY_HOST/$IMAGE_TAG_MODEL
                    kubectl  --kubeconfig=/etc/deploy/kubegpu rollout status deployment/$CONTAINER_NAME
                  """
                }
              }
            }
          } catch (Exception e) {
            throw e
          }
        }
      }
    }
  }
}

构建镜像时使用的Dockerfile文件如图所示, 引用的基础镜像ollama-with-gpt-oss的制作方法参见笔者的这篇文章下载体验了一下OpenAI号称手机也能运行起来的开源大模型gpt-oss-20b

js 复制代码
FROM reg.xxx.com:9088/base-images/ai-code-review/ollama-with-gpt-oss:latest
CMD ["serve"]

第二步编写代码审查大模型程序

简单说一下AI代码审查主流程的执行逻辑:

step1 初始化与配置

  • 导入模块:requests, argparse, os, sys, time, base64, json, functools 等。

  • 重写 print 函数,使其自动刷新输出。

  • 配置 GitLab API

    1. GITLAB_API:GitLab API 基础地址。
    2. PRIVATE_TOKEN:私有 Token,用于访问 GitLab。
  • 配置 Ollama 本地模型

    1. OLLAMA_MODEL:模型名称(如 gpt-oss:20b)。
    2. OLLAMA_CLIENT:使用 OpenAI SDK 指向 Ollama 本地服务。
  • 定义系统提示 SYSTEM_PROMPT,用于引导模型执行代码审查。

step2: 解析启动命令传递的Gitlab合并事件参数

这些参数来自于gitlab webhook推送的合并事件,后面查询合并请求变更文件的时候要用到。使用 argparse 获取命令行参数:

  • --project-id:GitLab 项目 ID
  • --project-name:GitLab 项目名称
  • --mr-iid:Merge Request IID

step3 获取 MR 变更文件

  • 调用 GitLab API /merge_requests/{mr_iid}/changes 获取 MR 的文件变更列表。

  • 过滤文件, 下列这些文件不会被审查:

    1. 删除的文件。
    2. 自动生成文件。
    3. 仅重命名但内容未变的文件。
    4. 非允许的文件扩展名。
    5. 前端项目只检查在src/下的文件。
    6. Python项目只检查在 app/ 下的文件。
    7. 只修改空格、缩进、空行或注释的文件。
    8. 大文件(diff 超过 10 KB)被忽略。
  • 返回符合条件的文件列表。

step4 获取变更文件内容

  • 调用 GitLab API /merge_requests/{mr_iid} 获取源分支名,用于后续获取合并源分支改变文件完整内容。
  • 调用 GitLab API /repository/files/{file_path} 获取改动文件完整内容。
  • 文件内容为 Base64 编码,函数解码成 UTF-8 字符串返回。

step 5 调用 gpt-oss:20b 模型评审代码

  • 构建 messages

    1. 系统角色消息:包含 SYSTEM_PROMPT

    2. 用户角色消息:可选:上一次审查反馈。 当前待审查代码。

  • 调用 Ollama 本地模型接口 chat.completions.create 生成审查意见。

  • 返回模型输出文本。

step6 提交 MR 评论

  • 调用 GitLab API /merge_requests/{mr_iid}/notes 提交评论。
  • 将 AI 模型生成的审查结果作为 MR 评论发布。
python 复制代码
import requests
import argparse
import os
import sys
import time
import base64
from openai import OpenAI
import json
import functools

print = functools.partial(print, flush=True)

# ==== GitLab API 配置 ====
GITLAB_API = "https://git.xxx.com/api/v4"
PRIVATE_TOKEN = "gitlab-token"


# ==== Ollama 本地 API 配置 ====
OLLAMA_MODEL = "gpt-oss:20b"
OLLAMA_CLIENT = OpenAI(
    api_key="ollama",
    base_url="http://大模型服务对外暴露的IP:端口/v1"
)

SYSTEM_PROMPT = """
请你扮演一个资深的中文代码审查专家,
精通 Web 前端、Python 和 Java 编程语言,
请用简体中文帮我分析下面代码中可能存在的逻辑错误、
性能问题或不好的编码风格。

如果有问题,请用如下的格式输出审查结果:

##### 问题1
- 问题描述: xxx
- 修改建议: xxx

##### 问题2
- 问题描述: xxx
- 修改建议: xxx

...

要求:
- 问题描述和修改建议要简短,尽量控制在 100 字以内。
- 如果没有问题,请回复 "nice" 字样,不要输出任何多余的字符。
"""

# ==== GitLab API 封装 ====
def get_mr_changes(project_id, mr_iid):
    url = f"{GITLAB_API}/projects/{project_id}/merge_requests/{mr_iid}/changes"
    headers = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
    resp = requests.get(url, headers=headers)
    resp.raise_for_status()
    changes = resp.json()["changes"]

    # 允许审查的文件类型
    allowed_extensions = (
        '.vue', '.tsx', '.ts', '.js', '.css', '.less', '.scss', 
        '.py', '.pyi', '.ipynb', '.yaml', '.yml', '.ini', '.toml', '.json', '.env',
        '.java', '.kt', '.xml', '.properties', '.gradle'
    )

    def is_only_whitespace_changes(diff_text: str) -> bool:
        """判断 diff 是否只包含空格、缩进、空行或注释变动"""
        if not diff_text.strip():
            return True

        diff_lines = [
            line[1:]  # 去掉前缀 '+' 或 '-'
            for line in diff_text.splitlines()
            if line.startswith(('+', '-')) and not line.startswith(('+++', '---'))
        ]

        for line in diff_lines:
            stripped = line.strip()
            # 如果有非空、非注释的代码,则返回 False
            if stripped and not (stripped.startswith("//") or stripped.startswith("#") or stripped.startswith("/*") or stripped.startswith("*") or stripped.startswith("*/")):
                return False

        return True


    filtered_changes = []
    for change in changes:
        file_path = change.get("new_path") or change.get("old_path", "未知文件")
        normalized_path = file_path.replace("\\", "/")

        # 1. 删除文件
        if change.get("deleted_file", False):
            print(f"跳过删除文件: {file_path}")
            continue
        # 2. 自动生成文件
        if change.get("generated_file", False):
            print(f"跳过自动生成文件: {file_path}")
            continue
        # 3. 重命名但内容没变
        if change.get("renamed_file", False) and not change.get("diff", "").strip():
            print(f"跳过仅重命名无内容变动的文件: {file_path}")
            continue
        # 4. 非允许扩展名
        if not file_path.endswith(allowed_extensions):
            print(f"跳过非代码文件: {file_path}")
            continue
        # 5. 前端文件需在 src/ 下
        if file_path.endswith(('.ts', '.tsx', '.js', '.css', '.less', '.scss', '.vue')):
            if not normalized_path.startswith("src/"):
                print(f"跳过 src 目录外的前端文件: {file_path}")
                continue
        # 6. Python 文件需在 app/ 下
        elif file_path.endswith(('.py', '.pyi', '.ipynb')):
            if not normalized_path.startswith("app/"):
                print(f"跳过 app 目录外的 Python 文件: {file_path}")
                continue
        # 7. 只改空格/缩进的修改
        if is_only_whitespace_changes(change.get("diff", "")):
            print(f"跳过仅格式调整的文件: {file_path}")
            continue
        # 8. 忽略大文件,这里假设 diff 字符串长度超过 10KB 的文件被认为是大文件
        if change.get("diff") and len(change["diff"].encode("utf-8")) > 10 * 1024:
            print(f"跳过大文件(>{10}KB): {file_path}")
            continue


        filtered_changes.append(change)

    return filtered_changes


def get_mr_source_branch(project_id, mr_iid):
    url = f"{GITLAB_API}/projects/{project_id}/merge_requests/{mr_iid}"
    headers = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
    resp = requests.get(url, headers=headers)
    resp.raise_for_status()
    return resp.json()["source_branch"]

def get_file_content(project_id, file_path, ref):
    url = f"{GITLAB_API}/projects/{project_id}/repository/files/{requests.utils.quote(file_path, safe='')}"
    headers = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
    params = {"ref": ref}
    resp = requests.get(url, headers=headers, params=params)
    if resp.status_code == 200:
        file_info = resp.json()
        return base64.b64decode(file_info["content"]).decode("utf-8", errors="ignore")
    else:
        raise Exception(f"获取文件内容失败: {file_path} (status={resp.status_code})")

def post_mr_comment(project_id, mr_iid, body):
    url = f"{GITLAB_API}/projects/{project_id}/merge_requests/{mr_iid}/notes"
    headers = {"PRIVATE-TOKEN": PRIVATE_TOKEN, "Content-Type": "application/json"}
    resp = requests.post(url, headers=headers, json={"body": body})
    resp.raise_for_status()
    return resp.json()

# ==== 直接调用 Ollama 模型 ====
def call_ollama_model(code_text, last_review=None):
    messages = [{"role": "system", "content": SYSTEM_PROMPT.strip()}]
    if last_review:
        messages.append({
            "role": "user",
            "content": f"上一次代码审查的结果反馈是:\n{last_review}\n请根据这些反馈改进这次代码审查。"
        })
    messages.append({
        "role": "user",
        "content": f"以下是待评审代码内容: \n{code_text} \n请给出代码审查结果。"
    })

    # print("开始代码评审, messages 内容如下:")
    # print(json.dumps(messages, ensure_ascii=False, indent=2))

    try:
        response = OLLAMA_CLIENT.chat.completions.create(
            model=OLLAMA_MODEL,
            messages=messages,
            max_tokens=8192,
            temperature=0.3
        )
        # print("接口完整响应:", response)
        # print(f"评审意见...{response.choices[0].message.content}")
        return response.choices[0].message.content.strip()
    except Exception as e:
        print(f"调用模型异常: {e}")
        return f"模型响应失败: {e}"

# ==== 主流程 ====
def main():
    parser = argparse.ArgumentParser(description="AI 代码评审 for GitLab MR(Ollama直连版)")
    parser.add_argument("--project-id", required=True, help="GitLab 项目ID")
    parser.add_argument("--project-name", required=True, help="GitLab 项目名称")
    parser.add_argument("--mr-iid", required=True, help="Merge Request IID")
    args = parser.parse_args()

    if not PRIVATE_TOKEN:
        print("请设置环境变量 GITLAB_PRIVATE_TOKEN", file=sys.stderr)
        sys.exit(1)

    try:
        changes = get_mr_changes(args.project_id, args.mr_iid)
        source_branch = get_mr_source_branch(args.project_id, args.mr_iid)
    except Exception as e:
        print(f"获取信息失败: {e}", file=sys.stderr)
        sys.exit(1)

    if not changes:
        print("无变更内容,退出。")
        sys.exit(0)



    for change in changes:
        file_path = change.get("new_path", "未知文件")

        try:
            full_content = get_file_content(args.project_id, file_path, source_branch)
        except Exception as e:
            print(f"获取完整内容失败: {e}", file=sys.stderr)
            continue

        prompt = f"{file_path} 文件的完整代码内容如下:\n\n{full_content}"
        review = call_ollama_model(prompt)

        if not review.strip() or review.strip() == "nice" or review.startswith("模型响应失败"):
            continue

        comment_body = f"🔍 **AI 审查建议 - 文件 `{file_path}`**\n\n{review}"
        try:
            post_mr_comment(args.project_id, args.mr_iid, comment_body)
            print(f"✅ 已成功写入 `{file_path}` 的评审评论。")
            time.sleep(1)
        except Exception as e:
            print(f"写评论失败: {e}", file=sys.stderr)

    print("所有文件评审完成。")

if __name__ == "__main__":
    main()

这里重点说一下调用本地大模型的入参和响应字段含义,因为这一块知识平常接触的比较少。

调用本地大模型入参数说明:

参数 类型 必填 默认 说明
model str --- 模型名称,如 "gpt-oss:20b"
messages list[dict] --- 消息列表,每条消息为 { "role": "system/user/assistant", "content": "..." }
temperature float 1.0 控制随机性,0 越确定性,1 越自由
max_tokens int 2048 最大生成 token 数
top_p float 1.0 核采样概率阈值,通常和 temperature 配合使用
frequency_penalty float 0.0 对重复内容的惩罚系数
presence_penalty float 0.0 对新话题的奖励系数
stop str / list --- 生成停止符或字符串列表
stream bool False 是否启用流式输出
n int 1 返回的候选结果数量
logit_bias dict --- 对指定 token 概率加权,形式 {token_id: bias}

messages 字段说明

字段 类型 必填 说明
role str "system""user""assistant"
content str 消息内容文本

响应字段说明:

字段 类型 说明
id str 唯一请求 ID
object str 对象类型,通常 "chat.completion"
created int UNIX 时间戳
model str 使用的模型
usage.prompt_tokens int 输入 prompt token 数
usage.completion_tokens int 输出生成 token 数
usage.total_tokens int 总 token 数
choices list 生成的候选结果数组
choices[i].index int 候选序号
choices[i].message.role str 消息角色,通常 "assistant"
choices[i].message.content str 模型生成文本
choices[i].finish_reason str 生成结束原因,如 "stop""length"
  • 代码审查类任务:推荐 temperature=0.1~0.3,更严谨、更少幻想;
  • 自由生成任务:可以调高 temperaturetop_p

第三步 编写Jenkins Job运行大模型逻辑

Stage1:制作代码审查主流程镜像

目的:构建并推送 AI 代码审查主流程的 Docker 镜像。

执行流程:

  1. 使用 withCredentials 获取 Docker 仓库登录账号和密码。

  2. 设置镜像标签:REVIEW_IMAGE_TAG = "base-images/ai-code-review:review-${CODE_VERSION}"

  3. 进入项目目录 common-tools/ai-code-review/ai-review-main

  4. 检查镜像是否已存在:用 curl 请求 Docker Registry 的 manifests API,HTTP 返回码 200 表示镜像存在。

  5. 如果镜像不存在:(1) 登录 Docker Registry; (2) 构建镜像 Dockerfile.review; (3)推送镜像到仓库。

Stage 2:AI 本地大模型代码审查

目的:运行 AI 代码审查脚本对 MR 内容进行自动审查。

执行流程:

  1. 使用 docker.image(...).inside 启动容器环境,运行之前构建的 REVIEW_IMAGE_TAG 镜像。
  2. 在容器内执行脚本: 参数从 webhook JSON 中获取。
bash 复制代码
    cd /app
    python ai_review.py --project-id=xxx --project-name=xxx --mr-iid=xxx
  1. 获取脚本输出 result, 输出到 Jenkins 控制台。 4.简单检测错误:
js 复制代码
pipeline {
  agent { label 'jenkins-runner-1' }

  environment {
    REGISTRY_HOST = 'reg.xxx.com:9088'
    CONTAINER_NAME = 'codellama-review'
  }

    stage('制作代码审查主流程镜像') {
      steps {
        script {
          try {
            withCredentials([
              usernamePassword(
                  credentialsId: 'REGISTRY',
                  usernameVariable: 'REGISTRY_USERNAME',
                  passwordVariable: 'REGISTRY_PASSWORD'
              )
            ]) {
              env.REVIEW_IMAGE_TAG = "base-images/ai-code-review:review-${env.CODE_VERSION}"
              dir('common-tools/ai-code-review/ai-review-main') {
                def imageReviewExists = sh(
                  script: "curl -s -o /dev/null -w \"%{http_code}\" -u $REGISTRY_USERNAME:$REGISTRY_PASSWORD https://$REGISTRY_HOST/v2/${REVIEW_IMAGE_TAG.split(':')[0]}/manifests/${REVIEW_IMAGE_TAG.split(':')[1]}",
                  returnStdout: true
                ).trim() == '200'
                // 如果镜像不存在,则构建并推送
                if (!imageReviewExists) {
                  sh """
                    echo "\$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" -u "\$REGISTRY_USERNAME" --password-stdin
                    docker build -f Dockerfile.review -t $REGISTRY_HOST/$REVIEW_IMAGE_TAG .
                    docker push $REGISTRY_HOST/$REVIEW_IMAGE_TAG
                  """
                }
              }
            }
          } catch (Exception e) {
            throw e
          }
        }
      }
    }

    stage('AI本地大模型代码审查') {
      // when() {
      //   expression { return env.WEBHOOK_JSON_object_attributes_iid == '218' }
      // }
      steps {
        script {
          docker.image("${REGISTRY_HOST}/${REVIEW_IMAGE_TAG}").inside {
            script {
              def result = sh(script: """
                  cd /app
                  python ai_review.py \
                    --project-id=${WEBHOOK_JSON_project_id} \
                    --project-name=${WEBHOOK_JSON_object_attributes_source_name} \
                    --mr-iid=${WEBHOOK_JSON_object_attributes_iid}
              """, returnStdout: true).trim()

              echo "脚本输出:\n${result}"

              def safeResult = result ?: ''
              if (safeResult.contains('Traceback') || safeResult.contains('Error')) {
                error('AI Review 脚本执行异常,检测到错误信息')
              }
            }
          }
        }
      }
    }
  }
}

步骤1使用的Dockerfile.review定义如下所示:主要是安装Python,以及ai_review.py所用到的依赖,复制主文件到容器, 这里要注意一下书写顺序, 因为前三步不会变,第四步经常变化, 如果把第四步写在第三步前面,会造成每次ai_review.py内容有变更时,都重新下载Python项目依赖。

js 复制代码
# 基础镜像:Python 3.10 精简版
FROM python:3.10-slim

# 设置工作目录
WORKDIR /app

# 先安装依赖,利用缓存
RUN pip install --no-cache-dir openai requests

# 复制项目文件到镜像
COPY ai_review.py /app/

第四步 配置Webhook

要同时在Jenkins Job和Gitlab中配置webhook。Gitlab中的webhook会将各种git操作事件数据推送过来, Jenkins Job会对推送过来的事件进行监听处理

在Jenkins中设置webhook

重点配:

  • Post content parameters变量merge_state,WEBHOOK_JSON
  • Token变量,这里配置成项目名
  • Optional filter 对webhook事件进行过滤

在Gitlab中配置webhook

只所以要先在Jenkins Job中配置webhook,是因为在Gitlab中配置webhook需要的两个参数:url和Secret 令牌,来自于Jenkins Job中的设置。

  • url 填写 http://JENKINS_URL/generic-webhook-trigger/invoke
  • Secret 令牌 填写在Jenkins Job webhook中配置的token

第五步 运行测试

测试方法: 基于dev分支创建一个feat分支, 故意复制一个函数, 改个名字,在Gitlab 界面发起向dev分支的合并请求,看看大模型能否评审出代码重复。

结果令人满意,大模型果然检查出了重复的代码。我看了一下大模型的评审建议, 比大多数人工审查更专业,更细致。

最后

使用大模型做了一件能产生价值的事情, 内心很有充实感,这时间花的很值。如果你的公司也打算实现AI大模型代码审查功能, 那你看我的文章, 可算是找对人了,能让你少走一些弯路,尤其是大模型的选择与下载那一块。没事多逛逛掘金,开卷有益。

相关推荐
cver12311 分钟前
人脸情绪检测数据集-9,400 张图片 智能客服系统 在线教育平台 心理健康监测 人机交互优化 市场研究与广告 安全监控系统
人工智能·安全·yolo·计算机视觉·目标跟踪·机器人·人机交互
逾明19 分钟前
Electron自定义菜单栏及Mac最大化无效的问题解决
前端·electron
技术老金22 分钟前
LangGraph入门与避坑指南:从ReAct到复杂流程编排
人工智能·python
辰九九24 分钟前
Uncaught URIError: URI malformed 报错如何解决?
前端·javascript·浏览器
月亮慢慢圆24 分钟前
Echarts的基本使用(待更新)
前端
大佬喝可乐25 分钟前
卷积神经网络(CNN)全面解析
人工智能·神经网络·cnn
martinzh31 分钟前
上下文学习的神奇魔法:轻松理解AI如何无师自通
人工智能
芜青37 分钟前
实现文字在块元素中水平/垂直居中详解
前端·css·css3
useCallback41 分钟前
Elpis全栈项目总结
前端
Hcoco_me44 分钟前
【4】Transformers快速入门:自然语言模型 vs 统计语言模型
人工智能·语言模型·自然语言处理