零基础学习AI智能体
AI agent (= LLM(模型)+memory(记忆)+planning skills(规划)+tool use(工具))
一、建立认知与体感
- ChatBot、RAG 和 Agent 的区别
| 维度 | Chatbot (聊天机器人) | RAG (检索增强生成) | Agent (智能体) |
|---|---|---|---|
| 核心隐喻 | 鹦鹉学舌 / 客服话术 | 开卷考试的学霸 | 能独立办事的员工 |
| LLM 角色 | 文本生成器 | 阅读理解 + 总结器 | 大脑 / 决策中枢 |
| 数据流向 | 用户输入 → LLM → 输出 | 用户输入 → 检索 → LLM → 输出 | 用户输入 → LLM → 规划/调用工具 → 观察结果 → 循环... → 输出 |
| 是否有外部交互 | ❌ 无(仅靠预训练知识) | ✅ 只读(读取知识库) | ✅ 读写+执行(调API、查库、写文件) |
| 控制流 | 单轮/多轮线性对话 | 单次检索-生成管道 | 动态循环 |
| 能否处理未知任务 | ❌ 只能回答预设问题 | ⚠️ 只能回答知识库里有的 | ✅ 能拆解陌生任务并尝试解决 |
| 工程复杂度 | 低 | 中 | 高 |
| 典型失败模式 | 幻觉、拒答 | 检索不准、引用错误 | 死循环、工具调用失败、成本爆炸 |
- 需要掌握的 python 基础
- 异步编程(asyncio) Agent多用于 I/O密集的场景
- async def:定义一个异步函数(协程)
- await:挂起当前任务,等待结果(只有遇到网络请求、文件读写等耗时操作时使用)
- asyncio.gather:核心!用于并发执行多个异步任务
- asycio.run():启动异步程序的入口
import asyncio import time # 模拟异步工具调用(实际开发中这里是 httpx 或 aiohttp 的网络请求) async def search_weather_tool(city: str) -> str: await asyncio.sleep(2) # 模拟网络延迟 2 秒 return f"{city}的天气是晴天" async def search_stock_tool(symbol: str) -> str: await asyncio.sleep(2) # 模拟网络延迟 2 秒 return f"{symbol}的股价是 150" async def agent_execute_tools(): print("Agent 开始并发调用工具...") start_time = time.time() # ✅ 核心:使用 gather 并发执行,总耗时约等于最慢的那个任务(2秒),而不是相加(4秒) results = await asyncio.gather( search_weather_tool("北京"), search_stock_tool("AAPL") ) print(f"工具返回结果: {results}") print(f"总耗时: {time.time() - start_time:.2f} 秒") # 运行异步程序 if __name__ == "__main__": asyncio.run(agent_execute_tools())
- 类型提示(type Hints)与 pydantic:Agent 的“契约与骨架”
- 让 LLM 调用工具时,框架(如 LangChain/LangGraph)会自动读取 python 函数的类型提示和 pydantic 模型,将其转换为 json schema 喂给大模型,告诉大模型“你该传什么参数”,如果没有类型提示,大模型就不知道该怎么调用你的工具;
- 核心就是:既让 LLM阅读用户的自然语言,并且对照 Pydantic 生成的 Schema,在内部做了一个 “语义到结构的映射,又将大模型(LLM)输出的“非结构化文本”安全、可靠地转化为程序可执行的“结构化数据”。
- 几种核心的语法
- 基础类型:
str,int,float,bool - 容器类型:
list[str],dict[str, int] - 可选类型:
str | None(代替以前的Optional[str])from pydantic import BaseModel, Field # ✅ 定义 Agent 工具的入参模型 class SearchRequest(BaseModel): """用于搜索信息的请求参数""" # 这个文档字符串会被提取给 LLM 看! query: str = Field(description="搜索的关键词", min_length=1) max_results: int = Field(default=5, description="最大返回结果数") tags: list[str] | None = Field(default=None, description="过滤标签") # 模拟 LLM 返回的 JSON 数据(通常是字符串) llm_raw_output = '{"query": "Agent 开发", "max_results": 10, "tags": ["AI", "Python"]}' # ✅ Pydantic 自动将 JSON 解析为 Python 对象,并进行类型校验 # 如果 LLM 返回的 max_results 是字符串 "10",Pydantic 会自动将其转为 int request_obj = SearchRequest.model_validate_json(llm_raw_output) print(request_obj.query) # 输出: Agent 开发 print(type(request_obj.max_results)) # 输出: <class 'int'>
- 基础类型:
- 常用数据处理:Agent 的“消化系统”
- agent 的数据处理主要围绕着“字典/JSON 的解析、上下文的裁剪、文本的清洗”
- 内置 json 模块:Agent 和外部系统交互的通用语言,
- 常用的函数:json.loads() 将 JSON 字符串 解析为 Python 对象 (dict/list)(注和Pydantic 区别是json.loads() 只负责把 JSON 字符串变成 Python 字典,它不关心内容对不对;而 Pydantic 在解析的同时,完成了校验、类型转换和结构化三件事)
- json.dumps()将 Python 对象 序列化为 JSON 字符串;
- 如何处理非标准的 json 对象(处理脏数据)
- 方案一:正则提取(最轻量,推荐首选)
import re import json def safe_json_loads(raw_text: str) -> dict: """从 LLM 的脏输出中安全提取并解析 JSON""" # 1. 尝试去除 Markdown 代码块标记 # 匹配 ```json ... ``` 或 ``` ... ``` md_pattern = r'```(?:json)?\s*\n?(.*?)\n?\s*```' match = re.search(md_pattern, raw_text, re.DOTALL) if match: raw_text = match.group(1).strip() # 2. 如果没匹配到代码块,尝试找第一个 { 和最后一个 } 之间的内容 if not raw_text.startswith('{'): brace_match = re.search(r'\{.*\}', raw_text, re.DOTALL) if brace_match: raw_text = brace_match.group(0) # 3. 尝试解析 return json.loads(raw_text) # 测试各种脏输入 dirty_outputs = [ '```json\n{"query": "AI"}\n```', '好的,结果如下:{"query": "AI"} 请查收', '{"query": "AI"}', # 干净的也能处理 ] for output in dirty_outputs: result = safe_json_loads(output) print(f"✅ 解析成功: {result}")
- Pydantic 容错(最稳健,生产级推荐)
from pydantic import BaseModel, ValidationError import re class SearchRequest(BaseModel): query: str max_results: int = 5 def parse_llm_output(raw_text: str) -> SearchRequest: """带容错的 LLM 输出解析""" try: # Pydantic v2 的 model_validate_json 比 json.loads 更宽容 return SearchRequest.model_validate_json(raw_text) except ValidationError: pass # 回退:用正则提取后再试 extracted = re.search(r'\{.*\}', raw_text, re.DOTALL) if extracted: return SearchRequest.model_validate_json(extracted.group(0)) raise ValueError(f"无法从 LLM 输出中提取有效 JSON: {raw_text[:200]}")
json_repair:自动修复几乎所有常见 JSON 错误pip install json_repair from json_repair import repair_json broken = "{'key': 'value',}" # 单引号 + 尾随逗号 fixed = repair_json(broken, return_objects=True) # 直接返回 dict print(fixed) # {'key': 'value'}
- 方案一:正则提取(最轻量,推荐首选)
- LLM api 交互
- Prompt(提示词)
- system prompt:开发者预设的角色和行为约束,比如"你是一个后端开发"
- user prompt:用户当前输入的具体问题或任务
- prompt的质量直接决定输出质量
- token(词元)是模型处理文本的最小单位,也是计费和上下文容量的基本度量
- 1个token约等于0.75个单词,约等于1个常用汉字
- 不同模型的tokenizer独立训练,同一段文本在不同的模型中切分结果可能不同
- 一次调用的消耗=输入token+输出token
- context window(上下文窗口)是模型本轮对话能看到的全部信息(system prompt +历史对话+工具返回结果等),而context window 是这些信息能容纳最大token数上限
- 不同模型窗口大小差异很大:8k/32k/128k/1m等
- 超出窗口的信息会被“遗忘”,这也是rag(检索增强生成)技术存在的意义
- Functing calling (工具调用)是让大模型从“只会说”变成“能做事”的关键能力,核心流程是:
- 定义工具说明书(json schema):告诉模型有哪些工具可用,参数是什么
- 模型决策:模型根据用户问题,返回“要调用哪个工具,传什么参数”的结构化指令
- 应用层执行:你的代码真正去调用API/函数
- 结果回灌:将执行结构返回给模型,模型根据此生成最终回答
- streaming(流式输出)是指模型不等待完整回答生成后再一次性返回,而是逐token/逐句实时推送给客户端,好处是:
- 用户感知延迟大幅降低(不用干等);
- 可以实时展示工具调用进度(如“正在查询天气”->"已获取结构"->正在生产建议);
- 技术实现通常基于SSE(Server-sent events)
- 百炼(以及所有大模型 API)绝对不会帮你执行任何外部工具,它做的事情只有三件:
- 读懂说明书:根据你传的 TOOLS JSON Schema,知道有哪些工具、参数是什么
- 决定调不调 + 传什么参:根据用户问题和对话历史,判断是否需要调用工具,如果需要,就返回一个包含函数名和参数的结构化数据(tool_calls)
- 组织最终回答:当你把工具的真实返回结果以 role="tool" 的消息回传后,模型根据这个结果生成自然语言回答
import json import requests from openai import OpenAI # ===== 配置 ===== client = OpenAI( api_key="sk-你的API_Key", base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", ) MODEL = "glm-5.2" # ===== 1) 工具说明书 ===== TOOLS = [{ "type": "function", "function": { "name": "get_weather", "description": "根据用户提供的城市名查询该城市当前天气", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名, 如 北京"} }, "required": ["city"] } } }] # ===== 2) 工具的真实实现(改成调真实API) ===== def get_weather(city): """调用和风天气API,返回真实天气数据""" # 第一步:根据城市名查城市ID geo_url = f"https://geocoding.qweather.com/v1/city/lookup?location={city}&key=你的和风天气Key" geo_resp = requests.get(geo_url).json() if geo_resp["code"] != "200": return {"error": f"找不到城市:{city}"} location_id = geo_resp["location"][0]["id"] location_name = geo_resp["location"][0]["name"] # 第二步:根据城市ID查实时天气 weather_url = f"https://devapi.qweather.com/v7/weather/now?location={location_id}&key=你的和风天气Key" weather_resp = requests.get(weather_url).json() if weather_resp["code"] != "200": return {"error": "天气查询失败"} now = weather_resp["now"] return { "city": location_name, "temp": now["temp"] + "°C", "feels_like": now["feelslike"] + "°C", "humidity": now["humidity"] + "%", "wind": now["wind_dir"] + " " + now["wind_speed"] + "km/h", "condition": now["text"], } TOOL_IMPL = {"get_weather": get_weather} # ===== 3) Agent 工具调用循环(完全不用改) ===== def agent_loop(messages, max_turns=5): for turn in range(max_turns): print(f"\n--- 第 {turn + 1} 轮调用模型 ---") response = client.chat.completions.create( model=MODEL, messages=messages, tools=TOOLS, ) msg = response.choices[0].message if not msg.tool_calls: print(f"模型最终回答: {msg.content}") return msg.content for call in msg.tool_calls: fn_name = call.function.name fn_args = json.loads(call.function.arguments) print(f"🔧 调用工具: {fn_name}({fn_args})") fn = TOOL_IMPL[fn_name] result = fn(**fn_args) print(f"📦 工具返回: {result}") messages.append(msg) messages.append({ "role": "tool", "tool_call_id": call.id, "content": json.dumps(result, ensure_ascii=False) }) return "已达最大调用次数,终止" # ===== 4) 运行 ===== if __name__ == "__main__": messages = [{"role": "user", "content": "北京天气怎么样?"}] result = agent_loop(messages) print("\n✅ 最终结果:", result)
- 感知模块(输入口、获取多模态信息并转换)
- 黑板理论(大模型的context window是一块面积有限的黑板,如果不加管理,最早的关键信息会被后续对话擦除)
- 工程化手段:
- System prompt(人设注入):锁定黑板顶端,固化“资深专家”人设;
- Context Management(记忆剪枝):定期压缩历史记录,剔除寒暄,保留关键结论;
- AI的知识来源与记忆管理
- 把和用户的对话存在知识库里,企业的私有知识也存在知识库里面,把用户的问题转换成向量,去向量知识库里面查找,找到相关的片段,就把相关的资料和企业的私有资料拼接在一起交给大模型再回答(RAG技术检索增强生成)

- 大脑模块(思考中枢,负责决策、推理与规划)
- 行动模块(输出端,调用工具与环境互动)
- 多智能体合作(不同的专业任务需要不一样的协同方式)
- 上下级协同

- 师生式协同

- 竞争式协同

适合开放性的复杂任务
- 上下级协同
- 大模型应用场景(三合一叫多模态模型)
- 自然语言处理(NLP)
- 语音处理(SLP)
- 图像视频处理
- gradio 轻量webUI ai简单前端交互
import gradio as gr import numpy as np import cv2 #功能实现 def image_to_skectch(image): gray_image = image.convert("L") invered_image = 255 - np.array(gray_image) blurred = cv2.GaussianBlur(invered_image, (21, 21), 0) invered_blurred = 255 - blurred pencil_sketch = cv2.divide(np.array(gray_image), invered_blurred, scale=255.0) return pencil_sketch #界面配置 demo = gr.Interface( fn=image_to_skectch, inputs = [gr.Image(label="上传图片",type="pil")], outputs = [gr.Image(label="铅笔画")], title="图像转铅笔画", description="将上传的图片转为铅笔画" ) demo.launch()
- 提示词工程(prompt engineering)是一项通过优化提示词(prompt)和生成策略,从而获取更好的模型返回结构的工程技术

- 应用可以设置自己的知识库,如果知识库没有就不返回(可以做自己公司业务的ai智能体)

json加【】 和[ ] 时可以自动转换类型,如果不加识别不出来,除非给实例examples
- 应用可以设置自己的知识库,如果知识库没有就不返回(可以做自己公司业务的ai智能体)
7. react 理论

8.agent的划分


8.RAG
- 纠正性RAG(CRAG)是一种用于RAG的策略,它包含对检索文档进行自我反思/评分

- self-rag是RAG的一种策略,它包含对检索到的文档和生成内容进行自我反思/自我评分

简单if判断反射(简单反射代理)---》加上记忆(基于模型的反射代理)---》加上目标有规划能力(目标性代理)---》考虑实现目标的最佳方式 如边际效用递减(效用型代理)----》从过去经验中学习,由学习元素、评议员、性能元素和问题生成器(学习型代理)
浙公网安备 33010602011771号