计科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 只要三分之一。这是整个项目里最划算的一步优化。

六、遇到的问题

  1. stdout 污染协议:调试 print 让客户端解析失败,改 stderr 后正常。
  2. Windows 控制台中文乱码:sys.stdout.reconfigure(encoding="utf-8") 解决。
  3. 天气接口不稳定:单接口失败就整轮失败,改成双源兜底 + 超时 8 秒。
  4. 路由误伤:早期规则把“帮我记个待办”也拦下来,没给内容就报错。后来要求必须匹配到内容,并把语义模糊的句子一律放行给模型。

七、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 项全部通过。

posted @ 2026-10-21 14:48  段旷卓  阅读(32)  评论(0)    收藏  举报