🦞 飞行侠实现:主持龙虾生成纪要
核心功能:
- Meeting 模型:添加 host_agent_id, host_instance_id
- 会议纪要 API:记录获取 + 纪要上传 + 结束通知
- 会议结束自动通知主持龙虾生成纪要
- 平台留存纪要供参会者下载
API 端点:
- GET /api/v1/meetings/{id}/records/ - 获取会议记录(主持专用)
- POST /api/v1/meetings/{id}/minutes/upload/ - 上传纪要(主持专用)
- POST /api/v1/meetings/{id}/end-notify/ - 会议结束通知
测试:
- test_host_minutes.py: 完整流程测试通过
算力分配:
- 中央平台:消息路由 + 数据存储(轻量级)
- 主持龙虾:生成纪要(消耗用户算力)
- 平台留存:纪要供所有参会者下载
This commit is contained in:
@@ -5,6 +5,7 @@ from rest_framework.routers import DefaultRouter
|
|||||||
from meetings.views import MeetingViewSet, ParticipantViewSet
|
from meetings.views import MeetingViewSet, ParticipantViewSet
|
||||||
from users.views import LoginView, RegisterView
|
from users.views import LoginView, RegisterView
|
||||||
from instances.views import InstanceRegisterView, MeetingJoinView, InstanceListView, WebhookNotifyView
|
from instances.views import InstanceRegisterView, MeetingJoinView, InstanceListView, WebhookNotifyView
|
||||||
|
from meetings.minutes_api import MeetingRecordsView, MinutesUploadView, MeetingEndNotifyView
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register(r'meetings', MeetingViewSet, basename='meeting')
|
router.register(r'meetings', MeetingViewSet, basename='meeting')
|
||||||
@@ -19,6 +20,10 @@ urlpatterns = [
|
|||||||
path("api/v1/instances/join-meeting/", MeetingJoinView.as_view()),
|
path("api/v1/instances/join-meeting/", MeetingJoinView.as_view()),
|
||||||
path("api/v1/instances/", InstanceListView.as_view()),
|
path("api/v1/instances/", InstanceListView.as_view()),
|
||||||
path("api/v1/instances/webhook-test/", WebhookNotifyView.as_view()),
|
path("api/v1/instances/webhook-test/", WebhookNotifyView.as_view()),
|
||||||
|
# 会议纪要 API(主持龙虾专用)
|
||||||
|
path("api/v1/meetings/<uuid:pk>/records/", MeetingRecordsView.as_view()),
|
||||||
|
path("api/v1/meetings/<uuid:pk>/minutes/upload/", MinutesUploadView.as_view()),
|
||||||
|
path("api/v1/meetings/<uuid:pk>/end-notify/", MeetingEndNotifyView.as_view()),
|
||||||
re_path(r'^api/v1/meetings/(?P<pk>[^/.]+)/generate-minutes/$', MeetingViewSet.as_view({'get': 'minutes'}), name='meeting-minutes'),
|
re_path(r'^api/v1/meetings/(?P<pk>[^/.]+)/generate-minutes/$', MeetingViewSet.as_view({'get': 'minutes'}), name='meeting-minutes'),
|
||||||
path("api/v1/", include(router.urls)),
|
path("api/v1/", include(router.urls)),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Generated by Django 6.0.3 on 2026-04-04 04:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("meetings", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="meeting",
|
||||||
|
name="host_agent_id",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, max_length=100, null=True, verbose_name="主持 Agent ID"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="meeting",
|
||||||
|
name="host_instance_id",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, max_length=100, null=True, verbose_name="主持实例 ID"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="meeting",
|
||||||
|
name="minutes_generated",
|
||||||
|
field=models.BooleanField(default=False, verbose_name="纪要已生成"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="meeting",
|
||||||
|
name="minutes_uploaded_at",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
blank=True, null=True, verbose_name="纪要上传时间"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
223
backend/meetings/minutes_api.py
Normal file
223
backend/meetings/minutes_api.py
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
会议纪要生成 API
|
||||||
|
供主持龙虾调用
|
||||||
|
"""
|
||||||
|
|
||||||
|
from rest_framework import serializers, status, views
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.decorators import api_view, permission_classes
|
||||||
|
from rest_framework.permissions import AllowAny
|
||||||
|
from .models import Meeting, Message, Participant
|
||||||
|
from .serializers import MeetingSerializer
|
||||||
|
from django.utils import timezone
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class MeetingRecordsSerializer(serializers.Serializer):
|
||||||
|
"""获取会议记录(主持龙虾专用)"""
|
||||||
|
meeting_id = serializers.UUIDField()
|
||||||
|
agent_id = serializers.CharField()
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
# 验证是否是主持龙虾
|
||||||
|
try:
|
||||||
|
meeting = Meeting.objects.get(id=data['meeting_id'])
|
||||||
|
if meeting.host_agent_id != data['agent_id']:
|
||||||
|
raise serializers.ValidationError('只有主持龙虾可以获取完整记录')
|
||||||
|
except Meeting.DoesNotExist:
|
||||||
|
raise serializers.ValidationError('会议不存在')
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class MeetingRecordsView(views.APIView):
|
||||||
|
"""
|
||||||
|
获取会议记录(主持龙虾专用)
|
||||||
|
|
||||||
|
GET /api/v1/meetings/{id}/records/?agent_id=xxx
|
||||||
|
"""
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
|
||||||
|
def get(self, request, pk=None):
|
||||||
|
agent_id = request.query_params.get('agent_id')
|
||||||
|
|
||||||
|
if not agent_id:
|
||||||
|
return Response({'error': '缺少 agent_id'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
try:
|
||||||
|
meeting = Meeting.objects.get(id=pk)
|
||||||
|
|
||||||
|
# 验证是否是主持龙虾
|
||||||
|
if meeting.host_agent_id != agent_id:
|
||||||
|
return Response(
|
||||||
|
{'error': '只有主持龙虾可以获取完整记录'},
|
||||||
|
status=status.HTTP_403_FORBIDDEN
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取所有消息
|
||||||
|
messages = Message.objects.filter(
|
||||||
|
meeting=meeting
|
||||||
|
).select_related('sender').order_by('created_at')
|
||||||
|
|
||||||
|
# 获取参会者列表
|
||||||
|
participants = Participant.objects.filter(
|
||||||
|
meeting=meeting
|
||||||
|
).select_related('user')
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'meeting': MeetingSerializer(meeting).data,
|
||||||
|
'messages': [{
|
||||||
|
'id': m.id,
|
||||||
|
'sender_name': m.sender.nickname,
|
||||||
|
'sender_emoji': m.sender.agent_emoji,
|
||||||
|
'content': m.content,
|
||||||
|
'created_at': m.created_at.isoformat(),
|
||||||
|
'requires_response': m.requires_response
|
||||||
|
} for m in messages],
|
||||||
|
'participants': [{
|
||||||
|
'agent_id': p.agent_id,
|
||||||
|
'agent_name': p.agent_name,
|
||||||
|
'agent_emoji': p.agent_emoji,
|
||||||
|
'is_host': p.is_host
|
||||||
|
} for p in participants]
|
||||||
|
})
|
||||||
|
|
||||||
|
except Meeting.DoesNotExist:
|
||||||
|
return Response({'error': '会议不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取会议记录失败:{e}")
|
||||||
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
|
||||||
|
class MinutesUploadSerializer(serializers.Serializer):
|
||||||
|
"""上传会议纪要"""
|
||||||
|
agent_id = serializers.CharField()
|
||||||
|
content = serializers.CharField()
|
||||||
|
format = serializers.CharField(default='markdown')
|
||||||
|
|
||||||
|
|
||||||
|
class MinutesUploadView(views.APIView):
|
||||||
|
"""
|
||||||
|
上传会议纪要(主持龙虾专用)
|
||||||
|
|
||||||
|
POST /api/v1/meetings/{id}/minutes/upload/
|
||||||
|
{
|
||||||
|
"agent_id": "flying_hero",
|
||||||
|
"content": "# 会议纪要\n\n...",
|
||||||
|
"format": "markdown"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
|
||||||
|
def post(self, request, pk=None):
|
||||||
|
serializer = MinutesUploadSerializer(data=request.data)
|
||||||
|
if not serializer.is_valid():
|
||||||
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
try:
|
||||||
|
meeting = Meeting.objects.get(id=pk)
|
||||||
|
agent_id = serializer.validated_data['agent_id']
|
||||||
|
|
||||||
|
# 验证是否是主持龙虾
|
||||||
|
if meeting.host_agent_id != agent_id:
|
||||||
|
return Response(
|
||||||
|
{'error': '只有主持龙虾可以上传纪要'},
|
||||||
|
status=status.HTTP_403_FORBIDDEN
|
||||||
|
)
|
||||||
|
|
||||||
|
# 存储纪要(简化版:存为文本,生产环境应该存文件)
|
||||||
|
from meetings.models import MeetingMinutes
|
||||||
|
minutes, created = MeetingMinutes.objects.update_or_create(
|
||||||
|
meeting=meeting,
|
||||||
|
defaults={
|
||||||
|
'content': serializer.validated_data['content'],
|
||||||
|
'generated_at': timezone.now()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 更新会议状态
|
||||||
|
meeting.minutes_generated = True
|
||||||
|
meeting.minutes_uploaded_at = timezone.now()
|
||||||
|
meeting.save()
|
||||||
|
|
||||||
|
logger.info(f"✅ 会议纪要已上传:{meeting.id} by {agent_id}")
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'status': 'success',
|
||||||
|
'message': '会议纪要已上传',
|
||||||
|
'minutes_id': str(minutes.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
except Meeting.DoesNotExist:
|
||||||
|
return Response({'error': '会议不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"上传会议纪要失败:{e}")
|
||||||
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
|
||||||
|
class MeetingEndNotifyView(views.APIView):
|
||||||
|
"""
|
||||||
|
会议结束通知(触发主持龙虾生成纪要)
|
||||||
|
|
||||||
|
POST /api/v1/meetings/{id}/end-notify/
|
||||||
|
"""
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
|
||||||
|
def post(self, request, pk=None):
|
||||||
|
try:
|
||||||
|
meeting = Meeting.objects.get(id=pk)
|
||||||
|
|
||||||
|
if meeting.status != 'ended':
|
||||||
|
return Response(
|
||||||
|
{'error': '会议尚未结束'},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
# 如果已有纪要,跳过
|
||||||
|
if meeting.minutes_generated:
|
||||||
|
return Response({
|
||||||
|
'status': 'skipped',
|
||||||
|
'message': '纪要已生成'
|
||||||
|
})
|
||||||
|
|
||||||
|
# 获取主持龙虾信息
|
||||||
|
if not meeting.host_agent_id:
|
||||||
|
return Response({
|
||||||
|
'status': 'skipped',
|
||||||
|
'message': '未指定主持龙虾'
|
||||||
|
})
|
||||||
|
|
||||||
|
# 通知主持龙虾(通过 Webhook)
|
||||||
|
from instances.webhook import push_message_to_instances
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
'event': 'meeting_ended',
|
||||||
|
'meeting_id': str(meeting.id),
|
||||||
|
'topic': meeting.topic,
|
||||||
|
'host_agent_id': meeting.host_agent_id,
|
||||||
|
'message': '会议已结束,请生成会议纪要',
|
||||||
|
'records_url': f'http://localhost:8000/api/v1/meetings/{meeting.id}/records/',
|
||||||
|
'upload_url': f'http://localhost:8000/api/v1/meetings/{meeting.id}/minutes/upload/'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 推送给主持龙虾所在实例
|
||||||
|
push_message_to_instances(
|
||||||
|
str(meeting.id),
|
||||||
|
payload,
|
||||||
|
target_agent_ids=[meeting.host_agent_id]
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"📬 会议结束通知已发送:{meeting.id} -> {meeting.host_agent_id}")
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'status': 'success',
|
||||||
|
'message': f'已通知主持龙虾 {meeting.host_agent_id}'
|
||||||
|
})
|
||||||
|
|
||||||
|
except Meeting.DoesNotExist:
|
||||||
|
return Response({'error': '会议不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"发送结束通知失败:{e}")
|
||||||
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
@@ -22,6 +22,12 @@ class Meeting(models.Model):
|
|||||||
started_at = models.DateTimeField(null=True, blank=True)
|
started_at = models.DateTimeField(null=True, blank=True)
|
||||||
ended_at = models.DateTimeField(null=True, blank=True)
|
ended_at = models.DateTimeField(null=True, blank=True)
|
||||||
|
|
||||||
|
# 主持龙虾(负责生成会议纪要)
|
||||||
|
host_agent_id = models.CharField(max_length=100, null=True, blank=True, verbose_name='主持 Agent ID')
|
||||||
|
host_instance_id = models.CharField(max_length=100, null=True, blank=True, verbose_name='主持实例 ID')
|
||||||
|
minutes_generated = models.BooleanField(default=False, verbose_name='纪要已生成')
|
||||||
|
minutes_uploaded_at = models.DateTimeField(null=True, blank=True, verbose_name='纪要上传时间')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = 'meetings'
|
db_table = 'meetings'
|
||||||
verbose_name = '会议室'
|
verbose_name = '会议室'
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ class MeetingSerializer(serializers.ModelSerializer):
|
|||||||
model = Meeting
|
model = Meeting
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'topic', 'host', 'host_name', 'status', 'invite_code',
|
'id', 'topic', 'host', 'host_name', 'status', 'invite_code',
|
||||||
'created_at', 'started_at', 'ended_at', 'participant_count'
|
'created_at', 'started_at', 'ended_at', 'participant_count',
|
||||||
|
'host_agent_id', 'host_instance_id', 'minutes_generated', 'minutes_uploaded_at'
|
||||||
]
|
]
|
||||||
read_only_fields = ['host', 'invite_code', 'status']
|
read_only_fields = ['host', 'invite_code', 'status']
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,14 @@ class MeetingViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
meeting = serializer.save(host=host)
|
meeting = serializer.save(host=host)
|
||||||
|
|
||||||
|
# 指定主持龙虾(可选)
|
||||||
|
host_agent_id = request.data.get('host_agent_id')
|
||||||
|
host_instance_id = request.data.get('host_instance_id')
|
||||||
|
if host_agent_id:
|
||||||
|
meeting.host_agent_id = host_agent_id
|
||||||
|
meeting.host_instance_id = host_instance_id
|
||||||
|
meeting.save()
|
||||||
|
|
||||||
# 创建主持人参会记录
|
# 创建主持人参会记录
|
||||||
Participant.objects.create(
|
Participant.objects.create(
|
||||||
meeting=meeting,
|
meeting=meeting,
|
||||||
@@ -79,6 +87,21 @@ class MeetingViewSet(viewsets.ModelViewSet):
|
|||||||
meeting.ended_at = timezone.now()
|
meeting.ended_at = timezone.now()
|
||||||
meeting.save()
|
meeting.save()
|
||||||
|
|
||||||
|
# 触发通知主持龙虾生成纪要
|
||||||
|
try:
|
||||||
|
from .minutes_api import MeetingEndNotifyView
|
||||||
|
from django.test import RequestFactory
|
||||||
|
|
||||||
|
factory = RequestFactory()
|
||||||
|
notify_request = factory.post(f'/api/v1/meetings/{meeting.id}/end-notify/')
|
||||||
|
response = MeetingEndNotifyView.as_view()(notify_request, pk=str(meeting.id))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
pass # 通知成功
|
||||||
|
except Exception as e:
|
||||||
|
# 通知失败不影响会议结束
|
||||||
|
pass
|
||||||
|
|
||||||
return Response({'status': '会议已结束'})
|
return Response({'status': '会议已结束'})
|
||||||
|
|
||||||
@action(detail=True, methods=['post'])
|
@action(detail=True, methods=['post'])
|
||||||
|
|||||||
149
backend/test_host_minutes.py
Normal file
149
backend/test_host_minutes.py
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
测试主持龙虾生成纪要完整流程
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
API_BASE = 'http://localhost:8000/api/v1'
|
||||||
|
|
||||||
|
def test_host_agent_minutes():
|
||||||
|
print("="*60)
|
||||||
|
print("🦞 测试主持龙虾生成纪要流程")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
# 1. 登录
|
||||||
|
res = requests.post(f'{API_BASE}/auth/login/', json={
|
||||||
|
'username': 'test',
|
||||||
|
'password': 'test123'
|
||||||
|
})
|
||||||
|
token = res.json()['token']
|
||||||
|
headers = {'Authorization': f'Bearer {token}'}
|
||||||
|
print(f"✅ 登录成功")
|
||||||
|
|
||||||
|
# 2. 注册实例(模拟 OpenClaw)
|
||||||
|
print("\n📝 注册实例...")
|
||||||
|
res = requests.post(f'{API_BASE}/instances/register/', json={
|
||||||
|
'instance_id': 'host-openclaw-001',
|
||||||
|
'instance_name': '主持龙虾实例',
|
||||||
|
'agent_ids': ['flying_hero'],
|
||||||
|
'webhook_url': 'http://localhost:8888/meeting-notify'
|
||||||
|
})
|
||||||
|
print(f"✅ 实例注册:{res.json()}")
|
||||||
|
|
||||||
|
# 3. 创建会议(指定主持龙虾)
|
||||||
|
print("\n🏛️ 创建会议(指定主持龙虾)...")
|
||||||
|
res = requests.post(f'{API_BASE}/meetings/', json={
|
||||||
|
'topic': '主持龙虾测试会议',
|
||||||
|
'host_agent_id': 'flying_hero',
|
||||||
|
'host_instance_id': 'host-openclaw-001'
|
||||||
|
}, headers=headers)
|
||||||
|
meeting = res.json()
|
||||||
|
meeting_id = meeting['id']
|
||||||
|
print(f"✅ 会议创建:{meeting_id}")
|
||||||
|
print(f" 主持龙虾:{meeting.get('host_agent_id')}")
|
||||||
|
|
||||||
|
# 4. 发送消息(模拟会议讨论)
|
||||||
|
print("\n💬 发送会议消息...")
|
||||||
|
messages = [
|
||||||
|
"大家好,开始今天的会议!",
|
||||||
|
"我来汇报一下 Q2 的进度。",
|
||||||
|
"这个项目需要更多资源支持。",
|
||||||
|
"好的,我会跟进这件事。",
|
||||||
|
"那我们下周再开会讨论细节。"
|
||||||
|
]
|
||||||
|
for msg in messages:
|
||||||
|
requests.post(f'{API_BASE}/meetings/{meeting_id}/send_message/', json={
|
||||||
|
'content': msg
|
||||||
|
}, headers=headers)
|
||||||
|
print(f"✅ 发送 {len(messages)} 条消息")
|
||||||
|
|
||||||
|
# 5. 结束会议(自动通知主持龙虾)
|
||||||
|
print("\n⏹️ 结束会议...")
|
||||||
|
res = requests.post(f'{API_BASE}/meetings/{meeting_id}/end/', headers=headers)
|
||||||
|
print(f"✅ 会议结束:{res.json()}")
|
||||||
|
|
||||||
|
# 6. 主持龙虾获取会议记录
|
||||||
|
print("\n📋 主持龙虾获取会议记录...")
|
||||||
|
res = requests.get(f'{API_BASE}/meetings/{meeting_id}/records/?agent_id=flying_hero')
|
||||||
|
if res.status_code == 200:
|
||||||
|
records = res.json()
|
||||||
|
print(f"✅ 获取成功")
|
||||||
|
print(f" 消息数:{len(records['messages'])}")
|
||||||
|
print(f" 参会者:{len(records['participants'])}")
|
||||||
|
else:
|
||||||
|
print(f"❌ 获取失败:{res.json()}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 7. 主持龙虾生成纪要(模拟 AI 生成)
|
||||||
|
print("\n🤖 主持龙虾生成纪要...")
|
||||||
|
minutes_content = f"""# 📋 会议纪要
|
||||||
|
|
||||||
|
**主题:** {meeting['topic']}
|
||||||
|
**时间:** {meeting['created_at']}
|
||||||
|
**主持:** 飞行侠 🦸
|
||||||
|
|
||||||
|
## 💬 讨论内容
|
||||||
|
|
||||||
|
会议共 {len(messages)} 条消息,主要讨论:
|
||||||
|
- Q2 进度汇报
|
||||||
|
- 资源需求
|
||||||
|
- 后续安排
|
||||||
|
|
||||||
|
## ✅ 决议事项
|
||||||
|
|
||||||
|
1. 跟进资源支持事宜
|
||||||
|
2. 下周继续开会讨论细节
|
||||||
|
|
||||||
|
---
|
||||||
|
*生成时间:现在*
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 8. 上传纪要
|
||||||
|
print("\n📤 上传会议纪要...")
|
||||||
|
res = requests.post(f'{API_BASE}/meetings/{meeting_id}/minutes/upload/', json={
|
||||||
|
'agent_id': 'flying_hero',
|
||||||
|
'content': minutes_content,
|
||||||
|
'format': 'markdown'
|
||||||
|
})
|
||||||
|
if res.status_code == 200:
|
||||||
|
print(f"✅ 纪要上传成功:{res.json()}")
|
||||||
|
else:
|
||||||
|
print(f"❌ 上传失败:{res.json()}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 9. 验证会议状态
|
||||||
|
print("\n📊 验证会议状态...")
|
||||||
|
res = requests.get(f'{API_BASE}/meetings/{meeting_id}/', headers=headers)
|
||||||
|
meeting = res.json()
|
||||||
|
print(f" 状态:{meeting['status']}")
|
||||||
|
print(f" 纪要已生成:{meeting.get('minutes_generated', False)}")
|
||||||
|
print(f" 上传时间:{meeting.get('minutes_uploaded_at')}")
|
||||||
|
|
||||||
|
# 10. 获取纪要(平台留存)
|
||||||
|
print("\n📥 下载会议纪要...")
|
||||||
|
res = requests.get(f'{API_BASE}/meetings/{meeting_id}/minutes/?output=markdown', headers=headers)
|
||||||
|
if res.status_code == 200:
|
||||||
|
minutes = res.json()
|
||||||
|
print(f"✅ 获取成功")
|
||||||
|
print(f" 内容预览:{minutes['markdown'][:100]}...")
|
||||||
|
else:
|
||||||
|
print(f"❌ 获取失败:{res.json()}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("✅ 主持龙虾生成纪要流程测试通过!")
|
||||||
|
print("="*60)
|
||||||
|
print("\n📊 流程总结:")
|
||||||
|
print("1. 用户创建会议 → 指定主持龙虾")
|
||||||
|
print("2. 会议进行 → 消息中央路由")
|
||||||
|
print("3. 会议结束 → 通知主持龙虾")
|
||||||
|
print("4. 主持龙虾 → 获取记录 + 生成纪要")
|
||||||
|
print("5. 上传纪要 → 平台留存供下载")
|
||||||
|
print("\n💡 算力分配:")
|
||||||
|
print("- 中央平台:消息路由 + 数据存储(轻量级)")
|
||||||
|
print("- 主持龙虾:生成纪要(消耗用户算力)")
|
||||||
|
return True
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test_host_agent_minutes()
|
||||||
Reference in New Issue
Block a user