大家好,我是AI淇橦学。
上期我们做出了第一个能真正跑通的工具模块——读模板、填内容、保存文件,整个流程在终端里跑起来了。
但仔细想想,第3期的程序里其实没有任何「AI」——write_content 做的只是字符串替换,把【客户名称】换成「深圳科技有限公司」,这是最普通的文本处理,和 AI 没有关系。
这一期我们做两件事:
第一件:接入大模型 API,给 Agent 装上真正的 AI 大脑。 从这期开始,Agent 里的决策和理解,都由真实的大模型来完成。
第二件:给 Agent 装上记忆系统。 建立客户资料知识库,输入「深圳那家做传感器的」,大模型理解你的意思,找到对应客户,自动填写报价单。
做完这期,你会得到:
- 一个封装好的
llm.py模块,可以调用国产大模型(本教程使用智谱 GLM 的glm-4-flash) - Agent 能记住你的使用偏好(不用每次重新配置)
- 客户资料知识库,用大模型做语义理解,输入关键词自动匹配客户
- 在终端里演示:说「深圳科技」,大模型理解后找到客户,自动完成报价单填写
一、AI 为什么没有记忆——上下文窗口
在动手之前,先把一个底层概念说清楚,否则后面的设计你会看不懂为什么要这么做。
把 AI 的工作状态想象成一张桌面。你说的话、AI 的回复、工具执行的结果,全都摆在这张桌面上。
问题是:这张桌面面积有限。 内容太多,最早放上去的东西就会被推掉——AI 就「忘记」了。这个桌面大小叫「上下文窗口」,以 Token 计量(约 1 个汉字 = 1.5 个 Token),不同模型上限不同,但都有上限。
更关键的是:每次对话结束,桌面清空。 下次打开程序,是全新的空桌面,上次的一切不见了。
这就是 AI 「没有记忆」的本质——不是忘了,是根本没有地方存。
所以,要给 Agent 加记忆,必须由我们自己来设计「存在哪里、什么时候取出来用」。
二、三种记忆类型,分别解决不同问题
记忆问题没有一个万能方案,针对不同场景有三种机制:
第一种:短期记忆
就是当次任务的上下文。Agent 在处理一份报价单的过程中,记住「已经填了哪些字段、还剩哪些没填」——任务完成后这些信息就可以丢弃了。这种记忆第3期的程序里已经天然存在,不需要额外设计。
第二种:长期记忆
把重要的配置信息存入本地文件,下次启动程序还在。比如:你的模板文件夹路径、日期格式偏好、默认报价有效期……这些信息不应该每次都要重新告诉 Agent。
第三种:知识库记忆(RAG)
你有几十个客户的资料,不可能每次都全部读进来让 AI 处理(Token 不够用)。RAG 的作用是:建一个可以搜索的客户资料库,你说「深圳科技」,它自动找到最匹配的客户信息。
这一期我们把第二种和第三种都做出来。
三、RAG 是什么——用档案柜来理解
RAG = Retrieval Augmented Generation,检索增强生成。名字拗口,但思路很简单:
💡 一句话理解:在 AI 执行任务之前,先从你的资料库里找到最相关的内容,一起交给 AI 参考——让它基于真实资料工作,而不是靠猜。
用生活场景类比:你有一个装了几百份文件的档案柜。每次需要某份文件时,不是把整个档案柜搬给助理(搬不动),而是先在档案柜里找到最相关的那几份,只把这几份给助理参考。RAG 做的就是这件事——自动地、快速地从大量文档里找到最相关的内容。
RAG 的完整 5 步流程:
① 切片:把每份客户资料切成小段,保留「来自哪个文件」的信息
② 向量化:把每段文字转换成一串数字(向量),代表这段文字的「语义指纹」。语义相近的文字,数字也相近——「深圳科技有限公司」和「深圳那家做传感器的」的向量比「深圳科技」和「北京餐饮公司」的向量近得多
③ 存入向量数据库:把所有向量存进专门的数据库,建好索引。这步只做一次
④ 检索:你输入「深圳科技」,这句话也被转成向量,在数据库里找距离最近的片段
⑤ 生成:把找到的客户资料 + 你的指令一起交给 AI,AI 基于真实资料来填报价单
为什么不直接用关键词搜索?
关键词搜索只找包含完全相同词语的内容。你搜「深圳科技」,找不到备注里写的「那家做工业传感器的深圳客户」。
语义搜索理解含义——「深圳科技」「深圳那家传感器公司」「SZ科技」在语义上都很接近,都能被找到。对销售场景来说,你经常只记得客户的片段信息,语义搜索能帮你找到,关键词搜索找不到。
四、接入国产大模型 API
重要说明: 这一期我们需要调用大模型 API,会消耗少量 API 费用(建议预留 5-10 元测试预算)。如果你暂时不想花钱,可以跳过这一期,直接看第 5 期。
国产大模型有很多选择,推荐以下几款(任选其一即可):
| 模型 | 厂商 | API 文档 | 特点 |
|---|---|---|---|
| 智谱 GLM | 智谱AI | open.bigmodel.cn/dev/api | 开发友好,免费额度较多,本教程使用 |
| 豆包 | 字节跳动 | www.volcengine.com/docs/82379 | 性价比高,文档清晰,适合新手 |
| 通义千问 | 阿里云 | help.aliyun.com/zh/model-st… | 功能完善,企业级稳定性 |
| 文心一言 | 百度 | cloud.baidu.com/doc/WENXINW… | 中文理解能力强 |
这期我以智谱 GLM 为例,使用 `glm-4.7 模型(其他模型流程类似,只是 API 调用方式不同)。
步骤 1:注册并获取 API Key
以智谱 GLM 为例:
- 打开 智谱AI开放平台
- 注册/登录账号
- 进入「API Key 管理」
- 创建新的 API Key,复制保存(只显示一次,务必保存好)
⚠️ 重要提示:API Key 相当于你的密码,不要泄露给他人,不要上传到公开的 GitHub 仓库。
步骤 2:封装 llm.py 模块
打开 Codex,,发送以下提示词:
📋 发给 Codex 的提示词(直接复制使用):
123456789101112131415161718192021222324252627282930帮我创建一个 llm.py 文件,封装智谱 GLM API 调用。
【使用智谱 GLM API】
- API 地址:https://open.bigmodel.cn/api/paas/v4/chat/completions
- 模型:glm-4-flash(智谱最新模型,速度快、成本低)
- 不使用 SDK,直接用 requests 库调用 API
【需要实现的函数】
1. chat(messages, model="glm-4-flash")
- 输入:messages 列表,格式为 [{"role": "user", "content": "..."}]
- 使用 requests 库调用智谱 API
- 请求头:Authorization: Bearer {API_KEY}
- 请求体:{"model": model, "messages": messages}
- 返回:模型的文本回复(字符串)
- 打印:API 调用的 token 使用情况
- 错误处理:网络超时、API 错误、返回格式错误等
2. encode_text(text)
- 输入:一段文本
- 调用智谱的 embedding API(如果有的话),或者用 chat 模型生成文本摘要
- 返回:文本的向量表示(list of floats)
- 如果没有 embedding API,可以暂时用简单的文本哈希代替
【技术要求】
- 使用 requests 库(Python 标准库,无需安装额外依赖)
- 代码有清晰的中文注释
- 每个函数要有错误处理(网络超时、API 错误等)
- API Key 从环境变量读取(os.getenv("ZHIPU_API_KEY")),不要硬编码
- 设置合理的超时时间(30秒)
Codex 生成代码后,在终端里设置环境变量:
Windows:
1set ZHIPU_API_KEY=你的API_Key
Mac/Linux:
1export ZHIPU_API_KEY=你的API_Key
然后运行测试:
1python llm.py
如果看到正常的返回结果,说明 API 接入成功。
五、实操开始:给 Agent 装上记忆系统
现在动手。我们在第3期的 sales-agent 项目基础上继续,不需要新建项目。
这期的实操分 4 步:
- 建立长期记忆——用配置文件存偏好设置
- 准备客户资料文件
- 用 Codex 生成知识库模块(基于大模型的 RAG)
- 测试:输入客户关键词,大模型自动匹配客户,完成报价单填写
第一步:建立长期记忆(配置文件)
把下面这段内容复制进去,根据你的实际情况修改:
12345678910{
"template_folder": "templates",
"output_folder": "output",
"date_format": "YYYY年MM月DD日",
"currency_format": "人民币",
"quote_validity_days": 30,
"company_name": "你的公司名称",
"salesperson_name": "你的名字",
"contact_phone": "你的联系电话"
}
📌 把
公司名称、你的名字、联系电话改成你自己的真实信息。这些信息会自动填入每份报价单。
这个文件就是 Agent 的「长期记忆」——每次启动程序,它都会先读取这个文件,知道你的偏好设置,不需要你重新告诉它。
第二步:准备客户资料文件
在 sales-agent 文件夹里新建一个子文件夹,命名为 customers。
然后在 customers 文件夹里新建一个文件 客户资料.json,把下面的内容复制进去(这是示例数据,之后可以换成你真实的客户)
当然,这个地方你也可以交给Codex去做,我这样是为了让你初次学习体验一下,你可以专门的创建一个客户资料,直接告诉Codex帮你保存就可以。
1234567891011121314151617181920212223242526272829303132333435[ { "id": "C001", "company_name": "深圳科技有限公司", "contact_person": "李总", "contact_phone": "138-0001-0001", "industry": "工业传感器制造", "address": "广东省深圳市南山区科技园", "notes": "主要采购工业传感器套件,对价格敏感,付款周期30天", "last_quote": "2025-03-15", "preferred_products": ["XS-2000B传感器", "XS-3000控制模块"]
},
{
"id": "C002",
"company_name": "上海贸易集团",
"contact_person": "王经理",
"contact_phone": "139-0002-0002",
"industry": "进出口贸易",
"address": "上海市浦东新区陆家嘴",
"notes": "批量采购为主,要求交货期不超过15天,开具增值税发票",
"last_quote": "2025-04-20",
"preferred_products": ["XS-1000基础套件"]
},
{
"id": "C003",
"company_name": "北京智能装备公司",
"contact_person": "张总",
"contact_phone": "136-0003-0003",
"industry": "智能制造设备",
"address": "北京市朝阳区望京",
"notes": "注重产品稳定性和售后服务,预算充足,决策周期较长",
"last_quote": "2025-05-01",
"preferred_products": ["XS-3000控制模块", "XS-4000高精度套件"]
}
]
📌 你之后可以随时往这个文件里加更多客户,每次加完,重新运行一次「建立知识库」的命令,新客户就会被纳入搜索范围。
你的文件夹结构现在应该是这样的:
12345678sales-agent/
├── templates/
│ └── 报价单模板.docx
├── output/
├── customers/
│ └── 客户资料.json
├── config.json
└── agent.py
第三步:用 Codex 生成知识库模块(基于大模型)
打开 Codex,然后把下面这段提示词发给 Codex:
📋 发给 Codex 的提示词(直接复制使用):
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354我在做一个销售报价单 Agent,现在需要给它加一个记忆模块,
文件名是 memory.py,请帮我实现以下功能:
【背景说明】
- 项目结构:customers/客户资料.json 存放客户信息,config.json 存放配置
- 客户资料是一个 JSON 数组,每个客户有:id、company_name、contact_person、
contact_phone、industry、address、notes、last_quote、preferred_products
- 我需要能通过自然语言描述来搜索客户,比如「深圳科技」或「做传感器的那家」
- 使用大模型 API 来做语义理解和匹配(已封装好的 llm.py)
【需要实现的功能】
1. load_config()
- 读取 config.json,返回配置字典
- 如果文件不存在,返回默认配置并打印提示
- 打印:已加载配置
2. load_customers()
- 读取 customers/客户资料.json,返回客户列表
- 打印读取到的客户数量
3. search_customer_with_llm(query, customers, llm_func)
- 输入:搜索关键词(自然语言描述)、客户列表、大模型函数
- 构造提示词:让大模型根据客户资料,找到最匹配的客户
- 提示词模板:
"""
以下是客户资料列表:
{客户列表,每个客户一行,包含 id、company_name、industry、notes}
用户搜索:{query}
请根据以上客户资料,找到最匹配的用户搜索意图的客户。
返回格式:只返回客户ID,例如:C001
"""
- 调用 llm.chat() 发送请求
- 解析大模型返回的客户ID
- 返回:匹配到的客户对象(如果找不到返回 None)
- 打印:搜索「xxx」,找到客户:公司名
4. save_quote_history(customer_id, output_file, config)
- 把本次报价记录追加写入 customers/报价历史.json
- 记录内容:客户ID、输出文件名、时间戳
- 打印:已记录本次报价历史
【在文件末尾加一个演示函数 demo_search()】
- 加载配置和客户数据
- 依次搜索三个关键词:「深圳科技」「上海贸易」「智能制造」
- 打印每次搜索结果,展示找到了哪个客户
【技术要求】
- 从 llm.py 导入 chat 函数
- 代码有清晰的中文注释
- 每个函数要有错误处理
- 大模型调用失败时提供友好的错误提示
Codex 直接生成并创建 memory.py。
测试知识库是否正常: 在终端里运行:
1python memory.py
你应该看到类似这样的输出:
第四步:把记忆系统接入主程序
现在把 memory.py 和第3期的 agent.py 连接起来,让整个流程变成:
输入客户关键词 → 大模型理解并搜索客户资料 → 自动填写报价单 → 保存新文件
打开 Codex 对话框,把下面这段提示词发给Codex(这次是修改 agent.py):
📋 发给 Codex 的提示词(直接复制使用):
12345678910111213141516171819202122232425262728293031323334353637383940请帮我修改 agent.py,把记忆系统接入主流程。
【当前状态】
- agent.py 里有三个工具函数:read_file、write_content、save_file
- memory.py 里有记忆模块:load_config、load_customers、search_customer_with_llm、save_quote_history
- llm.py 提供了 chat() 函数,可以调用大模型
【需要修改的内容】
把 agent.py 底部的 main() 函数改成新的交互流程:
1. 启动时自动加载配置(load_config)和客户数据(load_customers)
- 打印:Agent 启动中,正在加载配置和客户数据...
2. 读取报价单模板,打印找到的占位符列表
3. 在终端提示用户输入:「请输入客户名称或关键词(例如:深圳科技、上海那家贸易公司):」
4. 用 search_customer_with_llm 搜索客户,传入 llm.chat 函数
- 找到客户后,打印:找到客户「xxx」,是否使用?(y/n)
- 用户确认后继续
5. 从客户资料自动提取填写信息:
- 客户名称 → company_name
- 联系人 → contact_person
- 联系电话 → contact_phone
- 报价日期 → 今天的日期(用 config 里的 date_format 格式)
- 销售员 → config 里的 salesperson_name
- 其他模板里有但客户资料没有的字段(如产品名称、报价金额)→ 在终端提示用户手动输入
6. 调用 write_content 填写内容,调用 save_file 另存为新文件
- 文件名格式:报价单_{客户名称}_{日期}.docx
7. 调用 save_quote_history 记录本次操作
8. 打印完成提示:✅ 报价单已生成:output/报价单_xxx_xxx.docx
【注意】
- 从 memory.py 和 llm.py 导入需要的函数
- 产品名称、报价金额这类每次都不同的字段,用 input() 让用户在终端输入
- 代码要有清晰的中文注释
Codex 修改完 agent.py 后,在终端里运行:
1python agent.py
你会看到类似这样的交互过程:
1234567891011121314151617181920212223242526🚀 Agent 启动中,正在加载配置和客户知识库...
✅ 已加载配置
✅ 已读取 3 个客户,知识库建立完成
📋 读取模板:templates/报价单模板.docx
找到待填写字段 8 个:【客户名称】【联系人】【联系电话】【报价日期】
【产品名称】【产品型号】【报价金额】【备注】
请输入客户名称或关键词:深圳科技
🔍 搜索中...
找到客户「深圳科技有限公司」(匹配度:82%)
联系人:李总 | 电话:138-0001-0001
是否使用该客户?(y/n):y
✅ 已自动填入客户信息
需要手动输入以下字段:
产品名称:工业传感器套件
产品型号:XS-2000B
报价金额:58,000元
备注:含安装调试服务
✏️ 正在填写报价单...
💾 正在保存文件...
✅ 报价单已生成:output/报价单_深圳科技有限公司_2025年6月15日.docx
📝 已记录本次报价历史
第五步:验收结果
运行完成后,做以下检查:
功能验收:
- 输入「深圳科技」能正确找到「深圳科技有限公司」
- 输入「上海贸易」能正确找到「上海贸易集团」
- 客户的名称、联系人、电话自动填入,无需手动输入
- 手动输入的字段(产品、金额)能正确填入对应位置
- output 文件夹里生成了新文件,文件名包含客户名和日期
- 原始模板文件内容未被修改
- customers/报价历史.json 里有本次操作的记录
现在你的 Agent 有了记忆: 它认识你的客户,知道你的配置偏好,用大模型理解你的意图,并且会记录每次报价历史。
六、遇到问题怎么办
情况一:搜索结果不准确,找到了错误的客户
在 Codex 对话框里说:「search_customer_with_llm 搜索『深圳科技』时返回了错误的客户,请优化提示词,要求大模型仔细对比 company_name 和 notes 字段」
情况二:API 调用失败,提示认证错误
检查环境变量是否设置正确:
12345# Windows
echo %ZHIPU_API_KEY%
# Mac/Linux
echo $ZHIPU_API_KEY
确认 API Key 没有过期,且有足够的额度。
情况三:大模型返回格式不正确
在 Codex 对话框里说:「大模型返回的内容不是纯客户ID,而是一段话,请优化提示词,强调只返回客户ID(如 C001),不要返回其他内容」
情况四:客户资料.json 读取失败
确认文件路径:customers/客户资料.json,注意文件夹名是 customers,JSON 格式要正确(可以把文件内容粘贴到 jsonlint.com 检查格式是否正确)
情况五:日期格式显示不对
在 Codex 对话框里说:「报价日期显示的是 2025-06-15 这种格式,但我希望是 2025年6月15日,请修改日期格式化的代码,读取 config.json 里的 date_format 配置来决定格式」
七、这期做了什么——小结
这期你给 Agent 做了三件事:
第一件:接入了国产大模型 API
- 封装了
llm.py模块,可以调用智谱 GLM/豆包/通义千问等国产大模型 - 本教程使用智谱 GLM 的
glm-4-flash模型,不依赖 SDK,直接用 requests 调用 - 从此以后,Agent 的决策和理解都由真实的大模型来完成,不再是简单的字符串替换
第二件:装上了两层记忆
- 长期记忆(config.json):记住你的配置偏好,每次启动自动加载
- 客户知识库(RAG):存放客户资料,用大模型做语义理解,输入关键词自动匹配
第三件:实现了智能客户搜索
- 你输入「深圳科技」或「那家做传感器的」,大模型理解你的意思
- 从客户资料库里找到最匹配的客户,自动填入报价单
现在的流程是:你输入客户关键词 → 大模型理解并搜索客户 → 自动填入已知信息 → 你只需要补充产品和金额 → 生成报价单。
坦白说,做完这个功能之后,我觉得效率提升挺明显的。以前每次都要改代码里的客户信息,现在只需要输入一个模糊的关键词,大模型就能理解并自动匹配,省了不少时间。
这个方案特别适合的场景:
- 客户数量超过 10 个,经常需要复用客户信息
- 客户信息比较复杂(公司名、联系人、地址、备注等)
- 你经常只记得客户的部分信息(比如「深圳那家做传感器的」)
- 需要大模型理解你的自然语言表达,而不是精确匹配关键词
但现在还有一个问题:每次只能处理一个客户。如果你有 10 份报价单要做,还是要跑 10 次程序。下期我们来解决这个问题——给 Agent 加上规划能力,让它自己制定计划、批量处理多份报价单,你只需要告诉它「帮我处理这 10 个客户的报价单」,它自己把事情做完。
下期预告: 现在你每次只能处理一个客户。如果你有一批客户要同时出报价单,Agent 能不能自己规划步骤、批量搞定?第5期我们来做这件事——让 Agent 学会「自己想清楚要做什么,再去做」,从「你推一步它走一步」变成「你说目标,它自己做完」。
思考题: 你试着往 客户资料.json 里加了你真实的客户信息吗?用大模型搜索的时候准确吗?你用的是哪款国产大模型?体验如何?评论告诉我。
关注公众号「AI淇橦学」,和 AI 一起成长。
有问题或建议?后台留言即可。