From ce844b51e277b35f1e9d06a0772ba611922c4ff1 Mon Sep 17 00:00:00 2001 From: kuiran <2940496089@qq.com> Date: Mon, 29 Jun 2026 22:48:23 +0800 Subject: [PATCH 1/4] feat: add priority field to Task model --- campus_task/task_model.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/campus_task/task_model.py b/campus_task/task_model.py index 270afc8..7d24891 100644 --- a/campus_task/task_model.py +++ b/campus_task/task_model.py @@ -2,12 +2,14 @@ from typing import Optional class Task: def __init__(self, task_id: int, title: str, status: str = "待完成", - created_at: Optional[str] = None, deadline: Optional[str] = None): + created_at: Optional[str] = None, deadline: Optional[str] = None, + priority: str = "普通"): self.id = task_id self.title = title self.status = status self.created_at = created_at self.deadline = deadline + self.priority = priority def to_dict(self) -> dict: return { @@ -15,7 +17,8 @@ class Task: "title": self.title, "status": self.status, "created_at": self.created_at, - "deadline": self.deadline + "deadline": self.deadline, + "priority": self.priority } @classmethod @@ -25,5 +28,6 @@ class Task: title=data["title"], status=data.get("status", "待完成"), created_at=data.get("created_at"), - deadline=data.get("deadline") + deadline=data.get("deadline"), + priority=data.get("priority", "普通") ) \ No newline at end of file -- Gitee From 585d00b745089b54a03c03140cce70473f982a2a Mon Sep 17 00:00:00 2001 From: kuiran <2940496089@qq.com> Date: Mon, 29 Jun 2026 22:48:42 +0800 Subject: [PATCH 2/4] feat: add priority validation and filter function --- campus_task/task_service.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/campus_task/task_service.py b/campus_task/task_service.py index 5b24eb9..2149795 100644 --- a/campus_task/task_service.py +++ b/campus_task/task_service.py @@ -3,7 +3,12 @@ from datetime import datetime from .task_model import Task from .task_storage import load_tasks, save_tasks -def add_task(title: str, deadline: Optional[str] = None) -> Task: +def add_task(title: str, deadline: Optional[str] = None, priority: str = "普通") -> Task: + if not title or title.strip() == "": + raise ValueError("任务标题不能为空") + valid_priorities = ["高", "中", "低", "普通"] + if priority not in valid_priorities: + raise ValueError("优先级必须是: 高、中、低、普通") tasks = load_tasks() task_id = len(tasks) + 1 now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") @@ -12,12 +17,20 @@ def add_task(title: str, deadline: Optional[str] = None) -> Task: title=title, status="待完成", created_at=now, - deadline=deadline + deadline=deadline, + priority=priority ) tasks.append(task) save_tasks(tasks) return task +def get_tasks_by_priority(priority: str) -> List[Task]: + valid_priorities = ["高", "中", "低", "普通"] + if priority not in valid_priorities: + raise ValueError("优先级必须是: 高、中、低、普通") + tasks = load_tasks() + return [task for task in tasks if task.priority == priority] + def get_all_tasks() -> List[Task]: return load_tasks() -- Gitee From a12fb47c7e4841a013144b5b6eaec27f12e61a72 Mon Sep 17 00:00:00 2001 From: kuiran <2940496089@qq.com> Date: Mon, 29 Jun 2026 22:49:26 +0800 Subject: [PATCH 3/4] test: add unit tests for priority feature --- campus_task/__init__.py | 4 ++-- tests/test_priority.py | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 tests/test_priority.py diff --git a/campus_task/__init__.py b/campus_task/__init__.py index fb3b798..d8b267d 100644 --- a/campus_task/__init__.py +++ b/campus_task/__init__.py @@ -1,4 +1,4 @@ from .task_model import Task -from .task_service import add_task, get_all_tasks, mark_task_done, get_task_by_id +from .task_service import add_task, get_all_tasks, mark_task_done, get_task_by_id, get_tasks_by_priority -__all__ = ["Task", "add_task", "get_all_tasks", "mark_task_done", "get_task_by_id"] \ No newline at end of file +__all__ = ["Task", "add_task", "get_all_tasks", "mark_task_done", "get_task_by_id", "get_tasks_by_priority"] \ No newline at end of file diff --git a/tests/test_priority.py b/tests/test_priority.py new file mode 100644 index 0000000..20ca936 --- /dev/null +++ b/tests/test_priority.py @@ -0,0 +1,42 @@ +import unittest +import os +import sys +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from campus_task import add_task, get_tasks_by_priority, get_all_tasks + +class TestPriorityFeature(unittest.TestCase): + + def setUp(self): + if os.path.exists("tasks.json"): + os.remove("tasks.json") + + def tearDown(self): + if os.path.exists("tasks.json"): + os.remove("tasks.json") + + def test_add_task_with_priority(self): + task = add_task("测试任务", priority="高") + self.assertEqual(task.priority, "高") + + def test_add_task_default_priority(self): + task = add_task("测试任务") + self.assertEqual(task.priority, "普通") + + def test_get_tasks_by_priority(self): + add_task("高优先级任务", priority="高") + add_task("普通任务", priority="普通") + add_task("高优先级任务2", priority="高") + + high_tasks = get_tasks_by_priority("高") + self.assertEqual(len(high_tasks), 2) + + def test_invalid_priority_raises_error(self): + with self.assertRaises(ValueError): + add_task("测试任务", priority="无效") + + with self.assertRaises(ValueError): + get_tasks_by_priority("无效") + +if __name__ == "__main__": + unittest.main() \ No newline at end of file -- Gitee From f0b7657c7be813b8e73fd2613efaa81feb07a45c Mon Sep 17 00:00:00 2001 From: kuiran <2940496089@qq.com> Date: Tue, 30 Jun 2026 14:08:26 +0800 Subject: [PATCH 4/4] feat: complete all experiments 4-8 --- .gitignore | 5 + CHANGELOG.md | 56 ++++ USER_GUIDE.md | 213 ++++++++++++ ai_harness.py | 317 ++++++++++++++++++ campus_task/__init__.py | 4 +- campus_task/task_service.py | 48 ++- eval_cases.json | 82 +++++ main.py | 270 ++++++++++++++- pyproject.toml | 13 + tests/test_experiment5.py | 83 +++++ ...6\351\252\2145\346\212\245\345\221\212.md" | 94 ++++++ ...6\351\252\2146\346\212\245\345\221\212.md" | 53 +++ ...6\351\252\2147\346\212\245\345\221\212.md" | 51 +++ ...6\351\252\2148\346\212\245\345\221\212.md" | 49 +++ 14 files changed, 1318 insertions(+), 20 deletions(-) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 USER_GUIDE.md create mode 100644 ai_harness.py create mode 100644 eval_cases.json create mode 100644 pyproject.toml create mode 100644 tests/test_experiment5.py create mode 100644 "\345\256\236\351\252\2145\346\212\245\345\221\212.md" create mode 100644 "\345\256\236\351\252\2146\346\212\245\345\221\212.md" create mode 100644 "\345\256\236\351\252\2147\346\212\245\345\221\212.md" create mode 100644 "\345\256\236\351\252\2148\346\212\245\345\221\212.md" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf20539 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd +.pytest_cache/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..59b1552 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,56 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [0.2.1] - 2026-06-30 + +### Added + +- **实验4功能** + - 任务优先级字段(高/中/低/普通) + - 任务删除功能 + - 状态筛选功能 + - 优先级筛选功能 + +- **实验5功能** + - 按截止日期排序功能 + - 按优先级排序功能 + - 任务关键词搜索功能 + - 任务导出为CSV功能 + - CHANGELOG.md 版本变更记录 + +- **实验6功能** + - pyproject.toml 项目配置文件 + - GitHub Actions CI/CD 配置 + - 自动测试流水线 + +- **实验7功能** + - `--version` / `-v` 版本信息选项 + - `--help` / `-h` 帮助信息选项 + - 操作日志记录(campus_task.log) + - USER_GUIDE.md 用户手册 + +- **实验8功能** + - AI Harness 模块(ai_harness.py) + - 自然语言任务管理 + - 模拟大模型输出(mock_model) + - 安全护栏(guardrail) + - 评测用例(eval_cases.json) + - 执行追踪(trace.jsonl) + +### Changed + +- 更新任务数据结构,支持优先级字段 +- 更新命令行界面,添加新功能命令 +- 更新交互式模式,添加新功能选项 + +## [0.1.0] - 2026-06-12 + +### Added + +- 任务添加功能(支持标题和截止日期) +- 任务列表查看功能 +- 任务完成标记功能 +- JSON文件存储任务数据 +- 命令行交互模式 +- 基本单元测试 \ No newline at end of file diff --git a/USER_GUIDE.md b/USER_GUIDE.md new file mode 100644 index 0000000..2b384b4 --- /dev/null +++ b/USER_GUIDE.md @@ -0,0 +1,213 @@ +# CampusTask 用户手册 + +## 概述 + +CampusTask 是一个命令行任务管理工具,帮助学生管理课程作业、实验报告和考试复习任务。 + +## 安装 + +```bash +git clone +cd campus_task +``` + +## 运行方式 + +### 方式一:直接运行 main.py + +```bash +python main.py +``` + +### 方式二:作为模块运行 + +```bash +python -m campus_task +``` + +## 命令行用法 + +### 显示版本信息 + +```bash +python -m campus_task --version +# 或 +python -m campus_task -v +``` + +### 显示帮助信息 + +```bash +python -m campus_task --help +# 或 +python -m campus_task -h +``` + +### 添加任务 + +```bash +python -m campus_task add "任务标题" [截止日期] [优先级] +``` + +参数说明: +- title: 任务标题(必填) +- deadline: 截止日期,格式 YYYY-MM-DD(可选) +- priority: 优先级,可选值:高、中、低、普通(默认:普通) + +示例: +```bash +python -m campus_task add "完成软件工程实验" +python -m campus_task add "提交数据结构作业" 2026-07-15 +python -m campus_task add "重要考试复习" 2026-07-01 高 +``` + +### 列出所有任务 + +```bash +python -m campus_task list +``` + +### 标记任务完成 + +```bash +python -m campus_task done <任务编号> +``` + +示例: +```bash +python -m campus_task done 1 +``` + +### 删除任务 + +```bash +python -m campus_task delete <任务编号> +``` + +示例: +```bash +python -m campus_task delete 1 +``` + +### 按状态筛选任务 + +```bash +python -m campus_task filter <状态> +``` + +状态可选值:待完成、已完成 + +示例: +```bash +python -m campus_task filter 待完成 +python -m campus_task filter 已完成 +``` + +### 按优先级筛选任务 + +```bash +python -m campus_task priority <优先级> +``` + +优先级可选值:高、中、低、普通 + +示例: +```bash +python -m campus_task priority 高 +``` + +### 按截止日期排序 + +```bash +python -m campus_task sort deadline +``` + +### 按优先级排序 + +```bash +python -m campus_task sort priority +``` + +### 搜索任务 + +```bash +python -m campus_task search <关键词> +``` + +示例: +```bash +python -m campus_task search 软件工程 +``` + +### 导出任务为CSV + +```bash +python -m campus_task export <文件名> +``` + +示例: +```bash +python -m campus_task export tasks.csv +``` + +## 交互式模式 + +直接运行不带参数的命令,进入交互式模式: + +```bash +python -m campus_task +``` + +交互式模式提供菜单选择: +1. 添加任务 +2. 查看任务列表 +3. 标记任务完成 +4. 删除任务 +5. 按状态筛选任务 +6. 按优先级筛选任务 +7. 按截止日期排序 +8. 按优先级排序 +9. 搜索任务 +10. 导出任务为CSV +11. 退出 + +## 数据存储 + +任务数据保存在 `tasks.json` 文件中,每个任务包含以下字段: +- id: 任务编号 +- title: 任务标题 +- status: 任务状态(待完成/已完成) +- priority: 优先级(高/中/低/普通) +- created_at: 创建时间 +- deadline: 截止日期(可选) + +## 日志文件 + +操作日志保存在 `campus_task.log` 文件中,记录了: +- 添加任务 +- 标记任务完成 +- 删除任务 +- 导出任务 +- 错误信息 + +## 常见问题 + +### Q: 任务编号是如何分配的? + +A: 任务编号是自动分配的,从1开始递增。删除任务后,编号不会重新分配。 + +### Q: 如何修改任务? + +A: 当前版本不支持修改任务,如果需要修改,可以删除后重新添加。 + +### Q: 数据文件在哪里? + +A: 数据文件 `tasks.json` 位于运行命令的当前目录。 + +### Q: 如何备份任务数据? + +A: 可以复制 `tasks.json` 文件作为备份,或者使用 `export` 命令导出为CSV文件。 + +## 技术支持 + +如有问题或建议,请提交 Issue 到项目仓库。 \ No newline at end of file diff --git a/ai_harness.py b/ai_harness.py new file mode 100644 index 0000000..8e841fc --- /dev/null +++ b/ai_harness.py @@ -0,0 +1,317 @@ +import json +import os +from datetime import datetime, timedelta +from typing import Dict, Any, List, Optional + +from campus_task import add_task, get_all_tasks, mark_task_done, delete_task, \ + get_tasks_by_status, get_tasks_by_priority, sort_tasks_by_deadline, \ + sort_tasks_by_priority, search_tasks, export_tasks_to_csv, get_task_by_id + +TRACE_FILE = "trace.jsonl" + +def prompt_builder(user_input: str, task_state: str) -> str: + prompt = f"""你是一个任务管理助手,帮助用户管理校园任务。 + +当前任务状态: +{task_state} + +用户输入: {user_input} + +请根据用户输入,分析用户意图并生成JSON格式的操作指令。 + +可用操作: +- add_task: 添加任务,参数: title(必填), deadline(可选), priority(可选,高/中/低/普通) +- mark_task_done: 标记任务完成,参数: task_id(必填) +- delete_task: 删除任务,参数: task_id(必填) +- get_all_tasks: 获取所有任务,无参数 +- get_tasks_by_status: 按状态筛选,参数: status(待完成/已完成) +- get_tasks_by_priority: 按优先级筛选,参数: priority(高/中/低/普通) +- sort_tasks_by_deadline: 按截止日期排序,无参数 +- sort_tasks_by_priority: 按优先级排序,无参数 +- search_tasks: 搜索任务,参数: keyword(必填) +- export_tasks_to_csv: 导出CSV,参数: file_path(必填) + +日期格式说明: +- tomorrow: 明天 +- today: 今天 +- YYYY-MM-DD: 具体日期 + +输出格式(必须是纯JSON): +{{ + "action": "操作名称", + "args": {{参数}} +}} +""" + return prompt + +def mock_model(prompt: str) -> str: + user_input = prompt.split("用户输入: ")[1].split("\n")[0] + + lower_input = user_input.lower() + + if "添加" in user_input or "新建" in user_input or "创建" in user_input: + title = user_input.replace("添加", "").replace("新建", "").replace("创建", "").strip() + title = title.replace("任务", "").strip() + + deadline = None + priority = "普通" + + if "明天" in user_input: + deadline = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d") + elif "今天" in user_input: + deadline = datetime.now().strftime("%Y-%m-%d") + + if "高" in user_input or "重要" in user_input: + priority = "高" + elif "中" in user_input: + priority = "中" + elif "低" in user_input: + priority = "低" + + args = {"title": title} + if deadline: + args["deadline"] = deadline + if priority != "普通": + args["priority"] = priority + + return json.dumps({"action": "add_task", "args": args}, ensure_ascii=False) + + elif "完成" in user_input or "做完" in user_input: + task_id = None + try: + for word in user_input.split(): + if word.isdigit(): + task_id = int(word) + break + except: + pass + + if task_id: + return json.dumps({"action": "mark_task_done", "args": {"task_id": task_id}}, ensure_ascii=False) + else: + return json.dumps({"action": "get_all_tasks", "args": {}}, ensure_ascii=False) + + elif "删除" in user_input or "移除" in user_input: + task_id = None + try: + for word in user_input.split(): + if word.isdigit(): + task_id = int(word) + break + except: + pass + + if task_id: + return json.dumps({"action": "delete_task", "args": {"task_id": task_id}}, ensure_ascii=False) + else: + return json.dumps({"action": "get_all_tasks", "args": {}}, ensure_ascii=False) + + elif "列表" in user_input or "所有" in user_input or "查看" in user_input: + return json.dumps({"action": "get_all_tasks", "args": {}}, ensure_ascii=False) + + elif "待完成" in user_input: + return json.dumps({"action": "get_tasks_by_status", "args": {"status": "待完成"}}, ensure_ascii=False) + + elif "已完成" in user_input: + return json.dumps({"action": "get_tasks_by_status", "args": {"status": "已完成"}}, ensure_ascii=False) + + elif "优先级" in user_input: + priority = "高" + if "低" in user_input: + priority = "低" + elif "中" in user_input: + priority = "中" + elif "普通" in user_input: + priority = "普通" + return json.dumps({"action": "get_tasks_by_priority", "args": {"priority": priority}}, ensure_ascii=False) + + elif "排序" in user_input: + if "日期" in user_input or "截止" in user_input: + return json.dumps({"action": "sort_tasks_by_deadline", "args": {}}, ensure_ascii=False) + else: + return json.dumps({"action": "sort_tasks_by_priority", "args": {}}, ensure_ascii=False) + + elif "搜索" in user_input or "查找" in user_input: + keyword = user_input.replace("搜索", "").replace("查找", "").strip() + return json.dumps({"action": "search_tasks", "args": {"keyword": keyword}}, ensure_ascii=False) + + elif "导出" in user_input: + return json.dumps({"action": "export_tasks_to_csv", "args": {"file_path": "tasks.csv"}}, ensure_ascii=False) + + else: + return json.dumps({"action": "get_all_tasks", "args": {}}, ensure_ascii=False) + +def parse_model(output: str) -> Dict[str, Any]: + try: + return json.loads(output) + except json.JSONDecodeError: + return {"action": "get_all_tasks", "args": {}} + +def guardrail_action(action: str) -> bool: + safe_actions = [ + "add_task", "mark_task_done", "delete_task", + "get_all_tasks", "get_tasks_by_status", "get_tasks_by_priority", + "sort_tasks_by_deadline", "sort_tasks_by_priority", + "search_tasks", "export_tasks_to_csv" + ] + return action in safe_actions + +def guardrail_tool(action: str) -> bool: + dangerous_tools = [] + return action not in dangerous_tools + +def execute_tool(action: str, args: Dict[str, Any]) -> Any: + if action == "add_task": + title = args.get("title", "") + deadline = args.get("deadline") + priority = args.get("priority", "普通") + if deadline == "tomorrow": + deadline = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d") + elif deadline == "today": + deadline = datetime.now().strftime("%Y-%m-%d") + return add_task(title, deadline, priority) + + elif action == "mark_task_done": + task_id = args.get("task_id", 0) + return mark_task_done(task_id) + + elif action == "delete_task": + task_id = args.get("task_id", 0) + return delete_task(task_id) + + elif action == "get_all_tasks": + return get_all_tasks() + + elif action == "get_tasks_by_status": + status = args.get("status", "待完成") + return get_tasks_by_status(status) + + elif action == "get_tasks_by_priority": + priority = args.get("priority", "普通") + return get_tasks_by_priority(priority) + + elif action == "sort_tasks_by_deadline": + return sort_tasks_by_deadline() + + elif action == "sort_tasks_by_priority": + return sort_tasks_by_priority() + + elif action == "search_tasks": + keyword = args.get("keyword", "") + return search_tasks(keyword) + + elif action == "export_tasks_to_csv": + file_path = args.get("file_path", "tasks.csv") + return export_tasks_to_csv(file_path) + + else: + return None + +def write_trace(event: Dict[str, Any]) -> None: + event["timestamp"] = datetime.now().isoformat() + with open(TRACE_FILE, 'a', encoding='utf-8') as f: + f.write(json.dumps(event, ensure_ascii=False) + '\n') + +def run_eval(eval_cases: List[Dict[str, str]]) -> Dict[str, Any]: + passed = 0 + failed = 0 + + for case in eval_cases: + user_input = case["input"] + expected_action = case["expected_action"] + + task_state = "[]" + prompt = prompt_builder(user_input, task_state) + model_output = mock_model(prompt) + parsed = parse_model(model_output) + actual_action = parsed.get("action", "") + + if actual_action == expected_action: + passed += 1 + else: + failed += 1 + + write_trace({ + "type": "eval", + "input": user_input, + "expected_action": expected_action, + "actual_action": actual_action, + "passed": actual_action == expected_action + }) + + total = len(eval_cases) + accuracy = passed / total if total > 0 else 0 + + return { + "total": total, + "passed": passed, + "failed": failed, + "accuracy": accuracy + } + +def ai_harness_main(user_input: str) -> str: + tasks = get_all_tasks() + task_state = json.dumps([t.to_dict() for t in tasks], ensure_ascii=False) + + write_trace({"type": "user_input", "content": user_input}) + + prompt = prompt_builder(user_input, task_state) + write_trace({"type": "prompt", "content": prompt[:200] + "..." if len(prompt) > 200 else prompt}) + + model_output = mock_model(prompt) + write_trace({"type": "model_output", "content": model_output}) + + parsed = parse_model(model_output) + write_trace({"type": "parsed_output", "content": parsed}) + + action = parsed.get("action", "") + args = parsed.get("args", {}) + + if not guardrail_action(action): + write_trace({"type": "guardrail", "result": "blocked", "reason": f"危险操作: {action}"}) + return f"抱歉,'{action}' 是危险操作,无法执行。" + + if not guardrail_tool(action): + write_trace({"type": "guardrail", "result": "blocked", "reason": f"危险工具: {action}"}) + return f"抱歉,'{action}' 工具被禁用。" + + write_trace({"type": "guardrail", "result": "allowed"}) + + result = execute_tool(action, args) + write_trace({"type": "execution_result", "action": action, "success": result is not None}) + + if action in ["add_task"]: + return f"已添加任务: {result.title}" if result else "添加任务失败" + + elif action == "mark_task_done": + return "任务已标记为完成" if result else "标记失败,未找到任务" + + elif action == "delete_task": + return "任务已删除" if result else "删除失败,未找到任务" + + elif action == "export_tasks_to_csv": + return "任务已导出到 tasks.csv" if result else "导出失败" + + elif action in ["get_all_tasks", "get_tasks_by_status", "get_tasks_by_priority", + "sort_tasks_by_deadline", "sort_tasks_by_priority", "search_tasks"]: + if not result: + return "暂无任务" + lines = [] + for task in result: + deadline = task.deadline if task.deadline else "" + lines.append(f"{task.id}. [{task.status}] {task.priority} - {task.title} ({deadline})") + return "\n".join(lines) + + else: + return f"执行操作: {action}" + +if __name__ == "__main__": + import sys + + if len(sys.argv) >= 2: + user_input = " ".join(sys.argv[1:]) + result = ai_harness_main(user_input) + print(result) + else: + print("用法: python ai_harness.py <用户输入>") + print("示例: python ai_harness.py \"帮我添加一个明天下午交的软件工程实验报告任务\"") \ No newline at end of file diff --git a/campus_task/__init__.py b/campus_task/__init__.py index d8b267d..ea509a8 100644 --- a/campus_task/__init__.py +++ b/campus_task/__init__.py @@ -1,4 +1,4 @@ from .task_model import Task -from .task_service import add_task, get_all_tasks, mark_task_done, get_task_by_id, get_tasks_by_priority +from .task_service import add_task, get_all_tasks, mark_task_done, get_task_by_id, get_tasks_by_status, delete_task, get_tasks_by_priority, sort_tasks_by_deadline, sort_tasks_by_priority, search_tasks, export_tasks_to_csv -__all__ = ["Task", "add_task", "get_all_tasks", "mark_task_done", "get_task_by_id", "get_tasks_by_priority"] \ No newline at end of file +__all__ = ["Task", "add_task", "get_all_tasks", "mark_task_done", "get_task_by_id", "get_tasks_by_status", "delete_task", "get_tasks_by_priority", "sort_tasks_by_deadline", "sort_tasks_by_priority", "search_tasks", "export_tasks_to_csv"] \ No newline at end of file diff --git a/campus_task/task_service.py b/campus_task/task_service.py index 2149795..7e4963f 100644 --- a/campus_task/task_service.py +++ b/campus_task/task_service.py @@ -48,4 +48,50 @@ def get_task_by_id(task_id: int) -> Optional[Task]: for task in tasks: if task.id == task_id: return task - return None \ No newline at end of file + return None + +def delete_task(task_id: int) -> bool: + tasks = load_tasks() + for i, task in enumerate(tasks): + if task.id == task_id: + del tasks[i] + save_tasks(tasks) + return True + return False + +def get_tasks_by_status(status: str) -> List[Task]: + valid_statuses = ["待完成", "已完成"] + if status not in valid_statuses: + raise ValueError("状态必须是: 待完成、已完成") + tasks = load_tasks() + return [task for task in tasks if task.status == status] + +def sort_tasks_by_deadline() -> List[Task]: + tasks = load_tasks() + return sorted(tasks, key=lambda t: t.deadline if t.deadline else "9999-12-31") + +def sort_tasks_by_priority() -> List[Task]: + priority_order = {"高": 0, "中": 1, "普通": 2, "低": 3} + tasks = load_tasks() + return sorted(tasks, key=lambda t: priority_order.get(t.priority, 3)) + +def search_tasks(keyword: str) -> List[Task]: + tasks = load_tasks() + keyword = keyword.lower() + return [task for task in tasks if keyword in task.title.lower()] + +def export_tasks_to_csv(file_path: str) -> bool: + import csv + tasks = load_tasks() + try: + with open(file_path, 'w', newline='', encoding='utf-8') as f: + writer = csv.writer(f) + writer.writerow(["编号", "标题", "状态", "优先级", "创建时间", "截止日期"]) + for task in tasks: + writer.writerow([ + task.id, task.title, task.status, + task.priority, task.created_at, task.deadline or '' + ]) + return True + except IOError: + return False \ No newline at end of file diff --git a/eval_cases.json b/eval_cases.json new file mode 100644 index 0000000..aa4b1c3 --- /dev/null +++ b/eval_cases.json @@ -0,0 +1,82 @@ +[ + { + "input": "帮我添加一个复习软件工程的任务", + "expected_action": "add_task" + }, + { + "input": "添加一个高优先级的任务", + "expected_action": "add_task" + }, + { + "input": "新建明天要交的作业", + "expected_action": "add_task" + }, + { + "input": "标记任务1完成", + "expected_action": "mark_task_done" + }, + { + "input": "完成第2个任务", + "expected_action": "mark_task_done" + }, + { + "input": "删除任务3", + "expected_action": "delete_task" + }, + { + "input": "移除编号为5的任务", + "expected_action": "delete_task" + }, + { + "input": "查看所有任务", + "expected_action": "get_all_tasks" + }, + { + "input": "列出任务列表", + "expected_action": "get_all_tasks" + }, + { + "input": "待完成的任务有哪些", + "expected_action": "get_tasks_by_status" + }, + { + "input": "已完成的任务", + "expected_action": "get_tasks_by_status" + }, + { + "input": "高优先级任务", + "expected_action": "get_tasks_by_priority" + }, + { + "input": "低优先级的任务", + "expected_action": "get_tasks_by_priority" + }, + { + "input": "按日期排序", + "expected_action": "sort_tasks_by_deadline" + }, + { + "input": "按截止日期排序任务", + "expected_action": "sort_tasks_by_deadline" + }, + { + "input": "按优先级排序", + "expected_action": "sort_tasks_by_priority" + }, + { + "input": "搜索软件工程", + "expected_action": "search_tasks" + }, + { + "input": "查找作业相关的任务", + "expected_action": "search_tasks" + }, + { + "input": "导出任务", + "expected_action": "export_tasks_to_csv" + }, + { + "input": "把任务导出为CSV", + "expected_action": "export_tasks_to_csv" + } +] \ No newline at end of file diff --git a/main.py b/main.py index dc617c5..23ddaf9 100644 --- a/main.py +++ b/main.py @@ -1,27 +1,45 @@ import sys -from campus_task import add_task, get_all_tasks, mark_task_done +import logging +from campus_task import add_task, get_all_tasks, mark_task_done, get_tasks_by_status, delete_task, get_tasks_by_priority, sort_tasks_by_deadline, sort_tasks_by_priority, search_tasks, export_tasks_to_csv -def print_tasks(): - tasks = get_all_tasks() +VERSION = "0.1.0" + +logging.basicConfig( + filename='campus_task.log', + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + encoding='utf-8' +) + +def print_tasks(tasks=None): + if tasks is None: + tasks = get_all_tasks() if not tasks: print("暂无任务") return - print("=" * 60) - print(f"{'编号':<4} {'状态':<6} {'标题':<30} {'创建时间':<19} {'截止日期'}") - print("-" * 60) + print("=" * 70) + print(f"{'编号':<4} {'状态':<6} {'优先级':<4} {'标题':<30} {'创建时间':<19} {'截止日期'}") + print("-" * 70) for task in tasks: deadline = task.deadline if task.deadline else '' - print(f"{task.id:<4} {task.status:<6} {task.title:<30} {task.created_at:<19} {deadline}") - print("=" * 60) + print(f"{task.id:<4} {task.status:<6} {task.priority:<4} {task.title:<30} {task.created_at:<19} {deadline}") + print("=" * 70) def handle_add(args): if len(args) < 1: - print("用法: python main.py add [deadline]") + print("用法: python main.py add <title> [deadline] [priority]") + print("priority 可选值: 高、中、低、普通(默认:普通)") return title = args[0] deadline = args[1] if len(args) > 1 else None - task = add_task(title, deadline) - print(f"任务已添加: {task.title}") + priority = args[2] if len(args) > 2 else "普通" + try: + task = add_task(title, deadline, priority) + print(f"任务已添加: {task.title}") + logging.info(f"添加任务: id={task.id}, title={task.title}, deadline={deadline}, priority={priority}") + except ValueError as e: + print(e) + logging.error(f"添加任务失败: {e}") def handle_list(): print_tasks() @@ -35,10 +53,137 @@ def handle_done(args): success = mark_task_done(task_id) if success: print(f"任务 {task_id} 已标记为完成") + logging.info(f"标记任务完成: id={task_id}") + else: + print(f"未找到任务 {task_id}") + logging.warning(f"标记任务完成失败: 未找到任务 id={task_id}") + except ValueError: + print("任务编号必须是数字") + logging.error(f"标记任务完成失败: 任务编号必须是数字") + +def handle_delete(args): + if len(args) < 1: + print("用法: python main.py delete <id>") + return + try: + task_id = int(args[0]) + success = delete_task(task_id) + if success: + print(f"任务 {task_id} 已删除") + logging.info(f"删除任务: id={task_id}") else: print(f"未找到任务 {task_id}") + logging.warning(f"删除任务失败: 未找到任务 id={task_id}") except ValueError: print("任务编号必须是数字") + logging.error(f"删除任务失败: 任务编号必须是数字") + +def handle_filter(args): + if len(args) < 1: + print("用法: python main.py filter <status>") + print("status 可选值: 待完成, 已完成") + return + status = args[0] + try: + tasks = get_tasks_by_status(status) + if not tasks: + print(f"暂无{status}任务") + return + print(f"===== {status}任务 =====") + print_tasks(tasks) + except ValueError as e: + print(e) + +def handle_priority(args): + if len(args) < 1: + print("用法: python main.py priority <priority>") + print("priority 可选值: 高、中、低、普通") + return + priority = args[0] + try: + tasks = get_tasks_by_priority(priority) + if not tasks: + print(f"暂无{priority}优先级任务") + return + print(f"===== {priority}优先级任务 =====") + print_tasks(tasks) + except ValueError as e: + print(e) + +def handle_sort(args): + if len(args) < 1: + print("用法: python main.py sort <field>") + print("field 可选值: deadline, priority") + return + field = args[0] + if field == "deadline": + tasks = sort_tasks_by_deadline() + print("===== 按截止日期排序 =====") + print_tasks(tasks) + elif field == "priority": + tasks = sort_tasks_by_priority() + print("===== 按优先级排序 =====") + print_tasks(tasks) + else: + print("无效字段,可选值: deadline, priority") + +def handle_search(args): + if len(args) < 1: + print("用法: python main.py search <keyword>") + return + keyword = args[0] + tasks = search_tasks(keyword) + if not tasks: + print(f"未找到包含'{keyword}'的任务") + return + print(f"===== 搜索结果('{keyword}') =====") + print_tasks(tasks) + +def handle_export(args): + if len(args) < 1: + print("用法: python main.py export <file.csv>") + return + file_path = args[0] + success = export_tasks_to_csv(file_path) + if success: + print(f"任务已导出到 {file_path}") + logging.info(f"导出任务到CSV: {file_path}") + else: + print(f"导出失败,请检查文件路径") + logging.error(f"导出任务失败: {file_path}") + +def handle_version(): + print(f"CampusTask v{VERSION}") + +def handle_help(): + help_text = f"""CampusTask v{VERSION} - 校园任务清单管理工具 + +用法: python -m campus_task <command> [args] + +命令列表: + add <title> [deadline] [priority] 添加新任务 + list 列出所有任务 + done <id> 标记任务完成 + delete <id> 删除任务 + filter <status> 按状态筛选任务 + priority <priority> 按优先级筛选任务 + sort <field> 排序任务 (field: deadline, priority) + search <keyword> 搜索任务 + export <file.csv> 导出任务为CSV + +选项: + --version 显示版本信息 + --help 显示帮助信息 + +示例: + python -m campus_task add "完成作业" 2026-07-01 高 + python -m campus_task list + python -m campus_task done 1 + python -m campus_task sort deadline + python -m campus_task search 软件工程 + python -m campus_task export tasks.csv +""" + print(help_text) def interactive_mode(): while True: @@ -46,8 +191,15 @@ def interactive_mode(): print("1. 添加任务") print("2. 查看任务列表") print("3. 标记任务完成") - print("4. 退出") - choice = input("请输入选择 (1-4): ") + print("4. 删除任务") + print("5. 按状态筛选任务") + print("6. 按优先级筛选任务") + print("7. 按截止日期排序") + print("8. 按优先级排序") + print("9. 搜索任务") + print("10. 导出任务为CSV") + print("11. 退出") + choice = input("请输入选择 (1-11): ") if choice == "1": title = input("请输入任务标题: ").strip() @@ -56,8 +208,13 @@ def interactive_mode(): continue deadline = input("请输入截止日期 (可选,格式: YYYY-MM-DD): ").strip() deadline = deadline if deadline else None - task = add_task(title, deadline) - print(f"任务已添加: {task.title}") + priority = input("请输入优先级 (可选,高/中/低/普通,默认: 普通): ").strip() + priority = priority if priority else "普通" + try: + task = add_task(title, deadline, priority) + print(f"任务已添加: {task.title}") + except ValueError as e: + print(e) elif choice == "2": print_tasks() elif choice == "3": @@ -72,24 +229,103 @@ def interactive_mode(): except ValueError: print("任务编号必须是数字") elif choice == "4": + print_tasks() + try: + task_id = int(input("请输入要删除的任务编号: ")) + success = delete_task(task_id) + if success: + print(f"任务 {task_id} 已删除") + else: + print(f"未找到任务 {task_id}") + except ValueError: + print("任务编号必须是数字") + elif choice == "5": + status = input("请输入筛选状态 (待完成/已完成): ").strip() + try: + tasks = get_tasks_by_status(status) + if not tasks: + print(f"暂无{status}任务") + else: + print(f"===== {status}任务 =====") + print_tasks(tasks) + except ValueError as e: + print(e) + elif choice == "6": + priority = input("请输入筛选优先级 (高/中/低/普通): ").strip() + try: + tasks = get_tasks_by_priority(priority) + if not tasks: + print(f"暂无{priority}优先级任务") + else: + print(f"===== {priority}优先级任务 =====") + print_tasks(tasks) + except ValueError as e: + print(e) + elif choice == "7": + tasks = sort_tasks_by_deadline() + print("===== 按截止日期排序 =====") + print_tasks(tasks) + elif choice == "8": + tasks = sort_tasks_by_priority() + print("===== 按优先级排序 =====") + print_tasks(tasks) + elif choice == "9": + keyword = input("请输入搜索关键词: ").strip() + if not keyword: + print("关键词不能为空") + continue + tasks = search_tasks(keyword) + if not tasks: + print(f"未找到包含'{keyword}'的任务") + else: + print(f"===== 搜索结果('{keyword}') =====") + print_tasks(tasks) + elif choice == "10": + file_path = input("请输入导出文件路径 (如: tasks.csv): ").strip() + if not file_path: + print("文件路径不能为空") + continue + success = export_tasks_to_csv(file_path) + if success: + print(f"任务已导出到 {file_path}") + else: + print(f"导出失败,请检查文件路径") + elif choice == "11": print("退出程序") break else: - print("无效选择,请输入 1-4") + print("无效选择,请输入 1-11") def main(): if len(sys.argv) >= 2: command = sys.argv[1] args = sys.argv[2:] - if command == "add": + if command == "--version" or command == "-v": + handle_version() + elif command == "--help" or command == "-h": + handle_help() + elif command == "add": handle_add(args) elif command == "list": handle_list() elif command == "done": handle_done(args) + elif command == "delete": + handle_delete(args) + elif command == "filter": + handle_filter(args) + elif command == "priority": + handle_priority(args) + elif command == "sort": + handle_sort(args) + elif command == "search": + handle_search(args) + elif command == "export": + handle_export(args) else: print(f"未知命令: {command}") + logging.warning(f"未知命令: {command}") else: interactive_mode() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8ae2505 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,13 @@ +[project] +name = "campus_task" +version = "0.1.0" +description = "一个模块化设计的命令行任务管理工具,帮助学生管理课程作业、实验报告和考试复习任务" +requires-python = ">=3.8" + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["."] + +[build-system] +requires = ["setuptools>=45", "wheel"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/tests/test_experiment5.py b/tests/test_experiment5.py new file mode 100644 index 0000000..eafd6a3 --- /dev/null +++ b/tests/test_experiment5.py @@ -0,0 +1,83 @@ +import unittest +import os +import sys +import csv +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from campus_task import add_task, sort_tasks_by_deadline, sort_tasks_by_priority, search_tasks, export_tasks_to_csv + +class TestExperiment5Features(unittest.TestCase): + + def setUp(self): + if os.path.exists("tasks.json"): + os.remove("tasks.json") + if os.path.exists("test_export.csv"): + os.remove("test_export.csv") + + def tearDown(self): + if os.path.exists("tasks.json"): + os.remove("tasks.json") + if os.path.exists("test_export.csv"): + os.remove("test_export.csv") + + def test_sort_tasks_by_deadline(self): + add_task("任务C", "2026-07-03", "普通") + add_task("任务A", "2026-07-01", "高") + add_task("任务B", "2026-07-02", "中") + + sorted_tasks = sort_tasks_by_deadline() + self.assertEqual(len(sorted_tasks), 3) + self.assertEqual(sorted_tasks[0].title, "任务A") + self.assertEqual(sorted_tasks[1].title, "任务B") + self.assertEqual(sorted_tasks[2].title, "任务C") + + def test_sort_tasks_by_priority(self): + add_task("普通任务", None, "普通") + add_task("高优先级任务", None, "高") + add_task("中优先级任务", None, "中") + add_task("低优先级任务", None, "低") + + sorted_tasks = sort_tasks_by_priority() + self.assertEqual(len(sorted_tasks), 4) + self.assertEqual(sorted_tasks[0].priority, "高") + self.assertEqual(sorted_tasks[1].priority, "中") + self.assertEqual(sorted_tasks[2].priority, "普通") + self.assertEqual(sorted_tasks[3].priority, "低") + + def test_search_tasks_match(self): + add_task("完成软件工程实验") + add_task("提交数据结构作业") + add_task("软件工程复习") + + results = search_tasks("软件工程") + self.assertEqual(len(results), 2) + + def test_search_tasks_no_match(self): + add_task("完成软件工程实验") + add_task("提交数据结构作业") + + results = search_tasks("数学") + self.assertEqual(len(results), 0) + + def test_search_tasks_case_insensitive(self): + add_task("完成软件工程实验") + + results = search_tasks("软件工程") + self.assertEqual(len(results), 1) + + def test_export_tasks_to_csv(self): + add_task("测试任务1", "2026-07-01", "高") + add_task("测试任务2", "2026-07-02", "普通") + + success = export_tasks_to_csv("test_export.csv") + self.assertTrue(success) + self.assertTrue(os.path.exists("test_export.csv")) + + with open("test_export.csv", 'r', encoding='utf-8') as f: + reader = csv.reader(f) + rows = list(reader) + self.assertEqual(len(rows), 3) + self.assertEqual(rows[0], ["编号", "标题", "状态", "优先级", "创建时间", "截止日期"]) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git "a/\345\256\236\351\252\2145\346\212\245\345\221\212.md" "b/\345\256\236\351\252\2145\346\212\245\345\221\212.md" new file mode 100644 index 0000000..1cdb344 --- /dev/null +++ "b/\345\256\236\351\252\2145\346\212\245\345\221\212.md" @@ -0,0 +1,94 @@ +# 实验5:迭代开发与变更管理——处理用户的新需求 + +## 实验目的 + +通过收集用户反馈,整理变更请求,并实现新功能,理解需求会变化,软件工程的重点是用可控流程管理变化。 + +## 变更请求表 + +### 请求1:任务排序功能 + +| 项目 | 内容 | +|------|------| +| 用户原话 | "希望能够按照截止日期排序任务,这样可以优先处理快到期的任务" | +| 问题描述 | 用户无法快速了解哪些任务即将到期,需要手动查看每个任务的截止日期 | +| 建议功能 | 添加按截止日期排序的功能 | +| 优先级 | 高 | +| 验收标准 | 执行排序命令后,任务按截止日期升序排列,无截止日期的任务排在最后 | +| 风险 | 无 | +| 预计工作量 | 2小时 | + +### 请求2:优先级排序功能 + +| 项目 | 内容 | +|------|------| +| 用户原话 | "有时候任务太多,希望能按优先级排序,优先处理重要的任务" | +| 问题描述 | 用户无法快速识别高优先级任务 | +| 建议功能 | 添加按优先级排序的功能,优先级顺序:高 > 中 > 普通 > 低 | +| 优先级 | 高 | +| 验收标准 | 执行排序命令后,任务按优先级从高到低排列 | +| 风险 | 无 | +| 预计工作量 | 2小时 | + +### 请求3:任务搜索功能 + +| 项目 | 内容 | +|------|------| +| 用户原话 | "任务多了之后找起来很麻烦,希望能搜索任务标题" | +| 问题描述 | 用户无法快速定位特定任务 | +| 建议功能 | 添加按关键词搜索任务的功能 | +| 优先级 | 中 | +| 验收标准 | 输入关键词后,返回标题包含该关键词的所有任务 | +| 风险 | 无 | +| 预计工作量 | 2小时 | + +### 请求4:导出CSV功能 + +| 项目 | 内容 | +|------|------| +| 用户原话 | "希望能把任务导出成表格,方便打印和分享" | +| 问题描述 | 用户无法将任务数据导出为其他格式 | +| 建议功能 | 添加导出任务为CSV文件的功能 | +| 优先级 | 中 | +| 验收标准 | 执行导出命令后,生成包含所有任务信息的CSV文件 | +| 风险 | 文件写入权限问题 | +| 预计工作量 | 2小时 | + +## 功能实现记录 + +### 实现的功能 + +| 功能 | 实现位置 | 说明 | +|------|----------|------| +| 按截止日期排序 | task_service.py: sort_tasks_by_deadline() | 按deadline升序排列,无截止日期的排在最后 | +| 按优先级排序 | task_service.py: sort_tasks_by_priority() | 按优先级高到低排列:高 > 中 > 普通 > 低 | +| 任务搜索 | task_service.py: search_tasks() | 支持关键词模糊搜索 | +| 导出CSV | task_service.py: export_tasks_to_csv() | 将任务导出为CSV文件,包含所有字段 | + +### 修改的文件 + +| 文件 | 修改内容 | +|------|----------| +| campus_task/task_service.py | 添加4个新函数 | +| campus_task/__init__.py | 导出新函数 | +| main.py | 添加sort、search、export命令和交互模式支持 | +| tests/test_experiment5.py | 添加新功能的单元测试 | + +## 测试结果 + +所有测试用例通过,包括: +- 按截止日期排序测试 +- 按优先级排序测试 +- 任务搜索匹配测试 +- 任务搜索无匹配测试 +- CSV导出测试 + +## 课后反思 + +### 问题:为什么"用户说要什么"和"系统真正该做什么"不一定相同? + +用户需求往往是模糊的、不完整的,需要开发者进行分析和转化。用户说的是他们想要的功能,但系统真正需要做的是解决用户的问题。这需要: +1. 深入理解用户的真实需求 +2. 分析问题本质 +3. 设计最优的解决方案 +4. 与用户进行有效的沟通和验证 \ No newline at end of file diff --git "a/\345\256\236\351\252\2146\346\212\245\345\221\212.md" "b/\345\256\236\351\252\2146\346\212\245\345\221\212.md" new file mode 100644 index 0000000..666f5f0 --- /dev/null +++ "b/\345\256\236\351\252\2146\346\212\245\345\221\212.md" @@ -0,0 +1,53 @@ +# 实验6:持续集成与质量门禁——让机器每次提交都检查代码 + +## 实验目的 + +通过配置 GitHub Actions,实现每次 push 或 pull request 自动运行测试,理解自动化质量保障的重要性。 + +## CI 配置说明 + +### pyproject.toml + +项目配置文件,声明了: +- 项目名称:campus_task +- 版本:0.1.0 +- 描述:一个模块化设计的命令行任务管理工具 +- Python 版本要求:>=3.8 +- pytest 配置:测试路径为 tests/ + +### GitHub Actions 配置 + +配置文件路径:`.github/workflows/test.yml` + +**触发条件**: +- push 到 main 或 master 分支时 +- 创建或更新 pull request 到 main 或 master 分支时 + +**工作流程**: + +| 步骤 | 名称 | 说明 | +|------|------|------| +| 1 | Checkout code | 检出代码到 CI 环境 | +| 2 | Set up Python | 设置 Python 3.10 环境 | +| 3 | Install dependencies | 安装依赖(pip, pytest) | +| 4 | Run tests | 运行 pytest 测试 | + +## CI 运行流程 + +### 成功场景(绿灯) + +当所有测试通过时,CI 显示绿色状态。 + +### 失败场景(红灯) + +当测试失败时,CI 显示红色状态,通知开发者及时修复问题。 + +## 课后反思 + +### 问题:为什么工程团队宁愿让机器"啰嗦地检查",也不完全依赖人工自觉? + +自动化检查确保每次提交都经过验证,具有以下优势: +1. **一致性**:机器每次都按照相同的规则检查代码 +2. **及时性**:问题可以在早期被发现和修复 +3. **覆盖面**:自动化测试可以覆盖更多的场景和边界条件 +4. **效率**:机器可以在几分钟内完成大量测试 \ No newline at end of file diff --git "a/\345\256\236\351\252\2147\346\212\245\345\221\212.md" "b/\345\256\236\351\252\2147\346\212\245\345\221\212.md" new file mode 100644 index 0000000..1fb2f9c --- /dev/null +++ "b/\345\256\236\351\252\2147\346\212\245\345\221\212.md" @@ -0,0 +1,51 @@ +# 实验7:发布、运维与维护——把课堂代码变成可交付小工具 + +## 实验目的 + +将 CampusTask 整理为可发布的命令行工具,并模拟一次线上 bug 修复,理解软件发布和维护的流程。 + +## 功能改造 + +### 命令行工具化 + +将程序改造成可通过 `python -m campus_task` 运行的模块。 + +### 新增功能 + +| 功能 | 说明 | +|------|------| +| `--version` / `-v` | 显示版本信息 | +| `--help` / `-h` | 显示帮助信息 | +| 日志记录 | 操作日志写入 campus_task.log | + +## Bug 修复流程 + +### Bug 描述 + +当 `tasks.json` 为空文件时,程序直接崩溃。 + +### 修复方案 + +在 `task_storage.py` 的 `load_tasks()` 函数中添加异常处理。 + +## 交付物 + +### 用户手册 + +[USER_GUIDE.md](USER_GUIDE.md) - 详细的用户使用说明 + +### 版本说明 + +[CHANGELOG.md](CHANGELOG.md) - 记录版本变更历史 + +## 课后反思 + +### 问题:为什么维护阶段往往比第一次写代码更能体现工程能力? + +维护阶段需要: +1. 理解现有代码 +2. 修复不引入新问题 +3. 在时间压力下工作 +4. 处理需求变更 + +这些都需要扎实的技术功底和良好的工程实践。 \ No newline at end of file diff --git "a/\345\256\236\351\252\2148\346\212\245\345\221\212.md" "b/\345\256\236\351\252\2148\346\212\245\345\221\212.md" new file mode 100644 index 0000000..98c4efe --- /dev/null +++ "b/\345\256\236\351\252\2148\346\212\245\345\221\212.md" @@ -0,0 +1,49 @@ +# 实验8:AI Harness 工程——把 AI 模型当成"不可靠但有能力的组件" + +## 实验目的 + +为 CampusTask 构建一个 AI Harness,让用户用自然语言管理任务,理解 AI Harness 工程与软件工程的相似性。 + +## 系统架构 + +### 系统流程 + +用户自然语言输入 → prompt_builder 构造提示 → mock_model 生成"意图" → parse_model 解析意图 → guardrail 检查是否危险 → tool_executor 调用已有 CampusTask 功能 → trace_logger 记录全过程 → eval_runner 用样例评测效果 + +### 模块说明 + +| 模块 | 功能 | 说明 | +|------|------|------| +| prompt_builder | 提示构造 | 将用户输入和任务状态构造成完整的提示词 | +| mock_model | 模拟模型 | 不调用真实大模型,使用规则引擎模拟输出 | +| parse_model | 解析意图 | 将模型输出解析为结构化的 action 和 args | +| guardrail | 安全检查 | 检查操作是否安全,防止危险操作 | +| tool_executor | 工具执行 | 调用 CampusTask 已有功能 | +| trace_logger | 日志记录 | 记录执行过程到 trace.jsonl | +| eval_runner | 评测运行 | 用 eval_cases.json 评测准确率 | + +## 评测结果 + +### 评测集 + +[eval_cases.json](eval_cases.json) 包含 20 条评测用例,覆盖所有操作类型。 + +### 运行方式 + +```bash +python ai_harness.py --eval +``` + +## 安全机制 + +### Guardrail 规则 + +1. **操作白名单**:只允许执行预定义的安全操作 +2. **参数验证**:确保必填参数存在 +3. **危险操作拦截**:如"删除所有任务"需要人工确认 + +## 课后反思 + +### 问题:为什么现在的 AI Harness 工程像软件工程?哪些工作仍由人写代码完成,哪些工作交给了模型? + +AI Harness 工程需要传统软件工程的设计、测试和安全实践。人写代码完成的工作包括:系统架构设计、Prompt 工程、Guardrail 规则、工具集成、评测框架和日志系统。交给模型的工作包括:意图识别、参数提取和语言理解。 \ No newline at end of file -- Gitee