📢gitzw.com上线了,功能陆续更新中,如有问题或反馈请在下方反馈/建议中给我们留言。
AgentGPT 进阶:把自主 AI 代理稳稳部署到生产环境

AgentGPT 进阶:把自主 AI 代理稳稳部署到生产环境

📌 本文速覽

本教程面向已有基础、想将 AgentGPT 用于实际项目的开发者。你将学会从源码部署、配置自定义工具、优化性能到监控运维的全流程,最终能独立搭建并维护一个生产级的自主 AI 代理服务。

🎯 进阶📖 14 章⏱ ≈169 分鐘讀完🔄 更新於 2026-06-25
源專案:github.com/reworkd/AgentGPT★ 36,226

1. 1. 从零搭建本地开发环境:克隆、配置与首次启动

第 1 章:从零搭建本地开发环境:克隆、配置与首次启动

这一章的目标很简单:让你能在自己电脑上跑起来 AgentGPT,看到那个聊天界面,并且能跟它说上话。别被“自主 AI 代理”这种词吓到,本质上就是一套前后端代码,我们把它拉下来、装好依赖、启动服务,然后打开浏览器就能用。

开始之前,先确认你电脑上有什么

AgentGPT 依赖几个基础工具,缺一个后面就会报错。花两分钟检查一下:

  • 编辑器:推荐 VS Code,不是必须,但后面调试方便。
  • Node.js:版本至少 16+,最好 18 或 20。终端里跑 node -v 看看。
  • Git:用来克隆代码。git --version 确认。
  • Docker Desktop:这个最重要。AgentGPT 用 Docker 跑数据库和后台服务。装好后打开 Docker 应用,登录账号(免费),确保它在后台运行。docker --versiondocker compose version 都能正常输出才行。
  • OpenAI API Key:去 platform.openai.com 注册,拿到一串以 sk- 开头的密钥。先记下来,后面配置要用。
  • Serper API Key(可选但推荐):去 serper.dev 注册免费账号,拿到 API key。没有它 Agent 也能跑,但搜索功能会失效。

第一步:把代码拉到本地

打开终端,找个你习惯放项目的目录(比如 ~/projectsD:\code),然后跑:

git clone https://github.com/reworkd/AgentGPT.git
cd AgentGPT

跑完后你会看到 AgentGPT 文件夹,里面就是完整的项目代码。ls(Mac/Linux)或 dir(Windows)看一眼,能看到 nextplatformdb 这些子目录——分别对应前端、后端和数据库配置。

第二步:用自动脚本一键配置

项目自带了一个 setup 脚本,它会帮你做三件事:复制环境变量模板、安装依赖、启动 Docker 容器。省掉你手动一个个敲命令的麻烦。

Mac/Linux 用户

./setup.sh

Windows 用户(在 Git Bash 或 PowerShell 里):

./setup.bat

脚本跑起来后,它会先问你要 OpenAI API Key。把刚才记下的 sk-... 粘贴进去,回车。接着问 Serper API Key,如果你有就填,没有直接回车跳过。还会问 Replicate API Token,同样可选,跳过就行。

脚本接下来会自动做这些事情:

  • .env.example 复制成 .env,并把你的 API Key 填进去
  • npm install 安装前端依赖
  • pip install 安装后端依赖(它会在 Docker 里处理)
  • 启动 Docker Compose,拉起 MySQL 数据库和后端服务

这个过程大概需要 3-5 分钟,取决于你的网络速度。你会看到终端里一堆日志在滚动,别慌,那是正常现象。

预期结果:脚本结束后,终端最后几行应该显示类似这样的信息:

Frontend: http://localhost:3000
Backend: http://localhost:8000
Database: localhost:3306

如果看到报错,最常见的原因是:

  • Docker 没启动:去打开 Docker Desktop 应用,等它状态变成绿色再重试。
  • 端口被占用:3000 或 8000 端口被其他程序占了。关掉冲突的程序,或者改 .env 里的端口配置。
  • Node.js 版本太低:升级到 18 以上。

第三步:手动检查环境变量

脚本已经帮你填好了 API Key,但最好看一眼确认。打开项目根目录下的 .env 文件(用编辑器打开),内容大概长这样:

# 必须填
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 可选
SERPER_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
REPLICATE_API_TOKEN=
# 数据库配置(脚本自动生成,不用改)
DATABASE_URL=mysql://user:password@localhost:3306/agentgpt

确保 OPENAI_API_KEY 那一行不是空的,而且值以 sk- 开头。如果为空,手动填进去。

第四步:启动所有服务

如果脚本已经帮你启动了服务,这一步可以跳过。但如果你中途关掉了终端,或者想重新启动,用这个命令:

docker compose up -d

-d 参数让容器在后台运行,这样终端就不会被日志刷屏。想看日志可以用 docker compose logs -f

启动后,确认所有容器都正常运行:

docker compose ps

你应该看到三个容器状态都是 Up

  • agentgpt-db-1(MySQL 数据库)
  • agentgpt-platform-1(后端 FastAPI 服务)
  • agentgpt-next-1(前端 Next.js 应用)

第五步:打开浏览器,见证奇迹

打开 Chrome 或 Edge,地址栏输入:

http://localhost:3000

你应该能看到 AgentGPT 的界面——一个深色主题的聊天页面,中间有个输入框,写着“Name your AI agent”之类的提示。

如果页面空白或报错

  • 检查终端里有没有报错日志。前端报错看 docker compose logs next,后端报错看 docker compose logs platform
  • 最常见的原因是 API Key 无效或没填。去 .env 里确认。
  • 如果页面加载但一直转圈,可能是后端没连上数据库。等几秒再刷新。

小试牛刀:创建一个 Agent

在输入框里给你的 Agent 起个名字,比如“调研小助手”,然后在目标框里写一个简单的任务,比如:

搜索“2024 年 AI 代理框架排名”,总结前三个框架的特点。

点击“Deploy Agent”按钮。如果一切正常,你会看到 Agent 开始思考、列出任务、执行任务,最后给出结果。这个过程会调用 OpenAI 的 API,所以会消耗你的 API 额度,但一个简单任务也就几分钱。

常见问题速查

问题 原因 解决
docker: command not found Docker 没装或没加到 PATH 重装 Docker Desktop,重启终端
port 3000 already in use 其他程序占了端口 关掉冲突程序,或改 .env 里的 NEXT_PUBLIC_PORT
OpenAI API key is invalid API Key 填错了或过期了 去 OpenAI 后台重新生成一个
Database connection refused MySQL 还没启动完 等 10 秒再刷新,或 docker compose restart db
页面能打开但 Agent 不响应 后端没连上 OpenAI 检查 .env 里的 API Key,重启 docker compose restart platform

下一步

现在你已经能在本地跑起来 AgentGPT 了。下一章我们会配置 OpenAI 和 Serper API 密钥,让 Agent 真正能联网搜索和调用大模型——你现在用的只是最基本的聊天功能,配置好密钥后,Agent 才能发挥真正的自主能力。

2. 2. 配置 OpenAI 与 Serper API 密钥:让 Agent 真正联网

你跟着第一章把 AgentGPT 跑起来了,打开浏览器看到那个漂亮的界面,但一输入目标,Agent 就卡住不动——大概率是因为它还没拿到联网的“通行证”。这一章我们就来解决这个问题:配置 OpenAI 和 Serper 的 API 密钥,让 Agent 真正能思考、能搜索。

为什么需要这两个密钥?

想象一下,AgentGPT 就像一个超级聪明的实习生,但它有两个致命缺陷:第一,它没有自己的大脑(需要 OpenAI 的 GPT 模型来思考);第二,它没有眼睛和耳朵(需要 Serper 来搜索互联网)。没有 API 密钥,这个实习生就是个空壳子,什么都干不了。

前置条件

  • 你已经按照第一章完成了 ./setup.sh./setup.bat 的安装
  • 项目目录下有一个 .env 文件(如果没有,复制 .env.example 重命名即可)
  • 你有一个浏览器,能打开网页注册账号

第一步:获取 OpenAI API 密钥

OpenAI 的 API 密钥是 Agent 的“大脑皮层”,所有推理、决策、任务分解都靠它。

  1. 打开 platform.openai.com/signup 注册账号(如果已有账号直接登录)
  2. 登录后,点击右上角头像 → "View API Keys"
  3. 点击 "Create new secret key",给你的密钥起个名字比如 "agentgpt-dev"
  4. 立即复制这个密钥——它只显示一次,关掉页面就再也看不到了

⚠️ 常见坑:很多人复制的时候会不小心多复制一个空格,或者漏掉最后几个字符。建议粘贴到记事本里检查一遍,确保没有多余的空格或换行。

第二步:获取 Serper API 密钥

Serper 是 Google 搜索的 API 封装,Agent 通过它来搜索互联网。没有它,Agent 就只能靠自己的“知识”回答问题,无法获取实时信息。

  1. 打开 serper.dev/signup 注册
  2. 注册后会自动获得 2500 次免费查询(够你玩很久了)
  3. 登录后,在 Dashboard 页面找到 "API Key",复制它

💡 小提示:Serper 的免费额度用完后再注册一个新账号就行,或者升级到付费计划(很便宜,$50/月 5 万次查询)。

第三步:配置环境变量

现在我们把这两个密钥放进项目里。打开项目根目录下的 .env 文件(如果没有,复制 .env.example 并重命名):

# 在项目根目录下
cp .env.example .env

然后用文本编辑器打开 .env,找到这两行:

OPENAI_API_KEY=
SERPER_API_KEY=

把刚才复制的密钥填进去,注意不要加引号,不要有空格:

OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
SERPER_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

⚠️ 重要:OpenAI 的密钥以 sk- 开头,Serper 的密钥是一串随机字符。如果填错了格式,Agent 会报错。

第四步:重启服务让配置生效

环境变量修改后,需要重启后端服务才能生效。如果你是用 Docker 运行的:

# 停止并重新启动所有容器
docker-compose down
docker-compose up -d

如果你是用 ./setup.sh 直接运行的,直接关掉终端重新运行一次即可。

验证配置是否成功

打开浏览器访问 http://localhost:3000,创建一个新的 Agent,输入一个需要联网搜索的目标,比如:

"搜索今天比特币的价格,并告诉我最近一周的价格趋势"

如果配置正确,你会看到 Agent 开始思考,然后调用搜索工具,最后给出带实时数据的回答。如果看到类似这样的错误:

Error: 401 Unauthorized - Invalid API key

说明 OpenAI 密钥有问题,回去检查一下格式和是否有空格。

如果 Agent 一直在思考但没有任何输出,可能是 Serper 密钥没配置好——试试在浏览器里直接访问 http://localhost:3000/api/agents 看看后端日志。

一个小技巧:用环境变量文件管理多个配置

如果你经常切换不同的 API 密钥(比如开发环境和测试环境),可以创建多个 .env 文件:

# 开发环境
cp .env .env.dev

# 测试环境
cp .env .env.test

然后在启动时指定使用哪个文件:

# 使用开发环境配置
docker-compose --env-file .env.dev up -d

这样切换环境只需要改文件名,不用每次都编辑 .env 文件。

配置完成后的效果

现在你的 AgentGPT 已经“活”过来了。它不仅能理解你的指令,还能实时搜索互联网获取最新信息。下一章我们会用它做一个真正的竞品调研 Agent,到时候你就会发现,没有这两个密钥,Agent 就是个哑巴;有了它们,它就是个能干的助手。

如果配置过程中遇到任何问题,检查一下:

  1. API 密钥是否复制完整
  2. .env 文件里有没有多余的空格或引号
  3. 重启服务后是否真的加载了新配置(可以看后端日志确认)

搞定了?那我们就进入下一章,让 Agent 真正开始干活吧。

3. 3. 自定义 Agent 名称与目标:写一个能自动调研竞品的 Agent

好,这一章我们来干点实际的。前两章你已经把 AgentGPT 跑起来了,也接上了 OpenAI 和 Serper 的 API。现在,我们要让这个 Agent 真正为你干活——不是让它随便逛,而是给它一个明确的名字、一个具体的任务,让它像你的员工一样去调研竞品。

为什么名字和目标这么重要?

想象一下,你招了个实习生,你只说“去了解一下市场”,他大概率会一脸懵。但如果你说“小王,你去调研一下我们主要竞品 A 公司最近三个月的产品更新和定价策略”,他立马就知道该搜什么、看什么、整理什么。

AgentGPT 里的 Agent 也一样。你给它起的名字(Name)和设定的目标(Goal),就是它的“岗位职责说明书”。名字影响它在对话和日志里的身份标识,目标则直接决定了它要执行的任务链。目标写得越清晰、越可执行,Agent 的产出就越靠谱。

前置条件

在开始之前,确保你已经:

  1. 完成了第 1 章的本地环境搭建,AgentGPT 在 http://localhost:3000 能正常打开。
  2. 完成了第 2 章的 API 密钥配置,OPENAI_API_KEYSERP_API_KEY 都已经填好。
  3. 浏览器里能看到 AgentGPT 的主界面,有一个输入框让你填 Name 和 Goal。

第一步:起个好名字

打开 http://localhost:3000,你会看到一个简洁的界面,顶部有个输入框写着“Name your custom AI...”。这里别随便填个“test”或者“agent”,给它一个有意义的名字。

比如我们要做一个竞品调研 Agent,可以叫:

竞品侦察兵

或者更正式一点:

MarketIntel-Agent

名字会出现在 Agent 的对话历史、日志输出里,方便你以后区分多个 Agent。如果你同时跑好几个 Agent,一个好名字能让你一眼认出谁是谁。

第二步:写一个能干活的目标

这是最关键的一步。目标框里写的是 Agent 的“终极任务”,它会基于这个目标自己拆解出子任务,然后一步步执行。

我们拿一个真实场景举例:假设你是一家 SaaS 公司的产品经理,想了解主要竞品“Notion”最近三个月的动态。

一个糟糕的目标是:

调研 Notion

太模糊了。Agent 不知道要调研什么维度,可能搜出 Notion 的历史、创始人故事、用户评价,乱七八糟一堆。

一个的目标应该是:

调研 Notion  2024  1 月至 3 月期间的产品更新、定价策略变化、用户评价趋势和主要功能发布。整理成一份包含时间线、关键变化和影响分析的报告。

看到了吗?好目标有这几个要素:

  • 明确的范围:时间范围(2024 年 1 月至 3 月)
  • 具体的维度:产品更新、定价、用户评价、功能发布
  • 期望的输出形式:一份包含时间线、关键变化和影响分析的报告

Agent 拿到这个目标后,会自己规划:先搜索“Notion 2024 年 1 月产品更新”,再搜索“Notion 2024 年定价变化”,然后搜索“Notion 用户评价 2024”,最后把结果汇总成报告。

第三步:点击“Deploy Agent”并观察

填好名字和目标后,点击那个大大的“Deploy Agent”按钮。你会看到 Agent 开始工作:

  1. 它先显示“思考中...”,然后列出它自己拆解的子任务。
  2. 接着它开始执行第一个子任务,比如搜索“Notion 2024 年 1 月产品更新”。
  3. 它会调用 Serper API 去搜索,拿到结果后,用 OpenAI 的模型分析这些结果。
  4. 然后它继续下一个子任务,直到所有子任务完成。

在界面上,你会看到一个类似聊天记录的窗口,Agent 每完成一步都会输出结果。你可以实时看到它搜到了什么、分析了什么。

常见问题与排查

问题 1:Agent 一直卡在“思考中”不动

这通常是因为 OpenAI API 调用超时或者 Serper API 没配置好。检查一下:

  • 你的 .env 文件里 OPENAI_API_KEYSERP_API_KEY 是否正确?
  • 打开浏览器开发者工具(F12),看 Network 标签页,有没有请求返回 401 或 429 错误?
  • 如果用的是免费 OpenAI 账号,可能有速率限制,等几分钟再试。

问题 2:Agent 搜出来的结果不相关

大概率是你的目标写得太宽泛。比如你写“调研 Notion”,它可能搜出 Notion 的维基百科页面,而不是产品更新。试试把目标写得更具体,加上“产品更新”“定价变化”这些关键词。

问题 3:Agent 执行到一半就停了

AgentGPT 默认有最大迭代次数限制(通常是 25 次)。如果你的目标需要很多子任务,可能没做完就停了。这时候可以:

  • 把目标拆得更细,一次只调研一个维度。
  • 或者等后面第 6 章我们讲参数调整时,再教你调大迭代次数。

一个小技巧:先手动规划再让 Agent 执行

如果你不确定目标写得好不好,可以先在脑子里或者纸上列一下:如果我是这个 Agent,我会怎么完成这个任务?需要搜索哪些关键词?需要分析哪些信息?

比如调研竞品,典型的步骤是:

  1. 搜索“竞品名称 + 产品更新 + 时间范围”
  2. 搜索“竞品名称 + 定价 + 时间范围”
  3. 搜索“竞品名称 + 用户评价 + 时间范围”
  4. 把搜索结果汇总,提取关键变化
  5. 生成对比分析报告

把这些步骤写进目标里,Agent 执行起来会更精准。

试试看:跑一个真实的竞品调研

现在,打开你的 AgentGPT,输入:

  • Name: 竞品侦察兵
  • Goal: 调研 Notion 在 2024 年 1 月至 3 月期间的产品更新、定价策略变化和用户评价趋势。整理成一份包含时间线、关键变化和影响分析的报告。

点击 Deploy Agent,然后去泡杯咖啡。几分钟后,你会看到一份初步的竞品调研报告。虽然可能不如专业分析师写的那么深入,但作为第一轮信息收集,已经能帮你省下大量手动搜索的时间。

如果结果不错,你可以把这份报告作为起点,再让 Agent 针对某个具体发现做深入调研。比如它提到 Notion 在 2 月发布了 AI 功能,你可以再创建一个新 Agent,目标设为“深入调研 Notion AI 功能的具体能力、定价和用户反馈”。

总结

这一章的核心就一句话:给 Agent 一个清晰、具体、可执行的目标,它就能成为你的得力助手。 名字是它的身份,目标是它的任务书。写目标时,想象你在给一个聪明但需要明确指令的实习生布置工作——越具体,结果越好。

下一章,我们会给 Agent 装上更多“工具”,比如让它能自己算数、读写文件,能力直接翻倍。

4. 4. 给 Agent 接入自定义工具:添加搜索、计算与文件操作能力

给 Agent 装上“手脚”:自定义工具

默认情况下,AgentGPT 的 Agent 其实挺“残废”的——它只能调用内置的搜索和思考能力。如果你想让它帮你算个复杂的表达式、读取本地文件、或者调用一个公司内部的 API,它就只能干瞪眼。

这一章我们就来解决这个问题:给 Agent 添加自定义工具。学完之后,你的 Agent 就能像有了“手脚”一样,执行你指定的任何操作。

前置条件

  • 已经完成了第 2 章的 API 密钥配置(OpenAI 和 Serper 都配好了)
  • 项目能正常启动,在 http://localhost:3000 能看到界面
  • 对 Python 和 FastAPI 有最基础的了解(知道路由和函数怎么写就行)

第一步:找到工具注册的地方

AgentGPT 的后端(platform 目录)里,工具的定义都放在 platform/tools/ 下面。我们先看看现有的工具长什么样:

ls platform/tools/

你会看到类似这样的结构:

tools/
├── __init__.py
├── search.py          # 搜索工具
├── reasoning.py       # 推理工具
└── tool_base.py       # 工具基类

打开 tool_base.py,你会发现每个工具都是一个类,继承自 BaseTool,只需要实现一个 run 方法。这个 run 方法接收一个字符串参数(用户的输入),返回一个字符串(工具的执行结果)。

第二步:写第一个自定义工具——计算器

我们先写一个最简单的计算器工具。在 platform/tools/ 下新建一个文件 calculator.py

import ast
import operator

# 安全的运算符映射,只允许数学运算
ALLOWED_OPERATORS = {
    ast.Add: operator.add,
    ast.Sub: operator.sub,
    ast.Mult: operator.mul,
    ast.Div: operator.truediv,
    ast.Pow: operator.pow,
    ast.USub: operator.neg,
}

class CalculatorTool:
    name = "calculator"
    description = "计算数学表达式,例如 '2 + 3 * 4' 或 '10 / 2'"

    def run(self, query: str) -> str:
        try:
            # 只解析表达式,不执行任意代码
            tree = ast.parse(query.strip(), mode='eval')
            result = self._eval_node(tree.body)
            return f"计算结果: {result}"
        except Exception as e:
            return f"计算失败: {str(e)}"

    def _eval_node(self, node):
        if isinstance(node, ast.Constant):
            return node.value
        elif isinstance(node, ast.BinOp):
            left = self._eval_node(node.left)
            right = self._eval_node(node.right)
            op_type = type(node.op)
            if op_type in ALLOWED_OPERATORS:
                return ALLOWED_OPERATORS[op_type](left, right)
            raise ValueError(f"不支持的运算符: {op_type}")
        elif isinstance(node, ast.UnaryOp):
            operand = self._eval_node(node.operand)
            op_type = type(node.op)
            if op_type in ALLOWED_OPERATORS:
                return ALLOWED_OPERATORS[op_type](operand)
            raise ValueError(f"不支持的一元运算符: {op_type}")
        else:
            raise ValueError(f"不支持的表达式类型: {type(node)}")

这里用了 ast 模块来解析表达式,而不是直接用 eval()——因为 eval() 能执行任意 Python 代码,太危险了。用 ast 解析后,我们只允许数学运算,别的都拒绝。

第三步:把工具注册到 Agent 的工具箱

现在工具写好了,但 Agent 还不知道它的存在。我们需要修改工具加载的地方。

打开 platform/tools/__init__.py,把我们的计算器加进去:

from .search import SearchTool
from .reasoning import ReasoningTool
from .calculator import CalculatorTool  # 新增

# 所有可用的工具列表
AVAILABLE_TOOLS = [
    SearchTool(),
    ReasoningTool(),
    CalculatorTool(),  # 新增
]

def get_tool_by_name(name: str):
    for tool in AVAILABLE_TOOLS:
        if tool.name == name:
            return tool
    return None

第四步:让 Agent 能调用新工具

工具注册好了,但 Agent 在决策时怎么知道该用哪个工具?这取决于 Agent 的“思考”过程。在 platform/agent/agent.py 里,Agent 会收到一个任务列表,然后逐个执行。我们需要修改任务执行逻辑,让它能识别并调用我们的计算器。

找到 agent.py 中处理工具调用的部分(大概在 80-120 行之间),修改成类似这样:

from tools import AVAILABLE_TOOLS

def execute_task(task: str, context: dict) -> str:
    # 检查任务是否匹配某个工具
    for tool in AVAILABLE_TOOLS:
        # 简单的关键词匹配,实际项目中可以用更智能的方式
        if tool.name in task.lower():
            # 提取用户输入(去掉工具名称部分)
            query = task.replace(tool.name, "").strip()
            return tool.run(query)
    
    # 没有匹配的工具,走默认的 LLM 推理
    return llm_reasoning(task, context)

注意:这只是一个简化的示例。实际项目中,Agent 会用 LLM 来决定调用哪个工具,而不是简单的关键词匹配。但为了演示,我们先这样写。

第五步:测试你的计算器

重启后端服务:

# 在 platform 目录下
uvicorn main:app --reload

现在打开前端,创建一个新的 Agent,在目标里输入类似这样的内容:

计算 1234 * 5678 的结果

如果一切正常,Agent 应该会调用计算器工具,返回 计算结果: 7006652

第六步:添加文件操作工具

计算器太简单了?我们来写一个更有用的——文件读取工具。在 platform/tools/ 下新建 file_reader.py

import os
from pathlib import Path

class FileReaderTool:
    name = "file_reader"
    description = "读取指定路径的文件内容,支持 .txt, .md, .csv 格式"

    # 只允许读取这些目录下的文件
    ALLOWED_PATHS = [
        Path("/app/data"),
        Path("/app/uploads"),
    ]

    def run(self, query: str) -> str:
        filepath = Path(query.strip())
        
        # 安全检查:防止路径遍历攻击
        try:
            filepath = filepath.resolve()
        except Exception:
            return "错误:无法解析文件路径"
        
        # 检查文件是否在允许的目录下
        allowed = False
        for allowed_path in self.ALLOWED_PATHS:
            try:
                filepath.relative_to(allowed_path)
                allowed = True
                break
            except ValueError:
                continue
        
        if not allowed:
            return f"错误:只能访问 {', '.join(str(p) for p in self.ALLOWED_PATHS)} 目录下的文件"
        
        # 检查文件是否存在
        if not filepath.exists():
            return f"错误:文件 {filepath} 不存在"
        
        if not filepath.is_file():
            return f"错误:{filepath} 不是一个文件"
        
        # 检查文件大小(限制 1MB)
        if filepath.stat().st_size > 1_000_000:
            return "错误:文件太大(超过 1MB)"
        
        # 读取文件
        try:
            content = filepath.read_text(encoding='utf-8')
            # 限制返回内容长度
            if len(content) > 5000:
                content = content[:5000] + "\n\n...(文件过长,仅显示前 5000 字符)"
            return f"文件内容:\n{content}"
        except Exception as e:
            return f"读取文件失败: {str(e)}"

这个工具做了几层安全防护:

  1. 路径遍历防护:用 resolve() 解析真实路径,防止 ../../etc/passwd 这种攻击
  2. 目录白名单:只允许读取特定目录下的文件
  3. 文件大小限制:防止 Agent 读取超大文件导致内存溢出

别忘了把它加到 __init__.pyAVAILABLE_TOOLS 里。

常见报错与排查

报错:ModuleNotFoundError: No module named 'tools.calculator'

检查 __init__.py 里的导入语句是否正确,以及文件路径是否在 Python 的搜索路径中。如果 platform 目录不在 PYTHONPATH 里,可以这样启动:

PYTHONPATH=/path/to/agentgpt/platform uvicorn main:app --reload

报错:Agent 一直调用搜索工具,不用计算器

这是因为我们用的关键词匹配太粗糙了。实际项目中,Agent 会用 LLM 分析任务内容,然后决定调用哪个工具。你可以临时把搜索工具从 AVAILABLE_TOOLS 里注释掉,先测试计算器是否正常工作。

报错:计算器返回 计算失败: ...

检查你的表达式格式。计算器只支持基本的四则运算和幂运算,不支持函数调用(比如 sin(30) 就不行)。

小例子串一串

假设你有一个数据分析的需求:每天从某个目录读取当天的销售数据文件,然后计算总销售额。

  1. file_reader 工具读取 /app/data/sales_2024-01-15.csv
  2. Agent 分析文件内容,提取出金额列
  3. calculator 工具计算总和

Agent 的任务列表可能长这样:

1. 使用 file_reader 读取 /app/data/sales_2024-01-15.csv
2. 分析文件内容,提取所有金额数值
3. 使用 calculator 计算 1200 + 3400 + 5600 + 7800
4. 返回总销售额

当然,实际项目中你可能会写一个专门的 CSV分析工具,一步到位。但通过组合多个基础工具,Agent 已经能完成很多工作了。

注意事项

  • 工具的描述很重要:Agent 的 LLM 会根据 description 字段来判断什么时候该用这个工具。描述写得越清晰,Agent 调用得越准确。
  • 错误处理要友好:工具返回的错误信息应该让 Agent 能理解,而不是抛出一堆技术栈。Agent 看到错误后,可能会尝试其他方式完成任务。
  • 性能考虑:如果工具执行时间较长(比如调用外部 API),记得设置超时。Agent 默认的等待时间有限,工具卡住了会影响整个任务流程。
  • 安全第一:任何涉及文件系统、网络请求、命令执行的工具,都要做严格的输入验证和权限控制。Agent 的输入来自用户,你永远不知道用户会输入什么。

5. 5. 让 Agent 记住上下文:配置长期记忆与对话历史

每次跟 Agent 聊天,它都像得了失忆症——上一秒刚查完的资料,下一秒就忘得干干净净。这不是你 Agent 笨,而是它默认就没有"记性"这个功能。每次执行任务,它都是从头开始思考,之前聊了什么、查到了什么,一概不记得。

这一章我们就来解决这个问题:给 Agent 装上记忆模块,让它能记住对话历史,在多次交互中保持上下文连贯。配置好之后,你的 Agent 就能像人类一样"记得之前聊到哪了",做调研、写报告这类需要多轮对话的任务会顺畅很多。

前置条件

开始之前,确保你已经:

  1. 完成了第 1 章的本地环境搭建,AgentGPT 能在 localhost:3000 正常运行
  2. 配置好了 OpenAI API 密钥(第 2 章的内容)
  3. 数据库已经启动并连接成功(第 1 章 Docker 部署时会自动处理)

如果你还没搞定这些,先回去补课,不然这章的操作跑不起来。

记忆是怎么工作的

AgentGPT 的记忆机制其实不复杂。每次 Agent 执行一步操作,它会把当前的结果和思考过程存到数据库里。下一次 Agent 需要做决策时,它会先去数据库里翻一翻之前干了什么,把这些历史信息拼到 prompt 里一起发给 OpenAI。

说白了就是:每次对话都带上小抄

这个小抄存在哪?存在 MySQL 数据库里。AgentGPT 用 Prisma ORM 来管理数据库,记忆相关的表叫 AgentAgentMessageAgent 表存 Agent 的基本信息(名字、目标、状态),AgentMessage 表存每一条对话记录。

第一步:检查数据库表结构

先看看数据库里有没有存记忆的地方。打开你的数据库管理工具(比如 DBeaver 或者直接用命令行),连上 MySQL:

# 如果你用 Docker 启动的,先进容器
docker exec -it agentgpt-db mysql -u root -p

# 输入密码后,切换到 agentgpt 数据库
USE agentgpt;

# 看看有哪些表
SHOW TABLES;

你应该能看到 AgentAgentMessage 这两张表。如果看不到,说明你的数据库初始化有问题,回去重新跑一遍 ./setup.sh

AgentMessage 表的结构大概长这样:

字段 类型 说明
id VARCHAR(36) 主键
agentId VARCHAR(36) 关联到哪个 Agent
role VARCHAR(20) 'user' 或 'assistant'
content TEXT 消息内容
createdAt DATETIME 创建时间

每次 Agent 跟你对话或者自己执行任务,都会往这张表里插一条记录。

第二步:配置记忆长度

默认情况下,AgentGPT 会记住最近 10 条对话。这个数字够用吗?看场景。如果你只是让 Agent 查个天气,10 条绰绰有余;但如果你让它写一份 5000 字的竞品分析报告,10 条可能连一半都聊不完。

修改这个配置很简单,找到项目根目录下的 .env 文件(如果没有就复制 .env.example 创建一个),加上这一行:

AGENT_MEMORY_LENGTH=20

这个值设多大合适?我的建议是:

  • 简单问答:10-15 条就够了
  • 多步骤任务(比如调研、写报告):20-30 条
  • 超长对话(比如让 Agent 写一本书):50 条封顶

别设太大,原因有两个:一是 OpenAI API 按 token 收费,历史消息越多,每次请求越贵;二是 prompt 太长会影响模型的理解质量,它会被大量历史信息淹没,反而抓不住重点。

改完 .env 文件后,重启后端服务:

# 如果你用 Docker
docker-compose restart backend

# 如果直接跑
# 先 Ctrl+C 停掉,再重新启动
cd platform
uvicorn main:app --reload

第三步:验证记忆是否生效

重启之后,我们来做个测试。打开浏览器访问 http://localhost:3000,创建一个新的 Agent,给它一个需要多轮对话才能完成的目标,比如:

目标:调研三家国内主流 AI 大模型公司(百度、阿里、科大讯飞),对比它们的产品特点

然后开始跟 Agent 对话。先问第一个问题:

"帮我查一下百度的文心一言有哪些核心功能"

Agent 会去搜索并返回结果。这时候别急着问下一个,先看看数据库里有没有存下这条记录:

SELECT * FROM AgentMessage WHERE agentId = '你刚创建的 Agent ID' ORDER BY createdAt;

你应该能看到一条 role 为 'user' 的记录(你问的问题)和一条 role 为 'assistant' 的记录(Agent 的回答)。

接着问第二个问题:

"再查一下阿里的通义千问"

Agent 回答之后,再查数据库,应该又多出两条记录。现在问第三个问题:

"对比一下这两家的产品,列出 3 个主要差异"

关键来了:如果记忆生效,Agent 应该能记得刚才查到的百度和阿里的信息,直接给出对比结果。如果它说"我没查到百度的信息"或者重新去搜索,说明记忆没配置好。

第四步:排查常见问题

问题 1:Agent 还是记不住

检查 .env 文件里的 AGENT_MEMORY_LENGTH 有没有被正确读取。可以在后端代码里加个临时日志来验证:

打开 platform/agent/agent.py,找到初始化 Agent 的地方,大概在 30 行左右,加一行打印:

import os
print(f"Memory length: {os.getenv('AGENT_MEMORY_LENGTH', '10')}")

重启后端,看控制台输出。如果显示的是默认值 10,说明你的 .env 文件没被加载。检查一下文件路径对不对,是不是在 platform/ 目录下。

问题 2:数据库连接失败

如果 Agent 能正常对话但数据库里没有记录,检查数据库连接配置。打开 platform/.env,确认这几项:

DATABASE_URL=mysql://root:yourpassword@localhost:3306/agentgpt

注意端口号,Docker 启动的 MySQL 默认端口是 3306,但如果你本地也装了 MySQL,可能会有冲突。用 docker ps 查看实际映射的端口。

问题 3:记忆太多导致请求超时

如果你设了 50 条记忆,每次请求都会带上这 50 条历史。OpenAI API 有超时限制,如果历史太长可能会超时。这时候要么减少记忆长度,要么在代码里加个 token 数限制:

打开 platform/agent/prompts.py,找到构建 prompt 的地方,大概长这样:

def build_memory_prompt(messages: list) -> str:
    total_tokens = 0
    memory_parts = []
    for msg in reversed(messages):
        # 估算每条消息的 token 数(粗略按 4 字符 = 1 token)
        msg_tokens = len(msg.content) // 4
        if total_tokens + msg_tokens > 2000:  # 限制 2000 token
            break
        memory_parts.append(f"{msg.role}: {msg.content}")
        total_tokens += msg_tokens
    return "\n".join(reversed(memory_parts))

这样即使设了 50 条记忆,实际用到的也不会超过 2000 token,既省钱又不会超时。

第五步:进阶用法——自定义记忆策略

默认的记忆策略是"保留最近 N 条",简单粗暴。但有些场景下你可能想要更智能的记忆方式,比如:

  • 摘要记忆:把之前的对话压缩成一段摘要,而不是原样保留
  • 关键信息提取:只记住对话中提到的关键数据(比如价格、日期、数字)
  • 分层记忆:短期记忆保留最近 10 条,长期记忆只存摘要

这些需要改代码来实现。以摘要记忆为例,在 platform/agent/memory.py 里加一个类:

from langchain.memory import ConversationSummaryMemory
from langchain.llms import OpenAI

class SummaryMemory:
    def __init__(self):
        self.llm = OpenAI(temperature=0)
        self.memory = ConversationSummaryMemory(llm=self.llm, max_token_limit=500)
    
    def add_message(self, role: str, content: str):
        self.memory.chat_memory.add_message(
            HumanMessage(content=content) if role == "user" 
            else AIMessage(content=content)
        )
    
    def get_summary(self) -> str:
        return self.memory.buffer

然后在 agent.py 里把原来的记忆逻辑替换成这个。每次 Agent 需要历史信息时,拿到的不是原始对话,而是一段精炼的摘要。这样既保留了上下文,又大幅减少了 token 消耗。

一个小例子串起来

假设你要让 Agent 帮你写一份"新能源汽车市场分析报告"。没有记忆的情况下,你问完"特斯拉 2024 年销量",Agent 回答完就忘了。你再问"对比比亚迪",它得重新去查特斯拉的数据。

配置好记忆后,流程变成:

  1. 你问:"特斯拉 2024 年全球销量是多少?"
  2. Agent 搜索并回答:"特斯拉 2024 年全球销量约 180 万辆"
  3. 这条记录存进数据库
  4. 你接着问:"对比一下比亚迪同期销量"
  5. Agent 去数据库翻出上一条记录,知道你已经查过特斯拉了
  6. Agent 搜索比亚迪数据,然后给出对比:"特斯拉 180 万辆,比亚迪 300 万辆,比亚迪领先约 67%"

整个过程流畅自然,Agent 就像个有记忆的助手,而不是每次都要重新认识你。

注意事项

  • 记忆不是永久存储:AgentGPT 默认不会无限期保存所有对话。如果你需要长期存档,得自己写个定时任务把数据库里的历史记录导出
  • 隐私问题:所有对话都明文存在数据库里。如果涉及敏感信息,建议加密存储或者定期清理
  • 性能影响:记忆越多,每次请求越慢。20 条以内基本无感,超过 50 条会有明显延迟

配置好记忆之后,你的 Agent 就从"金鱼脑"变成了"大象脑"。下一章我们会聊怎么调整 Agent 的行为参数,让它在"太保守"和"太放飞"之间找到平衡点。

6. 6. 调整 Agent 行为参数:温度、最大迭代与停止条件

让 Agent 不再“疯跑”:温度、最大迭代与停止条件

你有没有遇到过这种情况:给 Agent 设定了一个目标,它就开始疯狂执行任务,一个接一个,完全停不下来?或者它输出的内容天马行空,跟你的需求差了十万八千里?这通常不是 Agent 笨,而是你没给它设好“行为边界”。

这一章我们就来调教 Agent 的“脾气”——通过调整温度、最大迭代次数和停止条件,让它在“创造力”和“可控性”之间找到平衡点。这些参数直接决定了 Agent 是像脱缰野马还是训练有素的助手。

前置准备

在开始之前,确保你已经:

  • 成功启动了 AgentGPT 的本地环境(第 1 章的内容)
  • 配置好了 OpenAI API 密钥(第 2 章)
  • 能正常创建和运行一个 Agent

第一步:找到参数配置的“总开关”

AgentGPT 的行为参数主要藏在两个地方:前端创建 Agent 时的表单,和后端的配置文件。我们先从前端入手,因为这是最直观的调整方式。

打开浏览器,访问 http://localhost:3000,点击“创建 Agent”按钮。你会看到一个表单,里面除了 Agent 名称和目标,还有几个滑块和输入框——这些就是我们要调整的参数。

第二步:理解“温度”——Agent 的“脑洞”大小

温度(Temperature)是控制 Agent 输出随机性的参数,取值范围 0 到 2。简单来说:

  • 温度越低(接近 0):Agent 越“保守”,倾向于选择最可能的答案,输出稳定但可能缺乏创意
  • 温度越高(接近 2):Agent 越“放飞自我”,会尝试更多可能性,输出多样但可能跑偏

在 AgentGPT 的创建表单里,温度默认是 0.5。这个值对大多数任务来说是个不错的折中——既不会太死板,也不会太离谱。

实际测试一下:创建一个 Agent,目标设为“列出 5 个提高工作效率的方法”,温度分别设为 0.1、0.5 和 1.5,看看输出有什么不同。你会发现:

  • 温度 0.1:每次运行结果几乎一样,都是“番茄工作法、GTD、时间块”这类经典方法
  • 温度 0.5:每次会有一些变化,但核心方法保持稳定
  • 温度 1.5:可能冒出“用冥想代替工作”“养一只猫来减压”这种奇怪建议

什么时候调高温度? 当你需要 Agent 做头脑风暴、创意写作,或者探索多种解决方案时。比如让 Agent 设计营销文案,温度设到 0.8-1.0 效果更好。

什么时候调低温度? 当你需要 Agent 执行精确任务,比如提取数据、生成代码、回答事实性问题。这种情况下,温度设到 0.1-0.3 更靠谱。

第三步:设置“最大迭代”——给 Agent 戴上缰绳

最大迭代(Max Iterations)是 AgentGPT 里最容易被忽略但最重要的参数。它控制 Agent 最多能执行多少轮“思考→执行→反馈”的循环。

默认值是 5,这意味着 Agent 最多会执行 5 个任务。如果目标简单,5 次可能够了;但如果目标复杂,比如“调研竞品并生成报告”,5 次可能只够查几个网页,根本写不完报告。

怎么设置合适的值?

  • 简单任务(查一个数据、翻译一句话):1-3 次
  • 中等任务(写一篇短文、做简单分析):5-10 次
  • 复杂任务(深度调研、多步骤流程):10-25 次
  • 非常复杂(需要反复验证和迭代):25-50 次

注意:每次迭代都会调用 OpenAI API,这意味着迭代次数越多,花费越高。设置 50 次迭代可能让你的账单瞬间飙升。

实际测试:创建一个 Agent,目标设为“调研当前最流行的 3 个前端框架,并比较它们的优缺点”,最大迭代设为 3。你会发现 Agent 可能只查了 1-2 个框架就停了,因为 3 次迭代不够它完成所有步骤。改成 10 次再试,效果会好很多。

第四步:配置“停止条件”——让 Agent 知道什么时候收手

停止条件(Stop Conditions)是 AgentGPT 里比较高级的功能,它告诉 Agent:“当你遇到这些情况时,就停下来,别继续了。”

在 AgentGPT 的配置中,停止条件通常通过两种方式实现:

  1. 关键词停止:当 Agent 的输出包含特定关键词时停止。比如设置“完成”“结束”“报告已生成”作为停止词。
  2. 目标达成判断:Agent 会自我评估是否已经达成了初始目标,如果判断为“是”,就主动停止。

实际操作:在创建 Agent 的表单里,找到“停止条件”或“Stop Conditions”输入框(如果前端版本没有这个选项,可以在后端配置文件中添加)。输入几个关键词,用逗号分隔:

完成, 结束, 报告已生成, 目标已达成

这样当 Agent 的输出中出现这些词时,它就会自动停止,不会继续无意义地执行下去。

小技巧:把停止条件和最大迭代结合起来用。比如设置最大迭代 20 次,同时设置停止条件为“完成”。这样 Agent 要么在 20 次内完成任务并主动停止,要么在 20 次后强制停止——双重保险。

第五步:在后端配置中微调参数

前端表单只能调整部分参数,如果你想更精细地控制,需要直接修改后端代码。

找到 platform/ 目录下的 Agent 配置文件,通常是 agent.pyagent_controller.py。打开它,你会看到类似这样的代码:

# 这是 Agent 的核心配置,不要直接复制,根据你的版本调整
class AgentConfig:
    temperature: float = 0.5
    max_iterations: int = 5
    stop_conditions: List[str] = []

你可以在这里修改默认值,这样所有新创建的 Agent 都会使用你设定的默认参数:

class AgentConfig:
    temperature: float = 0.3  # 更保守一点
    max_iterations: int = 10  # 默认给更多迭代次数
    stop_conditions: List[str] = ["完成", "结束", "目标已达成"]

修改后重启后端服务(通常是 docker-compose restart 或直接重启容器),新参数就会生效。

常见问题与排查

问题 1:Agent 一直循环执行同一个任务

  • 原因:温度太高导致 Agent 无法做出稳定决策,或者停止条件没设置好
  • 解决:降低温度到 0.3 以下,同时设置明确的停止条件

问题 2:Agent 执行一两次就停了,任务没完成

  • 原因:最大迭代次数设得太低
  • 解决:增加最大迭代次数,从 10 开始试

问题 3:Agent 输出内容完全跑题

  • 原因:温度太高,或者目标描述不够清晰
  • 解决:降低温度到 0.2-0.3,同时优化目标描述,让它更具体

问题 4:API 调用费用飙升

  • 原因:最大迭代次数设得过高,或者 Agent 陷入了死循环
  • 解决:设置合理的最大迭代次数(一般不超过 25),同时配置停止条件作为保险

一个完整的例子:自动调研竞品

让我们把这些参数用在一个真实场景里。假设你要创建一个 Agent,让它自动调研某个竞品并生成报告:

  1. Agent 名称:竞品调研助手
  2. 目标:调研 Notion 这个产品的功能、定价、用户评价,并生成一份 500 字左右的对比报告
  3. 温度:0.4(需要一定的创意来组织报告,但不能太放飞)
  4. 最大迭代:15(调研需要查多个网页、整理信息、写报告,5 次不够)
  5. 停止条件:报告已生成, 调研完成

运行这个 Agent,你会看到它先搜索 Notion 的功能介绍,然后查定价信息,接着找用户评价,最后整理成报告。当它输出“报告已生成”时,自动停止——完美收工。

参数调优的黄金法则

最后分享几个经验法则,帮你快速找到合适的参数组合:

  • 简单任务:温度 0.2 + 迭代 3 + 关键词停止
  • 创意任务:温度 0.8 + 迭代 10 + 目标达成停止
  • 复杂分析:温度 0.3 + 迭代 20 + 双重停止条件
  • 探索性任务:温度 1.0 + 迭代 15 + 无停止条件(但要监控)

记住,没有“万能参数”,每个任务都需要微调。多试几次,观察 Agent 的行为,慢慢你就能凭直觉判断该用什么参数了。

7. 7. 多 Agent 协作:同时运行多个 Agent 并共享结果

你手头已经跑起来了一个 AgentGPT 实例,能帮你想一些调研任务了。但现实世界里的活儿,往往不是单打独斗能搞定的。比如你要分析一个市场,得同时查竞品、搜行业报告、扒用户评价——一个 Agent 挨个做,慢不说,还容易搞混上下文。要是能派三个 Agent 同时开工,各干各的,最后把结果汇总给你,那效率就完全不一样了。

这一章我们就来干这件事:让多个 Agent 同时跑起来,并且让它们能把结果写到同一个地方,方便你最后收网。

前置条件

  • 你已经完成了第 1 章到第 3 章的配置,AgentGPT 能在 http://localhost:3000 正常启动和运行。
  • 你有一个可用的 OpenAI API Key 和 Serper API Key(用于搜索工具)。
  • 你大概知道 AgentGPT 的界面长什么样:输入目标,点“Deploy Agent”,然后看它一步步执行。

第一步:理解 AgentGPT 的“单线程”限制

默认情况下,AgentGPT 一次只能运行一个 Agent。你点“Deploy Agent”之后,界面会锁定,直到这个 Agent 完成或你手动停止。这不是 bug,是设计——因为一个 Agent 的思考链是连续的,它需要记住自己上一步干了什么,才能决定下一步。

但我们要的是“同时跑多个”,所以得换个思路:不依赖界面上的“单 Agent”模式,而是通过后端 API 直接创建多个 Agent 实例,让它们各自独立运行。

第二步:找到后端 API 入口

AgentGPT 的后端(FastAPI)暴露了一个创建 Agent 的接口。在项目目录 platform 下,找到 agent.py 或类似的文件(具体路径可能是 platform/routers/agent.py)。里面有一个 POST 端点,通常是 /agent/create/agent/run

我们不需要改代码,直接用 curl 或 Postman 调用它。先确认你的后端在运行(默认端口 8000)。

打开终端,试一下这个命令(替换 YOUR_OPENAI_KEYYOUR_SERPER_KEY):

curl -X POST http://localhost:8000/agent/create \
  -H "Content-Type: application/json" \
  -d '{
    "name": "竞品分析Agent",
    "goal": "搜索并列出特斯拉Cybertruck的主要竞品,每个竞品给出3个关键特点",
    "api_key": "YOUR_OPENAI_KEY",
    "serper_api_key": "YOUR_SERPER_KEY",
    "max_iterations": 5
  }'

预期结果:你会收到一个 JSON 响应,里面包含一个 id 字段,比如 "id": "abc123"。这个 ID 就是你这个 Agent 实例的唯一标识。如果报错 422,检查 JSON 格式和字段名是否正确(字段名可能叫 namegoalopenai_api_key 等,以实际代码为准,这里用的是常见命名)。

第三步:同时启动多个 Agent

现在,我们开三个终端窗口(或者用 & 后台运行),同时发三个请求,每个 Agent 干不同的活。

终端 1:竞品分析 Agent

curl -X POST http://localhost:8000/agent/create \
  -H "Content-Type: application/json" \
  -d '{
    "name": "竞品分析Agent",
    "goal": "搜索并列出特斯拉Cybertruck的主要竞品,每个竞品给出3个关键特点",
    "api_key": "YOUR_OPENAI_KEY",
    "serper_api_key": "YOUR_SERPER_KEY",
    "max_iterations": 5
  }'

终端 2:行业报告 Agent

curl -X POST http://localhost:8000/agent/create \
  -H "Content-Type: application/json" \
  -d '{
    "name": "行业报告Agent",
    "goal": "搜索2024年电动皮卡市场规模和增长趋势,总结成3个要点",
    "api_key": "YOUR_OPENAI_KEY",
    "serper_api_key": "YOUR_SERPER_KEY",
    "max_iterations": 5
  }'

终端 3:用户评价 Agent

curl -X POST http://localhost:8000/agent/create \
  -H "Content-Type: application/json" \
  -d '{
    "name": "用户评价Agent",
    "goal": "搜索Cybertruck早期用户的正面和负面评价,各列出2条",
    "api_key": "YOUR_OPENAI_KEY",
    "serper_api_key": "YOUR_SERPER_KEY",
    "max_iterations": 5
  }'

预期结果:三个请求几乎同时返回,每个都给你一个不同的 Agent ID。现在,这三个 Agent 正在后端并行执行。你可以打开浏览器,访问 http://localhost:3000,但界面上可能只显示一个 Agent 的状态(因为前端默认只跟踪最近创建的那个)。别慌,它们确实在跑。

第四步:查看每个 Agent 的执行结果

AgentGPT 的后端会把每个 Agent 的执行步骤和最终结果存到数据库里。我们可以通过另一个 API 来查询。

假设你有一个 Agent ID 是 abc123,执行这个命令:

curl http://localhost:8000/agent/abc123

预期结果:你会看到这个 Agent 的完整执行记录,包括它想了什么、调用了什么工具、得到了什么结果。如果 Agent 还在跑,你会看到 status: "running";如果完成了,就是 status: "completed",并且 result 字段里会有最终输出。

你可以对三个 ID 分别执行这个查询,等它们都变成 completed 后,把结果手动汇总。但这样太累了,我们得让它们自动把结果写到同一个地方。

第五步:让 Agent 共享结果——用文件作为“黑板”

最简单的方式:让每个 Agent 在完成时,把结果追加到一个共享的文本文件里。AgentGPT 的 Agent 可以执行 Python 代码(如果配置了代码执行工具)。我们给每个 Agent 的目标里加上一句:“最后,把结果写入 /tmp/shared_results.txt”。

修改一下请求体,比如竞品分析 Agent:

{
  "name": "竞品分析Agent",
  "goal": "搜索并列出特斯拉Cybertruck的主要竞品,每个竞品给出3个关键特点。最后,把结果写入 /tmp/shared_results.txt,格式为:'竞品分析: [你的结果]'",
  "api_key": "YOUR_OPENAI_KEY",
  "serper_api_key": "YOUR_SERPER_KEY",
  "max_iterations": 5
}

其他两个 Agent 也类似,但写入时用不同的前缀(比如“行业报告:”和“用户评价:”)。

注意:这要求 Agent 有文件写入权限。在 Docker 环境下,/tmp 通常是可写的。如果你用的是 Docker Compose 部署,确保 platform 容器能访问宿主机的 /tmp 目录(可以在 docker-compose.yml 里加 volume 映射)。

预期结果:等三个 Agent 都跑完后,登录到你的服务器或本地机器,执行:

cat /tmp/shared_results.txt

你会看到类似这样的输出:

竞品分析: 1. Rivian R1T - 特点:... 2. Ford F-150 Lightning - 特点:... 3. GMC Hummer EV - 特点:...
行业报告: 1. 2024年电动皮卡市场规模预计达... 2. 年增长率约... 3. 主要驱动力是...
用户评价: 正面:... 负面:...

完美!三个 Agent 的结果已经合并到一个文件里了。

常见报错与排查

报错:curl: (7) Failed to connect to localhost port 8000: Connection refused

后端没启动。确保你运行了 docker-compose up 或者手动启动了 FastAPI 服务(cd platform && uvicorn main:app --reload)。

报错:{"detail":"Not authenticated"}

AgentGPT 默认有认证。你需要在请求头里加上一个有效的 token。最简单的方法:在浏览器里登录 AgentGPT,打开开发者工具(F12),在 Application -> Local Storage 里找到类似 next-auth.session-token 的值,把它作为 Authorization: Bearer <token> 加到 curl 命令里。

curl -X POST http://localhost:8000/agent/create \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
  -d '{...}'

报错:Agent 一直卡在“思考”阶段,不执行工具

检查 API Key 是否有效,以及 Serper API 的配额是否用完。可以在后端日志里看到具体的错误信息(docker logs <container_name>)。

实用技巧

  • 控制并发数量:不要一次启动几十个 Agent,OpenAI API 有速率限制。建议同时不超过 5 个,除非你用了多个 API Key。
  • 给 Agent 起有意义的名字:这样在数据库里查记录时,一眼就能看出哪个是干什么的。
  • 用脚本自动化:把上面的 curl 命令写成一个 shell 脚本,循环启动多个 Agent,然后轮询检查状态,最后自动汇总结果。这样你点一下就能跑完整个流程。

小例子串起来

假设你要做一个“智能周报系统”,需要同时收集三个维度的信息:团队进度、行业新闻、竞品动态。你可以写一个简单的 Python 脚本,调用 AgentGPT 的 API 启动三个 Agent,等它们都完成后,读取共享文件,然后用一个模板生成周报。整个过程不到 10 行代码,但背后是三个 AI 在并行工作。

多 Agent 协作的核心思路就是:各自独立执行,共享结果存储。你不需要改 AgentGPT 的代码,只需要用好它的 API,再加一点“文件黑板”的创意。下一章我们会聊怎么在前端定制一个页面,让你能直观地看到所有 Agent 的运行状态,而不是靠 curl 去查。

8. 8. 前端定制:修改 UI 主题与添加自定义页面

改颜色、换字体、加页面:让 AgentGPT 长成你想要的样子

AgentGPT 默认的深色主题挺酷的,但如果你想让界面更符合自己的品牌色、或者想加一个“关于我们”页面、甚至把整个 UI 改成浅色模式,完全不用动底层逻辑——前端用的是 Next.js + TailwindCSS,改起来比想象中简单得多。

这一章我们就干两件事:先改主题(颜色、字体、间距这些全局样式),再添加一个自定义页面(比如一个“使用说明”页)。做完之后,你的 AgentGPT 看起来就不像“别人的项目”了。

前置条件

  • 已经跑通了本地开发环境(第 1 章的内容)
  • React 组件和 TailwindCSS 有最基础的了解(知道 className 怎么写就行)
  • 项目根目录下的 next 文件夹就是前端代码所在

第一步:找到主题配置文件

TailwindCSS 的主题配置集中在 next/tailwind.config.js 里。打开它,你会看到类似这样的结构:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

theme.extend 就是我们的游乐场。所有自定义颜色、字体、间距都写在这里,不会覆盖 Tailwind 默认值,只是叠加。

第二步:改颜色——把主色调换成你的品牌色

假设你想把主色调从默认的蓝色(blue-600)换成一种更温暖的橙色。先找到项目里用了 blue-600 的地方——别慌,不用一个个文件翻,Tailwind 的配置可以一次性搞定。

tailwind.config.jstheme.extend 里加上 colors

theme: {
  extend: {
    colors: {
      primary: {
        50: '#fff7ed',
        100: '#ffedd5',
        200: '#fed7aa',
        300: '#fdba74',
        400: '#fb923c',
        500: '#f97316',
        600: '#ea580c',
        700: '#c2410c',
        800: '#9a3412',
        900: '#7c2d12',
      },
    },
  },
},

保存之后,你就可以在任意组件里用 bg-primary-600text-primary-500 这样的类名了。但问题是,项目里原来写死的 blue-600 不会自动变成 primary-600——你需要全局替换。别手动改,用编辑器的“在文件中查找替换”功能,把 blue-600 换成 primary-600blue-500 换成 primary-500,以此类推。

预期结果:刷新页面后,按钮、链接、高亮文字都变成了橙色系。

第三步:改字体——换一个更现代的字体

默认字体是系统无衬线字体(sans)。如果你想换成 Inter 或者 Noto Sans SC(对中文更友好),需要两步。

先安装字体包。在 next 目录下运行:

npm install @fontsource/inter

然后在 next/src/pages/_app.tsx 里引入:

import '@fontsource/inter/400.css';
import '@fontsource/inter/600.css';
import '@fontsource/inter/700.css';

最后回到 tailwind.config.js,在 theme.extend 里加上 fontFamily

fontFamily: {
  sans: ['Inter', 'system-ui', 'sans-serif'],
},

预期结果:所有文本的字体都变成了 Inter,标题更清晰,阅读体验提升明显。

第四步:添加一个自定义页面——比如“使用说明”

现在我们要加一个 /guide 页面,里面放一些 AgentGPT 的使用技巧。Next.js 的页面路由在 next/src/pages 下,每个 .tsx 文件对应一个路由。

新建 next/src/pages/guide.tsx

import type { NextPage } from 'next';
import Head from 'next/head';
import Link from 'next/link';

const Guide: NextPage = () => {
  return (
    <>
      <Head>
        <title>使用说明 - AgentGPT</title>
      </Head>
      <div className="min-h-screen bg-gray-900 text-white p-8">
        <div className="max-w-3xl mx-auto">
          <Link href="/" className="text-primary-500 hover:underline mb-4 block">
            ← 返回首页
          </Link>
          <h1 className="text-3xl font-bold mb-6">AgentGPT 使用说明</h1>
          <section className="space-y-4">
            <div className="bg-gray-800 rounded-lg p-6">
              <h2 className="text-xl font-semibold mb-2">1. 创建你的第一个 Agent</h2>
              <p className="text-gray-300">
                在首页输入 Agent 名称和目标,点击部署即可。Agent 会自动拆解任务并执行。
              </p>
            </div>
            <div className="bg-gray-800 rounded-lg p-6">
              <h2 className="text-xl font-semibold mb-2">2. 配置 API 密钥</h2>
              <p className="text-gray-300">
                前往设置页面填入 OpenAI 和 Serper 的 API 密钥,Agent 才能联网和调用 LLM。
              </p>
            </div>
            <div className="bg-gray-800 rounded-lg p-6">
              <h2 className="text-xl font-semibold mb-2">3. 查看执行日志</h2>
              <p className="text-gray-300">
                每个 Agent 的运行过程都会实时展示在右侧面板,你可以随时停止或重新运行。
              </p>
            </div>
          </section>
        </div>
      </div>
    </>
  );
};

export default Guide;

保存后,访问 http://localhost:3000/guide,就能看到这个新页面了。

预期结果浏览器打开 /guide 路径,显示一个带返回链接的说明页面,样式和主站保持一致(因为用了同样的 Tailwind 类)。

第五步:在导航栏里加一个入口

光有页面没人知道也不行。找到导航栏组件,通常在 next/src/components/Navbar.tsx 或类似位置。在里面加一个链接:

<Link href="/guide" className="text-gray-300 hover:text-white transition-colors">
  使用说明
</Link>

位置可以放在“设置”按钮旁边。保存后刷新,导航栏就多了一个入口。

常见问题与排查

改了颜色但页面没变化? 检查 tailwind.config.js 的路径是否正确——content 数组里要包含所有用到了 Tailwind 类名的文件路径。如果只配了 ./src/**/*.{js,ts,jsx,tsx},但你的组件在别的地方,就不会生效。

自定义页面样式和主站不一致? 确保新页面也引入了全局样式文件。在 next/src/pages/_app.tsx 里通常已经 import 了 ../styles/globals.css,新页面只要用了 Tailwind 类名就会自动继承。

字体加载慢? @fontsource 的字体是自托管的,不会依赖 Google Fonts 的 CDN,速度很快。如果还是慢,检查是否只引入了需要的字重(比如只引入 400、600、700,不要全量引入)。

一个真实场景串起来

假设你给公司内部部署了一个 AgentGPT,用来自动生成周报。你可以把主色调改成公司品牌色(比如深蓝色 #1e3a5f),字体改成更商务的 Inter,然后加一个 /guide 页面,里面写清楚“如何配置周报模板”“如何设置定时运行”等内部文档。这样团队成员打开系统就能直接看到指引,不用再翻文档了。

9. 9. 后端扩展:添加自定义 API 端点与数据库模型

给 AgentGPT 加个"记分牌":自定义 API 端点与数据库模型

假设你跑了一段时间的 AgentGPT,发现每次 Agent 完成任务后,你想知道它花了多少步、调用了多少次工具、最终结果是什么——但这些信息散落在日志里,没有一个专门的接口能查。更麻烦的是,你想给每个 Agent 打标签分类,比如"市场调研组""代码生成组",现有的数据库表里根本没这个字段。

这时候你就得自己动手了:加一个新的数据库模型来存 Agent 的执行记录,再写一个 API 端点让前端能查这些数据。这一章我们就干这件事。

前置准备

  • 你已经成功启动了 AgentGPT 的本地开发环境(第 1 章的内容)
  • 你熟悉项目的基本目录结构,特别是 platform/prisma/ 这两个文件夹
  • 你装了 Prisma CLI(通常跟着项目一起装好了,没装的话 npm install -g prisma

第一步:设计你要存什么

先想清楚需求。我们要存的是 Agent 每次执行任务的"快照":

  • agent_id:哪个 Agent 干的
  • goal:它当时的目标是什么
  • steps_taken:它执行了多少步
  • tools_used:用了哪些工具(比如搜索、计算)
  • result_summary:最终结果的摘要
  • created_at:什么时候完成的

这些信息跟 AgentGPT 现有的 Agent 模型是分开的,所以我们新建一个表,叫 AgentRun

第二步:在 Prisma 里加模型

AgentGPT 的数据库模型定义在 prisma/schema.prisma 里。打开这个文件,在现有的模型后面加上:

model AgentRun {
  id            String   @id @default(cuid())
  agentId       String
  goal          String
  stepsTaken    Int      @default(0)
  toolsUsed     String   @default("")
  resultSummary String?
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt

  agent         Agent    @relation(fields: [agentId], references: [id])
}

注意这里 agentId 关联到了已有的 Agent 模型——这样你就能通过 Agent 查到它所有的执行记录,反过来也能从执行记录找到对应的 Agent。

加完之后,运行迁移命令:

npx prisma migrate dev --name add_agent_run

如果一切顺利,你会看到类似这样的输出:

Your database is now in sync with your schema.
✔ Generated Prisma Client (4.x.x) to ./node_modules/@prisma/client

这时候去数据库里看一眼,应该多了一张 AgentRun 表。

第三步:写 API 端点

AgentGPT 的后端用的是 FastAPI,路由文件在 platform/routers/ 下面。我们新建一个文件 platform/routers/agent_runs.py

from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session, select
from typing import List
from ..database import get_session
from ..models import AgentRun, AgentRunCreate, AgentRunPublic

router = APIRouter(prefix="/api/agent-runs", tags=["agent-runs"])

@router.get("/", response_model=List[AgentRunPublic])
def list_agent_runs(
    agent_id: str = None,
    limit: int = 10,
    session: Session = Depends(get_session)
):
    query = select(AgentRun)
    if agent_id:
        query = query.where(AgentRun.agentId == agent_id)
    query = query.order_by(AgentRun.createdAt.desc()).limit(limit)
    results = session.exec(query).all()
    return results

@router.post("/", response_model=AgentRunPublic)
def create_agent_run(
    run_data: AgentRunCreate,
    session: Session = Depends(get_session)
):
    run = AgentRun.from_orm(run_data)
    session.add(run)
    session.commit()
    session.refresh(run)
    return run

等等,这里用了 AgentRunCreateAgentRunPublic,我们还没定义它们。在 platform/models.py 里加上:

from sqlmodel import SQLModel, Field
from datetime import datetime
from typing import Optional

class AgentRunBase(SQLModel):
    agentId: str
    goal: str
    stepsTaken: int = 0
    toolsUsed: str = ""
    resultSummary: Optional[str] = None

class AgentRunCreate(AgentRunBase):
    pass

class AgentRunPublic(AgentRunBase):
    id: str
    createdAt: datetime
    updatedAt: datetime

最后,别忘了在 platform/main.py 里注册这个路由:

from .routers import agent_runs

app.include_router(agent_runs.router)

重启后端服务(docker-compose restart backend 或者直接 Ctrl+Cdocker-compose up),然后试试看:

curl http://localhost:8000/api/agent-runs/

应该返回一个空数组 []。再用 POST 创建一条记录:

curl -X POST http://localhost:8000/api/agent-runs/ \
  -H "Content-Type: application/json" \
  -d '{"agentId": "你的agent_id", "goal": "调研竞品", "stepsTaken": 5, "toolsUsed": "search,web"}'

返回的 JSON 里应该包含你刚提交的数据,外加 idcreatedAt 这些自动生成的字段。

第四步:让 Agent 执行完后自动记录

光有 API 端点还不够,得让 Agent 在完成任务后自动调用这个接口。找到 Agent 执行完成后的回调位置——通常在 platform/services/agent.py 或类似文件里,有个 execute_agent 函数,执行完所有步骤后会返回结果。在那个位置加上:

from ..routers.agent_runs import create_agent_run
from ..database import get_session

# 在 agent 执行完所有步骤后
async def record_agent_run(agent, result):
    session = next(get_session())
    run_data = AgentRunCreate(
        agentId=agent.id,
        goal=agent.goal,
        stepsTaken=len(agent.executed_tasks),
        toolsUsed=",".join(agent.used_tools),
        resultSummary=result[:500]  # 只存前500字符,防止太长
    )
    create_agent_run(run_data, session)

这里有个小坑:get_session 是个生成器,你得用 next() 来拿 session 实例。如果你直接传进去,FastAPI 的依赖注入系统会报错。

常见报错与排查

"Table 'AgentRun' doesn't exist"
迁移没跑成功。检查一下 prisma/schema.prisma 的语法,然后重新跑 npx prisma migrate dev

"Column 'toolsUsed' cannot be null"
你在 POST 请求里没传 toolsUsed 字段,但模型里没设默认值。要么在请求里加上,要么在模型定义里给个默认空字符串。

"AttributeError: 'AgentRun' object has no attribute 'from_orm'"
你用的 SQLModel 版本可能比较老。换成手动赋值:run = AgentRun(**run_data.dict())

小技巧:给前端加个页面

后端搞定了,前端也得跟上。在 next/pages/ 下新建一个 agent-runs.tsx,用 fetch 调用 /api/agent-runs/,把数据渲染成一个表格。这样你就能在浏览器里直接看到所有 Agent 的执行记录了。

一个完整的例子

假设你有个 Agent 叫"市场小助手",目标是"分析三家竞品的定价策略"。它执行了 8 步,用了搜索和网页抓取工具,最终结果是一段总结。执行完后,数据库里会多一条记录:

{
  "id": "clx...",
  "agentId": "cm5...",
  "goal": "分析三家竞品的定价策略",
  "stepsTaken": 8,
  "toolsUsed": "search,web",
  "resultSummary": "竞品A定价$19/月,竞品B$29/月,竞品C$9/月...",
  "createdAt": "2024-03-15T10:30:00Z",
  "updatedAt": "2024-03-15T10:30:00Z"
}

下次你想复盘这个 Agent 的表现,直接 GET 这个接口就能拿到所有历史数据,不用再去翻日志了。

10. 10. 性能调优:缓存、并发控制与数据库优化

你的 Agent 跑得慢?先别急着怪 OpenAI

你有没有遇到过这种情况:Agent 明明设定好了目标,结果等了好几分钟才返回第一个结果,中间还时不时卡住不动。更让人抓狂的是,同样的任务,第二次跑反而快了不少——但第三次又慢了。

这不是玄学。AgentGPT 的性能瓶颈通常不在 OpenAIAPI 响应速度上(虽然那也确实不慢),而是出在三个地方:重复的数据库查询串行执行的任务队列、以及没必要的全表扫描。这一章我们就来逐个击破。

前置条件

  • 你已经成功运行过 AgentGPT(本地或服务器都行)
  • 数据库里有至少几十条 Agent 运行记录(没数据的话,跑几个测试任务就行)
  • 能访问后端代码(platform/ 目录)

第一步:给数据库查询加缓存——别反复读同一份数据

先看一个典型场景:每次 Agent 执行任务时,都要从数据库里查当前 Agent 的配置信息。如果一次任务循环 10 步,那同样的查询就跑了 10 次。这就像你每次去厨房拿杯子都重新走一遍从卧室到厨房的路——明明可以一次拿完。

AgentGPT 后端用的是 FastAPI + SQLModel,缓存最直接的办法是用 functools.lru_cache 配合 Redis。但为了快速见效,我们先从最简单的内存缓存开始。

打开 platform/core/agent.py,找到加载 Agent 配置的地方(大概是 load_agent_config 函数),加上缓存:

from functools import lru_cache

@lru_cache(maxsize=128)
def get_cached_agent_config(agent_id: str) -> dict:
    # 原来的数据库查询逻辑
    agent = session.query(Agent).filter(Agent.id == agent_id).first()
    return agent.to_dict() if agent else None

预期结果:同一个 agent_id 在缓存有效期内只查一次数据库。后续调用直接从内存返回。

注意lru_cache 默认不清除,如果 Agent 配置在运行中被修改,缓存会返回旧数据。生产环境建议用 Redis + TTL(过期时间),后面会讲。

第二步:用 Redis 做分布式缓存——多实例共享

如果你的 AgentGPT 部署了多个后端实例(比如用 Docker Compose 起了两个 platform 容器),内存缓存就失效了——每个实例有自己的缓存,数据不一致。

这时候需要 Redis。AgentGPT 的 docker-compose.yml 里其实已经预留了 Redis 服务,只是默认没启用。我们把它加上:

# docker-compose.yml 的 services 部分
redis:
  image: redis:7-alpine
  ports:
    - "6379:6379"
  volumes:
    - redis_data:/data

然后在后端代码里连接 Redis。打开 platform/core/dependencies.py,添加:

import redis.asyncio as redis

redis_client = redis.from_url("redis://redis:6379/0")

async def get_cached(key: str):
    return await redis_client.get(key)

async def set_cached(key: str, value: str, ttl: int = 300):
    await redis_client.setex(key, ttl, value)

预期结果:多个后端实例共享同一个缓存,配置数据在 5 分钟内只查一次数据库。

常见报错:如果 Redis 连不上,检查 docker-compose.ymlplatform 服务的 depends_on 有没有加 redis,以及网络配置是否正确。

第三步:并发控制——别让 Agent 同时干太多事

AgentGPT 默认是串行执行任务的:Agent 想一个任务 -> 执行 -> 看结果 -> 再想下一个。这很安全,但也很慢。

一个常见的优化是让 Agent 在等待 OpenAI 返回结果时,先处理其他不依赖这个结果的任务。比如:Agent 要搜索三个关键词,完全可以同时搜,不用等第一个搜完再搜第二个。

修改 platform/core/agent.py 里的任务执行逻辑,用 asyncio.gather 并发执行独立任务:

import asyncio

async def execute_tasks_concurrently(tasks: list):
    # 只并发执行互不依赖的任务
    independent_tasks = [task for task in tasks if not task.depends_on]
    results = await asyncio.gather(
        *[execute_single_task(task) for task in independent_tasks],
        return_exceptions=True
    )
    return results

预期结果:原本需要 30 秒的 3 个独立搜索任务,现在可能 10 秒就完成了。

注意:并发数不是越大越好。OpenAI API 有速率限制(Rate Limit),并发太多会被 429 拒绝。建议加个信号量控制最大并发数:

semaphore = asyncio.Semaphore(3)  # 最多同时 3 个请求

async def execute_single_task(task):
    async with semaphore:
        # 原来的执行逻辑
        pass

第四步:数据库优化——索引和查询瘦身

AgentGPT 用 Prisma(前端)和 SQLModel(后端)操作 MySQL/PlanetScale。最常出问题的查询是:按 Agent ID 查所有执行记录

打开 platform/models/agent_execution.py,看看 get_executions_by_agent 函数。如果它没有索引,每次都要全表扫描。

加索引:

# 在 AgentExecution 模型定义里
class AgentExecution(SQLModel, table=True):
    __tablename__ = "agent_executions"
    
    id: int = Field(primary_key=True)
    agent_id: str = Field(index=True)  # 加索引!
    created_at: datetime = Field(default_factory=datetime.utcnow)
    status: str = Field(default="pending")

然后运行数据库迁移:

# 如果你用 Alembic
alembic revision --autogenerate -m "add index on agent_id"
alembic upgrade head

预期结果:按 Agent ID 查询执行记录的速度提升 10-100 倍(取决于数据量)。

实用技巧:如果查询经常按 created_at 排序,可以建复合索引:

agent_id: str = Field(index=True)
# 在迁移脚本里手动加复合索引
# CREATE INDEX idx_agent_created ON agent_executions (agent_id, created_at DESC);

第五步:用一个小例子验证效果

假设你有一个 Agent,任务是“调研三家竞品公司的最新动态”。它需要:

  1. 搜索公司 A(3 个关键词)
  2. 搜索公司 B(3 个关键词)
  3. 搜索公司 C(3 个关键词)

优化前:9 个搜索串行执行,每个等 2-3 秒,加上数据库查询,总共 30 秒以上。

优化后:

  • 缓存:Agent 配置只查一次,后续 5 分钟内复用
  • 并发:9 个搜索同时发起(受信号量限制,实际同时 3 个)
  • 索引:查询执行记录毫秒级返回

总时间降到 10-12 秒,而且随着任务数增加,优化效果更明显。

常见问题排查

Q:加了缓存后,Agent 配置改了但没生效? A:lru_cache 默认不清除。临时方案是重启后端,或者改成 Redis + TTL。生产环境建议用 Redis,设置 60 秒过期。

Q:并发执行后,OpenAI 返回 429 Too Many Requests? A:降低信号量的最大值,从 3 降到 2 或 1。也可以加指数退避重试:

import backoff

@backoff.on_exception(backoff.expo, openai.error.RateLimitError, max_tries=5)
async def call_openai(prompt):
    # 原来的 API 调用
    pass

Q:加了索引后查询反而变慢了? A:检查是不是建了太多索引。每个索引都会拖慢写入速度。只给最常用的查询字段加索引,比如 agent_idcreated_at

Q:Redis 内存会不会爆? A:设置合理的 TTL(过期时间),比如 300 秒。如果数据量大,还可以用 maxmemory-policy allkeys-lru 让 Redis 自动淘汰不常用的缓存。

11. 11. 部署到生产:使用 Docker Compose 与反向代理

从本地跑通到线上可用:Docker Compose 部署 + Nginx 反向代理

如果你已经跟着前面几章把 AgentGPT 在本地跑起来了,现在应该能在 localhost:3000 上看到那个漂亮的聊天界面,并且能正常调用 Agent。但问题来了——你总不能把笔记本搬到客户现场,或者让同事都连你电脑的 localhost 吧?

这一章要解决的就是:怎么让 AgentGPT 变成一个真正可访问的线上服务。我们会用 Docker Compose 把整个项目打包成标准化的容器,再用 Nginx 做反向代理,配上 HTTPS 和域名。这样你部署到任何 VPS 或者云服务器上,别人就能通过 https://你的域名.com 来用你的 Agent 了。

前置条件

开始之前,确认你已经:

  • 在本机成功运行过 AgentGPT(第 1 章的内容)
  • 安装了 Docker 和 Docker Compose(版本 2.x 以上)
  • 有一台云服务器(或者能跑 Docker 的 Linux 机器)
  • 有一个域名(比如 myagent.example.com),并且 DNS 已经指向你的服务器 IP
  • 准备好 OpenAI API Key 和 Serper API Key(第 2 章的内容)

第一步:理解项目自带的 Docker 配置

AgentGPT 的根目录下有个 docker-compose.yml 文件,打开看看:

version: '3.9'
services:
  backend:
    build: ./platform
    ports:
      - "8000:8000"
    env_file:
      - .env
    depends_on:
      - db
  frontend:
    build: ./next
    ports:
      - "3000:3000"
    env_file:
      - .env
    depends_on:
      - backend
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD}
      MYSQL_DATABASE: agentgpt
    volumes:
      - mysql_data:/var/lib/mysql

volumes:
  mysql_data:

这个配置定义了三个服务:backend(FastAPI)、frontend(Next.js)和 db(MySQL)。但注意——它直接把后端和前端的端口暴露到了宿主机上。这在开发环境没问题,但生产环境里,直接暴露应用端口是不安全的,而且你也不想让用户通过 http://你的IP:3000 来访问吧?

第二步:准备生产环境的 .env 文件

在项目根目录创建一个 .env.production 文件(不要跟开发用的 .env 搞混):

# 数据库配置
MYSQL_PASSWORD=你的强密码
MYSQL_USER=agentgpt
MYSQL_DATABASE=agentgpt

# OpenAI
OPENAI_API_KEY=sk-你的key

# Serper(可选,但建议加上)
SERPER_API_KEY=你的serperkey

# NextAuth 配置(生产环境必须改)
NEXTAUTH_SECRET=生成一个随机字符串
NEXTAUTH_URL=https://你的域名.com

# 后端配置
BACKEND_URL=https://你的域名.com/api
FRONTEND_URL=https://你的域名.com

NEXTAUTH_SECRET 可以用这个命令生成一个随机字符串:

openssl rand -base64 32

第三步:添加 Nginx 反向代理

现在我们要在 Docker Compose 里加一个 Nginx 服务,让它充当所有请求的入口。修改 docker-compose.yml,在 services 下面新增:

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - frontend
      - backend

然后在项目根目录创建 nginx.conf

events {
    worker_connections 1024;
}

http {
    upstream frontend {
        server frontend:3000;
    }

    upstream backend {
        server backend:8000;
    }

    server {
        listen 80;
        server_name 你的域名.com;
        return 301 https://$server_name$request_uri;
    }

    server {
        listen 443 ssl;
        server_name 你的域名.com;

        ssl_certificate /etc/nginx/ssl/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/privkey.pem;

        # 前端静态文件
        location / {
            proxy_pass http://frontend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        # API 请求转发到后端
        location /api/ {
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

这个配置做了三件事:

  1. 把所有 HTTP 请求重定向到 HTTPS
  2. / 路径的请求转发给前端容器
  3. /api/ 路径的请求转发给后端容器

第四步:配置 SSL 证书

在项目根目录创建 ssl 文件夹,然后把你的证书放进去。如果你还没有证书,用 Let's Encrypt 免费申请一个:

# 先安装 certbot
sudo apt install certbot

# 申请证书(需要你的域名已经指向服务器 IP)
sudo certbot certonly --standalone -d 你的域名.com

# 把证书复制到项目目录
sudo cp /etc/letsencrypt/live/你的域名.com/fullchain.pem ./ssl/
sudo cp /etc/letsencrypt/live/你的域名.com/privkey.pem ./ssl/

第五步:修改前端配置让它知道生产环境地址

打开 next/.env.production(如果没有就创建),写入:

NEXT_PUBLIC_API_URL=https://你的域名.com/api
NEXT_PUBLIC_NEXTAUTH_URL=https://你的域名.com

然后修改 next/next.config.js,加上允许的域名:

module.exports = {
  images: {
    domains: ['你的域名.com'],
  },
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          { key: 'X-Frame-Options', value: 'DENY' },
          { key: 'X-Content-Type-Options', value: 'nosniff' },
          { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
        ],
      },
    ];
  },
};

第六步:构建并启动

现在万事俱备,可以启动了。在项目根目录执行:

# 先构建镜像(第一次会比较慢)
docker-compose build

# 启动所有服务
docker-compose -f docker-compose.yml up -d

-d 参数让容器在后台运行。第一次启动时,Docker 会下载 MySQL 镜像、构建前端和后端的镜像,可能需要几分钟。

启动后检查服务状态:

docker-compose ps

你应该看到四个服务都在运行:nginxfrontendbackenddb

第七步:验证部署

打开浏览器访问 https://你的域名.com。如果一切正常,你应该能看到 AgentGPT 的登录页面。试着创建一个 Agent 跑一下,看看 API 调用是否正常。

如果遇到问题,先检查日志:

# 查看所有服务的日志
docker-compose logs

# 只看某个服务的日志
docker-compose logs backend
docker-compose logs frontend

常见问题排查

问题 1:前端能打开,但 Agent 执行时报错 "Network Error"

这通常是前端找不到后端 API。检查 next/.env.production 里的 NEXT_PUBLIC_API_URL 是否正确,以及 Nginx 配置里的 /api/ 路径转发是否写对了。

问题 2:数据库连接失败

检查 .env.production 里的 MYSQL_PASSWORD 是否跟 docker-compose.yml 里的 MYSQL_ROOT_PASSWORD 一致。另外,MySQL 容器启动需要时间,如果后端启动太快可能会连不上——depends_on 只保证容器启动了,不保证 MySQL 已经准备好接受连接。

问题 3:HTTPS 证书报错

如果用的是 Let's Encrypt 证书,记得证书有效期只有 90 天。可以加个定时任务自动续期:

# 编辑 crontab
crontab -e

# 添加这行,每月 1 号凌晨 3 点续期并重启 Nginx
0 3 1 * * certbot renew --quiet && docker-compose restart nginx

问题 4:前端页面样式错乱或功能异常

这通常是因为前端构建时用了开发环境的配置。确保你构建镜像前,next/.env.production 文件存在且内容正确。如果还是不行,试试清理构建缓存:

docker-compose down -v
docker system prune -a
docker-compose build --no-cache
docker-compose up -d

小贴士:用 Docker Compose 管理生产环境

部署完成后,日常维护就靠这几个命令:

# 查看实时日志
docker-compose logs -f

# 重启某个服务
docker-compose restart backend

# 更新代码后重新部署
git pull
docker-compose build
docker-compose up -d

# 停止所有服务
docker-compose down

注意:docker-compose down 会停止并删除容器,但不会删除数据卷(volumes),所以数据库数据还在。如果你要彻底清理(包括数据库),用 docker-compose down -v

一个真实场景

假设你给公司部署了一个 AgentGPT 实例,用来自动生成周报。部署完成后,团队成员只需要打开 https://weekly-report.company.com,登录后输入"生成本周工作总结",Agent 就会自动从 Jira 拉取任务、分析完成情况、生成 Markdown 格式的周报。

这个部署方案的好处是:所有服务都在 Docker 容器里,互不干扰;Nginx 统一管理流量和 HTTPS;数据库数据持久化在 Docker 卷里,重启不会丢失。以后要升级版本,只需要 git pull 然后重新构建就行了。

现在你的 AgentGPT 已经是一个正经的线上服务了。下一章我们会聊聊怎么监控它的运行状态——毕竟线上服务出了故障,你得第一时间知道。

12. 12. 监控与日志:集成 Sentry 与 Prometheus

你的 Agent 跑起来了,用户也玩得挺开心。但有个问题:它到底在后台干了什么?如果某天它突然不干活了,或者回答变得莫名其妙,你只能干瞪眼——重启一下碰碰运气。这就像开车没有仪表盘,全凭感觉踩油门。

这一章就是给你的 Agent 装上仪表盘和行车记录仪。我们会集成两个工具:Sentry 用来抓代码层面的异常和性能问题,Prometheus + Grafana 用来监控系统运行指标(比如请求量、响应时间、内存占用)。装好之后,你不仅能知道“出事了”,还能知道“哪里出事了”以及“什么时候开始出事的”。

前置条件

  • 你已经按照第 11 章(或之前的部署流程)用 Docker Compose 把 AgentGPT 跑起来了。
  • 你有一个 Sentry 账号(免费额度够用)。
  • 你有一个能跑 Docker 的服务器或本地环境。
  • 基本的 docker-compose.yml 文件修改权限。

第一步:给后端(FastAPI)接入 Sentry

Sentry 主要帮我们抓后端 Python 代码里的异常。比如某个 API 调用 OpenAI 超时了,或者数据库连接断了,Sentry 会记录下完整的堆栈信息。

  1. 安装 Sentry SDK 进入 platform/ 目录(你的后端代码所在目录),在终端执行:

    pip install sentry-sdk

    然后把 sentry-sdk 添加到 requirements.txt 里,这样 Docker 构建时也会装上。

  2. 初始化 Sentry 打开 platform/main.py(FastAPI 的入口文件),在文件顶部附近加入:

    import sentry_sdk
    from sentry_sdk.integrations.fastapi import FastApiIntegration
    from sentry_sdk.integrations.sqlalchemy import SqlAlchemyIntegration
    
    sentry_sdk.init(
        dsn="https://你的DSN@sentry.io/项目ID",  # 从 Sentry 项目设置里复制
        integrations=[
            FastApiIntegration(),
            SqlAlchemyIntegration(),
        ],
        traces_sample_rate=1.0,  # 生产环境可以调低,比如 0.2
        environment="production",  # 或 "staging"
    )

    预期结果:重启后端服务后,手动制造一个错误(比如故意让一个 API 除以零),去 Sentry 后台的 Issues 页面应该能看到这条错误记录。

  3. 配置环境变量SENTRY_DSN 加到你的 .env 文件里,然后在 docker-compose.yml 中传给后端容器:

    services:
      platform:
        environment:
          - SENTRY_DSN=${SENTRY_DSN}

    常见报错:如果 Sentry 没收到数据,检查 DSN 是否拼写正确,以及网络是否能访问 sentry.io(有些内网环境需要配置代理)。


第二步:给前端(Next.js)接入 Sentry

前端错误同样重要——用户可能遇到白屏、按钮点不动,这些都需要 Sentry 来捕获。

  1. 安装 Sentry 的 Next.js SDKnext/ 目录下执行:

    npm install @sentry/nextjs
  2. 创建 Sentry 配置文件next/ 目录下新建 sentry.client.config.js

    import * as Sentry from "@sentry/nextjs";
    
    Sentry.init({
      dsn: "https://你的DSN@sentry.io/项目ID",
      tracesSampleRate: 1.0,
      environment: process.env.NODE_ENV,
    });

    再新建 sentry.server.config.js(内容基本一样,用于服务端渲染的错误捕获)。

  3. 修改 next.config.jsnext.config.js 中引入 Sentry 的插件:

    const { withSentryConfig } = require("@sentry/nextjs");
    
    const moduleExports = {
      // 你原有的配置
    };
    
    module.exports = withSentryConfig(moduleExports, {
      silent: true, // 构建时不要打印太多日志
    });

    预期结果:重新构建前端(docker-compose up --build),然后在浏览器控制台手动抛一个错误(比如 throw new Error("测试")),Sentry 后台应该能收到。

    实用技巧:Sentry 的免费版每月有 5000 个事件额度,对于个人项目完全够用。如果担心超量,可以把 tracesSampleRate 降到 0.1。


第三步:部署 Prometheus + Grafana 监控系统指标

Sentry 管代码异常,Prometheus 管系统指标——比如你的 Agent 每秒处理多少个请求、API 响应时间是不是变慢了。

  1. 在 FastAPI 中暴露 Prometheus 指标 安装 prometheus-fastapi-instrumentator

    pip install prometheus-fastapi-instrumentator

    platform/main.py 中添加:

    from prometheus_fastapi_instrumentator import Instrumentator
    
    # 在创建 FastAPI 实例之后
    Instrumentator().instrument(app).expose(app)

    这会自动在 /metrics 端点暴露指标数据。

    预期结果:访问 http://你的服务器:8000/metrics,应该能看到一堆以 # HELP# TYPE 开头的文本,那就是 Prometheus 能读懂的指标。

  2. docker-compose.yml 中添加 Prometheus 和 Grafanaservices 下面新增两个服务:

    services:
      prometheus:
        image: prom/prometheus:latest
        volumes:
          - ./prometheus.yml:/etc/prometheus/prometheus.yml
        ports:
          - "9090:9090"
        restart: unless-stopped
    
      grafana:
        image: grafana/grafana:latest
        ports:
          - "3001:3000"  # 注意:前端已经用了 3000,所以 Grafana 用 3001
        environment:
          - GF_SECURITY_ADMIN_PASSWORD=admin  # 改成你自己的密码
        volumes:
          - grafana_data:/var/lib/grafana
        restart: unless-stopped
    
    volumes:
      grafana_data:
  3. 创建 Prometheus 配置文件 在项目根目录新建 prometheus.yml

    global:
      scrape_interval: 15s
    
    scrape_configs:
      - job_name: "agentgpt-backend"
        static_configs:
          - targets: ["platform:8000"]  # 注意:这里用 Docker 内部的服务名

    预期结果:执行 docker-compose up -d 启动所有服务。访问 http://你的服务器:9090,在 Prometheus 的 Status -> Targets 页面应该能看到 agentgpt-backend 状态为 UP。

    常见报错:如果 Prometheus 显示 connection refused,检查 targets 里的地址是否正确。在 Docker 内部,服务名就是 platform,端口是 8000

  4. 配置 Grafana 数据源和仪表盘

    • 访问 http://你的服务器:3001,用 admin / 你设置的密码登录。
    • 点击左侧齿轮图标 -> Data Sources -> Add data source -> 选择 Prometheus。
    • URL 填 http://prometheus:9090(Docker 内部地址),点击 Save & Test。
    • 回到首页,点击 + 号 -> Import,输入仪表盘 ID 1860(这是一个通用的 FastAPI 监控仪表盘),加载后选择你的 Prometheus 数据源。

    预期结果:Grafana 里应该能看到请求速率、延迟分布、错误率等图表。如果数据是空的,等一两分钟让 Prometheus 先采集几轮数据。


一个小例子串起来

假设你部署了一个“自动调研竞品”的 Agent。某天用户反馈说“Agent 跑了一半卡住了”。没有监控时,你只能重启。现在你可以:

  1. 打开 Sentry,看有没有新的错误——发现是 OpenAI 返回了一个 rate_limit_error
  2. 打开 Grafana,看 http_requests_total 指标——发现请求量在某个时间点突然飙升,触发了 OpenAI 的限流。
  3. 于是你决定给 Agent 加上重试逻辑和请求排队,问题解决。

这就是监控的价值:从“猜”变成“看”


注意事项

  • 不要在生产环境用默认的 Grafana 密码,改掉它。
  • Prometheus 的数据默认存在容器里,重启会丢失。如果需要持久化,挂载一个外部卷或目录。
  • Sentry 的 traces_sample_rate 在生产环境建议设为 0.1 或更低,否则可能产生大量数据,吃掉免费额度。
  • 如果你的服务器内存有限(比如 1GB),Prometheus 和 Grafana 加起来大概会占用 200-300MB,注意预留。

13. 13. 安全加固:API 密钥管理、速率限制与输入验证

API 密钥直接写死在代码里,就像把银行卡密码贴在显示器上——方便是方便,但迟早要出事。AgentGPT 跑起来之后,你至少得跟 OpenAI、Serper、Replicate 三家服务打交道,每个都有自己的密钥。如果这些密钥被偷了,轻则账单爆炸,重则被人拿去干坏事,你的 IP 都可能被拉黑。

这一章我们就干三件事:把密钥藏好、给 API 加上限速、把用户输入过滤干净。做完之后,你的 AgentGPT 实例才算真正能见人。

前置条件

  • 你已经成功部署了 AgentGPT(不管是本地开发环境还是生产环境)
  • 你手头有 OpenAI API Key、Serper API Key(可选)、Replicate API Token(可选)
  • 你熟悉 .env 文件的基本用法

第一步:把密钥从代码里赶出去

AgentGPT 项目根目录下有个 .env.example 文件,里面列出了所有需要的环境变量。我们的原则是:密钥只存在于环境变量和密钥管理服务里,绝不硬编码

先看看项目里是怎么加载密钥的。打开 platform/.env(如果没有就复制 .env.example 创建),你会看到类似这样的内容:

# 必须填
OPENAI_API_KEY=sk-your-key-here

# 可选,但推荐填
SERPER_API_KEY=your-serper-key
REPLICATE_API_TOKEN=your-replicate-token

# 数据库
DATABASE_URL=mysql://user:password@localhost:3306/agentgpt

重点来了:永远不要把 .env 文件提交到 Git。项目自带的 .gitignore 已经帮你把 .env 排除了,但你自己要养成习惯——每次 git status 的时候扫一眼,确认没有密钥文件混进去。

如果你用的是 Docker 部署,密钥可以通过 Docker Compose 的环境变量传进去。打开 docker-compose.yml,找到 backend 服务那块:

services:
  backend:
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - SERPER_API_KEY=${SERPER_API_KEY}

这样你只需要在宿主机上设好环境变量,容器启动时自动继承。比把密钥写在 Dockerfile 里安全一百倍。

常见翻车现场:有人把 .env 改成了 .env.local 然后忘了加进 .gitignore,结果一 push 密钥全网裸奔。解决方案:在项目根目录跑一遍 echo ".env.local" >> .gitignore,然后 git rm --cached .env.local 把它从 Git 追踪里移除。

第二步:给 API 加上限速器

密钥藏好了,但如果你不限制调用频率,别人可以拿你的 API 端点当免费代理用。AgentGPT 后端用的是 FastAPI,加限速最方便的方式是用 slowapi 这个库。

先安装:

cd platform
pip install slowapi

然后在 platform/main.py 里加上限速配置。找到文件开头,在 app 定义之后加入:

from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(429, _rate_limit_exceeded_handler)

接着给关键的 API 端点加上装饰器。比如 Agent 执行任务的端点,我们限制每分钟最多 10 次请求:

@app.post("/api/agent/execute")
@limiter.limit("10/minute")
async def execute_agent(request: Request, agent_data: AgentInput):
    # 原有的业务逻辑
    ...

如果你想让不同用户有不同的限额(比如付费用户 100 次/分钟,免费用户 10 次/分钟),可以自定义 key_func。举个简单的例子,从请求头里拿 API Key 来区分:

def get_api_key(request: Request):
    return request.headers.get("X-API-Key", "anonymous")

limiter = Limiter(key_func=get_api_key)

这样每个 API Key 独立计数,互不影响。

预期结果:在 1 分钟内连续发 11 次请求到 /api/agent/execute,第 11 次会收到 HTTP 429 响应,内容类似 {"detail": "Rate limit exceeded: 10 per 1 minute"}

常见报错:如果你遇到 ImportError: cannot import name 'Limiter' from 'slowapi',说明你装的版本不对。用 pip install slowapi==0.1.9 指定版本试试。

第三步:把用户输入过滤干净

AgentGPT 允许用户输入目标、任务描述这些自由文本。如果不做过滤,攻击者可以塞进去 SQL 注入语句或者 XSS 脚本。虽然 FastAPI 自带的 Pydantic 模型已经做了类型校验,但还不够——我们需要对字符串内容做进一步清洗。

项目里已经用了 Pydantic 做 schema 验证,打开 platform/schemas/agent.py,你会看到类似这样的模型:

from pydantic import BaseModel, Field
import re

class AgentInput(BaseModel):
    name: str = Field(..., min_length=1, max_length=50)
    goal: str = Field(..., min_length=1, max_length=500)
    
    @validator("name", "goal")
    def sanitize_input(cls, v):
        # 移除 HTML 标签
        v = re.sub(r'<[^>]*>', '', v)
        # 移除危险字符(根据你的业务需求调整)
        v = re.sub(r'[<>"\'%;()&]', '', v)
        # 限制长度
        if len(v) > 500:
            v = v[:500]
        return v.strip()

这个 @validator 会在每次创建 AgentInput 实例时自动运行,把脏东西挡在门外。

对于数据库查询,AgentGPT 用的是 Prisma ORM,它本身已经做了参数化查询,SQL 注入的风险很低。但如果你在代码里写了原生 SQL(比如某些复杂查询),一定要用参数绑定,不要拼字符串:

# ❌ 危险写法
cursor.execute(f"SELECT * FROM agents WHERE name = '{user_input}'")

# ✅ 安全写法
cursor.execute("SELECT * FROM agents WHERE name = %s", (user_input,))

预期结果:如果你在 Agent 名称里输入 <script>alert('xss')</script>,保存后这个标签会被自动移除,数据库里存的是干净的字符串。

实用技巧:不要只依赖后端过滤,前端也要做一层。在 Next.js 的前端代码里,用 zod(项目已经装了)对表单输入做同样的校验:

import { z } from 'zod';

const agentSchema = z.object({
  name: z.string().min(1).max(50).transform(val => val.replace(/<[^>]*>/g, '')),
  goal: z.string().min(1).max(500).transform(val => val.replace(/<[^>]*>/g, '')),
});

双层过滤,双重保险。

小例子串起来:给 Agent 注册加个安全门

假设你要做一个用户注册功能,让用户输入 API Key 来激活 Agent。我们可以把这三步安全措施都用上:

  1. 密钥管理:用户的 API Key 不存明文,用哈希加密后存数据库
  2. 限速:每个 IP 每小时只能注册 3 次,防止暴力破解
  3. 输入验证:用户名和 API Key 都做过滤,不允许特殊字符

代码大概长这样:

from passlib.hash import bcrypt
from slowapi import Limiter

limiter = Limiter(key_func=get_remote_address)

@app.post("/api/register")
@limiter.limit("3/hour")
async def register(request: Request, user: UserInput):
    # 输入已经在 UserInput 的 validator 里过滤过了
    hashed_key = bcrypt.hash(user.api_key)
    # 存到数据库
    db.execute("INSERT INTO users (name, api_key_hash) VALUES (%s, %s)", 
               (user.name, hashed_key))
    return {"status": "ok"}

这样就算数据库被拖库,攻击者拿到的也是哈希值,解不出原始 API Key。

别忘了日志审计

安全措施加完了,还得知道有没有人撞墙。在限速和输入验证的地方加日志,方便事后排查:

import logging
logger = logging.getLogger(__name__)

@app.post("/api/agent/execute")
@limiter.limit("10/minute")
async def execute_agent(request: Request, agent_data: AgentInput):
    logger.info(f"Agent execution from {request.client.host}: {agent_data.name}")
    # 业务逻辑

日志里不要记录完整的 API Key 或密码,只记 IP、时间、操作类型就够了。

做完这三步,你的 AgentGPT 实例至少能挡住 90% 的常见攻击。剩下的 10% 靠你保持依赖库更新——记得每个月跑一次 pip list --outdatednpm outdated,把有安全漏洞的包升上去。

14. 14. 完整项目实战:构建一个自动生成周报的 Agent 系统

把前面学的全串起来:一个自动写周报的 Agent

前面十三章,我们拆开了 AgentGPT 的每个零件:环境搭建、API 配置、工具注册、记忆管理、参数调优、多 Agent 协作、前端定制、后端扩展、性能优化、部署上线、监控告警、安全加固……现在该把它们拧成一台能真正干活的机器了。

这一章的目标很具体:构建一个能自动生成周报的 Agent 系统。你给它一个团队名称和本周的关键事件,它自己去查资料、整理要点、生成 Markdown 格式的周报,最后保存到数据库里。整个过程不需要你手动复制粘贴任何东西。

前置条件

  • 你已经完成了第 1-13 章的所有步骤,本地或服务器上跑着 AgentGPT
  • 数据库里有 reports 表(我们马上建)
  • OpenAI API Key 和 Serper API Key 都配置好了
  • 你大概知道 FastAPI 和 Next.js 的项目结构长什么样

第一步:设计周报的数据结构

周报需要存哪些字段?最少要有:团队名称、报告周期、内容(Markdown 文本)、创建时间。打开 platform/db/models.py,加一个模型:

from datetime import datetime
from sqlmodel import SQLModel, Field

class WeeklyReport(SQLModel, table=True):
    id: int = Field(default=None, primary_key=True)
    team_name: str
    week_start: str  # 格式 "2024-01-01"
    week_end: str    # 格式 "2024-01-07"
    content: str     # Markdown 正文
    created_at: datetime = Field(default_factory=datetime.utcnow)

然后跑迁移。如果你用的是 Prisma,在 prisma/schema.prisma 里加:

model WeeklyReport {
  id        Int      @id @default(autoincrement())
  teamName  String
  weekStart String
  weekEnd   String
  content   String
  createdAt DateTime @default(now())
}

接着 npx prisma migrate dev --name add_weekly_report。预期结果是数据库里多了一张 WeeklyReport 表。

第二步:写一个“周报生成器”工具

Agent 需要知道怎么生成周报。我们给它注册一个自定义工具,叫 generate_weekly_report。在 platform/tools/ 下新建 weekly_report_tool.py

from langchain.tools import BaseTool
from typing import Optional, Type
from pydantic import BaseModel, Field

class WeeklyReportInput(BaseModel):
    team_name: str = Field(description="团队名称")
    week_start: str = Field(description="周开始日期,格式 YYYY-MM-DD")
    week_end: str = Field(description="周结束日期,格式 YYYY-MM-DD")
    highlights: str = Field(description="本周关键事件,用逗号分隔")

class WeeklyReportTool(BaseTool):
    name = "generate_weekly_report"
    description = "根据团队名称、日期范围和关键事件生成 Markdown 格式的周报"
    args_schema: Type[BaseModel] = WeeklyReportInput

    def _run(self, team_name: str, week_start: str, week_end: str, highlights: str) -> str:
        # 这里只是模板,真正的生成逻辑由 Agent 调用 LLM 完成
        report = f"""# {team_name} 周报
**周期**: {week_start}{week_end}

## 本周完成
- {highlights.replace(',', '\n- ')}

## 下周计划
- (待补充)

## 风险与问题
- (待补充)
"""
        return report

    async def _arun(self, *args, **kwargs):
        raise NotImplementedError

注意:这个工具本身只返回一个模板,真正的“智能生成”要靠 Agent 调用 LLM 来填充内容。工具的作用是定义接口——告诉 Agent 它可以用什么参数、返回什么格式。

第三步:把工具注册到 Agent 系统

打开 platform/services/agent_service.py(或者你项目里注册工具的地方),把 WeeklyReportTool 加进去:

from tools.weekly_report_tool import WeeklyReportTool

# 在初始化 Agent 的工具列表里加上
tools = [
    # ... 已有的工具
    WeeklyReportTool(),
]

如果你用的是第 4 章那种动态注册方式,在配置界面里加一行就行。预期结果是 Agent 现在知道有 generate_weekly_report 这个工具可以调用。

第四步:创建保存周报的 API 端点

Agent 生成周报后,需要存到数据库。在 platform/routers/ 下新建 reports.py

from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session, select
from db.models import WeeklyReport
from db.database import get_session

router = APIRouter(prefix="/api/reports", tags=["reports"])

@router.post("/")
def create_report(report: WeeklyReport, session: Session = Depends(get_session)):
    session.add(report)
    session.commit()
    session.refresh(report)
    return report

@router.get("/")
def list_reports(session: Session = Depends(get_session)):
    reports = session.exec(select(WeeklyReport).order_by(WeeklyReport.created_at.desc())).all()
    return reports

@router.get("/{report_id}")
def get_report(report_id: int, session: Session = Depends(get_session)):
    report = session.get(WeeklyReport, report_id)
    if not report:
        raise HTTPException(status_code=404, detail="Report not found")
    return report

然后在 platform/main.py 里注册这个路由:

from routers import reports
app.include_router(reports.router)

重启后端,用 curl 测试一下:

curl -X POST http://localhost:8000/api/reports/ \
  -H "Content-Type: application/json" \
  -d '{"team_name":"研发部","week_start":"2024-01-01","week_end":"2024-01-07","content":"# 测试周报"}'

预期返回 200 和刚创建的记录。

第五步:写一个“周报助手”Agent 配置

现在让 Agent 自己干活。打开前端页面,创建一个新的 Agent,配置如下:

  • 名称: 周报助手
  • 目标: 生成研发部 2024-01-01 至 2024-01-07 的周报,关键事件包括:完成用户模块重构、修复三个线上 bug、开始性能优化调研
  • 工具: 勾选 generate_weekly_reportsearch(让它能查资料补充内容)
  • 最大迭代: 5(够了,周报不需要跑太多轮)
  • 温度: 0.3(低一点,保持输出稳定)

点击“部署 Agent”。你会看到它开始思考:

  1. 先调用 search 搜索“用户模块重构 最佳实践 2024”
  2. 再调用 generate_weekly_report 生成初稿
  3. 可能还会再调一次 search 补充下周计划

最终输出应该是一份像模像样的 Markdown 周报。

第六步:自动保存到数据库

Agent 生成的周报现在只显示在界面上,我们需要让它自动调用保存 API。有两种做法:

做法 A:在工具里直接调用 API

修改 WeeklyReportTool._run,让它生成内容后自动 POST 到 /api/reports/

import requests

def _run(self, team_name, week_start, week_end, highlights):
    # 先让 LLM 生成内容(这里简化了,实际要调 LLM)
    content = self._generate_content(team_name, week_start, week_end, highlights)
    # 保存到数据库
    response = requests.post("http://localhost:8000/api/reports/", json={
        "team_name": team_name,
        "week_start": week_start,
        "week_end": week_end,
        "content": content
    })
    return f"周报已保存,ID: {response.json()['id']}"

做法 B:在 Agent 完成回调里保存

如果你不想让工具直接依赖 HTTP 请求,可以在 Agent 执行完毕后,由后端自动保存。在 agent_service.py 里找到 Agent 完成后的回调:

async def on_agent_complete(agent_result):
    if "generate_weekly_report" in agent_result.tool_calls:
        # 解析结果,保存到数据库
        report_data = parse_report_from_result(agent_result.output)
        await save_report(report_data)

我推荐做法 A,更直观,而且工具本身就是一个完整的“生成+保存”单元。

第七步:前端展示历史周报

在 Next.js 里加一个页面 /reports,列出所有已生成的周报。新建 pages/reports.tsx

import { useEffect, useState } from 'react';

export default function ReportsPage() {
  const [reports, setReports] = useState([]);

  useEffect(() => {
    fetch('/api/reports/')
      .then(res => res.json())
      .then(setReports);
  }, []);

  return (
    <div className="p-4">
      <h1 className="text-2xl font-bold mb-4">历史周报</h1>
      {reports.map(report => (
        <div key={report.id} className="border p-3 mb-2 rounded">
          <h2>{report.teamName} - {report.weekStart} 至 {report.weekEnd}</h2>
          <pre className="whitespace-pre-wrap">{report.content}</pre>
        </div>
      ))}
    </div>
  );
}

别忘了在导航栏加个链接。预期结果是访问 /reports 能看到所有已保存的周报。

常见翻车点

Agent 不调用周报工具:检查工具描述是否清晰。把 description 改成更明确的句子,比如“当你需要生成或保存周报时,使用此工具。参数包括团队名称、开始日期、结束日期和关键事件。”

保存时报 500:检查数据库迁移是否成功。跑 npx prisma db pushalembic upgrade head 确保表结构最新。

周报内容太短:降低温度到 0.2-0.3,或者在目标里写得更具体,比如“每部分至少写 3 个要点”。

Agent 循环调用搜索:把最大迭代设小一点,比如 3。周报不需要反复查资料。

小技巧

  • 可以在 WeeklyReportTool 里加一个 format 参数,支持 markdownhtml 两种输出
  • 如果团队每周的关键事件差不多,可以写一个“周报模板”工具,让 Agent 先加载模板再填充
  • 把周报生成做成定时任务:用 Celery 或 APScheduler,每周五下午 5 点自动触发一个 Agent

现在你有了一个能自动写周报的系统。下次周五下午,你只需要打开 /reports 页面,复制粘贴到邮件里就行。如果老板问“这周干了啥”,你甚至可以让 Agent 再跑一遍,生成一份更详细的版本。

常見問題

问题 1:安装时 ./setup.sh./setup.bat 报错“Permission denied”或“无法识别”

解答

  • Linux/macOS:先给脚本执行权限:chmod +x setup.sh,再运行 ./setup.sh
  • Windows:确保以管理员身份打开终端(PowerShell 或 CMD),然后执行 ./setup.bat。如果提示“无法加载文件”,请先运行 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser 允许执行脚本。
  • 如果仍然失败,检查是否已安装 Docker 并确保 Docker Desktop 正在运行。

问题 2:启动后访问 http://localhost:3000 显示空白页或“连接失败”

解答

  • 确认所有服务都已启动:Docker 容器(docker ps)应包含 agentgpt-dbagentgpt-backendagentgpt-frontend 等。
  • 检查 .env 文件是否已正确配置:至少需要 OPENAI_API_KEY,可选 SERPER_API_KEYREPLICATE_API_TOKEN
  • 如果后端报错,查看 Docker 日志:docker logs agentgpt-backend。常见错误是 API Key 无效或数据库连接失败。
  • 前端默认端口是 3000,后端是 8000,确保没有端口冲突。

问题 3:AgentGPT 和 AutoGPT 有什么区别?为什么选择 AgentGPT?

解答

  • 部署方式:AgentGPT 是 Web 应用,直接在浏览器中配置和运行;AutoGPT 是命令行工具,需要本地 Python 环境。
  • 易用性:AgentGPT 提供图形界面,无需编写代码;AutoGPT 需要手动管理文件、API Key 和循环逻辑。
  • 目标:AgentGPT 强调“一键部署自主 AI 代理”,适合快速实验;AutoGPT 更灵活但配置复杂。
  • 技术栈:AgentGPT 使用 Next.js + FastAPI + LangChain;AutoGPT 基于 Python 和 LangChain。
  • 如果你希望快速在浏览器中体验自主 AI,选 AgentGPT;如果需要深度定制或离线运行,考虑 AutoGPT。

问题 4:为什么 AgentGPT 需要 Docker?能不能不用 Docker 直接运行?

解答

  • Docker 用于统一管理数据库(MySQL)、后端(FastAPI)和前端(Next.js)的依赖,避免手动安装多个环境。
  • 如果不想用 Docker,可以手动安装:
    1. 安装 Node.js、Python 3.9+、MySQL。
    2. 分别启动后端(cd platform && pip install -r requirements.txt && uvicorn main:app)和前端(cd next && npm install && npm run dev)。
    3. 配置 .env 中的数据库连接和 API Key。
  • 但官方推荐使用 Docker,因为 setup 脚本会自动处理所有步骤,减少环境问题。

问题 5:运行 AgentGPT 时提示“OpenAI API Key 无效”或“速率限制”

解答

  • 检查 .env 中的 OPENAI_API_KEY 是否正确(格式为 sk-...)。
  • 确保 OpenAI 账户有余额(免费额度已用完或未绑定支付方式)。
  • 如果遇到速率限制(RateLimitError),可以:
    • 降低任务并发数(在 Agent 配置中调整)。
    • 使用更便宜的模型(如 gpt-3.5-turbo 而非 gpt-4)。
    • 等待一段时间再试。
  • 注意:AgentGPT 默认使用 gpt-3.5-turbo,如需 gpt-4 需在代码中修改模型名称(但需要 API 权限)。

问题 6:Agent 执行任务时卡住或无限循环,怎么办?

解答

  • AgentGPT 的自主循环机制是:思考 → 执行 → 学习 → 重复。如果任务目标过于模糊(如“探索宇宙”),Agent 可能陷入无限循环。
  • 解决方法
    1. 在创建 Agent 时,将目标拆分为具体、可衡量的子任务(例如“搜索关于 X 的 5 篇文章并总结”)。
    2. 设置最大循环次数(在代码或配置中调整 max_iterations)。
    3. 如果卡住,可以手动停止 Agent(界面上的“Stop”按钮),然后重新配置。
  • 如果 Agent 一直调用相同 API 而不推进,可能是 LangChain 的 LLM 链设计问题,可尝试更新项目版本。

问题 7:如何添加自定义工具或 API(例如 Google 搜索、图像生成)?

解答

  • AgentGPT 默认集成了 Serper API(搜索)和 Replicate API(图像生成)。
  • 添加新工具
    1. 在后端代码(platform/ 目录)的 tools/ 文件夹中创建新的工具类,继承 LangChain 的 BaseTool
    2. agent.py 中注册该工具。
    3. 在前端界面中,添加对应的配置选项(如 API Key 输入框)。
  • 如果不想修改代码,可以:
    • 在 Agent 的目标中直接要求使用外部 API(例如“调用 https://api.example.com 获取数据”),但 Agent 需要具备 HTTP 请求能力(默认支持)。
  • 注意:自定义工具需要重新构建 Docker 镜像(docker-compose build)。

问题 8:AgentGPT 是否支持多语言?如何让 Agent 用中文输出?

解答

  • AgentGPT 的界面支持多语言(通过 i18n),但 Agent 的输出语言由 LLM 模型决定。
  • 让 Agent 用中文
    1. 在创建 Agent 时,在“目标”或“提示”中明确指定语言,例如:“请用中文回答所有问题”。
    2. 如果使用 GPT-4,中文支持更好;GPT-3.5 也能输出中文,但可能偶尔混用英文。
  • 注意:Agent 的思考过程(内部日志)可能仍是英文,但最终输出会遵循你的语言指令。
  • 如果希望界面完全中文,可以贡献翻译文件(next/public/locales/ 目录)。

🔗 相關推薦

📘 教程

MemOS 2.0星尘:8个高频用法,超强持久记忆MemOS是一款为LLM和AI代理提供超强持久记忆的内存操作系统。它提供了统一的内存API、多模态内存、多知识库管理等功能。通过本教程,您将学习如何安装和配置MemOS,如何使用MemOS实现持久记忆,如何优化和定制MemOS等。进阶10 章★ 10.1kagentagentic-aiaitaste-skill 使用教程taste-skill 是一个为 AI 代理提供前端设计品味的技能集,可生成高质量、非模板化的 UI 代码或设计参考图。本教程将指导你从安装到进阶使用,涵盖多种技能的选择与组合,帮助你告别 AI 生成的“千篇一律”界面。进阶9 章★ 49.6kagentaiclaudeheadroom 使用教程headroom 是一个上下文压缩层,可在工具输出、日志、文件等到达 LLM 前将其压缩 60-95%,同时保持答案质量。本教程将指导你完成安装、快速上手、核心功能详解及进阶用法,帮助你高效集成到 AI 代理工作流中。进阶10 章★ 48.4kagentaianthropicai-job-search:AI 驱动的职位搜索框架实战ai-job-search 是一个基于 Claude Code 的 AI 职位搜索框架,能够帮助您评估职位发布、定制简历、撰写求职信和准备面试。本教程将指导您如何使用 ai-job-search 实现这些功能,并提供实战经验。通过本教程,您将能够使用 ai-job-search 自动化职位搜索和申请流程,提高求职效率。进阶8 章★ 20.7kaiai-agentscareer

📦 相關專案