Add interactive project doc page
This commit is contained in:
@@ -0,0 +1,654 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
ArrowRight,
|
||||||
|
Check,
|
||||||
|
CheckCircle2,
|
||||||
|
CircleDashed,
|
||||||
|
FolderTree,
|
||||||
|
Network,
|
||||||
|
Route,
|
||||||
|
Sparkles,
|
||||||
|
X,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
|
|
||||||
|
type RoleId = "user" | "builder" | "admin";
|
||||||
|
type ProductId = "chatgpt" | "cursor" | "claude" | "zk";
|
||||||
|
type MemoryId = "chatgpt" | "letta" | "mem0" | "rag" | "ours";
|
||||||
|
type SkillPartId = "skillmd" | "knowledge" | "scripts" | "examples" | "readme" | "update";
|
||||||
|
|
||||||
|
const roles: Record<
|
||||||
|
RoleId,
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
question: string;
|
||||||
|
answer: string;
|
||||||
|
workflow: string[];
|
||||||
|
proof: string[];
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
user: {
|
||||||
|
name: "普通使用者",
|
||||||
|
title: "我想让它替我完成一段工作",
|
||||||
|
question: "我为什么不用 ChatGPT 网页版问一问?",
|
||||||
|
answer:
|
||||||
|
"因为这里不是只回答一句话,而是把输入、计划、人工 review、脚本执行、文件产物和后续导出放进同一个任务空间。",
|
||||||
|
workflow: ["选择 Skill", "描述目标", "review 计划", "等待执行", "拿 output 文件"],
|
||||||
|
proof: ["会话文件面板", "右侧活动链路", "output/ 产物", "飞书在线文档转换"],
|
||||||
|
},
|
||||||
|
builder: {
|
||||||
|
name: "Skill 作者",
|
||||||
|
title: "我想把自己的经验变成团队能力",
|
||||||
|
question: "为什么不是每个人自己写脚本、自己维护 prompt?",
|
||||||
|
answer:
|
||||||
|
"Skill 把流程、知识、脚本、样例和维护说明放到一个目录。别人不用理解你的全部细节,也能在 Web 上调用这份能力。",
|
||||||
|
workflow: ["建 skill 目录", "写 SKILL.md", "沉淀 knowledge", "放 scripts", "提交 Git", "页面更新 Skill"],
|
||||||
|
proof: ["skills/product-data", "skills/online-mining-v2", "skills/label-master", "Skill 更新按钮"],
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
name: "平台维护者",
|
||||||
|
title: "我想让多人稳定使用,而不是每个人本地各跑各的",
|
||||||
|
question: "为什么不用 Claude Code 本地跑?",
|
||||||
|
answer:
|
||||||
|
"Claude Code 很适合个人本地工程闭环;这里补的是团队 Web 入口、多用户会话、账号记忆、管理后台、共享 Skill 和统一部署。",
|
||||||
|
workflow: ["管理用户", "看会话与用量", "处理记忆队列", "更新服务", "维护公共 Skill"],
|
||||||
|
proof: ["/admin", "memory.db", "systemd 服务", "dev / online 环境", "会话隔离"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const products: Record<
|
||||||
|
ProductId,
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
position: string;
|
||||||
|
bestFor: string;
|
||||||
|
limits: string;
|
||||||
|
tech: string[];
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
chatgpt: {
|
||||||
|
name: "ChatGPT 网页版",
|
||||||
|
position: "最好的通用对话入口",
|
||||||
|
bestFor: "问答、写作、临时分析、个人信息辅助。",
|
||||||
|
limits: "不天然绑定团队数据源、产物目录、Skill 更新和多用户运维。",
|
||||||
|
tech: ["Saved Memory", "Reference Chat History", "Files", "Connectors"],
|
||||||
|
},
|
||||||
|
cursor: {
|
||||||
|
name: "Cursor",
|
||||||
|
position: "IDE 内的代码协作体验",
|
||||||
|
bestFor: "围绕代码仓库改文件、补全、解释、局部重构。",
|
||||||
|
limits: "偏个人 IDE 场景,业务流程共享、产物管理和多人 Web 入口不是核心。",
|
||||||
|
tech: ["Codebase Index", "Composer", "IDE Context", "Rules"],
|
||||||
|
},
|
||||||
|
claude: {
|
||||||
|
name: "Claude Code",
|
||||||
|
position: "本地工程 Agent 的强基准",
|
||||||
|
bestFor: "读代码、改文件、跑测试、长任务、多工具工程闭环。",
|
||||||
|
limits: "团队业务能力共建、账号级 Skill 记忆、Web 管理和数据开发产物链需要另搭平台。",
|
||||||
|
tech: ["Agent Loop", "Tools", "Skills", "MCP", "Terminal Workspace"],
|
||||||
|
},
|
||||||
|
zk: {
|
||||||
|
name: "ZK Data Agent",
|
||||||
|
position: "团队业务流程 Agent 工作台",
|
||||||
|
bestFor: "把数据开发、标签判断、线上挖掘、格式导出沉淀成共享 Skill。",
|
||||||
|
limits: "不追求替代所有 IDE/终端 Agent,优先服务团队业务流程沉淀。",
|
||||||
|
tech: ["Skill Registry", "Review Gate", "Session Workspace", "Skill Memory", "Admin"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const comparisonRows = [
|
||||||
|
{ label: "Web 多用户入口", chatgpt: "partial", cursor: "no", claude: "no", zk: "yes" },
|
||||||
|
{ label: "团队共享 Skill", chatgpt: "partial", cursor: "partial", claude: "partial", zk: "yes" },
|
||||||
|
{ label: "业务数据源接入", chatgpt: "partial", cursor: "no", claude: "partial", zk: "yes" },
|
||||||
|
{ label: "会话产物管理", chatgpt: "partial", cursor: "no", claude: "partial", zk: "yes" },
|
||||||
|
{ label: "人类 Review Gate", chatgpt: "partial", cursor: "partial", claude: "partial", zk: "yes" },
|
||||||
|
{ label: "Skill 级记忆", chatgpt: "no", cursor: "no", claude: "partial", zk: "yes" },
|
||||||
|
{ label: "管理后台/用量/队列", chatgpt: "no", cursor: "no", claude: "no", zk: "yes" },
|
||||||
|
{ label: "数据开发链路沉淀", chatgpt: "partial", cursor: "no", claude: "partial", zk: "yes" },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const objectives = [
|
||||||
|
{
|
||||||
|
goal: "先回答为什么用我们",
|
||||||
|
frontend: "用产品对比矩阵 + 选中产品详情,明确展示我们补的是团队流程平台能力。",
|
||||||
|
message: "不是模型比 Claude Code 强,而是把 Web 多用户、共享 Skill、产物管理、记忆和管理后台补齐。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: "让不同同事知道怎么融入工作",
|
||||||
|
frontend: "用角色切换:普通使用者、Skill 作者、平台维护者分别看到自己的路径。",
|
||||||
|
message: "看完不是只知道概念,而是知道自己明天怎么开始用。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: "证明不是闭门造车",
|
||||||
|
frontend: "用记忆方案谱系,把 ChatGPT、Letta、Mem0、RAG 和我们的取舍放在同一屏。",
|
||||||
|
message: "我们参考了主流方案,但按团队 Skill 场景做了轻量可控实现。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: "解释 Skill 为什么能沉淀能力",
|
||||||
|
frontend: "用可点击文件树解剖 SKILL.md、knowledge、scripts、examples、README 和更新机制。",
|
||||||
|
message: "Skill 不是 prompt,而是可维护、可安装、可更新的能力包。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: "让技术同事能追代码",
|
||||||
|
frontend: "每条链路都给出代码路径、落盘位置和真实运行节点。",
|
||||||
|
message: "页面不是海报,是技术讲解和后续维护入口。",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const memoryPatterns: Record<
|
||||||
|
MemoryId,
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
source: string;
|
||||||
|
borrowed: string;
|
||||||
|
notEnough: string;
|
||||||
|
ourChoice: string;
|
||||||
|
docs: string;
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
chatgpt: {
|
||||||
|
name: "ChatGPT Saved Memory / Reference Chat History",
|
||||||
|
source: "OpenAI 的记忆分成显式 Saved Memories 和从历史中提炼的 Reference Chat History。",
|
||||||
|
borrowed: "借鉴:用户可管理、可删除、可开关;显式记忆和历史参考分层。",
|
||||||
|
notEnough: "不足:它面向通用个性化,不知道某个团队 Skill 的操作经验应该只在该 Skill 生效。",
|
||||||
|
ourChoice: "我们的取舍:用户记忆全局注入,Skill 使用记忆只在启用对应 Skill 时注入。",
|
||||||
|
docs: "OpenAI Memory FAQ",
|
||||||
|
href: "https://help.openai.com/en/articles/8590148-memory-faq",
|
||||||
|
},
|
||||||
|
letta: {
|
||||||
|
name: "Letta / MemGPT Core + Archival Memory",
|
||||||
|
source: "Letta 把 stateful agent 的消息、工具调用、记忆持久化,并区分 in-context memory blocks 和 archival memory。",
|
||||||
|
borrowed: "借鉴:核心记忆常驻上下文,外部记忆通过工具或检索进入;记忆是 Agent state 的一部分。",
|
||||||
|
notEnough: "不足:完整自管理记忆体系复杂,早期直接照搬会增加主链路风险和运维成本。",
|
||||||
|
ourChoice: "我们的取舍:不让主 Agent 实时自改记忆,而是把交互事件异步交给后台 worker 整理。",
|
||||||
|
docs: "Letta Stateful Agents / Archival Memory",
|
||||||
|
href: "https://docs.letta.com/guides/core-concepts/stateful-agents",
|
||||||
|
},
|
||||||
|
mem0: {
|
||||||
|
name: "Mem0 / Managed Memory Layer",
|
||||||
|
source: "Mem0 把用户、Agent、Session 记忆做成可托管的长期记忆层,并支持 add/search/update/delete。",
|
||||||
|
borrowed: "借鉴:每次交互后抽取记忆;调用前按用户请求检索并注入。",
|
||||||
|
notEnough: "不足:我们当前更需要可审阅、可编辑、可随项目部署的轻量实现,而不是先接托管记忆服务。",
|
||||||
|
ourChoice: "我们的取舍:SQLite 做事件队列,Markdown 做人可编辑记忆正文,后续可替换成 Mem0/向量服务。",
|
||||||
|
docs: "Mem0 Platform Overview",
|
||||||
|
href: "https://docs.mem0.ai/platform/overview",
|
||||||
|
},
|
||||||
|
rag: {
|
||||||
|
name: "RAG / Vector Memory",
|
||||||
|
source: "把资料切片、embedding、向量检索,再把相关片段注入上下文。",
|
||||||
|
borrowed: "借鉴:大规模知识不应全部塞进 prompt,要按任务召回。",
|
||||||
|
notEnough: "不足:Skill 使用经验通常是流程偏好和纠错结论,不是纯语义相似文本召回。",
|
||||||
|
ourChoice: "我们的取舍:业务知识先放 skill/knowledge;记忆先用结构化 Markdown,后续知识膨胀再接召回索引。",
|
||||||
|
docs: "Letta Archival Memory",
|
||||||
|
href: "https://docs.letta.com/guides/ade/archival-memory",
|
||||||
|
},
|
||||||
|
ours: {
|
||||||
|
name: "ZK Data Agent Memory",
|
||||||
|
source: "结合团队 Skill 场景:用户偏好和 Skill 使用经验是两类不同记忆。",
|
||||||
|
borrowed: "借鉴:显式记忆、历史抽取、长期持久化、用户可编辑、异步后台处理。",
|
||||||
|
notEnough: "主动放弃:不做实时自修改、不强依赖向量库、不把所有历史都注入。",
|
||||||
|
ourChoice: "最终形态:user.md + skills/*.md + memory.db queue + worker + enabled skill 注入。",
|
||||||
|
docs: "本项目实现",
|
||||||
|
href: "#memory-implementation",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const memoryPipeline = [
|
||||||
|
{ title: "检测信号", text: "显式记住 / 纠错 / Skill 流程 / 工具失败", code: "detect_memory_signals" },
|
||||||
|
{ title: "事件入队", text: "主链路只写 pending event,不同步整理", code: "enqueue_interaction" },
|
||||||
|
{ title: "批量整理", text: "后台 worker 每轮最多取 8 条,按 priority 处理", code: "_process_account_events" },
|
||||||
|
{ title: "LLM 合并", text: "合并旧记忆,不追加流水账;失败走规则 fallback", code: "_generate_memory_updates" },
|
||||||
|
{ title: "Markdown 落盘", text: "用户可看、可改、可删;revision 可追踪", code: "user.md / skills/*.md" },
|
||||||
|
{ title: "按需注入", text: "只注入用户记忆和当前启用 Skill 的记忆", code: "render_injection" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const skills: Record<
|
||||||
|
SkillPartId,
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
purpose: string;
|
||||||
|
agentSees: string;
|
||||||
|
maintainerDoes: string;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
skillmd: {
|
||||||
|
name: "SKILL.md",
|
||||||
|
path: "skills/<name>/SKILL.md",
|
||||||
|
purpose: "定义 Agent 如何识别任务、何时澄清、何时 review、何时调用脚本。",
|
||||||
|
agentSees: "流程协议、工具优先级、产物约定、人类确认边界。",
|
||||||
|
maintainerDoes: "把经验写成可执行步骤,而不是把所有业务细节塞成一大段 prompt。",
|
||||||
|
},
|
||||||
|
knowledge: {
|
||||||
|
name: "knowledge/",
|
||||||
|
path: "skills/<name>/knowledge/",
|
||||||
|
purpose: "放业务定义、标签边界、字段说明、判断准则。",
|
||||||
|
agentSees: "按需读取具体知识文件,用于分析和生成。",
|
||||||
|
maintainerDoes: "把经常变的业务规则放这里,降低改 SKILL.md 的频率。",
|
||||||
|
},
|
||||||
|
scripts: {
|
||||||
|
name: "scripts/",
|
||||||
|
path: "skills/<name>/scripts/",
|
||||||
|
purpose: "放确定性能力:校验、格式转换、导出、数据清洗。",
|
||||||
|
agentSees: "通过 python_exec 执行脚本,不再现场手写复杂转换逻辑。",
|
||||||
|
maintainerDoes: "把易错代码沉淀成可测试脚本,输入输出写清楚。",
|
||||||
|
},
|
||||||
|
examples: {
|
||||||
|
name: "examples/",
|
||||||
|
path: "skills/<name>/examples/",
|
||||||
|
purpose: "放典型输入输出,帮助 Agent 对齐格式和边界。",
|
||||||
|
agentSees: "少量高质量样例,比长篇抽象说明更稳。",
|
||||||
|
maintainerDoes: "新增失败 case 的最小复现,作为回归材料。",
|
||||||
|
},
|
||||||
|
readme: {
|
||||||
|
name: "README.md",
|
||||||
|
path: "skills/<name>/README.md",
|
||||||
|
purpose: "给人看的维护说明:这是什么、怎么测、怎么扩展。",
|
||||||
|
agentSees: "通常不强制注入,但可被读取用于理解项目背景。",
|
||||||
|
maintainerDoes: "保证同事能接手,不必找原作者口头说明。",
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: "更新 Skill",
|
||||||
|
path: "Web 会话页 > Skills > 更新 Skill",
|
||||||
|
purpose: "同事提交 Git 后,页面内拉取最新 Skill 文件,刷新可用能力。",
|
||||||
|
agentSees: "更新后的 Skill 列表会进入模型可见能力列表。",
|
||||||
|
maintainerDoes: "提交 Skill 改动后提醒使用者点更新;平台发布时再统一收敛。",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const taskFlows = [
|
||||||
|
{
|
||||||
|
name: "产品定义 → 数据集",
|
||||||
|
skill: "product-data",
|
||||||
|
input: "产品文档 / 手写标签边界 / 示例 query",
|
||||||
|
review: "目标标签、complex、覆盖范围、数量计划",
|
||||||
|
outputs: ["records.jsonl", "review.csv", "train.jsonl", "eval.csv"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "线上日志 → 专项样本",
|
||||||
|
skill: "online-mining-v2 + elk-fetch",
|
||||||
|
input: "线上 badcase / query 特征 / domain 条件 / ELK 表",
|
||||||
|
review: "筛选策略、候选质量、是否直接转样本还是继续生成",
|
||||||
|
outputs: ["candidates.csv", "sample_review.md", "records.jsonl"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "标签定义 → 判断辅助",
|
||||||
|
skill: "标签大师",
|
||||||
|
input: "query / 多轮上下文 / 标签定义知识库",
|
||||||
|
review: "复杂度、多指令、自动任务、业务标签、输出形态",
|
||||||
|
outputs: ["label decision", "boundary evidence", "target function"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const runtimeTrace = [
|
||||||
|
{ stage: "前端发送", text: "携带 account/session/model/enabled skills", path: "app/api/chat/route.ts" },
|
||||||
|
{ stage: "上下文拼接", text: "Skill 列表 + 用户记忆 + Skill 记忆 + workspace", path: "backend/api/server.py" },
|
||||||
|
{ stage: "Agent Loop", text: "模型决定回复、调用工具、执行脚本或等待 review", path: "src/agent_runtime.py" },
|
||||||
|
{ stage: "工具执行", text: "python_exec / file / search / MCP / Skill scripts", path: "src/agent_tools.py" },
|
||||||
|
{ stage: "事件流", text: "tool_start、tool_end、content_delta、tool_delta", path: "RunStateStore" },
|
||||||
|
{ stage: "产物落盘", text: "scratchpad 存中间过程,output 存最终文件", path: ".port_sessions/.../sessions" },
|
||||||
|
{ stage: "后台记忆", text: "交互完成后异步整理长期经验", path: "src/personal_memory.py" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const sourceLinks = [
|
||||||
|
{ label: "OpenAI Memory FAQ", href: "https://help.openai.com/en/articles/8590148-memory-faq" },
|
||||||
|
{ label: "Letta Stateful Agents", href: "https://docs.letta.com/guides/core-concepts/stateful-agents" },
|
||||||
|
{ label: "Letta Archival Memory", href: "https://docs.letta.com/guides/ade/archival-memory" },
|
||||||
|
{ label: "Mem0 Overview", href: "https://docs.mem0.ai/platform/overview" },
|
||||||
|
{ label: "Semantic Kernel + Mem0", href: "https://learn.microsoft.com/en-us/semantic-kernel/frameworks/agent/agent-memory" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function StatusIcon({ value }: { value: "yes" | "partial" | "no" }) {
|
||||||
|
if (value === "yes") return <Check className="doc-status yes" />;
|
||||||
|
if (value === "partial") return <CircleDashed className="doc-status partial" />;
|
||||||
|
return <X className="doc-status no" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProductComparison() {
|
||||||
|
const [selected, setSelected] = useState<ProductId>("zk");
|
||||||
|
const detail = products[selected];
|
||||||
|
const productIds: ProductId[] = ["chatgpt", "cursor", "claude", "zk"];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="doc-section doc-compare-lab" id="why-us">
|
||||||
|
<div className="doc-section-heading">
|
||||||
|
<p className="doc-kicker">先回答最尖锐的问题</p>
|
||||||
|
<h2>为什么不用 Claude Code / ChatGPT / Cursor 就够了?</h2>
|
||||||
|
<p>
|
||||||
|
不是因为我们模型更强,而是因为我们补的是团队业务流程平台:多人入口、共享 Skill、数据源、产物、记忆和管理。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="doc-compare-layout">
|
||||||
|
<div className="doc-product-switcher">
|
||||||
|
{productIds.map((id) => (
|
||||||
|
<button
|
||||||
|
className={selected === id ? "is-selected" : ""}
|
||||||
|
key={id}
|
||||||
|
onClick={() => setSelected(id)}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span>{products[id].name}</span>
|
||||||
|
<small>{products[id].position}</small>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="doc-product-card">
|
||||||
|
<p className="doc-kicker">当前选中</p>
|
||||||
|
<h3>{detail.name}</h3>
|
||||||
|
<div className="doc-product-facts">
|
||||||
|
<div>
|
||||||
|
<span>最适合</span>
|
||||||
|
<p>{detail.bestFor}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>边界</span>
|
||||||
|
<p>{detail.limits}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="doc-chip-list">
|
||||||
|
{detail.tech.map((item) => (
|
||||||
|
<span className="doc-chip" key={item}>{item}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="doc-matrix">
|
||||||
|
<div className="doc-matrix-head">
|
||||||
|
<span>能力</span>
|
||||||
|
{productIds.map((id) => <strong key={id}>{products[id].name}</strong>)}
|
||||||
|
</div>
|
||||||
|
{comparisonRows.map((row) => (
|
||||||
|
<div className="doc-matrix-row" key={row.label}>
|
||||||
|
<span>{row.label}</span>
|
||||||
|
{productIds.map((id) => (
|
||||||
|
<div className={selected === id ? "is-highlighted" : ""} key={id}>
|
||||||
|
<StatusIcon value={row[id]} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RoleSwitcher() {
|
||||||
|
const [selected, setSelected] = useState<RoleId>("user");
|
||||||
|
const role = roles[selected];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="doc-section doc-role-lab" id="roles">
|
||||||
|
<div className="doc-section-heading">
|
||||||
|
<p className="doc-kicker">融入工作的方式</p>
|
||||||
|
<h2>同一个平台,三类人看到的是三条不同路径</h2>
|
||||||
|
</div>
|
||||||
|
<div className="doc-role-grid">
|
||||||
|
<div className="doc-role-tabs">
|
||||||
|
{(Object.keys(roles) as RoleId[]).map((id) => (
|
||||||
|
<button
|
||||||
|
className={selected === id ? "is-selected" : ""}
|
||||||
|
key={id}
|
||||||
|
onClick={() => setSelected(id)}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{roles[id].name}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="doc-role-panel">
|
||||||
|
<h3>{role.title}</h3>
|
||||||
|
<div className="doc-role-question">{role.question}</div>
|
||||||
|
<p>{role.answer}</p>
|
||||||
|
<div className="doc-flow-chips">
|
||||||
|
{role.workflow.map((step) => (
|
||||||
|
<span key={step}>{step}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="doc-proof-panel">
|
||||||
|
<p className="doc-kicker">平台证据</p>
|
||||||
|
{role.proof.map((item) => (
|
||||||
|
<div className="doc-proof-item" key={item}>
|
||||||
|
<CheckCircle2 />
|
||||||
|
<span>{item}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ObjectiveBoard() {
|
||||||
|
return (
|
||||||
|
<section className="doc-section doc-objectives" id="design-goals">
|
||||||
|
<div className="doc-section-heading">
|
||||||
|
<p className="doc-kicker">这页怎么讲清楚系统</p>
|
||||||
|
<h2>每个前端模块都必须服务一个表达目标</h2>
|
||||||
|
</div>
|
||||||
|
<div className="doc-objective-grid">
|
||||||
|
{objectives.map((item, index) => (
|
||||||
|
<article className="doc-objective" key={item.goal}>
|
||||||
|
<span>{String(index + 1).padStart(2, "0")}</span>
|
||||||
|
<h3>{item.goal}</h3>
|
||||||
|
<p>{item.frontend}</p>
|
||||||
|
<strong>{item.message}</strong>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MemoryAtlas() {
|
||||||
|
const [selected, setSelected] = useState<MemoryId>("ours");
|
||||||
|
const item = memoryPatterns[selected];
|
||||||
|
const order: MemoryId[] = ["chatgpt", "letta", "mem0", "rag", "ours"];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="doc-section doc-memory-lab" id="memory">
|
||||||
|
<div className="doc-section-heading">
|
||||||
|
<p className="doc-kicker">记忆方案谱系</p>
|
||||||
|
<h2>我们不是闭门造车:先看主流方案,再看为什么这样落地</h2>
|
||||||
|
<p>
|
||||||
|
记忆不是一个功能开关。我们参考了显式保存、历史参考、状态化 Agent、归档检索、托管记忆层等方案,最后按团队 Skill 场景做了轻量实现。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="doc-memory-map">
|
||||||
|
<div className="doc-memory-rail">
|
||||||
|
{order.map((id, index) => (
|
||||||
|
<button
|
||||||
|
className={selected === id ? "is-selected" : ""}
|
||||||
|
key={id}
|
||||||
|
onClick={() => setSelected(id)}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span>{String(index + 1).padStart(2, "0")}</span>
|
||||||
|
<strong>{memoryPatterns[id].name}</strong>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="doc-memory-detail-v2">
|
||||||
|
<div className="doc-memory-source">
|
||||||
|
<a href={item.href}>{item.docs}</a>
|
||||||
|
</div>
|
||||||
|
<h3>{item.name}</h3>
|
||||||
|
<div className="doc-memory-grid-v2">
|
||||||
|
<div><span>它怎么做</span><p>{item.source}</p></div>
|
||||||
|
<div><span>我们借鉴</span><p>{item.borrowed}</p></div>
|
||||||
|
<div><span>为什么不够</span><p>{item.notEnough}</p></div>
|
||||||
|
<div><span>我们的取舍</span><p>{item.ourChoice}</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="doc-memory-implementation" id="memory-implementation">
|
||||||
|
<div className="doc-section-heading compact">
|
||||||
|
<p className="doc-kicker">当前实现</p>
|
||||||
|
<h2>用户记忆 + Skill 记忆 + 异步队列</h2>
|
||||||
|
</div>
|
||||||
|
<div className="doc-memory-pipeline-v2">
|
||||||
|
{memoryPipeline.map((step, index) => (
|
||||||
|
<div className="doc-memory-step" key={step.title}>
|
||||||
|
<span>{String(index + 1).padStart(2, "0")}</span>
|
||||||
|
<strong>{step.title}</strong>
|
||||||
|
<p>{step.text}</p>
|
||||||
|
<code>{step.code}</code>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SkillWorkbench() {
|
||||||
|
const [selected, setSelected] = useState<SkillPartId>("skillmd");
|
||||||
|
const part = skills[selected];
|
||||||
|
const order: SkillPartId[] = ["skillmd", "knowledge", "scripts", "examples", "readme", "update"];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="doc-section doc-skill-workbench" id="skill-dev">
|
||||||
|
<div className="doc-section-heading">
|
||||||
|
<p className="doc-kicker">Skill 怎么开发,怎么共享,怎么更新</p>
|
||||||
|
<h2>把一个人的经验打包成团队能力,不靠口口相传</h2>
|
||||||
|
</div>
|
||||||
|
<div className="doc-skill-workbench-grid">
|
||||||
|
<div className="doc-file-tree-v2">
|
||||||
|
{order.map((id) => (
|
||||||
|
<button className={selected === id ? "is-selected" : ""} key={id} onClick={() => setSelected(id)} type="button">
|
||||||
|
<FolderTree />
|
||||||
|
<span>{skills[id].name}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="doc-skill-part">
|
||||||
|
<code>{part.path}</code>
|
||||||
|
<h3>{part.name}</h3>
|
||||||
|
<div className="doc-skill-part-grid">
|
||||||
|
<div><span>解决什么</span><p>{part.purpose}</p></div>
|
||||||
|
<div><span>Agent 看到什么</span><p>{part.agentSees}</p></div>
|
||||||
|
<div><span>维护者做什么</span><p>{part.maintainerDoes}</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="doc-skill-steps">
|
||||||
|
<h3>一个 Skill 的提交路径</h3>
|
||||||
|
{["创建目录", "补 SKILL.md", "沉淀脚本/知识", "本地测试", "提交 Git", "页面点更新 Skill"].map((step, index) => (
|
||||||
|
<div key={step}><span>{index + 1}</span>{step}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TaskFlowMap() {
|
||||||
|
return (
|
||||||
|
<section className="doc-section doc-task-map" id="tasks">
|
||||||
|
<div className="doc-section-heading">
|
||||||
|
<p className="doc-kicker">已经能进入工作的场景</p>
|
||||||
|
<h2>不是展示功能点,而是展示从输入到产物的链路</h2>
|
||||||
|
</div>
|
||||||
|
<div className="doc-task-grid">
|
||||||
|
{taskFlows.map((flow) => (
|
||||||
|
<article className="doc-task-card" key={flow.name}>
|
||||||
|
<div className="doc-task-title"><Route /><h3>{flow.name}</h3></div>
|
||||||
|
<div className="doc-task-meta"><span>Skill</span><strong>{flow.skill}</strong></div>
|
||||||
|
<div className="doc-task-meta"><span>输入</span><p>{flow.input}</p></div>
|
||||||
|
<div className="doc-task-meta"><span>Review 点</span><p>{flow.review}</p></div>
|
||||||
|
<div className="doc-output-list">
|
||||||
|
{flow.outputs.map((output) => <span key={output}>{output}</span>)}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RuntimeTrace() {
|
||||||
|
return (
|
||||||
|
<section className="doc-section doc-runtime-trace" id="runtime">
|
||||||
|
<div className="doc-section-heading">
|
||||||
|
<p className="doc-kicker">运行时链路</p>
|
||||||
|
<h2>从用户按下发送,到文件、活动区和记忆更新</h2>
|
||||||
|
</div>
|
||||||
|
<div className="doc-runtime-rail">
|
||||||
|
{runtimeTrace.map((step, index) => (
|
||||||
|
<div className="doc-runtime-node" key={step.stage}>
|
||||||
|
<span>{String(index + 1).padStart(2, "0")}</span>
|
||||||
|
<strong>{step.stage}</strong>
|
||||||
|
<p>{step.text}</p>
|
||||||
|
<code>{step.path}</code>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DocPage() {
|
||||||
|
return (
|
||||||
|
<main className="doc-page doc-page-v2">
|
||||||
|
<header className="doc-hero-v2">
|
||||||
|
<nav className="doc-nav">
|
||||||
|
<a className="doc-brand" href="/">
|
||||||
|
<span className="doc-brand-mark">ZK</span>
|
||||||
|
<span>Data Agent</span>
|
||||||
|
</a>
|
||||||
|
<div className="doc-nav-links">
|
||||||
|
<a href="#why-us">为什么用</a>
|
||||||
|
<a href="#roles">怎么融入</a>
|
||||||
|
<a href="#memory">记忆取舍</a>
|
||||||
|
<a href="#skill-dev">开发 Skill</a>
|
||||||
|
<a href="#runtime">运行链路</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<section className="doc-hero-v2-grid">
|
||||||
|
<div className="doc-hero-v2-copy">
|
||||||
|
<p className="doc-kicker">不是更聪明的聊天框</p>
|
||||||
|
<h1>这是一个把团队业务流程沉淀成共享 Skill 的 Agent 工作台</h1>
|
||||||
|
<p>
|
||||||
|
它补的不是“再问模型一次”,而是团队级能力复用:多人入口、业务 Skill、线上数据源、会话产物、Skill 记忆、管理后台和可审阅执行链路。
|
||||||
|
</p>
|
||||||
|
<div className="doc-hero-actions">
|
||||||
|
<a className="doc-primary-link" href="#why-us">先回答为什么用我们 <ArrowRight /></a>
|
||||||
|
<a className="doc-secondary-link" href="#skill-dev">我想开发 Skill</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="doc-command-wall">
|
||||||
|
<div className="doc-command-head"><Sparkles /> 一次真实任务会经过什么</div>
|
||||||
|
{["选择 product-data Skill", "对齐标签和 complex", "生成可 review 计划", "调用 scripts 导出 records/train/eval", "文件进入当前会话 output"].map((item, index) => (
|
||||||
|
<div className="doc-command-row" key={item}>
|
||||||
|
<span>{String(index + 1).padStart(2, "0")}</span>
|
||||||
|
<strong>{item}</strong>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<ObjectiveBoard />
|
||||||
|
<ProductComparison />
|
||||||
|
<RoleSwitcher />
|
||||||
|
<TaskFlowMap />
|
||||||
|
<MemoryAtlas />
|
||||||
|
<SkillWorkbench />
|
||||||
|
<RuntimeTrace />
|
||||||
|
|
||||||
|
<section className="doc-section doc-source-links">
|
||||||
|
<div className="doc-section-heading">
|
||||||
|
<p className="doc-kicker">参考来源</p>
|
||||||
|
<h2>记忆和 Agent 设计参考了这些现成方案</h2>
|
||||||
|
</div>
|
||||||
|
<div className="doc-source-grid">
|
||||||
|
{sourceLinks.map((link) => (
|
||||||
|
<a href={link.href} key={link.href}><Network />{link.label}</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
+1753
-1
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user