简介:这份PPT面向希望系统入门大模型应用与AI智能体开发的开发者、产品经理及高校师生,围绕大模型应用开发平台展开,重点讲解女娲智能体平台的构建、训练与部署全流程。资源包内含1个pptx文件,约10.4MB,以图文并茂的幻灯片形式呈现,便于课堂讲授与自学翻阅。内容从平台概述切入,依次拆解插件、工作流、触发器三大技能模块,并深入讲解文档、表格、照片三类知识库与长期记忆、变量、数据库、文件盒子等记忆机制,还通过母婴助手智能体实例完整演示创建、提示词编写、技能添加、调试与工作流搭建步骤。目前已有535人学习,适合想快速理解智能体开发框架、掌握提示词设计与工作流编排思路的读者参考借鉴。
1. 从一份 PPT 到一个能跑的智能体:AI 智能体开发平台到底在解决什么
很多人第一次接触 AI 智能体开发平台,是从一份标题类似「大模型应用-AI智能体开发平台.pptx」的汇报材料开始的。PPT 里画着漂亮的架构图:底层是大模型,中间是工具调用和记忆,上层是各种业务智能体。但真到自己动手,问题立刻来了——大模型 API 怎么接?工具怎么注册?多轮对话的状态存哪?流式输出怎么渲染?这份 PPT 讲的是「应该长什么样」,而这篇笔记讲的是「怎么让它真的跑起来」。
AI 智能体开发平台,本质是把「大模型 + 工具调用 + 记忆 + 编排」这套重复劳动封装成可复用的工程结构。它要解决的核心问题是:让一个只会聊天的模型,变成能查数据、调接口、记住上下文、按流程干活的执行体。适合谁?适合已经会调大模型 API、但每次做业务都要重写一遍工具调度和会话管理的后端或全栈工程师,也适合想用 Dify、Coze 这类平台快速验证想法、但需要理解底层机制的产品和算法同学。下面从最小可运行结构讲起,一路讲到参数、坑和验证方法。
2. 拆开智能体开发平台的四层结构:模型、工具、记忆、编排
2.1 为什么不能只用一个 while 循环调大模型
新手最容易写出的「智能体」是这样:把用户输入拼进 prompt,调一次大模型,拿到回复返回。这只能叫聊天机器人,不是智能体。真正的智能体要能根据任务决定「要不要调工具」「调哪个工具」「拿到结果后怎么继续」。如果只用 while 循环硬写,工具一多,if-else 会爆炸,而且模型返回的调用意图是自然语言,解析全靠正则,稍微换个说法就翻车。
所以平台化的第一层价值是结构化工具描述。主流做法是让模型输出结构化的调用请求(JSON),平台负责解析、执行、把结果塞回上下文。这样模型只负责「决策」,平台负责「执行」,职责分离。常见做法是用 function calling 或 tool use 协议,把每个工具的名字、参数、描述以 schema 形式告诉模型。
2.2 四层结构各自负责什么
一个能落地的智能体平台,通常拆成四层,缺一层都会在某个场景下出问题:
| 层 | 职责 | 关键实现点 |
|---|---|---|
| 模型层 | 理解意图、生成决策和回复 | 支持多模型切换、流式输出、超时重试 |
| 工具层 | 注册、校验、执行外部能力 | schema 定义、参数校验、异常兜底 |
| 记忆层 | 保存会话历史和长期知识 | 短期窗口裁剪、长期向量检索 |
| 编排层 | 控制多步流程和状态流转 | 状态机或图结构、最大步数限制 |
模型层要处理的核心是「不确定性」:同一个问题,模型可能这次调工具、下次直接答。平台不能假设模型一定听话,必须做兜底。工具层要处理的是「安全」:模型可能编造参数,必须在执行前校验。记忆层要处理的是「成本」:上下文不能无限增长,必须裁剪或摘要。编排层要处理的是「死循环」:模型可能反复调同一个工具,必须设最大步数。
2.3 用 Python 搭一个最小可运行骨架
下面这段代码不依赖任何平台 SDK,只用标准库和一个假的大模型调用,把四层结构的最小形态跑通。你可以把它当成理解 Dify、Coze 这类平台内部逻辑的「透明模型」。
import json # 工具注册表:名字 -> (描述, 参数schema, 执行函数) TOOLS = {} def register_tool(name, desc, params, func): TOOLS[name] = {"desc": desc, "params": params, "func": func} # 一个示例工具:查询订单状态 def query_order(order_id): # 真实场景这里查数据库 return {"order_id": order_id, "status": "已发货", "eta": "2天"} register_tool( "query_order", "根据订单号查询订单状态", {"order_id": "string, 订单编号"}, query_order ) def build_tool_prompt(): # 把工具schema拼进系统提示,让模型知道有哪些工具 lines = ["你可以调用以下工具,需要时输出JSON: {\"tool\": 名字, \"args\": {...}}"] for name, meta in TOOLS.items(): lines.append(f"- {name}: {meta['desc']} 参数: {meta['params']}") return "\n".join(lines) def fake_llm(messages): # 这里替换成真实大模型调用,返回文本 # 简化演示:如果用户提到订单号,就返回工具调用 last = messages[-1]["content"] if "订单" in last and any(c.isdigit() for c in last): oid = "".join(c for c in last if c.isdigit()) return json.dumps({"tool": "query_order", "args": {"order_id": oid}}) return "你好,请问有什么可以帮你?" def run_agent(user_input, max_steps=5): messages = [ {"role": "system", "content": build_tool_prompt()}, {"role": "user", "content": user_input} ] for step in range(max_steps): reply = fake_llm(messages) # 尝试解析工具调用 try: call = json.loads(reply) if "tool" in call: tool_name = call["tool"] if tool_name not in TOOLS: messages.append({"role": "assistant", "content": reply}) messages.append({"role": "user", "content": f"工具{tool_name}不存在,请重新决策"}) continue result = TOOLS[tool_name]["func"](**call["args"]) messages.append({"role": "assistant", "content": reply}) messages.append({"role": "user", "content": f"工具返回: {json.dumps(result, ensure_ascii=False)}"}) continue except json.JSONDecodeError: pass # 不是工具调用,直接作为最终回复 return reply return "达到最大步数,任务未完成" if __name__ == "__main__": print(run_agent("帮我查一下订单12345的状态"))这段代码的逻辑说明:register_tool把工具的描述和参数 schema 存起来,build_tool_prompt把这些信息拼成系统提示,让模型知道「有哪些工具可用」。run_agent是编排层的核心,它循环调用模型,每次尝试把模型输出解析成 JSON 工具调用;如果解析成功且工具存在,就执行工具、把结果塞回消息列表继续循环;如果解析失败,就当作最终回复返回。max_steps是防止死循环的关键参数,一般设 5 到 10,复杂任务可以放宽到 15,但要有超时兜底。
参数说明:max_steps控制最大推理步数,太小会导致多步任务中断,太大会让异常情况消耗大量 token;工具 schema 里的参数类型要写清楚,模型对string、integer的区分很敏感,写错会导致参数校验失败。真实接入大模型时,fake_llm换成带 function calling 的 API 调用,把工具 schema 用官方格式传入,比让模型自己输出 JSON 更稳。
3. 把工具调用接进真实大模型:schema、流式与中断处理
3.1 function calling 的 schema 怎么写才不翻车
上一节的骨架用「让模型输出 JSON」的方式演示,真实项目里更稳的做法是用模型厂商提供的 function calling 能力。以常见的 OpenAI 兼容接口为例,工具定义是一个数组,每个工具包含type、function.name、function.description、function.parameters。参数用 JSON Schema 描述,required字段一定要写,否则模型可能漏传关键参数。
tools = [ { "type": "function", "function": { "name": "query_order", "description": "根据订单号查询订单状态,仅在用户明确提供订单号时调用", "parameters": { "type": "object", "properties": { "order_id": { "type": "string", "description": "订单编号,纯数字字符串" } }, "required": ["order_id"] } } } ]逻辑说明:description不是写给用户看的,是写给模型看的决策依据。写「仅在用户明确提供订单号时调用」能显著降低模型乱调工具的概率。required里列出的参数,模型必须提供,否则接口会报错。参数说明里写「纯数字字符串」是为了避免模型传成整数,很多订单号带前导零,传整数会丢零。
参数怎么改:如果工具参数是枚举类型,用enum限定取值范围,比让模型自由发挥可靠得多。如果参数是数组,items里要写清楚元素类型。工具数量超过 20 个时,建议按业务分组,每次只把相关工具传给模型,否则模型选择困难,准确率会下降。
3.2 流式输出怎么和工具调用共存
用户等一个智能体回复,如果等 10 秒才出字,体验很差。所以流式输出(SSE)几乎是标配。但流式和工具调用会打架:流式是一边生成一边推,工具调用需要等模型输出完整的调用请求才能执行。常见做法是分两阶段——第一阶段流式输出模型的「思考文本」,如果检测到工具调用,暂停流式,执行工具,把结果塞回去,再开启第二轮流式输出最终回复。
def stream_agent(user_input): # 伪代码:展示流式与工具调用的协作顺序 messages = [{"role": "user", "content": user_input}] # 第一轮:流式请求,同时收集工具调用片段 tool_call_buffer = "" for chunk in llm_stream(messages, tools=tools): if chunk.type == "text": yield chunk.text # 直接推给前端 elif chunk.type == "tool_call": tool_call_buffer += chunk.delta # 如果有工具调用,执行后进入第二轮 if tool_call_buffer: call = parse_tool_call(tool_call_buffer) result = execute_tool(call) messages.append({"role": "assistant", "content": None, "tool_calls": [call]}) messages.append({"role": "tool", "content": json.dumps(result)}) for chunk in llm_stream(messages): if chunk.type == "text": yield chunk.text逻辑说明:第一轮流式过程中,文本片段直接推给前端,工具调用片段先缓存不推。等流结束,如果缓存里有工具调用,执行工具,把结果作为tool角色消息追加,再发起第二轮流式请求。这样用户能先看到模型的思考过程,工具执行完再看到最终答案。
参数说明:llm_stream的tools参数只在第一轮传,第二轮不需要再传,避免模型再次调用工具陷入循环。前端配合abort能力,用户点停止时中断流式请求,后端要捕获中断信号,避免工具执行到一半被丢弃。超时时间建议设 30 秒,工具执行本身也要有独立超时,一般 5 到 10 秒。
3.3 记忆层:短期窗口和长期检索怎么配合
智能体的记忆分两种。短期记忆是当前会话的消息列表,直接塞进上下文。长期记忆是跨会话的知识,比如用户偏好、历史工单,需要向量化后存起来,用的时候检索。短期记忆的问题是上下文长度有限,消息一多就超限;长期记忆的问题是检索不准,召回一堆无关内容反而干扰模型。
常见做法是:短期记忆保留最近 N 轮对话,N 一般取 10 到 20,超出部分做摘要压缩成一条系统消息。长期记忆用向量数据库存,检索时取 top 3 到 top 5,相似度阈值设 0.7 以上,低于阈值的不注入。这样既控制 token 成本,又保证相关性。
def build_context(user_input, history, vector_store, max_turns=10): # 短期:保留最近max_turns轮 recent = history[-max_turns*2:] # 长期:检索相关记忆 recalled = vector_store.search(user_input, top_k=3, threshold=0.7) memory_text = "\n".join([r.text for r in recalled]) messages = [{"role": "system", "content": f"相关背景:\n{memory_text}"}] messages.extend(recent) messages.append({"role": "user", "content": user_input}) return messages逻辑说明:max_turns控制短期窗口大小,top_k和threshold控制长期记忆的召回量和精度。memory_text拼进系统消息,而不是用户消息,避免污染对话历史。如果召回为空,系统消息里就不写背景,保持干净。
参数怎么改:max_turns根据模型上下文窗口调整,8K 窗口建议 5 到 8 轮,32K 窗口可以到 20 轮。threshold太高会漏召回,太低会引入噪声,0.7 是常见起点,需要根据实际数据调。向量模型的选择也影响召回质量,中文场景建议用支持中文的 embedding 模型。
4. 避坑与排查:智能体开发平台最常见的五类翻车
4.1 模型不调工具,直接编答案
现象:用户问「订单 12345 状态」,模型不调query_order,直接回复「您的订单已发货」。原因:工具描述不够明确,或者系统提示里没有强调「涉及实时数据必须调工具」。解决:在工具description里写清楚调用条件,在系统提示里加一句「涉及订单、库存、价格等实时数据时,必须先调用工具获取,不得凭记忆回答」。如果还不行,用 few-shot 示例在提示里演示一次正确调用。
4.2 工具参数类型对不上,执行报错
现象:模型传order_id为整数12345,但工具函数期望字符串,或者传了多余字段导致TypeError。原因:JSON Schema 描述不严谨,模型自由发挥。解决:在 schema 里把类型写死,required列全,执行前做一次参数清洗,只取 schema 里定义的字段,类型不对就尝试转换,转换失败返回明确错误让模型重试。
4.3 多轮对话后上下文爆炸,响应变慢
现象:聊了 30 轮后,每次请求 token 数飙升,响应从 2 秒变 10 秒。原因:短期记忆没有裁剪,全部历史都塞进上下文。解决:按max_turns裁剪,超出部分做摘要。摘要用便宜的小模型生成,把 20 轮压缩成 3 句话。同时监控每次请求的 token 数,设一个上限告警。
4.4 工具执行超时,整个智能体卡死
现象:某个工具查数据库慢,用户等 30 秒没反应,前端超时断开。原因:工具执行没有独立超时,编排层同步等待。解决:给每个工具设超时(一般 5 到 10 秒),超时返回「查询超时,请稍后重试」,让模型决定是否重试或换方式。编排层整体也要有超时,避免单个工具拖垮整个请求。
4.5 流式输出中断后状态不一致
现象:用户点停止,前端不再显示,但后端工具已经执行了一半,数据被改了。原因:中断信号没有传递到工具执行层。解决:工具执行设计成幂等,或者把写操作放到确认之后。中断时记录日志,必要时做补偿。流式接口要正确处理客户端断开事件,及时释放资源。
5. 验证智能体是否真的可用:三个可量化的检查点
5.1 用固定用例集做回归测试
智能体最大的问题是「这次行下次不行」。所以必须有一套固定用例集,每次改提示或换模型都跑一遍。用例集不用多,20 到 30 条覆盖核心场景即可,每条包含输入、期望的工具调用、期望的回复要点。跑的时候记录工具调用准确率和回复命中率,两个指标都低于 90% 就说明有问题。
test_cases = [ {"input": "订单12345到哪了", "expect_tool": "query_order", "expect_args": {"order_id": "12345"}}, {"input": "你好", "expect_tool": None, "expect_reply_contains": "你好"}, ] def run_regression(agent, cases): tool_hit = 0 reply_hit = 0 for case in cases: result = agent(case["input"]) if case["expect_tool"]: if result.tool_called == case["expect_tool"]: tool_hit += 1 else: if not result.tool_called: tool_hit += 1 if case.get("expect_reply_contains") and case["expect_reply_contains"] in result.reply: reply_hit += 1 print(f"工具准确率: {tool_hit/len(cases):.2%}, 回复命中率: {reply_hit/len(cases):.2%}")逻辑说明:expect_tool为None表示这条用例不应该调工具,用来测模型是否乱调。expect_args可以进一步校验参数是否正确。跑完打印两个指标,低于阈值就排查是提示问题还是模型问题。
5.2 监控 token 消耗和延迟分布
上线后要盯两个数:每次请求的平均 token 数和 P95 延迟。token 数突然上涨,通常是记忆没裁剪或工具返回内容太长。延迟 P95 超过 5 秒,要查是模型慢还是工具慢。把这两个指标打到监控面板,设告警阈值,比事后用户投诉再查高效得多。
5.3 人工抽检工具调用日志
自动化指标只能测「有没有调」,测不了「调得对不对」。每周抽 50 条工具调用日志人工看一遍,重点看参数是否正确、是否有不必要的调用、工具返回后模型是否合理利用。我自己的习惯是建一个「翻车日志」,每次发现模型犯蠢就记一条,攒够 10 条就回头改提示或加 few-shot 示例。这个习惯坚持三个月,智能体的稳定性能提升一个档次。希望帮到你。
本文还有配套的精品资源,点击获取