📖 Day 5 学习页:阶段一综合实战:命令行 AI 助手
Day 5:阶段一综合实战——命令行 AI 助手
🎯 今日目标:把 Day 2-4 的三块积木(Pydantic 模型、工具注册、流式输出 + Function Calling) 拼装成一个完整程序。今天几乎没有新知识,考的是组装能力——这正是"能跑 demo"和"能写项目"的分水岭。
⏱ 今日安排(约 2 小时)
| 时间 | 内容 | 方式 |
|---|---|---|
| 15min | 架构梳理:看懂组件关系图 | 阅读 |
| 90min | 动手实操:完成 7 个 TODO,拼出完整 Agent | 编码 |
| 15min | 验收 + 交互测试:跑验收脚本,玩一下自己的 CLI | 验收 |
📖 第一步:看懂架构(今天的"理论"就这一张图)
┌─────────────────────────── CLI_Agent ───────────────────────────┐
│ │
│ messages: List[ChatMessage] ← 全部状态都在这(Pydantic 模型) │
│ │
│ chat(user_input): │
│ 1. messages.append(user 消息) │
│ 2. for round in range(max_tool_rounds): ← 有上限! │
│ async for chunk in client.chat(messages, tools): │
│ │ ← StreamingLLMClient │
│ ├─ chunk 是 [TOOL_CALL] 标记? │
│ │ └─ 解析 → execute_tool() → 追加 tool 消息 │
│ └─ 普通文本 → 累积 + (可选)实时打印 │
│ 有工具调用 → continue 下一轮(LLM 会引用工具结果) │
│ 没有工具调用 → return 最终回答 │
└──────────────────────────────────────────────────────────────────┘
数据流向:用户输入 → LLM(流式)→ [TOOL_CALL] → 执行工具 → tool 消息回传 → LLM 再流式 → 最终回答。
今天 mock 的流式客户端会在第一轮流式输出文字 + 一个 [TOOL_CALL] 工具名: {参数JSON} 标记,
在第二轮(messages 里已有 tool 消息时)输出引用工具结果的最终回答——你的主循环要正确处理这两个阶段。
🛠 第二步:动手实操
- 打开模板:day05_practice_template.py,7 个 TODO:
- TODO 1.1:可以补充 ToolResult 等 Pydantic 模型(Day 2 功底)
- TODO 2.2:
get_tools_for_api()(Day 4 原题) - TODO 2.3:
execute_tool()(Day 4 原题) - TODO 3.1:
StreamingLLMClient.chat()改造成异步生成器(Day 3 功底:async def + yield) - TODO 4.1:
CLI_Agent.chat()主循环(核心,按架构图写)
- 运行自测:
python day05_practice_template.py,3 个测试应看到流式输出和工具调用链。 - 玩自己的 Agent:把文件末尾
asyncio.run(run_cli())的注释打开,和它对话: 输入"帮我计算 3 + 5 * 2"、"北京天气"、"你好"(不走工具)、"clear"(清历史)、"exit"。 - 验收:
python day05_practice_validator.py—— 验收会构建你的 Agent 发起真实对话, 检查回答里是否包含工具算出的 13(工具没真的执行是混不过去的),并检查 messages 里有没有 role=tool 的回传消息。
⚠️ 避坑
- ❌ 解析
[TOOL_CALL]后忘记把工具结果追加成role=tool消息 → 下一轮 LLM 拿不到结果 - ❌ 主循环没有轮数上限 / 没有终止条件 → 验收直接超时判 0 分
- ❌ 在 async 循环里直接用
input()阻塞事件循环(模板 run_cli 已示范asyncio.to_thread的正确写法) - ✅ stream=False 时不要打印 chunk,返回完整字符串——验收就是这么调你的
✅ 阶段一结业标准
完成今天后你应该能不查资料手写:Pydantic Schema、工具注册装饰器、异步生成器、Function Calling 主循环。 明天开始进入真正的 Agent 架构。