ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

通用智能的本质是适应:构建可动态调度工具的AI Agent框架

2026/9/2 11:58:17 拓冰建站 浏览量
通用智能的本质是适应:构建可动态调度工具的AI Agent框架 大家在业务系统里做智能化改造时是不是经常遇到这种情况需求方一开始说“帮我写一套工单自动分类逻辑”你老老实实把分类规则、关键词表、优先级枚举全写死在代码里上线没两周业务又加了新的工单类型甚至出现了“存量客户投诉转营销线索”这种跨域任务你只能连夜改规则、加判断、发版上线。这个问题的根源不在于你少写了某个分支而在于整个系统把“能力”预设得太死。规则引擎、状态机、流程编排都是典型的“预设能力”——它们适合范围明确、边界固定的场景。可一旦任务空间是开放的预设方案的维护成本就会指数级上升。这也是为什么业内讨论“通用智能”时越来越多人认可一个判断通用智能的本质是适应而非预设能力。这篇文章不打算做纯理论辨析而是围绕这个观点从 AI Agent智能体开发的角度完整拆解为什么说“适应”才是通用性的关键以及如何在工程上落地一套能动态调度工具、记忆上下文、在运行中自我修正的小型 Agent 框架。适合正在做 LLM 应用、Agent 编排、自动化流程的开发者也适合想理解“通用智能”技术含义的产品和技术负责人。读完你可以直接照着把代码跑起来并在自己的项目中复用这套思路。1. 通用智能是什么为什么“适应”比“预设”更关键1.1 从“预设能力”说起先给“预设能力”下一个通俗定义所谓预设能力就是把完成某个任务所需的步骤、规则、分支、参数在系统设计阶段就固定下来。典型例子包括传统规则引擎if order.amount 10000 then level VIP。有状态工作流状态机里定义好所有节点和转移条件。传统的意图识别 槽位填充只识别预先定义好的查天气、定闹钟等意图超出范围就回退到兜底话术。这种方式有两个明显优点行为可预期、排错容易。但它也有一个致命短板——任务空间一旦扩大规则数量会爆炸。100种规则还算可控10000种规则就是维护灾难。更麻烦的是规则之间还会互相干扰很多团队最后改到一个规则就影响另一个规则只能重构。1.2 “适应”在 AI 系统中的含义“适应”则不同。它不是提前写好每一个任务的实现步骤而是让系统具备下面三种底层能力感知变化能从输入中识别当前任务是什么、需要哪些资源、处于什么上下文。动态决策根据感知到的信息在运行时选择工具、方法、执行顺序而不是走固定分支。反馈修正执行后根据结果和错误信息调整策略直到任务完成或被安全终止。用一个形象的对比预设能力的系统像一本印刷好的旅行手册只覆盖写过的路线适应能力的系统像一个会看地图、会问路、会根据天气调整行程的旅行者。后者不会被困在一本手册里。1.3 对开发者意味着什么当我们在 LLM 时代谈“通用智能”并不是让每个团队去训练一个通用人工智能模型而是指上层系统设计应该具备开放性。具体到工程上至少有三点转变从写死规则转向让模型理解任务并组合工具从固定流程转向“观察-决策-行动-反思”的循环从一次性调用转向带记忆和闭环反馈的多轮执行。这也是后面几节要做的用一个可运行的 Agent 示例把这套“适应”能力落到代码上。2. 环境准备与系统设计2.1 开发环境与依赖本文示例使用 Python 3.10 及以上版本核心逻辑不依赖任何重型框架。为了让读者在没有大模型 API Key 的情况下也能端到端运行我实现了两种推理后端StubLLM本地模拟后端用规则逻辑代替大模型方便本机验证整体 Agent 流程。OpenAICompatLLM兼容 OpenAI Chat Completions 接口的后端可替换为任意兼容该协议的模型服务生产环境推荐使用。依赖方面示例尽量使用 Python 标准库只有OpenAICompatLLM需要额外安装openai库和配置环境变量。你完全可以直接复制项目跑起来再按需接入真实模型。2.2 系统总体架构本文将实现一个小型 Agent 框架由五个核心模块组成模块职责Tool定义工具的统一接口包括名称、描述、参数、执行方法ToolRegistry工具注册表支持运行时动态注册、查找工具Memory会话记忆保存系统提示、历史消息、观察结果LLMBackend模型推理后端负责把 Agent 状态转成决策Agent主循环串联感知、决策、行动、观察和反思整体执行流程如下接收用户任务从工具注册表加载可用工具的描述将任务、工具信息、历史记录一起交给 LLM 后端LLM 返回决策 JSON包含thought和actionAgent 执行对应工具得到观察结果把观察结果追加进内存进入下一轮当 LLM 返回finish时结束循环输出最终回答。2.3 示例项目结构agent-demo/ ├── main.py # 启动入口演示完整流程 ├── agent/ │ ├── __init__.py │ ├── core.py # Agent 主类 │ ├── memory.py # 会话记忆 │ ├── tools.py # 工具抽象类和注册表 │ ├── llm.py # 推理后端Stub OpenAI兼容 │ └── system_prompt.py # 系统提示词模板 └── tools/ ├── __init__.py ├── time_tool.py # 示例工具获取当前时间 ├── calculator.py # 示例工具四则运算 └── file_reader.py # 示例工具安全读取白名单目录文件3. 核心原理拆解Agent 如何实现“适应”在写完整代码前先把四个关键机制讲透。这不只是为跑通示例更是为了让你在自己的项目里能举一反三。3.1 动态工具发现与注册传统程序的“功能”往往写死在方法里调用关系是编译期确定的。而 Agent 的核心变化是工具的描述以结构化文本形式暴露给模型模型在运行时决定调用哪个工具。也就是说“有什么能力”和“用哪个能力”被解耦了。ToolRegistry 在启动时加载所有工具并将每个工具的name、description、parameters格式化成模型能读懂的列表。模型不是从代码里找到某个函数而是从上下文里的工具描述中作出选择。更关键的是ToolRegistry 支持运行时注册。比如项目启动后加载了一个tools/extra.py其中定义了一个新工具Agent 下一轮决策时就能感知到它。这就实现了“系统在运行中长出新的能力”——这是“适应”在工程上最直观的体现。3.2 基于任务意图的推理循环Agent 不是调用一次模型就返回而是走一个循环Thought模型先思考当前任务处于什么阶段距离完成还差什么Action选择要调用的工具名和参数Observation工具执行结果返回给模型Repeat直到模型判定任务完成输出最终答案。这个循环把一次性对话变成了持续的问题求解过程。任务越复杂循环轮数越多。实现时需要注意给循环设置最大轮数防止模型陷入死循环。3.3 记忆与上下文管理记忆对“适应”至关重要。没有记忆的 Agent每一轮都像失忆的人重新开始无法利用上一轮的观察结果。Memory 模块保存两类信息用户的原始请求、以及 Agent 每一步产生的thought/action/observation。每次请求模型时把最近的记忆拼进上下文。还要设置窗口上限避免上下文无限膨胀——这个问题在小模型或长任务场景尤其明显。3.4 反思与纠错机制“适应”的另一个关键点是容错。真实环境中模型可能传错参数、调用不存在的工具、返回不合法 JSON。Agent 需要有明确的反思机制如果工具调用报错把错误信息作为 Observation 塞回上下文如果模型返回了未注册的工具尝试纠正为相近工具名如果连续多轮失败终止循环并给出可读的失败原因。这个“错误也是一种观察结果”的思路是 Agent 和普通程序最大的差异。普通程序报错就中断Agent 可以把报错当作下次决策的输入。4. 完整实战实现一个可适应新任务的 Agent接下来我们用代码把上面的设计落地。为了让代码可以直接运行StubLLM会通过关键词识别模拟模型决策OpenAICompatLLM则为真实环境预留了接入点。4.1 定义工具接口与注册表文件路径agent/tools.py# agent/tools.py from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional class Tool(ABC): 所有工具必须继承 Tool并实现 name/description/parameters/execute。 property abstractmethod def name(self) - str: 工具名称模型通过该名称调用工具。 property abstractmethod def description(self) - str: 工具功能描述模型会依据描述做决策要写清楚用途和边界。 property def parameters(self) - Dict[str, Any]: 参数 JSON Schema用于描述工具需要哪些参数。 return {type: object, properties: {}} abstractmethod def execute(self, **kwargs: Any) - str: 执行工具入参由模型解析后传入。返回结果必须是字符串。 class ToolRegistry: 工具注册表支持运行时动态注册、查找和描述格式化。 def __init__(self) - None: self._tools: Dict[str, Tool] {} def register(self, tool: Tool) - None: if tool.name in self._tools: raise ValueError(f工具 {tool.name} 已注册) self._tools[tool.name] tool print(f[ToolRegistry] 注册工具: {tool.name}) def unregister(self, tool_name: str) - None: if tool_name not in self._tools: raise KeyError(f工具 {tool_name} 不存在) del self._tools[tool_name] print(f[ToolRegistry] 注销工具: {tool_name}) def get(self, tool_name: str) - Optional[Tool]: return self._tools.get(tool_name) def list_tools(self) - List[str]: return list(self._tools.keys()) def describe_tools(self) - str: lines [] for tool in self._tools.values(): lines.append( f- {tool.name}: {tool.description} | 参数: {tool.parameters} ) return \n.join(lines)这里要解释几个细节Tool是抽象基类所有业务功能都要实现成 Tool 子类。这样 Agent 主循环不需要关心“计算器怎么算”“文件怎么读”只关心工具名和参数。describe_tools()会把所有工具的描述格式化成一段文本喂给模型。这也是“适应”得以实现的基础模型通过描述而不是硬编码调用关系来了解系统能力。注册表用字典保存工具名字不能重复。运行时注册新工具是很自然的事只要调用register()即可。4.2 编写三个示例工具文件路径tools/time_tool.py# tools/time_tool.py from datetime import datetime from typing import Any, Dict from agent.tools import Tool class GetCurrentTimeTool(Tool): property def name(self) - str: return get_current_time property def description(self) - str: return 获取当前系统时间返回格式为 YYYY-MM-DD HH:MM:SS property def parameters(self) - Dict[str, Any]: return {type: object, properties: {}} def execute(self, **kwargs: Any) - str: return datetime.now().strftime(%Y-%m-%d %H:%M:%S)文件路径tools/calculator.py# tools/calculator.py from typing import Any, Dict from agent.tools import Tool class CalculatorTool(Tool): property def name(self) - str: return calculator property def description(self) - str: return 执行四则运算支持加减乘除。传入表达式例如 1 2 * 3 property def parameters(self) - Dict[str, Any]: return { type: object, properties: { expression: { type: string, description: 待计算的数学表达式, } }, required: [expression], } def execute(self, **kwargs: Any) - str: expression kwargs.get(expression, ) # 出于演示目的使用 eval生产环境必须使用安全的表达式求值库 try: result eval(expression, {__builtins__: {}}, {}) return str(result) except Exception as e: return f表达式计算失败: {e}这里特别说明eval只在本地演示中使用真实项目请引入asteval或simpleeval这类安全求值库否则会引入代码执行漏洞。安全边界会在第 6 节详细讲。文件路径tools/file_reader.py# tools/file_reader.py import os from pathlib import Path from typing import Any, Dict from agent.tools import Tool class SafeFileReaderTool(Tool): 只能读取白名单目录下的文件防止路径穿越。 ALLOWED_DIR Path(./data) property def name(self) - str: return read_file property def description(self) - str: return 读取白名单目录 data 下的文本文件内容 property def parameters(self) - Dict[str, Any]: return { type: object, properties: { filename: { type: string, description: 相对 data 目录的文件名, } }, required: [filename], } def execute(self, **kwargs: Any) - str: filename kwargs.get(filename, ) base self.ALLOWED_DIR.resolve() target (base / filename).resolve() # 校验目标路径是否还在白名单目录内 if not str(target).startswith(str(base)): return 错误不允许访问白名单目录以外的文件 if not target.exists() or not target.is_file(): return f错误文件 {filename} 不存在 try: return target.read_text(encodingutf-8) except Exception as e: return f读取文件失败: {e}SafeFileReaderTool是一个很好的例子AI 的能力边界必须通过代码硬性约束。模型可以建议读哪个文件但“能不能读”由工具自己决定而不是模型决定。4.3 实现会话记忆与推理后端文件路径agent/memory.py# agent/memory.py from typing import List, Dict, Any class ConversationMemory: 保存多轮对话和观察结果并限制最大窗口长度。 def __init__(self, max_messages: int 20) - None: self.messages: List[Dict[str, str]] [] self.max_messages max_messages def add_user(self, content: str) - None: self.messages.append({role: user, content: content}) def add_system(self, content: str) - None: self.messages.append({role: system, content: content}) def add_assistant(self, content: str) - None: self.messages.append({role: assistant, content: content}) def add_observation(self, content: str) - None: self.messages.append({role: user, content: f[观察结果] {content}}) def trim(self) - None: # 保留最近的 max_messages 条消息 if len(self.messages) self.max_messages: self.messages self.messages[-self.max_messages:] def to_openai_messages(self) - List[Dict[str, str]]: self.trim() return self.messages记忆模块需要注意两点观察结果不单独设计一个角色而是放进user消息并加了[观察结果]前缀。这样对主流 Chat 模型来说语义更兼容。trim()通过截断历史控制上下文长度避免超长。实际项目里可以做摘要压缩或向量检索但那是另一个层面的优化。文件路径agent/llm.py# agent/llm.py import json import os import re from abc import ABC, abstractmethod from typing import Any, Dict, List class LLMBackend(ABC): 模型推理后端抽象类。 abstractmethod def chat(self, messages: List[Dict[str, str]]) - str: 输入对话消息返回模型输出文本。 class StubLLM(LLMBackend): 本地模拟模型后端通过关键词匹配返回格式化的 JSON 决策。 仅用于演示 Agent 循环实际项目请接入真实模型。 def chat(self, messages: List[Dict[str, str]]) - str: last_user for message in reversed(messages): if message[role] user: last_user message[content] break text last_user.lower() if 现在几点 in text or 当前时间 in text: return json.dumps( { thought: 用户想获取当前时间可以使用时间工具。, action: {name: get_current_time, args: {}}, }, ensure_asciiFalse, ) if 计算 in text: match re.search(r计算\s*([\d\s\-*/().]), text) expression match.group(1).strip() if match else 11 return json.dumps( { thought: 用户需要做数学计算调用计算器工具。, action: { name: calculator, args: {expression: expression}, }, }, ensure_asciiFalse, ) if 读取 in text: filename_match re.search(r读取\s*([^\s]), text) filename filename_match.group(1) if filename_match else demo.txt return json.dumps( { thought: 用户需要读取文件内容调用文件读取工具。, action: { name: read_file, args: {filename: filename}, }, }, ensure_asciiFalse, ) return json.dumps( { thought: 任务无法通过现有工具完成或者用户请求与工具集不匹配。, action: {name: __finish__, args: {}}, answer: 抱歉我没有找到能完成该任务的工具。, }, ensure_asciiFalse, ) class OpenAICompatLLM(LLMBackend): 兼容 OpenAI Chat Completions 协议的模型后端。 生产环境将 OPENAI_API_KEY 和 OPENAI_BASE_URL 配置为你的服务地址 模型自动读取 OPENAI_MODEL_NAME 指定的模型名。 def __init__(self, model: str | None None) - None: api_key os.getenv(OPENAI_API_KEY) if not api_key: raise ValueError(未配置 OPENAI_API_KEY 环境变量) try: from openai import OpenAI except ImportError: raise ImportError(请先安装 openai 库pip install openai) self._client OpenAI(api_keyapi_key) self._model model or os.getenv(OPENAI_MODEL_NAME, gpt-4o-mini) def chat(self, messages: List[Dict[str, str]]) - str: response self._client.chat.completions.create( modelself._model, messagesmessages, temperature0.2, ) return response.choices[0].message.content or StubLLM虽然很简陋但它体现了 Agent 循环的关键模型输出不是自由文本而是结构化的{thought, action}JSON。真实模型中我们通过提示词要求模型返回同样结构再由 Agent 解析。这样Stub 和真实模型可以用同一套循环逻辑。4.4 实现 Agent 主循环与系统提示词文件路径agent/core.py# agent/core.py import json from typing import Any, Dict, List from agent.memory import ConversationMemory from agent.tools import ToolRegistry class Agent: def __init__( self, registry: ToolRegistry, llm, memory: ConversationMemory | None None, max_steps: int 5, system_prompt: str , ) - None: self.registry registry self.llm llm self.memory memory or ConversationMemory() self.max_steps max_steps self.system_prompt system_prompt def run(self, user_input: str) - str: self.memory.add_user(user_input) step 0 while step self.max_steps: step 1 print(f\n Step {step} ) tools_desc self.registry.describe_tools() system_message self.system_prompt.replace( {{tools}}, tools_desc ) messages [ {role: system, content: system_message} ] self.memory.to_openai_messages() try: raw_response self.llm.chat(messages) decision self._parse_decision(raw_response) except Exception as e: self.memory.add_observation(f模型决策解析失败: {e}) continue thought decision.get(thought, ) print(f[Thought] {thought}) action decision.get(action, {}) action_name action.get(name, ) action_args action.get(args, {}) if action_name __finish__: answer decision.get(answer, 任务已完成。) self.memory.add_assistant(answer) return answer tool self.registry.get(action_name) if tool is None: obs f错误工具 {action_name} 不存在。当前可用工具: {, .join(self.registry.list_tools())} print(f[Observation] {obs}) self.memory.add_observation(obs) continue try: result tool.execute(**action_args) except Exception as e: result f工具 {action_name} 执行异常: {e} print(f[Action] {action_name}({action_args})) print(f[Observation] {result}) self.memory.add_observation(result) return 已达到最大执行轮数任务未能完成。请简化任务或增加可用的工具。 def _parse_decision(self, raw: str) - Dict[str, Any]: 解析模型返回文本兼容从 json 代码块中提取 JSON。 text raw.strip() if json in text: start text.find(json) 7 end text.find(, start) text text[start:end].strip() elif in text: start text.find() 3 end text.find(, start) text text[start:end].strip() return json.loads(text)Agent.run()是整个框架的核心它把第 3 节讲的推理循环变成了真实代码。每一轮决策都基于系统提示词、历史记忆和工具描述工具执行结果无论成功失败都会被记录为观察结果供下一轮决策参考。这就是 Agent 能“适应”变化的直接原因。文件路径agent/system_prompt.py# agent/system_prompt.py SYSTEM_PROMPT 你是一个运行在安全沙箱中的通用智能助手。你要根据用户请求和可用工具自主完成推理和行动。 你可以使用以下工具 {{tools}} 要求 1. 每次只输出一个 JSON 对象不要输出多余文字。 2. JSON 格式必须为 {thought: 你当前的思考, action: {name: 工具名, args: {参数名: 参数值}}} 3. 当你认为任务已经完成或者现有工具无法完成用户请求时输出 {thought: 总结, action: {name: __finish__, args: {}}, answer: 给用户的最终回答} 4. 调用工具前必须确认该工具确实已在可用工具列表中。不要臆造不存在的工具。 5. 如果工具执行报错根据错误信息调整参数最多重试两次。 6. 禁止调用不在列表中的工具禁止访问未授权目录或执行危险操作。 7. 如果连续多轮没有进展请尽快输出 finish并向用户说明原因。 请确保行动可观测、可回滚任何外部副作用操作都必须在执行前充分说明。 这个系统提示词可以看作“通用安全的智能体的系统提示词”的最小模板。它强调了三件事工具白名单、结构化输出、失败兜底。真实项目中你还需要加入敏感信息过滤、数据脱敏、审计日志等约束。4.5 编写启动入口并运行文件路径main.py# main.py from agent.core import Agent from agent.memory import ConversationMemory from agent.llm import StubLLM from agent.system_prompt import SYSTEM_PROMPT from agent.tools import ToolRegistry from tools.calculator import CalculatorTool from tools.file_reader import SafeFileReaderTool from tools.time_tool import GetCurrentTimeTool def build_agent() - Agent: registry ToolRegistry() registry.register(GetCurrentTimeTool()) registry.register(CalculatorTool()) registry.register(SafeFileReaderTool()) # 默认使用 StubLLM 本地运行配置好 OPENAI_API_KEY 后可直接切换为 # llm OpenAICompatLLM() llm StubLLM() memory ConversationMemory(max_messages20) return Agent( registryregistry, llmllm, memorymemory, max_steps5, system_promptSYSTEM_PROMPT, ) if __name__ __main__: agent build_agent() tasks [ 现在几点, 帮我计算 (12 34) * 2, 读取 data/demo.txt 的内容, 帮我把房间温度调高一点, ] for task in tasks: print(\n) print(f[用户请求] {task}) answer agent.run(task) print(f[Agent 回答] {answer})在项目根目录创建data/demo.txt写入如下内容你好这是 Agent 白名单目录中的示例文件。运行命令python main.py预期输出大致如下为了排版做了简化 [用户请求] 现在几点 Step 1 [ToolRegistry] 注册工具: get_current_time ... [Thought] 用户想获取当前时间可以使用时间工具。 [Action] get_current_time({}) [Observation] 2025-02-13 10:24:31 [Agent 回答] 任务已完成。注意由于main.py中先输出了注册日志实际控制台会有多条注册日志。关键是最后三个任务都能被 Agent 正确识别并调用对应工具第四个“调节温度”没有对应工具Agent 会输出“抱歉”类回答。这就体现了 Agent 在开放任务下的“适应”边界能做的调用工具完成不能做的显式说明。如果你配置了OPENAI_API_KEY和兼容服务地址可以把llm StubLLM()替换为llm OpenAICompatLLM()再用一句话测试切换效果export OPENAI_API_KEYyour_api_key export OPENAI_MODEL_NAMEgpt-4o-mini python main.py真实模型能理解更复杂的自然语言任务但 Agent 循环本身不需要改一行代码。这是接口抽象带来的好处。5. 常见问题与排查思路在实践这套 Agent 方案时下面几个问题最容易遇到。整理成表格方便快速定位。问题现象常见原因解决思路模型总是返回不存在的工具工具描述不清晰或系统提示词未强调白名单约束检查describe_tools()输出确保工具描述写清楚用途在提示词中反复强调“只能使用列表中的工具”Agent 陷入死循环不断调用同一工具观察结果没有让模型意识到状态变化检查工具返回结果是否有足够信息在提示词中要求模型总结进展增加最大轮数限制输出 JSON 解析失败模型返回了json包裹的代码块或者夹杂解释文字使用_parse_decision()中的兼容逻辑在提示词中严格限定输出格式文件读取工具被路径穿越对用户传入的路径未做白名单校验参考SafeFileReaderTool用resolve()后对比基准目录上下文窗口超限多轮循环后记忆消息过多缩小ConversationMemory.max_messages或引入摘要压缩StubLLM 能跑换真实模型后效果变差真实模型对格式和工具描述更敏感优化系统提示词给工具增加更完整的参数description降低temperature至 0.2 以下下面再展开三个高频场景。5.1 模型“幻觉工具”问题即使是 GPT-4 级别的模型也可能在面对模糊工具名时“编造”出一个不存在的工具。比如项目里工具叫read_file模型却输出readFile或getFileContent。这通常不是模型笨而是工具名不符合常见命名习惯或者系统提示词没有强调可用工具列表。解决方案是双管齐下。第一工具命名尽量短小、符合英语惯例。第二在解析层做一次容错映射如果模型输出的名称未注册尝试在工具列表中找一个最相近的比如忽略大小写和分隔符后再匹配。示例中ToolRegistry.get()是精确查找真实项目可以扩展一个fuzzy_get()方法。5.2 Agent 反复执行同一工具没有进展假设模型已经调用了calculator但结果是一次字符串拼接错误下一轮模型可能又用同样参数再调一次。这种“原地打转”的本质是模型没有从错误中反思。建议在三层加强提示词层要求“如果同一工具连续两次报错必须换一种策略”代码层记录最近 N 轮 action 的签名如果重复出现直接给观察结果加上“该操作已失败过”的提示控制层设置max_steps保证系统不会无限消耗资源。5.3 上下文窗口溢出多轮工具调用会让Observation快速堆积尤其是文件读取类工具返回大段文本时。此时简单粗暴地截断早期消息会丢失重要信息更好的做法分两步对单次 Observation 做截断比如最多保留 1000 字符对超过窗口的历史消息做摘要用一句“用户之前请求过 X已完成 tool Y结果为 Z”替换整段历史。这个摘要策略在很多生产级 Agent 中都很常见既能压缩 token又能保留关键上下文。6. 工程最佳实践安全、成本与可观测性把这套 Agent 从 Demo 推向生产环境有几个工程问题必须在设计阶段就考虑清楚。尤其当智能体拥有调用外部工具、读写文件、访问网络的能力时“通用安全智能体”不是一句口号而是一组硬约束。6.1 安全边界与最小权限原则Agent 的能力越强安全边界越要收窄。建议从四个维度进行控制工具白名单所有工具必须显式注册Agent 不能动态创建工具。业务需要新增能力时走代码评审和发布流程。路径安全文件读写工具必须做目录白名单校验。示例中的SafeFileReaderTool已经演示了resolve()startswith()的校验方式生产项目还要考虑软链接、权限位等因素。命令执行风险不要在生产环境中使用eval/exec执行模型生成的表达式。如果确实有计算需求使用simpleeval、asteval这类受限解释器或者直接调用系统自带的安全计算库。人工审核与回滚涉及发消息、改配置、删数据等有副作用的行为必须设计审批节点。工具执行前记录参数执行后记录结果便于审计和回滚。6.2 成本控制与请求优化LLM 调用按 token 计费Agent 循环天然会放大调用次数。一个 5 步任务至少产生 5 次模型调用每轮还要把所有历史记录重新发一遍。成本优化有几个抓手精简系统提示词删除与当前任务无关的规则描述把工具描述控制在必要长度。压缩历史消息每轮对话结束后用摘要保留信息而不是全量保存。复用模型结果对相似请求做缓存比如天气查询、计算结果这类确定性输出可以用 Redis 缓存减少模型调用。设置预算上限记录每次任务的累计 token 数达到阈值后强制终止并转人工。建议给每次 Agent 运行打印 token 监控日志方便后续优化。示例中的OpenAICompatLLM返回对象里本身包含 usage 信息可以在这里采集。6.3 日志、追踪与可观测性Agent 的决策过程是非确定性的同一句话可能走出不同路径。因此必须把每一步的 thought、action、args、observation 全部记录下来形成完整的 trace。建议每条 trace 至少包含会话 ID、用户 ID脱敏后用户原始输入每轮模型的 thought 和 action工具执行耗时和结果最终回答或失败原因日志记录不仅是排错依据也是后续优化系统提示词、调整模型参数的重要数据。很多团队踩坑后都感慨没有 trace 的 Agent 就像一个黑盒出了问题完全不知道是模型理解错、工具写错还是上下文截断了。6.4 系统提示词的设计原则结合前文提到的“通用安全的智能体的系统提示词”我总结四条设计原则供你在实际项目中复用结构化输出优先不要用自然语言描述期望输出格式而是直接给出 JSON 模板并要求模型“只输出 JSON不要输出其他内容”。边界描述明确明确说“只能使用工具列表中的工具”“禁止访问未授权路径”约束要具体而不是笼统地写“请谨慎操作”。给模型留出反思空间要求模型在每轮行动前输出thought这既是为了输出稳定也是给后续排错留证据。结束条件清晰定义什么情况下必须结束循环比如“任务完成”“连续两次相同错误”“用户要求停止”。没有清晰结束条件的 Agent 容易陷入无效循环。7. 总结与下一步学习路线这篇文章从“通用智能的本质是适应而非预设能力”的判断出发完整实现了一个可运行的 Agent 示例。你现在应该已经掌握了几件事预设能力和适应能力的核心区别在于任务空间是否开放Agent 通过工具注册、动态决策、记忆管理和纠错机制实现“适应”一个最小可运行的 Agent 框架只需要工具注册表、记忆模块、模型后端和主循环四部分生产环境必须在工具白名单、路径校验、成本控制、日志追踪等方面加固才能真正做到“通用且安全”。下一步建议你从两个方向继续深入。如果你关注模型侧可以学习如何用函数调用Function Calling代替手工 JSON 解析很多模型服务商都原生支持结构化工具调用效果比提示词约束更稳定。如果你关注系统侧可以把重点放在记忆压缩、多 Agent 协作和工具自动生成上——比如让 Agent 在运行中通过生成代码来创建新工具这正是“适应能力”的进一步延伸。当然这一套代码并不是真正的 AGI它也只是一个工程原型。但理解“适应而非预设”这个原则会帮助你避免很多无效的堆规则式开发。下次再遇到“能不能给我加个新功能”的需求你可以先想想是继续往规则里加分支还是把能力作为一种可配置、可组合、可动态调度的资源交给模型去编排。如果这篇文章对你有帮助建议收藏备用也欢迎在评论区聊聊你在 Agent 开发中踩过的“预设能力”的坑。