feat: 岗位职责迁至业务链席位,并支持 WeKnora 嵌入页知识库切换
将岗位职责从智能体配置迁移到业务链席位 JSON,会商面板只读展示;WeKnora 嵌入 iframe 支持在授权可见范围内切换知识库并重新签发会话。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
e860357563
commit
e881c242fe
16 files changed
+434
-257
No files matched your search
@@ -84,7 +84,6 @@ interface AgentDraft {
|
||||
/** 会商席位「技能识别强化」开关。 */
|
||||
seatSkillDirective: boolean;
|
||||
knowledgeBaseIds: string[];
|
||||
jobResponsibilities: string;
|
||||
}
|
||||
|
||||
function draftFromAgent(agent: Agent): AgentDraft {
|
||||
@@ -99,7 +98,6 @@ function draftFromAgent(agent: Agent): AgentDraft {
|
||||
templateName: agent.template_name ?? "",
|
||||
seatSkillDirective: agent.seat_skill_directive ?? false,
|
||||
knowledgeBaseIds: agent.llmwiki_knowledge_base_ids ?? [],
|
||||
jobResponsibilities: agent.job_responsibilities ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -153,6 +151,7 @@ export function AgentDetailPanel({
|
||||
skillsIcon,
|
||||
skillsTitle,
|
||||
showCommonQuestions = false,
|
||||
jobResponsibilities,
|
||||
panelTitle,
|
||||
embedded,
|
||||
hideStatusBadges = false,
|
||||
@@ -173,6 +172,11 @@ export function AgentDetailPanel({
|
||||
skillsIcon?: ReactNode;
|
||||
skillsTitle?: string;
|
||||
showCommonQuestions?: boolean;
|
||||
/**
|
||||
* 业务链席位上配置的岗位职责。传入(含空字符串)时在常用问题上方展示;
|
||||
* 不传则隐藏该区块。此字段来自业务链,不随智能体本身编辑。
|
||||
*/
|
||||
jobResponsibilities?: string | null;
|
||||
/** Overrides the standard panel heading for an embedded consumer. */
|
||||
panelTitle?: string;
|
||||
/** Renders one fixed panel instead of the responsive collapsible rail. */
|
||||
@@ -341,8 +345,6 @@ export function AgentDetailPanel({
|
||||
request.template_name = draft.templateName.trim();
|
||||
if (draft.seatSkillDirective !== (agent.seat_skill_directive ?? false))
|
||||
request.seat_skill_directive = draft.seatSkillDirective;
|
||||
if (draft.jobResponsibilities.trim() !== (agent.job_responsibilities ?? ""))
|
||||
request.job_responsibilities = draft.jobResponsibilities.trim();
|
||||
if (
|
||||
JSON.stringify(draft.knowledgeBaseIds) !==
|
||||
JSON.stringify(agent.llmwiki_knowledge_base_ids ?? [])
|
||||
@@ -725,37 +727,21 @@ export function AgentDetailPanel({
|
||||
</section>
|
||||
|
||||
|
||||
{/* ── 岗位职责 ───────────────────────────────────────────────────── */}
|
||||
<Section
|
||||
icon={<BriefcaseBusinessIcon className="size-3.5" />}
|
||||
title="岗位职责"
|
||||
>
|
||||
{editing && draft ? (
|
||||
<Textarea
|
||||
value={draft.jobResponsibilities}
|
||||
onChange={(e) =>
|
||||
setDraft({ ...draft, jobResponsibilities: e.currentTarget.value })
|
||||
}
|
||||
placeholder="描述该智能体对应岗位的职责…"
|
||||
className="min-h-20 text-sm"
|
||||
/>
|
||||
) : agent.job_responsibilities?.trim() ? (
|
||||
<p className="text-muted-foreground text-sm leading-relaxed whitespace-pre-wrap">
|
||||
{desensitize(agent.job_responsibilities)}
|
||||
</p>
|
||||
) : canEdit ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={beginEdit}
|
||||
className="text-muted-foreground hover:text-foreground hover:border-border w-full rounded-lg border border-dashed px-3 py-4 text-center text-xs transition-colors"
|
||||
>
|
||||
<PlusIcon className="mr-1 inline size-3.5 align-[-2px]" />
|
||||
添加岗位职责
|
||||
</button>
|
||||
) : (
|
||||
<p className="text-muted-foreground/60 text-sm">暂无岗位职责</p>
|
||||
)}
|
||||
</Section>
|
||||
{/* ── 岗位职责(业务链席位配置,只读)────────────────────────────── */}
|
||||
{jobResponsibilities !== undefined && (
|
||||
<Section
|
||||
icon={<BriefcaseBusinessIcon className="size-3.5" />}
|
||||
title="岗位职责"
|
||||
>
|
||||
{jobResponsibilities?.trim() ? (
|
||||
<p className="text-muted-foreground text-sm leading-relaxed whitespace-pre-wrap">
|
||||
{desensitize(jobResponsibilities)}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-muted-foreground/60 text-sm">暂无岗位职责</p>
|
||||
)}
|
||||
</Section>
|
||||
)}
|
||||
|
||||
|
||||
{/* ── 常用问题 ───────────────────────────────────────────────────── */}
|
||||
|
||||
@@ -62,8 +62,6 @@ export interface Agent {
|
||||
* (名称 + SKILL.md 路径 + 必须先 read_file 的硬指令) 拼进本轮任务以补偿弱模型。
|
||||
* 与业务链条同名开关取 OR。null/false = 关 (默认)。 */
|
||||
seat_skill_directive?: boolean | null;
|
||||
/** 岗位职责。会商智能体配置面板展示在常用问题上方。空/缺省 = 未填写。 */
|
||||
job_responsibilities?: string | null;
|
||||
/** DeerFlow LLMWiki mapping ids this agent allows conversations to select. */
|
||||
llmwiki_knowledge_base_ids?: string[] | null;
|
||||
tags?: Tag[];
|
||||
@@ -101,8 +99,6 @@ export interface CreateAgentRequest {
|
||||
template_name?: string | null;
|
||||
/** 会商席位「技能识别强化」开关;缺省/false = 关。 */
|
||||
seat_skill_directive?: boolean | null;
|
||||
/** 岗位职责;缺省/空 = 未填写。 */
|
||||
job_responsibilities?: string | null;
|
||||
llmwiki_knowledge_base_ids?: string[] | null;
|
||||
}
|
||||
|
||||
@@ -121,7 +117,5 @@ export interface UpdateAgentRequest {
|
||||
template_name?: string | null;
|
||||
/** 会商席位「技能识别强化」开关;缺省/false = 关。 */
|
||||
seat_skill_directive?: boolean | null;
|
||||
/** 岗位职责;传空字符串表示清除。 */
|
||||
job_responsibilities?: string | null;
|
||||
llmwiki_knowledge_base_ids?: string[] | null;
|
||||
}
|
||||
@@ -417,6 +417,7 @@ function EightBfAgentWorkspace({
|
||||
showRecentChats={false}
|
||||
avatarSrc={agentConfigLogo}
|
||||
positionName={positionNameById(selectedSeat?.position_id)}
|
||||
jobResponsibilities={selectedSeat?.job_responsibilities ?? ""}
|
||||
skillsIcon={<img src={doubleArrowDecoration} alt="" aria-hidden="true" />}
|
||||
skillsTitle="技能列表"
|
||||
showCommonQuestions
|
||||
@@ -502,10 +503,12 @@ function EightBfAgentRow({
|
||||
}) {
|
||||
const name = agent?.name || seat.name || "未命名智能体";
|
||||
const description = agent?.description || seat.description || "智能体助手";
|
||||
const avatarText = Array.from(name.trim() || "?")[0]?.toUpperCase() || "?";
|
||||
const avatarText = firstAgentAvatarChar(name);
|
||||
return (
|
||||
<button type="button" className="eightbf-roundtable__agent-selector-card" onClick={onSelect}>
|
||||
<span className="eightbf-roundtable__agent-selector-avatar" aria-hidden="true">{avatarText}</span>
|
||||
<span className="eightbf-roundtable__agent-selector-avatar" aria-hidden="true">
|
||||
{avatarText ? avatarText : <img src={agentConfigLogo} alt="" />}
|
||||
</span>
|
||||
<span className="eightbf-roundtable__agent-selector-main">
|
||||
<strong>{name}</strong>
|
||||
<p>{description}</p>
|
||||
@@ -515,6 +518,13 @@ function EightBfAgentRow({
|
||||
);
|
||||
}
|
||||
|
||||
/** 名称首字若是【、( 这类标点,列表卡片改用智能体 logo,不把标点当头像。 */
|
||||
function firstAgentAvatarChar(name: string): string | null {
|
||||
const first = Array.from(name.trim())[0];
|
||||
if (!first || /^[\p{P}\p{S}]$/u.test(first)) return null;
|
||||
return first.toUpperCase();
|
||||
}
|
||||
|
||||
function intentSections(intentReady: object | null): Array<{ label: string; items: string[] }> {
|
||||
const source = intentReady && typeof intentReady === "object" ? intentReady as IntentRecord : null;
|
||||
if (!source) return [];
|
||||
|
||||
@@ -300,6 +300,11 @@
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.eightbf-roundtable .eightbf-roundtable__agent-selector-avatar img {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.eightbf-roundtable .eightbf-roundtable__agent-selector-main {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
|
||||
@@ -5706,6 +5706,10 @@ function RightWorkspace({
|
||||
}
|
||||
skillsTitle={specialAgentConfig ? "技能列表" : undefined}
|
||||
showCommonQuestions={specialAgentConfig}
|
||||
jobResponsibilities={
|
||||
chain?.seats.find((seat) => seat.agent_id === selectedAgentId)
|
||||
?.job_responsibilities ?? ""
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -55,6 +55,11 @@ export interface ChainSeat {
|
||||
* 使同一条业务链既能继续用于原会商,也能在岗位协同中按岗位筛选可问答的智能体。
|
||||
*/
|
||||
position_id?: string | null;
|
||||
/**
|
||||
* 该席位在业务链中配置的岗位职责。会商「智能体配置」面板只读展示;
|
||||
* 随 `seats` JSON 落库(无需 DB 列)。空 / 缺省 = 未填写。
|
||||
*/
|
||||
job_responsibilities?: string | null;
|
||||
}
|
||||
|
||||
/** 一条可复用的业务链条(与后端 `/api/roundtable-chains` 记录对应)。 */
|
||||
|
||||
@@ -476,6 +476,19 @@ export default function BusinessChainEditorPage() {
|
||||
);
|
||||
}, []);
|
||||
|
||||
const setSeatJobResponsibilitiesCb = useCallback((agentId: string, jobResponsibilities: string) => {
|
||||
setStageGroups((prev) =>
|
||||
prev.map((group) => ({
|
||||
...group,
|
||||
seats: group.seats.map((seat) =>
|
||||
seat.agent_id === agentId
|
||||
? { ...seat, job_responsibilities: jobResponsibilities }
|
||||
: seat,
|
||||
),
|
||||
})),
|
||||
);
|
||||
}, []);
|
||||
|
||||
// ── 提示词 AI 优化(总控提示词 / 各 Stage 阶段目标)────────────────────────
|
||||
const [optimizingCoordinator, setOptimizingCoordinator] = useState(false);
|
||||
const [goalOptimizingIdx, setGoalOptimizingIdx] = useState<number | null>(null);
|
||||
@@ -681,6 +694,8 @@ export default function BusinessChainEditorPage() {
|
||||
seat_mode: s.seat_mode ?? null,
|
||||
// 岗位协同专用岗位归属:普通圆桌忽略,但保存时必须原样保留。
|
||||
position_id: s.position_id ?? null,
|
||||
// 业务链席位岗位职责:会商智能体配置面板只读展示。
|
||||
job_responsibilities: s.job_responsibilities?.trim() || null,
|
||||
};
|
||||
});
|
||||
// 总控编排提示:trim 后空串落 null(清空),与后端「空=不注入」一致。
|
||||
@@ -1284,6 +1299,7 @@ export default function BusinessChainEditorPage() {
|
||||
positionConfigMode={positionConfigMode}
|
||||
positionOptions={positionOptions}
|
||||
onSeatPositionChange={setSeatPositionCb}
|
||||
onSeatJobResponsibilitiesChange={setSeatJobResponsibilitiesCb}
|
||||
onStageGoalChange={setGoal}
|
||||
goalOptimizingIdx={goalOptimizingIdx}
|
||||
onOptimizeGoal={handleOptimizeStageGoal}
|
||||
@@ -1468,6 +1484,7 @@ function StagesColumn({
|
||||
positionConfigMode,
|
||||
positionOptions,
|
||||
onSeatPositionChange,
|
||||
onSeatJobResponsibilitiesChange,
|
||||
onStageGoalChange,
|
||||
goalOptimizingIdx,
|
||||
onOptimizeGoal,
|
||||
@@ -1492,6 +1509,7 @@ function StagesColumn({
|
||||
/** Live directory from the administrator's position-role configuration. */
|
||||
positionOptions: readonly PositionOption[];
|
||||
onSeatPositionChange: (agentId: string, positionId: PositionId | null) => void;
|
||||
onSeatJobResponsibilitiesChange: (agentId: string, jobResponsibilities: string) => void;
|
||||
onStageGoalChange: (stageIdx: number, goal: string) => void;
|
||||
/** 正在 AI 优化阶段目标的 stage 下标(null = 没有进行中的优化)。 */
|
||||
goalOptimizingIdx: number | null;
|
||||
@@ -1601,6 +1619,7 @@ function StagesColumn({
|
||||
positionConfigMode={positionConfigMode}
|
||||
positionOptions={positionOptions}
|
||||
onSeatPositionChange={onSeatPositionChange}
|
||||
onSeatJobResponsibilitiesChange={onSeatJobResponsibilitiesChange}
|
||||
onGoalChange={(goal) => onStageGoalChange(stageIdx, goal)}
|
||||
goalOptimizing={goalOptimizingIdx === stageIdx}
|
||||
anyGoalOptimizing={goalOptimizingIdx !== null}
|
||||
@@ -1636,6 +1655,7 @@ function StageSection({
|
||||
positionConfigMode,
|
||||
positionOptions,
|
||||
onSeatPositionChange,
|
||||
onSeatJobResponsibilitiesChange,
|
||||
onGoalChange,
|
||||
goalOptimizing,
|
||||
anyGoalOptimizing,
|
||||
@@ -1660,6 +1680,7 @@ function StageSection({
|
||||
positionConfigMode: boolean;
|
||||
positionOptions: readonly PositionOption[];
|
||||
onSeatPositionChange: (agentId: string, positionId: PositionId | null) => void;
|
||||
onSeatJobResponsibilitiesChange: (agentId: string, jobResponsibilities: string) => void;
|
||||
onGoalChange: (goal: string) => void;
|
||||
/** 本 Stage 的阶段目标正在 AI 优化中。 */
|
||||
goalOptimizing: boolean;
|
||||
@@ -1827,6 +1848,9 @@ function StageSection({
|
||||
showPositionConfig={positionConfigMode}
|
||||
positionOptions={positionOptions}
|
||||
onPositionChange={(positionId) => onSeatPositionChange(seat.agent_id, positionId)}
|
||||
onJobResponsibilitiesChange={(value) =>
|
||||
onSeatJobResponsibilitiesChange(seat.agent_id, value)
|
||||
}
|
||||
onDetail={() => onSeatDetail(seat.agent_id, seat.name)}
|
||||
/>
|
||||
))}
|
||||
@@ -1856,6 +1880,7 @@ function SeatStrip({
|
||||
showPositionConfig,
|
||||
positionOptions,
|
||||
onPositionChange,
|
||||
onJobResponsibilitiesChange,
|
||||
onDetail,
|
||||
}: {
|
||||
seat: ChainSeat;
|
||||
@@ -1880,6 +1905,7 @@ function SeatStrip({
|
||||
/** Current position directory, injected by the business-chain editor. */
|
||||
positionOptions: readonly PositionOption[];
|
||||
onPositionChange: (positionId: PositionId | null) => void;
|
||||
onJobResponsibilitiesChange: (jobResponsibilities: string) => void;
|
||||
onDetail: () => void;
|
||||
}) {
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } =
|
||||
@@ -1955,6 +1981,17 @@ function SeatStrip({
|
||||
{seat.description}
|
||||
</div>
|
||||
)}
|
||||
<label className="mt-1.5 flex flex-col gap-0.5">
|
||||
<span className="text-[10px] font-medium text-muted-foreground">岗位职责</span>
|
||||
<textarea
|
||||
value={seat.job_responsibilities ?? ""}
|
||||
onChange={(event) => onJobResponsibilitiesChange(event.currentTarget.value)}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
placeholder="填写该席位在本业务链中的岗位职责"
|
||||
rows={2}
|
||||
className="w-full resize-none rounded border border-border bg-background px-2 py-1 text-[11px] leading-4 text-foreground outline-none placeholder:text-muted-foreground/60 focus:border-primary/50"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* 岗位协同从专用入口进来时才展示。使用 TDesign Select,未分配不会阻塞普通圆桌使用。 */}
|
||||
|
||||
Reference in new issue
Block a user