数据从文件里来、到文件里去 · 程序怎么和模型对话 · 模型怎么用你的函数
自备电脑Jupyter + WorkBuddy本课件的代码可以直接运行
· 读一个 txt、把结果写回一个文件
· 说清一次 API 调用的四个要素
· 讲清 AI 调用工具的完整过程
上半场 Python:数据从文件里来、到文件里去;下半场 AI:程序怎么和模型对话、怎么把工具交给它。
把结果交回去,不是只 print
空列表 + 循环 + 条件 + append
目标 · 背景 · 限制 · 怎样算完
给 WorkBuddy 的长期说明书
nums = [59, 60, 85] # 数据在列表里
result = []
for x in nums:
if x >= 60:
result.append(x)
result = []
with open("scores.txt", encoding="utf-8") as f:
for line in f: # 数据在文件里
if line.strip() != "":
result.append(line.strip())
数据来源从「列表」换成「文件」——形状全是熟悉的筛选四步。
页面里装了一个真正的 Python(Pyodide,跑在浏览器里)。不用装软件,也不用服务器。
Shift+Enter 运行 · 同一页上面定义的变量下面能用 · 改坏了点「↺ 还原」
作业还是在你自己电脑的 Jupyter 里做:打开 L3_homework.ipynb,能跑出 print 的举手。
成绩在 txt / CSV 里——程序要能读进来、写出去,才能干真活。
open("scores.txt"),文件放哪儿很要紧推荐:把数据文件和 notebook 放在同一个文件夹,用相对路径。路径写错,程序就找不到文件。
# 三个参数,各管一件事
open("scores.txt", # ① 文件名(或路径)
"r", # ② 模式:读 / 写 / 追加
encoding="utf-8") # ③ 编码:防中文乱码
| 模式 | 含义 |
|---|---|
| "r" | 只读(默认);文件不存在 → FileNotFoundError |
| "w" | 只写;会清空原内容;不存在则新建 |
| "a" | 追加;写到末尾,不清空 |
encoding="utf-8":不写它,中文容易报错或乱码。
1 with … as f: 打开,f 就是这个文件
2 缩进里就是「文件打开着」的范围
3 中途出错也会关——本课统一用 with
左边是这个网页里的「虚拟文件夹」,每次运行后会刷新;新文件、改过的文件会闪一下。
| 写法 | 得到什么 | 什么时候用 |
|---|---|---|
| f.read() | 整个文件 → 一个字符串 | 整段文本要一起处理时 |
| f.readlines() | 每行一个元素 → 列表(带 \n) | 想先拿到一整个列表时 |
| for line in f: | 一行一行拿,边读边处理 | ✅ 推荐:正好接上循环 |
"85\n" ≠ "85"文件里每行末尾都藏着 \n;不 strip 掉,比较永远失败。line.strip() 去掉首尾的空白和换行。
write() 不会自动加换行,要自己写 "\n";writelines(list) 逐条写,也不加换行。
FileNotFoundError 找第 5 / 6 条,中文乱码找第 3 条。
通过后挑战 L1 的 B4:把它写成函数 process_scores(input_path, output_path),每行写「姓名 PASS/FAIL」,返回有效条数。
你和 AI 的每一次聊天,
背后都是一次 API 调用。
发出去什么、收回来什么,都有固定格式
模型跑在服务商的机房里
你的程序只负责发请求、接回答
| 要素 | 是什么 | 打个比方 |
|---|---|---|
| base_url | 请求发到哪;每家服务商一个 | 去哪家店 |
| api_key | 证明你是谁;像密码,别外传 | 会员卡 |
| model | 用哪个模型,如 deepseek-flash | 点哪个厨子 |
| messages | 对话内容,一个列表 | 你的菜单 |
默认是离线模拟模型:不联网、不花钱、结果固定。右上角可以换成真实模型。
输出里有「20」——你的程序第一次直接和模型对话。
Key 是密码,谁拿到谁花钱。别写进要提交的作业,别发到群里。
| system | 设定:AI 是谁、按什么风格回答 |
| user | 你说的话:问题、要求 |
| assistant | AI 的回答:上一轮说过什么 |
它给每个候选词一个概率,再按概率抽一个。
高分词几乎必中 → 回答稳定。本课演示统一用 0。
冷门词也有机会 → 更有创意,也更容易跑偏。
想让 AI「接着聊」,就把之前的问答都放进 messages 一起发过去。这不是缺陷,是设计。
② 用的是 ① 里创建的 client——先跑 ①。
想让它干活?
把工具挂到这条对话通道上——工具,就是你定义的函数。
理解你的问题,给出回答
写出一段 Python 代码
决定「这件事该怎么做」
读你电脑里的 scores.txt
自己运行一段代码
把结果写进一个新文件
精确地算大数
模型是「猜下一个词」猜出答案的,大数心算常常差几位。要让它干真活,得把「手脚」借给它——这就是 Tool Use。
| 发生了什么 | 代码 | |
|---|---|---|
| 1 | 带上工具清单,发起提问 | llm_with_tools.invoke([HumanMessage("...")]) |
| 2 | AI 返回「调用请求」:工具名 + 参数 | ai_msg.tool_calls |
| 3 | 你的程序执行函数 | calculate.invoke(tool_call) |
| 4 | 执行结果加回对话历史 | messages.append(tool_msg) |
| 5 | 再问一次,AI 给出最终回答 | llm_with_tools.invoke(messages) |
全程 AI 只做两件事:决定调什么、消化结果。真正执行函数的,是你的 Python 程序。
没绑定的工具,AI 完全不知道它存在——你写的 process_scores 也一样,绑了才能用。
没有自己算,也没有瞎编答案:调哪个工具、参数是什么。菜,得你的程序来做。
① 跑完后,把最后一行换成 calculate.schema(),看模型读到的说明书。
② 把 query 改成 "(3+4)*5 是多少"。
AI 看到的是完整历史:问题、点菜单、执行结果——消化了结果,才给出最终回答。
这一格接着用上一页的 messages、ai_msg、calculate。
清单里没有的工具,它可能自己「发明」一个,还说调用成功了。
→ 绑定的工具清单要写全
args 格式不对,函数当场报错。
→ 参数的类型和说明要写清
返回明明是 "Error: Invalid expression",它却说算好了。
→ 返回值的含义要写明
| 工具名 | 输入 | 输出 | 出错时 |
|---|---|---|---|
| calculate(expression) | expression: str,数学表达式 | 结果的字符串 | 返回 "Error: Invalid expression" |
| process_scores(in, out) | 输入文件路径、输出文件路径 | 结果文件 + 有效条数 | 文件不存在 → 报错,提醒用户 |
| 查单词(word, word_list) | 一个单词、一个单词列表 | 位置(从 0 起)或 -1 | 查不到 → 返回 -1,不报错 |
| 你的新工具… | ? | ? | ? |
@tool 定义里的每一行——名字、参数、说明、返回值——填进这张表就是一格。
L7 分组以后,你们要一起把这张表填起来。
改上面的格子,下面的代码和约定表跟着变。互看只查两件事:输入输出是否清晰?能不能单独测试?
① 改写 grade 或换成你自己的工具
② 先单独测试函数(不用模型)
③ 再绑定,看模型会不会点它
把 docstring 删掉再跑——离线模拟模型是按 docstring 找工具的,真模型也一样依赖它。
| 做什么 | 交什么 | |
|---|---|---|
| 作业 ① | 文件读写:读 scores.txt → 扔掉空行和 # 行 → 写入 result.txt,最后一行「共 N 条有效成绩」 | L3_homework.ipynb,result.txt 正好 6 行 |
| 作业 ② | 为学习场景设计 2 个工具 | 每个:盒子图 + 按 @tool 格式写说明(工具名 / 参数及类型 / docstring);写得清楚的下节课现场绑上演示 |
| 选读 | L1_补充_Python数据结构.ipynb | 不计考核;给 L4 铺路 |
交作业前对照六坑自查一遍。别把 API Key 写进要提交的作业。
数据会放进文件了,下一步是把它处理得更聪明。