不是每台电脑都应该运行本地大模型
本地运行 7B / 8B 级开源模型会消耗显存、磁盘和下载时间;普通笔记本不需要强行尝试。课程提供四种技术路线,它们的学习目标相同:行情和新闻先由 Python 整理,再由模型生成有边界的文字;差别在于“模型在哪里运行、数据如何流动”。
| 路线 | 学生电脑需要什么 | 数据流向 | 最适合练习什么 |
|---|---|---|---|
| 公有云 API | 普通电脑与网络 | 上下文发送到第三方服务 | 请求、JSON、Prompt 和错误处理。 |
| Coze Bot | 普通电脑与浏览器 | 由平台处理 Bot 输入 | 角色、工作流、测试和交互界面。 |
| 本机 GPU | 兼容的 NVIDIA GPU、足够显存与磁盘 | 数据和模型都留在本机 | 模型下载、加载和本地推理。 |
| 校内 GPU API | 普通电脑 + 校园网 | 受控地发送到校内服务器 | 客户端—服务端架构、权限和部署思维。 |
先做硬件检查,再下载任何模型
如果你计划走本机 GPU 路线,请打开 Trae 终端,在课程虚拟环境中运行下面命令。第一条检查系统是否能找到 NVIDIA 驱动;第二条由 PyTorch 检查 Python 是否能使用 CUDA。
nvidia-smi
python -c "import torch; print('CUDA 可用:', torch.cuda.is_available()); print('GPU:', torch.cuda.get_device_name(0) if torch.cuda.is_available() else '未找到')"False。Mac 电脑怎么办?Apple Silicon 使用的是不同的图形计算体系,不应照抄 CUDA / nvidia-smi 步骤。对于本课程的 7B/8B 本地 GPU 演示,推荐直接使用校内 GPU API 或云端 API 路线。
认识 ModelScope:先读模型卡,再下载
ModelScope(魔搭社区)是模型、数据集和相关工具的社区平台。模型页最重要的不是“下载”按钮,而是模型卡:它会说明模型 ID、基础模型、指令能力、许可证、下载大小、上下文长度、推荐硬件和运行示例。不同模型的加载方式可能不同,模型卡优先于任何课程示例。
登录并搜索模型
打开 ModelScope 官网,搜索你有权使用的指令模型。课程示例以 Qwen 系列的 7B/8B 指令模型说明流程,不绑定某个永久版本。
阅读许可证
查看许可、使用范围与是否需要同意协议。教学练习不等于自动取得商业用途、再分发或上传敏感数据的权限。
记录模型身份
在 Notebook 顶部记录
model_id、revision(如有)、下载日期、Python 版本与显卡信息。模型更新后,同一 Prompt 的结果可能不同。估计资源
除模型文件外,还要为缓存、临时文件和推理显存留余量。显存不足时,先选择模型卡明确支持的量化或更小模型,不要随意删掉报错检查。
安装本机推理所需的包
python -m pip install -U modelscope transformers accelerate torch
python -c "import modelscope, transformers, torch; print('modelscope:', modelscope.__version__); print('torch:', torch.__version__)"这里每个包的分工不同:modelscope 负责下载与管理模型资源;transformers 提供常见的 tokenizer 与模型加载接口;torch 执行张量和 GPU 计算;accelerate 帮助在可用设备间安排模型。安装成功不代表显存一定够用。
下载到明确的本地目录
第一次下载通常较慢。下面用 snapshot_download 把模型快照下载到本机缓存,并打印得到的目录。运行前将 model_id 改成模型卡上真实的 ID;不要手打一个不确定的名称。
from modelscope import snapshot_download
model_id = "Qwen/Qwen2.5-7B-Instruct" # 以当前模型卡显示的 ID 为准
model_dir = snapshot_download(model_id=model_id)
print("模型下载或缓存目录:")
print(model_dir)如果下载中断、磁盘满或权限不足,不要随意删除整个缓存目录。先记录报错、检查可用空间和网络,再按 ModelScope 文档处理缓存。
本机最小推理:先让模型回答一个无敏感信息的问题
首次加载模型时,先用一个与金融数据无关的小问题测试 tokenizer、模型和 GPU。不要一开始就把长行情表、新闻正文和真实账户信息送入模型;先确认基础环境能稳定输出。
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
if not torch.cuda.is_available():
raise RuntimeError(
"当前 Python 没有可用 CUDA GPU。请改用校内 GPU API 或云端路线。"
)
tokenizer = AutoTokenizer.from_pretrained(model_dir)
model = AutoModelForCausalLM.from_pretrained(
model_dir,
torch_dtype="auto",
device_map="auto",
)
messages = [
{"role": "system", "content": "你是课程助理,回答必须简洁、客观。"},
{"role": "user", "content": "为什么时间戳对事件驱动分析很重要?"},
]
text = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True
)
inputs = tokenizer([text], return_tensors="pt").to(model.device)
with torch.no_grad():
generated = model.generate(**inputs, max_new_tokens=120, temperature=0.2)
new_tokens = generated[:, inputs.input_ids.shape[1]:]
answer = tokenizer.batch_decode(new_tokens, skip_special_tokens=True)[0]
print(answer)ForCausalLM 指自回归文本生成这一类任务。再接入第十一节 context 的正确时机:只有上面的最小示例能稳定完成后,才把 user 内容替换为上一节的 context 与四段式输出要求。若最小示例都失败,先排查模型、GPU、驱动或依赖,而不是怀疑金融数据。
教师端:把 GPU 模型封装成校内 FastAPI 服务
对大多数学生而言,最佳折中是:教师在校内 GPU 服务器加载一次模型,提供一个受控接口;学生只发送经过第十一节处理的 context。FastAPI 的优势是:请求体有清晰结构,自动生成交互文档,可在 /docs 检查接口。以下代码是教学用最小服务,不是直接暴露到公网的生产方案。
服务器项目的文件结构
school-finance-agent/
├── server.py # FastAPI 服务端
├── .env # 模型 ID 与访问 token,只留在服务器
├── requirements.txt # 依赖版本
└── logs/ # 只保存脱敏的运行日志(如课程需要)服务器端 .env 示例
TEACHING_API_TOKEN=请生成一串足够长的随机 token
MODEL_ID=Qwen/Qwen2.5-7B-Instructserver.py:从健康检查到受保护的 POST 接口
import os
import torch
from dotenv import load_dotenv
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel, Field
from transformers import AutoTokenizer, AutoModelForCausalLM
load_dotenv()
ACCESS_TOKEN = os.environ["TEACHING_API_TOKEN"]
MODEL_ID = os.environ["MODEL_ID"]
app = FastAPI(
title="Teaching Finance Event Agent",
description="仅限课程内网教学使用;不提供投资建议。",
)
class AgentRequest(BaseModel):
prompt: str = Field(min_length=20, max_length=12000)
@app.get("/health")
def health():
return {
"status": "ok",
"model": MODEL_ID,
"cuda_available": torch.cuda.is_available(),
}
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
model = AutoModelForCausalLM.from_pretrained(
MODEL_ID, torch_dtype="auto", device_map="auto"
)
def require_token(authorization: str | None):
if authorization != f"Bearer {ACCESS_TOKEN}":
raise HTTPException(status_code=401, detail="invalid token")
@app.post("/finance-agent")
def finance_agent(
body: AgentRequest,
authorization: str | None = Header(default=None),
):
require_token(authorization)
messages = [
{
"role": "system",
"content": "你是课程金融信息整理助手。不得提供投资建议。"
},
{"role": "user", "content": body.prompt},
]
text = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True
)
inputs = tokenizer([text], return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs, max_new_tokens=350, temperature=0.2
)
new_tokens = outputs[:, inputs.input_ids.shape[1]:]
report = tokenizer.batch_decode(
new_tokens, skip_special_tokens=True
)[0].strip()
return {"analysis_report": report, "model": MODEL_ID}| 服务器代码部分 | 具体作用 | 为什么不能省略 |
|---|---|---|
load_dotenv() | 读取服务器本机的密钥和模型 ID。 | 避免把 token 写在 server.py 中,也便于换模型。 |
AgentRequest | 规定请求体必须有 prompt,并限制最小、最大长度。 | 错误输入能更早返回清楚错误,避免超长文本耗尽显存。 |
/health | 不做模型推理,只报告服务和 CUDA 状态。 | 学生请求失败时,先判断是网络问题还是推理问题。 |
Header | 读取 HTTP Authorization 头。 | 没有访问 token 的请求应被拒绝,避免任何人调用 GPU。 |
require_token | 将鉴权判断单独写成函数。 | 便于检查,也避免以后新增接口时漏掉权限检查。 |
max_new_tokens | 限制每个请求的最大输出长度。 | 防止一个请求占用过多显存和时间,影响其他同学。 |
安装与启动服务
python -m pip install -U "fastapi[standard]" python-dotenv transformers torch
fastapi dev server.py“127.0.0.1”只代表服务器自己。它适合先在服务器本地测试。是否允许校园内其他电脑访问、应使用哪个内网地址、是否需要反向代理和 HTTPS,必须由教师和学校网络管理员按校内安全要求配置;不要为求方便将 8000 端口直接暴露到公网。
学生端:先检查服务健康,再发送 context
学生端不下载模型,也不需要 transformers。它只要有上一节的 context、校内服务器地址和由教师单独提供的访问 token。示例地址中的 IP 只是占位符,必须替换为教师公布的校内地址;不要把 token 写入作业截图。
学生端 .env 增加两行
TEACHING_AGENT_URL=http://教师公布的校内地址:8000
TEACHING_API_TOKEN=教师单独提供的访问 token第一步:调用 health,不消耗模型推理
from dotenv import load_dotenv
import os
import requests
load_dotenv()
base_url = os.environ["TEACHING_AGENT_URL"].rstrip("/")
health_response = requests.get(f"{base_url}/health", timeout=10)
print("状态码:", health_response.status_code)
health_response.raise_for_status()
print(health_response.json())第二步:发送第十一节生成的 context
access_token = os.environ["TEACHING_API_TOKEN"]
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
body = {"prompt": user_prompt} # user_prompt 来自第十二节的四段式任务
response = requests.post(
f"{base_url}/finance-agent",
headers=headers,
json=body,
timeout=120,
)
print("状态码:", response.status_code)
response.raise_for_status()
result = response.json()
print("服务器使用的模型:", result["model"])
print("分析报告:")
print(result["analysis_report"])/health 时得到双斜杠。安全、权限与故障排查
校内服务的“私有化”是一个系统问题,不只是把模型换到服务器。下面的清单适用于教学环境;正式科研或对外服务还需要学校信息安全、数据管理和网络管理要求。
| 学生看到的错误 | 含义 | 学生应做什么 |
|---|---|---|
| 连接超时 / ConnectionError | 网络到服务器不通,或服务未启动。 | 确认已连接校园网;先访问 /health;将报错时间发给教师。 |
| 401 invalid token | 未携带、携带了错误 token,或 token 已被撤销。 | 检查 .env 变量名,切勿把 token 发到群里。 |
| 422 validation error | 请求 JSON 不符合 AgentRequest 的结构或长度限制。 | 检查是否使用 {"prompt": user_prompt},以及 Prompt 是否过长。 |
| 500 | 服务器模型加载、显存或代码出现异常。 | 不要反复重试;保存状态码、时间和简短错误信息交给教师。 |
| 返回格式不对 | 服务升级或代码期望字段不一致。 | 先 print(response.text[:500]),再对照教师公布的 API 文档。 |
绝对不要做:把教师的内网 token 放进公开 GitHub;将服务器地址、模型端口和调试密码发布到公开网页;为了绕过权限把服务改成无鉴权;或将课程报告当作真实交易决策工具。
结业项目:四选一,标准不降低
你可以选择公有云 API、Coze、本机 GPU 或校内 GPU API 之一。选择不同,不代表评分标准不同:每条路线都必须说明数据从哪里来、什么时候抓取、模型看到了什么、输出哪里不确定,以及为什么不能据此荐股。
提交可运行代码
至少包含:数据读取、字段检查、预处理、Prompt 组装、模型 / API 调用、响应解析、文件保存和错误处理。代码要有注释,但注释不能替代实际检查。
提交可复现材料
提交
requirements、模型或 API 版本、日期范围、脱敏样例输出、运行截图与目录说明。密钥、Cookie、账号和受限原始数据不得提交。提交一份短报告
建议 1,500 字以内,包含架构图、数据字典、测试案例、一个失败情况、输出边界和后续改进。报告要明确区分“程序自动生成的文字”与“你的人工判断”。
可选扩展
多标的批处理、定时生成日报、指标计算、RAG 知识库或工具调用都可以加分;但每个扩展都必须增加数据来源、权限和失败处理说明。
本部分真正要学会的事:模型可以是云端、校内或本机;但一份可信的课程项目始终依赖同一条原则——输入可查、过程可复现、错误可定位、结论有边界。