Claude Code 这个名字,很多后端开发应该已经不陌生了。它是 Anthropic 推出的命令行 AI 编程助手,可以像结对编程伙伴一样,直接读取项目目录、生成代码、改文件、执行命令,甚至跑测试。而本地大模型,指的是把开源模型部署在自己电脑或内网服务器上,断网也能用,数据不出内网。把这两个组合起来,就构成了一个比较有意思的全栈开发工作流:用 Claude Code 作为对话和操作入口,用本地模型作为推理引擎,在私有、可控、按需付费(其实是按电费)的环境里完成项目开发。
这篇文章会把整套方案拆开讲清楚:从安装、配置、协议原理,到跑通一个完整的全栈项目,再到常见的坑和工程化建议。内容比较长,适合两类读者:一类是刚接触 AI 编程工具、想找一个隐私友好方案的开发者;另一类是已经在用 Claude Code 官方模型,但想了解如何接入本地模型做降级或离线开发的进阶用户。
1. 背景与核心概念
1.1 Claude Code 到底是什么
Claude Code 本质上是一个跑在终端里的 Agent(智能体)。它不是简单的“对话生成代码”工具,而是能感知当前项目上下文的编程代理。
举例来说,你在一个 Flask 项目目录里执行 claude "帮我给所有接口加上异常处理",它会:
- 扫描当前目录结构,识别技术栈。
- 自动读取相关源码文件。
- 判断哪些接口需要改。
- 直接修改代码,并提示你确认。
- 如果需要装依赖或跑测试,它还能执行 Shell 命令。
相比你在网页上复制粘贴代码,Claude Code 的竞争力在于“它真实地工作在你的项目上下文里”。它知道你项目的文件结构、依赖文件、现有代码风格,因此生成的代码贴合度更高。
不过要注意:Claude Code 本身只是一个客户端和调度框架,真正写代码的“大脑”在背后的模型。默认情况下,它连接的是 Anthropic 官方的 Claude 系列模型,需要 API Key 或相关订阅权限。如果希望使用本地模型,就需要把它的“后端”替换成你本地部署的推理服务,这就是本文的核心主题。
1.2 为什么要把本地大模型接入 Claude Code
本地大模型接入代码开发工具有几个现实诉求:
第一,数据安全。绝大多数公司的业务代码都是核心资产,不可能随意提交到外部 API 服务。本地部署模型之后,代码资产始终留在内网,适合金融、政企、医药等合规要求较高的环境。
第二,离线可用。在开发机、飞机上、没有稳定外网的环境里,本地模型依然可以工作。
第三,成本可控。官方 API 按 token 计费,高频调用、长上下文对话、反复调试时费用容易失控。本地模型只要硬件扛得住,调用次数基本不受限制。
第四,实验自由度。你可以随时换不同参数量、不同架构的模型,测试哪种模型在自己的业务场景下效果最好。
但也要客观认识局限:本地模型在代码生成质量、工具调用稳定性、长上下文理解方面,通常还达不到顶级闭源模型的水平。尤其面对复杂架构、跨多文件重构时,本地模型容易出现“理解不到位、动手打折扣”的情况。所以更合理的定位是:本地模型承担简单任务、私有任务、高频小任务;复杂任务保留给能力更强的大模型。这是一个组合策略。
1.3 准备掌握的关键能力
学完本文,你将能完成这些事:
- 在自己电脑上安装 Claude Code 命令行工具。
- 安装并启动 Ollama,拉取一个适合作代码任务的本地模型。
- 通过一层本地协议代理,把 Claude Code 的请求转发到本地模型。
- 用这套组合,完成一个 Flask + 原生前端的待办事项全栈项目,包括生成代码、运行调试、迭代功能。
- 理解常见报错和排查方式,知道如何在工程中安全地使用 AI 编程工具。
需要说明的是:AI 编程工具和模型版本迭代非常快,本文重点不是锁定某个版本号,而是把“可复用的方案思路”和“完整的操作链路”讲清楚。你阅读时使用的版本如果提示不同,以实际版本的帮助信息为准。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与版本说明
在开始之前,先明确整体技术组合。本文的示例环境如下:
| 组件 | 用途 | 备注 |
|---|---|---|
| Node.js 20+ | 安装和运行 Claude Code | 不同版本要求不同,过低会导致安装失败 |
| Claude Code CLI | AI 编程客户端 | 通过 npm 全局安装 |
| Python 3.10+ | 编写 Flask 示例项目 | 后端开发语言 |
| Ollama | 本地模型运行时 | 负责拉模型、起服务 |
| 本地代码模型 | 推理引擎 | 常见选择有 qwen2.5-coder 等 |
| LiteLLM 代理 | 协议转换层 | Claude Code 与 Ollama 之间做格式适配 |
版本以你安装时的最新稳定版为准。本文演示的是整套工作流,配置思路比具体版本号更重要。
2.1 安装 Node.js
Claude Code 是一个 npm 包,因此第一步是安装 Node.js。建议安装 Node.js 20 或更高版本。如果你本机已经有 nvm,推荐用 nvm 管理,方便切换版本。
安装完成后,验证版本:
bash复制node -v
npm -v
如果能正常输出版本号,说明 Node.js 环境没有问题。
2.2 安装 Claude Code
使用 npm 全局安装官方命令行包:
bash复制npm install -g @anthropic-ai/claude-code
安装完成后,运行:
bash复制claude --version
如果命令找不到,检查 npm 全局目录是否已加入 PATH。在 Windows 上通常会自动添加,macOS 和 Linux 有时需要手动配置。
首次运行 claude 时,它会进入交互式界面,并询问是否登录或配置 API 凭证。这里先不用急着登录,因为我们的目标是连接本地模型。
2.3 安装并启动 Ollama
Ollama 是一个极简的本地大模型运行工具,支持 Windows、macOS、Linux。它的特点是安装简单,一条命令就能拉起一个模型服务。
安装方式根据系统不同而不同,Linux 上常用脚本安装:
bash复制curl -fsSL https://ollama.com/install.sh | sh
Windows 用户直接去官网下载安装包即可。macOS 同理。
安装完成后,先启动服务:
bash复制ollama serve
在另一个终端窗口,拉取一个适合写代码的模型。以 qwen2.5-coder 为例:
bash复制ollama pull qwen2.5-coder:7b
实际拉取哪一个模型,取决于你的显存和任务复杂程度。7B 参数量的模型对 8GB 显存左右的机器比较友好;如果你显存足够,也可以尝试更大的参数版本。
拉取完成后,可以用命令验证模型可用:
bash复制ollama run qwen2.5-coder:7b "用 Python 写一个快速排序"
如果它能正常输出代码片段,说明 Ollama 和模型本身没有问题。
2.4 安装协议转换代理
默认情况下,Claude Code 向 Anthropic API 发出请求,协议形态是 Anthropic Messages 格式。而 Ollama 主要提供的是 OpenAI 兼容接口。两者之间的差异通常靠代理层解决,社区常用方案是 LiteLLM。
安装 LiteLLM 代理版本:
bash复制pip install "litellm[proxy]"
LiteLLM 可以读取一个 YAML 配置文件,作为入口服务启动,并把请求转发给本地 Ollama。我们会在下一节详细讲解配置。
3. 调用原理与配置拆解
3.1 整体调用链路
这套方案的数据流向可以用下面这个简图表示:
text复制Claude Code(终端交互)
↓
LiteLLM 本地代理 (127.0.0.1:4000)
↓
Ollama 本地服务 (127.0.0.1:11434)
↓
本地开源模型(qwen2.5-coder 等)
Claude Code 是面向用户的交互入口;LiteLLM 负责把 Claude Code 发出的请求翻译成 Ollama 能理解的格式;Ollama 真正负责加载模型和推理;模型生成的文本最终原路返回给 Claude Code,由 Claude Code 解析后决定下一步动作。
为什么要引入 LiteLLM,而不直接把 ANTHROPIC_BASE_URL 指向 Ollama?原因是协议不完全一致。Claude Code 内部默认走 Anthropic 的 /v1/messages 接口,携带的是 Anthropic 格式的 messages、system、tools 参数;而 Ollama 默认的兼容端点通常是 OpenAI 的 /v1/chat/completions 格式。两者在工具调用、系统提示、响应结构上有差异。直接对接容易出现请求失败、工具调用解析不了等问题。
社区有些网关或工具已经直接兼容了 Anthropic 格式,如果你使用的是这类工具,可以省去 LiteLLM。本文的示例选择 LiteLLM,是因为它部署简单、配置清晰,也更适合说明“协议转换”这一核心概念。
3.2 配置 LiteLLM 代理
新建一个配置文件 litellm-config.yaml:
yaml复制model_list:
- model_name: claude-local
litellm_params:
model: ollama_chat/qwen2.5-coder
api_base: http://localhost:11434
这里解释一下配置的关键参数:
model_name是 LiteLLM 对外暴露的模型名称,Claude Code 或后续工具通过这个名称来调用模型。model是后端实际使用的模型标识,ollama_chat/qwen2.5-coder表示通过 Ollama 的 chat 接口访问 qwen2.5-coder。api_base是 Ollama 服务地址,默认就在本机的 11434 端口。
启动代理:
bash复制litellm --config litellm-config.yaml --port 4000
如果看到类似 Server running on http://0.0.0.0:4000 的日志,说明代理启动成功。
需要特别说明的是:不同版本的 Claude Code 请求的默认模型名称可能不同。如果你的代理日志提示找不到对应模型,可以把 model_name 改成 Claude Code 实际请求的模型名,或者通过环境变量指定发送到代理的模型名。这一点在 6.2 的排查部分会再提到。
3.3 配置 Claude Code 环境变量
Claude Code 支持通过环境变量覆盖 API 地址和 Token。打开 shell 配置文件(macOS/Linux 为 ~/.bashrc 或 ~/.zshrc,Windows 可设置系统环境变量):
bash复制export ANTHROPIC_BASE_URL="http://127.0.0.1:4000"
export ANTHROPIC_AUTH_TOKEN="sk-local"
ANTHROPIC_BASE_URL:告诉 Claude Code,所有 API 请求都发到本机 4000 端口的 LiteLLM 代理,而不是 Anthropic 官方服务。ANTHROPIC_AUTH_TOKEN:代理要求的一个简单认证令牌,LiteLLM 默认并不强制校验,但 Claude Code 不会发送没有带 token 的请求,因此随意填一个本地占位值即可。
配置完成后,在项目目录里启动:
bash复制claude
如果一切正常,Claude Code 会连接本地代理并开始对话。你可以在终端里输入一个简单请求,比如:
text复制帮我写一个 Python 函数,接收两个整数并返回它们的和。
如果本地模型正常返回代码,说明整套链路已经打通。
3.4 Claude Code 的交互模型与工具机制
Claude Code 之所以适合全栈开发,是因为它不只是文本生成,而是一个能调用工具的 Agent。它的核心工具包括:
Read:读取项目文件,用于理解上下文。Edit:修改文件内容。Bash:执行终端命令,比如安装依赖、运行测试。Glob/Grep:搜索文件名和代码内容。
实际使用中,Claude Code 会通过“思考 → 调用工具 → 查看结果 → 继续思考”的循环来完成任务。你可以把它理解为:它不是一次性把代码全丢给你,而是先理解需求,再分步操作,每步操作都会经过你的确认或安全策略允许。
为了安全,初次使用时建议用偏保守的权限模式,例如先让 AI 只做规划,不直接改文件。具体参数以你当前版本 claude --help 的输出为准。下面是一个示例:
bash复制claude --permission-mode plan
在 plan 模式下,Claude Code 会先输出实施计划,而不是直接调工具改代码。确认计划后再切换到默认执行模式,能明显减少“AI 想改哪里就改哪里”的失控风险。
4. 完全实战:用 Claude Code 驱动本地模型开发全栈项目
理论讲完,下面进入重点的动手环节。我们要做一个待办事项管理工具(Todo App),技术栈为:
- 后端:Python Flask,提供 REST API。
- 数据存储:本地 JSON 文件。
- 前端:原生 HTML + JavaScript,页面静态加载,通过 fetch 调用后端接口。
选择这个项目,是因为它麻雀虽小、五脏俱全,足够演示“让 Claude Code 读项目、写代码、跑命令”的完整闭环。
4.1 第一步:先建项目目录和需求文档
很多新手喜欢一上来就让 AI 写整个项目。但实践证明,需求越模糊,AI 生成结果越不可控。更稳妥的做法是:先手动创建项目目录,把需求写清楚,再让 Claude Code 在这个上下文里工作。
bash复制mkdir -p todo-local/static
cd todo-local
创建 requirement.md 文件,内容为:
markdown复制# 待办事项工具需求说明
## 功能
1. 页面展示所有待办任务。
2. 用户可以输入新任务标题,提交后添加到列表。
3. 每条任务右侧有删除按钮,点击删除该任务。
4. 数据保存到本地 JSON 文件,服务重启后数据不丢失。
## 技术栈
- 后端:Python Flask
- 前端:原生 HTML + CSS + JavaScript
- 数据:JSON 文件持久化
## 接口设计
- GET /api/tasks:返回任务列表
- POST /api/tasks:新增任务,JSON body 中包含 title
- DELETE /api/tasks/<id>:删除指定任务
这份文档让 Claude Code 一进入项目就能理解你的真实需求,减少来回试探的次数。
4.2 启动 Claude Code,输入项目级提示
在 todo-local 目录里执行:
bash复制claude
当交互界面出现后,粘贴下面这段提示词:
text复制请阅读 requirement.md,按照需求实现一个待办事项工具。
要求:
1. 后端使用 Flask,文件放在 app.py,接口遵循 requirement.md 中的设计。
2. 前端页面放在 static/index.html,样式要简洁清晰。
3. 任务数据保存在 data/tasks.json,每次修改后要同步写入文件。
4. 完成代码后,帮我列出运行命令和验证方式。
5. 不要删除项目里已有文件。
这一步你可以观察 Claude Code 的工作过程:它会读取 requirement.md、检查目录结构,然后分步创建文件。在真正执行写操作或 Shell 命令前,通常会向你确认,按 y 批准,按 n 拒绝。
4.3 使用 /status 和 /context 观察执行状态
在交互界面中,有一些斜杠命令值得熟悉:
| 命令 | 作用 |
|---|---|
/status |
查看当前会话执行状态,包括已完成的动作 |
/context |
查看当前加载了哪些项目文件 |
/clear |
清空当前会话上下文 |
/help |
查看当前版本支持的全部命令 |
/permissions |
管理工具调用权限 |
当 Claude Code 长时间没有动作时,可以输入 /status 确认它是否卡在思考或者等待确认。如果上下文过于混乱,用 /clear 清空后重新输入需求,往往比继续纠错更高效。
4.4 生成结果示例:完整的全栈项目代码
下面是本项目最终应该具备的完整代码,供你对照 Claude Code 生成的结果。如果 AI 生成的版本有所差异,功能等价即可。
文件路径 requirements.txt
txt复制flask>=3.0
文件路径 app.py
python复制# 文件路径:todo-local/app.py
import json
import os
import uuid
from datetime import datetime
from flask import Flask, jsonify, request, send_from_directory
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(BASE_DIR, "data")
DATA_FILE = os.path.join(DATA_DIR, "tasks.json")
app = Flask(__name__, static_folder="static", static_url_path="")
def ensure_data_file():
"""确保数据目录和 JSON 文件存在。"""
os.makedirs(DATA_DIR, exist_ok=True)
if not os.path.exists(DATA_FILE):
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump([], f, ensure_ascii=False, indent=2)
def read_tasks():
"""读取全部任务。"""
ensure_data_file()
with open(DATA_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def write_tasks(tasks):
"""覆盖写入全部任务。"""
ensure_data_file()
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(tasks, f, ensure_ascii=False, indent=2)
@app.get("/")
def index():
"""返回前端页面。"""
return send_from_directory(app.static_folder, "index.html")
@app.get("/api/tasks")
def list_tasks():
"""查询任务列表。"""
return jsonify(read_tasks())
@app.post("/api/tasks")
def add_task():
"""新建任务。"""
payload = request.get_json(force=True, silent=True) or {}
title = (payload.get("title") or "").strip()
if not title:
return jsonify({"error": "任务标题不能为空"}), 400
tasks = read_tasks()
new_task = {
"id": uuid.uuid4().hex,
"title": title,
"done": False,
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}
tasks.append(new_task)
write_tasks(tasks)
return jsonify(new_task), 201
@app.delete("/api/tasks/<task_id>")
def delete_task(task_id):
"""删除指定任务。"""
tasks = read_tasks()
remaining = [task for task in tasks if task["id"] != task_id]
if len(remaining) == len(tasks):
return jsonify({"error": "任务不存在"}), 404
write_tasks(remaining)
return jsonify({"ok": True})
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000, debug=True)
这段代码有两个容易出错但又很关键的细节:
第一,写 JSON 文件时使用 encoding="utf-8",并且 ensure_ascii=False。如果不这样做,中文任务标题会变成 \uXXXX 形式,虽然功能没错,但肉眼排查数据时非常痛苦。
第二,static_folder="static" 和 static_url_path="" 配合,才能让根路径 / 直接返回静态目录下的 index.html。如果省略第二个参数,Flask 通常会默认以 /static 作为静态路由前缀,根路径反而不容易直接访问。
文件路径 static/index.html
html复制<!-- 文件路径:todo-local/static/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>本地待办事项</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
background: #f5f6fa;
display: flex;
justify-content: center;
padding: 48px 16px;
}
.container {
width: 100%;
max-width: 560px;
background: #fff;
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}
h1 {
font-size: 20px;
margin: 0 0 16px;
}
.form-row {
display: flex;
gap: 8px;
margin-bottom: 16px;
}
input {
flex: 1;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
}
button {
padding: 8px 16px;
border: none;
border-radius: 6px;
background: #2563eb;
color: #fff;
cursor: pointer;
}
button:hover {
background: #1d4ed8;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
li {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
li:last-child {
border-bottom: none;
}
.delete-btn {
background: #ef4444;
}
.empty {
text-align: center;
color: #999;
padding: 24px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>待办事项</h1>
<div class="form-row">
<input id="task-input" type="text" placeholder="输入新的任务..." />
<button id="add-btn">添加</button>
</div>
<ul id="task-list"></ul>
<div id="empty-tip" class="empty">暂无任务</div>
</div>
<script>
const taskList = document.getElementById("task-list");
const emptyTip = document.getElementById("empty-tip");
const taskInput = document.getElementById("task-input");
const addBtn = document.getElementById("add-btn");
async function loadTasks() {
const res = await fetch("/api/tasks");
const tasks = await res.json();
taskList.innerHTML = "";
emptyTip.style.display = tasks.length ? "none" : "block";
tasks.forEach((task) => {
const li = document.createElement("li");
li.textContent = task.title;
const del = document.createElement("button");
del.className = "delete-btn";
del.textContent = "删除";
del.addEventListener("click", async () => {
await fetch(`/api/tasks/${task.id}`, { method: "DELETE" });
loadTasks();
});
li.appendChild(del);
taskList.appendChild(li);
});
}
async function addTask() {
const title = taskInput.value.trim();
if (!title) return;
await fetch("/api/tasks", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title }),
});
taskInput.value = "";
loadTasks();
}
addBtn.addEventListener("click", addTask);
taskInput.addEventListener("keydown", (e) => {
if (e.key === "Enter") addTask();
});
loadTasks();
</script>
</body>
</html>
前端代码非常简单:页面加载时请求 GET /api/tasks,把任务渲染成列表;点击添加按钮时发送 POST /api/tasks;点击删除按钮时发送 DELETE /api/tasks/<id>。因为后端返回的就是 JSON,前端几乎没有转换成本。
文件路径 claude.md
Claude Code 支持读取项目中的记忆文件,通常命名为 claude.md 或 CLAUDE.md。这个文件可以帮助它在新会话里快速恢复对项目的理解。
示例内容:
markdown复制# 项目约定
1. 后端入口文件为 app.py,使用 Flask。
2. 前端静态文件位于 static 目录,直接通过 Flask 静态路由服务。
3. 数据文件为 data/tasks.json,读写时必须使用 utf-8 编码。
4. 新增功能时,优先复用现有接口和数据结构,不要随意改名。
有了这个文件,之后每次打开 Claude Code,它都能自动加载项目约定,减少重复说明。
4.5 运行与验证
依赖安装:
bash复制pip install -r requirements.txt
启动服务:
bash复制python app.py
先用 curl 验证接口:
bash复制# 查询任务列表,初始应为空数组
curl http://127.0.0.1:5000/api/tasks
# 新增一条任务
curl -X POST http://127.0.0.1:5000/api/tasks \
-H "Content-Type: application/json" \
-d '{"title": "学习 Claude Code"}'
# 再次查询
curl http://127.0.0.1:5000/api/tasks
预期输出类似:
json复制[
{
"id": "9b8aff3b0c4544faa0c0d5d4821d2f1b",
"title": "学习 Claude Code",
"done": false,
"created_at": "2025-01-01 10:00:00"
}
]
最后,浏览器打开 http://127.0.0.1:5000,你应该能看到前端页面内容。新增和删除任务后,页面会即时更新,数据也会写入 data/tasks.json。
4.6 用 Claude Code 继续迭代
项目跑通之后,继续实战很有价值。你可以在 Claude Code 里输入类似这样的提示词:
text复制现在给页面增加一个“统计未完成数量”的功能,显示在标题下方。
或者:
text复制请优化 delete_task 接口,改成批量删除:支持接收 id 数组,一次删除多个任务。
每次迭代时,Claude Code 都会基于 claude.md 中的项目约定,结合当前代码结构修改代码。你需要做的是认真 review 它改动的文件,尤其是在涉及数据删除、覆盖写入时,先确认改动范围再放行。
5. 常见问题与排查思路
本地大模型加 Claude Code 的组合,最让人头疼的是问题层叠:工具没跑通、模型不输出、调用被断。下面整理一套高频排错表。
| 问题现象 | 可能原因 | 解决思路 |
|---|---|---|
| Claude Code 启动后一直无响应 | LiteLLM 代理没启动,或端口错误 | 先启动 litellm --config xxx.yaml --port 4000,再启动 claude |
| 请求发到代理,但返回 404 | LiteLLM 中没有匹配的模型名 | 查看代理日志,找出 Claude Code 请求的模型名,把它加到 model_name |
| Ollama 已启动,但代理代理报模型不存在 | 模型名写错或模型未拉取 | 执行 ollama list 查看可用模型,修正 YAML 中的模型名 |
| 输出代码质量明显差 | 本地模型太小或任务太复杂 | 换更大参数的代码模型,或拆分任务,让 AI 一次只做一个改动 |
| 工具调用经常中断 | 本地模型不支持可靠工具调用 | 优先选择工具调用能力强的模型;减少长链路任务 |
| 中文显示为乱码 | 文件编码或 JSON 写入参数问题 | 统一使用 UTF-8 编码,写文件时设置 ensure_ascii=False |
| 执行 Shell 命令被拒 | Claude Code 权限策略限制 | 查看 /permissions,按需授权;不要轻易开启完全跳过权限模式 |
| npm 安装 Claude Code 失败 | Node 版本过低 | 使用 nvm 安装 Node.js 20+ 版本后重试 |
5.1 排查顺序建议
遇到问题时,不要盲目改配置,按下面顺序排查:
- 先确认链路最后端:Ollama 能否完整输出一个代码片段。可以用
ollama run <模型名>单独测试。 - 再确认代理层:LiteLLM 日志里有没有收到 Claude Code 的请求。关注日志中的模型名、路径、错误码。
- 最后确认客户端:Claude Code 的启动目录是否正确、环境变量是否在当前 shell 生效。可以用
echo $ANTHROPIC_BASE_URL查看。
这个顺序可以帮你快速定位问题出在哪一层,而不是在客户端和模型之间来回折腾。
5.2 关于“模型名不匹配”
在本地模型接入时,最常见的隐藏坑是请求模型名不匹配。Claude Code 在连接官方 API 时,会在请求体里带上一个官方模型名。转到本地代理后,如果 YAML 配置中的 model_name 和 Claude Code 实际发送的模型名不一致,LiteLLM 就不知道把请求路由给谁。
解决办法有两种:
- 在 LiteLLM 配置中,把
model_name改成 Claude Code 实际发送的那个名字。 - 如果你的代理工具支持参数改写,也可以把请求体中的 model 改成目标本地模型名。
这类问题没有统一答案,因为不同版本的 Claude Code 默认请求名不同。关键在于查看代理日志或请求日志,确认实际到达的模型名。
6. 最佳实践与工程建议
6.1 先写需求文档,再写提示词
Claude Code 的能力上限不取决于它有多聪明,而取决于你给它的上下文有多清晰。强烈建议每个项目根目录都放一份 requirement.md,任务、接口、边界写在纸面上。你给它的提示词,只是在把这份人话翻译成任务指令。有了文档,即使换一个新会话、换一个模型,AI 依然能快速进入状态。
6.2 把 claude.md 当作项目交接档案
这个文件是给 AI 看的项目档案,同时也是团队协作的好习惯。里面可以写编码风格、目录约定、安全注意事项。它不要求长篇大论,但要有针对性。每当你发现 Claude Code 反复在同一个问题上出错,就把它写进 claude.md,下个会话它就会提前知道。
6.3 权限最小化,尤其是写操作和 Shell 命令
默认情况下,Claude Code 在写文件和执行命令前会征求确认。千万不要图省事开启“跳过所有权限确认”的模式。尤其在生产环境或团队共享代码库中,AI 误删文件、误跑命令的后果是真实存在的。
建议在项目启动阶段使用 plan 权限模式,让 AI 先输出计划;确认后再进入执行阶段。对 Bash、Edit、Write 类工具,按需放行,而不是一次全开。
6.4 每次让 AI 改代码前,先提交一次 Git
这条建议适用于所有 AI 编程工具。在开始一次改动前,先把当前工作区提交到一个干净的 commit。这样 AI 改坏代码时,你随时可以通过 git checkout . 还原。AI 生成的代码不是你写的,你对它越没有把握,就越要给自己留后路。
6.5 本地模型选型要看任务类型
代码任务不要盲目选择通用大模型。目前更适合代码生成的模型包括 qwen2.5-coder 系列、codeqwen 系列等。在选型时可以遵循一个规律:优先选择在代码能力评测中表现好的模型,而不是参数最大的模型。参数越大,推理越慢,对显存要求越高,反而影响实际开发体验。
如果你需要处理的是中文需求分析、文档总结,可以再准备一个通用模型;需要写代码时切回代码模型。Ollama 支持同时管理多个模型,切换成本并不高。
6.6 合理划分本地模型和官方模型的职责
基于当前模型能力差异,一个务实的做法是:
- 简单机械任务(生成 CRUD 接口、写单元测试、格式化代码):优先本地模型。
- 复杂重构(跨文件拆模块、修改核心架构、解释陌生框架源码):交给能力更强的外部大模型或人工 review。
- 含敏感信息的代码(密钥、内网地址、未公开算法):只走本地模型,绝不出内网。
这个策略能同时兼顾隐私、成本和代码质量。
6.7 把异常处理写进提示词
让 AI 写代码时,建议在提示词里明确包含“错误处理和边界条件”。很多本地模型默认只会生成“最顺利路径”的代码,一旦输入为空、文件不存在、接口异常,程序就直接崩了。你可以在 claude.md 中约定:
markdown复制所有接口必须处理参数为空、数据不存在、文件读写异常三类情况。
这样做比每次在提示词里临时重复说明要稳定得多。
6.8 善用日志和测试闭环
不要只让 AI 写业务代码,更应该让它写工具验证自身。比如 Flask 项目可以提示:
text复制为 app.py 的关键接口增加 pytest 测试用例。
AI 生成测试用例的过程,也是它重新审视代码逻辑的过程。本地模型往往能发现自己在实现时遗漏的边界条件,这是一种低成本的自检方式。
7. 总结与下一步学习路线
这套方案的核心思路可以浓缩成一个公式:交互入口统一用 Claude Code,模型推理按需切换,敏感任务走本地,复杂任务走外部。掌握了这套链路后,换模型、换网关、换业务,都只是配置层的调整,而不必推翻整个工作方式。
下一步你可以从三个方向继续深入:
第一个方向是理解工具调用机制。Claude Code 的 Agent 能力依赖模型对 function calling 的把握。可以单独做实验:让本地模型反复完成“读取文件 → 修改变量 → 写入文件”的任务,观察不同模型在工具调用上的稳定性。
第二个方向是完善项目的工程化能力。目前我们用的是 JSON 文件存储,但在真实项目中建议换成 SQLite 或 PostgreSQL。让 Claude Code 带着迁移任务去改代码,是一个很好的压力测试。
第三个方向是学习和备份你自己的一套高效提示词。每个团队都存在高频复用需求:生成接口测试、补充日志、翻译报错、解释配置。把它们沉淀成 Prompt 模板,Claude Code 的价值会进一步放大。
实操中最值得记住的一条经验是:AI 编程工具不是用来替代你写代码的,而是用来减少重复劳动、帮你快速搭出骨架和方案。真正决定工程质量的核心,依然是需求拆解、代码 Review、权限控制和测试兜底。本地大模型接入 Claude Code 后,你获得了隐私和成本上的主动权,但同时也要承担更多调试和评估责任。
如果这篇文章对你有帮助,可以收藏备用。后面我也会继续更新关于本地模型评估、工具调用稳定性和全栈工程落地的内容,欢迎在评论区交流你踩过的坑。
