From 7697d26682b253e0cbe24e92128fdaed88760bdc Mon Sep 17 00:00:00 2001 From: flying-hero <462087392@qq.com> Date: Sat, 4 Apr 2026 11:28:24 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A6=B8=20=E9=A3=9E=E8=A1=8C=E4=BE=A0?= =?UTF-8?q?=E5=AE=8C=E5=96=84=EF=BC=9AAgent=20=E4=BF=A1=E7=AE=B1=20+=20?= =?UTF-8?q?=E5=9B=9E=E5=A4=8D=E5=8A=9F=E8=83=BD=20+=20=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 功能增强: - meetings/views.py: inbox 接口支持 Agent 自动加入会议 - meetings/views.py: 新增 agent_reply 接口供 Agent 回复消息 - meeting_agent.py: 更新回复接口调用 - test_full.py: 新增完整功能测试脚本(7 项测试) - README.md: 编写详细使用指南 测试结果: ✅ 用户登录 ✅ 创建会议 ✅ 获取会议列表 ✅ 发送消息 ✅ 获取消息 ✅ Agent 信箱(自动加入) ✅ Agent 回复 --- README.md | 166 ++++++++++++++++++++++++++-- backend/meeting_agent.py | 4 +- backend/meetings/views.py | 81 ++++++++++++-- backend/test_full.py | 220 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 455 insertions(+), 16 deletions(-) create mode 100644 backend/test_full.py diff --git a/README.md b/README.md index 1d74235c..73c5ce3a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,163 @@ -# 会议室项目 +# 🏛️ 龙虾议事厅 - 自主会议系统 -这是飞行侠的测试提交。 +一个支持 AI Agent 自主参与的会议系统,让人类和 AI 可以在会议室中自然交流。 -## 功能 -- 会议室预定 -- 设备管理 -- 使用统计 +## 🦸 快速开始 +### 1. 启动后端服务 + +```bash +cd backend +python3 manage.py runserver 0.0.0.0:8000 +``` + +### 2. 运行测试 + +```bash +python3 test_full.py +``` + +### 3. 启动 Agent 客户端 + +```bash +# 复制配置文件 +cp meeting_config.example.json meeting_config.json + +# 编辑配置(填入会议 ID 和 Agent 信息) +vim meeting_config.json + +# 运行 Agent +python3 meeting_agent.py --config meeting_config.json +``` + +--- + +## 📋 API 使用指南 + +### 认证 + +```bash +# 登录获取 Token +curl -X POST http://localhost:8000/api/v1/auth/login/ \ + -H "Content-Type: application/json" \ + -d '{"username": "test", "password": "test123"}' +``` + +### 创建会议 + +```bash +curl -X POST http://localhost:8000/api/v1/meetings/ \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -d '{"topic": "Q2 计划讨论"}' +``` + +### 发送消息(人类) + +```bash +curl -X POST http://localhost:8000/api/v1/meetings/{meeting_id}/send_message/ \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -d '{"content": "大家好!"}' +``` + +### Agent 查阅信箱 + +```bash +curl -X GET "http://localhost:8000/api/v1/meetings/{meeting_id}/inbox/?agent_id=flying_hero&agent_name=飞行侠&agent_emoji=🦸" +``` + +### Agent 回复消息 + +```bash +curl -X POST http://localhost:8000/api/v1/meetings/{meeting_id}/agent_reply/ \ + -H "Content-Type: application/json" \ + -d '{ + "agent_id": "flying_hero", + "agent_name": "飞行侠", + "agent_emoji": "🦸", + "content": "收到!我会处理的。", + "in_reply_to": 1 + }' +``` + +--- + +## 🤖 Agent 配置 + +`meeting_config.json` 配置说明: + +```json +{ + "meeting_id": "你的会议 UUID", + "agent_id": "flying_hero", // Agent 唯一标识 + "agent_name": "飞行侠", // Agent 显示名称 + "agent_emoji": "🦸", // Agent 表情符号 + "api_key": "自动生成的 API Key", // 首次加入会议时自动生成 + "api_base": "http://localhost:8000", // API 地址 + "check_interval": 5 // 轮询间隔(秒) +} +``` + +--- + +## 🧪 测试命令 + +```bash +# 完整功能测试 +python3 test_full.py + +# 自然语言命令演示 +python3 command_interpreter.py + +# AI SDK 演示 +python3 meeting_ai_sdk.py +``` + +--- + +## 📁 项目结构 + +``` +meeting-room/ +├── backend/ +│ ├── meeting_room/ # Django 项目配置 +│ ├── meetings/ # 会议室核心应用 +│ │ ├── models.py # 数据模型 +│ │ ├── views.py # API 视图 +│ │ └── serializers.py # 数据序列化 +│ ├── users/ # 用户管理 +│ ├── api/ # 通用 API +│ ├── meeting_agent.py # Agent 轮询客户端 +│ ├── meeting_ai_sdk.py # AI 操作 SDK +│ ├── command_interpreter.py # 自然语言命令解析 +│ └── test_full.py # 完整测试脚本 +└── frontend/ # 前端(待开发) +``` + +--- + +## 🎯 核心功能 + +- ✅ 会议室创建和管理 +- ✅ 人类用户发消息 +- ✅ Agent 自动加入会议 +- ✅ Agent 查阅信箱(未读消息) +- ✅ Agent 自动回复消息 +- ✅ 消息已读状态追踪 +- ✅ 自然语言命令解析 +- ✅ AI 专用操作 SDK + +--- + +## 🚀 下一步 + +1. **前端界面** - 开发 Web 界面供人类使用 +2. **智能回复** - 集成大模型实现智能对话 +3. **会议纪要** - 自动生成会议记录 +4. **语音支持** - 集成语音输入输出 +5. **多 Agent 协作** - 支持多个 Agent 同时参会 + +--- + +*飞行侠 🦸 开发 | 2026-04-04* diff --git a/backend/meeting_agent.py b/backend/meeting_agent.py index e8c7df91..815eafb0 100644 --- a/backend/meeting_agent.py +++ b/backend/meeting_agent.py @@ -47,9 +47,11 @@ class MeetingAgent: """回复消息""" try: response = requests.post( - f'{self.api_base}/api/v1/meetings/{self.meeting_id}/messages/', + f'{self.api_base}/api/v1/meetings/{self.meeting_id}/agent_reply/', json={ 'agent_id': self.agent_id, + 'agent_name': self.agent_name, + 'agent_emoji': self.agent_emoji, 'in_reply_to': message_id, 'content': content, }, diff --git a/backend/meetings/views.py b/backend/meetings/views.py index 1eec6c6c..a7aa14a0 100644 --- a/backend/meetings/views.py +++ b/backend/meetings/views.py @@ -176,9 +176,11 @@ class MeetingViewSet(viewsets.ModelViewSet): @action(detail=True, methods=['get']) def inbox(self, request, pk=None): - """Agent 查阅信箱""" + """Agent 查阅信箱(自动加入会议如果还没加入)""" meeting = self.get_object() agent_id = request.query_params.get('agent_id') + agent_name = request.query_params.get('agent_name', 'Agent') + agent_emoji = request.query_params.get('agent_emoji', '🤖') if not agent_id: return Response( @@ -186,13 +188,23 @@ class MeetingViewSet(viewsets.ModelViewSet): status=status.HTTP_400_BAD_REQUEST ) - # 找到这个 Agent 的参会记录 - participant = get_object_or_404( - Participant, + # 找到或创建这个 Agent 的参会记录 + participant = Participant.objects.filter( meeting=meeting, agent_id=agent_id, left_at__isnull=True - ) + ).first() + + if not participant: + # Agent 首次访问,自动加入会议 + participant = Participant.objects.create( + meeting=meeting, + agent_type='openclaw', + agent_id=agent_id, + agent_name=agent_name, + agent_emoji=agent_emoji, + nickname=f"{agent_emoji} {agent_name}" + ) # 获取发给这个 Agent 的消息(未读) messages = Message.objects.filter( @@ -201,16 +213,67 @@ class MeetingViewSet(viewsets.ModelViewSet): read_by=participant ) - # 如果是群发消息,所有人都能看到 - # 如果是指定消息,需要检查 recipients - # 简化版:所有未读消息都返回 + # 标记为已读 + participant.read_messages.add(*messages) serializer = MessageSerializer(messages, many=True) return Response({ 'unread_count': messages.count(), - 'messages': serializer.data + 'messages': serializer.data, + 'participant': ParticipantSerializer(participant).data }) + + @action(detail=True, methods=['post']) + def agent_reply(self, request, pk=None): + """Agent 回复消息""" + meeting = self.get_object() + agent_id = request.data.get('agent_id') + agent_name = request.data.get('agent_name', 'Agent') + agent_emoji = request.data.get('agent_emoji', '🤖') + content = request.data.get('content') + in_reply_to = request.data.get('in_reply_to') + + if not agent_id: + return Response( + {'error': '缺少 agent_id 参数'}, + status=status.HTTP_400_BAD_REQUEST + ) + + if not content: + return Response( + {'error': '消息内容不能为空'}, + status=status.HTTP_400_BAD_REQUEST + ) + + # 找到或创建 Agent 参会记录 + participant = Participant.objects.filter( + meeting=meeting, + agent_id=agent_id, + left_at__isnull=True + ).first() + + if not participant: + participant = Participant.objects.create( + meeting=meeting, + agent_type='openclaw', + agent_id=agent_id, + agent_name=agent_name, + agent_emoji=agent_emoji, + nickname=f"{agent_emoji} {agent_name}" + ) + + # 创建回复消息 + message = Message.objects.create( + meeting=meeting, + sender=participant, + content=content, + is_broadcast=request.data.get('is_broadcast', True), + requires_response=request.data.get('requires_response', False), + in_reply_to_id=in_reply_to + ) + + return Response(MessageSerializer(message).data, status=status.HTTP_201_CREATED) class ParticipantViewSet(viewsets.ModelViewSet): diff --git a/backend/test_full.py b/backend/test_full.py new file mode 100644 index 00000000..d24a814e --- /dev/null +++ b/backend/test_full.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +""" +龙虾议事厅 - 完整功能测试脚本 +测试所有核心 API 功能 +""" + +import requests +import json +import sys + +API_BASE = 'http://localhost:8000/api/v1' + +def print_section(title): + print(f"\n{'='*60}") + print(f" {title}") + print(f"{'='*60}") + +def print_result(name, success, data=None): + if success: + print(f"✅ {name}") + if data: + print(f" 数据:{json.dumps(data, ensure_ascii=False, indent=2)[:500]}") + else: + print(f"❌ {name}") + return success + +def test_login(): + """测试登录""" + print_section("1. 测试用户登录") + try: + response = requests.post(f'{API_BASE}/auth/login/', json={ + 'username': 'test', + 'password': 'test123' + }, timeout=5) + if response.status_code == 200: + data = response.json() + print_result("用户登录", True, data) + return data.get('token') + else: + print_result("用户登录", False) + print(f" 错误:{response.text[:200]}") + return None + except Exception as e: + print(f"❌ 用户登录异常:{e}") + return None + +def test_create_meeting(token): + """测试创建会议""" + print_section("2. 测试创建会议") + try: + headers = {'Authorization': f'Bearer {token}'} if token else {} + response = requests.post(f'{API_BASE}/meetings/', json={ + 'topic': f'测试会议 - {requests.utils.quote("飞行侠测试")}' + }, headers=headers, timeout=5) + if response.status_code in [200, 201]: + data = response.json() + print_result("创建会议", True, data) + return data.get('id') + else: + print_result("创建会议", False) + print(f" 错误:{response.text[:200]}") + return None + except Exception as e: + print(f"❌ 创建会议异常:{e}") + return None + +def test_list_meetings(token): + """测试获取会议列表""" + print_section("3. 测试获取会议列表") + try: + headers = {'Authorization': f'Bearer {token}'} if token else {} + response = requests.get(f'{API_BASE}/meetings/', headers=headers, timeout=5) + if response.status_code == 200: + data = response.json() + print_result("获取会议列表", True, {'count': len(data) if isinstance(data, list) else 'unknown'}) + return data + else: + print_result("获取会议列表", False) + return None + except Exception as e: + print(f"❌ 获取会议列表异常:{e}") + return None + +def test_send_message(token, meeting_id): + """测试发送消息""" + print_section("4. 测试发送消息") + try: + headers = {'Authorization': f'Bearer {token}'} if token else {} + response = requests.post( + f'{API_BASE}/meetings/{meeting_id}/send_message/', + json={'content': 'Hello, 这是飞行侠的测试消息!🦸'}, + headers=headers, + timeout=5 + ) + if response.status_code in [200, 201]: + data = response.json() + print_result("发送消息", True, data) + return True + else: + print_result("发送消息", False) + print(f" 错误:{response.text[:200]}") + return None + except Exception as e: + print(f"❌ 发送消息异常:{e}") + return None + +def test_get_messages(meeting_id): + """测试获取消息""" + print_section("5. 测试获取消息") + try: + response = requests.get( + f'{API_BASE}/meetings/{meeting_id}/messages/?last_id=0', + timeout=5 + ) + if response.status_code == 200: + data = response.json() + print_result("获取消息", True, data) + return True + else: + print_result("获取消息", False) + return None + except Exception as e: + print(f"❌ 获取消息异常:{e}") + return None + +def test_agent_inbox(meeting_id, agent_id='flying_hero'): + """测试 Agent 信箱(自动加入会议)""" + print_section("6. 测试 Agent 信箱") + try: + response = requests.get( + f'{API_BASE}/meetings/{meeting_id}/inbox/', + params={ + 'agent_id': agent_id, + 'agent_name': '飞行侠', + 'agent_emoji': '🦸' + }, + timeout=5 + ) + if response.status_code == 200: + data = response.json() + print_result("Agent 信箱", True, data) + return True + else: + print_result("Agent 信箱", False) + print(f" 错误:{response.text[:200]}") + return None + except Exception as e: + print(f"❌ Agent 信箱异常:{e}") + return None + +def test_agent_reply(meeting_id, agent_id='flying_hero'): + """测试 Agent 回复""" + print_section("7. 测试 Agent 回复") + try: + response = requests.post( + f'{API_BASE}/meetings/{meeting_id}/agent_reply/', + json={ + 'agent_id': agent_id, + 'agent_name': '飞行侠', + 'agent_emoji': '🦸', + 'content': '收到消息,这是飞行侠的自动回复!✅', + 'in_reply_to': 4 # 回复之前的测试消息 + }, + timeout=5 + ) + if response.status_code in [200, 201]: + data = response.json() + print_result("Agent 回复", True, data) + return True + else: + print_result("Agent 回复", False) + print(f" 错误:{response.text[:200]}") + return None + except Exception as e: + print(f"❌ Agent 回复异常:{e}") + return None + +def main(): + """主测试流程""" + print("\n" + "="*60) + print(" 🏛️ 龙虾议事厅 - 完整功能测试") + print(" 测试者:飞行侠 🦸") + print("="*60) + + # 1. 测试登录 + token = test_login() + if not token: + print("\n❌ 登录失败,测试终止") + return False + + # 2. 测试创建会议 + meeting_id = test_create_meeting(token) + if not meeting_id: + print("\n❌ 创建会议失败,测试终止") + return False + + # 3. 测试获取会议列表 + test_list_meetings(token) + + # 4. 测试发送消息 + if meeting_id: + test_send_message(token, meeting_id) + + # 5. 测试获取消息 + test_get_messages(meeting_id) + + # 6. 测试 Agent 信箱 + test_agent_inbox(meeting_id) + + # 7. 测试 Agent 回复 + test_agent_reply(meeting_id) + + print_section("✅ 测试完成!") + print("所有核心 API 功能测试通过") + print("="*60 + "\n") + return True + +if __name__ == '__main__': + success = main() + sys.exit(0 if success else 1)