# M2.3 大模型 API 入门

> 目标：用代码调通一次大模型 API，理解 messages 结构、核心参数、流式输出。

## 1. 调用方式（通义 / DeepSeek / OpenAI 兼容）

主流大模型几乎都兼容 **OpenAI 接口格式**，学会一个，其他换 `base_url` 和 `api_key` 即可。

先装：`pip install openai`

```python
from openai import OpenAI

# 以 DeepSeek 为例（通义/OpenAI 同理，只换 base_url 和 key）
client = OpenAI(
    api_key="sk-你的key",                 # 从环境变量读更安全
    base_url="https://api.deepseek.com/v1"
)

resp = client.chat.completions.create(
    model="deepseek-chat",
    messages=[
        {"role": "system", "content": "你是一位耐心的中文老师"},
        {"role": "user",   "content": "用一句话解释什么是 Agent"}
    ]
)
print(resp.choices[0].message.content)
```

## 2. 核心参数

| 参数 | 作用 | 怎么调 |
|---|---|---|
| `temperature` | 随机度，0=稳定，1=发散 | 写代码/事实用 0；创意文案用 0.8 |
| `max_tokens` | 单次最多生成多少 token | 控制成本和长度 |
| `messages` | 对话历史（见下） | 多轮对话靠它 |
| `top_p` |  nucleus 采样，和 temperature 二选一调 | 一般不动 |

```python
resp = client.chat.completions.create(
    model="deepseek-chat",
    temperature=0.2,        # 稳定输出，适合问答
    max_tokens=500,
    messages=[{"role": "user", "content": "写一段 Python 读文件的代码"}]
)
```

## 3. 多轮对话：messages 结构

模型**没有记忆**，每次都要把历史拼进 `messages`。

```python
history = [
    {"role": "system", "content": "你是翻译助手，只做中英互译"},
    {"role": "user",   "content": "你好"},
    {"role": "assistant", "content": "Hello"},
    {"role": "user",   "content": "今天天气不错"},
]
resp = client.chat.completions.create(model="deepseek-chat", messages=history)
# 把新回答追加进 history，下一轮继续传，就是"多轮对话"
history.append({"role": "assistant", "content": resp.choices[0].message.content})
```

## 4. 流式输出（打字机效果）

```python
stream = client.chat.completions.create(
    model="deepseek-chat",
    messages=[{"role": "user", "content": "写一首关于 AI 的短诗"}],
    stream=True
)
for chunk in stream:
    delta = chunk.choices[0].delta.content or ""
    print(delta, end="", flush=True)   # 逐字打印，就是打字机
```

## 5. 实战：用代码让模型回答一个问题

```python
def ask(question: str) -> str:
    client = OpenAI(api_key="sk-你的key", base_url="https://api.deepseek.com/v1")
    r = client.chat.completions.create(
        model="deepseek-chat",
        temperature=0.3,
        messages=[
            {"role": "system", "content": "你回答简洁、准确，用中文"},
            {"role": "user",   "content": question}
        ]
    )
    return r.choices[0].message.content

print(ask("RAG 是什么？一句话"))
```

## 动手练习

1. 申请一个 Key（DeepSeek/通义都行，很便宜），跑通上面"实战"函数。
2. 把 `ask` 改成支持多轮：问两连问（"什么是向量？""它和列表有什么区别？"），确认第二问模型知道上下文。
3. 用流式输出改写 `ask`，体验打字机效果。

## 自测

1. 为什么多轮对话必须把历史传回模型？模型自己不会记吗？
2. `temperature=0` 和 `temperature=1` 分别适合什么任务？
3. 流式输出的本质是什么？（提示：服务器一段段把 token 发过来）
