feat: 新增 agentfx 任务深链与研判报告入库流程
支持 goPath=agentfx 的 iframe 嵌入问答、内置研判助手种子与报告导入协助卡,并完善深度研究流式报告能力。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
6e9e89a6cb
commit
52b9cbf707
34 files changed
+4488
-392
No files matched your search
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* 「agentfx」任务深链:复制自 [[task-deeplink]] 的 3qfx 单智能体模式,供新系统 iframe 嵌入
|
||||
* (``${loginBase}?password=xxx&taskId=xxx&goPath=agentfx&embed=1&hideJump=1&theme=dark-blue``)。
|
||||
*
|
||||
* 与 3qfx 的差异(也是单独复制一份的原因,后续逻辑改动互不影响):
|
||||
* 1. 任务详情不走外部 consumer(``VITE_AIPI_CONSUMER_URL``),改调本系统 Gateway 的
|
||||
* ``GET {backend}/taskAnalyseSearch/cop-task-detail?taskId=``(带 DeerFlow 登录态,任务数据
|
||||
* 来自 TaskCOP 任务表,即任务列表 / 报告导入用的同一份)。
|
||||
* 2. **固定使用内置智能体** ``agentfx-analyst``(后端 seeder ``_agentfx_seed.py`` 启动时补建:
|
||||
* 搜集信息 → 生成 report.md + task-reports.json(14 类)→ 总结 → 前端「入库」协助卡 /
|
||||
* task-report-import 技能入库),不走「配置管理」业务映射。
|
||||
* 3. 路由挂在 ``plan-task-agentfx`` 前缀下,页面代码见 [[AgentFxWorkspace]]。
|
||||
*
|
||||
* 任务详情整包仍暂存到与 3qfx 相同的 sessionStorage 键(``login.copTaskDetail``,见
|
||||
* [[persistCopTaskDetail]]):该键按标签页隔离(嵌入态无跨标签污染),工作区「当前任务」
|
||||
* 卡片与 AgentChatPage 的 taskBind 自恢复逻辑都读它,保持行为一致。
|
||||
*/
|
||||
|
||||
import { apiFetch } from "@/core/api/fetch-client";
|
||||
import { getBackendBaseURL } from "@/core/config";
|
||||
import { clearTaskContext, persistTaskContext } from "@/core/auth/task-purpose";
|
||||
import {
|
||||
type CopTaskDetail,
|
||||
type TaskDeeplinkTarget,
|
||||
} from "@/core/auth/task-deeplink";
|
||||
|
||||
/** 深链 goPath 基础码(大小写不敏感匹配;不支持 ``-A`` 会商后缀)。 */
|
||||
export const AGENTFX_GOPATH = "agentfx";
|
||||
|
||||
/**
|
||||
* 固定使用的内置智能体 id(后端 ``_agentfx_seed.py`` 启动时补建并 upsert 进 agents 表,
|
||||
* ``user_id IS NULL`` 对所有用户可见)。前端深链不查业务映射,直接进该智能体的问答。
|
||||
*/
|
||||
export const AGENTFX_BUILTIN_AGENT_ID = "agentfx-analyst";
|
||||
|
||||
/** 工作区路由前缀(与 StrategyRoutes 的 plan-task-agentfx 子树一致)。 */
|
||||
export const AGENTFX_TASK_BASE = "/page/strategy/task-management/plan-task-agentfx";
|
||||
|
||||
/** 判断 goPath 是否为 agentfx(大小写不敏感)。 */
|
||||
export function isAgentfxGoPath(goPath: string): boolean {
|
||||
return goPath.trim().toLowerCase() === AGENTFX_GOPATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取任务详情:本系统 Gateway 的 cop-task-detail(返回与旧 consumer 同构的
|
||||
* ``{state, msg, data}`` 信封)。任务不存在 / 网络失败时返回 null,调用方(LoginPage)
|
||||
* 用仅含任务 id 的 stub 兜底继续跳转。
|
||||
*/
|
||||
export async function fetchAgentfxTaskDetail(taskId: string): Promise<CopTaskDetail | null> {
|
||||
if (!taskId) return null;
|
||||
try {
|
||||
// apiFetch 已统一注入 DeerFlow 登录态(Authorization / cookie),登录刚完成即可用。
|
||||
const query = new URLSearchParams({ taskId });
|
||||
const res = await apiFetch(
|
||||
`${getBackendBaseURL()}/taskAnalyseSearch/cop-task-detail?${query.toString()}`,
|
||||
{ method: "GET" },
|
||||
);
|
||||
if (!res.ok) {
|
||||
console.warn("[agentfx-deeplink] cop-task-detail failed", { taskId, status: res.status });
|
||||
return null;
|
||||
}
|
||||
const body = (await res.json().catch(() => null)) as { data?: unknown } | null;
|
||||
const data = body && typeof body === "object" ? body.data : null;
|
||||
if (!data || typeof data !== "object") return null;
|
||||
const d = data as Record<string, unknown>;
|
||||
return {
|
||||
id: (d.id as string | number | null) ?? null,
|
||||
overview: typeof d.overview === "string" ? d.overview : "",
|
||||
taskDirection: typeof d.taskDirection === "string" ? d.taskDirection : "",
|
||||
taskName: typeof d.taskName === "string" ? d.taskName : "",
|
||||
taskContent: typeof d.taskContent === "string" ? d.taskContent : "",
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn("[agentfx-deeplink] cop-task-detail failed", { taskId, error });
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用 taskName / taskContent 拼开场信息文本;末尾附任务 id(task_id)供后端识别任务。
|
||||
* 复制自 task-deeplink 的 buildOpeningText 基础三行(无 xdfx 行动追加),便于后续按新系统
|
||||
* 要求单独调整格式。
|
||||
*/
|
||||
export function buildAgentfxOpeningText(detail: CopTaskDetail, taskId: string): string {
|
||||
return [
|
||||
detail.taskName ? `任务名称:${detail.taskName}` : "",
|
||||
detail.taskContent ? `任务内容:${detail.taskContent}` : "",
|
||||
taskId ? `任务id(task_id)为${taskId}` : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 agentfx 跳转目标:内置「任务研判报告智能体」的问答工作区(任务↔对话 1:N,按
|
||||
* taskId + agentId 隔离)。清残留任务上下文 → 暂存任务上下文(taskId + agentId + 开场
|
||||
* 信息)→ 返回 chats 索引路径,由工作区决定开最新历史对话 / 新建(首次无历史自动发
|
||||
* 开场信息,见 AgentChatPage 的 taskBind)。
|
||||
*/
|
||||
export async function resolveAgentfxDeeplinkTarget(
|
||||
detail: CopTaskDetail,
|
||||
taskId: string,
|
||||
): Promise<TaskDeeplinkTarget | null> {
|
||||
// 清掉上一次深链可能残留的任务上下文(下面用本次任务重新写入)。
|
||||
clearTaskContext();
|
||||
|
||||
const agentId = AGENTFX_BUILTIN_AGENT_ID;
|
||||
persistTaskContext({
|
||||
taskId,
|
||||
agentId,
|
||||
openingText: buildAgentfxOpeningText(detail, taskId),
|
||||
actionId: taskId,
|
||||
partitionByAction: false,
|
||||
});
|
||||
return { path: `${AGENTFX_TASK_BASE}/agent/${agentId}/chats` };
|
||||
}
|
||||
Reference in new issue
Block a user