刚开始做 Agent 项目时我踩过不少坑模型返回了工具调用结果却因为上下文切换把状态弄丢了Agent 明明有工具却总是在关键步骤上“想当然”线上部署后一旦某个执行步骤超时整个任务就直接中断。正是这些经历让我意识到掌握 Agent 的基本概念并不难难的是把一个“能聊天的 Agent”做成一个“能稳定干活的专用智能体”。本文就以 Blitz Agent 这个专业化 Agent 项目为例完整拆解从概念、环境搭建、核心循环、实战代码到部署排查的全流程希望能给正在研究 AI Agent 开发的读者一条相对完整的参考路径。Blitz Agent 的定位是“Your specialized agent”也就是一个面向特定业务场景的专用智能体而不是通用聊天机器人。它强调的是“专业”围绕具体任务做减法把工具、提示词、记忆和流程控制都收敛到目标场景上。下面我们就从 Agent 的技术本质开始一步步搭建一个可运行的 Blitz Agent。1. Blitz Agent 到底是什么先理解 Agent 的核心价值1.1 从一个“专用智能体”的设定说起很多刚接触 Agent 的开发者容易陷入一个误区认为 Agent 就是一个“能调用工具的大模型”。实际上Agent 与普通的大模型 API 调用最大的区别在于自主决策。普通 LLM 调用是“你问一句它答一句”而 Agent 会在一个循环中不断观察当前状态、决定下一步动作、执行工具调用然后根据结果继续推理直到完成目标。Blitz Agent 的思路其实是“场景优先”。它不会试图做一个什么都会的通用助手而是围绕某个具体领域比如项目数据分析、自动化运维、代码审查等定制工具集和决策流程。这样做的好处非常明显工具范围收敛模型不容易选错工具。提示词可以写得更细输出稳定性更高。记忆和上下文结构更容易设计。权限和风险边界更清晰。1.2 Agent 与普通 API 调用的本质区别用一个最简单的对比来说明对比点普通 LLM 调用Agent 调用输入输出一次请求一次响应多轮规划-执行-观察循环工具使用需要外部代码预先编排Agent 自主决定是否调用工具状态管理无状态或由业务代码维护Agent 内部维护中间状态失败恢复直接返回错误可重试、可修正计划适用场景文本生成、问答、摘要需要多步骤操作的复杂任务这里特别要强调的是“自主调用工具”这一点。普通 API 调用中如果业务需要查询数据库再生成报告这段编排逻辑必须由开发者写死在业务代码中。而在 Agent 模式下模型会根据用户的自然语言目标自主判断“我是否需要查询数据库”然后调用对应的工具函数再基于工具返回结果继续生成下一步内容。1.3 Agent 开发的应用场景从近年的开源项目和企业实践来看AI Agent 的应用场景主要集中在以下几个方向自动化工作流例如自动整理邮件、生成周报、抓取网页数据并汇总。垂直领域专家助手例如法律文书审核、医疗报告解读、运维故障排查。代码开发助手例如根据需求自动生成代码、执行测试、修复问题。数据分析助手例如连接数据库、分析指标、生成图表。多智能体协作系统例如规划 Agent 拆解任务执行 Agent 完成具体操作审查 Agent 校验结果。Blitz Agent 这个项目名本身就体现了这类场景的核心诉求它要像闪电一样快速、准确地完成专项任务而不是在通用对话中模棱两可。2. 环境准备与项目初始化2.1 运行环境与依赖选择Agent 开发对运行环境的要求并不高一般使用 Python 3.9 即可因为大部分 Agent 框架和工具库都基于 Python 生态。如果你使用的是较新的 Python 3.11 或 3.12也完全兼容本文示例。其他依赖如下Python 3.9建议 3.10 及以上pip 包管理工具一个可用的 LLM API Key例如 OpenAI、通义千问、文心一言等本文以 OpenAI 风格接口为例可选LangChain、LangGraph、Pydantic 等库需要注意不同 Agent 框架的版本变化较快本文示例以相对通用的实现思路为主并不绑定某个框架的特定版本。实际开发时请以你安装的依赖版本为准。建议创建虚拟环境避免依赖冲突mkdir blitz-agent cd blitz-agent python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activate pip install --upgrade pip2.2 初始化 Blitz Agent 项目结构一个规范的 Agent 项目不能把代码全部堆在一个文件里。下面是我在 Blitz Agent 中使用的基础目录结构blitz-agent/ ├── venv/ # 虚拟环境 ├── src/ │ ├── __init__.py │ ├── tools/ # 工具层Agent 可调用的工具 │ │ ├── __init__.py │ │ ├── calculator.py # 示例计算器工具 │ │ └── todo.py # 示例待办事项工具 │ ├── agent/ │ │ ├── __init__.py │ │ ├── prompt.py # 提示词模板 │ │ ├── memory.py # 记忆管理 │ │ └── loop.py # Agent 核心循环 │ └── main.py # 程序入口 ├── config/ │ └── settings.yaml # 配置文件 ├── logs/ # 运行日志 ├── requirements.txt # 依赖清单 └── .env # 环境变量API Key这种结构的核心思路是分层工具层负责执行具体动作Agent 层负责决策和循环入口文件负责组装。后续新增工具时只需要在tools目录下加一个文件然后注册到工具列表即可。3. Agent 核心机制拆解Loop、Harness、记忆与协作很多开发者卡在“能跑通 Demo”和“能落地项目”之间根本原因是对 Agent 的几个核心机制理解不透。下面把这几个关键点逐个拆开讲。3.1 Agent Loop智能体运行的核心循环Agent Loop智能体循环是 Agent 运行的核心结构。一个标准的 Agent Loop 通常包含以下步骤接收用户目标。将目标、工具描述、历史上下文组装成提示词发送给 LLM。LLM 返回一个决策结果要么生成最终回答要么要求调用某个工具。如果有工具调用需求执行对应工具把结果追加到上下文。回到第 2 步重复执行。直到 LLM 生成最终回答或者达到最大迭代次数。用文字描述可能不够直观可以用下面的流程来理解用户输入 - LLM 推理 - 判断下一步 - 调用工具 - 观察结果 - LLM 继续推理 - ... - 输出最终结果这个循环的关键在于“多步决策”。每一步 LLM 都能看到上一步工具的执行结果所以它能不断修正自己的计划。这也是 Agent 比普通 LLM 调用更擅长完成复杂任务的原因。以下是一个简化版的 Agent Loop 伪代码def run_agent(user_goal, max_steps10): messages [{role: user, content: user_goal}] for step in range(max_steps): response llm_chat(messages, toolstool_schemas) if response.tool_calls: tool_result execute_tool(response.tool_calls) messages.append(response.message) messages.append({role: tool, content: tool_result}) continue return response.content raise Exception(Agent 达到最大执行步数任务未完成)3.2 Harness 与 Agent 的区别在 Agent 开发中Harness 和 Agent 是两个经常被混淆的概念。简单来说Agent是“大脑”负责理解目标、做出决策、调用工具。Harness是“运行容器”负责任务编排、生命周期管理、超时控制、错误捕获、日志采集等外围逻辑。更通俗地讲Harness 更像是一个 Agent 的执行环境。它决定了 Agent 在什么条件下启动、每一步允许执行多少时间、出现异常时是否重试、以及如何把工具执行结果回传给模型。在实际工程中Harness 的价值往往比 Agent 本身更大。原因是模型决策不可能 100% 准确Harness 的职责就是把这些不确定性兜住。例如Agent 调用了一个不存在的工具名Harness 要能捕获并告诉模型“工具不存在请换一个”。Agent 长时间未响应Harness 要能触发超时中断。Agent 在循环中反复调用同一个错误工具Harness 要能识别死循环并终止任务。3.3 Agent 记忆短期上下文与长期记忆记忆是决定 Agent 体验的关键一环。目前比较主流的记忆方案分为两层短期记忆存放在当前会话上下文中每次请求都会携带。通常我们把历史对话、工具调用结果塞进 messages 数组。缺点是上下文窗口有限太长会导致成本飙升。长期记忆存放在外部存储中例如向量数据库、Redis 或普通数据库。Agent 可以根据当前任务召回相关的历史经验、用户偏好或领域知识。短期记忆的实现一般比较直接就是在每次请求时拼接历史消息。长期记忆则需要考虑“什么时候写入”“什么时候召回”“如何防止旧记忆干扰当前任务”。最简单实用的方案是class SimpleMemory: def __init__(self, max_history20): self.max_history max_history self.history [] def add(self, message): self.history.append(message) if len(self.history) self.max_history: self.history.pop(0) def get_context(self): return self.history[-self.max_history:]对于更复杂的项目可以用向量数据库存储记忆片段在 Agent 开始推理前先做一次相似度检索把相关的历史经验注入提示词效果会比无脑拼接全部历史好很多。3.4 多 Agent 协作从单兵作战到团队协作当单个 Agent 的职责过于复杂时多 Agent 协作是常见的解法。比如一个“数据分析 Agent 团队”可以拆成规划 Agent负责拆解任务生成执行计划。查询 Agent负责连接数据库执行 SQL 查询。分析 Agent负责对查询结果进行统计和洞察。报告 Agent负责将分析结果整理成用户可读的文档。多 Agent 协作不等于“多个 Agent 同时跑”它更强调消息传递和任务交接。常见的协作模式有两种串行模式Agent A 完成后把结果交给 Agent B 继续处理。编排模式一个主 Agent 负责任务分配多个子 Agent 并行执行主 Agent 汇总结果。在工程实现时多 Agent 协作会增加大量状态管理和异常处理成本建议只有当单 Agent 确实处理不过来时才引入。4. Blitz Agent 实战从零实现一个专业化 Agent4.1 需求分析与功能拆分我们以一个“待办事项管理助手”作为 Blitz Agent 的业务场景。用户可以通过自然语言创建待办、查询待办、标记完成。这个场景很典型既涉及工具调用又需要理解用户意图还不需要引入外部数据库方便本地演示。需求拆解如下用户输入例如“帮我添加一个明天上午开会的待办”。Agent 理解意图识别为“创建待办事项”。Agent 调用工具执行create_todo(明天上午开会, due2025-01-10 09:00)。Agent 汇总结果向用户返回“好的已添加明天上午开会的待办”。同时还需要支持查询所有待办list_todos()标记完成complete_todo(todo_id)删除待办delete_todo(todo_id)4.2 工具层让 Agent 拥有行动能力在 Agent 开发中工具是模型之外的“外部能力”。工具函数本身不需要任何魔法它就是一个普通的 Python 函数关键是提供清晰的描述和参数说明让模型知道什么时候该调用。创建src/tools/todo.pyfrom datetime import datetime from typing import Optional todos [] _todo_id 0 def _next_id(): global _todo_id _todo_id 1 return _todo_id def create_todo(content: str, due: Optional[str] None) - str: 创建一个待办事项。 Args: content: 待办内容必填。 due: 截止时间可选例如 2025-01-10 09:00。 todo { id: _next_id(), content: content, due: due, done: False, created_at: datetime.now().strftime(%Y-%m-%d %H:%M:%S), } todos.append(todo) return f已创建待办id{todo[id]}, 内容{content} def list_todos() - str: 查看所有待办事项列表。 if not todos: return 当前没有任何待办事项。 lines [] for t in todos: status 已完成 if t[done] else 未完成 due t[due] if t[due] else 无截止时间 lines.append(fid{t[id]}, 内容{t[content]}, 截止{due}, 状态{status}) return \n.join(lines) def complete_todo(todo_id: int) - str: 根据待办 id 将待办标记为已完成。 for t in todos: if t[id] todo_id: t[done] True return f待办 {todo_id} 已标记为完成。 return f未找到 id{todo_id} 的待办事项。 def delete_todo(todo_id: int) - str: 根据待办 id 删除一个待办事项。 global todos for t in todos: if t[id] todo_id: todos [x for x in todos if x[id] ! todo_id] return f待办 {todo_id} 已删除。 return f未找到 id{todo_id} 的待办事项。这里使用全局变量存储数据仅用于演示。生产环境中应替换为数据库。工具函数返回值必须是字符串因为模型看到的是文本结果而不是 Python 对象。接下来把工具注册信息集中管理创建src/tools/__init__.pyfrom .todo import create_todo, list_todos, complete_todo, delete_todo TOOLS [ { name: create_todo, description: 创建一个新的待办事项, parameters: { type: object, properties: { content: {type: string, description: 待办内容}, due: {type: string, description: 截止时间可选}, }, required: [content], }, }, { name: list_todos, description: 查询所有待办事项, parameters: {type: object, properties: {}}, }, { name: complete_todo, description: 根据 id 将待办标记为完成, parameters: { type: object, properties: {todo_id: {type: integer}}, required: [todo_id], }, }, { name: delete_todo, description: 根据 id 删除待办事项, parameters: { type: object, properties: {todo_id: {type: integer}}, required: [todo_id], }, }, ] TOOL_MAP { create_todo: create_todo, list_todos: list_todos, complete_todo: complete_todo, delete_todo: delete_todo, }4.3 提示词与角色模板设计提示词在 Agent 中的作用常常被低估。一个边界清晰的系统提示词能显著减少 Agent 胡乱调用工具的概率。创建src/agent/prompt.pySYSTEM_PROMPT 你是一个专业的待办事项管理助手 Blitz Agent。 你的职责是帮助用户管理待办事项而不是闲聊。 使用规则 1. 当用户提出待办管理需求时必须调用对应工具。 2. 如果不确定使用哪个工具优先查询 list_todos。 3. 工具执行结果必须原样反馈给用户不要杜撰结果。 4. 如果用户问与待办管理无关的问题礼貌提示你的职责范围。 5. 每次调用工具后基于工具返回结果给用户一个简洁的答复。 注意提示词中尽量避免使用“我不允许”“我不能”这类否定表达而是明确告诉模型“你可以做什么、应该怎么做”。4.4 核心调度循环实现现在实现核心调度循环。为了通用性我直接使用 OpenAI 风格的 API 接口不引入额外框架。你需要先安装openai库pip install openai python-dotenv创建.env文件OPENAI_API_KEY你的_API_Key OPENAI_BASE_URLhttps://api.openai.com/v1 OPENAI_MODELgpt-4o-mini创建src/agent/loop.pyimport json import os from openai import OpenAI from dotenv import load_dotenv from src.tools import TOOLS, TOOL_MAP from src.agent.prompt import SYSTEM_PROMPT load_dotenv() client OpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL, https://api.openai.com/v1), ) MODEL os.getenv(OPENAI_MODEL, gpt-4o-mini) MAX_STEPS 8 def execute_tool_call(tool_call) - str: 执行单个工具调用返回字符串结果。 func_name tool_call.function.name args json.loads(tool_call.function.arguments or {}) if func_name not in TOOL_MAP: return f错误未知工具 {func_name} func TOOL_MAP[func_name] try: result func(**args) return json.dumps({result: result}, ensure_asciiFalse) except Exception as e: return f工具执行异常{str(e)} def run_agent(user_input: str) - str: messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_input}, ] for step in range(MAX_STEPS): response client.chat.completions.create( modelMODEL, messagesmessages, toolsTOOLS, ) message response.choices[0].message if not message.tool_calls: return message.content or 无输出 messages.append(message) for tool_call in message.tool_calls: tool_result execute_tool_call(tool_call) messages.append( { role: tool, tool_call_id: tool_call.id, content: tool_result, } ) return 已达到最大执行步数任务未能完成建议调整指令或工具。这个循环有几个值得注意的细节每次拿到模型返回后先判断有没有tool_calls。如果没有说明模型已经生成了最终答复。执行工具后工具结果以roletool追加并且必须带上tool_call_id这样模型才能把工具结果和之前的调用请求对应起来。使用MAX_STEPS限制最大步数防止模型陷入死循环。4.5 使用 LangChain 风格快速搭建如果你更希望站在框架肩膀上快速开发LangChain 系列是目前社区使用较广的方案。以 LangChain 的create_tool_calling_agent为例大致思路如下from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain_core.prompts import ChatPromptTemplate tool def create_todo(content: str, due: str ) - str: 创建一个新的待办事项。 # 这里可以接入你的本地函数或数据库 return f已创建待办{content} tools [create_todo] prompt ChatPromptTemplate.from_messages( [ (system, 你是一个专业的待办事项管理助手。), (human, {input}), (placeholder, {agent_scratchpad}), ] ) llm ChatOpenAI(modelgpt-4o-mini, temperature0) agent create_tool_calling_agent(llm, tools, prompt) agent_executor AgentExecutor(agentagent, toolstools, verboseTrue) result agent_executor.invoke({input: 帮我添加一个明天上午开会的待办}) print(result[output])框架的好处是封装了很多细节坏处是版本演进快。我在实际项目中更建议先用框架跑通 Demo再逐步理解框架内部实现最后按需自研核心循环。4.6 运行与验证编写入口文件src/main.pyfrom src.agent.loop import run_agent if __name__ __main__: print(Blitz Agent 已启动输入你的待办管理需求输入 exit 退出) while True: user_input input( ).strip() if user_input.lower() in (exit, quit): break if not user_input: continue output run_agent(user_input) print(Agent:, output) print(- * 50)运行python -m src.main预期交互效果类似Blitz Agent 已启动输入你的待办管理需求输入 exit 退出 帮我添加一个明天上午开会的待办 Agent: 好的已为你创建待办明天上午开会id1。 列出所有待办 Agent: 当前待办事项 id1, 内容明天上午开会, 截止无, 状态未完成5. 常见问题与排查思路5.1 高频报错现象表问题现象常见原因解决思路Agent 执行超时类似 “provider did not respond in time”模型服务响应过慢、网络不稳定、单步等待时间过短增加超时重试次数检查网络和 API 服务状态Agent 循环中断提示 “Agent terminated due to error”工具调用抛异常、输出格式解析失败、上下文长度超限检查工具函数内部异常为工具调用增加 try-except模型始终不调用工具提示词边界不清晰、工具描述不明确、模型版本不支持 function calling优化工具 description必要时在提示词中强制先调用工具工具执行成功但模型答非所问工具结果没有正确回传给模型或消息格式错误检查tool_call_id是否匹配工具结果是否为字符串反复调用同一个错误工具工具返回的错误信息不够明确在工具错误信息中明确告诉模型应该换哪个工具5.2 执行超时与 Provider 未响应类似 “the agent execution provider did not respond in time” 的报错通常是 Agent 执行框架在等待模型返回时超过了阈值。可能原因包括模型服务端负载高响应变慢。提问的上下文过长导致推理耗时增加。网络链路不稳定请求被中断。框架的超时配置设置得太短。排查顺序可以是先用一个最简单的请求直接调用模型 API确认模型服务本身是否正常。查看 Agent 日志确认超时发生在哪一步是模型推理、工具执行还是结果回传。调大超时时间或增加重试次数。检查上下文长度考虑裁剪历史消息。5.3 Agent 循环中断与重试策略另一种高频报错是 “Agent terminated due to error. You can prompt the model to try again or start...”。这类提示说明 Agent 在执行过程中遇到了不可恢复的错误框架终止了循环。根因一般有三个方向工具内部逻辑异常例如数据库连接失败、除零错误、文件不存在。这些异常应该被 Agent 框架捕获并在工具返回内容中说明。模型输出解析失败有些模型返回的 tool_calls 格式不规范导致解析器判断失败。触发安全或预算限制部分框架会在执行成本超过阈值时主动终止。我的建议是工具函数内部不要直接抛出异常而是把异常信息转换为字符串返回让模型有机会根据错误信息修正下一步计划。def safe_execute(func, **kwargs): try: return str(func(**kwargs)) except Exception as e: return f工具执行失败{e}请检查参数后重试5.4 工具调用失败与格式解析问题使用工具调用的常见坑点是参数格式。模型返回的arguments是一个 JSON 字符串如果模型生成的 JSON 不合法例如多余的逗号、单引号json.loads就会失败。这时候可以加一个容错处理def parse_arguments(raw_args: str): try: return json.loads(raw_args) except json.JSONDecodeError: # 尝试去掉首尾空白后再次解析 cleaned raw_args.strip() return json.loads(cleaned)更稳妥的做法是使用 Pydantic 等库对参数做校验在工具层统一收口参数错误。6. Agent 工程化最佳实践6.1 安全边界提示词注入与权限控制Agent 的安全问题比普通应用更值得重视因为模型会执行工具调用而工具调用往往涉及真实系统资源。常见的安全风险包括提示词注入用户通过输入内容诱导模型执行非预期操作。工具权限过大Agent 拥有删除数据库、执行系统命令等高危权限。数据泄露模型可能将内部系统提示词或工具结果泄露给用户。对应实践建议工具层采用最小权限原则单个工具只暴露必要能力。对用户的输入做敏感词过滤和长度限制。高危工具删除、更新、执行命令必须增加二次确认机制。日志中不打印 API Key、用户敏感数据。6.2 可观测性日志、轨迹与调试Agent 调试比普通代码调试更难因为每一步都是由模型决策驱动的。因此可观测性是 Agent 工程化的重要环节。至少应该记录以下信息用户输入内容。每一步的模型返回包括 tool_calls 原始信息。工具执行参数和结果。消息列表长度、累计 token 消耗、耗时。推荐使用 JSON Lines 格式记录 Agent 运行轨迹方便回放和分析logs/ ├── agent_run_20250110_120000.jsonl ├── agent_run_20250110_123000.jsonl └── agent_run_20250110_130000.jsonl每条日志大致如下{step: 0, type: llm_response, content: ..., tool_calls: [...]} {step: 1, type: tool_call, tool: create_todo, args: {...}, result: ...}6.3 部署与性能优化Agent 服务部署时建议关注以下几点并发控制为每个用户会话分配独立上下文避免串号。限流与预算设置单次任务最大步数和 token 上限防止成本失控。缓存常用结果对于重复性查询类工具可以设置短暂缓存减少模型调用次数。异步化长耗时任务使用消息队列异步执行前端轮询进度。一个简单的 Dockerfile 示例FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [python, -m, src.main]需要注意的是容器内不推荐写入.env文件而是使用 Docker Secret 或环境变量注入配置。7. 总结与后续学习路线本文围绕 Blitz Agent 这个专业化 Agent 项目从概念到代码、从工具层到调度循环、从问题排查到工程化实践完整梳理了 Agent 开发的基本路径。你可以在本地运行这套待办事项管理 Agent体验从自然语言到工具调用的完整链路。如果你希望继续深入建议按下面的顺序进阶把全局变量存储替换为真正的数据库理解持久化。为 Agent 增加长期记忆使用向量数据库做记忆召回。引入 LangGraph 等编排框架体验显式状态图。研究 MCPModel Context Protocol等标准协议了解 Agent 与外部工具之间的标准化通信。关注 Agent 安全评测自学提示词注入攻击与防御方法。最后提醒一句Agent 不是模型能力的简单包装它是对目标任务的系统性工程拆解。把 Blitz Agent 的小例子跑通只是开始真正值得投入精力的是打磨工具边界、提升循环稳定性、健全可观测体系。如果本文对你有帮助可以收藏备用后续实战中遇到具体问题也欢迎在评论区交流。