feat(team-communication): 团队沟通配置 — 团队角色(+agentid) + 问题类型(冒泡路径)

新增通用问题冒泡的数据模型与配置 CRUD:
- models/json: pipeline_team_roles(角色+agentid,多agent区分) + pipeline_problem_types(escalation_path)
- pipeline_core/init.py: _simple_crud 通用 CRUD + 6 个 create/update/delete 注册
- wwwroot/api: 6 个 dspy 端点
- scripts/load_path.py: RBAC 路径
- skills_library/all/team-communication/SKILL.md: 团队沟通技能
This commit is contained in:
yumoqing 2026-08-16 21:25:38 +08:00
parent 670e73d8e5
commit ff02533cb6
13 changed files with 434 additions and 0 deletions

View File

@ -0,0 +1,27 @@
{
"tblname": "pipeline_problem_types",
"title": "问题类型",
"params": {
"sortby": "name",
"browserfields": {
"exclouded": ["id", "description", "escalation_path"],
"cwidth": {}
},
"editexclouded": [
"id", "created_at"
],
"editable": {
"new_data_url": "{{entire_url('../api/pipeline_problem_types_create.dspy')}}",
"update_data_url": "{{entire_url('../api/pipeline_problem_types_update.dspy')}}",
"delete_data_url": "{{entire_url('../api/pipeline_problem_types_delete.dspy')}}"
},
"confidential_fields": [],
"subtables": [
{
"field": "pipeline_id",
"title": "所属产线",
"subtable": "pipelines"
}
]
}
}

View File

@ -0,0 +1,27 @@
{
"tblname": "pipeline_team_roles",
"title": "团队角色",
"params": {
"sortby": "role_name",
"browserfields": {
"exclouded": ["id", "description"],
"cwidth": {}
},
"editexclouded": [
"id", "created_at"
],
"editable": {
"new_data_url": "{{entire_url('../api/pipeline_team_roles_create.dspy')}}",
"update_data_url": "{{entire_url('../api/pipeline_team_roles_update.dspy')}}",
"delete_data_url": "{{entire_url('../api/pipeline_team_roles_delete.dspy')}}"
},
"confidential_fields": [],
"subtables": [
{
"field": "pipeline_id",
"title": "所属产线",
"subtable": "pipelines"
}
]
}
}

View File

@ -0,0 +1,87 @@
{
"summary": [
{
"name": "pipeline_problem_types",
"title": "问题类型表",
"primary": [
"id"
]
}
],
"fields": [
{
"name": "id",
"title": "id",
"type": "str",
"length": 32,
"nullable": "no",
"uitype": "hidden"
},
{
"name": "pipeline_id",
"title": "所属产线",
"type": "str",
"length": 32,
"nullable": "no",
"uitype": "select"
},
{
"name": "name",
"title": "问题类型标识",
"type": "str",
"length": 50,
"nullable": "no",
"uitype": "text"
},
{
"name": "title",
"title": "显示名",
"type": "str",
"length": 100,
"uitype": "text"
},
{
"name": "description",
"title": "说明",
"type": "text",
"uitype": "textarea"
},
{
"name": "escalation_path",
"title": "冒泡路径(JSON角色数组如[\"main_agent\",\"customer\"])",
"type": "text",
"uitype": "textarea"
},
{
"name": "created_at",
"title": "创建时间",
"type": "timestamp",
"uitype": "timestamp"
}
],
"indexes": [
{
"name": "idx_problemtype_pipeline",
"idxtype": "index",
"idxfields": [
"pipeline_id"
]
},
{
"name": "idx_problemtype_name",
"idxtype": "unique",
"idxfields": [
"pipeline_id",
"name"
]
}
],
"codes": [
{
"field": "pipeline_id",
"table": "pipelines",
"valuefield": "id",
"textfield": "name"
}
]
}

View File

@ -0,0 +1,106 @@
{
"summary": [
{
"name": "pipeline_team_roles",
"title": "团队角色表",
"primary": [
"id"
]
}
],
"fields": [
{
"name": "id",
"title": "id",
"type": "str",
"length": 32,
"nullable": "no",
"uitype": "hidden"
},
{
"name": "pipeline_id",
"title": "所属产线",
"type": "str",
"length": 32,
"nullable": "no",
"uitype": "select"
},
{
"name": "role_name",
"title": "角色标识",
"type": "str",
"length": 50,
"nullable": "no",
"uitype": "text"
},
{
"name": "agentid",
"title": "Agent标识(同角色多agent区分人可空)",
"type": "str",
"length": 64,
"nullable": "yes",
"uitype": "text"
},
{
"name": "role_type",
"title": "角色类型",
"type": "str",
"length": 20,
"nullable": "no",
"default": "agent",
"uitype": "select"
},
{
"name": "display_name",
"title": "显示名",
"type": "str",
"length": 100,
"uitype": "text"
},
{
"name": "description",
"title": "职责说明",
"type": "text",
"uitype": "textarea"
},
{
"name": "created_at",
"title": "创建时间",
"type": "timestamp",
"uitype": "timestamp"
}
],
"indexes": [
{
"name": "idx_teamrole_pipeline",
"idxtype": "index",
"idxfields": [
"pipeline_id"
]
},
{
"name": "idx_teamrole_name",
"idxtype": "unique",
"idxfields": [
"pipeline_id",
"role_name",
"agentid"
]
}
],
"codes": [
{
"field": "pipeline_id",
"table": "pipelines",
"valuefield": "id",
"textfield": "name"
},
{
"field": "role_type",
"table": "appcodes_kv",
"valuefield": "k",
"textfield": "v",
"cond": "parentid='team_role_type'"
}
]
}

View File

@ -297,6 +297,67 @@ async def delete_llm(params_kw):
return json.dumps(result, ensure_ascii=False, default=str)
async def _simple_crud(table, op, params_kw):
"""团队沟通配置表通用 CRUDtable: 表名, op: create/update/delete"""
result = {'success': False, 'message': ''}
try:
db, dbname = _get_sor()
async with db.sqlorContext(dbname) as sor:
data = params_kw.copy()
data.pop('page', None)
data.pop('rows', None)
data.pop('data_filter', None)
if op == 'create':
data['id'] = getID()
await sor.C(table, data)
result['id'] = data['id']
result['success'] = True
result['message'] = '创建成功'
elif op == 'update':
rid = data.pop('id', None)
if not rid:
result['message'] = '缺少id'
else:
await sor.U(table, {**data, 'id': rid})
result['success'] = True
result['message'] = '更新成功'
else:
rid = params_kw.get('id')
if not rid:
result['message'] = '缺少id'
else:
await sor.D(table, {'id': rid})
result['success'] = True
result['message'] = '删除成功'
except Exception as e:
result['message'] = str(e)
return json.dumps(result, ensure_ascii=False, default=str)
async def create_pipeline_team_role(params_kw):
return await _simple_crud('pipeline_team_roles', 'create', params_kw)
async def update_pipeline_team_role(params_kw):
return await _simple_crud('pipeline_team_roles', 'update', params_kw)
async def delete_pipeline_team_role(params_kw):
return await _simple_crud('pipeline_team_roles', 'delete', params_kw)
async def create_pipeline_problem_type(params_kw):
return await _simple_crud('pipeline_problem_types', 'create', params_kw)
async def update_pipeline_problem_type(params_kw):
return await _simple_crud('pipeline_problem_types', 'update', params_kw)
async def delete_pipeline_problem_type(params_kw):
return await _simple_crud('pipeline_problem_types', 'delete', params_kw)
def load_pipeline_core():
"""注册函数到 ServerEnv"""
env = ServerEnv()
@ -325,6 +386,19 @@ def load_pipeline_core():
env.update_pipeline_versions = update_pipeline_version
env.delete_pipeline_version = delete_pipeline_version
env.delete_pipeline_versions = delete_pipeline_version
# Team communication (团队沟通:团队角色 + 问题类型)
env.create_pipeline_team_role = create_pipeline_team_role
env.create_pipeline_team_roles = create_pipeline_team_role
env.update_pipeline_team_role = update_pipeline_team_role
env.update_pipeline_team_roles = update_pipeline_team_role
env.delete_pipeline_team_role = delete_pipeline_team_role
env.delete_pipeline_team_roles = delete_pipeline_team_role
env.create_pipeline_problem_type = create_pipeline_problem_type
env.create_pipeline_problem_types = create_pipeline_problem_type
env.update_pipeline_problem_type = update_pipeline_problem_type
env.update_pipeline_problem_types = update_pipeline_problem_type
env.delete_pipeline_problem_type = delete_pipeline_problem_type
env.delete_pipeline_problem_types = delete_pipeline_problem_type
# Publish
env.publish_pipeline = publish_pipeline
debug(f'[{MODULE_NAME}] module loaded')

View File

@ -45,6 +45,23 @@ PATHS_LOGINED = [
f"/{MOD}/api/pipelines_create.dspy",
f"/{MOD}/api/pipelines_update.dspy",
f"/{MOD}/api/pipelines_delete.dspy",
# Team communication (团队沟通:团队角色 + 问题类型 CRUD)
f"/{MOD}/pipeline_team_roles/index.ui",
f"/{MOD}/pipeline_team_roles/get_pipeline_team_roles.dspy",
f"/{MOD}/pipeline_team_roles/add_pipeline_team_roles.dspy",
f"/{MOD}/pipeline_team_roles/update_pipeline_team_roles.dspy",
f"/{MOD}/pipeline_team_roles/delete_pipeline_team_roles.dspy",
f"/{MOD}/pipeline_problem_types/index.ui",
f"/{MOD}/pipeline_problem_types/get_pipeline_problem_types.dspy",
f"/{MOD}/pipeline_problem_types/add_pipeline_problem_types.dspy",
f"/{MOD}/pipeline_problem_types/update_pipeline_problem_types.dspy",
f"/{MOD}/pipeline_problem_types/delete_pipeline_problem_types.dspy",
f"/{MOD}/api/pipeline_team_roles_create.dspy",
f"/{MOD}/api/pipeline_team_roles_update.dspy",
f"/{MOD}/api/pipeline_team_roles_delete.dspy",
f"/{MOD}/api/pipeline_problem_types_create.dspy",
f"/{MOD}/api/pipeline_problem_types_update.dspy",
f"/{MOD}/api/pipeline_problem_types_delete.dspy",
]

View File

@ -0,0 +1,66 @@
---
name: team-communication
description: 团队沟通通用问题冒泡——问题按类型沿冒泡路径在团队角色间流转解决即停、未解决继续冒泡、尽头由人兜底。触发agent 发现问题、角色提问、PM 退回、故障上报、客户提问等。
---
# 团队沟通(通用问题冒泡)
## 核心概念
- **团队** = 一组不同角色的 agent + 人(`pipeline_team_roles` 表,每个成员 = role_name + agentid
- 多 agent 存在:同一角色可有多个 agent必须用 `agentid` 区分到具体处理方。
- `role_type``agent`AI agent/ `human`(人,如客户)。
- **问题类型**`pipeline_problem_types`= 一条冒泡路径 `escalation_path`(处理方序列)。
- **冒泡路径条目**`"role_name"`(该角色任意 agent/人)或 `{"role":"x","agentid":"y"}`(指定 agent
## 冒泡规则(核心)
```
提出问题 raise → 沿路径第一个处理方开始
处理方判断:
解决了 → resolvestatus=answered停止冒泡任务恢复
没解决 → escalateescalation_pos+1沿路径继续走到下一个处理方
路径尽头 = 最后一个处理方(通常是"人" customer兜底
```
## 工具
| 工具 | 作用 |
|---|---|
| `list_questions` | 列出「当前该我主agent处理」的问题不混入退回给角色 agent 的) |
| `answer_question` | 回答/解决 → 停止冒泡,任务恢复 |
| `escalate_question` | 答不了 → 沿冒泡路径转下一个处理方(通常转客户) |
| `diagnose_project` | 「待回答问题」计数 = 当前该主 agent 处理的问题数 |
后端引擎(`pipeline_service/communication.py``raise_problem` / `resolve_problem` / `escalate_problem` / `list_problems_for`
## 处理流程(发现「待回答问题」> 0 时必须执行)
1. `list_questions` 列出所有「待我处理」的 pending 问题,不要只报统计数字。
2. 对每个问题:
- 你能解决 → `answer_question` 直接回答(任务自动恢复,冒泡停止)。
- 你答不了 → `escalate_question` 沿冒泡路径转下一个处理方(客户),等对方回答后再闭环。
## SDLC 产线默认问题类型sdlc_general
| 问题类型 | 冒泡路径 | 说明 |
|---|---|---|
| `need_info` | `[main_agent, customer]` | 角色 agent 缺信息 → 主 agent 答 / 转客户 |
| `review_reject` | `[被退角色, pm]` | PM 退回 → 被退角色响应 / 兜回 pm |
| `fault_report` | `[main_agent, customer]` | 任务失败报障 → 主 agent / 客户人工介入 |
## 多 agent 路由规则
- 处理方 `agentid` 为空 = 该角色任意 agent 可处理。
- 处理方 `agentid` 指定 = 只有该 agent 能处理(同角色其他 agent 看不到)。
- 角色 agent 重新认领任务时,只注入「本角色/本 agent」应响应的 pending 退回意见(`get_task_qa(include_pending_for=role, include_pending_agentid=agentid)`)。
## 相关文件
- `pipeline_service/communication.py` — 通用冒泡引擎raise/resolve/escalate/list_for
- `pipeline_service/questions.py` — 向后兼容层agent_ask/answer_question/forward_question 委托 communication
- `pipeline_service/agent_loop.py` — 角色提问(need_info)、PM 退回(review_reject)、故障(fault_report)、PM 审核通过答结
- `pipeline_service/sdlc_ability.py` — cockpit 工具 list_questions/answer_question/escalate_question
- `pipeline-core/models/pipeline_team_roles.json` + `pipeline_problem_types.json` — 团队角色 + 问题类型定义

View File

@ -0,0 +1,5 @@
func = create_pipeline_problem_type
if func is None:
return json.dumps({'status': 'error', 'message': 'function not found'})
result = await func(params_kw)
return result

View File

@ -0,0 +1,5 @@
func = delete_pipeline_problem_type
if func is None:
return json.dumps({'status': 'error', 'message': 'function not found'})
result = await func(params_kw)
return result

View File

@ -0,0 +1,5 @@
func = update_pipeline_problem_type
if func is None:
return json.dumps({'status': 'error', 'message': 'function not found'})
result = await func(params_kw)
return result

View File

@ -0,0 +1,5 @@
func = create_pipeline_team_role
if func is None:
return json.dumps({'status': 'error', 'message': 'function not found'})
result = await func(params_kw)
return result

View File

@ -0,0 +1,5 @@
func = delete_pipeline_team_role
if func is None:
return json.dumps({'status': 'error', 'message': 'function not found'})
result = await func(params_kw)
return result

View File

@ -0,0 +1,5 @@
func = update_pipeline_team_role
if func is None:
return json.dumps({'status': 'error', 'message': 'function not found'})
result = await func(params_kw)
return result