计科8班小组软工作业--构建一个能说会做的智能体
这个作业属于哪个课程 :https://edu.cnblogs.com/campus/gdgy/Class78-Grade2024-CS
|-------------------|-----------------|
这个作业要求在哪里 : https://edu.cnblogs.com/campus/gdgy/Class78-Grade2024-CS/homework/16761
这个作业的目标 :理解智能体(Agent)的基本概念与核心组成,搭建一个具备感知、决策、行动能力的简易智能体;学习智能体的运行逻辑,区分智能体与普通程序的差异;通过实践掌握智能体的任务拆解、提示词设计、工具调用流程,验证智能体自主完成指定任务的能力,总结实现过程中的难点与优化方向。
小组队员的学号 :3124004466、3124004479、3124004473
本智能体的Agent说明文档:https://www.cnblogs.com/duan245790/p/23386067
校园小助手:基于 MCP 的“会说会做”智能体
课程作业 · 智能体(Agent)实践
代码的仓库地址:【 https://github.com/Duan-12345/Duan-12345/blob/main/campus_agent.py 】
一、我们想做的东西
作业要求做一个“能说 + 会做”的智能体。我们观察了一下学生日常:记事情用备忘录、算数用计算器、查天气用 App、写点东西再开个文档——四件小事,四个入口。于是决定做一个只要说人话就能把事办完的校园助理。
它支持四类动作:
| 你说 | 它做的事 |
|---|---|
| 帮我记一条待办:周五 23:00 前交实验报告 | 写入待办清单,返回编号 |
| 看看我的待办 / 把第 1 条标记完成 | 查询、勾选完成 |
| 算一下 (128+72)*3/4 | 安全求值,返回 150 |
| 明天要去广州开会,那边热不热? | 查天气,用人话总结 |
| 帮我把组会要点写成笔记:…… | 生成 Markdown 笔记 |
| 读一下组会要点 | 读回笔记内容 |
这里的“做”是真的做:写的是磁盘上的文件,查的是真实天气接口,不是模型嘴上说一句“已帮你完成”。
二、需求描述
功能需求
- F1 待办管理:新增 / 查看 / 标记完成,重启不丢数据。
- F2 四则运算:支持括号、乘方、整除、取模;非法表达式给提示而不是崩溃。
- F3 天气查询:给城市名返回温度、天气、体感、湿度。
- F4 笔记读写:写 Markdown、读回内容。
- F5 多轮对话:保留最近几轮上下文,可一键清空。
- F6 一键演示:
--demo跑固定脚本,方便验收。
非功能需求
- 省 token:作业允许自由发挥,我们把“少花 token”当成硬指标(下面有对照实验)。
- 能离线降级:没有 API Key 时,确定性指令照样能执行。
- 安全:计算不用
eval;文件工具限制目录;Key 不进仓库。 - 零依赖:纯 Python 标准库,
python run.py直接跑。
三、业务流描述
核心设计是两级路由:
用户输入
│
├─ 本地路由(正则,0 token)
│ 命中 → 直接调 MCP 工具 → 回复
│
└─ 未命中 → 大模型(携带工具清单)
├─ 模型返回 tool_calls → 调工具 → 结果回填
└─ 模型生成最终回复
为什么这么分? “算一下 (128+72)*3/4”“看看我的待办”这类句子语法完全确定,让大模型去“理解”纯属浪费;而“明天去广州开会,那边热不热”需要理解指代(“那边”=广州)和意图(查天气),必须交给模型。第一级判断“是否确定”,第二级处理“不确定”。
一次查天气的完整链路:
用户 → CLI → Agent ──route() 未命中──→ 大模型
│ │
│ tool_calls: weather(广州)
│ ▼
└── MCP tools/call → MCP 服务器 → open-meteo
│
结果回填模型 → 生成人话回复 → 打印
异常分支也都有兜底:模型报错重试两次后给可读提示;工具异常转成 isError 交给模型解释;模型连续调工具不收敛最多 3 轮;天气接口 open-meteo 失败自动转 wttr.in。
四、实现说明
4.1 技术选型
- 协议:Model Context Protocol(MCP),stdio + JSON-RPC 2.0。
- 模型:DeepSeek
deepseek-chat,OpenAI 兼容接口,只用urllib调,不引 SDK。 - 语言:Python 3.9+,零第三方依赖。
4.2 为什么真的用 MCP
MCP 把“能力”和“大脑”解耦:工具跑在独立子进程,通过标准协议暴露给任何 MCP 客户端。所以我们写一次工具,既能给自己用,也能直接挂到 Claude Desktop 上——配置文件填上 command: python, args: ["-m", "mcp_server.server"] 就行。
为了“零依赖也能跑”,我们按 MCP 规范实现了最小子集,而不是装官方 SDK:
| 方法 | 作用 |
|---|---|
initialize |
协商协议版本,取 serverInfo |
notifications/initialized |
通知,无响应 |
tools/list |
返回工具名、描述、JSON Schema |
tools/call |
执行工具,返回 content[].text |
一条真实报文:
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"calc","arguments":{"expression":"(128+72)*3/4"}}}
{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"(128+72)*3/4 = 150"}],"isError":false}}
踩过的坑:stdout 是协议通道,第一版在服务器里 print 调试信息,客户端立刻 JSON 解析失败。后来所有日志都改走 stderr。
4.3 工具清单
| 工具 | 参数 | 实现要点 |
|---|---|---|
todo_add |
text |
追加到 data/todos.json,自增编号 |
todo_list |
— | 只回显最近 20 条 |
todo_done |
id |
幂等 |
calc |
expression |
ast 白名单求值,禁函数调用与属性访问 |
weather |
city |
open-meteo 为主,wttr.in 兜底 |
file_write |
name, content |
限制在 data/files/,自动补 .md |
file_read |
name |
截断到 800 字 |
4.4 三个关键实现细节
(1)计算器绝不用 eval。 用户输入是不可信数据,eval("__import__('os').system('...')") 就是一条命令执行漏洞。我们用 ast.parse(mode="eval") 解析后按白名单递归求值,只放行数字常量和四则、整除、取模、乘方、一元正负,并限制指数 ≤ 64、结果 ≤ 1e15(防 9**9**9 卡死)。
(2)文件工具做沙箱。 _safe_path() 拒绝 ../、/、绝对路径、超长文件名,再 resolve() 后二次校验仍在 data/files 下。
(3)工具 schema 一处定义、两处复用。 MCP 的 inputSchema 直接转成 OpenAI 的 function.parameters,不用维护两份定义。
4.5 省 token 的具体做法
| 手段 | 效果 |
|---|---|
| 本地路由优先 | 8 轮里 5 轮完全不调模型,0 token |
| 系统提示词 60 字 | 约 40 tokens |
| 工具描述一行 | 7 个工具约 200 tokens |
| 历史只留 3 轮,工具报文不写回历史 | 上下文不随轮次膨胀 |
max_tokens=256,temperature=0 |
回复短且稳定 |
| 工具输出截断 600 字 | 防大文件撑爆上下文 |
五、使用示例(功能演示)
cp .env.example .env # 填 DEEPSEEK_API_KEY
python run.py --demo
真实运行日志:
[01] 我:帮我记一条待办:周五 23:00 前交软件工程实验报告
↳ 调用工具 todo_add(text='周五 23:00 前交软件工程实验报告')
↳ 路径:本地路由(0 token),本轮 0 tokens
小助手:已添加待办 #1:周五 23:00 前交软件工程实验报告(当前共 1 条未完成)
[02] 我:看看我的待办
↳ 调用工具 todo_list()
↳ 路径:本地路由(0 token),本轮 0 tokens
小助手:待办(未完成 1 条):
[ ] #1 周五 23:00 前交软件工程实验报告
[03] 我:把第1条标记完成
↳ 调用工具 todo_done(id=1)
小助手:已完成 #1:周五 23:00 前交软件工程实验报告
[04] 我:算一下 (128+72)*3/4
↳ 调用工具 calc(expression='(128+72)*3/4')
小助手:(128+72)*3/4 = 150
[05] 我:明天要去广州开会,那边现在热不热?
↳ 调用工具 weather(city='广州')
↳ 路径:大模型 + 工具调用,本轮 1643 tokens
小助手:广州现在 33.9°C,晴,体感 37.5°C,挺热的,注意防晒补水。
[06] 我:帮我把今天的组会要点写成笔记:确定选题为校园智能助理,下周三前完成原型
↳ 调用工具 file_write(name='组会要点', content='组会要点\n1. 确定选题:校园智能助理\n2. 下周三前完成原型')
小助手:已写入笔记「组会要点.md」:选题为校园智能助理,下周三前完成原型。
[07] 我:读一下组会要点
↳ 调用工具 file_read(name='组会要点')
小助手:组会要点
1. 确定选题:校园智能助理
2. 下周三前完成原型
[08] 我:你好,你都能做什么?
小助手:我能管待办(增删查)、算数、查天气、读写笔记。需要哪样?
===== 演示结束 =====
模型调用 5 次 | 输入 3990 + 输出 183 = 4173 tokens(缓存命中 2944) | 本地路由命中 5 次 | 工具调用 7 次
省 token 的对照实验
同一份 8 轮脚本,只把本地路由关掉(python run.py --demo --no-router):
| 指标 | 路由开启 | 全部交给大模型 | 变化 |
|---|---|---|---|
| 模型调用次数 | 5 | 15 | −67% |
| 输入 tokens | 3 990 | 11 571 | −66% |
| 输出 tokens | 183 | 455 | −60% |
| 合计 | 4 173 | 12 026 | −65% |
同样的任务,token 只要三分之一。这是整个项目里最划算的一步优化。
六、遇到的问题
- stdout 污染协议:调试
print让客户端解析失败,改 stderr 后正常。 - Windows 控制台中文乱码:
sys.stdout.reconfigure(encoding="utf-8")解决。 - 天气接口不稳定:单接口失败就整轮失败,改成双源兜底 + 超时 8 秒。
- 路由误伤:早期规则把“帮我记个待办”也拦下来,没给内容就报错。后来要求必须匹配到内容,并把语义模糊的句子一律放行给模型。
七、GitHub 仓库
【待填:GitHub 公共仓库链接】
仓库结构:run.py 入口、agent/(cli / brain / router / mcp_client)、mcp_server/(server / tools)、tests/、docs/。密钥只放 .env,已在 .gitignore 中排除。
八、小组分工
| 成员 | 学号 | 负责内容 |
| --- | --- | --- | --- |
| 【段旷卓】 | 【3124004466】 | 需求分析、MCP 服务器与工具实现 |
|【刘俊宁】 | 【3124004479】 | 大模型接入、工具调用循环、token 优化 |
| 【李彦峰】 | 【3124004473】 | 本地路由、测试、文档与演示 |
九、小组成员心得
【段旷卓】 【本次作业我主要负责需求梳理以及 MCP 服务器和工具的开发实现。在前期需求分析阶段,我们确定基于 MCP 协议构建一个具备文件读写、命令执行能力的本地智能体,明确智能体可以操作本地文件系统的能力边界。开发 MCP 服务时,我学习了 Model Context Protocol 的通信规范,封装对应的工具函数,提供给大模型调用。
我最大的感受是,MCP 协议给智能体提供了一套标准化的工具接入方式,不再需要自己自定义工具调用格式。同时我也意识到安全问题:给智能体开放本地文件操作权限风险很高,必须在服务层做好权限和操作范围限制。这次实践让我理解了 MCP 在 Agent 体系里的定位,它是连接大模型和本地环境的桥梁,也为后续大项目里智能体工具扩展打下基础。】
【刘俊宁】 【我负责对接大模型、搭建 Agent 思考 - 调用的循环逻辑,同时做 prompt 与 token 优化。核心工作是让大模型可以识别 MCP 提供的工具,在合适时机发起工具调用,拿到 MCP 返回结果之后继续推理,形成闭环。
开发过程里遇到不少难点:模型有时候不会正确生成符合 MCP 规范的调用指令,或者重复调用工具、产生冗余对话。我通过精简系统提示词、裁剪历史消息、控制上下文长度来降低 token 消耗,提升响应速度。这次作业让我体会到智能体和传统程序最大区别:程序流程不是写死的,由大模型自主决策执行路径。同时也认识到 token 开销、上下文管理是智能体工程落地里不可忽视的工程问题。】
【李彦峰】 【我的工作是搭建本地路由连通各个模块,设计测试用例,对整套智能体进行功能与边界测试,整理截图、撰写项目文档,准备演示内容。
测试智能体和普通软件测试差别很大,除了正常功能,还要测试异常场景:非法文件路径、错误指令、模糊输入等,观察智能体会不会产生幻觉、越权操作。在反复测试中,我记录下稳定性问题并反馈给组员迭代优化。整理博客园文档和演示材料的过程,帮助我把整套 MCP 智能体的架构从头到尾梳理清楚。我认识到智能体系统是多组件协同的工程,路由连通、测试验证、文档交付同样是软件工程中重要的一环。】
运行环境:Python 3.9+,无第三方依赖;模型 deepseek-chat;测试 20 项全部通过。

浙公网安备 33010602011771号