Files
HomeAgent/site/index.html
JianFeeeee 47052cb115 fix(site): 更正媒体机制描述 + 页脚补备案号
## 描述性错误(用户指出)
1. **「引用计数 GC」已不存在**。「有引用绝不删」「媒体靠引用计数 GC」
   两处都在讲一个已废弃的账本 —— 实测源码里已无 media_refs/ref_count,
   `internal/memory/media/media.go` 明确写「这不是 GC,也不看引用计数」。
   现行规则是「删除持有它的记忆块即删内容」,与文本块同一套
   (medialoop.go 的 payloadHeld 只在确认无块共享时才删字节)。
2. **「描述才是持久语义」整张卡已过时**。旧实现靠视觉模型生成的描述当索引;
   现已弃用 —— `mediaref.go` 写明标签「不再包含任何生成的描述文本」,
   图片改按统一空间向量检索,`graphmedia.go` 还带一个把旧描述式实体
   迁移成原生记忆块的迁移函数。卡片改为「图片靠自己的向量被检索」。

## 备案号
页脚补 豫ICP备2024074105号-1 与 豫公网安备41070202001579号,
链接到 beian.miit.gov.cn / beian.mps.gov.cn。取值来源是现网
门户配置(/root/portal/dashy/conf.yml),未凭记忆编造。

## 验证
深浅双主题下渲染正确、两条链接 href 实测无误、无 JS 错误。
2026-09-20 00:00:23 +08:00

2126 lines
92 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HomeAgent — 常驻型个人 Agent 框架</title>
<meta name="description" content="HomeAgent 是一个持续运行的个人智能 Agent 框架:内核零 IO所有 IO 由插件承载、三层记忆Context → Document → Graph、输入调度两类别 + 四级中断)与驻留式子 agent。" />
<link rel="icon" type="image/svg+xml" href="assets/logo.svg" />
<meta property="og:title" content="HomeAgent — 常驻型个人 Agent 框架" />
<meta property="og:description" content="内核零 IO · 三层记忆 · 四级中断调度 · 驻留式子 agent" />
<meta property="og:type" content="website" />
<script>
/* 在首次绘制前定主题,避免浅/深闪一下。
优先级localStorage 手动选择 > 系统偏好。 */
(function () {
try {
var saved = localStorage.getItem("ha-theme");
var sys = window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches
? "light" : "dark";
document.documentElement.setAttribute("data-theme", saved || sys);
} catch (e) {
document.documentElement.setAttribute("data-theme", "dark");
}
})();
</script>
<style>
/* ── 设计令牌 ──
品牌色两主题共用(蓝/青/金 = 三层记忆);语义色按主题覆盖。
默认跟随系统,可用导航栏按钮手动切换(存 localStorage。 */
:root {
--brand-blue: #3b82f6; /* Context 层 */
--brand-cyan: #06b6d4; /* Document 层 */
--brand-gold: #f59e0b; /* Graph / Kernel */
--brand-violet: #8b5cf6;
--brand-sky: #38bdf8;
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 14px;
--radius-pill: 999px;
--font-sans: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--font-mono: "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
--ease: cubic-bezier(0.22, 1, 0.36, 1);
--ease-back: cubic-bezier(0.34, 1.56, 0.64, 1);
--dur: 300ms;
/* 深色(默认) */
--bg: #070b16;
--bg-soft: rgba(17, 24, 44, 0.72);
--bg-card: rgba(17, 24, 44, 0.58);
--bg-card-hi: rgba(23, 32, 58, 0.72);
--border: rgba(255, 255, 255, 0.09);
--border-hi: rgba(59, 130, 246, 0.38);
--text: #eef1f8;
--text-dim: #a7b0c4;
--text-muted: #77809a;
--code-bg: rgba(6, 10, 22, 0.85);
--code-fg: #cdd3f5;
--nav-bg: rgba(7, 11, 22, 0.72);
--aurora-a: rgba(59, 130, 246, 0.22);
--aurora-b: rgba(6, 182, 212, 0.18);
--aurora-c: rgba(139, 92, 246, 0.16);
--aurora-d: rgba(245, 158, 11, 0.10);
--particle: 148, 190, 255;
--grid-line: rgba(255, 255, 255, 0.035);
--spot: rgba(255, 255, 255, 0.07);
--hover: rgba(255, 255, 255, 0.06);
--sheen: rgba(255, 255, 255, 0.38);
--shadow-md: 0 4px 16px rgba(0, 0, 0, 0.28);
--shadow-lg: 0 12px 36px rgba(0, 0, 0, 0.38);
--shadow-glow: 0 0 0 1px rgba(59, 130, 246, 0.22),
0 8px 32px rgba(59, 130, 246, 0.18);
}
/* 浅色:同一套语义变量,另一组取值 */
:root[data-theme="light"] {
--bg: #f4f7fc;
--bg-soft: rgba(255, 255, 255, 0.82);
--bg-card: rgba(255, 255, 255, 0.72);
--bg-card-hi: rgba(255, 255, 255, 0.9);
--border: rgba(15, 23, 42, 0.10);
--border-hi: rgba(37, 99, 235, 0.45);
--text: #0d1526;
--text-dim: #48546c;
--text-muted: #78849a;
--code-bg: #f1f5fb;
--code-fg: #1e293b;
--nav-bg: rgba(244, 247, 252, 0.78);
--aurora-a: rgba(59, 130, 246, 0.22);
--aurora-b: rgba(6, 182, 212, 0.18);
--aurora-c: rgba(139, 92, 246, 0.14);
--aurora-d: rgba(245, 158, 11, 0.14);
--particle: 37, 99, 235;
--grid-line: rgba(15, 23, 42, 0.045);
--spot: rgba(15, 23, 42, 0.05);
--hover: rgba(15, 23, 42, 0.05);
--sheen: rgba(15, 23, 42, 0.16);
--shadow-md: 0 4px 16px rgba(15, 23, 42, 0.08);
--shadow-lg: 0 12px 36px rgba(15, 23, 42, 0.12);
--shadow-glow: 0 0 0 1px rgba(37, 99, 235, 0.16),
0 8px 28px rgba(37, 99, 235, 0.14);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
/* 防横向溢出:装饰层(极光/粒子)永远不该产生滚动条。
实测 canvas 曾因按 innerWidth 铺开而多出 15px故这里兜一层。 */
body { overflow-x: clip; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: var(--font-sans);
line-height: 1.75;
-webkit-font-smoothing: antialiased;
}
/* 背景光晕:蓝/青/金,呼应三层记忆 */
body::before {
content: "";
position: fixed;
inset: 0;
z-index: -1;
background:
radial-gradient(60rem 40rem at 15% -10%, rgba(59, 130, 246, 0.16), transparent 60%),
radial-gradient(50rem 35rem at 85% 5%, rgba(6, 182, 212, 0.12), transparent 60%),
radial-gradient(45rem 30rem at 50% 100%, rgba(245, 158, 11, 0.08), transparent 60%);
pointer-events: none;
}
.wrap { width: min(1120px, 92vw); margin: 0 auto; }
/* ── 顶部导航 ── */
header {
position: sticky;
top: 0;
z-index: 50;
backdrop-filter: blur(14px);
background: var(--nav-bg);
border-bottom: 1px solid var(--border);
}
nav {
display: flex;
align-items: center;
gap: 1.25rem;
padding: 0.85rem 0;
}
nav .brand {
display: flex;
align-items: center;
gap: 0.6rem;
font-weight: 700;
letter-spacing: 0.01em;
margin-right: auto;
}
nav .brand img { width: 30px; height: 30px; border-radius: 8px; }
nav a.link {
color: var(--text-dim);
text-decoration: none;
font-size: 0.92rem;
padding: 0.35rem 0.2rem;
border-bottom: 2px solid transparent;
transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
nav a.link:hover { color: var(--text); border-bottom-color: var(--brand-blue); }
nav .en { color: var(--text-muted); font-size: 0.85rem; }
@media (max-width: 760px) {
nav { flex-wrap: wrap; gap: 0.6rem; }
nav .hide-sm { display: none; }
}
/* ── Hero ── */
.hero { padding: 6rem 0 4.5rem; }
.hero-grid { display: grid; grid-template-columns: 1fr; }
.hero-copy { max-width: 46rem; }
@media (max-width: 860px) {
.hero { padding: 3rem 0 2rem; }
.hero-grid { gap: 2rem; text-align: center; }
.hero-copy { margin: 0 auto; }
.hero .cta { justify-content: center; }
}
.tagline {
display: inline-block;
font-size: 0.8rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--brand-sky);
border: 1px solid rgba(56, 189, 248, 0.3);
background: rgba(56, 189, 248, 0.08);
padding: 0.3rem 0.7rem;
border-radius: var(--radius-pill);
margin-bottom: 1.1rem;
}
h1 {
font-size: clamp(2.1rem, 5vw, 3.2rem);
line-height: 1.2;
margin: 0 0 1rem;
letter-spacing: -0.02em;
}
h1 .grad {
background: linear-gradient(120deg, var(--brand-blue), var(--brand-cyan) 50%, var(--brand-gold));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.lede {
font-size: 1.08rem;
color: var(--text-dim);
max-width: 40rem;
margin: 0 0 1.6rem;
}
@media (max-width: 860px) { .lede { margin-inline: auto; } }
.cta { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; }
.btn {
display: inline-flex;
align-items: center;
gap: 0.45rem;
padding: 0.62rem 1.1rem;
border-radius: var(--radius-md);
font-size: 0.94rem;
font-weight: 600;
text-decoration: none;
border: 1px solid transparent;
transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
background var(--dur) var(--ease);
}
.btn:hover { transform: translateY(-1px); }
.btn-primary {
background: linear-gradient(135deg, var(--brand-blue), var(--brand-cyan));
color: #04121f;
box-shadow: var(--shadow-glow);
}
.btn-ghost {
background: var(--bg-soft);
color: var(--text);
border-color: var(--border);
}
.btn-ghost:hover { background: var(--hover); }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
}
/* ── 通用区块 ── */
section { padding: 3.6rem 0; }
section > .wrap > h2 {
font-size: clamp(1.4rem, 3vw, 1.85rem);
margin: 0 0 0.5rem;
letter-spacing: -0.01em;
}
section > .wrap > .sub {
color: var(--text-dim);
margin: 0 0 2rem;
max-width: 46rem;
}
.card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 1.4rem;
transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease),
box-shadow var(--dur) var(--ease);
}
.card:hover {
border-color: rgba(59, 130, 246, 0.35);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.card h3 { margin: 0 0 0.5rem; font-size: 1.05rem; }
.card p { margin: 0; color: var(--text-dim); font-size: 0.93rem; }
.card .icon {
width: 34px; height: 34px;
border-radius: var(--radius-sm);
display: grid; place-items: center;
margin-bottom: 0.8rem;
font-size: 1.05rem;
}
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.1rem; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.1rem; }
@media (max-width: 860px) {
.grid-2, .grid-3 { grid-template-columns: 1fr; }
}
/* ── 三层记忆:用品牌三色 ── */
.layers { display: grid; gap: 0.9rem; }
.layer {
display: grid;
grid-template-columns: 4px 1fr;
gap: 1.1rem;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
transition: border-color var(--dur) var(--ease);
}
.layer:hover { border-color: var(--border); }
.layer .bar { border-radius: 0; }
.layer .body { padding: 1.05rem 1.2rem 1.05rem 0; }
.layer h3 { margin: 0 0 0.35rem; font-size: 1.02rem; }
.layer h3 .en { color: var(--text-muted); font-weight: 400; font-size: 0.85rem; margin-left: 0.4rem; }
.layer p { margin: 0; color: var(--text-dim); font-size: 0.92rem; }
.l-ctx .bar { background: var(--brand-blue); }
.l-doc .bar { background: var(--brand-cyan); }
.l-gph .bar { background: var(--brand-gold); }
.l-ctx h3 span.mark { color: var(--brand-blue); }
.l-doc h3 span.mark { color: var(--brand-cyan); }
.l-gph h3 span.mark { color: var(--brand-gold); }
/* ── 消息流程图 ──
不用 ASCII那只能表达"上下关系",工具循环与分支都画不出来。
语义化结构 + CSS深浅主题跟着走窄屏也能重排。 */
.flow {
position: relative;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 1.6rem 1.5rem 1.3rem;
}
/* 注:这里原来有一条贯穿全图的 ::before 轴线。
但节点背景是半透明令牌(--bg-card-hi轴线会直接透出来——
实测在「处理管道」里穿过整个编号列表,看着像画错了。
连接线 .lnk 本身就是那条主轴,不需要再来一条。 */
/* ── 节点 ── */
.n {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: 1.9rem auto;
grid-template-areas: "ico t" "ico s";
align-items: center;
gap: 0 0.7rem;
width: 100%;
max-width: 34rem;
margin: 0 auto;
padding: 0.6rem 1.1rem 0.6rem 0.75rem;
background: var(--bg-card-hi);
border: 1px solid var(--border-hi);
border-radius: var(--radius-md);
box-shadow: var(--shadow-md);
}
.n-ico {
grid-area: ico;
display: grid;
place-items: center;
width: 1.9rem; height: 1.9rem;
border-radius: 50%;
font-size: 0.85rem;
background: rgba(59, 130, 246, 0.14);
color: var(--brand-blue);
}
.n-t {
grid-area: t;
font-weight: 700;
font-size: 0.95rem;
color: var(--text);
line-height: 1.35;
}
.n-s {
grid-area: s;
font-size: 0.78rem;
color: var(--text-muted);
font-family: var(--font-mono);
line-height: 1.45;
}
/* 各类节点用颜色区分:与三层记忆的三色一致 */
.n-in .n-ico { background: rgba(119, 128, 154, 0.16); color: var(--text-dim); }
.n-sched .n-ico { background: rgba(56, 189, 248, 0.16); color: var(--brand-sky); }
.n-pipe { border-color: rgba(6, 182, 212, 0.45); }
.n-pipe .n-ico { background: rgba(6, 182, 212, 0.16); color: var(--brand-cyan); }
.n-mem { border-color: rgba(245, 158, 11, 0.45); }
.n-mem .n-ico { background: rgba(245, 158, 11, 0.16); color: var(--brand-gold); }
.n-out .n-ico { background: rgba(59, 130, 246, 0.16); color: var(--brand-blue); }
/* ── 连接线:中轴上的一小段,带流动感 ── */
.lnk {
position: relative;
height: 2.1rem;
width: 2px;
margin: 0 auto;
background: var(--border-hi);
}
.lnk::before {
/* 静态箭头reduce 下动画停了,方向也要看得出来 */
content: "";
position: absolute;
left: 50%; bottom: -1px;
width: 7px; height: 7px;
margin-left: -3.5px;
border-right: 2px solid var(--border-hi);
border-bottom: 2px solid var(--border-hi);
transform: rotate(45deg);
}
.lnk::after {
/* 下行的光点 */
content: "";
position: absolute;
left: 50%; top: 0;
width: 6px; height: 6px;
margin-left: -3px;
border-radius: 50%;
background: var(--brand-sky);
box-shadow: 0 0 8px var(--brand-sky);
animation: dotDown 2.4s var(--ease) infinite;
animation-delay: var(--d, 0s);
}
@keyframes dotDown {
0% { top: 0; opacity: 0; }
18% { opacity: 1; }
82% { opacity: 1; }
100% { top: 100%; opacity: 0; }
}
/* ── 调度分支 ── */
.br {
position: relative;
z-index: 1;
list-style: none;
max-width: 34rem;
margin: 0.55rem auto 0;
padding: 0;
display: grid;
gap: 0.28rem;
}
.br li {
position: relative;
font-size: 0.78rem;
color: var(--text-dim);
padding: 0.3rem 0.7rem 0.3rem 1.6rem;
border-left: 2px solid var(--border);
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
background: rgba(119, 128, 154, 0.06);
}
.br li::before {
content: "└";
position: absolute;
left: 0.42rem;
color: var(--text-muted);
font-family: var(--font-mono);
}
.br li b { color: var(--text); font-weight: 600; }
.br li i { font-style: normal; font-weight: 600; }
.br-que i, .br-que b { color: var(--text-dim); }
.br-pre { border-left-color: rgba(245, 158, 11, 0.55); }
.br-pre i { color: var(--brand-gold); }
.br-off { border-left-color: rgba(139, 92, 246, 0.55); }
.br-off i { color: var(--brand-violet); }
/* ── 工具循环 ── */
.loop {
position: relative;
z-index: 1;
max-width: 34rem;
margin: 0.75rem auto 0;
padding: 2.4rem 1rem 2.6rem;
border: 1px dashed rgba(6, 182, 212, 0.4);
border-radius: var(--radius-md);
background: rgba(6, 182, 212, 0.045);
}
.loop-tag {
position: absolute;
top: -0.68rem; left: 1rem;
padding: 0.12rem 0.55rem;
font-size: 0.7rem;
font-family: var(--font-mono);
color: var(--brand-cyan);
background: var(--bg);
border: 1px solid rgba(6, 182, 212, 0.4);
border-radius: var(--radius-pill);
}
.seq {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.34rem;
}
.seq-item {
display: grid;
grid-template-columns: 1.5rem 1fr;
gap: 0.6rem;
align-items: baseline;
}
.seq-n {
display: grid;
place-items: center;
width: 1.5rem; height: 1.5rem;
border-radius: 50%;
font-size: 0.7rem;
font-family: var(--font-mono);
color: var(--brand-cyan);
background: rgba(6, 182, 212, 0.14);
border: 1px solid rgba(6, 182, 212, 0.3);
}
.seq-b { font-size: 0.82rem; color: var(--text); line-height: 1.5; }
.seq-b b { font-weight: 600; }
.seq-b code { font-size: 0.78rem; }
.seq-b em {
display: block;
font-style: normal;
font-size: 0.74rem;
color: var(--text-muted);
}
/* 循环回边:内嵌徽标。
曾试过从框底绕出去的弧线,但它会压到下一条连接线,看着像画错了。 */
.loop-back {
position: absolute;
right: 0.7rem; bottom: 0.7rem;
display: flex;
align-items: center;
gap: 0.34rem;
padding: 0.2rem 0.6rem;
font-size: 0.7rem;
font-family: var(--font-mono);
color: var(--brand-cyan);
background: rgba(6, 182, 212, 0.12);
border: 1px solid rgba(6, 182, 212, 0.35);
border-radius: var(--radius-pill);
white-space: nowrap;
pointer-events: none;
}
.loop-back::before {
content: "↻";
font-size: 0.85rem;
line-height: 1;
}
.flow-note {
position: relative;
z-index: 1;
margin: 1.1rem auto 0;
max-width: 34rem;
text-align: center;
font-size: 0.76rem;
color: var(--text-muted);
line-height: 1.6;
}
.flow-note code { font-size: 0.74rem; }
/* 窄屏:中轴线收边,节点撑满 */
@media (max-width: 620px) {
.flow { padding: 1.2rem 1rem 1.1rem; }
.flow::before { display: none; }
.n { width: auto; max-width: none; }
.br, .flow-note { max-width: none; }
.loop { max-width: none; padding: 2.2rem 0.8rem 2.4rem; }
.lnk { height: 1.6rem; }
}
/* ── 代码块 ── */
.code {
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 1rem 1.15rem;
font-family: var(--font-mono);
font-size: 0.86rem;
color: var(--code-fg);
overflow-x: auto;
line-height: 1.7;
/* ❗必须显式声明HTML 源码里的换行默认会被折叠成一个空格,
实测不写这行整段命令会挤成一行“make build build-cli # 启动…
./build/homed” 全连在一起)。 */
white-space: pre;
}
.code .c { color: var(--text-muted); }
.code .s { color: #7cb342; }
:root:not([data-theme="light"]) .code .s { color: #c3e88d; }
/* ── 数字 ── */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; }
@media (max-width: 860px) { .stats { grid-template-columns: repeat(2, 1fr); } }
.stat {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 1.2rem 1rem;
text-align: center;
}
.stat .n {
font-size: 1.7rem;
font-weight: 700;
letter-spacing: -0.02em;
background: linear-gradient(120deg, var(--brand-blue), var(--brand-cyan));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.stat .l { color: var(--text-muted); font-size: 0.86rem; }
/* ── 插件徽章 ── */
.badges { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.badge {
font-family: var(--font-mono);
font-size: 0.8rem;
padding: 0.32rem 0.65rem;
border-radius: var(--radius-pill);
background: var(--hover);
border: 1px solid var(--border);
color: var(--text-dim);
transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.badge:hover { border-color: rgba(6, 182, 212, 0.45); color: var(--text); }
/* ── 组件角色 ── */
.comp { display: grid; gap: 0.7rem; }
.comp .row {
display: grid;
grid-template-columns: minmax(9rem, auto) 1fr;
gap: 1rem;
padding: 0.75rem 1rem;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
font-size: 0.9rem;
align-items: baseline;
}
.comp .row code {
font-family: var(--font-mono);
font-size: 0.85rem;
color: var(--brand-sky);
}
.comp .row span { color: var(--text-dim); }
@media (max-width: 760px) {
.comp .row { grid-template-columns: 1fr; gap: 0.25rem; }
}
/* ── FAQ ── */
details.faq {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 0.95rem 1.15rem;
margin-bottom: 0.7rem;
}
details.faq summary {
cursor: pointer;
font-weight: 600;
font-size: 0.96rem;
list-style: none;
}
details.faq summary::-webkit-details-marker { display: none; }
details.faq summary::before { content: " "; color: var(--brand-cyan); }
details.faq[open] summary::before { content: " "; }
details.faq p {
margin: 0.7rem 0 0;
color: var(--text-dim);
font-size: 0.92rem;
}
/* ── 页脚 ── */
footer {
border-top: 1px solid var(--border);
padding: 2.4rem 0 3rem;
color: var(--text-muted);
font-size: 0.88rem;
}
footer .cols {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.6rem;
margin-bottom: 1.8rem;
}
@media (max-width: 860px) { footer .cols { grid-template-columns: repeat(2, 1fr); } }
footer h4 { color: var(--text); font-size: 0.9rem; margin: 0 0 0.6rem; }
footer ul { list-style: none; padding: 0; margin: 0; }
footer li { margin-bottom: 0.35rem; }
footer a { color: var(--text-dim); text-decoration: none; }
footer a:hover { color: var(--brand-cyan); }
footer .note {
border-top: 1px solid var(--border);
padding-top: 1.3rem;
line-height: 1.7;
}
a.plain { color: var(--brand-sky); text-decoration: none; }
a.plain:hover { text-decoration: underline; }
.mono { font-family: var(--font-mono); }
.muted { color: var(--text-muted); }
/* 滚入淡入:**默认可见**JS 就绪后才接管。
为什么这样写:若默认 opacity:0则 JS 未执行/抛错/被禁用时整页内容
永久不可见实测30 个 .reveal 里 26 个停在 opacity:0
所以动画必须由 JS 主动加 .js-reveal 才启用 —— 渐进增强,不是降级。 */
.js-reveal {
opacity: 0;
transform: translateY(14px);
transition: opacity 600ms var(--ease), transform 600ms var(--ease);
}
.js-reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
.js-reveal { opacity: 1; transform: none; transition: none; }
}
/* ── 插件:徽章可点,下方显示详情 ── */
.badges {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 1rem;
}
.badge {
/* 重置 button 默认样式 */
appearance: none;
font: inherit;
font-family: var(--font-mono);
font-size: 0.8rem;
padding: 0.34rem 0.7rem;
border-radius: var(--radius-pill);
background: var(--bg-card);
border: 1px solid var(--border);
color: var(--text-dim);
cursor: pointer;
transition: border-color 0.22s var(--ease), color 0.22s var(--ease),
background 0.22s var(--ease), transform 0.22s var(--ease-back),
box-shadow 0.22s var(--ease);
}
.badge:hover {
border-color: var(--border-hi);
color: var(--text);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.badge:focus-visible {
outline: 2px solid var(--brand-sky);
outline-offset: 2px;
}
/* 选中态:淡底 + 主色描边实心渐变太重20 个齐亮像一堵墙) */
.badge[aria-pressed="true"] {
/* 回退:不支持 color-mix 的浏览器给淡青底 */
background: rgba(6, 182, 212, 0.14);
background: color-mix(in srgb, var(--brand-cyan) 12%, var(--bg-card));
border-color: var(--brand-cyan);
color: var(--text);
}
/* 小圆点提示可点 */
.badge::after {
content: "";
display: inline-block;
width: 4px; height: 4px;
margin-left: 0.4rem;
border-radius: 50%;
background: currentColor;
opacity: 0.45;
vertical-align: middle;
transition: opacity 0.22s var(--ease);
}
.badge[aria-pressed="true"]::after { opacity: 1; }
/* 「展开全部」:虚线边框,与具体插件徽章区分 */
.badge.muted {
font-style: italic;
color: var(--text-muted);
border-style: dashed;
}
.badge.muted::after { display: none; }
.pdetail {
margin-top: 1.2rem;
display: grid;
gap: 0.85rem;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
.phint {
grid-column: 1 / -1;
margin: 0;
text-align: center;
color: var(--text-muted);
font-size: 0.88rem;
padding: 0.85rem 0;
border: 1px dashed var(--border);
border-radius: var(--radius-md);
}
.pcard {
display: none;
position: relative;
background: var(--bg-card);
border: 1px solid var(--border-hi);
border-radius: var(--radius-lg);
padding: 1rem 1.15rem;
box-shadow: var(--shadow-md);
}
.pcard.show {
display: block;
animation: pcardIn 0.4s var(--ease-back);
}
@keyframes pcardIn {
from { opacity: 0; transform: translateY(10px) scale(0.98); }
to { opacity: 1; transform: none; }
}
.pcard-head {
display: flex;
align-items: baseline;
gap: 0.6rem;
margin-bottom: 0.5rem;
}
.pcard-head code {
font-size: 0.95rem;
font-weight: 700;
color: var(--brand-cyan);
}
.pcard-head .ver {
font-size: 0.75rem;
color: var(--text-muted);
font-family: var(--font-mono);
margin-left: auto;
}
.pcard p {
margin: 0;
font-size: 0.88rem;
color: var(--text-dim);
line-height: 1.65;
}
/* 源码链接:卡片底部一行,点进对应源码仓路径 */
.psrc {
display: flex;
align-items: center;
gap: 0.4rem;
margin-top: 0.75rem;
padding-top: 0.65rem;
border-top: 1px dashed var(--border);
font-size: 0.76rem;
font-family: var(--font-mono);
color: var(--text-muted);
text-decoration: none;
transition: color 0.2s var(--ease);
}
a.psrc:hover { color: var(--brand-cyan); }
a.psrc:focus-visible {
outline: 2px solid var(--brand-sky);
outline-offset: 2px;
border-radius: var(--radius-sm);
}
.psrc-r {
flex: 0 0 auto;
padding: 0.06rem 0.4rem;
border-radius: var(--radius-sm);
font-size: 0.7rem;
background: rgba(119, 128, 154, 0.16);
color: var(--text-dim);
}
.psrc-p {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* editdoc卡片写 v2.0.0,公开仓只有 v1.0.0 —— 显式标出,不静默指过去 */
.psrc-warn .psrc-r { background: rgba(245, 158, 11, 0.18); color: var(--brand-gold); }
.psrc-note {
margin: 0.4rem 0 0;
font-size: 0.72rem;
line-height: 1.55;
color: var(--brand-gold);
}
.psrc-out {
margin-left: auto;
opacity: 0.55;
transition: transform 0.2s var(--ease), opacity 0.2s var(--ease);
}
a.psrc:hover .psrc-out { opacity: 1; transform: translate(2px, -2px); }
/* 无公开地址:不可点,视觉上也要看得出来 */
.psrc-none {
cursor: default;
opacity: 0.75;
font-style: italic;
}
/* 全部展开时的网格 */
.pdetail.all {
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
.pdetail.all .pcard { display: block; }
.pdetail.all .phint { display: none; }
/* ── 统计卡:补一行小字,避免数字孤零零 ── */
.stat .sm {
display: block;
margin-top: 0.25rem;
font-size: 0.72rem;
color: var(--text-muted);
letter-spacing: 0.01em;
}
.stat .l { line-height: 1.5; }
/* ── 版面精致化:统一节奏与呼吸 ── */
section { padding: 4.5rem 0; }
@media (max-width: 860px) { section { padding: 3rem 0; } }
h2 { margin: 0 0 0.7rem; letter-spacing: -0.01em; }
.sub { max-width: 44rem; }
/* 卡片内边距与标题间距收敛一档 */
.card { padding: 1.4rem 1.5rem; }
.card h3 { margin: 0 0 0.55rem; }
.card p { font-size: 0.9rem; }
/* 组件表:行高收敛、代码列定宽对齐 */
.comp .row { padding: 0.65rem 1rem; }
.comp .row code { min-width: 7.5rem; }
/* 统计卡内边距 */
.stat { padding: 1.25rem 1rem; }
.stat .n { margin-bottom: 0.15rem; }
/* 三层记忆卡收紧 */
.layer .body { padding: 1rem 1.2rem 1rem 0; }
.layer h3 { margin-bottom: 0.3rem; }
.layer p { margin: 0; }
/* 分隔:章节之间给一条极淡的线,强化节奏 */
section + section > .wrap > h2,
section + section > .wrap > .sub {
/* 占位:保持选择器有效,视觉由间距承担 */
}
/* 备案号:公开站点合规要求,需始终可见 */
.beian {
margin: 1rem 0 0;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
font-size: 0.78rem;
color: var(--text-muted);
}
.beian a {
color: var(--text-muted);
text-decoration: none;
border-bottom: 1px solid transparent;
transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.beian a:hover {
color: var(--text-dim);
border-bottom-color: var(--border-hi);
}
.beian:focus-within a { outline: none; }
.beian a:focus-visible {
outline: 2px solid var(--brand-sky);
outline-offset: 2px;
border-radius: 2px;
}
.beian-sep { opacity: 0.45; }
/* 打印:不显示装饰与折叠 */
@media print {
.aurora, #fx, #progress, .badges, .pdetail { display: none !important; }
body { background: #fff; color: #000; }
}
/* ═══════════════════════════════════════════════════
动效层。全部遵守 prefers-reduced-motion见文件末脚本
原则:**内容默认可读**动效只是增强JS 挂了也不影响阅读。
═══════════════════════════════════════════════════ */
/* ── 极光背景:四团光斑各自漂移 ── */
.aurora {
position: fixed;
inset: -20%;
z-index: -2;
pointer-events: none;
filter: blur(38px);
opacity: 0.9;
}
.aurora span {
position: absolute;
display: block;
border-radius: 50%;
will-change: transform;
}
.aurora .a1 {
width: 46vw; height: 46vw; left: 2%; top: -6%;
background: radial-gradient(circle at 35% 35%, var(--aurora-a), transparent 68%);
animation: drift1 26s ease-in-out infinite;
}
.aurora .a2 {
width: 40vw; height: 40vw; right: -4%; top: 4%;
background: radial-gradient(circle at 60% 40%, var(--aurora-b), transparent 68%);
animation: drift2 32s ease-in-out infinite;
}
.aurora .a3 {
width: 44vw; height: 44vw; left: 22%; bottom: -12%;
background: radial-gradient(circle at 50% 50%, var(--aurora-c), transparent 68%);
animation: drift3 38s ease-in-out infinite;
}
.aurora .a4 {
width: 30vw; height: 30vw; right: 14%; bottom: 6%;
background: radial-gradient(circle at 50% 50%, var(--aurora-d), transparent 68%);
animation: drift4 30s ease-in-out infinite;
}
@keyframes drift1 {
0%,100% { transform: translate3d(0,0,0) scale(1); }
33% { transform: translate3d(6vw,4vh,0) scale(1.12); }
66% { transform: translate3d(-3vw,7vh,0) scale(0.94); }
}
@keyframes drift2 {
0%,100% { transform: translate3d(0,0,0) scale(1); }
40% { transform: translate3d(-7vw,5vh,0) scale(1.1); }
70% { transform: translate3d(-2vw,-4vh,0) scale(0.92); }
}
@keyframes drift3 {
0%,100% { transform: translate3d(0,0,0) scale(1); }
50% { transform: translate3d(5vw,-6vh,0) scale(1.15); }
}
@keyframes drift4 {
0%,100% { transform: translate3d(0,0,0) scale(1); }
45% { transform: translate3d(-5vw,-3vh,0) scale(1.08); }
}
/* ── 细网格:给「工程感」打底 ── */
body::after {
content: "";
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
background-image: linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size: 56px 56px;
mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 25%, transparent 78%);
-webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 0%, #000 25%, transparent 78%);
}
/* ── 粒子画布 ── */
#fx {
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
opacity: 0.75;
}
/* ── 顶部滚动进度条(三色渐变) ── */
#progress {
position: fixed;
top: 0; left: 0;
height: 2px;
width: 0%;
z-index: 100;
background: linear-gradient(90deg, var(--brand-blue), var(--brand-cyan) 55%, var(--brand-gold));
box-shadow: 0 0 12px rgba(56, 189, 248, 0.55);
transition: width 90ms linear;
}
/* ── 主题切换按钮 ── */
.theme-btn {
display: inline-grid;
place-items: center;
width: 34px; height: 34px;
border-radius: var(--radius-pill);
border: 1px solid var(--border);
background: var(--bg-soft);
color: var(--text-dim);
cursor: pointer;
padding: 0;
transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
transform var(--dur) var(--ease), background var(--dur) var(--ease);
}
.theme-btn:hover {
color: var(--text);
border-color: var(--border-hi);
transform: rotate(-18deg) scale(1.06);
}
.theme-btn svg { width: 16px; height: 16px; display: block; }
.theme-btn .i-moon { display: none; }
:root[data-theme="light"] .theme-btn .i-moon { display: block; }
:root[data-theme="light"] .theme-btn .i-sun { display: none; }
/* ── 标题与文字的入场:分块上浮 ── */
.js-fx .rise {
opacity: 0;
transform: translateY(22px);
transition: opacity 0.75s var(--ease), transform 0.75s var(--ease);
transition-delay: var(--d, 0ms);
}
.js-fx .rise.in { opacity: 1; transform: none; }
.js-fx .rise-zoom {
opacity: 0; transform: scale(0.93) translateY(14px);
transition: opacity 0.8s var(--ease), transform 0.8s var(--ease-back);
transition-delay: var(--d, 0ms);
}
.js-fx .rise-zoom.in { opacity: 1; transform: none; }
/* ── Hero 大标题:渐变流动 + 描边光 ── */
h1 .grad {
background: linear-gradient(110deg, var(--brand-blue), var(--brand-cyan) 32%,
var(--brand-violet) 58%, var(--brand-gold) 82%, var(--brand-cyan));
background-size: 280% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: gradFlow 9s ease infinite;
}
@keyframes gradFlow {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* 光标扫过标题时的光泽 */
h1 {
position: relative;
text-shadow: 0 0 40px rgba(56, 189, 248, 0.10);
}
/* ── 按钮:光泽扫过 + 悬浮抬升 ── */
.btn { position: relative; overflow: hidden; }
.btn::after {
content: "";
position: absolute;
top: 0; left: -120%;
width: 60%; height: 100%;
background: linear-gradient(100deg, transparent, var(--sheen), transparent);
transform: skewX(-18deg);
transition: left 0.75s var(--ease);
pointer-events: none;
}
.btn:hover::after { left: 130%; }
.btn-primary {
background-size: 180% 100%;
animation: gradFlow 7s ease infinite;
}
.btn-primary:hover { box-shadow: 0 10px 30px rgba(6, 182, 212, 0.42); }
/* ── 卡片:跟随光标的聚光 + 3D 微倾 ── */
.card, .layer, .stat, .comp .row, details.faq {
position: relative;
transform-style: preserve-3d;
}
.card::before, .stat::before {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: radial-gradient(
260px circle at var(--mx, 50%) var(--my, 50%),
var(--spot), transparent 62%);
opacity: 0;
transition: opacity var(--dur) var(--ease);
pointer-events: none;
}
.card:hover::before, .stat:hover::before { opacity: 1; }
.card:hover, .stat:hover {
border-color: var(--border-hi);
box-shadow: var(--shadow-md);
}
/* 图标在悬浮时轻转 */
.card .icon { transition: transform 0.5s var(--ease-back); }
.card:hover .icon { transform: rotate(-8deg) scale(1.12); }
/* ── 三层记忆色条:入场时自上而下「灌注」 ── */
.layer .bar {
transform: scaleY(0);
transform-origin: top center;
transition: transform 0.9s var(--ease) 0.15s;
box-shadow: 0 0 18px currentColor;
}
.layer.in .bar { transform: scaleY(1); }
.layer.l-ctx .bar { color: var(--brand-blue); }
.layer.l-doc .bar { color: var(--brand-cyan); }
.layer.l-gph .bar { color: var(--brand-gold); }
/* ── 流程图的「数据流动」──
光带扫描会盖住节点,改成让节点自己的图标呼吸。 */
.js-fx .flow .n-ico {
animation: nodePulse 4.5s ease-in-out infinite;
}
.js-fx .flow .n:nth-of-type(1) .n-ico { animation-delay: 0s; }
.js-fx .flow .n .n-ico { animation-delay: 0.9s; }
.js-fx .flow .n-mem .n-ico { animation-delay: 1.8s; }
@keyframes nodePulse {
0%, 100% { box-shadow: 0 0 0 0 transparent; }
50% { box-shadow: 0 0 0 4px rgba(56, 189, 248, 0.10); }
}
/* 序号依次点亮,跟着流程走 */
.js-fx .flow .seq-n {
animation: seqPulse 4.5s ease-in-out infinite;
}
.js-fx .flow .seq-item:nth-child(1) .seq-n { animation-delay: 0s; }
.js-fx .flow .seq-item:nth-child(2) .seq-n { animation-delay: 0.35s; }
.js-fx .flow .seq-item:nth-child(3) .seq-n { animation-delay: 0.7s; }
.js-fx .flow .seq-item:nth-child(4) .seq-n { animation-delay: 1.05s; }
.js-fx .flow .seq-item:nth-child(5) .seq-n { animation-delay: 1.4s; }
.js-fx .flow .seq-item:nth-child(6) .seq-n { animation-delay: 1.75s; }
@keyframes seqPulse {
0%, 70%, 100% { border-color: rgba(6, 182, 212, 0.3); color: var(--brand-cyan); }
12% { border-color: var(--brand-cyan); color: var(--text); }
}
/* ── 数字卡片:数值滚动到位 ── */
.stat .n { font-variant-numeric: tabular-nums; }
/* ── 插件徽章:错落浮现 ── */
.js-fx .badge {
opacity: 0;
transform: translateY(10px) scale(0.94);
transition: opacity 0.5s var(--ease), transform 0.5s var(--ease-back);
}
.js-fx .badges.in .badge { opacity: 1; transform: none; }
/* ── 分节标题:渐变下划线展开 ── */
section > .wrap > h2 { position: relative; }
section > .wrap > h2::after {
content: "";
display: block;
width: 62px;
height: 3px;
margin-top: 0.6rem;
border-radius: 3px;
background: linear-gradient(90deg, var(--brand-blue), var(--brand-cyan), var(--brand-gold));
transform: scaleX(0);
transform-origin: left center;
transition: transform 0.9s var(--ease) 0.2s;
}
section > .wrap > h2.in::after { transform: scaleX(1); }
/* ── FAQ 展开:内容淡入 ── */
details.faq[open] p { animation: fadeUp 0.45s var(--ease); }
@keyframes fadeUp {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: none; }
}
/* ── 导航链接:下划线滑入 ── */
nav a.link { position: relative; }
nav a.link::after {
content: "";
position: absolute;
left: 0; bottom: -2px;
width: 100%; height: 2px;
background: linear-gradient(90deg, var(--brand-blue), var(--brand-cyan));
transform: scaleX(0);
transform-origin: right center;
transition: transform var(--dur) var(--ease);
}
nav a.link:hover::after { transform: scaleX(1); transform-origin: left center; }
/* ── 浅色下的微调:光晕别太亮、网格别太淡 ── */
:root[data-theme="light"] .aurora { opacity: 0.62; }
:root[data-theme="light"] #fx { opacity: 0.5; }
:root[data-theme="light"] nav .en,
:root[data-theme="light"] nav a.link { color: var(--text-dim); }
/* 浅色下代码块的光条更淡 */
:root[data-theme="light"] .flow::after {
background: linear-gradient(to bottom, transparent,
rgba(37, 99, 235, 0.07) 45%, rgba(37, 99, 235, 0.12) 50%, transparent);
}
/* ── 统一降级:减少动态效果时全部静止 ── */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
.js-fx .rise, .js-fx .rise-zoom, .js-fx .badge { opacity: 1; transform: none; }
.js-fx .flow .n-ico, .js-fx .flow .seq-n, .lnk::after { animation: none; }
.lnk::after { opacity: 0; }
.layer .bar { transform: scaleY(1); }
section > .wrap > h2::after { transform: scaleX(1); }
#progress { display: none; }
#fx { display: none; }
}
</style>
</head>
<body>
<div id="progress"></div>
<div class="aurora" aria-hidden="true">
<span class="a1"></span><span class="a2"></span>
<span class="a3"></span><span class="a4"></span>
</div>
<canvas id="fx" aria-hidden="true"></canvas>
<header>
<div class="wrap">
<nav>
<span class="brand">
<img src="assets/logo.svg" alt="HomeAgent logo" />
HomeAgent
</span>
<a class="link hide-sm" href="#features">特性</a>
<a class="link hide-sm" href="#memory">记忆</a>
<a class="link hide-sm" href="#architecture">架构</a>
<a class="link hide-sm" href="#quickstart">上手</a>
<a class="link" href="#faq">FAQ</a>
<button class="theme-btn" id="themeToggle" type="button"
aria-label="切换深浅主题" title="切换深浅主题">
<svg class="i-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round">
<circle cx="12" cy="12" r="4.2"/>
<path d="M12 2.5v2.2M12 19.3v2.2M4.2 4.2l1.6 1.6M18.2 18.2l1.6 1.6M2.5 12h2.2M19.3 12h2.2M4.2 19.8l1.6-1.6M18.2 5.8l1.6-1.6"/>
</svg>
<svg class="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 14.5A8.5 8.5 0 1 1 9.5 4a6.8 6.8 0 0 0 10.5 10.5z"/>
</svg>
</button>
</nav>
</div>
</header>
<main>
<!-- ── Hero ── -->
<section class="hero">
<div class="wrap hero-grid">
<div class="hero-copy">
<span class="tagline rise" style="--d:0ms">常驻型个人 Agent 框架</span>
<h1 class="rise" style="--d:80ms">一个<span class="grad">不会忘记</span><br />也不会被一条长任务卡死的管家</h1>
<p class="lede rise" style="--d:180ms">
HomeAgent 是内核 + 插件的双层 Agent 框架。内核只管
<strong>LLM 编排、记忆与调度</strong>;所有外部能力(消息、文件、网络、设备)
都是可独立安装、崩溃不牵连内核的插件。
</p>
<div class="cta rise" style="--d:280ms">
<a class="btn btn-primary" href="#quickstart">快速上手 →</a>
<a class="btn btn-ghost" href="https://gitcode.com/JianFeeeee/HomeAgent">查看源码</a>
</div>
</div>
</div>
</section>
<!-- ── 特性 ── -->
<section id="features">
<div class="wrap">
<h2 class="reveal rise">四个不那么常见的设计决定</h2>
<p class="sub reveal rise" style="--d:90ms">
先定<strong>边界</strong><strong>调度</strong>,再加能力。
</p>
<div class="grid-2">
<div class="card reveal rise" style="--d:0ms">
<div class="icon" style="background: rgba(59,130,246,.14); color: var(--brand-blue)"></div>
<h3>内核零 IO</h3>
<p>
内核不碰任何外部世界。消息、文件、网络、硬件全部由插件经
PluginSDK 实现。于是「内核是否可信」与「某个插件是否安全」
可以分开评估。
</p>
</div>
<div class="card reveal rise" style="--d:90ms">
<div class="icon" style="background: rgba(6,182,212,.14); color: var(--brand-cyan)"></div>
<h3>插件是独立进程</h3>
<p>
插件是普通 Go 二进制,由内核 spawn 为子进程,经 stdio JSON-RPC
(控制)、共享内存段(数据)、事件环(通知)通信。插件崩溃不牵连内核,
换掉 <span class="mono">plugin.bin</span> 即热重载。
</p>
</div>
<div class="card reveal rise" style="--d:180ms">
<div class="icon" style="background: rgba(245,158,11,.14); color: var(--brand-gold)"></div>
<h3>输入是有级别的</h3>
<p>
输入分「排队」与「中断」两类,中断再按紧迫度分 L1L4。
高级可抢占并保存现场同级不抢占。L4 归内核独占
—— 所以「停止」真的能立刻停下。
</p>
</div>
<div class="card reveal rise" style="--d:270ms">
<div class="icon" style="background: rgba(139,92,246,.14); color: var(--brand-violet)"></div>
<h3>忙时有人顶班</h3>
<p>
主 agent 忙长任务时,积压交给临时<strong>分诊助手</strong>
能办的直接办,需要主 agent 的立刻回「忙碌中,请稍候」。
不必干等十几分钟。
</p>
</div>
</div>
</div>
</section>
<!-- ── 三层记忆 ── -->
<section id="memory">
<div class="wrap">
<h2 class="reveal rise">三层记忆:从「刚才说了什么」到「三年前说过什么」</h2>
<p class="sub reveal rise" style="--d:90ms">
不是一个大向量库包打天下,而是三层各司其职、逐层下沉。
蓝 / 青 / 金三色对应的就是这三层。
</p>
<div class="layers">
<div class="layer l-ctx reveal">
<div class="bar"></div>
<div class="body">
<h3><span class="mark"></span> Context<span class="en">工作窗口</span></h3>
<p>
内存中的事件窗口。按与当前输入的相关性排序(预训练词向量 → 余弦相似度,
TF-IDF 回退),最近若干条受保护。低分事件自动下沉。
</p>
</div>
</div>
<div class="layer l-doc reveal">
<div class="bar"></div>
<div class="body">
<h3><span class="mark"></span> Document<span class="en">文件记忆</span></h3>
<p>
JSON + TF-IDF 索引的中间层。支持显式提交与隐式归档,
冷数据下沉后蒸馏成三元组进入图谱。
</p>
</div>
</div>
<div class="layer l-gph reveal">
<div class="bar"></div>
<div class="body">
<h3><span class="mark"></span> Graph<span class="en">图数据库</span></h3>
<p>
SQLite 持久化实体与关系BFS 遍历召回。三元组从对话中蒸馏而来,
越用越准 —— 「几个月前的事还记得」靠的就是这层。
</p>
</div>
</div>
</div>
<div class="grid-3" style="margin-top: 1.2rem">
<div class="card reveal rise" style="--d:360ms">
<h3>媒体也是一等节点</h3>
<p>
图片、音频不是附件,而是三层里的一类节点:内容寻址(相同字节只存一份)。
媒体不单独记引用账本 —— 删掉持有它的记忆块才删内容,与文本块同一套规则。
</p>
</div>
<div class="card reveal rise" style="--d:450ms">
<h3>图片靠自己的向量被检索</h3>
<p>
图像是真正的记忆块,不是「描述文本 + 可淘汰的 blob」。
旧实现靠视觉模型生成的描述当索引,现已弃用 ——
文本标签只留 <span class="mono">[mime digest]</span>,召回走统一空间的图像向量。
</p>
</div>
<div class="card reveal rise" style="--d:540ms">
<h3>统一多模态空间</h3>
<p>
文本与图像落在同一向量空间(默认 Chinese-CLIP
可以用一句话搜图,也可以用图找记忆。
</p>
</div>
</div>
</div>
</section>
<!-- ── 架构 ── -->
<section id="architecture">
<div class="wrap">
<h2 class="reveal rise">一条消息进来之后</h2>
<p class="sub reveal rise" style="--d:90ms">
输入先过调度器,再进处理管道。管道有 7 个阶段钩子,插件可改写或短路。
</p>
<div class="flow reveal rise">
<!-- 输入 -->
<div class="n n-in">
<span class="n-ico"></span>
<span class="n-t">外部输入</span>
<span class="n-s">QQ / WebUI / CLI / 邮件 / 设备</span>
</div>
<div class="lnk" style="--d:0.00s" aria-hidden="true"></div>
<!-- 调度器 -->
<div class="n n-sched">
<span class="n-ico"></span>
<span class="n-t">输入调度器</span>
<span class="n-s">两类别 · 四级中断 L1L4</span>
</div>
<ul class="br" aria-label="调度器的三种去向">
<li class="br-que"><b>同级不抢占</b> → 入队列排队</li>
<li class="br-pre"><b>更高级别</b><i>抢占</i>,现场压入中断栈,稍后恢复</li>
<li class="br-off"><b>忙得太久</b><i>转投</i>临时分诊助手</li>
</ul>
<div class="lnk" style="--d:0.55s" aria-hidden="true"></div>
<!-- 处理管道 -->
<div class="n n-pipe">
<span class="n-ico"></span>
<span class="n-t">处理管道</span>
<span class="n-s">记忆召回 + 人格注入 + LLM 调用 + 工具循环</span>
</div>
<!-- 循环体 -->
<div class="loop">
<span class="loop-tag">每轮复用</span>
<ol class="seq">
<li class="seq-item">
<span class="seq-n">1</span>
<div class="seq-b"><code>pre_action</code>
<em>构建消息与工具表 · 插件可在此短路</em></div>
</li>
<li class="seq-item">
<span class="seq-n">2</span>
<div class="seq-b"><b>LLM 调用</b>
<em>provider 回退 + 瞬时错误重试 · 流式累积</em></div>
</li>
<li class="seq-item">
<span class="seq-n">3</span>
<div class="seq-b"><code>post_action</code>
<em>插件可改写最终文本</em></div>
</li>
<li class="seq-item">
<span class="seq-n">4</span>
<div class="seq-b"><code>before_toolcall</code>
<em>逐个工具执行前 · 可拒</em></div>
</li>
<li class="seq-item">
<span class="seq-n">5</span>
<div class="seq-b"><b>执行工具</b>
<em>临界区:副作用不可回滚</em></div>
</li>
<li class="seq-item">
<span class="seq-n">6</span>
<div class="seq-b"><code>after_toolcall</code>
<em>上下文裁剪 · 相关记忆召回 · 结果入消息</em></div>
</li>
</ol>
<div class="loop-back" aria-hidden="true">
<span>有工具调用则回到 ①</span>
</div>
</div>
<div class="lnk" style="--d:1.10s" aria-hidden="true"></div>
<!-- 三层记忆 -->
<div class="n n-mem">
<span class="n-ico"></span>
<span class="n-t">三层记忆</span>
<span class="n-s">Context → Document → Graph</span>
</div>
<div class="lnk" style="--d:1.65s" aria-hidden="true"></div>
<!-- 输出 -->
<div class="n n-out">
<span class="n-ico"></span>
<span class="n-t">输出通道</span>
<span class="n-s">before_output · after_output · 可寻址到具体 agent</span>
</div>
<p class="flow-note">
<code>on_input</code> 在进管道之前跑;<code>before_output</code> / <code>after_output</code> 在全部轮次结束后跑一次。
</p>
</div>
<h2 class="reveal" style="margin-top: 3rem">核心组件</h2>
<div class="comp reveal rise">
<div class="row"><code>homed</code><span>常驻内核进程LLM 编排、记忆管理、输入调度、插件生命周期</span></div>
<div class="row"><code>PluginSDK</code><span>四通道:注册工具 / 挂阶段钩子 / 订阅事件 / 声明输出通道</span></div>
<div class="row"><code>inputch</code><span>最基本的输入路由单位,可划给某个 agent含驻留子</span></div>
<div class="row"><code>Stage</code><span>7 个处理阶段钩子,插件可改写或短路消息流</span></div>
<div class="row"><code>resident</code><span>驻留子 agent独立调度器 + temp 图记忆,父可查看/收发/压缩/回收</span></div>
<div class="row"><code>hmapdev</code><span>插件工具链:生成工程、构建 .hmap、安装</span></div>
</div>
<div class="stats reveal" style="margin-top: 2rem">
<div class="stat"><div class="n">36</div><div class="l">在跑插件<br /><span class="sm">16 内置 + 20 外部</span></div></div>
<div class="stat"><div class="n">340</div><div class="l">可用工具<br /><span class="sm">实测注册数</span></div></div>
<div class="stat"><div class="n">109k</div><div class="l">Go 代码行<br /><span class="sm">不含第三方</span></div></div>
<div class="stat"><div class="n">9</div><div class="l">LLM 适配器<br /><span class="sm">另有 1 个网关脚本</span></div></div>
</div>
</div>
</section>
<!-- ── 上手 ── -->
<section id="quickstart">
<div class="wrap">
<h2 class="reveal rise">跑起来</h2>
<p class="sub reveal rise" style="--d:90ms">
依赖 Go 1.25+ 与 CGosqlite3。内核需 LinuxWindows 上
<span class="mono">homed</span> 跑在 WSL2 里,仅需构建 <span class="mono">waiter.exe</span>
</p>
<div class="code reveal rise"><span class="c"># 构建守护进程 + CLI</span>
make build build-cli
<span class="c"># 启动内核(数据目录自定)</span>
./build/homed -data /tmp/ha
<span class="c"># 另开一个终端:交互模式,或单条消息</span>
./build/waiter
echo <span class="s">"你好,记住我喜欢喝咖啡"</span> | ./build/waiter</div>
<p class="sub reveal rise" style="--d:90ms; margin-top: 1.2rem">
WebUI 与 API 密钥在 <span class="mono">http://localhost:8080</span> 配置,持久化到 SQLite。
</p>
<h2 class="reveal" style="margin-top: 3rem">开箱可用的插件</h2>
<p class="sub reveal rise" style="--d:90ms">外部插件经 <span class="mono">hmapdev</span> 构建为独立二进制,可单独装卸、热重载。<strong>点任意插件看它能做什么。</strong></p>
<div class="badges reveal">
<button class="badge" type="button" data-plugin="a2a" style="--i:0">a2a</button>
<button class="badge" type="button" data-plugin="acp" style="--i:1">acp</button>
<button class="badge" type="button" data-plugin="bili" style="--i:2">bili</button>
<button class="badge" type="button" data-plugin="browser" style="--i:3">browser</button>
<button class="badge" type="button" data-plugin="calendar" style="--i:4">calendar</button>
<button class="badge" type="button" data-plugin="deepsearch" style="--i:5">deepsearch</button>
<button class="badge" type="button" data-plugin="editdoc" style="--i:6">editdoc</button>
<button class="badge" type="button" data-plugin="homeagent-mail-bridge" style="--i:7">homeagent-mail-bridge</button>
<button class="badge" type="button" data-plugin="mc" style="--i:8">mc</button>
<button class="badge" type="button" data-plugin="memo" style="--i:9">memo</button>
<button class="badge" type="button" data-plugin="music" style="--i:10">music</button>
<button class="badge" type="button" data-plugin="ocr" style="--i:11">ocr</button>
<button class="badge" type="button" data-plugin="plugindev" style="--i:12">plugindev</button>
<button class="badge" type="button" data-plugin="qq" style="--i:13">qq</button>
<button class="badge" type="button" data-plugin="recoverydiag" style="--i:14">recoverydiag</button>
<button class="badge" type="button" data-plugin="rss" style="--i:15">rss</button>
<button class="badge" type="button" data-plugin="sanitizer" style="--i:16">sanitizer</button>
<button class="badge" type="button" data-plugin="vanblog" style="--i:17">vanblog</button>
<button class="badge" type="button" data-plugin="vikunja" style="--i:18">vikunja</button>
<button class="badge" type="button" data-plugin="weather" style="--i:19">weather</button>
<button class="badge muted" type="button" id="expandAll">展开全部</button>
</div>
<div class="pdetail" id="pdetail">
<p class="phint" id="phint">点上方任一插件查看用途</p>
<div class="pcard" id="p-a2a">
<div class="pcard-head"><code>a2a</code><span class="ver">v1.3.1</span></div>
<p>Agent-to-Agent 协议通信插件,支持双向 A2A 通信:可查询其他 Agent 并回复其请求。提供 HTTP 服务端暴露本 Agent 能力。</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/a2a" target="_blank" rel="noopener noreferrer" title="SDK example/a2a"><span class="psrc-r">SDK</span><span class="psrc-p">example/a2a</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-acp">
<div class="pcard-head"><code>acp</code><span class="ver">v1.2.1</span></div>
<p>Agent Client Protocol 通信插件:充当 ACP 服务端接受其他 Agent 的任务请求,同时提供客户端工具向远程 ACP Agent如 opencode发起会话并读取回复</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/acp" target="_blank" rel="noopener noreferrer" title="SDK example/acp"><span class="psrc-r">SDK</span><span class="psrc-p">example/acp</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-bili">
<div class="pcard-head"><code>bili</code><span class="ver">v1.2.0</span></div>
<p>B站视频下载工具基于 yt-dlp 引擎。支持查看视频清晰度列表、指定格式下载、可配置下载目录。</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/bili" target="_blank" rel="noopener noreferrer" title="SDK example/bili"><span class="psrc-r">SDK</span><span class="psrc-p">example/bili</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-browser">
<div class="pcard-head"><code>browser</code><span class="ver">v2.4.1</span></div>
<p>统一浏览器插件搜索、HTTP抓取(quick)、无头渲染(normal)、交互式浏览器(interactive/CDP)</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/browser" target="_blank" rel="noopener noreferrer" title="SDK example/browser"><span class="psrc-r">SDK</span><span class="psrc-p">example/browser</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-calendar">
<div class="pcard-head"><code>calendar</code><span class="ver">v1.1.0</span></div>
<p>日历事件管理,支持提醒和重复事件</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/calendar" target="_blank" rel="noopener noreferrer" title="SDK example/calendar"><span class="psrc-r">SDK</span><span class="psrc-p">example/calendar</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-deepsearch">
<div class="pcard-head"><code>deepsearch</code><span class="ver">v1.1.2</span></div>
<p>为 agent 提供真正的联网信息检索:本地 SearXNG 聚合多引擎(返回标题/URL/摘要/时间),支持新闻、时间范围、指定引擎;并提供网页正文抽取与「搜索+读前K篇」的深检索</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/deepsearch" target="_blank" rel="noopener noreferrer" title="SDK example/deepsearch"><span class="psrc-r">SDK</span><span class="psrc-p">example/deepsearch</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-editdoc">
<div class="pcard-head"><code>editdoc</code><span class="ver">v2.0.0</span></div>
<p>全能办公插件:新建/读取/编辑/转换 docx·xlsx·pptx·md·csv·txt基于 python-docx / openpyxl / python-pptx / soffice / pandoc</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/editdoc" target="_blank" rel="noopener noreferrer" title="SDK example/editdoc"><span class="psrc-r">SDK</span><span class="psrc-p">example/editdoc</span><span class="psrc-out"></span></a>
<p class="psrc-note">公开仓中的 example 仍是 v1.0.0v2.0.0 全能版源码尚未公开。</p>
</div>
<div class="pcard" id="p-homeagent-mail-bridge">
<div class="pcard-head"><code>homeagent-mail-bridge</code><span class="ver">v0.2.2</span></div>
<p>HomeAgent 接入 AgentMail邮件驱动的多智能体协作</p>
<a class="psrc" href="https://gitea.jianfgit.xyz/jianf/MailUI4Agents/src/branch/main/plugins/homeagent-mail-bridge" target="_blank" rel="noopener noreferrer" title="agentmail plugins/homeagent-mail-bridge"><span class="psrc-r">agentmail</span><span class="psrc-p">plugins/homeagent-mail-bridge</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-mc">
<div class="pcard-head"><code>mc</code><span class="ver">v0.1.8</span></div>
<p>把 mineflayer 虚拟 MC 客户端封装为 HomeAgent 的 chan:mc IO 通道(两阶段激活 + 动作工具)</p></span>
<span class="psrc psrc-none" title="该插件源码在本地开发仓,尚无公开地址"><span class="psrc-p">本地开发仓(无公开地址)</span></span>
</div>
<div class="pcard" id="p-memo">
<div class="pcard-head"><code>memo</code><span class="ver">v1.1.0</span></div>
<p>待办与备忘录插件。待办todo_add/todo_complete/todo_list会主动提醒备忘录memo_create/memo_list/memo_delete纯记事不提醒。</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/memo" target="_blank" rel="noopener noreferrer" title="SDK example/memo"><span class="psrc-r">SDK</span><span class="psrc-p">example/memo</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-music">
<div class="pcard-head"><code>music</code><span class="ver">v0.1.0</span></div>
<p>音乐搜索插件,支持搜索歌曲和查看歌词(基于网易云音乐)</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/music" target="_blank" rel="noopener noreferrer" title="SDK example/music"><span class="psrc-r">SDK</span><span class="psrc-p">example/music</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-ocr">
<div class="pcard-head"><code>ocr</code><span class="ver">v1.0.0</span></div>
<p>图片文字识别工具,基于 Tesseract OCR 引擎。支持从 URL 或 base64 图片中提取文字内容。</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/ocr" target="_blank" rel="noopener noreferrer" title="SDK example/ocr"><span class="psrc-r">SDK</span><span class="psrc-p">example/ocr</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-plugindev">
<div class="pcard-head"><code>plugindev</code><span class="ver">v1.0.0</span></div>
<p>把 hmapdev 工具链封装成 Agent 可调用的工具:脚手架生成插件工程、构建打包 .hmap、管理 SDK 版本。配合 plugin_install 即可让 Agent 自己做完「新建插件 → 构建 → 安装」全流程。</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/plugindev" target="_blank" rel="noopener noreferrer" title="SDK example/plugindev"><span class="psrc-r">SDK</span><span class="psrc-p">example/plugindev</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-qq">
<div class="pcard-head"><code>qq</code><span class="ver">v1.4.1</span></div>
<p>QQ 消息收发插件,通过 NapCat 协议桥接</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/qq" target="_blank" rel="noopener noreferrer" title="SDK example/qq"><span class="psrc-r">SDK</span><span class="psrc-p">example/qq</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-recoverydiag">
<div class="pcard-head"><code>recoverydiag</code><span class="ver">v0.2.0</span></div>
<p>快速检查/崩溃取证工具集diag_triage退出码/信号/存活粗分、diag_dbconfig.db 完整性 + LLM 源解析校验、diag_log_scan日志签名命中、diag_deltalast-good 快照 vs 现状 diff、diag_loc正交综合定位。全部返回结论而非原文确定性、不消耗 LLM token供 guard / failback 恢复决策使用。</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/recoverydiag" target="_blank" rel="noopener noreferrer" title="SDK example/recoverydiag"><span class="psrc-r">SDK</span><span class="psrc-p">example/recoverydiag</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-rss">
<div class="pcard-head"><code>rss</code><span class="ver">v1.1.0</span></div>
<p>RSS/Atom 订阅监控插件,自动检测更新并推送通知</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/rss" target="_blank" rel="noopener noreferrer" title="SDK example/rss"><span class="psrc-r">SDK</span><span class="psrc-p">example/rss</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-sanitizer">
<div class="pcard-head"><code>sanitizer</code><span class="ver">v0.1.0</span></div>
<p>清洗 LLM 输出中的工具调用残留(思维泄漏)</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/sanitizer" target="_blank" rel="noopener noreferrer" title="SDK example/sanitizer"><span class="psrc-r">SDK</span><span class="psrc-p">example/sanitizer</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-vanblog">
<div class="pcard-head"><code>vanblog</code><span class="ver">v1.0.0</span></div>
<p>管理 VanBlog 开源博客系统:文章的增删改查、分类标签管理、草稿发布、备份导出等</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/vanblog" target="_blank" rel="noopener noreferrer" title="SDK example/vanblog"><span class="psrc-r">SDK</span><span class="psrc-p">example/vanblog</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-vikunja">
<div class="pcard-head"><code>vikunja</code><span class="ver">v1.0.1</span></div>
<p>Vikunja 待办/任务管理任务增删改查、项目与看板桶、标签、指派、评论、关联、附件、保存筛选器、团队与分享、通知、订阅、Webhook、时间跟踪、数据导入、实例管理并附通用 API 直通工具兜底</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/vikunja" target="_blank" rel="noopener noreferrer" title="SDK example/vikunja"><span class="psrc-r">SDK</span><span class="psrc-p">example/vikunja</span><span class="psrc-out"></span></a>
</div>
<div class="pcard" id="p-weather">
<div class="pcard-head"><code>weather</code><span class="ver">v1.0.0</span></div>
<p>天气查询插件(基于 wttr.in支持实时天气和未来预报</p>
<a class="psrc" href="https://gitcode.com/JianFeeeee/homeagent-sdk/blob/main/example/weather" target="_blank" rel="noopener noreferrer" title="SDK example/weather"><span class="psrc-r">SDK</span><span class="psrc-p">example/weather</span><span class="psrc-out"></span></a>
</div>
</div>
</div>
</section>
<!-- ── FAQ ── -->
<section id="faq">
<div class="wrap">
<h2 class="reveal rise">常见问题</h2>
<p class="sub reveal rise" style="--d:90ms">最容易引起误解的六个地方。</p>
<div class="reveal rise">
<details class="faq">
<summary>「内核零 IO」是不是意味着内核很简单</summary>
<p>
恰恰相反。零 IO 是为了让内核专注做难的部分:输入调度(四级中断、抢占与
现场保存、上下文预算、三层记忆的蒸馏与召回。IO 挪出去之后,这些才有
清晰的所有权。
</p>
</details>
<details class="faq">
<summary>插件崩了会不会把 Agent 带崩?</summary>
<p>
不会。外部插件是独立子进程,与内核只通过 stdio JSON-RPC、共享内存段、
事件环通信。插件崩溃后,内核摘除它注册的工具、阶段与通道,按退避重启,
其余部分不受影响。
</p>
</details>
<details class="faq">
<summary>为什么消息会「排队」而不是立即处理?</summary>
<p>
因为同一时刻只应有一个任务在改上下文。同级抢占会让两边都做一半。
需要立刻插队时用更高级别的中断(如 L4内核与内核级插件
而不是让所有消息都变成抢占者。
</p>
</details>
<details class="faq">
<summary>长任务会不会让用户等很久?</summary>
<p>
这正是「分诊助手」的用途。主 agent 忙超阈值且积压够多时,内核拉起临时
驻留子接手:能办的直接办,需要主 agent 的立刻回「忙碌中,请稍候」。
</p>
</details>
<details class="faq">
<summary>记忆会不会无限膨胀?</summary>
<p>
不会。三层是迁移而非复制:上下文事件裁剪后归档成文档,文档冷掉后蒸馏成
图谱三元组。图谱有去重与 UNIQUE 约束;媒体跟着记忆块走,块被永久删除时才回收字节。
</p>
</details>
<details class="faq">
<summary>能换 LLM 吗?</summary>
<p>
可以。内核不绑定厂商:每个 LLM 源对应一个 Lua 脚本负责请求与响应转换。
内置 9 个适配器OpenAI、Anthropic、Gemini、DeepSeek、Groq、Mistral、
Ollama、GitHub、KimiCode也可自己写一个接任意兼容 API。
</p>
</details>
</div>
</div>
</section>
</main>
<footer>
<div class="wrap">
<div class="cols">
<div>
<h4>文档</h4>
<ul>
<li><a href="https://gitcode.com/JianFeeeee/HomeAgent/blob/main/assets/docs/zh/OVERVIEW.md">项目概览</a></li>
<li><a href="https://gitcode.com/JianFeeeee/HomeAgent/blob/main/assets/docs/zh/ARCHITECTURE.md">技术架构</a></li>
<li><a href="https://gitcode.com/JianFeeeee/HomeAgent/blob/main/assets/docs/zh/PLUGIN_DEV.md">插件开发</a></li>
<li><a href="https://gitcode.com/JianFeeeee/HomeAgent/blob/main/assets/docs/zh/ADAPTER.md">Lua 适配器</a></li>
</ul>
</div>
<div>
<h4>深入</h4>
<ul>
<li><a href="https://gitcode.com/JianFeeeee/HomeAgent/blob/main/docs/zh/input-scheduler-design.md">输入调度器设计</a></li>
<li><a href="https://gitcode.com/JianFeeeee/HomeAgent/blob/main/docs/zh/resident-subagent-design.md">驻留式子 Agent</a></li>
<li><a href="https://gitcode.com/JianFeeeee/HomeAgent/blob/main/docs/zh/multimodal-space.md">统一多模态空间</a></li>
</ul>
</div>
<div>
<h4>项目</h4>
<ul>
<li><a href="https://gitcode.com/JianFeeeee/HomeAgent">源码仓库</a></li>
<li><a href="https://gitcode.com/JianFeeeee/HomeAgent/releases">发布下载</a></li>
<li><a href="https://gitcode.com/JianFeeeee/homeagent-sdk">插件 SDK</a></li>
</ul>
</div>
<div>
<h4>状态</h4>
<ul>
<li><span class="muted">最新发布 v1.3.x 线</span></li>
<li><span class="muted">main 在研 1.4.0</span></li>
<li><span class="muted">许可 AGPL-3.0-only</span></li>
</ul>
</div>
</div>
<div class="note">
<p style="margin: 0 0 0.4rem">
<strong>AI 辅助声明</strong>:本项目代码、文档与提交历史含 AI 生成或修改的内容。
关键改动经人工审阅,使用前请自行评估验证。
</p>
<p style="margin: 0">
AGPL-3.0-only。通过 HTTP 提供交互式服务时须向使用者提供完整对应源码§13
</p>
<p class="beian">
<a href="https://beian.miit.gov.cn" target="_blank" rel="noopener noreferrer">豫ICP备2024074105号-1</a>
<span class="beian-sep">·</span>
<a href="https://beian.mps.gov.cn" target="_blank" rel="noopener noreferrer">豫公网安备41070202001579号</a>
</p>
</div>
</div>
</footer>
<script>
/* ═══════════════════════════════════════════════════════════
动效引擎。设计原则(吃过一次亏,见 git 历史):
· **内容默认可读** —— 不用 JS 也完整;只有本脚本真的跑起来
才给 <html> 加 .js-fx 接管初始隐藏,因此 JS 报错/被禁用
时页面不会变成一片空白(早期版本把 opacity:0 写在默认
样式里,结果 30 个元素里 26 个永久不可见)。
· 尊重 prefers-reduced-motion此时不动、不加 .js-fx
· 所有 listener 走 rAF 节流;粒子数按视口面积自适应。
═══════════════════════════════════════════════════════════ */
(function () {
var root = document.documentElement;
var reduce = window.matchMedia &&
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
/* ── 主题切换 ── */
var toggle = document.getElementById("themeToggle");
function paint(isLight) {
root.setAttribute("data-theme", isLight ? "light" : "dark");
}
if (toggle) {
toggle.addEventListener("click", function () {
var isLight = root.getAttribute("data-theme") === "light";
paint(!isLight);
try { localStorage.setItem("ha-theme", !isLight ? "light" : "dark"); } catch (e) {}
});
}
// 系统主题变化时跟随(用户没手动选过才跟)
try {
var mq = window.matchMedia("(prefers-color-scheme: light)");
if (mq.addEventListener) {
mq.addEventListener("change", function (e) {
if (!localStorage.getItem("ha-theme")) paint(e.matches);
});
}
} catch (e) {}
/* ── 滚动进度条 ── */
var bar = document.getElementById("progress");
var ticking = false;
function onScroll() {
if (ticking) return;
ticking = true;
requestAnimationFrame(function () {
if (bar) {
var h = document.documentElement.scrollHeight - window.innerHeight;
bar.style.width = (h > 0 ? (window.scrollY / h) * 100 : 0) + "%";
}
ticking = false;
});
}
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
/* ── 入场动画 ── */
if (!reduce) {
root.classList.add("js-fx");
var els = [].slice.call(document.querySelectorAll(
".rise, .rise-zoom, .reveal, section > .wrap > h2, .badges"));
function reveal(el) {
el.classList.add("in");
if (el.classList.contains("badges")) {
var bs = el.querySelectorAll(".badge");
for (var i = 0; i < bs.length; i++) {
bs[i].style.transitionDelay = (i * 28) + "ms";
}
}
}
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) { reveal(e.target); io.unobserve(e.target); }
});
}, { threshold: 0.1, rootMargin: "0px 0px -6% 0px" });
els.forEach(function (el) { io.observe(el); });
} else {
els.forEach(reveal);
}
// 保险:布局因图片/字体迟到而变化时,已在视口内的补上
window.addEventListener("load", function () {
els.forEach(function (el) {
var r = el.getBoundingClientRect();
if (r.top < window.innerHeight && r.bottom > 0) reveal(el);
});
});
// 兜底3 秒后无论 observer 是否生效,强制全部可见
setTimeout(function () { els.forEach(reveal); }, 3000);
}
/* ── 数字滚动 ── */
var stats = [].slice.call(document.querySelectorAll(".stat .n"));
function runCount(el) {
var raw = el.textContent.trim();
var m = raw.match(/^([\d.]+)(.*)$/);
if (!m) return;
var target = parseFloat(m[1]), suffix = m[2], dur = 1100, t0 = null;
// 小数位按原值保持(如 34.6
var dec = (m[1].split(".")[1] || "").length;
function step(ts) {
if (!t0) t0 = ts;
var p = Math.min((ts - t0) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = (target * eased).toFixed(dec) + suffix;
if (p < 1) requestAnimationFrame(step);
else el.textContent = raw;
}
requestAnimationFrame(step);
}
if (!reduce && "IntersectionObserver" in window && stats.length) {
var sio = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) { runCount(e.target); sio.unobserve(e.target); }
});
}, { threshold: 0.6 });
stats.forEach(function (el) { sio.observe(el); });
}
// 无 IO / reduce 时保留静态文本,不做数字动画
/* ── 卡片聚光 + 3D 微倾(仅精确指针;触屏不做) ── */
var fine = window.matchMedia && window.matchMedia("(hover: hover) and (pointer: fine)").matches;
if (!reduce && fine) {
var cards = [].slice.call(document.querySelectorAll(".card, .stat"));
cards.forEach(function (c) {
var raf = 0;
c.addEventListener("mousemove", function (ev) {
if (raf) return;
raf = requestAnimationFrame(function () {
var r = c.getBoundingClientRect();
var x = ev.clientX - r.left, y = ev.clientY - r.top;
c.style.setProperty("--mx", x + "px");
c.style.setProperty("--my", y + "px");
// 轻微倾斜:以中心为原点,最多 ±3.2°
var rx = ((y / r.height) - 0.5) * -4.2;
var ry = ((x / r.width) - 0.5) * 4.2;
c.style.transform =
"perspective(760px) rotateX(" + rx.toFixed(2) + "deg) rotateY(" +
ry.toFixed(2) + "deg) translateY(-2px)";
raf = 0;
});
});
c.addEventListener("mouseleave", function () {
if (raf) { cancelAnimationFrame(raf); raf = 0; }
c.style.transform = "";
});
});
}
/* ── 插件徽章:点击展开详情(可多开;「全部」按钮切全展开) ── */
(function () {
var badges = [].slice.call(document.querySelectorAll(".badge[data-plugin]"));
var hint = document.getElementById("phint");
var detail = document.getElementById("pdetail");
if (!badges.length || !detail) return;
function sync(btn) {
var id = "p-" + btn.dataset.plugin;
var card = document.getElementById(id);
var on = btn.getAttribute("aria-pressed") === "true";
if (card) card.classList.toggle("show", on);
}
badges.forEach(function (b) {
b.setAttribute("aria-pressed", "false");
b.addEventListener("click", function () {
var on = b.getAttribute("aria-pressed") === "true";
b.setAttribute("aria-pressed", on ? "false" : "true");
sync(b);
// 有任何一张打开就藏起提示
var any = badges.some(function (x) {
return x.getAttribute("aria-pressed") === "true";
});
if (hint) hint.style.display = any ? "none" : "";
// 首次打开时把详情滚进视口
if (!on) {
var c = document.getElementById("p-" + b.dataset.plugin);
if (c && c.getBoundingClientRect().top > window.innerHeight - 120) {
c.scrollIntoView({ behavior: reduce ? "auto" : "smooth", block: "center" });
}
}
});
});
// 「…共 N 个」按钮:一次全展开
var all = document.querySelector(".badge.muted");
if (all && !all.dataset.bound) {
all.dataset.bound = "1";
var expanded = false;
var total = badges.length;
all.setAttribute("role", "button");
all.setAttribute("tabindex", "0");
all.textContent = "展开全部 " + total + " 个";
function toggleAll() {
expanded = !expanded;
detail.classList.toggle("all", expanded);
badges.forEach(function (b) {
b.setAttribute("aria-pressed", expanded ? "true" : "false");
sync(b);
});
all.textContent = expanded ? "收起" : "展开全部 " + total + " 个";
if (hint) hint.style.display = expanded ? "none" : "";
}
all.addEventListener("click", toggleAll);
all.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); toggleAll(); }
});
}
})();
/* ── 粒子网络 ── */
var cv = document.getElementById("fx");
if (!reduce && cv && cv.getContext) {
var ctx = cv.getContext("2d");
var dpr = Math.min(window.devicePixelRatio || 1, 2);
var W = 0, H = 0, pts = [], rgb = "148, 190, 255", alive = true;
function themeColor() {
rgb = getComputedStyle(root).getPropertyValue("--particle").trim() ||
"148, 190, 255";
}
function resize() {
/* 用 clientWidth 而不是 innerWidth后者含滚动条宽度实测 390 vs 375
画布按 390 铺开会多出 15px 并把整页撑出横向滚动条。 */
var de = document.documentElement;
W = de.clientWidth; H = de.clientHeight;
cv.width = Math.round(W * dpr); cv.height = Math.round(H * dpr);
cv.style.width = W + "px"; cv.style.height = H + "px";
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
// 密度按面积自适应,上限 72再多就开始吃 CPU 了)
var n = Math.min(72, Math.round((W * H) / 26000));
pts = [];
for (var i = 0; i < n; i++) {
pts.push({
x: Math.random() * W, y: Math.random() * H,
vx: (Math.random() - 0.5) * 0.24,
vy: (Math.random() - 0.5) * 0.24,
r: Math.random() * 1.5 + 0.6
});
}
}
function frame() {
if (!alive) return;
ctx.clearRect(0, 0, W, H);
for (var i = 0; i < pts.length; i++) {
var p = pts[i];
p.x += p.vx; p.y += p.vy;
if (p.x < -20) p.x = W + 20; else if (p.x > W + 20) p.x = -20;
if (p.y < -20) p.y = H + 20; else if (p.y > H + 20) p.y = -20;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, 6.2832);
ctx.fillStyle = "rgba(" + rgb + ", 0.5)";
ctx.fill();
}
// 近邻连线:距离 < 130 才连,透明度线性衰减
for (var a = 0; a < pts.length; a++) {
for (var b = a + 1; b < pts.length; b++) {
var dx = pts[a].x - pts[b].x, dy = pts[a].y - pts[b].y;
var d2 = dx * dx + dy * dy;
if (d2 < 16900) {
var o = (1 - Math.sqrt(d2) / 130) * 0.22;
ctx.strokeStyle = "rgba(" + rgb + ", " + o.toFixed(3) + ")";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(pts[a].x, pts[a].y);
ctx.lineTo(pts[b].x, pts[b].y);
ctx.stroke();
}
}
}
requestAnimationFrame(frame);
}
themeColor(); resize(); frame();
var rraf = 0;
window.addEventListener("resize", function () {
if (rraf) return;
rraf = requestAnimationFrame(function () { resize(); rraf = 0; });
});
// 切主题时换粒子颜色
if (toggle) toggle.addEventListener("click", function () { setTimeout(themeColor, 30); });
// 标签页不可见时暂停(省电;回来再继续)
document.addEventListener("visibilitychange", function () {
if (document.hidden) { alive = false; }
else if (!alive) { alive = true; frame(); }
});
}
})();
</script>
</body>
</html>