【零基础学AI】第 30 章——对话记录与结构化输出

第 30 章 对话记录与结构化输出

文章目录

给学习计划助手一句"我想入门 Python,每天只有20分钟",程序得到一张任务表。再说"改成10分钟,保留动手代码练习",程序需要把前一轮要求带进去,也要检查新任务合计确实是10分钟。

本章把这两个需求放进一个小项目。五组输入及固定回应为原创教学数据,用来验证程序。

🌈 关于《AI 零基础 36 讲》

🧠 零基础也能开始学 AI

《AI 零基础 36 讲》是一套面向初学者的 AI 入门教程。课程从认识和使用 AI 开始,逐步学习

Python、数据处理、机器学习与深度学习,再进入大模型应用开发,最后完成个人知识库助手的综合实践。

📖 本篇是正式讲解文章

通过具体例子、原创图示和操作结果讲清概念。涉及编程的章节提供可运行代码,方便你边读边试。

🎯 读完以后,动手检验

建议按专栏顺序学习,并完成对应的课后训练,检查自己能否独立运用本章知识。

博客专栏:《AI 零基础 36 讲》

多轮对话需要自己携带历史

第 29 章每次只发送系统要求和一个问题。本章在消息列表中加入以前的用户需求与助手回答,再追加最新用户需求。

对话保存在会话文件中,消息按 user、assistant 成对排列。重新启动程序后,可以读取已经保存的记录。这个项目没有把记忆写进模型参数,历史来自本地文件并随请求发送。

历史会逐渐增长。本章最多保留最近三对有效消息,并对一次请求设置2000字符预算。超出时从最早的完整消息对开始删除,同时保留系统要求和最新用户需求。如果这两部分本身已经太长,就要求缩短需求,不发送请求。

字符预算方便初学者先看懂裁剪过程。它不能直接代表模型 Token 上限,实际生产程序还需要使用对应模型的 Token 计数方法和上下文限制。

先观察裁剪后角色的顺序。

python 复制代码
import json
from plan_core import build_messages

plan = {"goal": "Python", "daily_minutes": 20,
        "tasks": [{"title": "练习变量", "minutes": 20}]}
history = []
for i in range(3):
    history += [{"role": "user", "content": f"第{i+1}轮需求"},
                {"role": "assistant", "content": json.dumps(plan)}]
messages = build_messages(history, "改为10分钟", 10, max_pairs=1)
print([item["role"] for item in messages])
print(messages[1]["content"], messages[-1]["content"])

输出如下。

text 复制代码
['system', 'user', 'assistant', 'user']
第3轮需求 改为10分钟

最早两对没有发送。最新一对仍然包含用户和助手,避免只剩一半。系统要求中的当前分钟预算也会更新。

让计划成为可以计算的数据

如果回答只是一段自然语言,程序很难可靠地算出时间。本章要求返回一个 JSON 对象,字段固定为 goal、daily_minutes 和 tasks。

json 复制代码
{
  "goal": "入门 Python",
  "daily_minutes": 20,
  "tasks": [
    {"title": "阅读变量示例", "minutes": 8},
    {"title": "改写并运行两行代码", "minutes": 12}
  ]
}

这是第一组教学计划。goal 保存目标,daily_minutes 保存本轮预算,tasks 是任务列表,每个任务包含 title 和 minutes。接下来程序可以计算 8 加 12,也可以把任务显示成表格。

数据结构文件用 JSON Schema 描述类型、必需字段和列表范围。required 表示不能缺少的字段,additionalProperties=false 表示不能随意增加字段。

本章写了专用 Python 校验函数,逐项检查这个固定结构,没有实现一个通用 JSON Schema 引擎。空白目标、布尔值分钟和任务总时长另有业务检查。比如 Python 中 True 与整数有继承关系,分钟检查使用 type(value) is int,避免把 True 当作一分钟。

JSON 模式之后,还要做本地校验

客户端设置 response_format 为 json_object,系统要求包含 JSON 字样和一个形状示例。设置方法核对 DeepSeek JSON Output 文档。系统要求还说明输出应包含哪些字段,以及本轮分钟预算。

JSON 模式约束格式。程序仍需检查对象字段、字段类型、分钟总和及结束原因。一个可解析的对象可能缺少 tasks,也可能把 daily_minutes 写成字符串。

校验顺序从文本开始。先检查回答是否正常结束,再用 json.loads 转成对象,检查必需字段和类型,最后计算任务分钟之和。

python 复制代码
from plan_core import parse_plan

text = '{"goal":"Python","daily_minutes":20,"tasks":[{"title":"练习变量","minutes":20}]}'
plan = parse_plan(text, 20)
print(plan["goal"])
print(sum(task["minutes"] for task in plan["tasks"]))

输出为 Python 和 20。当前预算显式传入校验函数,所以即使模型返回一份30分钟的合法 JSON,也不能通过这次20分钟的任务检查。

校验通过说明程序能处理这些字段,以及已检查的时间约束成立。它不能证明任务难度适合读者、学习目标一定能完成或计划内容没有事实错误。读者仍需查看具体安排。

格式失败后,只修复一次

收到"这是一份计划"时,JSON 解析失败;收到只有 goal 的对象时,必需字段检查失败。程序把具体校验原因放进修复消息,再请求一次完整 JSON。

下面用固定回应模拟第一份对象缺字段,第二份补齐。

python 复制代码
from plan_core import empty_session, make_plan
from run_cases import TeachingReplay

client = TeachingReplay([
    '{"goal":"Python"}',
    '{"goal":"Python","daily_minutes":20,"tasks":[{"title":"练习变量","minutes":20}]}',
])
state, attempts = make_plan(empty_session(), "学Python", 20, client)
print(len(attempts), [item["valid"] for item in attempts])
print([item["role"] for item in state["history"]])

输出如下。

text 复制代码
2 [False, True]
['user', 'assistant']

临时修复消息没有混进长期历史。会话只保存原始需求和已经通过校验的计划。两次回答仍然失败时,程序结束本轮,原会话保持不变。缺失密钥、认证失败、限流和网络错误沿用第 29 章处理,不在这里自动重试。

真实模型的格式修复可能增加一次调用与用量。默认最多一次修复,命令行 --repair 0 可以关闭。修复请求也受字符预算限制,错误文本最多截取400字符,避免把过长无效回答重新发回去。

五组输入的实际程序结果

执行本地样例入口。

powershell 复制代码
py -3.12 examples/run_cases.py

它读取五组原创输入和计划,经过与真实入口相同的解析、字段及分钟校验流程,记录结果。

教学输入主题 当前预算 任务分钟 校验
Python 入门 20 8、12 通过
英语词汇复习 30 10、15、5 通过
线性回归 25 10、15 通过
表格清洗 15 6、9 通过
注意力计算 40 15、20、5 通过

完整结果还保存了20分钟改为10分钟的两轮记录、会话保存重载一致性、无效 JSON 和缺字段修复。五组固定回应没有进行模型生成,也没有验证自然语言理解效果。

接上真实模型

真实入口复用第 29 章的本机密钥与集中配置。第一次请求明确提供分钟预算。

powershell 复制代码
py -3.12 examples/plan_assistant.py "我想入门 Python,每天只有20分钟" --minutes 20

再次运行时,程序读取会话文件。修改分钟预算也要显式修改参数,避免只依赖自然语言推断。

powershell 复制代码
py -3.12 examples/plan_assistant.py "改成10分钟,保留动手代码练习" --minutes 10

本轮全部尝试的回答与用量另存独立记录,长期会话保存最近的有效计划。若读取到损坏的会话文件,程序报告错误并保留原文件。

本次真实模型验证

2026 年 10 月 4 日,向 deepseek-flash 发送同一组五个需求,返回任务分钟分别如下。

输入主题 预算 真实响应中的任务分钟 本次 Token 总量
Python 入门 20 10、10 213
英语词汇 30 10、12、8 225
线性回归 25 10、10、5 224
表格清洗 15 5、5、5 223
注意力计算 40 10、10、10、10 240

五份对象均通过字段、类型、当前预算和分钟求和校验。随后加载第一份真实计划,发送"改成10分钟,保留动手代码练习"。真实回应将任务改为4分钟基础语法和6分钟动手代码,校验通过,用量275个 Token。

这六次请求都一次通过,没有格式修复,合计1400个 Token。见五组真实结果与多轮记录。真实命令行入口另发送一个变量练习需求,返回一项20分钟任务,保存会话和独立用量记录,用量204个 Token。

本章真实验证合计七次请求、1604个 Token。输入为作者编制的教学需求,输出为当次平台实际生成。格式错误与缺字段仍通过本地固定失败案例验证,没有为了制造失败重复发送付费请求。

模型给出的任务分钟相加正确,计划是否适合自己的基础仍需阅读判断。先完成【零基础学AI】第 30 章课后练习与答案,下一章再让助手查找个人资料。

相关推荐
泯泷1 小时前
让知识"长骨架"——RAPTOR、GraphRAG 与 OpenViking(四)
人工智能·llm
kaixin_啊啊1 小时前
【零基础学AI】第 30 章课后练习与答案
人工智能
电科_银尘1 小时前
【深度学习项目实战·33】自定义数据集:把自己的图片接入PyTorch
人工智能·pytorch·深度学习
武子康2 小时前
单卡 A6000 跑 Qwen-Drive:怎样留下可比较的第一条基线?
linux·人工智能·深度学习
严肃小白兔2 小时前
第 06/24 期:神经网络的反向传播算法:从链式法则到梯度计算
人工智能·神经网络·算法
后端小肥肠2 小时前
公众号漫画又挖到长尾赛道:家常菜美食,流量稳定长期有收益
人工智能·aigc·agent
泯泷2 小时前
从"检索管道"到"主动助理"——Agentic RAG 与双层记忆(五)
人工智能·llm·agent
武子康3 小时前
ComfyUI 加载了动作 LoRA,H3-World 的时间绑定就接好了吗?
人工智能·机器学习·claude