简易AI Agent本地应答(自学AI Agent——Class1)
·
# -*- coding: utf-8 -*-
# @Time : 2026/6/6 10:46
# @Author : lhh
# @File : Studu_Demo.py
# @Project : python_learn
import json
from datetime import datetime
from abc import ABC, abstractmethod
#==============基类============
class TaskHandler(ABC):
'''所有任务处理器的基类'''
@abstractmethod
def can_handle(self, command: str)->bool:
"""
原本的代码应该是这么写的:
def can_handle(self, command)
raise NotImplementedError("子类必须实现") //提醒子类必须重写,否则报错
command: str 这个是新写法,告诉函数这个参数是字符串
->bool 告诉函数返回的应该是True或者Faults
"""
"""判断是否能执行这个命令"""
pass
@abstractmethod
def handle(self, args:str)->str:
"""执行具体逻辑,返回结果字符串"""
pass
def get_name(self)->str:
"""返回处理器名称(默认实现,子类可重写)"""
return self.__class__.__name__
#========子类:具体处理器==========
"""天气查询(模拟)"""
class WeatherHandler(TaskHandler): #新建一个子类继承父类TaskHandler
def can_handle(self, command: str) ->bool:
#父类函数重写,检测用户输入的命令关键词是否是"weather","w","天气"中的其中一个(w是weather的简写,可替换成其他的)
#"weather","w","天气"这仨是自定义的,可以随便写,我这里是因为这个代码是用来查询天气的,所以用这三个
return command in ("weather","w","天气")
def handle(self, city:str) ->str:
#判定用户是否输入了正确的城市名,从而判定输出的数据
#如果用户没有输入 城市名 ,则return "请告诉我城市,比如:weather 北京"
#如果用户没有输入 正确的城市名(字典中的城市) 则输出: xx天气:未知天气(模拟数据有限)
#如果输入了正确的城市名,则进行天气的输出,由于这里没有接入天气API,目前只用固定的字典进行应答
if not city:
return "请告诉我城市,比如:weather 北京"
#模拟天气数据(后面学asyncio时,改成调用真实API)
mock_data = {
"北京": "晴 25°C",
"上海": "小雨 22°C",
"广州": "多云 28°C"
}
weather = mock_data.get(city, "未知天气(模拟数据有限)")
#注意这里的.get()不是get函数,只是恰巧名字一样。这里的 字典.get() 是字典的方法,查字典用的
#字典.get(key, default) key:必须填 default:非必填
# .get() 就是拿着现成的 key(city)去字典里查,查到给真的,查不到给默认值,然后存进 weather ,最后 return 拼起来输出
return f"{city}天气:{weather}"
class ClacHandler(TaskHandler):
"""计算器"""
def can_handle(self, command: str) ->bool:
return command in ("calc","c","计算")
def handle(self, expression:str) ->str:
if not expression:
return "请输入算式,比如: clac 1+2*3"
try:
# 安全计算:只允许数字和运算符
allowed = set("0123456789+-*/.()")#创建一个集合
if all(c in allowed for c in expression):
#for c in expression:把算式里的每个字符都拎出来,叫c
#c in allowed:检查这个字符c在不在白名单里
#all(...):所有字符都必须通过检查,有一个不通过就是False
result = eval(expression)
#eval() 是 Python 的字符串执行器,把字符串当数学公式算,"1+2*3"
# 传进去 → 返回 7
# 然后 f-string 拼成 "结果:7" 返回
return f"结果:{result}"
return"算式包含非法字符"
# try 里的代码如果报错(比如除以0、括号不匹配),不会崩溃,而是跳到这里
# e是错误信息(比如"division by zero")
# 返回"计算错误:division by zero" ,程序继续运行
#except 是关键字(捕获), Exception 是所有错误的基类, as e 是把错误信息抓出来存到变量 e 里
except Exception as e:
return f"计算错误:{e}"
class NoteHandler(TaskHandler):
"""记事本(持久化到本地)"""
#这边定义了一个文件叫:notes.json
FILE = "notes.json"
def can_handle(self, command: str) ->bool:
return command in ("note", "n", "记事")
def handle(self, content:str) ->str:
if not content:
return "请输入内容,比如:note 记得交作业"
#读取已有记录
try:
#打开文件,用完自动关,不用手动close
#"r" 读模式
#encoding="utf-8" 指定编码,防止中文乱码
#这里是用open打开file,然后将打开的file命名为f,用with来保证自动关闭
with open(self.FILE,"r",encoding="utf-8")as f:
#json.load(f) 把JSON文件内容变成Python列表/字典
notes = json.load(f)
#捕获错误
except (FileNotFoundError,json.JSONDecodeError):
#出错时就当没有旧纪录,从空列表开始
notes = []
#添加新纪录
note = {
"time":datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"content":content
}
#追加到列表
notes.append(note)
#保存
with open(self.FILE,"w",encoding="utf-8")as f:
# json.dump(notes, f) 把Python列表变成JSON字符串写入文件
# ensure_ascii=False 中文不转义,直接写中文
# index = 2 格式化缩进,让所有JSON文件有换行和空格,方便人看
json.dump(notes,f,ensure_ascii=False,indent=2)
return f"已记录:{content}"
"""
上面那个版本是用了with方法,下面这个版本不用with
def handler(self, content: str) -> str:
if not content:
return "请输入内容,比如:note 记得交作业"
#====读文件====
notes = []
try:
f = open(self.FILE, "r", encoding = "utf-8") //手动打开
notes = json.load(f) //读取
f.close() //必须手动关闭
except (FileNotFoundError,json.JSONDecodeError):
notes = []
////注意:如果上面的json.load(f)报错了,f.close()根本执行不到,文件就会被一直占着
#====造数据====
note = {
"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"content": content
}
notes.append(note)
# ========== 写文件 ==========
f = open(self.FILE, "w", encoding="utf-8") # 1. 手动打开
json.dump(notes, f, ensure_ascii=False, indent=2)
f.close() # 2. 必须手动关闭
return f"已记录:{content}"
不用with的坏处就是:
1.
每次都要记着手动 f.close() ,忘了就内存泄漏
2.
如果 json.load(f) 那行报错了,程序直接跳到 except , f.close() 永远执行不到
3.
就像你开了串口、读了数据、中途出异常,串口没关,后面再开就报错
"""
class HelpHandler(TaskHandler):
"""帮助"""
def can_handle(self, command: str) ->bool:
return command in ("help","h","帮助")
def handle(self, args:str) ->str:
return """可用命令:
weather <城市> 查天气(示例:weather 北京)
calc <算式> 计算(示例:calc 1+2*3)
note <内容> 记事(示例:note 记得喝水)
help 显示帮助
exit 退出"""
#============核心出装:Agent路由器============
class Agent:
"""任务路由器——————Agent核心机制"""
def __init__(self):
#注册所有处理器(后面学框架时,这就是Tool Registry)
self.handlers = [
WeatherHandler(),
ClacHandler(),
NoteHandler(),
HelpHandler(),
]
#路由分发
def route(self, user_input:str)->str:
"""解析用户输入,路由到对应处理器"""
parts = user_input.strip().split(maxsplit=1)
#strip() 去掉用户输入字符串前后的空格和换行
#split(maxsplit = 1) 按照空格切割,最多切一次,分成两段
if not parts:
return "请输入命令"
command = parts[0].lower() #把切割出来的第一部分赋值给command,并转成小写
args = parts[1] if len(parts) > 1 else "" #把第二部分赋值给args 计算parts长度,大于1则args为该parts,否则为空
#多态:遍历所有处理器,找到能处理的
for i in self.handlers:
if i.can_handle(command):
return i.handle(args)
return f"未知命令:{command},输入 help 查看可用命令"
#========主程序=========
def main():
agent = Agent()
print("=====简易任务路由器=====")
print("输入 help 查看命令, exit 退出")
while True:
try:
user_input = input("\n> ").strip()
if user_input.lower() in ("exit","quit","退出"):
print("再见")
break
result = agent.route(user_input)
print(result)
except KeyboardInterrupt:
print("\n再见")
break
except Exception as e:
print(f"出错了:{e}")
if __name__ == "__main__":
main()
更多推荐

所有评论(0)