Claude API 实战教程:从入门到完整项目



Claude API 实战教程:从入门到完整项目


摘要

Claude 是 Anthropic 提供的大语言模型系列,支持网页对话、API 调用和终端编程助手 Claude Code。对新手来说,最容易上手的路线是先用网页端熟悉交互方式,再学习 Python SDK 调用 API,最后把 Claude Code 接入本地开发环境。

本教程按"从零开始 → 第一次调用 → 典型场景 → 进阶工作流 → 报错排查 → 安全合规"组织。重点解决国内开发者常见问题:账号注册、API Key 管理、网络与代理、模型选择、上下文管理、流式输出、Claude Code 配置、成本控制与伦理边界。


目录

一、导读:为什么选择 Claude,以及本教程适合谁

二、运行环境准备与账号快速配置

 2.1 网页端、API 与 Claude Code 三条路线

 2.2 国内使用环境与合规前提

 2.3 账号注册与访问方式

 2.4 运行环境与工具安装

三、核心概念解析与适用场景

 3.1 模型、Token 与上下文窗口

 3.2 System Prompt、Messages 与 Temperature

 3.3 流式输出、工具调用与多模态

 3.4 典型适用场景

四、API 密钥获取与安全存储方法

 4.1 官方控制台获取 API Key

 4.2 国内开发者可选的中转/聚合方案

 4.3 环境变量与 .env 文件管理

 4.4 密钥轮换、权限隔离与审计

五、基础对话调用代码实现步骤

 5.1 安装 Anthropic Python SDK

 5.2 第一次 API 调用

 5.3 使用 System Prompt 设定角色

 5.4 流式输出

 5.5 多轮对话与上下文管理

 5.6 JSON 结构化输出

六、完整实战案例:构建智能助手

 6.1 案例目标与系统架构

 6.2 项目结构

 6.3 配置文件

 6.4 客户端封装

 6.5 对话引擎

 6.6 智能助手主程序

 6.7 运行方式

七、参数调优技巧与输出效果提升

 7.1 模型选择策略

 7.2 max_tokens 与输出长度控制

 7.3 Temperature、Top-P 与稳定性

 7.4 长上下文与 Prompt 结构优化

 7.5 成本控制与缓存策略

八、常见报错信息分析与排查方案

 8.1 鉴权类错误

 8.2 限流与配额类错误

 8.3 请求格式类错误

 8.4 网络与连接类问题

 8.5 Claude Code 常见报错

九、上下文长度限制与应对策略

 9.1 上下文窗口的真实含义

 9.2 上下文膨胀与质量退化

 9.3 压缩、摘要与状态对象

 9.4 RAG 与检索增强

 9.5 Claude Code 长会话管理

十、本地开发集成与工作流优化

 10.1 VS Code / 终端集成 Claude Code

 10.2 与 Git、文件系统和知识库联动

 10.3 日志、重试与监控

 10.4 效率提升:快捷键、别名与插件

十一、安全使用规范与伦理注意事项

 11.1 密钥安全

 11.2 数据隐私与合规

 11.3 生成内容伦理边界

 11.4 生产环境风险控制

十二、实战案例复盘:完整工作流演示

十三、总结

十四、详细资料与延伸阅读

附录 A:环境变量清单

附录 B:常用模型标识符参考

附录 C:完整项目代码清单

附录 D:常见问题速查表


一、导读:为什么选择 Claude,以及本教程适合谁

Claude 适合以下几类用户:

  • 零基础用户:想先用网页端体验 AI 对话,再逐步接触 API。
  • Python 开发者:希望用官方 SDK 快速接入大模型能力。
  • AI Agent 与工作流创作者:需要把 Claude 接入本地工具链、知识库或自动化流程。
  • 内容创作者:希望用 AI 辅助短剧脚本、小说大纲、分镜提示词和角色设定。
  • 终端开发者:希望使用 Claude Code 在项目中完成代码生成、重构、文档梳理和调试辅助。

本教程不会只讲概念,而是尽量提供可复制的代码、可执行的命令和可落地的排错步骤。


二、运行环境准备与账号快速配置

2.1 网页端、API 与 Claude Code 三条路线

路线 适合人群 优点 注意事项
网页端 claude.ai 新手、内容创作者 无需编程,直接对话 有使用限额,地区可用性可能受限
Claude API 开发者、Agent 工作流 可编程、可集成、可批量调用 需要 API Key 和计费管理
Claude Code 终端开发者 贴近代码库,适合重构、调试 需要配置环境、网络和权限

网页端适合快速体验;API 适合构建应用;Claude Code 适合在本地项目中持续协作。

2.2 国内使用环境与合规前提

在国内使用 Claude 时,常见限制包括:

  • 网页端和官方 API 端点可能无法直接访问;
  • 注册可能需要海外邮箱或海外手机号;
  • 官方付费订阅通常依赖境外支付方式;
  • 企业网络、校园网或代理配置可能影响连接稳定性。

建议优先遵守以下原则:

  1. 不绕过法律法规或平台服务条款;
  2. 不使用来路不明的账号、代充或共享密钥;
  3. 企业场景优先使用合规的 API 渠道;
  4. 生产环境避免将密钥写入代码仓库。

2.3 账号注册与访问方式

2.3.1 网页端注册

官方网页端入口通常为 claude.ai。注册方式可能包括 Google 账号登录或邮箱注册。国内用户常遇到手机号验证、地区限制或支付绑定问题。如果无法完成注册,可考虑通过合规的第三方 API 服务商间接使用模型能力,而不是强行使用非官方接码或违规渠道。

2.3.2 API 控制台注册

开发者可访问 Anthropic 控制台创建 API Key。通常需要完成邮箱验证,并绑定支付方式进行预充值。API Key 创建后通常只显示一次,应立即保存。

2.3.3 Claude Code 安装

Claude Code 是终端原生的 AI 编程助手。常见安装方式包括 npm 全局安装或官方安装脚本。例如:

bash 复制代码
# 方式一:npm 全局安装
npm install -g @anthropic-ai/claude-code

# 验证安装
claude --version

如果安装后提示 command not found,需要检查 PATH 环境变量。首次运行时通常需要进行 OAuth 授权。

2.4 运行环境与工具安装

推荐准备以下环境:

  • Python 3.9 或更高版本;
  • pip 包管理工具;
  • 文本编辑器或 VS Code;
  • 终端环境:macOS、Linux 或 Windows PowerShell;
  • Git,用于版本管理;
  • 可选:Node.js,用于安装 Claude Code。

创建虚拟环境可以避免依赖冲突:

bash 复制代码
# 创建虚拟环境
python -m venv venv

# Windows 激活
venv\Scripts\activate

# macOS / Linux 激活
source venv/bin/activate

安装核心依赖:

bash 复制代码
pip install anthropic python-dotenv

anthropic 是官方 Python SDK;python-dotenv 用于从 .env 文件加载环境变量。


三、核心概念解析与适用场景

3.1 模型、Token 与上下文窗口

模型:Claude 提供多个模型系列,不同模型在推理能力、速度、成本和上下文窗口上存在差异。

Token:Token 是模型处理文本的基本单位。中文、英文、代码和标点都会占用 Token。通常 Token 数不完全等于字数。

上下文窗口:上下文窗口指模型在一次请求中能"看到"的最大 Token 数,包括系统提示、历史消息、上传文件和模型输出。窗口越大,不代表每次都应塞满内容;过多无关内容会降低响应质量并增加成本。

3.2 System Prompt、Messages 与 Temperature

概念 作用 使用建议
system 设定角色、规则、输出格式 放在对话顶层,避免频繁改动
messages 用户与助手的对话历史 保持清晰角色轮换
temperature 控制输出随机性 代码/结构化任务用较低值
max_tokens 限制输出长度 防止过长输出和成本失控

System Prompt 相当于"全局规则",适合写角色、边界、输出格式和禁止事项。Messages 是具体对话内容。Temperature 越高,输出越多样;越低,输出越稳定。

3.3 流式输出、工具调用与多模态

流式输出:适合长文本、代码生成和实时交互,可以让用户更快看到结果。

工具调用:模型可以按约定格式请求调用外部工具,例如读取文件、查询数据库或调用搜索接口。

多模态:部分模型支持图片、文档等输入,但具体支持情况取决于模型版本和平台配置。

3.4 典型适用场景

  • 代码生成、重构、单元测试;
  • 技术文档、API 文档、README 编写;
  • 短剧大纲、角色卡、分镜提示词;
  • 小说设定、章节扩写、风格润色;
  • AI Agent 规划、工具路由、工作流编排;
  • 知识库问答、日志分析、错误排查;
  • 终端编程辅助,如 Claude Code 项目级重构。

四、API 密钥获取与安全存储方法

4.1 官方控制台获取 API Key

  1. 访问 Anthropic 控制台;
  2. 注册或登录账号;
  3. 进入 API Keys 页面;
  4. 创建新密钥并命名;
  5. 复制密钥并保存到密码管理器;
  6. 绑定支付方式并确认余额。

注意:

  • API Key 通常只显示一次;
  • 不要截图发到公开群聊;
  • 不要写入 GitHub、GitLab 等公开仓库;
  • 不要硬编码在源码中。

4.2 国内开发者可选的中转/聚合方案

如果无法直接使用官方 API,可以考虑支持 Anthropic 协议或 OpenAI 兼容接口的合规中转服务。这类服务通常提供:

  • 国内直连;
  • 人民币充值;
  • 统一 API Key 管理;
  • 多模型路由。

使用中转服务时,需要确认其协议兼容性、计费方式、日志策略和合规声明。生产环境不建议依赖来源不明的免费 Key。

4.3 环境变量与 .env 文件管理

推荐在项目根目录创建 .env 文件:

ini 复制代码
# .env 文件
ANTHROPIC_API_KEY=sk-ant-你的密钥
ANTHROPIC_BASE_URL=

如果使用中转服务,可能需要设置自定义 base_url。不同服务商字段名称可能不同,应以对应文档为准。

同时在 .gitignore 中添加:

gitignore 复制代码
.env

Python 中加载环境变量:

python 复制代码
from dotenv import load_dotenv
import os

# 从 .env 文件加载环境变量
load_dotenv()

# 从环境变量中读取 API Key
api_key = os.getenv("ANTHROPIC_API_KEY")

if not api_key:
    raise RuntimeError("未找到 ANTHROPIC_API_KEY,请检查 .env 文件或环境变量。")

4.4 密钥轮换、权限隔离与审计

生产环境建议:

  • 为开发、测试、生产分别创建不同 Key;
  • 定期轮换密钥;
  • 限制单个 Key 的用量上限;
  • 记录每次调用的模型、Token 数和用途;
  • 发现泄露后立即撤销并重新生成。

五、基础对话调用代码实现步骤

5.1 安装 Anthropic Python SDK

bash 复制代码
pip install anthropic

确认安装成功:

python 复制代码
import anthropic
print(anthropic.__version__)

五、基础对话调用代码实现步骤(续)

5.2 第一次 API 调用(修正版)

上一版中模型标识符使用了简写,这里按 2026 年官方实际命名规范修正。Claude 模型标识符需使用完整版本字符串,例如 claude-sonnet-4-20250514。

python 复制代码
import os
import anthropic
from dotenv import load_dotenv

# 加载 .env 文件中的环境变量
load_dotenv()

# 从环境变量读取 API Key,避免硬编码
api_key = os.getenv("ANTHROPIC_API_KEY")

if not api_key:
    raise RuntimeError("缺少 ANTHROPIC_API_KEY,请在 .env 文件中配置。")

# 创建 Anthropic 客户端
# timeout 元组格式:(连接超时秒数, 读取超时秒数)
# 国内网络建议适当调大读取超时,避免长文本生成被误判超时
client = anthropic.Anthropic(
    api_key=api_key,
    timeout=(10.0, 60.0),
    max_retries=2  # 内置重试次数,对 429/5xx 自动退避重试
)

# 发起第一次对话
response = client.messages.create(
    model="claude-sonnet-4-20250514",  # 完整模型标识符
    max_tokens=512,                     # max_tokens 是 Claude API 必填参数
    messages=[
        {
            "role": "user",
            "content": "请用一句话解释什么是上下文窗口。"
        }
    ]
)

# 打印模型返回的文本
print(response.content[0].text)

# 查看 Token 消耗,用于成本监控
print(f"输入 Token: {response.usage.input_tokens}, 输出 Token: {response.usage.output_tokens}")

5.4 流式输出(修正版)

Claude SDK 流式输出的事件类型为 content_block_delta,需按此类型过滤增量文本。

python 复制代码
stream = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": "用 Python 写一个快速排序函数,并添加类型注解和注释。"
        }
    ],
    stream=True  # 开启流式输出
)

# 累积完整文本,便于后续使用
full_text = ""
for event in stream:
    # 流式事件中,增量文本在 content_block_delta 类型的 delta.text 中
    if event.type == "content_block_delta":
        print(event.delta.text, end="", flush=True)
        full_text += event.delta.text

print()  # 输出结束后换行
print(f"完整输出长度: {len(full_text)} 字符")

六、完整实战案例:构建智能助手(续)

6.4 客户端封装(完整版)

client.py:

python 复制代码
import anthropic
from config import API_KEY, DEFAULT_MODEL, DEFAULT_MAX_TOKENS


class ClaudeClient:
    """
    Claude API 客户端封装。
    负责创建 Anthropic 客户端,并提供统一的调用方法。
    """

    def __init__(self, api_key: str | None = None, model: str | None = None):
        """
        初始化客户端。
        :param api_key: API 密钥,默认从 config 读取
        :param model: 默认模型名称,默认从 config 读取
        """
        if not api_key:
            if not API_KEY:
                raise RuntimeError("API Key 未配置,请设置 ANTHROPIC_API_KEY。")
            api_key = API_KEY

        self.model = model or DEFAULT_MODEL

        # 创建 Anthropic 客户端
        # timeout: (连接超时, 读取超时),国内网络建议读取超时设大一些
        # max_retries: 对 429 限流和 5xx 服务错误自动退避重试
        self.client = anthropic.Anthropic(
            api_key=api_key,
            timeout=(10.0, 60.0),
            max_retries=2
        )

    def ask(
        self,
        prompt: str,
        system: str | None = None,
        max_tokens: int | None = None,
        temperature: float = 0.7,
        stream: bool = False,
    ) -> str:
        """
        单次对话调用。
        :param prompt: 用户输入
        :param system: 系统提示词,用于设定角色和规则
        :param max_tokens: 最大输出 Token 数
        :param temperature: 温度参数,0.0~1.0,越低越稳定
        :param stream: 是否流式输出
        :return: 模型回复文本
        """
        messages = [{"role": "user", "content": prompt}]

        if stream:
            return self._ask_stream(messages, system, max_tokens, temperature)
        else:
            return self._ask_normal(messages, system, max_tokens, temperature)

    def chat(
        self,
        messages: list[dict],
        system: str | None = None,
        max_tokens: int | None = None,
        temperature: float = 0.7,
        stream: bool = False,
    ) -> str:
        """
        多轮对话调用。
        :param messages: 消息列表,格式 [{"role": "user/assistant", "content": "..."}]
        :param system: 系统提示词
        :param max_tokens: 最大输出 Token 数
        :param temperature: 温度参数
        :param stream: 是否流式输出
        :return: 模型回复文本
        """
        if stream:
            return self._ask_stream(messages, system, max_tokens, temperature)
        else:
            return self._ask_normal(messages, system, max_tokens, temperature)

    def _ask_normal(
        self,
        messages: list[dict],
        system: str | None,
        max_tokens: int | None,
        temperature: float,
    ) -> str:
        """非流式调用"""
        response = self.client.messages.create(
            model=self.model,
            max_tokens=max_tokens or DEFAULT_MAX_TOKENS,
            system=system,
            messages=messages,
            temperature=temperature,
        )
        # 记录 Token 消耗,便于成本监控
        if hasattr(response, 'usage') and response.usage:
            print(f"[Token] 输入: {response.usage.input_tokens}, 输出: {response.usage.output_tokens}")
        return response.content[0].text

    def _ask_stream(
        self,
        messages: list[dict],
        system: str | None,
        max_tokens: int | None,
        temperature: float,
    ) -> str:
        """流式调用,逐字打印并返回完整文本"""
        stream = self.client.messages.create(
            model=self.model,
            max_tokens=max_tokens or DEFAULT_MAX_TOKENS,
            system=system,
            messages=messages,
            temperature=temperature,
            stream=True,
        )
        full_text = ""
        for event in stream:
            if event.type == "content_block_delta":
                print(event.delta.text, end="", flush=True)
                full_text += event.delta.text
        print()  # 换行
        return full_text

6.5 对话引擎

assistant.py:

python 复制代码
from client import ClaudeClient
from config import MAX_TURNS


# 不同场景的 System Prompt 模板
SYSTEM_PROMPTS = {
    "general": (
        "你是一个有帮助的 AI 助手。回答简洁、准确、有条理。"
        "如果不确定,请如实说明。"
    ),
    "code": (
        "你是一名资深软件工程师。请提供高质量、可运行的代码,"
        "包含必要的注释和类型注解。优先使用最佳实践。"
        "如果代码有潜在问题,请在代码后简要说明。"
    ),
    "writing": (
        "你是一名专业文案编辑。请对文本进行润色,保持原意不变,"
        "提升流畅度、逻辑性和可读性。输出润色后的文本即可。"
    ),
    "drama": (
        "你是一名短剧编剧。请根据用户提供的主题生成短剧大纲,"
        "包含:剧名、主角设定、核心冲突、分集梗概、高潮设计。"
        "输出使用 Markdown 格式,结构清晰。"
    ),
    "novel": (
        "你是一名小说策划编辑。请帮助用户构建小说设定,"
        "包括世界观、人物关系、主线剧情、章节大纲。"
        "风格可根据用户要求调整。"
    ),
    "json": (
        "你只输出合法 JSON,不输出任何额外解释或 Markdown 标记。"
        "确保 JSON 可以被 json.loads() 直接解析。"
    ),
}


class Assistant:
    """
    智能对话助手。
    管理多轮对话历史,支持场景切换和上下文压缩。
    """

    def __init__(self, client: ClaudeClient, scene: str = "general"):
        """
        初始化助手。
        :param client: ClaudeClient 实例
        :param scene: 场景名称,对应 SYSTEM_PROMPTS 中的键
        """
        self.client = client
        self.scene = scene
        # 对话历史,只保留 user 和 assistant 消息
        self.history: list[dict] = []

    @property
    def system_prompt(self) -> str:
        """获取当前场景的系统提示词"""
        return SYSTEM_PROMPTS.get(self.scene, SYSTEM_PROMPTS["general"])

    def set_scene(self, scene: str):
        """
        切换场景。
        切换后会清空历史,因为系统提示词已改变。
        """
        if scene not in SYSTEM_PROMPTS:
            print(f"未知场景 '{scene}',可用场景: {list(SYSTEM_PROMPTS.keys())}")
            return
        self.scene = scene
        self.history.clear()  # 切换场景后清空历史
        print(f"已切换到场景: {scene}")

    def ask(self, user_input: str, stream: bool = False) -> str:
        """
        发送一条用户消息并获取回复。
        :param user_input: 用户输入
        :param stream: 是否流式输出
        :return: 模型回复
        """
        # 添加用户消息到历史
        self.history.append({"role": "user", "content": user_input})

        # 检查上下文长度,超过阈值则压缩
        self._compress_if_needed()

        # 调用 API
        reply = self.client.chat(
            messages=self.history,
            system=self.system_prompt,
            stream=stream,
        )

        # 添加助手回复到历史
        self.history.append({"role": "assistant", "content": reply})
        return reply

    def _compress_if_needed(self):
        """
        如果对话历史超过最大轮数,进行压缩。
        保留最近 MAX_TURNS 轮,并将最早的内容摘要化提示。
        """
        if len(self.history) <= MAX_TURNS:
            return

        # 计算需要保留的消息数(保留最近的 MAX_TURNS 条)
        keep_count = MAX_TURNS
        removed = self.history[:-keep_count]
        self.history = self.history[-keep_count:]

        # 在历史开头插入一条摘要提示,提醒模型之前有过对话
        summary_hint = {
            "role": "user",
            "content": (
                f"[系统提示] 此前有 {len(removed)} 条对话因上下文限制已被压缩。"
                "请基于当前可见的对话历史继续回答。"
            )
        }
        self.history.insert(0, summary_hint)

    def clear_history(self):
        """清空对话历史"""
        self.history.clear()
        print("对话历史已清空。")

    def show_history(self):
        """打印当前对话历史"""
        if not self.history:
            print("对话历史为空。")
            return
        print(f"\n--- 对话历史(共 {len(self.history)} 条)---")
        for i, msg in enumerate(self.history, 1):
            role = "用户" if msg["role"] == "user" else "助手"
            # 只显示前 100 字符作为预览
            preview = msg["content"][:100].replace("\n", " ")
            print(f"  {i}. [{role}] {preview}...")
        print("---\n")

6.6 智能助手主程序

main.py:

python 复制代码
import sys
from assistant import Assistant
from client import ClaudeClient
from config import ENABLE_STREAM


def print_help():
    """打印帮助信息"""
    help_text = """
╔══════════════════════════════════════════════╗
║         Claude 智能助手 - 命令帮助            ║
╠══════════════════════════════════════════════╣
║  直接输入文字        → 普通对话               ║
║  /scene <场景>      → 切换场景                ║
║    可用场景: general, code, writing,          ║
║              drama, novel, json               ║
║  /clear             → 清空对话历史            ║
║  /history           → 查看对话历史            ║
║  /help              → 显示此帮助             ║
║  /quit 或 /exit     → 退出程序                ║
╚══════════════════════════════════════════════╝
"""
    print(help_text)


def main():
    """主程序入口"""
    print("=" * 50)
    print("  Claude 智能助手 v1.0")
    print("  输入 /help 查看可用命令")
    print("=" * 50)

    # 创建客户端和助手
    try:
        client = ClaudeClient()
    except RuntimeError as e:
        print(f"错误: {e}")
        print("请确保 .env 文件中配置了 ANTHROPIC_API_KEY")
        sys.exit(1)

    assistant = Assistant(client, scene="general")

    # 主循环
    while True:
        try:
            user_input = input("\n你: ").strip()
        except (EOFError, KeyboardInterrupt):
            print("\n再见!")
            break

        if not user_input:
            continue

        # 处理命令
        if user_input.startswith("/"):
            parts = user_input.split(maxsplit=1)
            command = parts[0].lower()

            if command in ("/quit", "/exit"):
                print("再见!")
                break
            elif command == "/help":
                print_help()
            elif command == "/clear":
                assistant.clear_history()
            elif command == "/history":
                assistant.show_history()
            elif command == "/scene":
                if len(parts) < 2:
                    print("用法: /scene <场景名称>")
                    print(f"可用场景: {list(assistant.__class__.__module__)}")
                    # 从 Assistant 的 SYSTEM_PROMPTS 获取
                    from assistant import SYSTEM_PROMPTS
                    print(f"可用场景: {list(SYSTEM_PROMPTS.keys())}")
                else:
                    assistant.set_scene(parts[1])
            else:
                print(f"未知命令: {command},输入 /help 查看帮助")
            continue

        # 普通对话
        print("\n助手:", end=" ")
        try:
            assistant.ask(user_input, stream=ENABLE_STREAM)
        except Exception as e:
            print(f"\n[错误] {e}")
            print("提示: 请检查 API Key 是否正确,网络连接是否正常。")


if __name__ == "__main__":
    main()

6.7 运行方式

bash 复制代码
# 1. 确保已激活虚拟环境
source venv/bin/activate  # macOS/Linux
# venv\Scripts\activate   # Windows

# 2. 确认 .env 文件已配置
cat .env

# 3. 运行程序
python main.py

示例对话:

复制代码
==================================================
  Claude 智能助手 v1.0
  输入 /help 查看可用命令
==================================================

你: /scene code
已切换到场景: code

你: 写一个 Python 函数,判断一个字符串是否是回文
助手: def is_palindrome(s: str) -> bool:
    """判断字符串是否是回文(忽略大小写和非字母数字字符)"""
    cleaned = [c.lower() for c in s if c.isalnum()]
    return cleaned == cleaned[::-1]

你: /scene drama
已切换到场景: drama

你: 生成一个都市悬疑短剧大纲,主角是外卖员
助手: ## 剧名:《最后一单》
### 主角设定
林小川,26岁,外卖员,前刑警...

七、参数调优技巧与输出效果提升

7.1 模型选择策略

截至 2026 年,Claude 主要模型系列如下:

模型 定位 上下文窗口 适用场景
Claude Opus 5.5 旗舰推理 1M 复杂推理、Agent 规划、长文档分析
Claude Sonnet 5.5 高性价比 1M 日常编码、文档编写、一般对话
Claude Opus 4.6 旗舰 200K / 1M (beta) 复杂任务、代码审查
Claude Sonnet 4.6 性价比 200K / 1M (beta) 通用编程、文本处理
Claude Haiku 4.5 轻量高速 200K 分类、提取、简单问答

选择建议:

  • 日常对话 / 文本润色:Sonnet 5.5 或 Sonnet 4.6,性价比高。
  • 复杂推理 / Agent 工作流:Opus 5.5 或 Opus 4.6,推理能力更强。
  • 批量分类 / 数据提取:Haiku 4.5,速度快、成本低。
  • 超长文档 / 代码库分析:选择支持 1M 上下文的模型,并确认所在平台已开放该能力。

模型标识符和可用性以 Anthropic 官方文档为准,不同平台可能使用不同命名。

7.2 max_tokens 与输出长度控制

max_tokens 是 Claude Messages API 的必填参数 。 如果输出被截断,stop_reason 会返回 "max_tokens"。

python 复制代码
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=4096,  # 根据需求调整
    messages=[...],
)

# 检查是否被截断
if response.stop_reason == "max_tokens":
    print("输出被截断,请增大 max_tokens 或拆分任务。")

建议值:

  • 短问答:512 ~ 1024
  • 代码生成:2048 ~ 4096
  • 长文 / 大纲:4096 ~ 8192
  • 超长输出:根据模型最大输出上限设置(如 64K / 128K)

7.3 Temperature、Top-P 与稳定性

参数 范围 建议
temperature 0.0 ~ 1.0 代码/JSON 用 0.0~0.3;创意写作用 0.7~1.0
top_p 0.0 ~ 1.0 通常与 temperature 二选一调节
stop_sequences 字符串数组 用于结构化输出收尾,如 ["\n\n"]
python 复制代码
# 确定性输出:代码生成
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    temperature=0.1,       # 低温度,输出稳定
    messages=[...],
)

# 创意输出:小说创作
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=2048,
    temperature=0.8,       # 高温度,更有创意
    messages=[...],
)

7.4 长上下文与 Prompt 结构优化

System Prompt 放规则,User Prompt 放数据:

python 复制代码
#  推荐写法
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=2048,
    system="你是一个 JSON 提取器。只输出 JSON,不要任何解释。",
    messages=[
        {"role": "user", "content": user_input_text}
    ],
)

长文档分段输入:当输入超过 100K token 时,建议先做摘要再送入模型,避免上下文窗口耗尽。

预填充技巧已失效 :2026 年的 Opus 4.6+ / Fable 5 模型不再支持在助手轮次中预填充 { 来强制 JSON 输出,会返回 400 错误。


7.5 结构化输出(Structured Output)

如果你需要模型返回严格的 JSON ,2026 年的推荐方案是使用 output_config + JSON Schema,而不是在 prompt 里写"请输出 JSON"。

python 复制代码
import json
from anthropic import Anthropic

client = Anthropic(api_key="your-api-key")

# 定义输出结构
schema = {
    "type": "object",
    "additionalProperties": False,
    "properties": {
        "title": {"type": "string"},
        "characters": {
            "type": "array",
            "items": {
                "type": "object",
                "additionalProperties": False,
                "properties": {
                    "name": {"type": "string"},
                    "role": {"type": "string"},
                    "age": {"type": "integer"}
                },
                "required": ["name", "role", "age"]
            }
        },
        "summary": {"type": "string"}
    },
    "required": ["title", "characters", "summary"]
}

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    system="你是一个短剧策划,根据用户输入生成角色设定。",
    messages=[
        {"role": "user", "content": "写一个都市悬疑短剧的角色设定"}
    ],
    output_config={
        "type": "json_schema",
        "json_schema": {
            "name": "DramaCharacters",
            "schema": schema,
            "strict": True
        }
    },
)

# 直接解析,无需担心格式问题
data = json.loads(response.content[0].text)
print(data["title"])
print(data["characters"])

要点:

  • additionalProperties: false 是必填项。
  • Schema 会被服务端缓存 24 小时,后续请求延迟更低。
  • 需要 Claude 3.5 Sonnet 或更新版本。
  • 旧版 beta header 在新版 SDK 中已不需要。

八、高级功能:Tool Use(工具调用)

Tool Use 让 Claude 可以调用你本地定义的函数,实现查询天气、读取文件、调用 API 等能力。

8.1 工具定义与调用流程

完整流程:定义工具 → 发送请求 → 模型返回 tool_use → 本地执行 → 将结果以 tool_result 回传 → 模型生成最终回答。

python 复制代码
import anthropic
import json

client = anthropic.Anthropic(api_key="your-api-key")

# 1. 定义工具列表
tools = [
    {
        "name": "get_weather",
        "description": "获取指定城市的当前天气信息",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "城市名称,如 北京、上海、重庆"
                }
            },
            "required": ["city"]
        }
    },
    {
        "name": "calculate",
        "description": "执行数学表达式计算",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "数学表达式,如 123 * 456"
                }
            },
            "required": ["expression"]
        }
    }
]

# 2. 本地工具实现
def get_weather(city: str) -> str:
    """模拟天气查询,实际项目中替换为真实 API"""
    weather_db = {
        "北京": "晴,25°C",
        "上海": "多云,22°C",
        "重庆": "小雨,18°C",
    }
    return weather_db.get(city, f"未找到 {city} 的天气信息")

def calculate(expression: str) -> str:
    """安全计算数学表达式"""
    try:
        # 只允许数字和运算符
        allowed = set("0123456789+-*/(). ")
        if not all(c in allowed for c in expression):
            return "错误:表达式包含非法字符"
        return str(eval(expression))
    except Exception as e:
        return f"计算错误:{e}"

# 工具映射表
TOOL_FUNCTIONS = {
    "get_weather": get_weather,
    "calculate": calculate,
}

# 3. 带工具调用的对话循环
def chat_with_tools(user_input: str, max_iterations: int = 5) -> str:
    messages = [{"role": "user", "content": user_input}]

    for _ in range(max_iterations):
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=1024,
            tools=tools,
            tool_choice={"type": "auto"},  # 让模型自行决定是否调用
            messages=messages,
        )

        # 检查是否有工具调用
        tool_use_blocks = [
            block for block in response.content
            if block.type == "tool_use"
        ]

        if not tool_use_blocks:
            # 没有工具调用,返回文本回复
            text_blocks = [
                block for block in response.content
                if block.type == "text"
            ]
            return text_blocks[0].text if text_blocks else ""

        # 执行工具调用
        for tool_block in tool_use_blocks:
            tool_name = tool_block.name
            tool_input = tool_block.input
            tool_id = tool_block.id

            # 本地执行
            if tool_name in TOOL_FUNCTIONS:
                result = TOOL_FUNCTIONS[tool_name](**tool_input)
            else:
                result = f"未知工具:{tool_name}"

            # 将工具调用和结果追加到消息历史
            messages.append({"role": "assistant", "content": response.content})
            messages.append({
                "role": "user",
                "content": [
                    {
                        "type": "tool_result",
                        "tool_use_id": tool_id,
                        "content": str(result)
                    }
                ]
            })

    return "已达到最大工具调用次数"

# 测试
print(chat_with_tools("重庆今天天气怎么样?"))
print(chat_with_tools("计算 1234 * 5678 的结果"))

8.2 封装到客户端

在 client.py 中增加工具调用支持:

python 复制代码
# 在 ClaudeClient 类中新增方法
def chat_with_tools(
    self,
    messages: list[dict],
    tools: list[dict],
    tool_functions: dict,
    max_iterations: int = 5,
    system: str | None = None,
    max_tokens: int | None = None,
    temperature: float = 0.7,
) -> str:
    """
    带工具调用的多轮对话。
    :param messages: 消息历史
    :param tools: 工具定义列表(JSON Schema 格式)
    :param tool_functions: 工具名 → 函数 的映射
    :param max_iterations: 最大工具调用轮数,防止无限循环
    :return: 最终文本回复
    """
    for _ in range(max_iterations):
        response = self.client.messages.create(
            model=self.model,
            max_tokens=max_tokens or DEFAULT_MAX_TOKENS,
            system=system,
            messages=messages,
            temperature=temperature,
            tools=tools,
            tool_choice={"type": "auto"},
        )

        # 提取工具调用
        tool_use_blocks = [
            b for b in response.content if b.type == "tool_use"
        ]

        if not tool_use_blocks:
            # 无工具调用,返回文本
            text_blocks = [b for b in response.content if b.type == "text"]
            return text_blocks[0].text if text_blocks else ""

        # 执行工具并回传结果
        messages.append({"role": "assistant", "content": response.content})
        for tb in tool_use_blocks:
            func = tool_functions.get(tb.name)
            result = func(**tb.input) if func else f"未知工具: {tb.name}"
            messages.append({
                "role": "user",
                "content": [
                    {"type": "tool_result", "tool_use_id": tb.id, "content": str(result)}
                ]
            })

    return "已达到最大工具调用轮数"

九、生产环境最佳实践

9.1 错误处理与重试

Claude API 常见错误码:

状态码 含义 处理方式
400 请求格式错误 检查参数,不要重试
401 API Key 无效 检查密钥配置
403 权限/地区限制 检查账户权限和网络
404 模型不存在 检查模型标识符
429 限流 指数退避重试
529 服务端过载 指数退避重试或降级模型

封装重试逻辑:

python 复制代码
import time
import random
from anthropic import APIError, RateLimitError, OverloadedError


def call_with_retry(func, max_retries: int = 5, base_delay: float = 1.0):
    """
    带指数退避 + 抖动的重试装饰器。
    :param func: 要执行的函数(无参)
    :param max_retries: 最大重试次数
    :param base_delay: 基础延迟秒数
    :return: 函数返回值
    """
    for attempt in range(max_retries + 1):
        try:
            return func()
        except RateLimitError as e:
            if attempt == max_retries:
                raise
            # 优先使用 retry-after 头
            retry_after = getattr(e, 'retry_after', None)
            if retry_after:
                delay = float(retry_after)
            else:
                delay = base_delay * (2 ** attempt) + random.uniform(0, 0.5)
            print(f"[限流] 第 {attempt + 1} 次重试,{delay:.1f} 秒后重试...")
            time.sleep(delay)
        except OverloadedError as e:
            # 529 服务端过载,同样退避重试
            if attempt == max_retries:
                raise
            delay = base_delay * (2 ** attempt) + random.uniform(0, 0.5)
            print(f"[过载] 第 {attempt + 1} 次重试,{delay:.1f} 秒后重试...")
            time.sleep(delay)
        except APIError as e:
            # 其他 API 错误,不重试
            raise

使用方式:

python 复制代码
response = call_with_retry(
    lambda: client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello"}],
    )
)

9.2 提示缓存(Prompt Caching)

对于包含大量静态内容(如长系统提示、文档、代码库)的场景,使用 Prompt Caching 可将重复内容的费用降低 90%。

python 复制代码
LONG_SYSTEM_PROMPT = """
你是一个代码审查助手。以下是项目的编码规范:
...(数千行规范文档)...
"""

# 使用 cache_control 标记静态内容
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=2048,
    system=[
        {
            "type": "text",
            "text": LONG_SYSTEM_PROMPT,
            "cache_control": {"type": "ephemeral"}  # 标记为可缓存
        }
    ],
    messages=[
        {"role": "user", "content": "审查以下代码..."}
    ],
)

# 查看缓存命中情况
usage = response.usage
print(f"缓存创建: {usage.cache_creation_input_tokens}")
print(f"缓存读取: {usage.cache_read_input_tokens}")

缓存预热 :对于延迟敏感的应用,可以在启动时用 max_tokens=0 预热缓存,首次响应时间最多缩短 52%。

python 复制代码
def prewarm_cache():
    """应用启动时预热缓存"""
    client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=0,  # 不生成内容,仅写入缓存
        system=[
            {
                "type": "text",
                "text": LONG_SYSTEM_PROMPT,
                "cache_control": {"type": "ephemeral"}
            }
        ],
        messages=[{"role": "user", "content": "warmup"}],
    )
    print("缓存预热完成")

注意:缓存默认 5 分钟过期,长间隔场景可使用 1 小时档。

9.3 成本监控

在 config.py 中增加成本估算:

python 复制代码
# 2026 年 API 价格(美元 / 百万 Token)
PRICING = {
    "claude-opus":    {"input": 5.0,  "output": 25.0},
    "claude-sonnet":  {"input": 3.0,  "output": 15.0},
    "claude-haiku":   {"input": 1.0,  "output": 5.0},
}

def estimate_cost(model: str, input_tokens: int, output_tokens: int) -> float:
    """估算单次调用的成本(美元)"""
    tier = None
    for prefix, price in PRICING.items():
        if prefix in model:
            tier = price
            break
    if not tier:
        return 0.0
    return (tier["input"] * input_tokens + tier["output"] * output_tokens) / 1_000_000

在客户端中记录成本:

python 复制代码
# 在 _ask_normal 方法末尾添加
from config import estimate_cost

cost = estimate_cost(self.model, response.usage.input_tokens, response.usage.output_tokens)
print(f"[成本] 本次调用约 ${cost:.4f}")

9.4 国内网络适配

中国大陆直连 api.anthic.com 存在网络限制。 如果直连不稳定,可配置中转:

python 复制代码
# .env 文件中增加
ANTHROPIC_BASE_URL=https://your-proxy.example.com

# config.py 中读取
import os
BASE_URL = os.getenv("ANTHROPIC_BASE_URL")

# client.py 中创建客户端时传入
self.client = anthropic.Anthropic(
    api_key=api_key,
    base_url=BASE_URL if BASE_URL else None,  # 未配置则使用默认
    timeout=(10.0, 60.0),
    max_retries=2,
)

十、项目总结与扩展方向

10.1 完整项目文件清单

复制代码
claude-tutorial/
├── .env                  # 环境变量(API Key,不提交到 Git)
├── .gitignore
├── requirements.txt      # 依赖列表
├── config.py             # 全局配置
├── client.py             # Claude API 客户端封装
├── assistant.py          # 智能对话助手(场景管理 + 上下文压缩)
├── tools.py              # 工具函数定义(天气、计算等)
├── main.py               # 主程序入口(交互式 CLI)
└── README.md             # 项目说明

requirements.txt:

复制代码
anthropic>=0.45.0
python-dotenv>=1.0.0

.gitignore:

复制代码
.env
__pycache__/
*.pyc
venv/
.venv/

tools.py(新增,工具函数集中管理):

python 复制代码
"""
工具函数定义。
每个工具包含:函数实现 + JSON Schema 描述。
"""

def get_weather(city: str) -> str:
    """获取指定城市的天气(模拟数据,实际项目替换为真实 API)"""
    weather_db = {
        "北京": "晴,25°C",
        "上海": "多云,22°C",
        "重庆": "小雨,18°C",
        "深圳": "晴,28°C",
    }
    return weather_db.get(city, f"未找到 {city} 的天气信息")


def calculate(expression: str) -> str:
    """安全计算数学表达式"""
    try:
        allowed = set("0123456789+-*/(). ")
        if not all(c in allowed for c in expression):
            return "错误:表达式包含非法字符"
        return str(eval(expression))
    except Exception as e:
        return f"计算错误:{e}"


# 工具 JSON Schema 定义
TOOLS_SCHEMA = [
    {
        "name": "get_weather",
        "description": "获取指定城市的当前天气信息",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "城市名称,如 北京、上海、重庆"
                }
            },
            "required": ["city"]
        }
    },
    {
        "name": "calculate",
        "description": "执行数学表达式计算",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "数学表达式,如 123 * 456"
                }
            },
            "required": ["expression"]
        }
    },
]

# 工具名 → 函数 映射
TOOL_FUNCTIONS = {
    "get_weather": get_weather,
    "calculate": calculate,
}

README.md:

markdown 复制代码
# Claude API 实战教程

## 快速开始

1. 安装依赖
   ```bash
   pip install -r requirements.txt
  1. 配置 API Key

    复制 .env.example 为 .env,填入你的 Anthropic API Key:

    复制代码
    ANTHROPIC_API_KEY=sk-ant-...
  2. 运行

    bash 复制代码
    python main.py
    bash 复制代码
    ## 可用命令
    
    | 命令 | 说明 |
    |---|---|
    | 直接输入文字 | 普通对话 |
    | `/scene <场景>` | 切换场景(general/code/writing/drama/novel/json) |
    | `/clear` | 清空历史 |
    | `/history` | 查看历史 |
    | `/tools on/off` | 开启/关闭工具调用 |
    | `/help` | 帮助 |
    | `/quit` | 退出 |
    
    ## 项目结构
    
    - `config.py` --- 全局配置
    - `client.py` --- API 客户端封装
    - `assistant.py` --- 对话助手(场景 + 上下文管理)
    - `tools.py` --- 工具函数(天气、计算)
    - `main.py` --- 主程序
    
    ## 扩展方向
    
    - 接入真实 API(天气、搜索、数据库)
    - 增加 RAG 知识库检索工具
    - 使用 Structured Output 做数据提取管道
    - 接入 Web UI(Gradio / Streamlit)
    - 部署为 API 服务(FastAPI)

10.2 main.py

python 复制代码
"""
Claude API 实战教程 --- 主程序入口
交互式 CLI,支持场景切换、工具调用开关、历史记录、上下文压缩。
"""

import os
import sys
from dotenv import load_dotenv

from config import DEFAULT_MODEL, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE, SCENES
from client import ClaudeClient
from tools import TOOLS_SCHEMA, TOOL_FUNCTIONS

# 加载 .env 中的 ANTHROPIC_API_KEY
load_dotenv()

API_KEY = os.getenv("ANTHROPIC_API_KEY")
if not API_KEY:
    print("错误:未找到 ANTHROPIC_API_KEY,请在 .env 文件中配置")
    sys.exit(1)


def print_help():
    """打印帮助信息"""
    print("\n=== 可用命令 ===")
    print("  /scene <场景>   切换场景(general/code/writing/drama/novel/json)")
    print("  /tools on/off   开启/关闭工具调用")
    print("  /clear          清空历史")
    print("  /history        查看历史消息")
    print("  /help           显示帮助")
    print("  /quit           退出")
    print("  直接输入文字     普通对话")


def print_scenes():
    """打印可用场景"""
    print("\n=== 可用场景 ===")
    for key, info in SCENES.items():
        print(f"  {key:10s} --- {info['description']}")


def run():
    """主循环"""
    client = ClaudeClient(model=DEFAULT_MODEL)
    current_scene = "general"
    tools_enabled = True  # 工具调用开关

    print("=" * 50)
    print("  Claude API 实战教程 --- 交互式 CLI")
    print("=" * 50)
    print(f"当前场景: {current_scene}  |  工具调用: {'开启' if tools_enabled else '关闭'}")
    print("输入 /help 查看帮助,输入 /quit 退出\n")

    while True:
        try:
            user_input = input(">>> ").strip()
        except (EOFError, KeyboardInterrupt):
            print("\n再见!")
            break

        if not user_input:
            continue

        # ---- 命令处理 ----

        if user_input.startswith("/"):
            parts = user_input.split(maxsplit=1)
            cmd = parts[0].lower()

            if cmd == "/quit" or cmd == "/exit":
                print("再见!")
                break

            elif cmd == "/help":
                print_help()
                continue

            elif cmd == "/clear":
                client.clear_history()
                print("历史已清空。")
                continue

            elif cmd == "/history":
                history = client.get_history()
                if not history:
                    print("暂无历史记录。")
                else:
                    print(f"\n=== 历史记录(共 {len(history)} 条)===")
                    for i, msg in enumerate(history, 1):
                        role = msg["role"]
                        content = msg["content"]
                        # content 可能是字符串或列表(工具调用场景)
                        if isinstance(content, list):
                            text_parts = []
                            for block in content:
                                if isinstance(block, dict):
                                    if block.get("type") == "text":
                                        text_parts.append(block.get("text", ""))
                                    elif block.get("type") == "tool_result":
                                        text_parts.append(
                                            f"[工具结果: {block.get('tool_use_id', '?')}]"
                                        )
                                    elif block.get("type") == "tool_use":
                                        text_parts.append(
                                            f"[调用工具: {block.get('name', '?')}]"
                                        )
                                    else:
                                        text_parts.append(str(block))
                                else:
                                    text_parts.append(str(block))
                            content = " ".join(text_parts)
                        print(f"  [{i}] {role}: {content[:200]}")
                continue

            elif cmd == "/scene":
                if len(parts) < 2:
                    print_scenes()
                    print(f"\n当前场景: {current_scene}")
                    continue
                scene_key = parts[1].lower()
                if scene_key in SCENES:
                    current_scene = scene_key
                    # 切换场景时清空历史,避免场景污染
                    client.clear_history()
                    print(f"场景已切换至: {current_scene} --- {SCENES[scene_key]['description']}")
                else:
                    print(f"未知场景: {scene_key}")
                    print_scenes()
                continue

            elif cmd == "/tools":
                if len(parts) < 2:
                    print(f"工具调用当前状态: {'开启' if tools_enabled else '关闭'}")
                    print("用法: /tools on  或  /tools off")
                    continue
                arg = parts[1].lower()
                if arg in ("on", "true", "1", "enable", "开启"):
                    tools_enabled = True
                    print("工具调用已开启。")
                elif arg in ("off", "false", "0", "disable", "关闭"):
                    tools_enabled = False
                    print("工具调用已关闭。")
                else:
                    print(f"未知参数: {arg},请使用 on 或 off")
                continue

            else:
                print(f"未知命令: {cmd},输入 /help 查看帮助")
                continue

        # ---- 普通对话 ----

        # 获取当前场景的 system prompt
        system = SCENES[current_scene]["system"]

        if tools_enabled:
            # 使用工具调用循环
            try:
                reply = client.chat_with_tools(
                    messages=[{"role": "user", "content": user_input}],
                    tools=TOOLS_SCHEMA,
                    tool_functions=TOOL_FUNCTIONS,
                    max_iterations=5,
                    system=system,
                    max_tokens=DEFAULT_MAX_TOKENS,
                    temperature=DEFAULT_TEMPERATURE,
                )
            except Exception as e:
                reply = f"[工具调用出错] {e}"
        else:
            # 普通对话,不带工具
            try:
                reply = client.chat(
                    user_input,
                    system=system,
                    max_tokens=DEFAULT_MAX_TOKENS,
                    temperature=DEFAULT_TEMPERATURE,
                )
            except Exception as e:
                reply = f"[请求出错] {e}"

        print(f"\n{reply}\n")


if __name__ == "__main__":
    run()
  • 关键设计说明
功能 实现方式
/tools on/off 布尔开关 tools_enabled,控制走 chat_with_tools 还是普通 chat
/scene 切换 current_scene,同时清空历史避免场景污染
/history 遍历 client.get_history(),兼容工具调用产生的 list 型 content
/clear 调用 client.clear_history()
工具循环 复用第八章封装的 chat_with_tools,最多 5 轮工具迭代
错误兜底 try/except 捕获 API 异常,不直接崩溃
  • 运行方式
bash 复制代码
# 1. 安装依赖
pip install -r requirements.txt

# 2. 配置 .env(已有则跳过)
echo "ANTHROPIC_API_KEY=sk-ant-..." > .env

# 3. 启动
python main.py

启动后效果:

复制代码
==================================================
  Claude API 实战教程 --- 交互式 CLI
==================================================
当前场景: general  |  工具调用: 开启
输入 /help 查看帮助,输入 /quit 退出

>>> /tools off
工具调用已关闭。

>>> 重庆今天天气怎么样?
[普通对话,无工具调用]

>>> /tools on
工具调用已开启。

>>> 重庆今天天气怎么样?
[模型调用 get_weather 工具,返回真实结果]

>>> /scene code
场景已切换至: code --- 编程助手

>>> 写一个 Python 快速排序
[使用 code 场景的 system prompt 回答]

>>> /quit
再见!

扩展一:RAG 知识库检索工具

RAG 的核心思路是:先检索,再生成。把私有文档切块、向量化后存入向量库;用户提问时,先召回最相关的片段,再拼进 Prompt 让 Claude 基于这些材料回答。

1. 安装依赖
bash 复制代码
pip install chromadb sentence-transformers
2. rag.py:知识库封装
python 复制代码
"""
RAG 知识库模块。
使用 ChromaDB + sentence-transformers 做本地向量检索。
"""

import os
import chromadb
from sentence_transformers import SentenceTransformer


class KnowledgeBase:
    """轻量本地知识库,适合教程和原型验证。"""

    def __init__(self, collection_name: str = "default_kb", persist_dir: str = "./chroma_db"):
        self.embedder = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")
        self.client = chromadb.PersistentClient(path=persist_dir)
        self.collection = self.client.get_or_create_collection(name=collection_name)

    def add_texts(self, texts: list[str], source: str = ""):
        """添加一批文本块到知识库。"""
        if not texts:
            return
        embeddings = self.embedder.encode(texts).tolist()
        ids = [f"{source}_{i}" for i in range(len(texts))]
        metadatas = [{"source": source} for _ in texts]
        self.collection.add(
            ids=ids,
            documents=texts,
            embeddings=embeddings,
            metadatas=metadatas,
        )
        print(f"已添加 {len(texts)} 个文本块(来源: {source})")

    def query(self, question: str, top_k: int = 3) -> list[dict]:
        """检索与问题最相关的 top_k 个文本块。"""
        query_emb = self.embedder.encode([question]).tolist()
        results = self.collection.query(
            query_embeddings=query_emb,
            n_results=top_k,
            include=["documents", "metadatas", "distances"],
        )
        chunks = []
        if results["documents"]:
            for doc, meta, dist in zip(
                results["documents"][0],
                results["metadatas"][0],
                results["distances"][0],
            ):
                chunks.append({"text": doc, "source": meta.get("source", ""), "distance": dist})
        return chunks

    def count(self) -> int:
        return self.collection.count()


def build_rag_prompt(question: str, chunks: list[dict]) -> str:
    """把检索结果拼成 Claude 的 Prompt。"""
    if not chunks:
        return f"问题:{question}\n\n注意:知识库中未找到相关资料,请基于通用知识回答,并说明缺少参考。"

    context_parts = []
    for i, c in enumerate(chunks, 1):
        context_parts.append(f"[资料{i}] 来源:{c['source']}\n{c['text']}")

    context = "\n\n".join(context_parts)
    return f"""你是一个基于知识库回答问题的助手。请严格依据以下资料回答问题。
如果资料不足以回答问题,请明确说明"资料不足",不要编造。
回答末尾请标注引用来源。

资料:
{context}

问题:{question}

回答:"""
3. 在 tools.py 中增加 RAG 工具
python 复制代码
from rag import KnowledgeBase, build_rag_prompt

# 全局知识库实例(实际项目中可改为单例或从外部注入)
_kb = KnowledgeBase()


def ask_knowledge_base(question: str) -> str:
    """从知识库检索相关内容,并返回格式化上下文。"""
    chunks = _kb.query(question, top_k=3)
    return build_rag_prompt(question, chunks)


def ingest_documents(source: str, texts: list[str]) -> str:
    """将一批文本块写入知识库。"""
    _kb.add_texts(texts, source=source)
    return f"已写入 {len(texts)} 条知识,来源: {source}"


# 在 TOOLS_SCHEMA 中追加两个工具定义
TOOLS_SCHEMA.extend([
    {
        "name": "ask_knowledge_base",
        "description": "从本地知识库检索与用户问题最相关的资料",
        "input_schema": {
            "type": "object",
            "properties": {
                "question": {"type": "string", "description": "用户的问题"}
            },
            "required": ["question"]
        }
    },
    {
        "name": "ingest_documents",
        "description": "将文本块写入知识库",
        "input_schema": {
            "type": "object",
            "properties": {
                "source": {"type": "string", "description": "来源标识,如文件名"},
                "texts": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "待写入的文本块列表"
                }
            },
            "required": ["source", "texts"]
        }
    },
])

# 在 TOOL_FUNCTIONS 中追加映射
TOOL_FUNCTIONS["ask_knowledge_base"] = ask_knowledge_base
TOOL_FUNCTIONS["ingest_documents"] = ingest_documents
4. 快速入库脚本 ingest.py
python 复制代码
"""
示例:把一批文本写入知识库。
运行:python ingest.py
"""

from rag import KnowledgeBase

kb = KnowledgeBase()

kb.add_texts([
    "Claude API 由 Anthropic 提供,支持文本对话、工具调用和结构化输出。",
    "Claude 模型系列包括 Opus、Sonnet、Haiku 三个层级,Sonnet 适合大多数场景。",
    "RAG 是检索增强生成,先检索外部知识再让模型生成答案。",
    "ChromaDB 是轻量向量数据库,支持本地持久化存储。",
], source="教程笔记")

print(f"知识库当前条目数:{kb.count()}")

运行一次后,重启 main.py,工具调用里就能用 ask_knowledge_base 查知识了。


扩展二:Streamlit Web UI

1. 安装依赖
bash 复制代码
pip install streamlit
2. webui.py:完整聊天界面
python 复制代码
"""
Claude API Streamlit Web UI
运行:streamlit run webui.py
"""

import os
import streamlit as st
from dotenv import load_dotenv

from config import DEFAULT_MODEL, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE, SCENES
from client import ClaudeClient
from tools import TOOLS_SCHEMA, TOOL_FUNCTIONS

load_dotenv()

API_KEY = os.getenv("ANTHROPIC_API_KEY")
if not API_KEY:
    st.error("未找到 ANTHROPIC_API_KEY,请在 .env 中配置")
    st.stop()

# ---- 侧边栏设置 ----
st.sidebar.title("Claude 实战教程")

scene = st.sidebar.selectbox(
    "场景",
    options=list(SCENES.keys()),
    format_func=lambda k: f"{k} --- {SCENES[k]['description']}",
)

use_tools = st.sidebar.checkbox("启用工具调用", value=True)
temperature = st.sidebar.slider("Temperature", 0.0, 1.0, DEFAULT_TEMPERATURE)
max_tokens = st.sidebar.number_input("Max Tokens", 256, 8192, DEFAULT_MAX_TOKENS)

if st.sidebar.button("清空对话"):
    st.session_state.messages = []
    st.session_state.client = ClaudeClient(model=DEFAULT_MODEL)
    st.rerun()

# ---- 初始化客户端 ----
if "client" not in st.session_state:
    st.session_state.client = ClaudeClient(model=DEFAULT_MODEL)
if "messages" not in st.session_state:
    st.session_state.messages = []

# ---- 主标题 ----
st.title(f"Claude 对话 --- {scene}")

# ---- 展示历史消息 ----
for msg in st.session_state.messages:
    with st.chat_message(msg["role"]):
        st.markdown(msg["content"])

# ---- 用户输入 ----
if prompt := st.chat_input("输入你的问题..."):
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)

    system = SCENES[scene]["system"]
    client = st.session_state.client

    with st.chat_message("assistant"):
        placeholder = st.empty()

        try:
            if use_tools:
                reply = client.chat_with_tools(
                    messages=[{"role": "user", "content": prompt}],
                    tools=TOOLS_SCHEMA,
                    tool_functions=TOOL_FUNCTIONS,
                    max_iterations=5,
                    system=system,
                    max_tokens=max_tokens,
                    temperature=temperature,
                )
            else:
                reply = client.chat(
                    prompt,
                    system=system,
                    max_tokens=max_tokens,
                    temperature=temperature,
                )
        except Exception as e:
            reply = f"请求失败:{e}"

        placeholder.markdown(reply)
        st.session_state.messages.append({"role": "assistant", "content": reply})
3. 运行方式
bash 复制代码
streamlit run webui.py

浏览器会自动打开 http://localhost:8501。左侧可以切换场景、开关工具、调节参数;右侧是对话区。


目录更新
复制代码
claude-tutorial/
├── .env
├── .gitignore
├── requirements.txt
├── config.py
├── client.py
├── assistant.py
├── tools.py
├── rag.py              # 新增:知识库
├── ingest.py           # 新增:数据入库脚本
├── main.py             # CLI 入口
├── webui.py            # 新增:Streamlit Web UI
└── README.md

requirements.txt 追加:

复制代码
chromadb>=0.5.0
sentence-transformers>=3.0.0
streamlit>=1.35.0

这样项目就同时具备 CLI 交互 、工具调用 、RAG 知识库 和 Web UI 四个能力,可以直接跑起来。


相关推荐
_孤傲_1 小时前
快速掌握 MySQL 语法
数据库·后端·mysql
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 圈子功能实现:总结
java·开发语言·交友
SL_staff2 小时前
JVS私有化文档系统上线避坑指南:5大运维与业务协同断点及检测加固实践
java·开源·全栈
数字新视界3 小时前
一体化机柜在数据中心运维中的创新应用与优势分析
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
FinelyYang3 小时前
考试系统简答题 AI 阅卷:覆盖度驱动、异步评分与人机协同落地实践
java·大数据·人工智能
hhhjhl3 小时前
Yup 跨端三方库仓颉适配全流程
java·开发语言
SelectDB3 小时前
从 StarRocks 走向 Apache Doris:如何实现引擎平滑替换与架构升级?
大数据·数据库·数据分析
为思念酝酿的痛3 小时前
MySQL的操作
数据库·mysql
用户3126874877203 小时前
Java 对象内存布局:压缩指针、对齐填充与 JOL 实测
java