API: wingman.team
Team 模块提供多 Runtime 协同组队、投票协调和群体通信功能。
模块概述
team 模块基于 Go server 的 TeamManager(内存态)实现分布式协同功能:
- 组队管理:加入/离开队伍,获取队友信息
- 投票协调:发起投票、参与投票、获取投票结果
- 状态汇报:上报自身状态,获取队友状态
- 群体通信:向所有队友广播消息
- 事件订阅:监听队伍相关事件
架构设计
┌─────────────────────────────────────────────────────────────────────────┐
│ Runtime A │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Team Module │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │joinTeam │ │leaveTeam │ │broadcast │ │ on(event, cb) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────────────────┐ │ │
│ │ │createVote│ │castVote │ │ Local State (members, votes)│ │ │
│ │ └──────────┘ └──────────┘ └──────────────────────────────┘ │ │
│ │ │ │
│ │ 脚本调用 → 发 team.outbound 事件(本地乐观更新) │ │
│ │ team.inbound 事件 → handleServerMessage 状态机 → on() 事件 │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ team.outbound ↑ team.inbound │
│ ↓ │ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ apps/agent 装配桥(EventHub ↔ RemoteClient) │ │
│ │ 出站:team.outbound → 同名 Notify 帧(team.join 等) │ │
│ │ 下行:inbox.message → team.inbound(处理即自动 inbox.ack) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ ↑ │
│ agent 通道(TCP, │
│ runtime 主动 outbound 连接) │
│ ↓ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Go Server │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ TeamManager │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ Teams: teamId → {leaderId, members[], state} │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ Votes: voteId → {teamId, subject, responses{}, deadline} │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ Inboxes: agentId → {msgId → {type, payload, acked}} │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
▲
│ agent 通道
│
┌─────────────────────────────────────────────────────────────────────────┐
│ Runtime B │
│ (相同架构) │
└─────────────────────────────────────────────────────────────────────────┘桥接细节见 docs/architecture-decisions.md「Team inbox push」与 docs/protocols.md inbox.message 行。团队与收件箱均为服务端内存态,服务重启丢失。
加入队伍
joinTeam(teamId, memberId?)
说明:加入指定队伍。
函数签名:
join_team(team_id: str, member_id: str = None) -> booljoinTeam(teamId: str, memberId: str = nil) -> boolean参数:
team_id/teamId- 队伍 IDmember_id/memberId- 可选,成员 ID(默认自动生成)
返回:是否加入成功
from wingman import team
# 加入队伍
if team.join_team("team_001", "player_alice"):
print("已加入队伍")
# 获取队伍信息
status = team.get_team_status()
print(f"队长: {status['leaderId']}")
print(f"队友: {status['members']}")离开队伍
leaveTeam()
说明:离开当前队伍。
函数签名:
leave_team() -> boolleaveTeam() -> boolean返回:是否离开成功
from wingman import team
# 离开队伍
if team.leave_team():
print("已离开队伍")投票功能
createVote(subject, timeout?)
说明:发起投票。
函数签名:
create_vote(subject: str, timeout: int = 30000) -> boolcreateVote(subject: str, timeout: number = 30000) -> boolean参数:
subject- 投票主题timeout- 超时时间(毫秒,默认 30000)
返回:是否发起成功
castVote(voteId, response)
说明:对指定投票进行投票。
函数签名:
cast_vote(vote_id: str, response: str) -> boolcastVote(voteId: str, response: str) -> boolean参数:
vote_id/voteId- 投票 IDresponse- 投票响应内容
返回:是否投票成功
getVoteResult(voteId)
说明:获取投票结果。
函数签名:
get_vote_result(vote_id: str) -> dictgetVoteResult(voteId: str) -> string参数:
vote_id/voteId- 投票 ID
返回:投票结果对象(JSON 字符串)
from wingman import team, json
# 发起投票
if team.create_vote("是否开始副本", 30000):
print("投票已发起")
# 获取投票结果
result_str = team.get_vote_result("vote_123")
result = json.decode(result_str)
print(f"投票主题: {result['subject']}")
print(f"是否活跃: {result['active']}")
print(f"投票响应: {result['responses']}")状态管理
reportStatus(status)
说明:上报自身状态到队伍。
函数签名:
report_status(status: dict) -> boolreportStatus(status: table) -> boolean参数:
status- 状态对象
返回:是否上报成功
from wingman import team
# 汇报状态
team.report_status({
"hp": 100,
"mp": 50,
"location": "town",
"ready": True
})getTeamStatus()
说明:获取当前队伍状态。
函数签名:
get_team_status() -> dictgetTeamStatus() -> string返回:队伍状态对象(JSON 字符串)
teamId- 队伍 IDleaderId- 队长 IDmembers- 成员列表state- 队伍状态(idle/voting/working)lastUpdate- 最后更新时间
from wingman import team, json
status_str = team.get_team_status()
status = json.decode(status_str)
print(f"队伍 ID: {status['teamId']}")
print(f"队长: {status['leaderId']}")
print(f"成员: {status['members']}")
print(f"状态: {status['state']}")群体通信
broadcast(message)
说明:向所有队友广播消息。
函数签名:
broadcast(message: dict | str) -> boolbroadcast(message: table | string) -> boolean参数:
message- 消息内容(对象或字符串)
返回:是否发送成功
from wingman import team
# 广播消息
team.broadcast({
"type": "coordination",
"action": "move_to",
"target": "boss_location",
"timestamp": 1234567890
})事件订阅
on(event, callback)
说明:订阅队伍事件。
函数签名:
on(event: str, callback: Callable) -> boolon(event: str, callback: function) -> boolean参数:
event- 事件类型(无需 "team." 前缀)callback- 回调函数
可用事件:
joined- 已加入队伍(服务器确认后触发)left- 已离开队伍vote_started- 投票开始vote_ended- 投票结束broadcast_received- 收到广播消息member_joined- 队友加入member_left- 队友离开
此外,团队范围推送的自定义类型(如 system.notice)以原类型名触发同名事件: on("system.notice", cb) 即可订阅,回调收到该帧完整载荷(未识别的 messageType 不再被静默丢弃)。
from wingman import team, json
# 监听投票开始
team.on("vote_started", lambda data: print(f"新投票: {json.decode(data)['subject']}"))
# 监听投票结束
team.on("vote_ended", lambda data: print(f"投票结束: {json.decode(data)['result']}"))
# 监听广播消息
team.on("broadcast_received", lambda data: print(f"收到广播: {json.decode(data)['message']}"))查询接口
isJoined()
说明:检查是否已加入队伍。
函数签名:
is_joined() -> boolisJoined() -> boolean返回:是否已加入队伍
getMemberId()
说明:获取当前成员 ID。
函数签名:
get_member_id() -> strgetMemberId() -> string返回:成员 ID
完整示例
队伍协同
from wingman import team, json, util
# 1. 加入队伍
if not team.join_team("dungeon_party", "warrior_01"):
print("加入队伍失败")
exit()
print("已加入队伍")
# 2. 监听队伍事件
team.on("vote_started", lambda data: print(f"投票开始: {json.decode(data)['subject']}"))
team.on("broadcast_received", lambda data: handle_broadcast(json.decode(data)))
def handle_broadcast(msg):
print(f"收到 {msg['senderId']} 的广播")
if msg['message']['type'] == 'attack':
coordinate_attack(msg['message'])
# 3. 汇报状态
team.report_status({
"role": "warrior",
"level": 50,
"ready": True
})
# 4. 等待并处理消息
while team.is_joined():
util.sleep(1000)
# 检查投票
status = json.decode(team.get_team_status())
if status['state'] == 'voting':
print("队伍正在投票...")
# 定期汇报状态
team.report_status({"hp": get_current_hp()})投票流程
from wingman import team, json, util
# 监听投票事件
team.on("vote_started", lambda data: handle_vote_start(json.decode(data)))
team.on("vote_ended", lambda data: print(f"投票结果: {json.decode(data)['result']}"))
def handle_vote_start(vote):
print(f"投票: {vote['subject']}")
print(f"截止时间: {vote['deadline']}")
# 自动决策(示例)
if vote['subject'] == "是否休息":
team.cast_vote(vote['voteId'], "agree")
else:
team.cast_vote(vote['voteId'], "disagree")
# 发起投票
team.create_vote("是否前往BOSS区域", 30000)
# 等待投票结果
util.sleep(35000)
result = json.decode(team.get_vote_result("vote_xxx"))
print(f"最终结果: {result['responses']}")可用接口
| Python 函数 | Lua 函数 | 说明 | 参数 |
|---|---|---|---|
join_team(teamId, memberId?) | joinTeam(teamId, memberId?) | 加入队伍 | teamId: 队伍ID memberId: 成员ID 返回: bool |
leave_team() | leaveTeam() | 离开队伍 | 返回: bool |
create_vote(subject, timeout?) | createVote(subject, timeout?) | 发起投票 | subject: 主题 timeout: 超时(ms) 返回: bool |
cast_vote(voteId, response) | castVote(voteId, response) | 投票 | voteId: 投票ID response: 响应 返回: bool |
get_vote_result(voteId) | getVoteResult(voteId) | 获取投票结果 | voteId: 投票ID 返回: JSON字符串 |
report_status(status) | reportStatus(status) | 汇报状态 | status: 状态对象 返回: bool |
broadcast(message) | broadcast(message) | 广播消息 | message: 消息对象 返回: bool |
get_team_status() | getTeamStatus() | 获取队伍状态 | 返回: JSON字符串 |
is_joined() | isJoined() | 是否已加入 | 返回: bool |
get_member_id() | getMemberId() | 获取成员ID | 返回: string |
on(event, callback) | on(event, callback) | 订阅事件 | event: 事件名 callback: 回调 返回: bool |
协议消息格式
出站帧经 agent 通道 Notify 直发(类型如下表);下行帧由 server 封装在 inbox.message (messageType 字段携带下列类型,载荷即 payload)推送,桌面 runtime 处理后自动回 inbox.ack。完整帧格式见 docs/protocols.md。
Runtime [Server]
加入队伍:
{
"type": "team.join",
"teamId": "team_001",
"memberId": "player_01",
"timestamp": 1234567890
}离开队伍:
{
"type": "team.leave",
"teamId": "team_001",
"memberId": "player_01",
"timestamp": 1234567890
}发起投票:
{
"type": "team.vote_create",
"teamId": "team_001",
"proposerId": "player_01",
"subject": "是否开始副本",
"timeout": 30000,
"timestamp": 1234567890
}投票:
{
"type": "team.vote_cast",
"teamId": "team_001",
"voteId": "vote_123",
"memberId": "player_01",
"response": "agree",
"timestamp": 1234567890
}广播:
{
"type": "team.broadcast",
"teamId": "team_001",
"memberId": "player_01",
"message": {"type": "coordination", "action": "move"},
"timestamp": 1234567890
}状态汇报:
{
"type": "team.status_report",
"teamId": "team_001",
"memberId": "player_01",
"status": {"hp": 100, "ready": true},
"timestamp": 1234567890
}Server [Runtime]
加入确认:
{
"type": "team.joined",
"teamId": "team_001",
"leaderId": "player_leader",
"memberId": "player_01",
"members": ["player_leader", "player_01"],
"timestamp": 1234567890
}队友加入:
{
"type": "team.member_joined",
"teamId": "team_001",
"memberId": "player_02",
"timestamp": 1234567890
}队友离开:
{
"type": "team.member_left",
"teamId": "team_001",
"memberId": "player_02",
"timestamp": 1234567890
}投票开始:
{
"type": "team.vote_started",
"voteId": "vote_123",
"teamId": "team_001",
"proposerId": "player_leader",
"subject": "是否开始副本",
"deadline": 1234599999,
"timestamp": 1234567890
}投票结束:
{
"type": "team.vote_ended",
"voteId": "vote_123",
"result": {
"voteId": "vote_123",
"subject": "是否开始副本",
"responses": {"player_01": "agree", "player_02": "agree"},
"active": false
},
"timestamp": 1234567890
}收到广播:
{
"type": "team.broadcast_received",
"senderId": "player_leader",
"teamId": "team_001",
"message": {"type": "coordination", "action": "attack"},
"timestamp": 1234567890
}注意事项
依赖 agent 通道连接:
- team 出站/下行都经 apps/agent 装配桥走 agent 通道 Notify(见 docs/protocols.md)
- runtime 未连接 Go server 时,team.* 只做本地乐观更新,收不到服务器事件
本地状态同步:
getTeamStatus()返回本地缓存的状态- 实际状态以服务器为准,通过事件更新
事件订阅时机:
- 建议在
joinTeam()之前订阅事件 - 确保不会错过任何事件通知
- 建议在
投票超时:
- 投票有超时时间,超时后自动结束
- 超时后的投票结果仍可通过
getVoteResult()获取
广播消息:
- 广播消息由服务器转发给所有队友
- 发送者不会收到自己的广播(避免重复)
线程安全:
- 所有函数都是线程安全的
- 事件回调可能在独立线程中执行