先用一句话概括:
智能体(Agent)是一个能够围绕目标持续观察、决策、调用工具,并根据执行结果调整下一步行动的系统。
大模型只是智能体的“大脑”,并不等同于完整智能体。完整系统还需要工具、记忆、运行循环、安全边界和可观测能力。
一、智能体的基本架构
┌──────────┐
│ 用户目标 │
└────┬─────┘
▼
┌────────────────────┐
│ Agent Runtime │
│ 上下文、循环、状态管理 │
└────┬───────────────┘
▼
┌──────────────┐
│ 大模型 / 策略 │
└────┬─────────┘
│
├── 已经能够回答 ──────────────┐
│ ▼
│ ┌────────┐
│ │最终答案│
│ └────────┘
│
└── 需要外部信息或执行动作
│
▼
┌──────────────┐
│ 工具执行层 │
│ API/数据库/代码│
└──────┬───────┘
│ 执行结果
└──────────────▶ 返回模型继续决策智能体最核心的是这样一个循环:
观察 Observation
↓
决策 Reasoning
↓
行动 Action
↓
获得新的观察结果
↓
继续决策只有当模型认为任务已经完成,或者达到最大执行次数时,循环才停止。
二、智能体的主要组成部分
1. 模型
模型负责决策,例如:
- 理解用户想做什么
- 判断是否需要工具
- 选择合适的工具
- 生成工具参数
- 分析工具执行结果
- 决定下一步行动
- 组织最终答案
但是模型通常不应该直接执行真实操作。
例如,模型可以生成:
{
"tool": "calculate",
"arguments": {
"expression": "(23 + 19) * 7"
}
}真正执行计算的是应用程序中的工具函数。
2. 工具
工具让模型能够与外部世界交互。
常见工具包括:
search_web(query) 搜索互联网
query_database(sql) 查询数据库
read_file(path) 读取文件
send_email(to, content) 发送邮件
create_order(product, count) 创建订单
run_code(source) 执行代码一个工具通常由两部分组成。
第一部分是提供给模型的工具描述:
{
"name": "get_current_time",
"description": "查询指定时区的当前时间",
"parameters": {
"timezone": "时区名称"
}
}第二部分是真正执行操作的函数:
def get_current_time(timezone: str) -> str:
...模型只负责决定“调用哪个工具、传入什么参数”,应用程序负责实际执行。
3. 状态与记忆
智能体需要记住已经发生过什么。
一般可以分成三层:
在最小智能体中,一个 messages 数组就可以充当工作记忆:
messages = [
{"role": "system", "content": "你是一个助手"},
{"role": "user", "content": "帮我计算 128 * 37"},
]每次模型调用工具后,把模型消息和工具结果继续加入 messages:
用户消息
模型工具调用
工具执行结果
模型继续决策4. Agent Runtime
运行时是智能体真正的控制中心,负责:
- 调用模型
- 保存消息历史
- 解析工具调用
- 校验工具参数
- 执行工具
- 把工具结果交还模型
- 控制最大执行次数
- 处理异常和超时
- 判断任务是否结束
它的核心伪代码非常简单:
for step in range(MAX_STEPS):
response = call_model(messages)
if response.has_tool_calls:
results = execute_tools(response.tool_calls)
messages.extend(results)
else:
return response.final_answer很多智能体框架,本质上都是在这个循环上增加记忆、计划、权限、追踪和多智能体能力。
5. 安全边界
不能因为模型说“调用某个函数”,程序就无条件执行。
至少要增加以下限制:
- 工具白名单
- 参数类型检查
- 文件访问范围
- 网络访问范围
- 超时限制
- 最大步骤数
- 用户确认
- 防止重复执行
- 敏感数据过滤
例如:
TOOL_FUNCTIONS = {
"calculate": calculate,
"get_current_time": get_current_time,
}模型即使生成了:
{
"name": "delete_database"
}程序也不会执行,因为该工具不在白名单中。
三、智能体和普通工作流的区别
固定工作流的执行路径通常提前确定:
读取订单
↓
检查库存
↓
扣减库存
↓
发送通知智能体的路径则由模型动态决定:
读取用户目标
↓
模型判断需要哪些信息
↓
选择工具
↓
根据工具结果调整下一步因此:
- 步骤固定时,优先使用普通代码或工作流。
- 路径无法提前完全确定时,才考虑智能体。
- 高风险业务一般采用“固定工作流 + 局部智能体”的混合模式。
四、最小可运行的 Python 智能体
下面实现一个可以调用两个工具的智能体:
calculate:计算数学表达式get_current_time:查询当前时间
模型使用 DeepSeek API。
DeepSeek 提供 OpenAI 兼容接口,官方地址是 https://api.deepseek.com。当前模型示例使用 deepseek-flash。DeepSeek API 文档
安装依赖:
pip install -U openai创建 mini_agent.py:
"""
最小可运行的 DeepSeek 智能体。
运行环境:
- Python 3.10+
- openai Python SDK
功能:
- 模型自主决定是否调用工具
- 程序执行工具
- 工具结果返回给模型
- 循环运行直到得到最终答案
"""
import ast
import json
import operator
import os
import sys
from datetime import datetime
from zoneinfo import ZoneInfo
from openai import OpenAI
# ============================================================
# 1. 基础配置
# ============================================================
# DeepSeek 提供 OpenAI 兼容接口。
BASE_URL = "https://api.deepseek.com"
# 可通过环境变量覆盖模型名称。
MODEL = os.getenv("DEEPSEEK_MODEL", "deepseek-flash")
# 防止模型无限调用工具。
MAX_STEPS = 8
def create_client() -> OpenAI:
"""创建 DeepSeek API 客户端。"""
api_key = os.getenv("DEEPSEEK_API_KEY")
if not api_key:
raise RuntimeError(
"没有找到 DEEPSEEK_API_KEY。\n"
"请先执行:export DEEPSEEK_API_KEY='你的 API Key'"
)
return OpenAI(
api_key=api_key,
base_url=BASE_URL,
timeout=30.0,
max_retries=2,
)
# ============================================================
# 2. 工具实现
# ============================================================
# 允许计算器使用的运算符。
BINARY_OPERATORS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.FloorDiv: operator.floordiv,
ast.Mod: operator.mod,
ast.Pow: operator.pow,
}
UNARY_OPERATORS = {
ast.UAdd: operator.pos,
ast.USub: operator.neg,
}
def calculate(expression: str) -> str:
"""
安全计算数学表达式。
不能直接使用 eval(expression),因为 expression 是模型生成的,
直接执行可能带来代码执行风险。
"""
if not isinstance(expression, str):
raise ValueError("expression 必须是字符串")
if len(expression) > 100:
raise ValueError("表达式过长")
tree = ast.parse(expression, mode="eval")
def evaluate(node):
# 处理整个表达式。
if isinstance(node, ast.Expression):
return evaluate(node.body)
# 只允许整数和浮点数。
if isinstance(node, ast.Constant):
if (
isinstance(node.value, bool)
or not isinstance(node.value, (int, float))
):
raise ValueError("只允许使用数字")
if abs(node.value) > 1_000_000_000_000:
raise ValueError("数字过大")
return node.value
# 处理加、减、乘、除等二元运算。
if isinstance(node, ast.BinOp):
operation = BINARY_OPERATORS.get(type(node.op))
if operation is None:
raise ValueError("包含不支持的运算符")
left = evaluate(node.left)
right = evaluate(node.right)
# 防止生成过于庞大的指数运算。
if isinstance(node.op, ast.Pow) and abs(right) > 10:
raise ValueError("指数不能超过 10")
return operation(left, right)
# 处理正数和负数。
if isinstance(node, ast.UnaryOp):
operation = UNARY_OPERATORS.get(type(node.op))
if operation is None:
raise ValueError("包含不支持的一元运算符")
return operation(evaluate(node.operand))
raise ValueError("表达式包含不支持的语法")
return str(evaluate(tree))
# 工具允许访问的时区白名单。
ALLOWED_TIMEZONES = {
"Asia/Shanghai",
"Asia/Tokyo",
"America/New_York",
"Europe/London",
"UTC",
}
def get_current_time(timezone: str) -> str:
"""获取指定时区的当前时间。"""
if timezone not in ALLOWED_TIMEZONES:
raise ValueError(f"不支持的时区:{timezone}")
current_time = datetime.now(ZoneInfo(timezone))
return current_time.isoformat(timespec="seconds")
# 工具白名单。
# 只有这里注册过的函数才允许被模型调用。
TOOL_FUNCTIONS = {
"calculate": calculate,
"get_current_time": get_current_time,
}
# ============================================================
# 3. 提供给模型的工具定义
# ============================================================
# 这些定义只是告诉模型有哪些工具。
# 模型不能直接执行对应的 Python 函数。
TOOLS = [
{
"type": "function",
"function": {
"name": "calculate",
"description": "准确计算数学表达式。遇到数学计算时使用。",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "数学表达式,例如:(23 + 19) * 7",
}
},
"required": ["expression"],
"additionalProperties": False,
},
},
},
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "获取指定时区的当前日期和时间。",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"enum": sorted(ALLOWED_TIMEZONES),
"description": "IANA 时区名称",
}
},
"required": ["timezone"],
"additionalProperties": False,
},
},
},
]
SYSTEM_PROMPT = """
你是一个可靠、简洁的中文智能体。
规则:
1. 遇到数学计算时,必须调用 calculate。
2. 遇到当前时间问题时,必须调用 get_current_time。
3. 不要编造工具执行结果。
4. 工具失败时,可以修正参数后重新调用。
5. 获得足够信息后,直接给出最终答案。
"""
# ============================================================
# 4. 工具执行器
# ============================================================
def execute_tool(tool_name: str, raw_arguments: str) -> str:
"""
校验并执行模型请求的工具。
工具结果需要以字符串形式返回给模型。
"""
tool_function = TOOL_FUNCTIONS.get(tool_name)
# 拒绝执行没有注册的函数。
if tool_function is None:
return json.dumps(
{
"ok": False,
"error": f"未知或未授权的工具:{tool_name}",
},
ensure_ascii=False,
)
try:
# 模型返回的 arguments 是 JSON 字符串。
arguments = json.loads(raw_arguments)
if not isinstance(arguments, dict):
raise ValueError("工具参数必须是 JSON 对象")
# 执行真正的 Python 工具函数。
result = tool_function(**arguments)
payload = {
"ok": True,
"result": result,
}
except json.JSONDecodeError:
payload = {
"ok": False,
"error": "工具参数不是合法 JSON",
}
except TypeError as error:
payload = {
"ok": False,
"error": f"工具参数错误:{error}",
}
except Exception as error:
payload = {
"ok": False,
"error": str(error),
}
return json.dumps(payload, ensure_ascii=False)
# ============================================================
# 5. Agent Loop
# ============================================================
def run_agent(user_input: str) -> str:
"""运行一次完整的智能体任务。"""
if not user_input.strip():
raise ValueError("用户输入不能为空")
client = create_client()
# messages 保存当前任务的上下文和执行历史。
messages = [
{
"role": "system",
"content": SYSTEM_PROMPT,
},
{
"role": "user",
"content": user_input,
},
]
for step in range(1, MAX_STEPS + 1):
# 把当前上下文和工具列表发送给 DeepSeek。
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=TOOLS,
stream=False,
# 为了保持最小示例简单,关闭思考模式。
extra_body={
"thinking": {
"type": "disabled",
}
},
)
assistant_message = response.choices[0].message
# 将模型消息保存到上下文。
# 如果其中包含工具调用,后续请求也必须携带该消息。
messages.append(assistant_message)
tool_calls = assistant_message.tool_calls or []
# 模型没有调用工具,说明它已经给出了最终答案。
if not tool_calls:
return assistant_message.content or "模型没有返回文本。"
# 执行模型请求的所有工具。
for tool_call in tool_calls:
tool_name = tool_call.function.name
raw_arguments = tool_call.function.arguments
print(
f"[步骤 {step}] 调用工具:{tool_name}\n"
f"参数:{raw_arguments}"
)
tool_result = execute_tool(
tool_name=tool_name,
raw_arguments=raw_arguments,
)
print(f"结果:{tool_result}\n")
# 将工具执行结果加入上下文。
# tool_call_id 用于关联具体的工具调用。
messages.append(
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": tool_result,
}
)
raise RuntimeError(
f"超过最大执行步数:{MAX_STEPS}"
)
# ============================================================
# 6. 命令行入口
# ============================================================
def main() -> None:
"""读取命令行参数并启动智能体。"""
user_input = " ".join(sys.argv[1:]).strip()
# 没有传入参数时,使用默认测试问题。
if not user_input:
user_input = (
"请计算 (23 + 19) * 7,"
"然后告诉我上海现在几点。"
)
try:
answer = run_agent(user_input)
print("智能体最终回答:")
print(answer)
except KeyboardInterrupt:
print("\n任务已取消。")
raise SystemExit(130)
except Exception as error:
print(f"运行失败:{error}", file=sys.stderr)
raise SystemExit(1)
if __name__ == "__main__":
main()五、运行实例
创建虚拟环境:
python3 -m venv .venv
source .venv/bin/activate安装依赖:
pip install -U openai设置 DeepSeek API Key:
export DEEPSEEK_API_KEY="你的 DeepSeek API Key"运行默认问题:
python mini_agent.py也可以传入自定义任务:
python mini_agent.py "计算 128 * 37,再告诉我上海现在几点"执行过程大致如下:
[步骤 1] 调用工具:calculate
参数:{"expression":"128 * 37"}
结果:{"ok":true,"result":"4736"}
[步骤 2] 调用工具:get_current_time
参数:{"timezone":"Asia/Shanghai"}
结果:{"ok":true,"result":"2026-09-16T10:30:00+08:00"}
智能体最终回答:
128 × 37 = 4736。上海当前时间是 2026 年 9 月 16 日 10:30。DeepSeek 的工具调用流程也是“模型生成调用请求、应用执行函数、应用把结果作为 tool 消息返回模型”,模型本身并不会直接执行 Python 函数。DeepSeek Tool Calls 文档
六、代码与架构的对应关系
这个例子虽然很小,但已经具备完整智能体的几个关键特征:
- 用户只提供目标,没有指定执行步骤。
- 模型自主判断是否需要工具。
- 模型能够根据工具结果继续行动。
- 运行路径不是提前完全写死的。
- 系统有明确的工具权限和停止条件。
从这个最小版本继续向生产系统演化时,通常依次增加持久化记忆、用户确认、数据库工具、工具超时与重试、执行日志、成本统计和自动化评测。
