第3章 环境准备与最小Agent跑通

本章你将学到

  • 配置DeepSeek API Key,让你的Agent拥有调用大模型的能力
  • 用Trae生成一个带 read_file 工具的Agent脚本
  • 理解工具定义、工具调用循环、异常处理在完整脚本中的位置
  • 首次运行Agent,让它真正读取你硬盘上的文件

本章你将产出:一个能读取CSV文件并描述其内容的Agent脚本 agent.py,以及成功运行后的一份终端日志
全部章节:收录在专栏《AI应用工程化实战教程》之【智能体工具使用实战】


3.1 DeepSeek API Key获取与配置

在第2章,你已经手写过 minimal_agent.py,在那里面你已经在 .env 文件中配置了DeepSeek的API Key。如果你已经完成了那一步,可以跳过3.1.1和3.1.2,直接进入3.1.3确认配置正确。

3.1.1 注册DeepSeek开放平台

打开浏览器,访问 platform.deepseek.com

点击“注册”,使用手机号或邮箱完成注册。

注册完成后,进入控制台,在左侧菜单找到“API Keys”或“API密钥”页面。点击“创建API Key”,给密钥起个名字(比如 data-analyst-agent),然后复制生成的密钥。

密钥格式:通常以 sk- 开头,后面跟着一串字符。这个密钥只显示一次,请立即保存。

3.1.2 在项目中配置环境变量

打开你的 data-analyst-agent 项目文件夹。如果你还没有创建 .env 文件,现在创建一个。

在Trae的文件树中,右键项目根目录 → 新建文件 → 命名为 .env

.env 文件中写入:

DEEPSEEK_API_KEY=sk-你的密钥

注意:sk-你的密钥 替换成你刚才复制的那一整串。等号两边不要有空格。

确认 .gitignore 已包含 .env。打开项目根目录下的 .gitignore 文件,确认其中有这一行:

.env

如果没有,加上。这是你在第一部第3章养成的习惯——绝不把API密钥提交到Git仓库

3.1.3 安装依赖

在Trae底部打开终端,确认当前在 data-analyst-agent 目录下,执行:

pip install openai python-dotenv

如果你在第2章已经装过,可以跳过。

3.1.4 验证配置

创建一个临时测试脚本验证配置是否正确。在Trae终端中运行:

python -c "
import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
client = OpenAI(
    api_key=os.getenv('DEEPSEEK_API_KEY'),
    base_url='https://api.deepseek.com'
)
response = client.chat.completions.create(
    model='deepseek-chat',
    messages=[{'role': 'user', 'content': '回复“配置成功”'}]
)
print(response.choices[0].message.content)
"

如果终端打印出“配置成功”(或类似的确认回复),说明DeepSeek API Key配置正确。

如果报 AuthenticationError401 错误,检查:

  • .env 文件中的密钥是否复制完整(没有多余空格)
  • .env 文件是否在项目根目录
  • 密钥是否还在有效期内

3.2 一个重要的再次确认:谁在用哪个模型

在正式开始之前,回顾一下前言中提到的一个重要区分。

现在你的开发环境里涉及两种AI调用:

Trae内置AI 你的Agent调用的AI
谁在用 你(开发者),在Trae中写代码时 你写的 agent.py,在运行时
干什么 帮你生成代码、回答开发问题 执行数据分析任务、决定调用哪个工具
模型 Trae自动选择,你不需要管 DeepSeek(你在 .env 中配置的)
API Key Trae自带 你刚才在 .env 中配置的

你在Trae的AI对话面板里输入指令时,是Trae内置AI在帮你干活——它生成 agent.py 的代码。而当 agent.py 运行起来、需要分析数据时,是DeepSeek在干活——它决定“该调用哪个工具”,生成分析结论。

Trae帮你写代码,DeepSeek帮你写的Agent完成任务。 别搞混。


3.3 用Trae生成第一个带工具的Agent脚本

环境就绪,现在用Trae帮你生成一个带 read_file 工具的Agent。

3.3.1 设计给Trae的指令

在Trae的AI对话面板中,输入以下指令:

在项目 data-analyst-agent 中,请帮我创建一个 agent.py 脚本。

这个脚本是一个带工具调用能力的Agent。要求:

## 模型配置
- 使用 openai 库调用 DeepSeek API
- 从 .env 读取 DEEPSEEK_API_KEY
- base_url 为 https://api.deepseek.com
- model 为 deepseek-v4-flash

## 系统提示词
- 角色:你是一个数据分析助手。你可以使用工具来读取文件、分析数据。
- 行为:当用户让你分析某个文件时,先使用 read_file 工具读取文件内容,再给出分析。

## 工具:read_file
- 功能:读取指定路径的文件内容
- 参数:
  - path(必填):文件路径,字符串
  - encoding(可选):编码,默认 "utf-8"
- 返回:文件内容的字符串。如果文件不存在或读取失败,返回错误信息字符串。

## 工具调用循环
- 实现完整的 Function Calling 循环
- 最多循环10次
- 每次调用后在终端打印工具名和参数
- 如果没有 tool_calls,打印模型回答并结束

## 核心函数
- 函数签名:run_agent(user_query: str) -> str
- 返回模型的最终回答

## 脚本底部测试
- 测试:用户请求为 "请读取 scores.csv 文件,告诉我这份数据的基本情况"
- 确保 scores.csv 存在(如果不存在,先生成一个包含学号、姓名、高数、线代、Python、英语列及10行模拟数据的文件)

## 代码风格
- 清晰注释
- 关键步骤用 print 输出日志
3.3.2 审查Trae生成的代码

Trae生成代码后,不要立刻运行。打开 agent.py,对照以下检查清单审查:

模型配置检查

  • OpenAI 客户端的 api_key 是否从环境变量读取?
  • base_url 是否指向 https://api.deepseek.com
  • model 是否为 deepseek-v4-flash

工具定义检查

  • read_file 工具的 type 是否为 "function"
  • description 是否清晰描述了工具的用途和限制?
  • parameterspath 是否为必填?
  • encoding 参数是否有默认值?

工具实现检查

  • read_file 函数是否用 try...except 包裹了文件操作?
  • 文件不存在时,是否返回了清晰的错误信息(而不是让程序崩溃)?
  • 编码错误时,是否有处理逻辑?

工具调用循环检查

  • 是否有循环(for turn in range(10))?
  • 每次循环是否调用了 client.chat.completions.create
  • 是否检查了 msg.tool_calls 是否为空?
  • 处理 tool_calls 时,是否正确解析了 function.namefunction.arguments
  • 工具执行结果是否以 role: "tool" 追加到了 messages
  • 是否保留了 tool_call_id

测试入口检查

  • if __name__ == "__main__": 是否调用了 run_agent
  • 测试的用户请求是否合理?
  • 是否确保测试文件存在?

如果发现任何问题,在Trae对话面板中直接指出,要求修正。例如:

agent.py 中 read_file 函数在文件不存在时没有返回错误信息,而是抛出了异常。请改为返回字符串 "错误:文件 [路径] 不存在"。

3.4 首次运行:让Agent读取一个真实的CSV文件

3.4.1 准备测试数据

在项目目录下创建 scores.csv。你可以在Trae中新建文件,复制以下内容:

学号,姓名,高数,线代,Python,英语
2024001,张三,78,82,90,75
2024002,李四,55,68,72,80
2024003,王五,92,88,95,91
2024004,赵六,61,59,70,68
2024005,孙七,43,55,60,72
2024006,周八,88,90,85,93
2024007,吴九,72,71,68,70
2024008,郑十,95,89,92,88
2024009,陈一,60,61,58,65
2024010,林二,77,80,82,79

或者让Trae帮你生成:在对话面板中输入“请在项目根目录创建一个 scores.csv,包含10行模拟学生成绩数据”。

3.4.2 运行Agent

在Trae终端中执行:

python agent.py

观察终端输出。你应该看到类似以下的日志:

============================================================
用户: 请读取 scores.csv 文件,告诉我这份数据的基本情况
============================================================

--- 第 1 轮调用模型 ---
模型要求调用 1 个工具
  → 调用工具: read_file({'path': 'scores.csv'})
  → 工具返回: 学号,姓名,高数,线代,Python,英语
2024001,张三,78,82,90,75
2024002,李四,55,68,72,80
...

--- 第 2 轮调用模型 ---
模型回答: 这份数据是10名学生的期末考试成绩表,包含以下信息:

- 共有4门课程:高数、线代、Python程序设计、大学英语
- 10名学生中...
- 各科成绩分布情况...

如果你看到这样的日志,恭喜你——你的Agent第一次真正“看到了”你硬盘上的文件。

注意第1轮和第2轮的区别:

  • 第1轮:Agent判断“我需要先读到文件内容”,于是它返回了 tool_calls,调用了 read_file是Python的 open() 函数真正打开了文件,不是AI在“猜测”文件内容。
  • 第2轮:Agent拿到了真实的文件内容,基于这些内容生成了分析。

这和你在第1章的经历完全不同。第1章的Agent只能泛泛地说“你可以用pandas读取”,现在它真正读了。

3.4.3 如果运行出错

报错:FileNotFoundError 或 “错误:文件不存在”
scores.csv 可能不在项目根目录。确认文件位置,或者使用绝对路径测试。

报错:AuthenticationError
.env 中的 DEEPSEEK_API_KEY 配置有误。重新检查3.1.4节。

Agent反复调用同一个工具,陷入死循环
→ 模型的判断出了问题,或者工具返回的内容没有被正确解析。检查 role: "tool" 的那条消息是否正确追加到了 messages

Agent没有调用工具,直接回答“我无法读取文件”
→ 检查工具定义是否在调用API时正确传入(tools 参数)。检查 read_filedescription 是否清晰说明“这个工具可以读取本地文件”。


3.5 理解你的Agent脚本结构

现在花五分钟,重新打开 agent.py,理解它的完整结构。这个结构会成为你后续所有工具增强型Agent的基础模板。

agent.py
│
├── 1. 导入与初始化
│   ├── import os, json
│   ├── from dotenv import load_dotenv
│   ├── from openai import OpenAI
│   └── client = OpenAI(api_key=..., base_url=...)
│
├── 2. 工具定义(给AI看的描述)
│   └── tools = [read_file_tool, ...]
│       每个工具: {"type": "function", "function": {"name": ..., "description": ..., "parameters": ...}}
│
├── 3. 工具实现(给代码执行的函数)
│   └── def read_file(path, encoding="utf-8"):
│       实际的文件读取逻辑 + 异常处理
│
├── 4. 工具映射表
│   └── TOOL_MAP = {"read_file": read_file, ...}
│
├── 5. 工具调用循环
│   └── def run_agent(user_query):
│       ├── messages = [system_prompt, user_query]
│       └── for turn in range(10):
│           ├── response = client.chat.completions.create(messages, tools)
│           ├── if no tool_calls → break, return content
│           └── if tool_calls → execute, append result, continue
│
├── 6. 测试入口
│   └── if __name__ == "__main__":
│       run_agent("测试请求")

这个结构的核心设计原则是分离关注点

  • 工具定义和工具实现分离——前者是写给AI看的界面说明,后者是真正的业务代码。改工具描述不会影响工具实现,改工具实现不会影响AI对工具的理解(只要接口不变)。
  • 工具调用循环和具体工具分离——循环只负责“调API→解析tool_calls→执行工具→追加结果”,它不关心具体工具的细节。新增一个工具,只需要在工具列表和TOOL_MAP里各加一项,循环不用改。
  • 运行逻辑和测试分离——run_agent 是核心函数,可以在任何地方被调用。底部的 if __name__ == "__main__" 只用于本地测试。

3.6 本章小结

  • DeepSeek API Key配置三步走:注册 → 创建Key → 写入 .env 文件。
  • Trae帮你写Agent代码:你给出清晰的指令(模型配置、工具定义、循环逻辑、测试需求),Trae生成完整脚本。你审查、修改、运行。
  • Agent首次读取了真实文件:不是AI“想象”文件内容,是Python的 open() 真正打开了 scores.csv。你亲眼看到终端日志里 read_file 的调用和返回。
  • Agent脚本的标准结构:六个模块(初始化、工具定义、工具实现、工具映射、调用循环、测试入口),关注点分离,易于扩展。
  • 再次确认:Trae帮你生成 agent.py,DeepSeek帮你写的Agent分析数据。两个模型各干各的。

现在你的Agent只有一个工具——read_file。它能读文件,但还不能执行计算。下一章,我们将解决一个必须面对的问题:当Agent需要执行Python代码时,如何确保它生成的代码不会损害你的电脑?我们将设计一个执行沙盒,并为Agent添加第二个工具——execute_python


课后练习

  1. 修改 agent.pyread_file 工具的描述,把“读取文件内容”改成“读取本地图片文件”。然后用之前一样的请求(读取scores.csv)重新运行。Agent的行为会有什么变化?它还会调用 read_file 吗?
  2. scores.csv 中增加一列“物理”成绩(随便填10个分数)。重新运行Agent。观察Agent能否自动识别新增的列并纳入分析?
  3. 尝试让Agent读取一个不存在的文件(比如把测试请求中的 scores.csv 改成 nonexistent.csv)。Agent的处理方式是什么?它在第几轮发现了问题?错误信息清晰吗?
  4. (预备思考)在 agent.py 中,如果Agent调用 read_file 后拿到了一万行数据,直接全部塞进对话历史会发生什么?这引出了工具增强型Agent的一个重要工程挑战——上下文窗口管理。你能想到什么解决方案?记下你的想法,我们将在后续章节讨论。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐