代码修改

This commit is contained in:
fengxiangdebaozi committed 2026-05-26 21:09:02 +08:00
1 parent 45eb46c3f3
commit 8e7fc54548
4 files changed
+108 -12

No files matched your search

@@ -368,8 +368,8 @@ export const HighFidelityReport: React.FC<HighFidelityReportProps> = ({
)}
</section>
{/* Section 3: Generated files per seat */}
<section className="bg-white border border-slate-200 rounded-2xl p-5 shadow-[0_2px_8px_rgba(0,0,0,0.02)]">
{/* Section 3: Generated files per seat —— 暂时隐藏(无真实产出文件链路) */}
<section className="hidden bg-white border border-slate-200 rounded-2xl p-5 shadow-[0_2px_8px_rgba(0,0,0,0.02)]">
<h4 className="text-xs font-black text-slate-900 uppercase tracking-widest mb-3 flex items-center gap-2">
<Download className="w-4 h-4 text-primary" />
生成的报告文件
@@ -3794,8 +3794,16 @@ export default function RoundtablePlanningPage() {
{/* ========================================================
RIGHT COLUMN (Current Intent Summary, Timeline/Logs)
第三步时所有 panel 都已隐藏 → 整个 right-sidebar 也不渲染,
让中间方案展示区自适应铺满空间。
======================================================== */}
<section id="right-sidebar" className="w-[300px] shrink-0 flex flex-col gap-4 overflow-y-auto pl-1">
<section
id="right-sidebar"
className={cn(
"w-[300px] shrink-0 flex flex-col gap-4 overflow-y-auto pl-1",
currentStep === 3 && "hidden",
)}
>
{currentStep === 3 ? (
<>
@@ -305,8 +305,10 @@ RECOMMENDER_AGENT_SOUL = """# 智能体推荐专员
- `reason` 必须**具体**,不要"很合适"、"很强"这类空话
## 输出风格
- 思路部分简短:用一句话概括你为什么挑这些(例如"按情报→设计→风险→执行的研讨节奏挑选了 4 位")
- 推荐理由要**具体到任务**,例如"负责检索同类合规改造案例,直接对应本次等保 2.0 的对标需求"
- 思路部分简短:用一句话概括你**针对本次任务**为什么挑这些智能体,**必须呼应任务目标本身**;
不要套用任何固定职能链(如"情报→设计→风险→执行"),那只在合规改造等少数任务里贴合,
在广告/产品/营销/科研等任务里完全不适用。
- 推荐理由要**具体到本次任务**,例如"负责设计落地页文案,直接对应本次品牌曝光目标"
- 适度用 emoji:📋 表示开始汇总
## 边界
@@ -317,7 +319,11 @@ RECOMMENDER_AGENT_SOUL = """# 智能体推荐专员
## 示例
**用户输入**(由后端拼装):
> 以下两个示例覆盖**不同任务类型**,目的是教你「思路要呼应任务本身」,**不要**把任何一个示例的具体职能链当模板。
### 示例 A:合规改造任务(职能型候选池)
**用户输入**:
```
任务目标:对生产系统进行安全改造,满足等保 2.0 合规要求
约束:预算 ≤ 500 万元;工期 ≤ 6 个月;必须满足等保 2.0
@@ -334,7 +340,7 @@ RECOMMENDER_AGENT_SOUL = """# 智能体推荐专员
```
**你的输出**:
按"情报→设计→风险→执行"的研讨节奏,挑选 5 位最贴合本次等保 2.0 改造任务的智能体,并加入成本测算覆盖预算硬约束。📋
本次任务核心是等保 2.0 合规改造,需要先看同类案例、再产出方案、最后审查合规风险并落地排期,因此挑选 5 位智能体覆盖这条链路并加入成本测算保障预算硬约束。📋
[RECOMMEND_READY]
```json
@@ -348,6 +354,40 @@ RECOMMENDER_AGENT_SOUL = """# 智能体推荐专员
]
}
```
### 示例 B:广告/营销任务(用户自建领域候选池)
**用户输入**:
```
任务目标:为某新能源汽车品牌设计一套季度品牌曝光广告投放方案
约束:预算 ≤ 200 万元;周期 1 个月内上线
关键假设:目标人群覆盖一二线城市;以社媒为主、信息流为辅
候选智能体池:
- ad-design (广告设计智能体):产出广告创意脚本、海报设计稿、文案 hook
- channel-planner (渠道规划师):按预算和受众设计跨平台投放组合
- audience-insight (受众洞察):画像、关键词、情绪点研究
- creative-copy (创意文案):产出落地页与社媒文案变体
- roundtable-risk-review (风险审查):合规红线检查与收敛建议
```
**你的输出**:
本次是品牌曝光广告投放,核心要先做受众洞察、再产出创意与文案、最后规划渠道投放组合,因此挑选 4 位直接相关的智能体;合规审查在广告场景里也保留一个位置避免文案踩雷。📋
[RECOMMEND_READY]
```json
{
"picks": [
{"agent_id": "audience-insight", "reason": "新能源汽车一二线人群画像与情绪点研究,决定后续创意切入角度"},
{"agent_id": "ad-design", "reason": "产出社媒主推海报与脚本,直接对应品牌曝光交付物"},
{"agent_id": "creative-copy", "reason": "为社媒/信息流产出多版本文案 hook,配合 A/B 测试"},
{"agent_id": "channel-planner", "reason": "200 万预算内规划社媒+信息流的组合与节奏"}
]
}
```
> **关键观察**:示例 A 选了 `roundtable-intelligence / solution-design / execution-plan` 等"职能型"智能体,示例 B **完全没用任何 `roundtable-*` 内置**,而是从用户自建的领域智能体里挑。
> 这是因为:**候选池里的智能体能不能匹配任务,只看 description 和任务的实际语义,不看 id 是不是 `roundtable-` 开头**。如果候选池里有"广告设计智能体"且任务是广告设计,**你必须挑它**,而不是套用示例 A 的职能链。
"""
@@ -112,7 +112,7 @@ def _build_prompt(intent: dict[str, Any], candidates: list[dict[str, Any]]) -> s
def _validate_picks(
raw_picks: Any,
candidate_ids: set[str],
candidates: list[dict[str, Any]],
) -> list[dict[str, str]]:
"""Filter the model's picks against the candidate pool and clamp to 2-6.
@@ -121,18 +121,50 @@ def _validate_picks(
model over-picked. Returns ``[]`` if validation leaves us with fewer than
``_MIN_PICKS`` valid picks — the caller treats that as "fall back to
defaults".
容错策略(2026-XX 修复 "用户自建 agent 被推荐丢失" 的问题):
模型有时把候选池里的 ``name`` 当成 ``agent_id`` 回填(尤其当 id 是 UUID
形式时, LLM 倾向用人类可读名),或者顺便也回了 ``name`` 字段。本函数:
1. 先按 agent_id 命中候选池(严格匹配,原行为);
2. 否则按 ``agent_id`` 的字面值反查 name → 找回真实 id;
3. 否则按 ``name`` 字段反查 name → 找回真实 id。
只要能定位到候选池里**唯一**一个 agent,就算 pick 成功。
"""
if not isinstance(raw_picks, list):
return []
id_set: set[str] = set()
name_to_id: dict[str, str] = {}
for c in candidates:
aid = str(c.get("agent_id") or "").strip()
if not aid:
continue
id_set.add(aid)
nm = str(c.get("name") or "").strip()
if nm:
# 大小写不敏感反查 —— 模型可能用空格 / 不同大小写 echo 中文名。
name_to_id.setdefault(nm.lower(), aid)
picked: list[dict[str, str]] = []
seen: set[str] = set()
for item in raw_picks:
if not isinstance(item, dict):
continue
aid = str(item.get("agent_id") or "").strip()
raw_id = str(item.get("agent_id") or "").strip()
raw_name = str(item.get("name") or "").strip()
reason = str(item.get("reason") or "").strip()
if not aid or aid not in candidate_ids or aid in seen:
aid = ""
if raw_id and raw_id in id_set:
aid = raw_id
elif raw_id and raw_id.lower() in name_to_id:
# 模型把"广告设计智能体"这种 name 当成 agent_id 回填了
aid = name_to_id[raw_id.lower()]
elif raw_name and raw_name.lower() in name_to_id:
# 模型 agent_id 字段乱了,但 name 字段对得上
aid = name_to_id[raw_name.lower()]
if not aid or aid in seen:
continue
picked.append({"agent_id": aid, "reason": reason})
seen.add(aid)
@@ -245,14 +277,30 @@ async def stream_recommend(request: Request, payload: dict = Body(...)) -> Strea
# 只扫描「最后一条非空 AIMessage」:推荐 agent 单轮输出,无多轮 READY 问题。
picks: list[dict[str, str]] = []
match = _RECOMMEND_READY_PATTERN.search(last_ai_content)
# 诊断:打印模型输出末尾(含 RECOMMEND_READY 块)。grep "recommend stream debug"
# 即可看到 LLM 实际推荐了什么、_validate_picks 解析前/后的差异。
logger.info(
"recommend stream debug | last_ai_tail=%r | match=%s | candidate_count=%d",
last_ai_content[-600:] if last_ai_content else "",
bool(match),
len(candidates),
)
if match:
try:
parsed = json.loads(match.group(1))
except json.JSONDecodeError as exc:
logger.warning("recommend picks JSON parse failed: %s", exc)
logger.warning("recommend picks JSON parse failed: %s | raw=%r", exc, match.group(1)[:300])
parsed = {}
if isinstance(parsed, dict):
picks = _validate_picks(parsed.get("picks"), candidate_ids)
raw_picks = parsed.get("picks")
# 传完整 candidates 列表(含 name) —— _validate_picks 容错按 name 反查 id,
# 修复 LLM 把"广告设计智能体"这种 name 当 agent_id 回填导致全丢的问题。
picks = _validate_picks(raw_picks, candidates)
logger.info(
"recommend stream debug | raw_picks=%r | validated_picks=%r",
raw_picks,
picks,
)
# asking ≠ 模型在追问用户,而是「网关未能产出合法 picks」,由前端 fallback。
status_value = "done" if picks else "asking"