简介:这份PPT面向希望系统入门大模型应用与AI智能体开发的开发者、学生及技术爱好者,聚焦智能体开发平台的核心能力与落地路径。内容围绕大模型应用开发平台展开,重点讲解女娲智能体平台的插件、工作流、触发器、知识库与记忆机制,并配有母婴助手智能体的完整搭建实例,涵盖角色定义、提示词编写、技能添加与调试等环节,帮助读者理解从平台认知到智能体落地的完整链路。资源包共1个pptx文件,约10.4MB,以图文并茂的课件形式呈现,适合课堂讲授、自学梳理或团队内部分享。目前已有535人学习浏览,可作为大模型智能体开发方向的入门参考,帮助读者快速建立平台功能认知、掌握提示词设计思路,并借助实例理解知识库与记忆的区分及实际应用场景。
1. 大模型应用落地:AI 智能体开发平台到底在解决什么问题
很多团队第一次做大模型应用,都会掉进同一个坑:把模型 API 调通、写个提示词、套个对话框,就以为做完了。上线之后才发现,多轮对话记不住上下文、工具调用全靠硬编码、换个模型整条链路就崩、出了问题连日志都查不到。这时候才意识到,缺的不是模型,而是一层能把模型、工具、记忆、流程编排统一管起来的平台。
AI 智能体开发平台要解决的正是这件事。它把「智能体」从一段提示词升级成一个可配置、可调试、可观测的工程对象:角色设定、可用工具、知识库、记忆策略、执行流程都在平台里定义,模型只是其中一个可替换的组件。适合谁用?一是想把大模型能力接进业务系统但不想从零造轮子的应用开发团队,二是需要快速验证智能体方案、频繁调参对比效果的算法同学,三是负责把 Demo 变成稳定服务的后端工程师。
这一篇不讲空泛概念,按「平台由哪些模块组成 → 怎么搭一个最小可跑通的智能体 → 工具调用和记忆怎么落地 → 参数怎么调 → 坑在哪」的顺序推下来,每一步都给可抄的代码和参数说明。
2. 拆开一个智能体开发平台:五个核心模块与选型理由
在动手之前,得先想清楚一个平台级方案和「直接调 API」的本质差别。直接调 API 是单次请求-响应,平台要管的是状态、编排和可观测性。下面把平台拆成五个模块,每个模块说清楚它为什么存在、不做好会出什么问题。
2.1 模型接入层:为什么要做统一抽象而不是直连
模型接入层的作用是把不同厂商、不同规格的模型统一成一个调用接口。如果业务代码里到处写死某个模型的 SDK,换模型时就是灾难。常见做法是定义一个LLMClient抽象,把chat、stream、embedding三类能力抽出来,具体厂商实现放在适配器里。
from abc import ABC, abstractmethod from typing import Iterator class LLMClient(ABC): @abstractmethod def chat(self, messages: list[dict], temperature: float = 0.7, max_tokens: int = 2048) -> str: """非流式对话,返回完整文本""" ... @abstractmethod def stream(self, messages: list[dict]) -> Iterator[str]: """流式对话,逐块返回增量文本""" ... class OpenAICompatClient(LLMClient): def __init__(self, base_url: str, api_key: str, model: str): self.base_url = base_url # 兼容接口地址 self.api_key = api_key self.model = model # 模型标识,配置化传入 def chat(self, messages, temperature=0.7, max_tokens=2048): # 实际调用走 HTTP,这里省略请求细节 payload = { "model": self.model, "messages": messages, "temperature": temperature, "max_tokens": max_tokens, } return self._post("/chat/completions", payload)["choices"][0]["message"]["content"]逻辑说明:抽象基类定义契约,适配器负责把统一参数翻译成各家格式。参数上,temperature控制随机性,做工具调用和结构化输出时建议压到 0.1~0.3;max_tokens要留足,因为智能体一轮里可能塞进工具返回的大段内容。选型理由很简单:接入层做薄,上层编排才不会被厂商绑定。
2.2 工具与函数调用层:让模型能真正干活
智能体区别于聊天机器人的关键,是它能调用外部工具。工具层要做三件事:注册工具、把工具描述成模型能理解的 schema、解析模型的调用意图并执行。
import json TOOL_REGISTRY = {} def tool(name: str, description: str, parameters: dict): """装饰器:把函数注册成可被模型调用的工具""" def wrapper(fn): TOOL_REGISTRY[name] = { "fn": fn, "schema": { "type": "function", "function": { "name": name, "description": description, "parameters": parameters, }, }, } return fn return wrapper @tool( name="query_order", description="根据订单号查询订单状态", parameters={ "type": "object", "properties": {"order_id": {"type": "string", "description": "订单编号"}}, "required": ["order_id"], }, ) def query_order(order_id: str) -> str: # 真实场景里这里查数据库 return json.dumps({"order_id": order_id, "status": "已发货"})逻辑说明:description和parameters直接决定模型能不能正确调用工具,写得含糊模型就会乱调或漏调。参数 schema 用 JSON Schema 描述,required字段一定要标全。执行时按name从注册表取函数,把模型给的参数json.loads后传入。这里最容易翻车的是参数类型——模型可能把数字传成字符串,执行前要做一次校验和转换。
2.3 记忆与上下文管理层:短期记忆和长期记忆怎么分工
记忆分两层。短期记忆是当前会话的消息列表,直接拼进 prompt;长期记忆是跨会话的知识,通常落到向量库,按相似度召回。两者不能混为一谈:短期记忆管「刚才说了什么」,长期记忆管「这个用户历史上偏好什么」。
class ShortTermMemory: def __init__(self, max_turns: int = 10): self.messages = [] self.max_turns = max_turns # 保留最近 N 轮,防止上下文爆炸 def add(self, role: str, content: str): self.messages.append({"role": role, "content": content}) # 超出窗口就丢弃最早的非系统消息 if len(self.messages) > self.max_turns * 2: self.messages = self.messages[-self.max_turns * 2:] def build(self, system_prompt: str) -> list[dict]: return [{"role": "system", "content": system_prompt}] + self.messages逻辑说明:max_turns是最关键的参数,设太大上下文超限、成本飙升,设太小智能体就「失忆」。经验值单轮对话 8~12 轮,任务型智能体可以更短。长期记忆的召回一般放在构建 prompt 之前,把召回片段拼进 system 或单独一条消息,注意控制召回条数,通常 top 3~5 就够,多了反而干扰模型判断。
2.4 编排与执行引擎:ReAct 循环的最小实现
编排引擎负责把「模型思考 → 调用工具 → 拿结果 → 再思考」这个循环跑起来,也就是常说的 ReAct 模式。核心是控制循环次数,防止模型陷入死循环。
def run_agent(client, memory, system_prompt, user_input, max_steps=6): memory.add("user", user_input) for step in range(max_steps): messages = memory.build(system_prompt) # 把工具 schema 一起传给模型 tools = [t["schema"] for t in TOOL_REGISTRY.values()] resp = client.chat_with_tools(messages, tools) if resp.get("tool_calls"): for call in resp["tool_calls"]: fn = TOOL_REGISTRY[call["name"]]["fn"] result = fn(**json.loads(call["arguments"])) memory.add("tool", result) else: memory.add("assistant", resp["content"]) return resp["content"] return "达到最大步数,任务未完成"逻辑说明:max_steps是保命参数,一般 5~8。没有它,模型可能反复调同一个工具。每轮把工具执行结果写回记忆,模型下一轮才能基于结果继续推理。注意工具返回内容要截断,否则一次返回几千字,几轮下来上下文就满了。
2.5 可观测与评测层:没有日志的智能体就是黑匣子
上线后最怕的是「用户说答错了,你复现不出来」。可观测层要记录每次请求的完整轨迹:输入、每轮模型输出、工具调用参数与返回、耗时、token 消耗。评测层则用固定用例集回归,改一次提示词就跑一遍,看通过率有没有掉。这两层不做,调优全靠玄学。
3. 从零搭一个最小可跑通的智能体:目录结构与启动流程
上一章讲了模块,这一章把它们拼成一个能跑起来的最小系统。目标很明确:本地起一个服务,能接收用户输入,能调用一个工具,能返回结果,并且有日志。
3.1 项目目录怎么划分才不返工
目录结构决定了后期扩展成本。常见做法是按职责分层,而不是按技术栈分层。
agent-platform/ ├── config/ │ └── settings.yaml # 模型、工具、记忆的参数集中放这里 ├── core/ │ ├── llm_client.py # 模型接入层 │ ├── tools.py # 工具注册与执行 │ ├── memory.py # 记忆管理 │ └── engine.py # 编排引擎 ├── api/ │ └── server.py # 对外 HTTP 接口 ├── logs/ │ └── agent.log # 轨迹日志 └── tests/ └── cases.yaml # 回归用例逻辑说明:config单独抽出来,是为了改参数不用动代码;core里每个文件对应上一章一个模块,职责单一;tests/cases.yaml放固定输入和期望输出,方便回归。这个结构的好处是换模型只改llm_client.py,加工具只改tools.py。
3.2 配置文件里必须写死的几个参数
配置集中管理是平台化的第一步。下面是一份最小配置,字段含义逐个说明。
llm: provider: openai_compat base_url: "http://localhost:8000/v1" # 本地推理服务地址 model: "qwen2.5-7b-instruct" temperature: 0.2 # 工具调用场景压低随机性 max_tokens: 2048 memory: max_turns: 10 # 短期记忆保留轮数 recall_top_k: 4 # 长期记忆召回条数 engine: max_steps: 6 # ReAct 最大循环步数 tool_timeout: 5 # 单次工具执行超时(秒) logging: level: INFO trace: true # 是否记录完整轨迹逻辑说明:temperature设 0.2 是因为工具调用需要稳定输出,太高会导致参数格式错乱;tool_timeout必须有,否则某个工具卡住整个请求就挂死;trace: true打开后每轮轨迹都落盘,排查问题时能完整回放。这几个参数是踩过坑之后一定会加的。
3.3 启动服务并跑通第一条链路
把引擎包成 HTTP 接口,用最轻量的方式起服务。
from fastapi import FastAPI from pydantic import BaseModel from core.llm_client import OpenAICompatClient from core.memory import ShortTermMemory from core.engine import run_agent import yaml cfg = yaml.safe_load(open("config/settings.yaml")) client = OpenAICompatClient( cfg["llm"]["base_url"], "dummy-key", cfg["llm"]["model"] ) app = FastAPI() class ChatRequest(BaseModel): session_id: str message: str SESSIONS = {} # 生产环境要换成 Redis @app.post("/chat") def chat(req: ChatRequest): mem = SESSIONS.setdefault(req.session_id, ShortTermMemory(cfg["memory"]["max_turns"])) answer = run_agent(client, mem, "你是一个订单助手", req.message, max_steps=cfg["engine"]["max_steps"]) return {"answer": answer}逻辑说明:session_id用来隔离不同用户的记忆,生产环境必须换成外部存储,否则服务重启记忆全丢。启动命令是uvicorn api.server:app --host 0.0.0.0 --port 8080。测试时用curl -X POST http://localhost:8080/chat -d '{"session_id":"u1","message":"帮我查订单 A123"}',正常应该看到模型调用query_order并返回状态。
3.4 用日志验证工具真的被调用了
跑通不代表调对了。打开logs/agent.log,确认轨迹里有工具调用记录。
import logging logger = logging.getLogger("agent") def run_agent(client, memory, system_prompt, user_input, max_steps=6): logger.info("user_input=%s", user_input) for step in range(max_steps): messages = memory.build(system_prompt) resp = client.chat_with_tools(messages, [t["schema"] for t in TOOL_REGISTRY.values()]) if resp.get("tool_calls"): for call in resp["tool_calls"]: logger.info("step=%d tool=%s args=%s", step, call["name"], call["arguments"]) result = TOOL_REGISTRY[call["name"]]["fn"](**json.loads(call["arguments"])) memory.add("tool", result) else: logger.info("step=%d final_answer", step) return resp["content"]逻辑说明:日志里能看到tool=query_order args={"order_id":"A123"}就说明工具调用链路通了。如果只有final_answer没有tool,说明模型没触发工具,多半是工具description写得不清楚,或者 system prompt 没告诉模型「查订单必须用工具」。
4. 工具调用与记忆的进阶配置:参数怎么调、边界在哪
最小系统跑通后,真正决定效果的是工具调用准确率和记忆策略。这一章讲几个必须调的参数和它们的边界。
4.1 工具描述怎么写才能提高调用准确率
工具调用失败,八成是描述问题。对比下面两种写法。
| 写法 | description | 模型表现 |
|---|---|---|
| 差 | "查询订单" | 经常不调用,或参数传错 |
| 好 | "根据订单号查询订单状态和物流信息,订单号格式为字母加数字,如 A123" | 调用准确,参数格式正确 |
好的描述包含三要素:做什么、什么时候用、参数长什么样。参数 schema 里每个字段也要写description,模型是靠这些文字判断的。经验是描述里带上一个示例值,模型对格式的把握会明显变好。
4.2 记忆窗口和召回条数的取舍
短期记忆窗口max_turns和长期召回recall_top_k是一对矛盾参数。窗口大、召回多,上下文质量高但成本和延迟上升;窗口小、召回少,响应快但容易答非所问。
| 场景 | max_turns | recall_top_k | 理由 |
|---|---|---|---|
| 任务型(下单、查询) | 6 | 2 | 任务短,历史干扰大 |
| 咨询型(客服问答) | 12 | 4 | 需要多轮澄清 |
| 知识型(文档助手) | 8 | 6 | 依赖召回质量 |
调参方法是固定一批测试用例,只改一个参数看通过率变化。别一次改好几个,否则根本不知道是哪个起的作用。
4.3 多工具场景下的选择冲突怎么解
当注册了十几个工具,模型经常选错。两个办法:一是按场景分组,不同智能体只挂相关工具,别把所有工具都塞给一个智能体;二是在 system prompt 里写清楚优先级,比如「用户提到订单号优先用 query_order,不要用通用搜索」。工具数量控制在 10 个以内,超过就该拆智能体了。
5. 避坑与排查:智能体上线后最常见的五类问题
这一章全是血泪经验,每条按现象、原因、解决写。
5.1 模型不调用工具,直接编答案
现象:问订单状态,模型不查库,直接回「您的订单已发货」。 原因:工具描述太模糊,或 system prompt 没强制要求。模型倾向于用训练数据里的常识回答。 解决:在 system prompt 里明确「任何订单相关问题必须先调用 query_order,禁止凭记忆回答」,并把工具 description 写具体。必要时在工具 schema 里加required强制参数。
5.2 工具参数格式错乱导致执行报错
现象:日志里args={"order_id": 123},但函数签名要字符串,直接抛异常。 原因:模型对参数类型不敏感,数字和字符串经常混。 解决:执行前做一次类型校验和转换,用 pydantic 定义参数模型,校验失败就把错误信息回传给模型让它重试,而不是直接崩。
5.3 上下文超限,请求被截断
现象:多轮对话到后面,模型开始答非所问,或接口报 token 超限。 原因:短期记忆没做窗口控制,工具返回的大段内容全塞进上下文。 解决:max_turns设上限,工具返回内容超过 500 字就截断或摘要,长期记忆召回条数别超过 6。
5.4 死循环:同一个工具反复调用
现象:日志里query_order被调了七八次,最后返回「达到最大步数」。 原因:工具返回结果模型没理解,或返回为空导致模型以为没查到。 解决:max_steps兜底,工具返回空时给明确提示「未查询到结果,请告知用户」,而不是返回空字符串。
5.5 换模型后整条链路失效
现象:本地换成另一个模型,工具调用格式全变了,解析报错。 原因:不同模型的 function call 输出格式有差异,代码里写死了某一种。 解决:接入层做格式归一化,把各家的工具调用输出统一成内部结构。这也是第 2 章强调统一抽象的原因。
6. 把智能体从能跑变成好用:一个可复用的评测技巧
前面五章把平台搭起来、跑通了,但「能跑」和「好用」之间差着一套评测。我一般会做一件事:建一个固定用例集,每次改提示词、换模型、调参数,都跑一遍,看通过率。这是唯一能避免「改好了这个、弄坏了那个」的办法。
用例集长这样,放在tests/cases.yaml:
- id: case_001 input: "帮我查订单 A123" expect_tool: query_order # 期望调用的工具 expect_args: {"order_id": "A123"} # 期望参数 expect_contains: "已发货" # 期望答案包含的关键词 - id: case_002 input: "今天天气怎么样" expect_tool: null # 不该调用任何工具 expect_contains: "无法"跑评测的脚本核心逻辑是:对每个用例执行run_agent,从日志里提取实际调用的工具和参数,和期望值比对,统计通过率。
import yaml def evaluate(client, cases_path="tests/cases.yaml"): cases = yaml.safe_load(open(cases_path)) passed = 0 for c in cases: mem = ShortTermMemory(10) trace = [] # 引擎里把每步工具调用 append 进来 answer = run_agent(client, mem, "你是一个订单助手", c["input"], trace=trace) actual_tool = trace[0]["name"] if trace else None ok = (actual_tool == c["expect_tool"] and c["expect_contains"] in answer) passed += ok print(f"{c['id']}: {'PASS' if ok else 'FAIL'} tool={actual_tool}") print(f"通过率 {passed}/{len(cases)}")逻辑说明:expect_tool为null的用例专门测「不该调用工具时别乱调」,这类负样本最容易被忽略,但恰恰是线上投诉的高发区。通过率不用追求 100%,先定个基线,比如 85%,之后每次改动不能低于基线。参数上,用例集至少覆盖三类:正常调用、不该调用、参数边界(比如订单号带空格)。
这套评测跑起来之后,调参就不再是玄学。改temperature从 0.2 到 0.5,通过率掉 10 个点,你就知道这个参数不能动。换模型前先跑一遍,通过率不达标就不上。我自己的习惯是:任何一次提示词改动,不跑评测不提交。这个习惯帮我省了无数次线上回滚。
平台化这件事,前期多花两天搭评测和日志,后期能省两周的排查时间。希望帮到你。
本文还有配套的精品资源,点击获取