- 角色规范: agent 角色 agent.{role}(_normalize_role 补前缀+ROLE_ALIASES/ROLE_CHAIN/SDL_ROLES 改规范名),
人角色 {orgtype}.{role}(默认 owner.superuser)
- role_agent_run/pm_review_run/handle_failed_task 硬编码状态 SQL 改 task_capability 工具
(submit/approve/reject/complete/mark_failed/retry,CAS+审计)
- communication/questions/sdlc_ability 默认 handler 角色规范化(main_agent→agent.main_agent, customer→owner.superuser)
- 通用助手路径(AgentExecutor)不动,保持 hermes 式自由对话
102 lines
3.7 KiB
Python
102 lines
3.7 KiB
Python
"""问题回路 — 向后兼容层,委托 communication.py 通用冒泡引擎。
|
||
|
||
规范(团队角色/问题类型/冒泡路径)在 team-communication skill 里;
|
||
本模块只保留旧签名供 DSPY(env.question_*) 与既有调用方使用,内部走 communication.py。
|
||
|
||
状态只有 pending/answered;归属不再靠 status,而靠 current_handler_role/agentid 两列。
|
||
"""
|
||
|
||
import json
|
||
import logging
|
||
from sqlor.dbpools import DBPools
|
||
|
||
from .communication import (
|
||
raise_problem,
|
||
resolve_problem,
|
||
escalate_problem,
|
||
list_problems_for,
|
||
get_task_qa,
|
||
)
|
||
|
||
DBNAME = "pipeline"
|
||
logger = logging.getLogger("pipeline.questions")
|
||
|
||
|
||
def _get_db():
|
||
db = DBPools()
|
||
if not db.databases:
|
||
from appPublic.jsonConfig import getConfig
|
||
config = getConfig()
|
||
if config.databases:
|
||
db.databases = config.databases
|
||
return db, DBNAME
|
||
|
||
|
||
async def agent_ask(tenant_id: str, task_id: str, from_role: str, question: str,
|
||
context: dict = None) -> str:
|
||
"""旧签名:按 from_role 推断问题类型 + 首处理方,走通用冒泡引擎。
|
||
|
||
新代码请直接调 communication.raise_problem 显式指定首处理方。
|
||
"""
|
||
r = (from_role or "").lower()
|
||
if "pm" in r:
|
||
ptype, hr = "review_reject", "agent.develop"
|
||
elif "main_agent" in r or "cockpit" in r:
|
||
ptype, hr = "fault_report", "agent.main_agent"
|
||
else:
|
||
ptype, hr = "need_info", "agent.main_agent"
|
||
return await raise_problem(ptype, question, from_role, "",
|
||
tenant_id=tenant_id, task_id=task_id,
|
||
context=context, first_handler_role=hr)
|
||
|
||
|
||
async def answer_question(question_id: str, answer: str, answered_by: str = "",
|
||
answer_source: str = "agent.main_agent", resume: bool = True):
|
||
"""回答问题(解决 → 停止冒泡,任务恢复 submitted)。"""
|
||
return await resolve_problem(question_id, answer, answered_by, answer_source,
|
||
resume_task=resume)
|
||
|
||
|
||
async def forward_question(question_id: str, next_handler_role: str = "owner.superuser",
|
||
next_handler_agentid: str = ""):
|
||
"""答不了 → 沿冒泡路径转给下一个处理方(默认业主/客户)。"""
|
||
return await escalate_problem(question_id, next_handler_role, next_handler_agentid,
|
||
by_role="agent.main_agent")
|
||
|
||
|
||
async def get_question(question_id: str):
|
||
"""取单条问题记录(含 context 解析)。"""
|
||
db, dbname = _get_db()
|
||
async with db.sqlorContext(dbname) as sor:
|
||
recs = await sor.R('pipeline_agent_questions', {'id': question_id})
|
||
if not recs:
|
||
return None
|
||
rec = recs[0]
|
||
try:
|
||
d = dict(rec)
|
||
except Exception:
|
||
d = {}
|
||
ctx = d.get('context')
|
||
if ctx and isinstance(ctx, str):
|
||
try:
|
||
d['context'] = json.loads(ctx)
|
||
except (json.JSONDecodeError, TypeError):
|
||
pass
|
||
return d
|
||
|
||
|
||
async def list_questions(tenant_id: str = None, task_id: str = None,
|
||
status: str = None, limit: int = 50) -> list:
|
||
"""客户视角:列出当前该「客户(人)」处理的问题。"""
|
||
rows = await list_problems_for("owner.superuser", tenant_id=tenant_id,
|
||
task_id=task_id, limit=limit)
|
||
if status:
|
||
rows = [r for r in rows if (r.get('status') or '') == status]
|
||
return rows
|
||
|
||
|
||
async def get_task_qna(task_id: str) -> list:
|
||
"""取任务的全部已回答问答(旧返回结构:list),供角色 agent 注入 prompt。"""
|
||
qa = await get_task_qa(task_id)
|
||
return qa.get('answered', []) if isinstance(qa, dict) else []
|