1
字典 dict
消息结构Agent 里的每一条消息都是一个字典:{"role":"user","content":"你好"}。学会对字典做增删改查,是操作消息历史的基本功。
▍真实 Agent 代码示例
message = {"role": "user", "content": "你好"}
# 查:读取字段
print(message["role"]) # user
# 增:新增字段
message["time"] = "10:30"
# 改:更新内容
message["content"] = "帮我查天气"
# 删:移除字段
del message["time"]
# 字典套字典:一层层取
tool_call = {"name": "search", "args": {"query": "北京天气"}}
print(tool_call["args"]["query"]) # 北京天气
▍练习题
✏️ 任务:
- 新建字典
msg = {"role":"assistant","content":"好的"} - 给 msg 增加字段
tool_calls,值为空列表[] - 把 content 改成 "我来帮你查询"
- 打印嵌套取值:
tc = {"name":"calc","args":{"expr":"1+2"}},取出"1+2"
📝 康锅的笔记
踩坑:写了 tool_call["query"] → 报 KeyError!正确:
tool_call["args"]["query"] —— 字典套字典要一层层取,不能跳层。
2
函数 def
工具封装Agent 的每个工具就是一个函数。def run(user_message: str) -> str 里:参数名、:str 是类型提示(输入字符串)、->str 是返回值类型提示。
▍真实 Agent 代码示例
def add_tool(a: int, b: int) -> dict:
# 工具返回字典,方便 json.dumps 发给模型
return {"result": a + b}
# 调用:函数名 + 括号 + 参数
out = add_tool(1, 2)
print(out["result"]) # 3
▍练习题
✏️ 任务:
- 写函数
def search_tool(query: str) -> dict: - 返回
{"query": query, "result": "模拟结果"} - 调用
search_tool("北京天气")并打印 result 字段
📝 康锅的笔记
三处纠正:①
-> str 应为 -> dict,因为返回的是字典;②
return 必须缩进在函数体内(4个空格),顶格写就跑到函数外了;③ 调用是
add_tool() 不是 run add_tool —— 调用只写函数名加括号,不带 def 不带空格。
3
列表 list
消息历史messages = [...] 是 Agent 的对话历史。append() 追加消息,索引 messages[0] 取第一条,messages[-1] 取最后一条。
▍真实 Agent 代码示例
messages = [{"role": "system", "content": "你是助手"}]
# append:新消息加到末尾
messages.append({"role": "user", "content": "你好"})
# 索引:0 是第一条,-1 是最后一条
print(messages[0]["role"]) # system
print(messages[-1]["content"]) # 你好
print(len(messages)) # 2
▍练习题
✏️ 任务:
- 创建
messages = [],依次 append 三条消息(system / user / assistant) - 打印最后一条消息的 content
- 打印列表长度,验证是 3
📝 康锅的笔记
踩坑:变量名没统一 —— 前面建的是 messages = [],后面却写 for r in results → NameError: name 'results' is not defined。教训:起名后全文统一,写完先扫一眼变量名。
4
for 循环
批量执行工具模型一次可能返回多个工具调用,用 for tc in tool_calls: 逐个遍历执行,把每个结果 append 回 messages。
▍真实 Agent 代码示例
tool_calls = [
{"name": "search", "args": {"query": "天气"}},
{"name": "calc", "args": {"expr": "1+2"}},
]
for tc in tool_calls:
print("执行工具:", tc["name"])
# result = TOOLS[tc["name"]](**tc["args"])
# messages.append({"role":"tool","content":result})
▍练习题
✏️ 任务:
- 用上面的 tool_calls 列表,for 循环打印每次的
tc["args"] - 循环里把
f"已执行 {tc['name']}"append 到一个logs = []列表 - 循环结束后打印 logs
📝 康锅的笔记
踩坑:循环里写了 TOOLS_name = tc["name"]、TOOLS_id = tc["id"],但后面根本没用到这两个变量。教训:废代码应删掉,留着只会误导自己。写完检查:每个变量都被用了吗?
5
if / else 分支
判断是否继续Agent 循环的核心:检查 finish_reason。如果是 "tool_calls" 就执行工具继续循环;否则说明对话结束,break 退出。
▍真实 Agent 代码示例
finish_reason = "tool_calls"
if finish_reason == "tool_calls":
print("需要调用工具,继续循环")
else:
print("对话结束,break")
# 多分支用 elif
if finish_reason == "stop":
pass
elif finish_reason == "tool_calls":
pass
else:
print("未知原因")
▍练习题
✏️ 任务:
- 定义
fr = "stop",用 if/else 判断:是"tool_calls"打印"继续",否则打印"结束" - 改成
fr = "tool_calls"再跑一遍,验证两条分支都通
📝 康锅的笔记
踩坑:写了两个独立的 if:if fr == "tool_calls": ...if fr != "tool_calls": ... ← 多余又易错正确:用
if / else —— 二选一的场景判断一次就够了,else 自动兜底。
6
f-string 字符串拼接
日志输出字符串前加 f,{变量} 会被替换成实际值。Agent 里用它打印日志、拼接提示词,比 + 拼接清晰得多。
▍真实 Agent 代码示例
tool_name = "search"
args = {"query": "北京天气"}
# f-string:{}里放变量或表达式
print(f"🔧 调用工具: {tool_name}")
print(f"📥 参数: {args['query']}")
print(f"第 {1 + 1} 次调用") # {}里还能算表达式
# [] 在 f-string 里是普通字符,直接写
print(f"[工具] {tool_name} 执行完毕")
▍练习题
✏️ 任务:
- 定义
name = "calc"、n = 3 - 用 f-string 打印
🔧 调用 calc,共 3 次 - 打印
[日志] 第 4 次调用(4 用n+1表达式生成)
📝 康锅的笔记
踩坑:写成 f"\[工具\] {tool_name}",结果输出里带着难看的反斜杠。正确:
f"[工具] {tool_name}" —— f-string 里 [ 和 ] 是普通字符,不需要转义。只有 {} 和引号才特殊。
7
import 导入
引入库import json 导入整个模块,用时写 json.loads();from openai import OpenAI 只取出一个名字,直接用 OpenAI()。
▍真实 Agent 代码示例
import json # 整包导入
from openai import OpenAI # 只取 OpenAI 这个类
client = OpenAI() # 直接用,不用写 openai.OpenAI
# 等价写法(也能跑,但前缀啰嗦):
# import openai
# client = openai.OpenAI()
data = json.loads('{"a": 1}') # 整包导入要加模块名前缀
▍练习题
✏️ 任务:
- 用
import math,打印math.sqrt(16) - 改用
from math import sqrt,直接打印sqrt(16) - 体会两种写法的区别
📝 康锅的笔记
❓ 追问:为什么不用
💡 答:两种都能跑!选择原则:
· 只用模块里的一个东西 →
· 要用模块里多个东西 →
client = openai.OpenAI()?💡 答:两种都能跑!选择原则:
· 只用模块里的一个东西 →
from x import y(代码更短)· 要用模块里多个东西 →
import x(加前缀反而清晰)8
json 序列化
数据交换json.dumps() 把 Python 字典转成 JSON 字符串(发给 LLM / 存文件);json.loads() 反过来把字符串解析回字典(读取模型返回的 arguments)。
▍真实 Agent 代码示例
import json
# dumps:字典 → 字符串(发给模型)
result = {"city": "北京", "temp": 26}
s = json.dumps(result, ensure_ascii=False)
print(s) # {"city": "北京", "temp": 26}
# loads:字符串 → 字典(解析模型返回的工具参数)
args_str = '{"query": "北京天气"}'
args = json.loads(args_str)
print(args["query"]) # 北京天气
▍练习题
✏️ 任务:
- 把
{"tool":"search","query":"上海天气"}dumps 成字符串(带 ensure_ascii=False)并打印 - 再 loads 回来,取出 query 打印
- 去掉 ensure_ascii=False 再 dumps 一次,对比输出差异
📝 康锅的笔记
❓ 追问:
💡 答:不写它,中文会变乱码
原则:传中文给 LLM,永远加上 ensure_ascii=False。
ensure_ascii=False 是干嘛的?💡 答:不写它,中文会变乱码
\u5317\u4eac(ASCII 转义);加上才输出正常中文 "北京"。原则:传中文给 LLM,永远加上 ensure_ascii=False。
🎯 练习提示
这 8 个模式覆盖了 90% 的 Agent 基础代码,不用贪多,把它们练到肌肉记忆
⏰每天 20 分钟不贪多,一次练 2~3 个模式,保持手感比突击有效。
📅两天过一遍第一天 1~4 模式,第二天 5~8 模式,循环滚动复习。
⌨️不看答案敲出来即过合上页面能独立敲对,才算真正掌握。卡住再看示例。
🐛报错是好事每个 KeyError / NameError 都是加深记忆的机会,记进你自己的笔记。