1. 从零理解 AI Agent它到底是个什么东西1.1 一句话说清 Agent 和 LLM 的关系很多人第一次接触 AI Agent脑子里蹦出来的第一个问题就是它和 LLM 到底什么关系我打个比方你就明白了。LLM 像是一个知识渊博但手脚被绑住的专家你问它什么它都能答但它只能“说”不能“做”。你问它“今天天气怎么样”它能根据训练数据给你编一段但它没法真的去查实时天气。而 AI Agent 就是给这个专家松了绑给它配了手Tools、配了眼睛Context、配了记忆Memory让它能真正去执行任务。所以从架构上看一个最简化的 AI Agent 公式是Agent LLM Tools Context Harness这四个东西缺一不可。LLM 是大脑负责推理和决策Tools 是手脚负责和外部世界交互Context 是工作记忆负责把当前任务相关的信息喂给大脑Harness 是骨架和神经系统负责把上面这些部件串起来、跑起来、管起来。我见过太多人一上来就去研究什么多 Agent 协作、什么 Agent 编排框架结果连一个单 Agent 的循环都没跑通。这就像还没学会走路就想跑马拉松。所以这篇入门手册我会从最基础的概念开始一步步带你搭出一个能跑起来的 Agent然后再聊进阶的东西。1.2 为什么现在大家都在聊 Agent说白了因为 LLM 本身的能力已经强到可以支撑“自主决策”这件事了。早期的语言模型只能做文本补全你给它上半句它接下半句没什么自主性。但现在的大模型具备了几个关键能力第一它能理解复杂的自然语言指令第二它能做多步推理也就是所谓的 Chain of Thought第三它能按照指定格式输出结构化数据比如 JSON这就让程序能解析它的输出并执行相应操作。这三个能力凑在一起Agent 的雏形就出来了。你告诉它“帮我查一下明天北京的天气如果下雨就提醒我带伞”它能自己拆解成第一步调用天气查询工具第二步判断结果第三步根据判断决定是否调用提醒工具。整个过程不需要你写 if-else它自己就推理出来了。这就是为什么 2024 年到 2025 年AI Agent 从一个学术概念变成了工程落地的热门方向。从自动发小红书消息到辅助期货交易分析从代码生成到客服自动化Agent 的应用场景在快速扩展。但我要泼一盆冷水大部分场景下Agent 的可靠性还远远达不到“放手不管”的程度。你需要理解它的原理才能知道哪里会出问题怎么去兜底。1.3 这篇手册适合谁看如果你是一个开发者想从零搭一个 Agent 但不知道从哪下手这篇手册就是为你写的。如果你是一个产品经理想理解 Agent 的能力边界在哪这篇也能帮你建立基本认知。如果你只是好奇“AI Agent 入门”到底要学什么那我会尽量用大白话把技术概念讲清楚。我默认你具备基本的编程概念比如知道什么是 API、什么是 JSON但不需要你精通机器学习。实际上搭一个能用的 Agent对 ML 知识的要求远低于对工程能力的要求。你更多是在做系统集成、状态管理、错误处理这些事情而不是在训练模型。2. 核心组件拆解LLM、Tools、Context、Harness 各自干什么活2.1 LLM 选型不是越贵越好而是越合适越好选 LLM 是搭 Agent 的第一步也是最容易纠结的一步。市面上的模型太多了从开源的 Llama 系列、Qwen 系列到闭源的 GPT 系列、Claude 系列还有各种垂直领域微调过的模型。我的建议是先看你的任务需要多强的推理能力再看你的预算和延迟要求。如果你的 Agent 只是做简单的信息提取和格式化输出一个小参数量的模型就够了比如 7B 到 13B 级别的开源模型跑在本地或者便宜的 API 上成本低延迟也低。但如果你的 Agent 需要做复杂的多步推理比如“根据用户描述的症状推荐可能的科室并预约挂号”那就需要更强的模型因为推理链一长小模型很容易在中间某一步跑偏。还有一个容易被忽略的点是模型的上下文窗口大小。这个参数直接决定了你的 Agent 一次能“看到”多少信息。早期的模型只有 4K token 的窗口现在主流模型动辄 128K 甚至 1M token。但窗口大不代表你就应该往里塞满因为上下文越长推理成本越高而且模型在超长上下文中的注意力分配也会变差。我实测下来大部分 Agent 任务把上下文控制在 8K 到 32K 之间是比较舒服的区间。关于 token 的理解有个很形象的类比token 就像是模型世界里的“货币”。你发给模型的每一段文字都要换算成 token 来计费模型生成的每一个字也是按 token 算钱。中文大致是 1 个汉字对应 1 到 2 个 token英文大致是 1 个单词对应 1 到 1.5 个 token。所以你在设计 Agent 的提示词时每多写一段废话都是在烧钱。2.2 Tools 设计Agent 的手脚怎么长出来Tools 是 Agent 和外部世界交互的接口。没有 Tools 的 Agent 就是一个聊天机器人有了 Tools 它才能查数据、发请求、写文件、控制设备。从技术实现上看一个 Tool 本质上就是一个函数你告诉 LLM 这个函数叫什么名字、接受什么参数、做什么事情LLM 在需要的时候就会输出一个调用请求你的程序解析这个请求执行对应的函数再把结果返回给 LLM。听起来很简单对吧但实际做的时候Tools 的设计是最容易出问题的环节。我踩过的坑包括工具描述写得太模糊LLM 不知道该什么时候调用参数定义不清晰LLM 传了错误的格式工具返回的结果太长把上下文撑爆了工具执行失败没有好的错误处理Agent 直接卡死。一个好的 Tool 设计应该遵循几个原则。第一命名要语义化。不要叫func1、tool_a要叫get_weather、search_flights让 LLM 一看名字就知道是干什么的。第二描述要精确。用自然语言写清楚这个工具做什么、什么时候用、输入输出是什么格式。这段描述会直接进入 LLM 的上下文所以它其实就是提示词的一部分。第三参数要少而精。能用一个参数搞定的事情不要拆成三个参数越多 LLM 出错的概率越大。第四返回值要精简。如果工具返回的是一个巨大的 JSON你最好在返回给 LLM 之前做一层过滤和摘要只保留关键字段。2.3 Context 管理Agent 的工作记忆怎么组织Context 是 Agent 最容易被低估的部分。很多人把 Context 简单理解为“对话历史”但实际上它包含的东西远不止这些。一个完整的 Agent Context 通常包括系统提示词定义 Agent 的角色和能力边界、对话历史用户和 Agent 的交互记录、工具调用记录调用了哪些工具、返回了什么结果、外部知识从知识库检索到的相关内容、当前任务状态做到哪一步了、下一步该做什么。Context 管理的核心挑战是窗口有限但信息无限。你不可能把所有东西都塞进去所以必须做取舍。常见的策略包括滑动窗口只保留最近 N 轮对话、摘要压缩把早期的对话用 LLM 总结成一段话、关键信息提取只保留和当前任务相关的信息、分层存储热数据放上下文冷数据放外部存储按需检索。我遇到过一个典型问题Agent 在长对话中突然“失忆”忘记了之前用户说过的关键信息。排查后发现是因为对话历史太长早期的消息被滑动窗口挤掉了。解决方案是在系统提示词里维护一个“关键信息区”把用户的核心需求、已确认的事实、当前进度这些信息结构化地记录下来每轮都带上不参与滑动窗口的淘汰。这样即使对话历史被截断Agent 也不会丢失关键上下文。还有一个坑是上下文污染。如果工具返回的结果里包含大量无关信息或者之前的对话中有错误的信息没有被纠正这些“噪音”会干扰 LLM 的判断。我一般的做法是在每轮对话开始前对上下文做一次清理把已经确认无效的信息标记出来或者直接移除。2.4 Harness 工程把零件组装成能跑的机器Harness 这个词在中文里没有特别好的对应翻译我倾向于叫它“运行框架”或者“执行骨架”。它的职责是接收用户输入组装 Context调用 LLM解析 LLM 的输出如果输出中包含工具调用就执行工具把工具结果放回 Context再次调用 LLM如此循环直到 LLM 输出最终答案或者达到最大循环次数。这个循环就是 Agent 的核心执行逻辑通常被称为Agent Loop或者ReAct LoopReasoning Acting。一个最基本的 Agent Loop 伪代码大概长这样def agent_loop(user_input, max_iterations10): context build_initial_context(user_input) for i in range(max_iterations): response llm.generate(context) if response.is_final_answer(): return response.content if response.has_tool_call(): tool_result execute_tool(response.tool_call) context.append(tool_result) return 达到最大迭代次数任务未完成看起来简单但工程上的细节非常多。比如怎么判断 LLM 的输出是最终答案还是工具调用通常是通过输出格式来区分比如要求 LLM 用特定的 JSON 结构来输出工具调用。怎么处理工具执行超时怎么处理 LLM 返回了不存在的工具名怎么防止 Agent 陷入死循环这些都是在 Harness 层面要解决的问题。Harness 和 Agent 的区别经常有人搞混。简单说Agent 是一个概念指的是“能自主决策和执行任务的智能体”Harness 是把这个概念落地的那套代码框架。你可以把 Harness 理解为 Agent 的“操作系统”它管理着 Agent 的运行生命周期。市面上有一些开源的 Harness 实现比如 DeepSeek Harness 相关的插件和工具就是帮你省去从零写循环的麻烦。但我的建议是入门阶段最好自己手写一遍 Agent Loop这样你才能真正理解每个环节在干什么。3. 手把手搭建你的第一个 AI Agent3.1 环境准备与技术栈选择在动手之前先把环境搭好。我推荐的技术栈是 Python OpenAI 兼容的 API 接口。为什么选 Python因为生态最成熟各种 LLM 客户端库、工具库都是 Python 优先。为什么用 OpenAI 兼容接口因为现在大部分模型服务商都提供了兼容 OpenAI 格式的 API你换模型的时候只需要改 base_url 和 api_key代码基本不用动。具体需要安装的东西pip install openai httpx pydanticopenai是官方客户端库httpx用于发 HTTP 请求比如调用外部工具pydantic用于定义工具的参数结构做数据校验。如果你要用本地的开源模型可以装transformers或者llama-cpp-python但入门阶段我建议先用 API省去环境配置的麻烦。关于 API Key 的管理千万不要硬编码在代码里。用环境变量或者.env文件来管理export LLM_API_KEYyour-key-here export LLM_BASE_URLhttps://api.example.com/v1然后在代码里通过os.environ读取。这是基本的安全习惯我见过太多人把 key 提交到公开仓库然后被刷爆的案例。3.2 定义你的第一个 Tool我们来定义一个最简单的工具查询当前时间。虽然简单但它包含了 Tool 设计的完整要素。from datetime import datetime from pydantic import BaseModel, Field class GetCurrentTimeParams(BaseModel): timezone: str Field(defaultAsia/Shanghai, description时区例如 Asia/Shanghai) def get_current_time(params: GetCurrentTimeParams) - str: 获取当前时间 now datetime.now() return now.strftime(%Y-%m-%d %H:%M:%S) # 工具的元信息用于告诉 LLM 这个工具的存在 TOOL_DEFINITION { type: function, function: { name: get_current_time, description: 获取当前日期和时间。当用户询问现在几点、今天几号等问题时使用。, parameters: { type: object, properties: { timezone: { type: string, description: 时区例如 Asia/Shanghai } }, required: [] } } }注意几个细节。description字段是写给 LLM 看的要写清楚这个工具做什么、什么时候用。参数定义用 JSON Schema 格式required列表里放必填参数。工具函数的返回值最好是字符串如果是复杂结构先序列化成 JSON 字符串再返回。3.3 组装 Agent Loop现在把 LLM、Tool、Context 串起来。下面是一个完整的、可运行的最小 Agent 实现import json import os from openai import OpenAI client OpenAI( api_keyos.environ[LLM_API_KEY], base_urlos.environ[LLM_BASE_URL] ) SYSTEM_PROMPT 你是一个有用的助手可以使用工具来帮助用户解决问题。 当需要获取实时信息时调用相应的工具。 每次只调用一个工具等待结果后再决定下一步。 def run_agent(user_input: str, max_iterations: int 10): messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_input} ] for i in range(max_iterations): response client.chat.completions.create( modelyour-model-name, messagesmessages, tools[TOOL_DEFINITION], tool_choiceauto ) message response.choices[0].message # 如果没有工具调用说明是最终答案 if not message.tool_calls: return message.content # 把 LLM 的回复加入上下文 messages.append(message) # 执行每个工具调用 for tool_call in message.tool_calls: tool_name tool_call.function.name tool_args json.loads(tool_call.function.arguments) if tool_name get_current_time: result get_current_time(GetCurrentTimeParams(**tool_args)) else: result f错误未知工具 {tool_name} messages.append({ role: tool, tool_call_id: tool_call.id, content: result }) return 任务未在限定步数内完成这段代码就是 Agent 的心脏。它的执行流程是把用户输入和系统提示词组装成消息列表发给 LLMLLM 返回一个响应如果响应里有工具调用就执行工具把结果追加到消息列表再次发给 LLM如果响应里没有工具调用说明 LLM 认为可以直接回答了返回内容。3.4 跑通第一个任务并观察行为用上面这个 Agent 跑一个测试result run_agent(现在几点了) print(result)你会看到 Agent 先调用get_current_time工具拿到时间后再生成一句自然语言回复比如“现在是 2025 年 1 月 15 日下午 3 点 30 分。”这个过程看起来简单但你可以通过打印中间步骤来观察 Agent 的“思考过程”。我习惯在每次 LLM 调用后打印message.content和message.tool_calls这样能清楚地看到 Agent 在每一步做了什么决策。这个调试习惯在后续排查问题时非常有用。4. 进阶话题并发、错误处理与性能优化4.1 Agent 怎么扛并发“AI Agent 怎么扛并发”是最近被问得很多的问题。先说结论Agent 的并发瓶颈通常不在 LLM 调用本身而在工具执行和状态管理。LLM 调用本身是 IO 密集型的你发一个 HTTP 请求等响应这段时间 CPU 是空闲的。所以用异步 IO 就能很好地支持并发。Python 里用asynciohttpx.AsyncClient就能把 LLM 调用并发起来。但问题在于Agent 是有状态的每个用户的对话历史、任务进度都是独立的你不能让两个请求共享同一个 Context。我的做法是每个会话分配一个独立的 Context 对象用会话 ID 做隔离。工具执行如果是 IO 密集型的比如查数据库、调外部 API也用异步如果是 CPU 密集型的比如图像处理就丢到线程池或者进程池里跑。状态存储用 Redis 或者数据库不要放在内存里否则服务重启就全丢了。还有一个容易被忽略的点是限流和重试。LLM API 通常有速率限制你并发太高会被限流。所以要在客户端做令牌桶限流并且对 429 错误做指数退避重试。我一般会设置最大并发数比如 10 到 20根据 API 的配额来调整。4.2 常见错误与排查技巧Agent 开发过程中会遇到各种报错我整理了一个速查表错误信息可能原因解决方法context length exceeded上下文太长超过模型窗口压缩历史、减少工具返回数据量、换更大窗口的模型tool call format errorLLM 输出的工具调用格式不对检查工具定义是否符合 JSON Schema在提示词中强调格式要求tool not foundLLM 调用了不存在的工具名检查工具注册列表在提示词中列出可用工具infinite loopAgent 反复调用同一个工具设置最大迭代次数在提示词中要求每次调用不同工具timeout工具执行超时或 LLM 响应超时设置超时时间对超时做降级处理其中最常见的是上下文超长的问题。报错信息通常是this models maximum context length is X tokens。遇到这个首先要做的是统计当前上下文有多少 token。可以用tiktoken库来算或者直接用 API 返回的 usage 字段。然后决定是压缩历史还是精简工具返回值。4.3 提示词工程的几个实用技巧Agent 的提示词和普通聊天机器人的提示词不太一样它需要更结构化的指令。我总结了几条经验第一明确角色和能力边界。在系统提示词里写清楚“你是一个 XX 助手你可以使用以下工具...你不能做以下事情...”。边界越清晰Agent 越不容易跑偏。第二给出输出格式示例。如果你要求 Agent 输出特定格式最好给一个例子。比如“当需要调用工具时输出 JSON 格式{tool: 工具名, args: {...}}”。有了示例LLM 的格式正确率会大幅提升。第三分步引导。对于复杂任务在提示词里把步骤拆开写。“第一步理解用户需求第二步判断是否需要调用工具第三步如果需要选择合适的工具第四步根据工具结果生成回复。”这种分步引导能显著提高 Agent 的推理质量。第四加入兜底指令。比如“如果你不确定该调用哪个工具先向用户确认”或者“如果工具执行失败告知用户并建议替代方案”。这些兜底指令能避免 Agent 在异常情况下胡言乱语。4.4 从单 Agent 到多 Agent 的演进路径当你把单 Agent 跑通之后自然会想能不能让多个 Agent 协作比如一个负责规划一个负责执行一个负责审核。这个方向是对的但我要提醒你多 Agent 的复杂度不是线性增长的是指数增长的。多 Agent 带来的新问题包括Agent 之间怎么通信任务怎么分配出现冲突怎么仲裁状态怎么同步这些问题的解决成本很高。我的建议是先把单 Agent 做到足够可靠确实遇到单 Agent 搞不定的场景比如需要不同专业领域的知识再考虑多 Agent。一个比较务实的演进路径是单 Agent 多工具 → 单 Agent 工具分组 → 多 Agent 协作。每一步都确保当前方案能稳定运行再往下一步走。不要为了用多 Agent 而用多 Agent那是本末倒置。5. 我踩过的坑和给你的建议5.1 不要过早追求“全自动”我刚开始做 Agent 的时候总想着让它全自动完成所有事情用户只需要说一句话Agent 就从头到尾搞定。结果发现在复杂任务上全自动的失败率非常高。后来我调整了策略在关键节点加入人工确认。比如 Agent 规划好了步骤先展示给用户确认用户点了“同意”再执行。这样虽然多了一步交互但成功率大幅提升用户体验反而更好。5.2 日志和可观测性比你想的重要Agent 的行为是不确定的同一个输入可能产生不同的执行路径。如果没有详细的日志出了问题你根本不知道是哪一步错了。我建议从第一天就加上结构化日志记录每次 LLM 调用的输入输出、每次工具调用的参数和结果、每轮循环的耗时。这些日志在调试和优化时是无价之宝。5.3 测试用例要覆盖异常路径普通软件的测试主要覆盖正常路径但 Agent 的测试必须重点覆盖异常路径。比如工具返回空结果怎么办LLM 返回了格式错误的 JSON 怎么办用户输入了完全无关的问题怎么办这些异常路径才是 Agent 最容易崩溃的地方。我一般会准备一组“刁钻”的测试用例每次修改提示词或工具定义后都跑一遍。5.4 成本控制要提前做LLM 调用是按 token 计费的Agent 因为有多轮循环token 消耗量比普通聊天大得多。如果不做控制月底账单可能会吓你一跳。我的做法是设置单次任务的 token 上限超过就终止对工具返回结果做截断只保留关键信息缓存重复的 LLM 调用结果在开发阶段用便宜的小模型上线前再用大模型做最终验证。5.5 保持学习但不要追新追到迷失AI Agent 这个领域变化非常快隔几天就有新框架、新论文、新工具出来。保持学习是必要的但不要每个新东西都去追。我的策略是底层原理要扎实上层工具选一个用熟。Agent Loop 的原理、Context 管理的策略、Tool 设计的模式这些底层的东西变化很慢值得深入。而上层的框架和库选一个社区活跃、文档齐全的用起来就行不用每个都试。最后分享一个我个人的习惯每做一个新 Agent我都会先用纸笔画出它的执行流程图标出每个决策点和可能的异常分支。这个习惯帮我避免了很多设计上的漏洞。Agent 开发本质上是在设计一个决策系统想清楚再动手比边写边改效率高得多。