简介:这份PDF文档系统梳理了华为首次提出的智能体参考架构,面向关注政企智能升级、新型智慧城市建设的从业者与研究者。内容从智能体的定义与关键特征切入,详解云网边端协同这一核心特点,并逐层拆解智能交互、智能联接、智能中枢、智慧应用四层架构,同时延伸至全场景智慧城市、智慧企业、智慧行业三个层面,以及新基建与智能体的内在关系。文档还收录了鹏城智能体、智慧政务、智慧气象等落地案例,帮助读者理解智能体在政府、交通、能源、金融等行业的实际应用路径。资源为单个PDF文件,压缩包约503KB,篇幅精炼、结构清晰,适合作为快速了解智能体顶层设计与建设蓝图的入门参考。目前已有75人学习,便于在通勤或碎片时间完成一次体系化认知梳理。
1. 什么是智能体.pdf:一份文档标题背后的技术路线图
第一次看到「什么是智能体.pdf」这个标题,我下意识以为是谁把一份科普讲义丢进了共享目录。点开之后才发现,这类文档真正有价值的地方不在「什么是」,而在它逼着我们把一个模糊概念拆成可落地的工程结构。智能体(Agent)这个词最近被用得很泛,产品经理嘴里的智能体、论文里的智能体、运维脚本里自称的智能体,往往不是一回事。这份文档标题之所以值得单独拿出来讲,是因为它指向一个具体诉求:把「智能体」从概念变成一份能读、能改、能跑的技术方案。
我带的几个做自动化的同事,最近都在问同一个问题——大模型已经能对话了,为什么还要套一层「智能体」?答案藏在任务闭环里。对话模型只负责生成,智能体负责「感知—决策—执行—反馈」这条链路。文档标题里的「.pdf」其实是个信号:它大概率是一份架构说明或选型笔记,而不是可运行代码。所以这篇笔记我不打算复述某份不存在的原文,而是顺着这个标题,把智能体系统的领域边界、最小可跑结构和落地路径讲清楚,让新手能照着搭,熟手能看到参数和边界。
2. 智能体的四层结构:从感知到执行的拆解
2.1 为什么「大模型 + 工具调用」不等于智能体
很多人把函数调用(Function Calling)当成智能体的全部,这是个典型误解。函数调用解决的是「模型知道该调哪个接口」,但智能体要解决的是「在多轮、多工具、状态会变的环境里,怎么保证任务不跑偏」。区别在于三个东西:状态管理、循环控制、失败恢复。
举个具体场景。用户说「帮我把这周的销售数据整理成报表并发给负责人」。纯函数调用模式下,模型可能一次性输出「调用查询接口 + 调用报表生成 + 调用邮件接口」三个动作,但它不知道第一步返回的数据格式是否匹配第二步的输入。智能体模式下,系统会维护一个任务状态机:查询完成 → 校验字段 → 生成报表 → 确认收件人 → 发送。每一步的结果写回上下文,下一步基于真实返回值决策。这个「基于真实返回值决策」就是智能体和普通工具调用的分水岭。
从工程角度看,智能体至少包含四个可独立测试的模块:感知层(输入解析与意图识别)、决策层(规划与工具选择)、执行层(工具调用与副作用控制)、记忆层(短期上下文与长期存储)。这四层里,记忆层最容易被忽略,也最容易在长任务里翻车。我见过一个跑了二十多轮的客服智能体,因为没做上下文压缩,到第十五轮开始重复问用户已经回答过的问题,这就是记忆层设计缺失的典型症状。
2.2 最小可跑智能体的目录与依赖
要复现一个能用的智能体,不需要一上来就上框架。我一般先用 Python 搭一个两百行以内的最小版本,把循环和状态跑通,再考虑引入编排框架。下面是一个最小项目的目录结构,每个文件职责单一,方便替换。
agent_min/ ├── main.py # 入口,负责循环控制 ├── planner.py # 决策层:根据状态选下一步动作 ├── tools.py # 执行层:所有可调用工具的注册与实现 ├── memory.py # 记忆层:上下文裁剪与持久化 └── config.yaml # 参数集中管理,避免硬编码依赖方面,核心只有三样:一个大模型 SDK、一个 HTTP 客户端、一个配置解析库。不要急着装向量数据库,最小版本用列表加滑动窗口就够了。等任务轮次超过三十轮、或者需要跨会话记忆时,再引入向量检索。
# main.py 核心循环,省略了异常兜底,后面章节会补 import yaml from planner import Planner from tools import ToolRegistry from memory import Memory def run_agent(user_input: str, max_steps: int = 8): cfg = yaml.safe_load(open("config.yaml")) memory = Memory(window_size=cfg["memory"]["window"]) tools = ToolRegistry() planner = Planner(model=cfg["model"]["name"], tools=tools.list_names()) memory.add("user", user_input) for step in range(max_steps): # 决策层拿到当前上下文,输出下一步动作 action = planner.next_action(memory.context()) if action["type"] == "finish": return action["payload"] # 执行层真正调用工具,结果写回记忆 result = tools.call(action["name"], action["args"]) memory.add("tool", result) return "达到最大步数仍未完成"这段代码里,max_steps是最关键的参数。设太小,复杂任务做不完;设太大,一旦决策层陷入循环,会白白烧掉调用额度。我的经验值是:单工具任务设 3 到 5,多工具协作设 8 到 12,超过 15 步还没收敛,基本可以判定是规划逻辑有问题,而不是步数不够。window_size控制记忆层保留多少轮上下文,一般设 10 到 20,具体看单轮内容的 token 量。
2.3 决策层的三种实现路线与选型
决策层是智能体的「大脑」,目前主流有三种做法,各有适用边界。
第一种是纯提示词规划。把可用工具列表和当前状态塞进提示词,让模型直接输出下一步动作的 JSON。优点是实现快、改起来灵活;缺点是模型可能输出不存在的工具名或参数格式错误,需要严格的输出校验。适合工具数量少于十个、任务流程相对固定的场景。
第二种是状态机加模型兜底。把已知的任务流程写成显式状态转移,模型只在「下一步该走哪个分支」上做判断。优点是可控性强,出错能定位到具体状态;缺点是流程变更要改代码。适合业务流程稳定、对可靠性要求高的场景,比如订单处理、工单流转。
第三种是规划器加执行器分离。先用一个模型调用生成完整任务计划,再由执行器逐步执行,执行失败时回到规划器重新规划。优点是能处理开放式任务;缺点是调用成本高,且计划质量高度依赖规划提示词的设计。适合研究性质或任务边界模糊的场景。
选型时我一般问三个问题:任务步骤是否可枚举?失败后能否重试?调用成本是否敏感?三个都偏向「是」就选状态机,偏向「否」就选规划器分离。中间地带用纯提示词规划过渡。
3. 把智能体跑起来:工具注册与状态管理实操
3.1 工具注册的接口设计与参数校验
工具是智能体与外部世界交互的唯一通道,接口设计直接决定系统稳定性。我见过太多项目把工具写成裸函数,参数靠模型「自觉」传对,结果线上频繁报类型错误。正确做法是给每个工具定义显式的参数 schema,调用前做校验。
# tools.py 工具注册与校验 import json from typing import Callable, Any class ToolRegistry: def __init__(self): self._tools: dict[str, dict] = {} def register(self, name: str, schema: dict, fn: Callable): # schema 描述参数类型和必填项,用于调用前校验 self._tools[name] = {"schema": schema, "fn": fn} def list_names(self) -> list[str]: return list(self._tools.keys()) def call(self, name: str, args: dict) -> Any: if name not in self._tools: return {"error": f"未知工具: {name}"} schema = self._tools[name]["schema"] # 必填项检查,缺参数直接返回错误而不是抛异常 for field in schema.get("required", []): if field not in args: return {"error": f"缺少参数: {field}"} try: return self._tools[name]["fn"](**args) except Exception as e: # 工具内部异常转成结构化错误,交给决策层处理 return {"error": str(e)}这里有两个设计要点。第一,工具执行异常不向上抛,而是转成结构化错误返回。这样决策层能拿到「失败原因」并决定重试还是换路径,而不是整个循环崩掉。第二,参数校验放在调用前,缺参数时返回明确提示,模型下一轮往往能自己补上。required字段是校验的核心,非必填参数用默认值处理,不要留空。
注册一个查询工具大概是这样:
def query_sales(week: str, region: str = "all"): # 模拟查询,真实场景替换为数据库或接口调用 return {"week": week, "region": region, "rows": 128} registry.register( name="query_sales", schema={"required": ["week"], "properties": {"week": "str", "region": "str"}}, fn=query_sales, )参数说明:week必填,格式约定为「2024-W01」这类周标识;region选填,默认全区域。约定格式比约定类型更重要,因为模型很容易把「本周」这种自然语言直接传进来,schema 里写清格式示例能显著降低出错率。
3.2 记忆层的上下文裁剪策略
记忆层要解决的核心矛盾是:上下文窗口有限,但任务需要的历史信息可能很长。常见做法有三种,我按推荐顺序排列。
第一种是滑动窗口。只保留最近 N 轮对话,超出部分丢弃。实现最简单,适合短任务。缺点是早期关键信息会丢失,比如用户第一轮说的约束条件。
第二种是摘要压缩。当上下文超过阈值时,调用模型把早期内容压缩成一段摘要,替换原始记录。优点是保留语义,缺点是摘要本身可能丢细节,且增加一次调用成本。
第三种是结构化记忆。把任务状态、已确认事实、待办事项分别存成结构化字段,只把必要部分拼进提示词。这是长任务最稳的做法,但需要提前设计状态字段。
# memory.py 滑动窗口 + 关键事实保留 class Memory: def __init__(self, window_size: int = 15): self.window_size = window_size self.records: list[dict] = [] self.facts: list[str] = [] # 关键事实单独存,不参与裁剪 def add(self, role: str, content): self.records.append({"role": role, "content": content}) # 超出窗口时裁剪最旧的记录,但 facts 不动 if len(self.records) > self.window_size: self.records = self.records[-self.window_size:] def pin_fact(self, fact: str): # 把用户明确约束、任务目标等钉住,避免被裁掉 self.facts.append(fact) def context(self) -> list[dict]: head = [{"role": "system", "content": "已知事实: " + "; ".join(self.facts)}] return head + self.recordswindow_size设 15 是个折中值,对应大约 3000 到 5000 token 的上下文占用。pin_fact是这套设计的关键:用户说的「预算不超过五万」「必须用顺丰」这类硬约束,一旦识别出来就钉进 facts,不随窗口滚动消失。识别时机可以放在感知层,用规则匹配加模型判断结合。
3.3 循环终止条件与死循环排查
智能体最烦人的故障是死循环:决策层反复调用同一个工具,或者两个工具来回踢皮球。排查这类问题,我一般按三步走。
第一步,看调用序列。把每步的工具名和参数打日志,如果出现「A 调用 → B 调用 → A 调用」的固定模式,基本可以确认是规划逻辑问题。第二步,看工具返回值。如果工具一直返回错误,而决策层没有针对错误的处理分支,就会无限重试。第三步,看终止条件。很多实现只判断「模型是否输出 finish」,但模型可能永远不输出,需要加硬性步数上限和重复检测。
# 在循环里加重复动作检测 seen_actions = set() for step in range(max_steps): action = planner.next_action(memory.context()) signature = json.dumps(action, sort_keys=True) if signature in seen_actions: # 同一动作重复出现,强制中断并返回当前状态 return {"status": "stuck", "last_action": action} seen_actions.add(signature) ...这个seen_actions集合是后悔药级别的兜底。它不解决规划质量问题,但能保证系统不会无限烧钱。配合max_steps使用,双保险。注意签名要对参数排序后再序列化,否则参数顺序不同会被误判为新动作。
4. 智能体落地的避坑清单:五个真实翻车场景
4.1 工具描述写得像散文,模型选错工具
现象:智能体在「查询订单」和「查询物流」两个工具之间反复选错,明明用户问的是物流,却调了订单接口。
原因:工具描述写成了「这个工具用来查询订单相关信息」这种模糊表述,模型无法区分边界。工具描述是模型选型的唯一依据,写得越含糊,选错概率越高。
解决:每个工具的描述必须包含「什么时候用」和「什么时候不用」。比如「查询订单:用于获取订单金额、状态、下单时间。不用于查询物流轨迹,物流请用 query_logistics」。把否定边界写进去,选错率能降一大半。
4.2 上下文无限增长,调用成本失控
现象:一个客服智能体上线三天,单次会话平均调用成本涨了四倍,排查发现上下文长度随对话轮次线性增长。
原因:记忆层只加不减,每轮都把完整历史塞进提示词。轮次一多,输入 token 量爆炸。
解决:引入滑动窗口加摘要压缩。窗口设 15 轮,超出部分用模型压缩成一段两百字以内的摘要。同时监控每轮输入 token 数,设一个告警阈值,超过就触发压缩。成本控制不是优化项,是必选项。
4.3 工具副作用没有幂等保护,重复执行出事故
现象:智能体在发送通知邮件时因为超时重试,同一封邮件发了两遍,收件人收到重复通知。
原因:执行层没有做幂等设计,工具调用失败后决策层直接重试,而「发送」这类操作本身有副作用。
解决:对有副作用的工具加幂等键。每次调用生成一个基于任务 ID 和步骤的 key,工具内部先查这个 key 是否已执行过,执行过就直接返回上次结果。发送类操作还要加人工确认环节,不要全自动。
4.4 模型输出格式不稳定,解析频繁失败
现象:决策层要求输出 JSON,但模型时不时在 JSON 前后加解释文字,导致解析报错,整个循环中断。
原因:提示词里只说了「输出 JSON」,没有约束输出格式的边界,也没有做容错解析。
解决:两层防护。第一层,提示词里明确「只输出 JSON,不要任何其他文字」,并给一个输出示例。第二层,解析时先用正则提取第一个完整 JSON 块,再尝试解析,解析失败时把原始输出和错误信息一起返回给模型,让它自我修正。不要指望模型百分之百听话,容错解析是必需品。
4.5 长任务中途状态丢失,重启后从头再来
现象:一个跑了半小时的数据处理任务,因为进程重启,所有中间状态丢失,只能从头执行。
原因:记忆层只存在内存里,没有持久化。任务状态和执行进度没有落盘。
解决:把任务状态定期写入本地文件或数据库,记录已完成步骤和中间结果。重启时先读状态文件,从断点继续。持久化频率不用每步都写,按步骤或按时间间隔写即可,但关键节点必须写。这是长任务智能体的基本要求。
5. 进阶技巧:用评估集把智能体从「能跑」推到「可靠」
智能体搭起来能跑只是起点,真正难的是让它稳定可靠。我见过太多项目在演示时表现良好,一上真实流量就原形毕露。区别就在于有没有一套评估机制。这一章讲一个我常用的轻量评估方法,不需要标注平台,一个 JSON 文件加一段脚本就能跑。
核心思路是构造「任务—期望动作序列」的评估集。每条用例包含用户输入、期望调用的工具序列、期望的最终输出关键字段。跑评估时,让智能体在隔离环境执行,记录实际动作序列,和期望序列做比对。比对不要求完全一致,而是看关键动作是否命中、顺序是否合理、最终输出是否正确。
# eval_agent.py 轻量评估脚本 import json def evaluate(cases_path: str, agent_runner): cases = json.load(open(cases_path, encoding="utf-8")) passed, failed = 0, [] for case in cases: result = agent_runner(case["input"]) actual_tools = [s["name"] for s in result["steps"]] expected_tools = case["expected_tools"] # 关键动作命中率:期望工具是否都出现过 hit = all(t in actual_tools for t in expected_tools) # 最终输出关键字段校验 output_ok = all( case["expected_output"].get(k) == result["output"].get(k) for k in case["expected_output"] ) if hit and output_ok: passed += 1 else: failed.append({"case": case["input"], "actual": actual_tools}) print(f"通过 {passed}/{len(cases)}") return failed评估集怎么建?我的做法是从真实日志里采样。上线初期先记录所有会话的工具调用序列,人工挑出二十到三十条有代表性的,标注期望序列。这个量级不需要多,但覆盖面要广:单工具任务、多工具串联、需要重试的失败场景、边界输入各占几条。评估集是活的,每次发现新的失败模式就补一条进去,慢慢就长成了回归测试集。
参数方面,评估时要把max_steps调成和生产一致,否则评估结果没有参考价值。另外评估环境要隔离工具的真实副作用,比如发送邮件换成写日志,避免评估污染真实数据。
一个具体技巧:给评估结果加「动作序列相似度」指标,而不只是通过与否。用编辑距离算实际序列和期望序列的差距,能看出智能体是「完全跑偏」还是「顺序略有不同但结果正确」。后者往往可以接受,前者必须修。这个指标比单纯的通过率更能指导优化方向。
最后说个我自己的习惯。每次改完提示词或工具描述,我一定先跑一遍评估集再上线,哪怕只改了一个词。因为智能体系统里,提示词的一个措辞变化可能影响工具选择,这种影响靠肉眼看是看不出来的,只有评估集能给你确定性的反馈。这套习惯帮我省下了不少线上排查的时间,也让我对每次改动的效果心里有数。希望帮到你。
本文还有配套的精品资源,点击获取