PYTHON × AGENT 开发基础

Agent 8 模式微练习

掌握这 8 个 Python 基础模式,你就能看懂并写出真正的 Agent 代码

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"])  # 北京天气

▍练习题

✏️ 任务:
  1. 新建字典 msg = {"role":"assistant","content":"好的"}
  2. 给 msg 增加字段 tool_calls,值为空列表 []
  3. 把 content 改成 "我来帮你查询"
  4. 打印嵌套取值: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

▍练习题

✏️ 任务:
  1. 写函数 def search_tool(query: str) -> dict:
  2. 返回 {"query": query, "result": "模拟结果"}
  3. 调用 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

▍练习题

✏️ 任务:
  1. 创建 messages = [],依次 append 三条消息(system / user / assistant)
  2. 打印最后一条消息的 content
  3. 打印列表长度,验证是 3
📝 康锅的笔记
踩坑:变量名没统一 —— 前面建的是 messages = [],后面却写 for r in resultsNameError: 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})

▍练习题

✏️ 任务:
  1. 用上面的 tool_calls 列表,for 循环打印每次的 tc["args"]
  2. 循环里把 f"已执行 {tc['name']}" append 到一个 logs = [] 列表
  3. 循环结束后打印 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("未知原因")

▍练习题

✏️ 任务:
  1. 定义 fr = "stop",用 if/else 判断:是 "tool_calls" 打印"继续",否则打印"结束"
  2. 改成 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} 执行完毕")

▍练习题

✏️ 任务:
  1. 定义 name = "calc"n = 3
  2. 用 f-string 打印 🔧 调用 calc,共 3 次
  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}')   # 整包导入要加模块名前缀

▍练习题

✏️ 任务:
  1. import math,打印 math.sqrt(16)
  2. 改用 from math import sqrt,直接打印 sqrt(16)
  3. 体会两种写法的区别
📝 康锅的笔记
❓ 追问:为什么不用 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"])   # 北京天气

▍练习题

✏️ 任务:
  1. {"tool":"search","query":"上海天气"} dumps 成字符串(带 ensure_ascii=False)并打印
  2. 再 loads 回来,取出 query 打印
  3. 去掉 ensure_ascii=False 再 dumps 一次,对比输出差异
📝 康锅的笔记
❓ 追问:ensure_ascii=False 是干嘛的?
💡 答:不写它,中文会变乱码 \u5317\u4eac(ASCII 转义);加上才输出正常中文 "北京"。
原则:传中文给 LLM,永远加上 ensure_ascii=False。

🎯 练习提示

这 8 个模式覆盖了 90% 的 Agent 基础代码,不用贪多,把它们练到肌肉记忆

每天 20 分钟不贪多,一次练 2~3 个模式,保持手感比突击有效。
📅两天过一遍第一天 1~4 模式,第二天 5~8 模式,循环滚动复习。
⌨️不看答案敲出来即过合上页面能独立敲对,才算真正掌握。卡住再看示例。
🐛报错是好事每个 KeyError / NameError 都是加深记忆的机会,记进你自己的笔记。
``` 页面已完成,包含: - **8 个模式卡片**:每个都有真实 Agent 代码示例(带语法高亮:关键字红、字符串蓝、函数紫、注释灰)、功能说明、编号练习题,以及完整的"📝 康锅的笔记"(含所有指定的踩坑记录和追问解答) - **主题切换**:默认暗色 `#0a0e14`,右上角按钮切换亮色,并用 localStorage 记住选择 - **交互设计**:卡片 hover 悬浮上移 + 边框发光、顶部快速导航锚点跳转 - **手机适配**:600px 以下自动缩小内边距和代码字号,提示网格自动折行 - **底部练习提示**:每天20分钟 / 两天过一遍 / 不看答案敲出来即过 / 报错是好事 四宫格 直接保存为 `.html` 文件用浏览器打开即可使用。