大模型 Agent 实践

Agent = 一个能“自主决策 + 自主执行任务”的大模型系统,而不是简单对话的 LLM。

  • 普通调用大模型:只有 LLM 单次回答
  • Agent:让 LLM 具备“循环、自我反思、调用工具、规划任务”的能力 → 相当于一个小型 AI 程序

一、Agent 组成

1. LLM 思考(Reasoning)

LLM 不是直接回答,而是负责:

  • 规划下一步做什么
  • 选择需要的工具
  • 分解任务
  • 评估结果是否满足
  • 决定是否继续循环

你会看到其 “思考链”(chain of thought),在真实系统中通常被隐藏。

2. 工具(Tool Calls / Function Calling)

  • HTTP 请求
  • 搜索引擎
  • 数据库查询
  • Python/JS 执行
  • 文件系统操作
  • 外部系统 API

LLM 并不直接执行,而是输出:

1
2
3
4
{
"tool": "search",
"query": "Bitcoin price now"
}

3. 循环(Agent Loop)

任务开始 → 思考 → 调用工具 → 再思考 → 再调用工具 → 判断是否完成 → 输出最终答案

4. 记忆

  • 过去任务结果
  • 用户偏好
  • 持久化信息(Vector DB)
  • 短期工作记忆(类似 stack)

让 Agent 具备“一致性”和“长期性”。

二、常见场景

1. 场景一:查询天气

1
帮我查一下北京明天的天气
  • 自动判断需要使用“天气查询工具”
  • 自动调用工具
  • 获得真实天气数据
  • 整理成可读的回答
  • 自动返回给你

2. 场景二:实现代码并上线

1
帮我写一个支持登录的聊天室,并部署到 Vercel

模型可能会有如下几个步骤

  • 分解任务
  • 生成项目结构、代码
  • 使用工具检查报错
  • 调用 Vercel API 自动部署
  • 测试功能
  • 返回最终网址

三、实际动手做一个 Agent

这里直接使用 NextJS 快速搭建一个

1. 界面

2. 项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
agent-demo/
├── app/
│ ├── api/
│ │ └── chat/
│ │ └── route.ts # Chat API Route
│ ├── globals.css # 全局样式
│ ├── layout.tsx # 根布局
│ ├── page.tsx # 主页面
│ └── page.module.css # 页面样式
├── components/
│ └── chat/
│ ├── ChatMessage.tsx # 消息气泡组件
│ ├── ChatInput.tsx # 输入框组件
│ ├── WelcomeScreen.tsx # 欢迎页组件
│ ├── LoadingIndicator.tsx
│ └── index.ts # 组件导出
├── lib/
│ ├── agent.ts # Agent 核心逻辑
│ ├── tools.ts # 工具定义和实现
│ └── types.ts # TypeScript 类型定义
├── package.json
├── tsconfig.json
└── README.md

3. Agent 核心逻辑

核心就是不断循环调用大模型,然后大模型会根据用户传入的 Prompt 来判断是否需要调用工具,如果需要调用工具,会返回一个 tool_calls 数组,数组里面有需要调用函数的名称和参数,然后根据函数名称和参数执行对应的逻辑。

再将执行得到的结果再次传给大模型,循环往复,知道大模型认为没有需要调用工具位置,大模型会返回终止信号,这个时候就是最终结果。

3.1 运行 agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
* 运行 Agent
* @param userMessage 用户消息
* @param history 历史消息
* @returns Agent 执行结果
*/
async run(
userMessage: string,
history: ChatMessage[] = []
): Promise<AgentResult> {
const messages: ChatMessage[] = [
{ role: "system", content: SYSTEM_PROMPT },
...history,
{ role: "user", content: userMessage },
]

const steps: AgentStep[] = []

// Agent 循环 - 持续执行直到得到最终响应
while (true) {
const response = await this.client.chat.completions.create({
model: this.model,
messages: messages as OpenAI.ChatCompletionMessageParam[],
tools: TOOLS as OpenAI.ChatCompletionTool[],
tool_choice: "auto",
})

const assistantMessage = response.choices[0].message

// 检查是否有工具调用
if (
assistantMessage.tool_calls &&
assistantMessage.tool_calls.length > 0
) {
// 记录 assistant 消息到历史
messages.push({
role: "assistant",
content: assistantMessage.content,
tool_calls: assistantMessage.tool_calls.map((tc) => ({
id: tc.id,
type: "function" as const,
function: {
name: tc.function.name,
arguments: tc.function.arguments,
},
})),
})

// 执行每个工具
for (const toolCall of assistantMessage.tool_calls) {
const funcName = toolCall.function.name
// 安全解析参数,处理空值情况
const argsString = toolCall.function.arguments || "{}"
const funcArgs = JSON.parse(argsString)

const step: AgentStep = {
type: "tool_call",
tool: funcName,
args: funcArgs,
}

// 执行工具函数
if (funcName in TOOL_FUNCTIONS) {
step.result = TOOL_FUNCTIONS[funcName](funcArgs)
} else {
step.result = `未知工具: ${funcName}`
}

steps.push(step)

// 添加工具结果到消息历史
messages.push({
role: "tool",
content: step.result,
tool_call_id: toolCall.id,
})
}
} else {
// 没有工具调用,返回最终响应
const finalResponse = assistantMessage.content || ""

steps.push({
type: "response",
content: finalResponse,
})

return {
response: finalResponse,
steps,
}
}
}
}

3.2 工具函数定义(OpenAI Function Calling)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// ============ 工具定义(OpenAI Function Calling 格式) ============

export const TOOLS: ToolDefinition[] = [
{
type: "function",
function: {
name: "get_current_time",
description: "获取当前的日期和时间",
parameters: {
type: "object",
properties: {},
required: [],
},
},
},
{
type: "function",
function: {
name: "calculator",
description:
"计算数学表达式,支持基本运算(+、-、*、/、^)和数学函数(sin、cos、sqrt、log 等)",
parameters: {
type: "object",
properties: {
expression: {
type: "string",
description:
"要计算的数学表达式,例如 '2 + 3 * 4' 或 'sqrt(16)' 或 '2^10'",
},
},
required: ["expression"],
},
},
},
{
type: "function",
function: {
name: "get_weather",
description: "获取指定城市的天气信息",
parameters: {
type: "object",
properties: {
city: {
type: "string",
description: "城市名称,例如 '北京'、'上海'",
},
},
required: ["city"],
},
},
},
]

3.3 工具函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/** 获取当前时间 */
function getCurrentTime(): string {
console.log("【getCurrentTime】")
const now = new Date()
return now.toLocaleString("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
})
}

/** 计算器 */
function calculator(expression: string): string {
console.log("【calculator】")
try {
const mathFunctions: Record<string, unknown> = {
sin: Math.sin,
cos: Math.cos,
tan: Math.tan,
sqrt: Math.sqrt,
abs: Math.abs,
round: Math.round,
floor: Math.floor,
ceil: Math.ceil,
log: Math.log,
log10: Math.log10,
pow: Math.pow,
PI: Math.PI,
pi: Math.PI,
E: Math.E,
e: Math.E,
}

// 支持 ^ 作为幂运算
const safeExpression = expression.replace(/\^/g, "**")

const func = new Function(
...Object.keys(mathFunctions),
`return ${safeExpression}`
)
const result = func(...Object.values(mathFunctions))

return String(result)
} catch (error) {
return `计算错误: ${error instanceof Error ? error.message : "未知错误"}`
}
}

/** 获取天气(模拟) */
function getWeather(city: string): string {
console.log("【getWeather】")
const weatherData: Record<string, string> = {
北京: "晴天,温度 15°C,空气质量良好",
上海: "多云,温度 18°C,有轻微雾霾",
广州: "小雨,温度 22°C,建议带伞",
深圳: "晴天,温度 24°C,适合户外活动",
杭州: "阴天,温度 16°C,体感舒适",
成都: "多云,温度 14°C,有小雨预警",
}
return weatherData[city] || `${city}:晴天,温度 20°C(模拟数据)`
}

总结

到这里一个 Agent 的简单实现就完成了,当然继续深入思考,还有很多需要优化的地方,比如:

  • 安全性校验
    • 输入限制
    • 工具执行权限控制
  • 数据持久存储
  • 监控和可观测性
    • 日志
    • 告警
    • 性能监控(响应时间、Token 消耗、错误率)
  • 考量工具函数直接抽离成单独的服务,或者说 MCP,减少耦合