AI Agent 的工程化实践全景图:从 prompt 工程到系统架构的完整知识体系

📅 2026/7/31 20:14:24
AI Agent 的工程化实践全景图:从 prompt 工程到系统架构的完整知识体系
AI Agent 的工程化实践全景图从 prompt 工程到系统架构的完整知识体系一、从调 API到造 Agent的认知跃迁今年 4 月我以为我已经懂了 AI Agent。毕竟我能用 langchain 搭一个简单的聊天机器人能调用工具能维护对话历史——这不就是 Agent 吗5 月份我把这个Agent集成到 dayuan 里作为智能文件搜索功能。用户说帮我找到最近一周修改过的、和数据库连接相关的配置文件。我的 Agent 的思考过程是理解用户意图 ✅决定调用find工具 ✅参数填错了--name写成了--pattern❌工具调用失败后不知道重试 ❌返回给用户一句没有找到相关文件 ❌那天晚上我读了一篇论文——Stanford 的《Generative Agents》里面有一句话点醒了我Agent 与简单 LLM 调用的核心区别在于Agent 有内部状态和自主决策能力**。**我的理解一直停留在把 LLM 的输出当命令来执行的层面而真正的 Agent 需要的是记忆、规划、反思、纠错——这些不只是代码逻辑更是系统架构。这篇文章是我从 4 月到 7 月从调 API 到设计和实现 dayuan Agent 系统的完整知识地图。二、AI Agent 的五层架构三、逐层拆解dayuan 的 Agent 实现第 1 层Prompt 工程 —— 不是写一段话是设计行为规范很多人理解的 prompt 工程是调几个形容词让 AI 回答得更好。但 Agent 的 System Prompt 是它的宪法——定义了 Agent 是谁、能做什么、不能做什么、决策框架是怎样的。/// dayuan 的 Agent System Prompt 结构 pub struct AgentPrompt { /// 角色定义Agent 的身份和职责边界 role: String, /// 能力边界Agent 能做什么更重要的是——不能做什么 capabilities: VecCapability, /// 工具使用协议如何使用工具、如何解释工具输出 tool_use_protocol: String, /// 输出格式要求期望 Agent 以什么格式输出 output_format: OutputFormat, /// 行为约束安全规则和伦理限制 constraints: VecConstraint, } impl AgentPrompt { /// 生成最终的 System Prompt /// 关键设计原则 /// ① 角色定义在前建立稳定的行为基线 /// ② 工具描述用 JSON Schema让 LLM 精确理解参数 /// ③ 行为约束用 MUST / MUST NOT不模糊 pub fn build_system_prompt(self) - String { format!( 你是一个 {role}。\n\ \n\ 你的职责\n\ {capabilities}\n\ \n\ 工具使用规则\n\ {tool_protocol}\n\ \n\ 行为约束违反将导致输出被拒绝\n\ {constraints}\n\ \n\ 输出格式{output_format}, role self.role, capabilities self.format_capabilities(), tool_protocol self.tool_use_protocol, constraints self.format_constraints(), output_format self.output_format.to_instruction(), ) } }第 2 层工具集成 —— MCP 协议的统一接口工具的定义不能只是一段自然语言描述。LLM 需要精确知道工具名、参数类型、参数约束。我选择了 MCPModel Context Protocol作为 dayuan 的工具集成标准。/// MCP 工具定义用 JSON Schema 精确描述工具 #[derive(Debug, Serialize, Deserialize)] pub struct ToolDefinition { /// 工具唯一标识名 pub name: String, /// 人类可读的描述 pub description: String, /// 参数 JSON Schema pub input_schema: serde_json::Value, } /// 文件搜索工具的 MCP 定义 pub fn file_search_tool() - ToolDefinition { ToolDefinition { name: search_files.to_string(), description: 在项目目录中搜索匹配模式的文件返回文件路径列表.to_string(), input_schema: serde_json::json!({ type: object, properties: { pattern: { type: string, description: glob 搜索模式如 **/*.rs 或 src/**/*.toml }, content_regex: { type: string, description: 可选的文件内容正则匹配如 database.*connect }, max_results: { type: integer, default: 20, description: 返回的最大文件数 } }, required: [pattern] // pattern 是必填参数 }), } } /// 工具调用执行器解析 LLM 的工具调用请求 → 执行 → 返回结果 pub async fn execute_tool_call( tool_name: str, arguments: serde_json::Value, project_root: Path, ) - ResultToolResult, ToolError { match tool_name { search_files { // ① 解析参数 let pattern arguments[pattern] .as_str() .ok_or(ToolError::MissingArg(pattern))?; // ② 执行搜索 let files glob_search(project_root, pattern)?; // ③ 可选内容过滤 let files if let Some(regex) arguments.get(content_regex) { filter_by_content(files, regex.as_str().unwrap())? } else { files }; // ④ 限制结果数量避免上下文过载 let max arguments[max_results].as_u64().unwrap_or(20) as usize; let files: Vec_ files.into_iter().take(max).collect(); Ok(ToolResult { success: true, data: serde_json::to_value(files)?, summary: format!(找到 {} 个匹配文件, files.len()), }) } _ Err(ToolError::UnknownTool(tool_name.to_string())), } }第 3 层记忆系统 —— ReAct 循环中的状态管理Agent 不是无状态的函数调用。它需要记住我刚才问了什么、刚才那步操作的结果是什么、用户的历史偏好是什么。/// Agent 的工作记忆ReAct 循环中的状态 pub struct AgentMemory { /// 对话历史支持滑动窗口避免 token 超限 messages: VecDequeChatMessage, /// 当前任务上下文本次任务的关键信息 task_context: TaskContext, /// 工具调用历史上次调了什么、什么结果 tool_call_history: VecToolCallRecord, /// 最大对话轮数防止无限循环 max_turns: usize, } impl AgentMemory { /// 添加一轮对话超出上限自动裁剪最早的 pub fn push_message(mut self, msg: ChatMessage) { self.messages.push_back(msg); // 滑动窗口超出上限时从最早的消息开始裁剪 // 但保留 System Prompt第一条消息 while self.messages.len() self.max_turns { if self.messages.len() 1 { self.messages.remove(1); // 跳过 index 0 的 System Prompt } } } /// 构建发给 LLM 的消息历史 pub fn build_context(self, current_task: str) - VecChatMessage { let mut context: VecChatMessage self.messages.iter().cloned().collect(); // 注入当前任务的额外上下文 context.push(ChatMessage::system(format!( 当前任务上下文项目 {project}分支 {branch}, project self.task_context.project_name, branch self.task_context.current_branch, ))); context } } /// 工具调用记录供 Agent 反思和纠错 #[derive(Debug, Clone)] pub struct ToolCallRecord { pub tool_name: String, pub arguments: serde_json::Value, pub result: ToolResult, pub timestamp: chrono::DateTimechrono::Utc, pub retry_count: u32, }第 4 层推理与规划 —— 让 Agent 先想再做这是 Agent 和简单 LLM 调用最大的分水岭。传统的用户问 → LLM 答是单步推理而 Agent 需要分析意图用户到底想要什么分解任务这个问题需要分几步选择工具每步需要用什么工具执行并观察工具返回了什么反思并调整结果对吗需要重试吗汇总输出把中间步骤的结果整合成用户的答案这个过程就是ReAct (Reasoning Acting)循环。/// ReAct 循环Agent 的核心决策逻辑 pub async fn react_loop( agent: mut Agent, user_input: str, memory: mut AgentMemory, ) - ResultString, AgentError { // 添加用户消息到记忆 memory.push_message(ChatMessage::user(user_input)); for turn in 0..memory.max_turns { // ① 构建上下文包括历史、任务信息、可用工具列表 let context memory.build_context(user_input); let tools agent.get_available_tools(); // ② 调用 LLM 获取下一个动作 let response agent.llm.chat_with_tools(context, tools).await?; match response.action { // ③ 如果 LLM 认为任务已完返回最终答案 AgentAction::FinalAnswer(answer) { memory.push_message(ChatMessage::assistant(answer)); return Ok(answer); } // ④ 如果 LLM 需要调用工具执行工具并记录结果 AgentAction::ToolCall { name, arguments } { let result execute_tool_call(name, arguments, agent.project_root).await; // 记录工具调用 memory.tool_call_history.push(ToolCallRecord { tool_name: name.clone(), arguments: arguments.clone(), result: result.clone(), timestamp: chrono::Utc::now(), retry_count: 0, }); // 将工具执行结果作为新的消息注入记忆 memory.push_message(ChatMessage::tool_result(name, result)); } } } Err(AgentError::MaxTurnsExceeded(memory.max_turns)) }第 5 层多 Agent 协作 —— dayuan 的三角色模式dayuan 的设计中有三个专业化 Agent四、我在 Agent 工程化中踩过的最大的三个坑坑一工具描述写得太模糊。LLM 需要精确的 JSON Schema而不是用 find 命令搜索文件。模糊描述会导致 LLM 编造参数、遗漏必填项。坑二没有设置最大步数。一次用户说帮我重构整个项目Agent 跑了一个小时还没停产生了 200 次工具调用。现在 dayuan 默认最大 15 步超出就问用户要继续吗坑三工具输出太大。搜索工具返回了 500 个文件路径把上下文窗口直接撑爆。现在每个工具返回都带truncated标志和结果数量限制。五、总结从调 API 到造 Agent核心的认知跃迁是Agent 不是更聪明的 LLM而是被 LLM 驱动的软件系统。这意味着你需要考虑的不仅是 prompt 怎么写还有状态管理、错误恢复、资源限制、安全边界——这些是系统架构的问题不是 NLP 的问题。对于同学我的建议是不要一上来就做多 Agent 系统。从 ReAct 循环开始——一个 LLM 两个工具 对话记忆——把这个最简系统调稳了再往上叠复杂度。所有伟大的框架LangChain、AutoGPT、CrewAI底层都是同样的核心循环。理解这个循环你就理解了 Agent 的本质。资料说明本文中的协议、版本、性能、成本和行业趋势应以可核验的一手资料为准。未标注统计口径的比例、时间表和预测仅作工程讨论不应视为行业事实。可参考 0731 资料来源索引并在发布前将具体来源贴到对应断言之后。