基于 HelloAgents SimpleAgent 构建软件开发学习助手:SoftwareDevHelper 的记忆、出题与自动化测试打分全链路设计
【免费下载链接】hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程项目地址: https://gitcode.com/datawhalechina/hello-agents
导读:本文以 Agent_Design.md 设计文档为核心,系统拆解 Datawhale《从零开始构建智能体》共创项目 SoftwareDevHelper 的实现。你将看到:如何用SimpleAgent+ 两个自定义工具(UserMemoryTool、CodeTestTool)完成"水平记忆 → 智能出题 → 开发指导 → 自动测试打分 → 反馈更新"的学习闭环,以及后端 FastAPI、前端多会话界面如何与 HelloAgents 框架无缝衔接。读完即可复刻一套可运行的"教-学-练-评"智能体系统,并理解框架扩展点的底层原理。
1. 项目定位与总体设计
SoftwareDevHelper 是一个专为软件开发初学者设计的智能学习助手,其核心设计目标包括:
- 水平评估与记忆:识别并持久化用户的编程水平(beginner / intermediate / advanced)与做题历史,实现跨会话的个性化学习;
- 智能出题:根据记忆中的用户水平,动态生成难度适宜的编程题目,或从网上搜索真实开发案例;
- 开发指导:在用户本地开发过程中,通过聊天界面提供针对性的代码建议与审查;
- 自动化测试与打分:接收用户上传的
.zip项目压缩包,由 LLM 动态编写pytest测试用例,自动执行并给出评分与代码审查反馈; - 反馈闭环:每次测试完成后更新用户的水平评估与做题记录。
整个系统基于HelloAgents框架的SimpleAgent范式构建(详见 helper_agent.py),架构上采用前后端分离:后端为 Python FastAPI 服务,前端为原生 HTML + JavaScript 的三栏式 Web 界面(左侧会话列表、中间聊天区、右侧用户档案),技术栈与依赖见 requirements.txt。
2. 系统提示词:用 Prompt 定义智能体的全部职责
智能体的行为和职责由系统提示词严格定义。完整提示词如下(与 Agent_Design.md 及源码 helper_agent.py 一致):
你是一个专业的软件开发学习助手。你的职责是: 1. 使用 user_memory 工具了解用户的当前编程水平和历史做题记录。 2. 根据用户水平,为他们出适合的编程题目,或者从网上搜索真实的开发案例。 3. 在用户开发过程中,提供有针对性的建议和指导。 4. 当用户完成开发并上传项目压缩包后,你需要: - 仔细分析题目要求。 - 编写严谨的 pytest 测试用例代码。注意:用户的代码通常在解压目录的某个子文件夹中(如 `test-projects/main.py`),你的测试代码需要能够递归查找 `.py` 文件并动态导入模块,而不是简单地假设代码在当前目录下。可以参考使用 `sys.path.insert(0, str(project_root))` 来辅助导入。 - 使用 code_test 工具,传入压缩包路径和你的测试代码,对用户的项目进行自动化测试。 - 根据测试结果给出最终打分和详细的代码审查反馈。 5. 任务完成后,使用 user_memory 工具更新用户的水平评估和做题记录。 请始终保持鼓励和专业的态度。这段提示词的设计要点值得剖析:
- 工具先行的记忆获取:第 1 条明确要求"先调用
user_memory工具再作答",确保智能体不会在缺乏用户画像的情况下盲目出题; - 测试用例的动态导入要求:源码在"编写 pytest 测试用例"处补充了关键工程细节——用户代码通常位于解压目录的子文件夹(如
test-projects/main.py),因此要求 LLM 生成的测试代码具备递归查找.py文件并动态导入模块的能力,并通过sys.path.insert(0, str(project_root))修正模块搜索路径,而非假设代码平铺在当前目录; - 鼓励性语气约束:末句"始终保持鼓励和专业的态度"界定了助教的交互风格,配合反馈机制中"指出优点和改进空间"的设计,共同营造适合初学者的学习体验。
3. 核心工具设计:记忆与代码测试
该智能体挂载了两个核心自定义工具,用于与文件系统、测试环境和记忆库交互。两个工具都在 helper_agent.py 中实现,均继承框架的Tool基类(框架中Tool基类定义了name、description、run(parameters)、get_parameters()等抽象接口,参考实现见 tools/base.py)。
3.1 UserMemoryTool:跨会话的个性化记忆
功能:管理用户的编程水平和做题历史记录,实现跨会话的个性化记忆。参数定义如下:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
action | string | ✅ | 操作类型,可选'get'(读取记忆)或'update'(更新记忆) |
level | string | ❌ | 用户的新水平评估(例如'beginner'、'intermediate'、'advanced') |
record | string | ❌ | 新完成的题目记录 |
底层实现(helper_agent.py)将数据持久化到data/user_memory.json文件:
- 构造时通过
_ensure_memory_file()自动初始化默认记忆:{"level": "beginner", "history": []}; action == "get"时,将整个记忆 JSON 序列化为字符串返回给 LLM;action == "update"时,若传入level则覆盖水平字段,若传入record则追加到history列表,随后写回文件;- 未知的
action返回ToolResponse.error,为 LLM 提供明确的错误信号。
值得说明的是,虽然工具的description是"获取或更新用户的编程水平和做题记录",但系统的另一条记忆入口在 Web 层:用户可以在右侧边栏手动修改水平等级(对应后端POST /api/user_memory/level),也可以一键清空记忆并重置为 beginner(对应DELETE /api/user_memory),这与工具层形成互补,详见 main.py。
3.2 CodeTestTool:压缩包解压、测试执行与自动打分
功能:接收用户上传的项目压缩包,自动解压,运行由 LLM 动态生成的测试代码,最后给出评分。参数定义如下:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
zip_path | string | ✅ | 用户上传的项目压缩包绝对路径 |
test_code | string | ✅ | 由智能体根据题目要求动态生成的pytest测试代码 |
底层实现(helper_agent.py)分五步执行:
- 清理并创建解压目录:先
shutil.rmtree删除旧的outputs/extracted,再重新创建,避免历史残留文件干扰本次测试; - 解压用户代码:使用标准库
zipfile.ZipFile的extractall解压到目标目录;解压失败(如损坏的压缩包)会返回错误信息; - 写入测试文件:将 LLM 生成的
test_code写入test_generated.py; - 运行测试:使用
subprocess.run调用["pytest", test_file_path, "-v"],指定cwd为解压目录,capture_output=True捕获标准输出与错误,并设置timeout=30秒防止测试卡死; - 评分与日志返回:根据退出码
returncode给出评分——全过为 100 分,有失败为 0 分,并将完整测试输出日志一并返回给智能体分析。超时(subprocess.TimeoutExpired)与执行异常也分别返回明确的错误信息。
result = subprocess.run( ["pytest", test_file_path, "-v"], cwd=self.extract_dir, capture_output=True, text=True, timeout=30 ) output = result.stdout + "\n" + result.stderr score = 100 if result.returncode == 0 else 0 # 简单评分逻辑,可根据 pytest 输出优化返回的 JSON 结构为{"score": 100/0, "test_output": <日志>, "status": "success"/"failed"},其中test_output是 LLM 编写审查反馈的核心依据——它能区分"逻辑错误"与"项目结构错误",从而给出更有针对性的修改建议。
4. 交互流程:从对话到打分的完整工作流
设计文档将系统工作流划分为五个阶段,源码层面每个阶段都有对应的 API 与数据落点支撑:
| 阶段 | 动作 | 关键实现 |
|---|---|---|
| 1. 初始化/对话 | 智能体通过UserMemoryTool获取用户水平与历史记录 | GET /api/user_memory预热前端档案;智能体run()时经 ToolRegistry 调度工具 |
| 2. 出题阶段 | 依据记忆信息生成难度适宜的题目 | 依赖系统提示词第 1、2 条约束,无需额外代码 |
| 3. 开发与指导 | 用户在本地开发,聊天界面实时答疑 | POST /api/chat普通消息通道 |
| 4. 提交与测试 | 上传.zip→ 后端保存 → 智能体写pytest→ 调用code_test | POST /api/upload_project |
| 5. 反馈与更新 | 输出打分与审查报告 → 更新记忆与水平 | UserMemoryTool.update+ 前端loadUserMemory()刷新 |
关键衔接点在于上传接口的"提示词注入"(main.py):
prompt = f"用户上传了项目压缩包,路径为:{file_path}。请根据当前题目要求,编写 pytest 测试用例,并使用 code_test 工具进行测试打分,最后给出反馈并更新用户水平记录。" response = agent.run(prompt)后端仅校验扩展名(只接受.zip)、以uuid重命名保存到outputs/uploads,然后把"文件绝对路径"以用户消息形式交给智能体,由 LLM 自主决定如何编写测试、调用工具、汇总打分——这正是 Agent 范式的核心:业务编排交由模型决策,工具只负责执行确定性操作。
5. 前后端集成与多会话管理
设计文档要求"支持多会话管理"、"聊天气泡内嵌工具调用可视化控件",这两点在 main.py 与 app.js 中均有完整落地。
5.1 会话持久化与上下文恢复
由于SimpleAgent默认在内存中保存历史,后端通过agent_sessions字典为每个session_id缓存独立的 Agent 实例(main.py)。同时实现稳健的上下文恢复机制:
- 每个会话的聊天记录持久化为
data/sessions/{session_id}.json,包含title、messages、updated_at字段; - 服务重启或刷新页面后,
get_or_create_agent()会读取会话文件,将历史消息逐条恢复为hello_agents.core.message.Message注入agent._history; - 恢复时只恢复纯文本而跳过不完整的
tool_calls,避免损坏的工具调用记录导致后续大模型请求报错——这是工程实践中一个很实用的细节。
5.2 工具调用可视化
/api/chat与/api/upload_project在处理请求前后分别记录agent.get_history()的长度,从而只遍历本轮新增消息,从中提取assistant消息中的tool_calls(含工具名、参数 JSON)以及对应tool角色的执行结果,组装后随响应返回前端(main.py)。
前端 app.js 的addMessage()会为每条工具调用渲染独立的"工具调用块",展示调用工具名、输入参数(JSON 格式化)与执行结果,让用户在聊天界面中实时看到智能体"后台做了什么",提升交互透明度。这与设计文档"在执行工具操作前,智能体会先向用户解释即将进行的操作"的反馈机制相呼应。
5.3 用户档案侧边栏
frontend/templates/index.html 的右侧边栏提供水平选择器(beginner / intermediate / advanced)、做题记录列表与"清空"按钮,所有改动实时写回后端user_memory.json;左侧边栏则展示按更新时间倒序排列的会话列表,支持切换、新建与悬停删除。
6. 模型与框架配置
6.1 模型配置
默认使用配置在.env中的大语言模型,项目在 README.md 中给出了兼容多种 LLM 的配置示例:
LLM_API_KEY=your_api_key_here LLM_BASE_URL=https://api-inference.modelscope.cn/v1/ LLM_MODEL_ID=Qwen/Qwen2.5-72B-Instruct源码中通过os.environ.get("LLM_MODEL_ID", "Qwen/Qwen2.5-72B-Instruct")读取模型 ID 并构造HelloAgentsLLM(helper_agent.py),因此同样支持替换为 Gemini 或 Azure OpenAI 等端点。需注意:修改.env后uvicorn --reload不会自动监听环境变量变化,需手动重启服务(详见 README 的"常见启动问题")。
6.2 框架特殊配置:禁用 TodoWrite
为兼容部分严格校验 JSON Schema 的模型(如 Azure/Gemini),初始化SimpleAgent时通过Config(todowrite_enabled=False)禁用了框架内置的 TodoWrite 工具:
from hello_agents.core.config import Config config = Config(todowrite_enabled=False) return SimpleAgent( name="SoftwareDevHelper", llm=llm, system_prompt=system_prompt, tool_registry=tool_registry, config=config )这是典型的**"框架默认行为与目标模型约束冲突"**的适配案例:TodoWrite 工具在大部分模型上提供任务规划能力,但其 JSON Schema 声明可能超出部分严格校验模型的能力范围,导致请求被拒;关闭后由自定义的两个工具承担全部职责,保证系统在多种模型后端上开箱即用。从框架的参考实现看,Config是统一配置类(基于 pydantic,支持默认值与类型校验,见 core/config.py),SimpleAgent在启用工具时会将工具描述拼入系统提示词并循环解析工具调用(见 agents/simple_agent.py),这也解释了为何本项目的系统提示词如此强调工具使用顺序。
7. 快速开始:本地运行与体验
依据 README.md,完整启动流程如下:
- 环境要求:Python 3.10+,推荐 Conda 环境;
- 安装依赖:
pip install -r requirements.txt - 配置 API 密钥:复制
README.md中的.env示例为.env并填入LLM_API_KEY/LLM_BASE_URL/LLM_MODEL_ID; - 进入项目目录并配置路径(确保模块可被导入):
cd Co-creation-projects/angelen-SoftwareDevHelper export PYTHONPATH=$PYTHONPATH:$(pwd) - 启动 FastAPI 后端:
uvicorn src.main:app --reload - 体验项目:浏览器访问
http://127.0.0.1:8000即可与助手对话、上传.zip项目并接收打分反馈。
常见问题排查:端口被占用时报[Errno 48] Address already in use,可改用--port 8001启动,或杀掉占用 8000 端口的进程(如lsof -ti :8000 | xargs kill -9)。
8. 从源码结构看框架扩展点
本项目是理解 HelloAgents 框架扩展机制的最佳样本之一。对比 helper_agent.py 与仓库中的框架参考实现,可以提炼出三条可复用的扩展规律:
- 自定义工具 = 继承
Tool+ 声明参数 + 实现run():框架只要求你提供name、description、run(parameters)与get_parameters()(见 tools/base.py),其余的消息循环、工具调用解析与结果回填全部由SimpleAgent完成; - 工具装配 =
ToolRegistry注册:在get_helper_agent()中tool_registry.register_tool(...)注册两个工具后传给SimpleAgent,工具即可被 LLM 感知并调用; - 行为定制 = 系统提示词 +
Config:提示词定义"何时用什么工具",Config(todowrite_enabled=False)定义"禁用哪些框架内建能力",二者叠加即可快速裁剪出领域专属的 Agent 行为。
9. 小结
SoftwareDevHelper 用最小化的框架改动(两个自定义工具 + 一段系统提示词 + 一套 Web 壳)实现了完整、可运行的"软件开发学习闭环"。其设计文档、后端 API、工具实现与前端交互在仓库中一一对应,可作为三类读者的学习素材:想搭建个性化教学 Agent 的产品开发者(参考其记忆机制与交互设计)、想扩展 HelloAgents 框架能力的开发者(参考其工具与配置扩展写法)、想理解 Agent 全链路编排的初学者(从系统提示词到工具执行再到反馈更新,一步不缺)。
相关文档与源码索引:设计文档 | README(含完整运行指南) | 工具与智能体实现 | FastAPI 后端 | 前端页面 | 前端交互逻辑
【免费下载链接】hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程项目地址: https://gitcode.com/datawhalechina/hello-agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考