从零到一实现 nano-agent(五):Agent 循环与对话管理
2024-11-24·8 分钟阅读
从零到一实现 nano-agent(五):Agent 核心循环
前言
Agent 核心循环是 AI Coding Agent 的心脏。它实现了 ReAct(Reasoning + Acting)模式:模型先思考,然后采取行动(调用工具),观察结果,再继续思考。本章将实现完整的 Agent 循环,整合 Provider 和 Tool 系统。
技术亮点
| 技术点 | 难度 | 面试价值 | 本文覆盖 |
|---|---|---|---|
| ReAct 模式实现 | ⭐⭐⭐⭐ | Agent 核心原理 | ✅ |
| 流式响应处理 | ⭐⭐⭐⭐ | 异步编程能力 | ✅ |
| 工具调用循环 | ⭐⭐⭐⭐ | 状态机设计 | ✅ |
| 消息状态管理 | ⭐⭐⭐ | 数据结构设计 | ✅ |
面试考点
- ReAct 模式的核心流程是什么?
- 如何处理 LLM 返回的多个工具调用?
- Agent 循环如何避免无限迭代?
设计思路:为什么 Agent 需要循环?
问题背景
传统程序是确定性的:给定输入,输出是确定的。但 AI Agent 不同:
┌─────────────────────────────────────────────────────────────────────┐
│ 传统程序 vs AI Agent │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 传统程序(确定性): │
│ 输入 ──▶ 处理 ──▶ 输出 │
│ "读取 package.json" ──▶ readFile() ──▶ 文件内容 │
│ │
│ AI Agent(不确定性): │
│ 输入 ──▶ LLM 思考 ──▶ 可能调用工具 ──▶ 观察结果 ──▶ 继续思考? │
│ │
│ 示例:用户说 "帮我分析这个项目的依赖" │
│ │
│ 第一轮:LLM 思考 "需要先读取 package.json" │
│ LLM 决定调用 read({ path: "package.json" }) │
│ 工具返回文件内容 │
│ │
│ 第二轮:LLM 观察结果,思考 "发现依赖列表,需要分析每个依赖" │
│ LLM 决定调用 grep({ pattern: "import.*from" }) │
│ 工具返回导入语句 │
│ │
│ 第三轮:LLM 观察结果,思考 "分析完成,可以回复用户" │
│ LLM 输出最终答案 │
│ │
│ 关键:LLM 需要多次思考-行动-观察,直到任务完成 │
│ │
└─────────────────────────────────────────────────────────────────────┘
ReAct 模式的本质
ReAct(Reasoning + Acting)模式的核心思想:
┌─────────────────────────────────────────────────────────────────────┐
│ ReAct 本质 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Reasoning(思考): │
│ - 分析当前情况 │
│ - 决定下一步行动 │
│ - 选择合适的工具 │
│ │
│ Acting(行动): │
│ - 执行工具调用 │
│ - 获取执行结果 │
│ │
│ Observation(观察): │
│ - 分析工具返回的结果 │
│ - 更新对任务的理解 │
│ - 决定是否继续 │
│ │
│ 循环终止条件: │
│ 1. LLM 不再调用工具,直接输出答案 │
│ 2. 达到最大迭代次数 │
│ 3. 发生错误 │
│ │
└─────────────────────────────────────────────────────────────────────┘
为什么需要最大迭代次数?
┌─────────────────────────────────────────────────────────────────────┐
│ 无限循环的风险 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 风险场景 1:LLM 陷入死循环 │
│ - LLM 不断重复调用同一个工具 │
│ - 消耗大量 Token 和费用 │
│ │
│ 风险场景 2:任务无法完成 │
│ - 用户给出不可能完成的任务 │
│ - LLM 永远在尝试 │
│ │
│ 风险场景 3:工具返回异常 │
│ - LLM 不断重试失败的工具 │
│ │
│ 解决方案:设置合理的最大迭代次数(如 20 次) │
│ - 大多数任务在 10 次内完成 │
│ - 复杂任务可能需要更多 │
│ - 超过限制时强制终止,返回当前结果 │
│ │
└─────────────────────────────────────────────────────────────────────┘
方案对比:Agent 循环实现
方案一:简单循环
async function runLoop(userInput: string) {
const messages = [{ role: "user", content: userInput }]
while (true) { // 危险:可能无限循环
const response = await llm.chat(messages)
if (response.toolCalls) {
for (const call of response.toolCalls) {
const result = await executeTool(call)
messages.push({ role: "tool", content: result })
}
} else {
return response.content
}
}
}
优点:简单直观
缺点:无迭代限制、无错误处理、无权限检查
结论:仅适用于原型验证
方案二:带限制的循环(本文方案)
async function runLoop(userInput: string, maxIterations = 20) {
const messages = [{ role: "user", content: userInput }]
for (let i = 0; i < maxIterations; i++) {
try {
const response = await llm.chat(messages)
if (!response.toolCalls?.length) {
return response.content
}
for (const call of response.toolCalls) {
if (!await checkPermission(call)) continue
const result = await executeTool(call)
messages.push({ role: "tool", content: result })
}
} catch (error) {
// 错误处理
}
}
return "达到最大迭代次数"
}
优点:安全限制、错误处理、权限检查
缺点:代码量增加
结论:推荐用于生产环境
方案三:状态机模式
enum State { IDLE, THINKING, EXECUTING, DONE, ERROR }
class AgentStateMachine {
private state: State = State.IDLE
async run(input: string) {
while (this.state !== State.DONE && this.state !== State.ERROR) {
switch (this.state) {
case State.IDLE:
this.state = State.THINKING
break
case State.THINKING:
// ...
break
}
}
}
}
优点:状态清晰,易于调试
缺点:复杂度高,对于简单场景过度设计
结论:适用于复杂状态转换场景
常见陷阱与解决方案
陷阱一:消息历史格式错误导致 LLM 混淆
问题描述:
// 错误:工具结果没有正确关联到工具调用
messages.push({ role: "assistant", content: "调用工具..." })
messages.push({ role: "user", content: "工具结果..." }) // 应该是 tool 类型
// LLM 不知道这是工具调用结果,可能误解为用户输入
解决方案:使用正确的消息类型
// 正确:使用 tool_use 和 tool_result
messages.push({
role: "assistant",
content: [
{ type: "text", text: "我来读取文件..." },
{ type: "tool_use", id: "1", name: "read", input: { path: "..." } }
]
})
messages.push({
role: "user",
content: [
{ type: "tool_result", tool_use_id: "1", content: "文件内容..." }
]
})
陷阱二:并行工具调用的结果顺序问题
问题描述:
// LLM 可能一次返回多个工具调用
const toolCalls = [
{ id: "1", name: "read", input: { path: "/a.ts" } },
{ id: "2", name: "read", input: { path: "/b.ts" } },
]
// 错误:按顺序执行,浪费时间
for (const call of toolCalls) {
results.push(await executeTool(call))
}
// 错误:并行执行,但结果顺序不对应
const results = await Promise.all(toolCalls.map(executeTool))
// results[0] 可能对应 toolCalls[1] 的结果
解决方案:保持 ID 关联
// 使用 Promise.allSettled 并行执行
const settled = await Promise.allSettled(
toolCalls.map(call => executeTool(call))
)
// 构建正确关联的结果
const toolResults = settled.map((result, i) => ({
type: "tool_result",
tool_use_id: toolCalls[i].id, // 使用原始 ID
content: result.status === "fulfilled" ? result.value.output : result.reason.message,
}))
陷阱三:工具执行失败时没有正确反馈给 LLM
问题描述:
// 错误:工具失败时直接抛出异常,中断整个循环
const result = await executeTool(call) // 抛出异常
// 结果:整个 Agent 循环中断
解决方案:将错误作为结果返回给 LLM
try {
const result = await executeTool(call)
messages.push({
role: "user",
content: [{
type: "tool_result",
tool_use_id: call.id,
content: result.output,
}]
})
} catch (error) {
// 让 LLM 知道发生了错误,可以尝试其他方案
messages.push({
role: "user",
content: [{
type: "tool_result",
tool_use_id: call.id,
content: `Error: ${error.message}`,
is_error: true, // 标记为错误
}]
})
}
陷阱四:忘记处理 LLM 返回的文本内容
问题描述:
// LLM 可能同时返回文本和工具调用
{
content: "让我先读取文件内容...",
toolCalls: [{ name: "read", ... }]
}
// 错误:只处理工具调用,忽略文本
if (response.toolCalls) {
// 处理工具调用,忽略 response.content
}
// 结果:用户看不到 LLM 的思考过程
解决方案:同时记录文本内容
const assistantBlocks = []
// 先记录文本
if (response.content) {
assistantBlocks.push({ type: "text", text: response.content })
callbacks.onEvent?.({ type: "text", content: response.content })
}
// 再记录工具调用
if (response.toolCalls) {
for (const call of response.toolCalls) {
assistantBlocks.push({
type: "tool_use",
id: call.id,
name: call.name,
input: call.input,
})
}
}
// 完整的消息
messages.push({ role: "assistant", content: assistantBlocks })
陷阱五:上下文爆炸导致超出 Token 限制
问题描述:
随着对话进行,消息历史越来越长:
第 1 轮:1000 tokens
第 5 轮:5000 tokens
第 10 轮:15000 tokens // 超出模型限制!
解决方案:上下文压缩或截断
// 策略 1:保留最近 N 条消息
if (messages.length > MAX_MESSAGES) {
messages = [messages[0], ...messages.slice(-MAX_MESSAGES + 1)]
}
// 策略 2:压缩历史消息为摘要
if (totalTokens > CONTEXT_LIMIT * 0.8) {
const summary = await summarize(messages.slice(0, -5))
messages = [
{ role: "assistant", content: `[历史摘要] ${summary}` },
...messages.slice(-5)
]
}
// 策略 3:使用支持长上下文的模型
// 或者让用户手动清除历史
ReAct 模式解析
什么是 ReAct?
ReAct(Reasoning + Acting)是 AI Agent 的核心范式:
┌─────────────────────────────────────────────────────────────┐
│ ReAct Loop │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ │
│ │ 用户 │ │
│ │ 输入 │ │
│ └────┬────┘ │
│ │ │
│ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Reason │────▶│ Act │────▶│ Observe │ │
│ │ (思考) │ │ (行动) │ │ (观察) │ │
│ └─────────┘ └─────────┘ └────┬────┘ │
│ ▲ │ │
│ │ ┌─────────┐ │ │
│ └─────────│继续思考 │◀──────────┘ │
│ └─────────┘ │
│ │
│ 示例: │
│ 用户: "帮我读取 package.json 的内容" │
│ │
│ Reason: 用户想查看 package.json,我需要使用 read 工具 │
│ Act: 调用 read({ path: "/project/package.json" }) │
│ Observe: 返回文件内容 │
│ Reason: 已获取内容,可以回复用户 │
│ Act: 返回结果给用户 │
│ │
└─────────────────────────────────────────────────────────────┘
消息流转
┌─────────────────────────────────────────────────────────────┐
│ Message Flow │
├─────────────────────────────────────────────────────────────┤
│ │
│ 消息列表: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ [0] user: "帮我读取 package.json" │ │
│ │ │ │
│ │ [1] assistant: [ │ │
│ │ { type: "text", text: "好的,我来读取..." }, │ │
│ │ { type: "tool_use", id: "1", name: "read", │ │
│ │ input: { path: "/project/package.json" } } │ │
│ │ ] │ │
│ │ │ │
│ │ [2] user: [ │ │
│ │ { type: "tool_result", tool_use_id: "1", │ │
│ │ content: "文件内容..." } │ │
│ │ ] │ │
│ │ │ │
│ │ [3] assistant: "package.json 的内容如下:..." │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Agent 接口定义
// src/agent/agent.ts
import { registry, ChatMessage, ContentBlock, calculateCost } from "../provider"
import { toolRegistry } from "../tool"
import { PermissionManager, PermissionRequest } from "../permission"
import { Logger } from "../util/logger"
const log = Logger.create({ service: "agent" })
/**
* Agent 配置
*/
export interface AgentConfig {
model: string // 模型名称
provider: string // 提供商
systemPrompt?: string // 系统提示词
temperature?: number // 温度参数
maxTokens?: number // 最大输出 Token
permission: PermissionManager // 权限管理器
workingDirectory: string // 工作目录
}
/**
* Agent 状态
*/
export interface AgentState {
messages: ChatMessage[] // 消息历史
totalTokens: { input: number; output: number } // Token 统计
totalCost: number // 累计成本
}
/**
* Agent 事件类型
*/
export interface AgentEvent {
type: "text" | "tool_use" | "tool_result" | "error" | "done"
content?: string
toolName?: string
toolInput?: Record<string, unknown>
toolResult?: string
error?: string
}
/**
* Agent 回调函数
*/
export interface AgentCallbacks {
onEvent?: (event: AgentEvent) => void // 事件回调
onToken?: (token: string) => void // 流式 Token 回调
onToolCall?: (name: string, input: Record<string, unknown>) => Promise<boolean> // 工具确认
}
Agent 类实现
构造函数和基本方法
// src/agent/agent.ts (续)
export class Agent {
private provider: ReturnType<typeof registry.get>
private state: AgentState
constructor(private config: AgentConfig) {
// 获取 Provider 实例
const p = registry.get(config.provider)
if (!p) {
throw new Error(`Provider not found: ${config.provider}`)
}
this.provider = p
// 初始化状态
this.state = {
messages: [],
totalTokens: { input: 0, output: 0 },
totalCost: 0,
}
}
/**
* 发送消息
*/
async sendMessage(content: string, callbacks: AgentCallbacks = {}): Promise<string> {
// 添加用户消息
this.state.messages.push({ role: "user", content })
// 运行循环
return this.runLoop(callbacks)
}
/**
* 获取当前状态
*/
getState(): AgentState {
return { ...this.state }
}
/**
* 重置状态
*/
reset(): void {
this.state = {
messages: [],
totalTokens: { input: 0, output: 0 },
totalCost: 0,
}
}
}
核心循环实现
// src/agent/agent.ts (续)
/**
* 核心 ReAct 循环
*/
private async runLoop(callbacks: AgentCallbacks): Promise<string> {
let finalResponse = ""
let iterations = 0
const maxIterations = 20 // 防止无限循环
while (iterations < maxIterations) {
iterations++
try {
// 调用 LLM
const response = await this.callLLM(callbacks.onToken)
// 更新 Token 统计
if (response.usage) {
this.state.totalTokens.input += response.usage.inputTokens
this.state.totalTokens.output += response.usage.outputTokens
this.state.totalCost += calculateCost(
this.config.model,
response.usage.inputTokens,
response.usage.outputTokens
)
}
// 构建助手消息的内容块
const assistantBlocks: ContentBlock[] = []
// 处理文本内容
if (response.content) {
assistantBlocks.push({ type: "text", text: response.content })
callbacks.onEvent?.({ type: "text", content: response.content })
finalResponse = response.content
}
// 处理工具调用
if (response.toolCalls && response.toolCalls.length > 0) {
const toolResults: ContentBlock[] = []
for (const call of response.toolCalls) {
// 记录工具调用
assistantBlocks.push({
type: "tool_use",
id: call.id,
name: call.name,
input: call.input,
})
callbacks.onEvent?.({
type: "tool_use",
toolName: call.name,
toolInput: call.input,
})
// 检查权限
const approved = await this.checkPermission(call.name, call.input, callbacks)
if (!approved) {
toolResults.push({
type: "tool_result",
tool_use_id: call.id,
content: "Tool call was not approved",
is_error: true,
})
continue
}
// 执行工具
try {
const result = await this.executeTool(call.name, call.input)
toolResults.push({
type: "tool_result",
tool_use_id: call.id,
content: result.output,
})
callbacks.onEvent?.({
type: "tool_result",
toolName: call.name,
toolResult: result.output,
})
log.info("Tool executed", { tool: call.name })
} catch (error) {
const msg = error instanceof Error ? error.message : String(error)
toolResults.push({
type: "tool_result",
tool_use_id: call.id,
content: `Error: ${msg}`,
is_error: true,
})
callbacks.onEvent?.({ type: "error", error: msg })
}
}
// 添加助手消息(包含工具调用)
this.state.messages.push({ role: "assistant", content: assistantBlocks })
// 添加用户消息(工具结果)
this.state.messages.push({ role: "user", content: toolResults })
// 继续循环,让 LLM 处理工具结果
continue
}
// 没有工具调用,循环结束
this.state.messages.push({
role: "assistant",
content: assistantBlocks.length === 1 && assistantBlocks[0].type === "text"
? assistantBlocks[0].text
: assistantBlocks,
})
callbacks.onEvent?.({ type: "done" })
return finalResponse
} catch (error) {
const msg = error instanceof Error ? error.message : String(error)
callbacks.onEvent?.({ type: "error", error: msg })
throw error
}
}
// 达到最大迭代次数
callbacks.onEvent?.({ type: "done" })
return finalResponse
}
LLM 调用
// src/agent/agent.ts (续)
/**
* 调用 LLM(流式)
*/
private async callLLM(onToken?: (token: string) => void): Promise<{
content: string
toolCalls?: Array<{ id: string; name: string; input: Record<string, unknown> }>
usage?: { inputTokens: number; outputTokens: number }
}> {
// 获取工具定义
const tools = toolRegistry.toLLMTools()
let content = ""
let toolCalls: Array<{ id: string; name: string; input: Record<string, unknown> }> = []
let usage: { inputTokens: number; outputTokens: number } | undefined
try {
// 流式调用
for await (const event of this.provider!.chatStream({
model: this.config.model,
messages: this.state.messages,
system: this.config.systemPrompt ?? this.getDefaultSystemPrompt(),
tools: tools.length > 0 ? tools : undefined,
maxTokens: this.config.maxTokens ?? 4096,
temperature: this.config.temperature,
onToken: (token) => {
content += token
onToken?.(token)
},
})) {
if (event.type === "tool_use") {
toolCalls.push({
id: event.id,
name: event.name,
input: event.input
})
} else if (event.type === "done" && event.usage) {
usage = event.usage
}
}
} catch (error) {
log.error("LLM call failed", {
error: error instanceof Error ? error.message : String(error)
})
throw error
}
return {
content,
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
usage
}
}
权限检查
// src/agent/agent.ts (续)
/**
* 检查工具调用权限
*/
private async checkPermission(
toolName: string,
input: Record<string, unknown>,
callbacks: AgentCallbacks
): Promise<boolean> {
const request: PermissionRequest = {
tool: toolName,
params: input,
patterns: this.extractPatterns(toolName, input),
}
const action = this.config.permission.check(request)
if (action === "deny") return false
if (action === "allow") return true
// 需要确认
if (callbacks.onToolCall) {
return callbacks.onToolCall(toolName, input)
}
return true
}
/**
* 从工具参数中提取敏感模式
*/
private extractPatterns(tool: string, params: Record<string, unknown>): string[] | undefined {
// 文件操作:提取路径
if (["read", "write", "edit"].includes(tool) && params.path) {
return [String(params.path)]
}
// Shell 命令:提取命令
if (tool === "bash" && params.command) {
return [String(params.command)]
}
return undefined
}
工具执行
// src/agent/agent.ts (续)
/**
* 执行工具
*/
private async executeTool(name: string, params: Record<string, unknown>) {
const tool = toolRegistry.get(name)
if (!tool) {
throw new Error(`Unknown tool: ${name}`)
}
// Zod 参数验证
const validatedParams = tool.parameters.parse(params)
// 执行工具
return tool.execute(validatedParams, {
sessionId: "default",
messageId: "",
workingDirectory: this.config.workingDirectory,
abortSignal: new AbortController().signal,
})
}
默认系统提示词
// src/agent/agent.ts (续)
/**
* 获取默认系统提示词
*/
private getDefaultSystemPrompt(): string {
return `You are a helpful AI coding assistant with access to powerful tools.
## Available Tools
### File Operations
- read: Read file contents
- write: Write to files
- edit: Make precise edits to files
- glob: Find files by pattern
### Code Search
- grep: Search for patterns in files using regex
### Execution
- bash: Execute shell commands
### Parallel Execution
- batch: Execute multiple tools in parallel
### Multi-Agent Collaboration
- task: Delegate work to specialized subagents
### Skills
- skill: Load specialized domain knowledge
## Guidelines
- Always use absolute paths
- Be careful with destructive operations (write, edit, bash)
- Explain what you're doing before taking actions
- Use 'batch' tool for parallel file operations
- Use 'task' tool to delegate complex work to subagents`
}
状态流转图
┌─────────────────────────────────────────────────────────────┐
│ Agent State Machine │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ │
│ │ IDLE │◀────────────────────────────────┐ │
│ └────┬────┘ │ │
│ │ sendMessage() │ │
│ ▼ │ │
│ ┌─────────┐ │ │
│ │ PENDING │ │ │
│ │ MESSAGE │ │ │
│ └────┬────┘ │ │
│ │ runLoop() │ │
│ ▼ │ │
│ ┌─────────┐ ┌─────────┐ │ │
│ │ CALLING │────▶│ PARSING │ │ │
│ │ LLM │ │ RESPONSE │ │ │
│ └─────────┘ └────┬────┘ │ │
│ │ │ │
│ ┌──────────┴──────────┐ │ │
│ ▼ ▼ │ │
│ ┌─────────┐ ┌─────────┐ │ │
│ │ EXECUTE │ │ DONE │───────┘ │
│ │ TOOLS │ │ │ │
│ └────┬────┘ └─────────┘ │
│ │ │
│ │ tool_results │
│ │ │
│ └──────────────▶ [Continue Loop] │
│ │
└─────────────────────────────────────────────────────────────┘
使用示例
import { Agent } from './agent'
import { PermissionManager, DEFAULT_RULES } from './permission'
// 创建权限管理器
const permission = new PermissionManager(DEFAULT_RULES)
// 创建 Agent
const agent = new Agent({
model: 'gpt-4o-mini',
provider: 'openai',
workingDirectory: '/Users/example/project',
permission,
})
// 发送消息
const response = await agent.sendMessage(
'帮我读取 package.json 并告诉我项目的依赖',
{
onEvent: (event) => {
switch (event.type) {
case 'text':
process.stdout.write(event.content!)
break
case 'tool_use':
console.log(`\n[Using tool: ${event.toolName}]`)
break
case 'tool_result':
console.log(`\n[Tool result received]`)
break
case 'done':
console.log('\n[Done]')
break
}
},
onToolCall: async (name, input) => {
// 自定义权限确认
console.log(`\nConfirm tool call: ${name}`)
console.log(`Input:`, input)
// 返回 true 允许,false 拒绝
return true
},
}
)
// 获取统计信息
const state = agent.getState()
console.log(`Tokens: ${state.totalTokens.input} in / ${state.totalTokens.output} out`)
console.log(`Cost: $${state.totalCost.toFixed(6)}`)
错误处理
常见错误类型
// 错误处理示例
try {
const response = await agent.sendMessage(userInput, callbacks)
} catch (error) {
if (error instanceof Error) {
// API 错误
if (error.message.includes('API key')) {
console.error('Invalid API key')
}
// 速率限制
else if (error.message.includes('rate limit')) {
console.error('Rate limit exceeded, please wait')
}
// 模型错误
else if (error.message.includes('model')) {
console.error('Model not available')
}
// 工具错误
else {
console.error('Agent error:', error.message)
}
}
}
重试策略
async function sendMessageWithRetry(
agent: Agent,
content: string,
callbacks: AgentCallbacks,
maxRetries = 3
): Promise<string> {
let lastError: Error | null = null
for (let i = 0; i < maxRetries; i++) {
try {
return await agent.sendMessage(content, callbacks)
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error))
// 判断是否可重试
if (isRetryableError(lastError)) {
console.log(`Retry ${i + 1}/${maxRetries}...`)
await sleep(1000 * (i + 1)) // 指数退避
continue
}
throw lastError
}
}
throw lastError
}
function isRetryableError(error: Error): boolean {
const retryableMessages = ['rate limit', 'timeout', 'network', 'ECONNRESET']
return retryableMessages.some(msg =>
error.message.toLowerCase().includes(msg)
)
}
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
小结
本章实现了 Agent 的核心 ReAct 循环,包括:
- 消息管理 - 维护对话历史和状态
- LLM 调用 - 流式响应处理和 Token 统计
- 工具执行 - 权限检查和结果处理
- 循环控制 - 最大迭代次数防止无限循环
关键要点:
- ReAct 模式是 Agent 的核心范式:思考 → 行动 → 观察
- 消息历史需要正确维护,包含工具调用和结果
- 流式响应提供更好的用户体验
- 权限检查是安全的关键环节
下一章我们将深入权限控制系统,实现更细粒度的权限管理。
参考资料
相关文章
从零到一实现 nano-agent(一):项目概述与架构设计
2024-10-09·5 分钟阅读
深入分析 AI 编程助手的核心架构,设计 nano-agent 项目的技术选型和核心模块,为构建生产级 AI Coding Agent 奠定基础。
Langchain4J 实战教程(八):Tools 与 Agent 开发
2025-06-23·4 分钟阅读
深入掌握 Function Calling 和 Agent 开发的核心技术,学习工具定义、多工具协作及复杂 Agent 系统的设计与实现。
从零到一实现 nano-agent(八):子 Agent 协作
2024-12-26·7 分钟阅读
实现多 Agent 协作系统,支持任务委托、SubAgent 架构和专业 Agent 类型,构建强大的 Agent 编排能力。