🦸 飞行侠完善:Agent 信箱 + 回复功能 + 完整测试

功能增强:
- meetings/views.py: inbox 接口支持 Agent 自动加入会议
- meetings/views.py: 新增 agent_reply 接口供 Agent 回复消息
- meeting_agent.py: 更新回复接口调用
- test_full.py: 新增完整功能测试脚本(7 项测试)
- README.md: 编写详细使用指南

测试结果:
 用户登录
 创建会议
 获取会议列表
 发送消息
 获取消息
 Agent 信箱(自动加入)
 Agent 回复
This commit is contained in:
2026-04-04 11:28:24 +08:00
parent c9f41b6410
commit 7697d26682
4 changed files with 455 additions and 16 deletions

166
README.md
View File

@@ -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*

View File

@@ -47,9 +47,11 @@ class MeetingAgent:
"""回复消息""" """回复消息"""
try: try:
response = requests.post( 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={ json={
'agent_id': self.agent_id, 'agent_id': self.agent_id,
'agent_name': self.agent_name,
'agent_emoji': self.agent_emoji,
'in_reply_to': message_id, 'in_reply_to': message_id,
'content': content, 'content': content,
}, },

View File

@@ -176,9 +176,11 @@ class MeetingViewSet(viewsets.ModelViewSet):
@action(detail=True, methods=['get']) @action(detail=True, methods=['get'])
def inbox(self, request, pk=None): def inbox(self, request, pk=None):
"""Agent 查阅信箱""" """Agent 查阅信箱(自动加入会议如果还没加入)"""
meeting = self.get_object() meeting = self.get_object()
agent_id = request.query_params.get('agent_id') 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: if not agent_id:
return Response( return Response(
@@ -186,13 +188,23 @@ class MeetingViewSet(viewsets.ModelViewSet):
status=status.HTTP_400_BAD_REQUEST status=status.HTTP_400_BAD_REQUEST
) )
# 找到这个 Agent 的参会记录 # 找到或创建这个 Agent 的参会记录
participant = get_object_or_404( participant = Participant.objects.filter(
Participant,
meeting=meeting, meeting=meeting,
agent_id=agent_id, agent_id=agent_id,
left_at__isnull=True 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 的消息(未读) # 获取发给这个 Agent 的消息(未读)
messages = Message.objects.filter( messages = Message.objects.filter(
@@ -201,16 +213,67 @@ class MeetingViewSet(viewsets.ModelViewSet):
read_by=participant read_by=participant
) )
# 如果是群发消息,所有人都能看到 # 标记为已读
# 如果是指定消息,需要检查 recipients participant.read_messages.add(*messages)
# 简化版:所有未读消息都返回
serializer = MessageSerializer(messages, many=True) serializer = MessageSerializer(messages, many=True)
return Response({ return Response({
'unread_count': messages.count(), '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): class ParticipantViewSet(viewsets.ModelViewSet):

220
backend/test_full.py Normal file
View File

@@ -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)