构建一个能说会做的智能体

能说会做的课程任务智能体设计与实现

| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Class78-Grade2024-CS |
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Class78-Grade2024-CS/homework/16761 |
| 这个作业的目标 | 构建一个具备自然语言对话和工具调用能力的 AI 智能体,让它不仅能“说”,还能调用本地工具完成任务,并完成需求描述、业务流、实现说明、使用示例、GitHub 链接和个人心得等文档。 |
| 学号 | 3124004235 |
| 姓名 | 蔡栩跃 |

GitHub 项目地址:https://github.com/pqbwdqm2vq-hub/course-agent-demo

  1. 需求描述

本次作业要求实现一个“能说会做”的 AI Agent。所谓“能说”,是指智能体能接收中文自然语言输入,并给出自然语言回复;所谓“会做”,是指智能体不能只聊天,还要能调用工具完成实际操作。

我选择做一个面向学生日常学习场景的课程任务与笔记智能体。它可以帮助用户完成以下事情:

  1. 用自然语言添加课程待办任务;
  2. 查看当前待办任务列表;
  3. 根据编号完成某个任务;
  4. 保存学习笔记;
  5. 查询城市天气并给出简单出行建议;
  6. 执行安全的四则运算;
  7. 把任务清单导出为本地文本文件。

这个智能体不依赖第三方 API,也不需要联网,适合课堂演示和提交作业。后续如果接入大模型,也可以在现有工具层上扩展成真正的大模型驱动 Agent。

  1. 业务流描述

本项目采用经典 Agent 循环:

用户自然语言输入
→ 意图识别
→ 实体提取
→ 选择工具
→ 调用工具执行
→ 观察执行结果
→ 生成自然语言回复

具体流程如下:

  1. 用户输入一句中文,例如:“帮我记下来周五18:00前交软件工程作业”。
  2. 智能体识别出用户想添加任务。
  3. 从句子中提取任务标题“交软件工程作业”和截止时间“周五18:00”。
  4. 调用 add_task 工具,把任务写入本地 JSON 文件。
  5. 工具返回执行结果。
  6. 智能体把结果转换成中文回复:“已记下任务:交软件工程作业,截止时间:周五18:00,优先级:普通。”

如果用户输入“我有哪些任务”,智能体会识别出查询意图,读取本地任务文件并返回任务列表。

如果用户输入“完成第1个任务”,智能体会提取编号 1,调用完成任务工具,把对应任务标记为已完成。

  1. 实现说明

3.1 技术选型

本项目使用 Python 标准库实现,不安装额外依赖,因此运行环境要求低,老师拿到代码后可以直接运行。

核心模块如下:

模块 作用
OfficeAgent 智能体主类,负责理解输入、选择工具、生成回复
TaskStore 本地文件存储层,负责读写任务和笔记 JSON 文件
understand() 自然语言意图识别
add_task() 添加任务工具
list_tasks() 查询任务工具
finish_task() 完成任务工具
save_note() 保存笔记工具
query_weather() 天气查询工具
calculate() 安全四则运算工具
export_tasks() 导出任务清单工具

3.2 Agent 主循环

智能体的核心不是聊天内容本身,而是“理解意图后执行工具”。主循环如下:

def run(self, user_text: str) -> str:
    intent, arguments = self.understand(user_text)
    if intent == "reply":
        return arguments["text"]

    handlers = {
        "add_task": self.add_task,
        "list_tasks": self.list_tasks,
        "finish_task": self.finish_task,
        "save_note": self.save_note,
        "query_weather": self.query_weather,
        "calculate": self.calculate,
        "export_tasks": self.export_tasks,
    }
    return handlers[intent](**arguments)

这段代码体现了 Agent 的基本结构:先理解用户输入,再选择对应工具,最后把工具执行结果返回给用户。

3.3 自然语言意图识别
为了避免依赖外部大模型 API,本项目当前使用规则识别自然语言。例如,如果句子中出现“作业”“实验”“报告”“考试”“复习”等词,就判断用户想添加任务:

task_words = ["作业", "实验", "报告", "会议", "考试", "复习", "提交", "项目", "答辩"]

if any(word in text for word in task_words):
    due = self._extract_due(text)
    title = re.sub(r"^(帮我|请)?(记下来|记一下|提醒我|要|需要|想|计划|准备|创建?)", "", text)
    title = title.replace(due, "").replace("前", "")
    return "add_task", {"title": title, "due": due, "priority": "普通"}

这种方式虽然不如大模型灵活,但优点是稳定、离线、可复现。

3.4 工具层设计
每个功能都被封装成独立函数。例如添加任务:

def add_task(self, title: str, due: str = "未设置", priority: str = "普通") -> str:
    tasks = self.store.load_tasks()
    next_id = max((task.task_id for task in tasks), default=0) + 1
    tasks.append(Task(next_id, title, due, priority, False))
    self.store.save_tasks(tasks)
    return f"已记下任务:{title},截止时间:{due},优先级:{priority}。"

把功能封装成工具的好处是:后续如果要接入 MCP 或大模型 Function Calling,只需要把这些函数注册成工具,不需要重写业务逻辑。

3.5 安全计算
四则运算没有直接使用危险的 eval(),而是通过 ast 模块限制表达式类型,只允许数字、加减乘除、括号和一元正负号:

if isinstance(node, ast.BinOp) and type(node.op) in BINARY_OPERATORS:
    return BINARY_OPERATORS[type(node.op)](visit(node.left), visit(node.right))

这样可以避免用户输入恶意代码,也比直接使用 eval() 更安全。

3.6 本地数据存储

任务和笔记分别保存在本地 JSON 文件中:

data/tasks.json
data/notes.json

这样做的好处是结构简单,调试方便,不需要安装数据库。虽然当前使用本地文件,但存储层和业务层是分开的,后续可以替换成数据库或云端存储。

  1. 使用示例

4.1 运行环境
Python 3.10 或更高版本
无需安装第三方依赖

4.2 运行演示

python3 office_agent.py --demo

实际运行结果如下:

你:帮我记下来 周五18:00前交软件工程作业
智能体:已记下任务:交软件工程作业,截止时间:周五18:00,优先级:普通。

你:帮我记一下 明天上午复习智能体课件
智能体:已记下任务:复习智能体课件,截止时间:明天上午,优先级:普通。

你:我有哪些任务
智能体:当前任务清单:
1. [待完成] 交软件工程作业(截止:周五18:00,优先级:普通)
2. [待完成] 复习智能体课件(截止:明天上午,优先级:普通)

你:完成第1个任务
智能体:已完成:交软件工程作业。

你:广州天气怎么样
智能体:广州当前多云,26~33℃。建议:午后有分散阵雨,出门建议带折叠伞。

你:计算 (12 + 8) * 3 / 2
智能体:计算结果:(12 + 8) * 3 / 2 = 30.0

你:记一下:Agent循环是理解、工具调用、观察、回复
智能体:已保存第 1 条笔记:Agent循环是理解、工具调用、观察、回复

你:导出任务
智能体:任务清单已导出到:tasks_export.txt

4.3 单轮调用

python3 office_agent.py --say "帮我记下来 周五18:00前交软件工程作业"

4.4 交互式对话

python3 office_agent.py

输入 退出 即可结束对话。

  1. 测试情况

本次测试覆盖以下场景:

测试编号 输入 预期结果 实际结果
1 帮我记下来周五18:00前交软件工程作业 成功添加任务,并提取截止时间 通过
2 帮我记一下明天上午复习智能体课件 成功添加任务,并提取“明天上午” 通过
3 我有哪些任务 返回任务列表 通过
4 完成第1个任务 第一条任务变为已完成 通过
5 广州天气怎么样 返回广州天气和建议 通过
6 计算 (12 + 8) * 3 / 2 返回 30.0 通过
7 记一下:Agent循环是理解、工具调用、观察、回复 成功保存笔记 通过
8 导出任务 生成 tasks_export.txt 通过
  1. 个人心得

这次作业让我真正理解了 Agent 和普通聊天程序的区别。普通聊天程序更像是“你问我答”,而 Agent 的关键在于:它能够理解用户想做什么,然后选择合适的工具去执行,最后把执行结果反馈给用户。

在实现过程中,我最先犯的错误是把“记一下”都当成笔记保存,结果“帮我记一下明天上午复习智能体课件”被错误识别成了笔记。后来我调整了意图判断顺序:先识别任务关键词,再处理带冒号的笔记,这个问题才解决。这个过程让我意识到,自然语言处理看起来简单,实际上有很多边界情况。

另一个收获是工具层设计。一开始我把所有逻辑都写在一个函数里,后来把添加任务、查询任务、完成任务、天气查询、计算等功能拆成独立函数,代码结构立刻清晰了很多。如果以后要把这些工具接入 MCP 或大模型 Function Calling,这种封装方式会很有帮助。

最后,我认为一个合格的 Agent 不一定一开始就要接入很复杂的大模型。先把“理解意图—选择工具—执行工具—返回结果”这个基本循环做正确,再逐步替换底层模型,反而是更稳妥的开发方式。

截屏2026-10-24 23.31.47
截屏2026-10-24 23.31.55
截屏2026-10-24 23.55.24
截屏2026-10-24 23.55.29
截屏2026-10-24 23.55.35

posted @ 2026-10-25 00:00  ybaiai  阅读(16)  评论(0)    收藏  举报