diff --git a/_skills_src/ai-skills b/_skills_src/ai-skills new file mode 160000 index 00000000..cde1c838 --- /dev/null +++ b/_skills_src/ai-skills @@ -0,0 +1 @@ +Subproject commit cde1c8385ead64df501c9b214e63d7ba9b283ff9 diff --git a/_skills_src/anthropics-skills b/_skills_src/anthropics-skills new file mode 160000 index 00000000..57546260 --- /dev/null +++ b/_skills_src/anthropics-skills @@ -0,0 +1 @@ +Subproject commit 57546260929473d4e0d1c1bb75297be2fdfa1949 diff --git a/_skills_src/awesome-claude-skills b/_skills_src/awesome-claude-skills new file mode 160000 index 00000000..92568c1e --- /dev/null +++ b/_skills_src/awesome-claude-skills @@ -0,0 +1 @@ +Subproject commit 92568c1edaff1bde5371154f036d959346c145a8 diff --git a/_skills_src/ce-kit b/_skills_src/ce-kit new file mode 160000 index 00000000..d4973c48 --- /dev/null +++ b/_skills_src/ce-kit @@ -0,0 +1 @@ +Subproject commit d4973c48270c66f4a4751131ec84b9dbb03669dc diff --git a/_skills_src/csm b/_skills_src/csm new file mode 160000 index 00000000..3fa16a94 --- /dev/null +++ b/_skills_src/csm @@ -0,0 +1 @@ +Subproject commit 3fa16a94e0aba3509e4d4b318ca175d3191b5b7d diff --git a/_skills_src/superpowers b/_skills_src/superpowers new file mode 160000 index 00000000..6fd45076 --- /dev/null +++ b/_skills_src/superpowers @@ -0,0 +1 @@ +Subproject commit 6fd4507659784c351abbd2bc264c7162cfd386dc diff --git a/_skills_src/tapestry-skills b/_skills_src/tapestry-skills new file mode 160000 index 00000000..80e1dc56 --- /dev/null +++ b/_skills_src/tapestry-skills @@ -0,0 +1 @@ +Subproject commit 80e1dc56df74d1cb849ad649c7ead9756e7929bb diff --git a/frontend-web/src/components/workspace/agents/agent-detail-panel.tsx b/frontend-web/src/components/workspace/agents/agent-detail-panel.tsx index 261c32ff..cedeb6c8 100644 --- a/frontend-web/src/components/workspace/agents/agent-detail-panel.tsx +++ b/frontend-web/src/components/workspace/agents/agent-detail-panel.tsx @@ -65,28 +65,6 @@ import { usePublishSquares } from "@/core/system-settings/hooks"; import { desensitize } from "@/lib/desensitize"; import { cn } from "@/lib/utils"; -/** - * Turn the admin-authored SOUL.md markdown into a clean, readable profile blurb - * for the narrow panel. The view mode is a profile card, not a document view — - * so we strip markdown syntax (headings, emphasis, inline code, list bullets, - * blockquotes) rather than rendering it, keeping the line structure intact. - */ -function cleanProfileText(raw: string): string { - return raw - .split("\n") - .map((line) => - line - .replace(/^\s{0,3}#{1,6}\s*/, "") - .replace(/^\s{0,3}>\s?/, "") - .replace(/^(\s*)[-*+]\s+/, "$1• ") - .replace(/(\*\*|__)(.*?)\1/g, "$2") - .replace(/(\*|_)(.*?)\1/g, "$2") - .replace(/`([^`]+)`/g, "$1"), - ) - .join("\n") - .replace(/\n{3,}/g, "\n\n") - .trim(); -} /** Local edit-mode draft — committed in one `updateAgent` call on 保存. */ interface AgentDraft { @@ -232,9 +210,6 @@ export function AgentDetailPanel({ () => normalizeAgentQuestions(agent?.common_questions), [agent?.common_questions], ); - const soulPreview = agent?.soul?.trim() - ? cleanProfileText(desensitize(agent.soul)) - : undefined; function beginEdit() { if (!agent || !canEdit) return; @@ -609,31 +584,6 @@ export function AgentDetailPanel({ )} - {/* ── SOUL / 人设 ────────────────────────────────────────────────── */} - {(editing || soulPreview) && ( - } - title="人设(SOUL)" - > - {editing && draft ? ( - - setDraft({ ...draft, soul: e.currentTarget.value }) - } - placeholder="输入 SOUL.md 内容" - className="max-h-[45vh] min-h-48 resize-y font-mono text-xs" - /> - ) : ( - // Viewport-relative cap: the SOUL preview expands with the screen - // (taller than the old fixed 240px box) but never swallows the - // whole panel — long content still scrolls inside. - - {soulPreview} - - )} - - )} {/* ── 常用问题 ───────────────────────────────────────────────────── */} {(editing || questions.length > 0 || canEdit) && ( diff --git a/frontend-web/src/components/workspace/artifacts/artifact-file-detail.tsx b/frontend-web/src/components/workspace/artifacts/artifact-file-detail.tsx index d3df711e..d582cbb2 100644 --- a/frontend-web/src/components/workspace/artifacts/artifact-file-detail.tsx +++ b/frontend-web/src/components/workspace/artifacts/artifact-file-detail.tsx @@ -4,16 +4,15 @@ import { ArrowDownIcon, Code2Icon, CopyIcon, - DownloadIcon, - EyeIcon, - FileType2Icon, - LoaderIcon, - PackageIcon, - SaveIcon, - SquareArrowOutUpRightIcon, - UploadCloudIcon, - XIcon, -} from "lucide-react"; + DownloadIcon, + EyeIcon, + FileType2Icon, + LoaderIcon, + PackageIcon, + SaveIcon, + UploadCloudIcon, + XIcon, +} from "lucide-react"; import { useParams } from "next/navigation"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import type { AnchorHTMLAttributes, HTMLAttributes, ReactNode } from "react"; @@ -38,12 +37,11 @@ import { import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { CodeEditor } from "@/components/workspace/code-editor"; import { publishArtifactToKnowledge } from "@/core/agents/api"; -import { useAgent } from "@/core/agents/hooks"; -import { apiFetch } from "@/core/api"; -import { - downloadArtifactFile, - openArtifactInNewTab, -} from "@/core/artifacts/download"; +import { useAgent } from "@/core/agents/hooks"; +import { apiFetch } from "@/core/api"; +import { + downloadArtifactFile, +} from "@/core/artifacts/download"; import { useArtifactContent, useUpdateArtifactContent, @@ -514,32 +512,7 @@ export function ArtifactFileDetail({ } }, [displayContent, filepath]); - const handleOpenWriteFileInNewTab = useCallback(() => { - const source = displayContent ?? ""; - if (!source.trim()) { - toast.error("文件内容为空,暂时无法打开"); - return; - } - if (language === "mermaid") { - void openMermaidSvgInNewTab(source).catch((error) => { - console.error("Failed to open mermaid artifact:", error); - toast.error(error instanceof Error ? error.message : "Failed to open artifact"); - }); - return; - } - const blobType = - language === "html" - ? "text/html;charset=utf-8" - : language === "markdown" - ? "text/markdown;charset=utf-8" - : "text/plain;charset=utf-8"; - const blob = new Blob([source], { type: blobType }); - const url = URL.createObjectURL(blob); - window.open(url, "_blank", "noopener,noreferrer"); - window.setTimeout(() => URL.revokeObjectURL(url), 60_000); - }, [displayContent, language]); - - const handleDownloadWriteFile = useCallback(() => { + const handleDownloadWriteFile = useCallback(() => { const source = displayContent ?? ""; if (!source.trim()) { toast.error("文件内容为空,暂时无法下载"); @@ -571,7 +544,7 @@ export function ArtifactFileDetail({ return ( - + @@ -601,7 +574,7 @@ export function ArtifactFileDetail({ - + {isSupportPreview && ( )} - - + + {canPublishToKnowledge && ( )} - { - if (useWriteFileStreaming) { - handleOpenWriteFileInNewTab(); - return; - } - if (language === "mermaid") { - try { - await openMermaidSvgInNewTab(displayContent ?? ""); - } catch (error) { - console.error(error); - toast.error( - error instanceof Error - ? error.message - : "Failed to open artifact", - ); - } - return; - } - try { - await openArtifactInNewTab({ - filepath, - threadId, - isMock, - publicShareCode, - }); - } catch (error) { - console.error(error); - toast.error( - error instanceof Error - ? error.message - : "Failed to open artifact", - ); - } - }} - /> {isCodeFile && ( - - setEnabled(!enabled)} - className={className} - > - - 知识库召回 - - {enabled ? "已开启" : "已关闭"} - - - - - ); +export function KnowledgeRagToggle({ + className: _className, +}: { + className?: string; +}) { + void _className; + return null; } diff --git a/frontend-web/src/core/page-layout/sidebar-menu.ts b/frontend-web/src/core/page-layout/sidebar-menu.ts index e8b99d6e..fe616f49 100644 --- a/frontend-web/src/core/page-layout/sidebar-menu.ts +++ b/frontend-web/src/core/page-layout/sidebar-menu.ts @@ -24,7 +24,6 @@ import { History, Layers, Layout, - Library, List, ListChecks, ListTodo, @@ -287,12 +286,6 @@ export const sidebarMenuItems: MenuItem[] = [ icon: BookOpen, path: "/page/workspace/knowledge", }, - { - id: "resource-knowledge-base", - label: "知识库资源管理", - icon: Library, - path: "/page/strategy/resource-management/resource-knowledge-base", - }, { id: "resource-evaluation-index", label: "作战资源管理", diff --git a/frontend-web/src/roundtable-planning/components/AgentDetailSheet.tsx b/frontend-web/src/roundtable-planning/components/AgentDetailSheet.tsx index 45165f8f..9b1e54d7 100644 --- a/frontend-web/src/roundtable-planning/components/AgentDetailSheet.tsx +++ b/frontend-web/src/roundtable-planning/components/AgentDetailSheet.tsx @@ -131,17 +131,6 @@ export function AgentDetailSheet({ )} - {/* 人设(SOUL.md) */} - {agent.soul?.trim() && ( - - - 人设 - - - {agent.soul} - - - )} > )} diff --git a/offline-backend-20260512/backend/app/gateway/routers/knowledge_ingest.py b/offline-backend-20260512/backend/app/gateway/routers/knowledge_ingest.py index 66b9f7d1..1893dfdc 100644 --- a/offline-backend-20260512/backend/app/gateway/routers/knowledge_ingest.py +++ b/offline-backend-20260512/backend/app/gateway/routers/knowledge_ingest.py @@ -13,6 +13,7 @@ from __future__ import annotations import json import logging import os +import hashlib from datetime import datetime from typing import Any @@ -20,16 +21,13 @@ import httpx from fastapi import APIRouter, HTTPException from pydantic import BaseModel, Field +from deerflow.config import get_app_config from deerflow.config.agents_config import load_agent_config logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/knowledge-ingest", tags=["knowledge-ingest"]) # 入库接口地址,可用环境变量覆盖(如切换 UAT/生产)。 -KNOWES_INGEST_URL = os.environ.get( - "KNOWES_INGEST_URL", - "https://ch1.b.uat.4.cn/knowes/api/knowledge-outer/add", -) _REQUEST_TIMEOUT_SECONDS = 30.0 # 标题字段在目标模板里可能叫「名称」或「标题」,按此顺序逐个尝试。 @@ -101,6 +99,21 @@ def _build_payload(template_name: str, batch_number: str, node_name: str, title_ } +def _build_node_name(title: str, timestamp: str) -> str: + """Build a stable node name as md5(title) + timestamp + .md.""" + title_md5 = hashlib.md5(title.encode("utf-8")).hexdigest() + return f"{title_md5}-{timestamp}.md" + + +def _resolve_ingest_url() -> str: + """Resolve the external ingest URL from config.yaml, with env fallback.""" + config_url = getattr(get_app_config().knowledge_ingest, "url", "").strip() + return os.environ.get( + "KNOWES_INGEST_URL", + config_url or "https://ch1.b.uat.4.cn/knowes/api/knowledge-outer/add", + ) + + @router.post("/publish", response_model=KnowledgeIngestPublishResponse, summary="Publish markdown artifact to external knowledge base") async def publish_to_knowledge_base(request: KnowledgeIngestPublishRequest) -> KnowledgeIngestPublishResponse: title = request.title.strip() @@ -121,8 +134,10 @@ async def publish_to_knowledge_base(request: KnowledgeIngestPublishRequest) -> K now = datetime.now() # nodeName 是入库记录的唯一标识:md 标题 + 时间戳。两次尝试必须共用同一个 # nodeName,避免第一次实际已入库时第二次产生重复记录。 - node_name = f"{title}-{now.strftime('%Y%m%d%H%M%S')}" + timestamp = now.strftime("%Y%m%d%H%M%S") + node_name = _build_node_name(title, timestamp) batch_number = f"md{now.strftime('%Y%m%d')}" + ingest_url = _resolve_ingest_url() logger.info( "[knowledge-ingest] 开始入库: agent=%s template=%s nodeName=%s batchNumber=%s title=%r content_len=%d url=%s", @@ -132,7 +147,7 @@ async def publish_to_knowledge_base(request: KnowledgeIngestPublishRequest) -> K batch_number, title, len(content), - KNOWES_INGEST_URL, + ingest_url, ) attempts: list[KnowledgeIngestAttempt] = [] @@ -148,7 +163,7 @@ async def publish_to_knowledge_base(request: KnowledgeIngestPublishRequest) -> K json.dumps({**payload, "list": [{**payload["list"][0], "data": [{"fieldName": d["fieldName"], "value": d["value"][:200]} for d in payload["list"][0]["data"]]}]}, ensure_ascii=False), ) try: - response = await client.post(KNOWES_INGEST_URL, json=payload, headers=headers) + response = await client.post(ingest_url, json=payload, headers=headers) except httpx.HTTPError as exc: logger.error("[knowledge-ingest] 第 %d 次发送请求异常 (标题字段=%s): %s", len(attempts) + 1, title_field, exc) attempts.append(KnowledgeIngestAttempt(title_field=title_field, error=str(exc))) diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/config/app_config.py b/offline-backend-20260512/backend/packages/harness/deerflow/config/app_config.py index 543e546d..205b88e4 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/config/app_config.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/config/app_config.py @@ -15,9 +15,10 @@ from deerflow.config.checkpointer_config import CheckpointerConfig, load_checkpo from deerflow.config.database_config import DatabaseConfig from deerflow.config.extensions_config import ExtensionsConfig from deerflow.config.gateway_config import GatewayConfig -from deerflow.config.guardrails_config import GuardrailsConfig, load_guardrails_config_from_dict -from deerflow.config.knowledge_config import KnowledgeConfig -from deerflow.config.log_system_config import LogSystemConfig +from deerflow.config.guardrails_config import GuardrailsConfig, load_guardrails_config_from_dict +from deerflow.config.knowledge_config import KnowledgeConfig +from deerflow.config.knowledge_ingest_config import KnowledgeIngestConfig +from deerflow.config.log_system_config import LogSystemConfig from deerflow.config.login_config import AuthLoginConfig from deerflow.config.memory_config import MemoryConfig, load_memory_config_from_dict from deerflow.config.model_config import ModelConfig @@ -135,8 +136,9 @@ class AppConfig(BaseModel): run_events: RunEventsConfig = Field(default_factory=RunEventsConfig, description="Run event storage configuration") checkpointer: CheckpointerConfig | None = Field(default=None, description="Checkpointer configuration") stream_bridge: StreamBridgeConfig | None = Field(default=None, description="Stream bridge configuration") - browser_context: BrowserContextConfig = Field(default_factory=BrowserContextConfig, description="Optional browser page capture integration") - knowledge: KnowledgeConfig = Field(default_factory=KnowledgeConfig, description="Product knowledge base (obsidian-wiki compatible vault) configuration") + browser_context: BrowserContextConfig = Field(default_factory=BrowserContextConfig, description="Optional browser page capture integration") + knowledge: KnowledgeConfig = Field(default_factory=KnowledgeConfig, description="Product knowledge base (obsidian-wiki compatible vault) configuration") + knowledge_ingest: KnowledgeIngestConfig = Field(default_factory=KnowledgeIngestConfig, description="External knowledge-ingest endpoint configuration") @classmethod def resolve_config_path(cls, config_path: str | None = None) -> Path: diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/config/knowledge_ingest_config.py b/offline-backend-20260512/backend/packages/harness/deerflow/config/knowledge_ingest_config.py new file mode 100644 index 00000000..b619e66b --- /dev/null +++ b/offline-backend-20260512/backend/packages/harness/deerflow/config/knowledge_ingest_config.py @@ -0,0 +1,12 @@ +"""Configuration for the external knowledge-ingest endpoint.""" + +from pydantic import BaseModel, Field + + +class KnowledgeIngestConfig(BaseModel): + """External knowledge-ingest configuration (`config.yaml` -> `knowledge_ingest`).""" + + url: str = Field( + default="https://ch1.b.uat.4.cn/knowes/api/knowledge-outer/add", + description="External knowledge-ingest endpoint URL.", + ) diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/agents/model.py b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/agents/model.py index 5a71ce10..ebb46d2f 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/agents/model.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/agents/model.py @@ -32,7 +32,7 @@ class AgentRow(Base): # ``is_private``. Empty string = "not assigned" → the listing layer treats # it as the admin-configured default square. Each agent belongs to at most # one square (single column), so it can never be published to two squares. - square_id: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True) + square_id: Mapped[str] = mapped_column(String(64), nullable=False, default="", server_default="", index=True) # NULL = not featured on the homepage selector. Non-NULL = admin-curated # featured rank (lower sorts first). Only ``published=True`` agents are # eligible; the router enforces that invariant on writes. BIGINT so the diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/skills/model.py b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/skills/model.py index 48eb2095..d631f112 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/persistence/skills/model.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/persistence/skills/model.py @@ -36,7 +36,7 @@ class SkillRow(Base): # Publish square (发布广场) this skill is filed under once published. Pure # organizational category, no access control. Empty string = unassigned → # the listing layer resolves it to the default square. One square per skill. - square_id: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True) + square_id: Mapped[str] = mapped_column(String(64), nullable=False, default="", server_default="", index=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=lambda: datetime.now(UTC)) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC)) diff --git a/offline-backend-20260512/config.yaml b/offline-backend-20260512/config.yaml index 422d36ed..b753224f 100644 --- a/offline-backend-20260512/config.yaml +++ b/offline-backend-20260512/config.yaml @@ -108,6 +108,8 @@ vision: model_name: deepseek-ai/DeepSeek-OCR # prompt: 请详细描述这张图片的内容。 # max_tokens: 2048 +knowledge_ingest: + url: "https://ch1.b.uat.4.cn/knowes/api/knowledge-outer/add" # 上传文件识别配置。 # - auto_convert_documents: true 时,上传的 PDF / Word / Excel / PPT 会在服务端自动转换为 # Markdown(.md),agent 通过 read_file / grep 读取其内容 —— 即“识别”文档内容。 diff --git a/web-code/components/Sidebar.tsx b/web-code/components/Sidebar.tsx index 528d6c7e..0cadbebe 100644 --- a/web-code/components/Sidebar.tsx +++ b/web-code/components/Sidebar.tsx @@ -255,7 +255,6 @@ const menuItems: MenuItem[] = [ label: "网络行为体资源管理", icon: Network, }, - { id: "resource-knowledge-base", label: "知识库资源管理", icon: Library }, { id: "resource-evaluation-index", label: "作战资源管理", icon: Library }, // { // id: "resource-evaluation-index",
- {soulPreview} -
- {agent.soul} -