Agent 到底是什么?在 AI 应用脚手架里应该怎么设计

发布时间:2026/7/11 23:11:44
Agent 到底是什么?在 AI 应用脚手架里应该怎么设计 上一篇文章讨论了 Workflow。Workflow 解决的是复杂任务如何按步骤组织的问题。当任务流程比较明确时可以把输入处理、Prompt 渲染、模型调用、结果校验、工具调用和最终输出拆成多个节点。开发者提前设计好执行顺序。系统按这个顺序一步步执行。但有些任务不是固定流程。比如用户问帮我分析这个项目为什么启动失败。系统可能需要先读错误日志。再判断是依赖问题、配置问题还是代码问题。如果是依赖问题就检查包版本。如果是配置问题就检查环境变量。如果信息不够还需要继续追问。这种任务的下一步并不总是固定的。它需要系统根据当前目标、上下文和工具结果决定下一步。这时候就会进入 Agent 的讨论范围。一、Agent 不是什么都能自动完成的魔法Agent 是一个很容易被神化的词。很多文章会把 Agent 描述成一个可以自主思考、自主规划、自主执行任务的系统。这种说法有一定道理但如果放到工程项目里就需要更谨慎。在真实 AI 应用中Agent 不能被理解成没有边界的自动执行器。它不应该想做什么就做什么。它也不应该随便访问系统资源。它更不应该在没有约束的情况下调用外部工具。更合理的理解是Agent 是一个可控的任务调度层。它围绕一个目标工作。它读取当前上下文。它在有限工具集中选择下一步动作。它观察执行结果。它决定继续、重试、切换策略或终止。所以Agent 不是魔法。它是一种围绕“目标驱动”和“动态决策”设计的工程结构。二、Agent 和普通 LLM 调用有什么区别普通 LLM 调用通常是一次性的。例如messages[{role:user,content:请总结这段文章}]resultllm.chat(messages)print(result.content)这个过程很简单。输入给模型。模型返回输出。调用结束。Agent 不完全一样。Agent 更像是一个循环。例如接收目标 ↓ 分析当前状态 ↓ 决定下一步动作 ↓ 调用工具或模型 ↓ 观察结果 ↓ 判断是否完成 ↓ 继续或结束普通 LLM 调用关注的是一次生成。Agent 关注的是围绕目标的一系列决策和动作。例如普通 LLM 调用可以回答“请总结这段文本。”Agent 更适合处理“请帮我找出这个项目启动失败的原因并给出修复建议。”后者可能需要多步执行。每一步要根据上一步结果决定下一步。这就是 Agent 和普通 LLM 调用的核心区别。三、Agent 和 Workflow 有什么区别前面已经提到Workflow 和 Agent 都可以处理多步骤任务。但它们的重点不同。Workflow 更偏确定性。开发者提前定义好流程。例如清洗输入 - 渲染 Prompt - 调用 LLM - 校验结果 - 输出Agent 更偏动态决策。系统根据目标和当前状态决定下一步做什么。例如分析目标 - 选择工具 - 调用工具 - 观察结果 - 决定下一步简单理解Workflow 是预先编排。Agent 是运行时决策。Workflow 更适合流程稳定的任务。Agent 更适合路径不固定的任务。但它们不是对立关系。Agent 内部可以使用 Workflow。Workflow 的某个节点也可以调用 Agent。在脚手架设计里二者应该是可以协作的模块而不是互相替代。四、Agent 的核心组成一个工程化 Agent 通常可以拆成几个核心部分。第一Goal。Goal 是目标。它描述 Agent 要完成什么任务。例如分析项目启动失败的原因并输出修复建议。第二Context。Context 是上下文。它包含当前任务相关的信息。例如用户输入、历史步骤、工具返回结果、中间结论。第三Tool。Tool 是 Agent 可以调用的工具。例如文件读取、搜索、数据库查询、代码执行、HTTP 请求。第四Planner。Planner 负责决定下一步做什么。例如选择哪个工具、是否继续执行、是否需要追问用户。第五Executor。Executor 负责执行 Planner 选择的动作。例如真正调用某个工具。第六Observer。Observer 负责观察执行结果并把结果写回上下文。第七Stop Condition。Stop Condition 决定什么时候结束。例如任务完成、达到最大步数、遇到不可恢复错误、需要用户输入。这些部分组合起来才是一个相对完整的 Agent。如果只写一个大函数让模型随便决定下一步后期会很难维护。五、Agent 的基本执行循环Agent 的核心通常是一个循环。可以先用伪代码理解classAgent:defrun(self,goal:str):contextAgentContext(goalgoal)forstepinrange(self.max_steps):actionself.planner.plan(context)ifaction.typefinish:returnaction.output resultself.executor.execute(action)context.add_observation(action,result)raiseRuntimeError(Agent reached max steps)这个循环里有几个重点。第一必须有最大步数。否则 Agent 可能一直循环。第二每一步都要记录 action 和 observation。否则很难调试。第三Planner 和 Executor 要分开。Planner 决定做什么。Executor 负责执行。第四Agent 必须有终止条件。不能让它无限尝试。这个循环看起来简单。但它体现了 Agent 工程化的基本骨架。六、Agent 必须有边界Agent 最危险的地方是让模型拥有过大的行动空间。如果没有边界Agent 可能会做很多不可控的事情。例如不断循环。调用不该调用的工具。访问不该访问的数据。输出不符合业务要求的内容。在错误方向上越走越远。所以 Agent 必须有边界。常见边界包括最大执行步数。最大运行时间。工具白名单。工具调用参数校验。权限控制。成本限制。用户确认机制。失败兜底策略。这些边界不是可有可无。它们是 Agent 能不能进入真实项目的关键。没有边界的 Agent很难被信任。可控的 Agent才有工程价值。七、工具不是越多越好Agent 经常和 Tool Calling 一起出现。工具能力越强Agent 能做的事情越多。但工具不是越多越好。如果一次性给 Agent 太多工具它可能会选择困难也可能调用错误工具。更好的做法是根据任务场景提供有限工具集。例如代码分析 Agent 可以使用- read_file - list_files - search_code - run_tests而内容生成 Agent 可能只需要- search_reference - render_prompt - call_llm工具应该有清晰描述。输入参数应该有明确 schema。输出结果应该结构化。例如classTool:name:strdescription:strinput_schema:dictdefrun(self,arguments:dict)-dict:...Agent 不应该随便拼字符串调用工具。它应该通过明确的工具接口调用工具。这样才能做参数校验、权限控制和日志记录。八、Agent 的上下文要可追踪Agent 执行过程中会产生很多中间信息。例如用户目标。当前步骤。Planner 的决策。调用的工具。工具输入。工具输出。模型中间分析。最终答案。这些信息需要进入上下文。可以设计一个简单的 AgentContext。fromdataclassesimportdataclass,fielddataclassclassAgentStep:action:strinput:dictoutput:dict|NoneNoneerror:str|NoneNonedataclassclassAgentContext:goal:strsteps:list[AgentStep]field(default_factorylist)final_answer:str|NoneNonedefadd_step(self,step:AgentStep):self.steps.append(step)这样每一步都能被记录。调试时可以看到 Agent 为什么做出某个决策。如果最终结果不对可以回看执行轨迹。没有上下文记录的 Agent很难排查问题。九、Agent 需要失败兜底Agent 不可能每次都成功。它可能遇到很多失败情况。例如工具返回空结果。工具调用失败。模型输出无法解析。Planner 选择了错误工具。执行超过最大步数。任务信息不足。所以 Agent 需要失败兜底。例如如果工具调用失败可以重试一次。如果多次失败就切换策略。如果信息不足就向用户追问。如果达到最大步数就返回当前已知结论。如果任务风险较高就停止并要求人工确认。这比让 Agent 硬着头皮继续执行更可靠。工程项目里失败处理不是异常情况。它是设计的一部分。十、Agent 不应该绕过权限系统Agent 能调用工具就意味着它可能产生真实影响。例如读取文件、修改数据、调用接口、发送消息、执行代码。所以 Agent 必须遵守权限边界。例如只读工具和写入工具要分开。危险操作需要用户确认。敏感数据不能直接暴露给模型。生产环境不能随意执行代码。外部请求需要域名白名单。工具调用要记录审计日志。这些限制不是在削弱 Agent。而是在让 Agent 能够进入真实业务系统。如果一个 Agent 没有权限控制它越强风险越高。十一、Agent 和 Memory 的关系Agent 经常需要 Memory。但 Memory 也不能被随便滥用。Memory 可以分为几类。短期记忆当前任务里的上下文、步骤和观察结果。长期记忆用户偏好、历史任务经验、项目背景信息。外部记忆数据库、向量库、文档库、知识库。在脚手架里Agent 不应该自己随便管理所有记忆。更好的做法是让 Memory 成为独立模块。Agent 通过接口读取或写入 Memory。例如memory.search(query)memory.save(event)这样 Memory 可以被权限、过期策略和数据治理控制。Agent 只使用 Memory不直接拥有 Memory。十二、Agent 在 AI Scaffold 里的位置在 AI Scaffold 里Agent 可以放在agents/目录。例如app/ ├── agents/ │ ├── __init__.py │ ├── base.py │ ├── context.py │ ├── planner.py │ └── code_review_agent.py ├── llm/ │ ├── base.py │ └── factory.py ├── tools/ │ ├── base.py │ ├── file_tool.py │ └── search_tool.py ├── memory/ │ ├── base.py │ └── vector_memory.py ├── prompts/ │ └── agent/ │ └── planner_v1.md └── workflows/ └── summary_workflow.py这里有几个边界。Agent 依赖 LLM但不直接依赖某个模型供应商 SDK。Agent 使用 Tools但不直接绕过工具权限。Agent 可以读取 Memory但通过 Memory 接口访问。Agent 可以调用 Workflow但不把所有流程都塞进自己内部。这种结构能避免 Agent 变成一个巨大的混合模块。十三、第一版 Agent 不要做太大脚手架里的第一版 Agent 不需要一开始就做得很复杂。可以先支持几个基础能力设置目标。维护上下文。限制最大步数。使用有限工具集。记录每一步执行。支持完成、继续、失败三种状态。例如agentAgent(llmllm,tools[read_file_tool,search_code_tool],max_steps5,)resultagent.run(分析项目启动失败的原因)这已经足够作为工程起点。后续再逐步扩展更复杂的 Planner。多 Agent 协作。Memory 接入。Tool 权限系统。人工确认节点。成本和耗时控制。先把边界和结构做好比一开始追求复杂能力更重要。十四、总结Agent 不是一个神秘概念。在工程项目里它可以被理解为围绕目标、上下文、工具和执行策略的一层任务调度机制。Agent 和普通 LLM 调用不同。普通 LLM 调用通常是一次输入和一次输出。Agent 则需要围绕目标进行多步决策和执行。Agent 和 Workflow 也不同。Workflow 更偏确定性流程。Agent 更偏运行时动态决策。一个合理的 Agent 设计至少应该做到有明确目标。有可追踪上下文。有有限工具集。有 Planner 和 Executor 的职责划分。有最大步数和超时限制。有权限边界。有失败兜底。有执行日志。和 LLM、Tool、Memory、Workflow 保持模块边界。对于 AI Scaffold 来说Agent 层不应该是一个把所有能力揉在一起的万能模块。它应该是一个可控、可观察、可扩展的任务调度层。当 Agent 的边界设计清楚以后下一篇文章可以继续讨论 ToolAI 应用里的工具调用应该如何设计才能既让 Agent 有行动能力又不让工具系统变得不可控。