跳转到内容

Peep 命理工作台

社区案例 #2

Peep 是一站式国学命理工作台 —— 人物档案、文档管理、紫微斗数、大六壬、六爻、知识库六大模块深度整合。我们通过把 rtc-agent 作为 Web Component 嵌入 React 应用,让 AI 直接操作业务 API:

  • 🧑 “创建张三的人物档案” → 调用 PersonCreate,自动跳转详情页
  • 🔮 “排张三的紫微盘,看今年的流年” → 调用 ZiWei / GetScopeData,展示排盘结果
  • 🎲 “起一卦,问最近适不适合跳槽” → 调用 LiuYaoCreate,展示卦象
  • 📚 “写一篇关于紫微命宫的学习笔记” → 调用 WikiCreate,跳转编辑页
  • 📅 “今天农历是多少?” → 调用 SolarToLunar,返回农历日期

AI 不只是聊天 —— 它能 读取数据、调用函数、操作界面。

上方视频(4x 倍速)展示了用户用自然语言与 AI 交互,完成人物创建、排盘、起卦、文档管理的完整流程。

Peep 命理工作台界面截图

截图展示 Peep 主界面:左侧 5 份为紫微斗数主内容区,右侧 3 份为嵌入式 <rtc-agent> AI 助手面板。

如果说 Mermaid Live Editor 展示了最简悬浮窗接入,Peep 则展示了生产级嵌入式集成:

特性Mermaid Live EditorPeep
框架SvelteReact 19
窗口模式悬浮窗(左下角最小化)嵌入式(5:3 双栏布局)
鉴权方式默认(rtc-agent 自带)自定义 AuthProvider
业务复杂度单一编辑器6 大业务域、40+ 函数
安全机制无写操作确认机制
主题同步Svelte $effect轮询 + API 同步
语言同步无zh-CN / en-US 切换

💡 rtc-agent 的接入方式非常灵活 —— 从最简单的悬浮窗到生产级嵌入式集成,都可以用同一套组件实现。

┌─────────────────────────────────────────────────────────────────┐
│ Peep (React 19 + Vite 8) │
│ │
│ ┌────────────────────────────┐ ┌──────────────────────────┐ │
│ │ │ │ │ │
│ │ 主内容区(5 份) │ │ AI 助手(3 份) │ │
│ │ │ │ ┌────────────────────┐ │ │
│ │ 🧭 紫微斗数 / 大六壬 / │ │ │ <rtc-agent> │ │ │
│ │ 🎲 六爻 / 📚 知识库 │ │ │ Web Component │ │ │
│ │ │ │ │ 嵌入式最大化 │ │ │
│ │ │ │ │ 无关闭/最小化 │ │ │
│ │ │ │ └────────────────────┘ │ │
│ │ │ │ │ │
│ └────────────────────────────┘ └──────────────────────────┘ │
│ ▲ │ │
│ │ peepApi() 内部通道 │ │
│ └──────────────────────────────┘ │
│ (AI 调用业务 API) │
└─────────────────────────────────────────────────────────────────┘

关键设计决策:

决策原因
嵌入式而非悬浮窗AI 助手是应用的核心组成部分,不是可选插件
5:3 固定比例保证排盘/文档主区有足够空间,同时 AI 面板也足够宽敞
禁用拖拽/缩放/最小化嵌入式模式下不需要窗口操作,AI 始终可见
自定义 AuthProvider完全委托给 peep 的认证系统,共享登录状态
写操作确认机制防止 AI 误操作造成不可逆的数据变更

整个接入通过 npm 安装 @rtc-agent/component,核心改动集中在 3 个文件:

文件改动说明
src/core/rtcAgent.ts创建 rtc-agent 实例配置嵌入式模式、AuthProvider、函数分组
src/core/rtcAgent/*.ts按业务域定义 40+ 函数Zod Schema 驱动,自动生成 OpenAPI 文档
src/App.tsx挂载到 5:3 布局右侧slot.replaceChildren(agent)

AI 面板作为应用的原生组成部分,不是浮窗:

// App.tsx — 全局布局
<div className="rtc-layout">
<div className="rtc-layout-main">
{/* 主内容区:紫微斗数 / 大六壬 / 六爻 / 知识库 */}
<BrowserRouter basename="/peep">
<Layout>
<Routes>...</Routes>
</Layout>
</BrowserRouter>
</div>
<aside className="rtc-layout-side" aria-label="AI 助手">
<div ref={rtcSlotRef} id="rtc-slot" />
</aside>
</div>
/* 5:3 双栏布局 */
.rtc-layout {
display: grid;
grid-template-columns: 5fr 3fr;
height: 100vh;
}

使用 createRtcAgent 工厂函数,配置嵌入式最大化模式和自定义 AuthProvider:

src/core/rtcAgent.ts
import { createRtcAgent } from '@rtc-agent/component';
import type { RtcAgentWithLifecycle } from '@rtc-agent/component';
export function createPeepRtcAgent(): RtcAgentWithLifecycle {
return createRtcAgent({
appLabel: '窥见人生 · 命理 AI 助手',
agentName: 'PeepAstro',
agentDescription: '紫微斗数 · 大六壬 · 六爻 · 知识库 —— 命理分析 AI 助手',
persona: PERSONA_ZH, // 或 PERSONA_EN,根据语言切换
// 嵌入式模式配置
window: {
embedded: true,
defaultMode: 'maximized',
draggable: false,
resizable: false,
showMinimize: false,
showMaximize: false,
showClose: false,
},
// 自定义 AuthProvider(模式 3)
auth: {
getToken: async () => {
const tokens = getTokens();
if (!tokens) throw new Error('Not authenticated');
return tokens.access_token;
},
refreshToken: async () => {
const tokens = getTokens();
const newTokens = await refreshAccessToken(tokens.refresh_token);
return {
accessToken: newTokens.access_token,
refreshToken: newTokens.refresh_token,
expiresIn: Math.floor((newTokens.expires_at - Date.now()) / 1000),
};
},
isLoggedIn: () => {
const tokens = getTokens();
if (!tokens) return false;
return !isTokenExpired(tokens, 5 * 60 * 1000);
},
logout: async () => {
clearTokens();
},
getUserId: () => {
const tokens = getTokens();
if (!tokens) throw new Error('Not authenticated');
return tokens.user_id;
},
deviceId: getOrCreateDeviceIdSync(),
},
// 业务函数分组(6 组,40+ 函数)
groups: FUNCTION_GROUPS,
scenariosURL: `${import.meta.env.BASE_URL}scenarios/`,
workerURL: `${import.meta.env.BASE_URL}rtc-agent/shared-worker.js`,
server: {
url: 'https://rtc-agent.cherish.chat',
redirectUri: `${import.meta.env.BASE_URL}auth/callback.html`,
},
theme: getTheme(),
lang: document.documentElement.lang as 'zh-CN' | 'en-US',
databaseName: 'peep-rtc',
});
}

rtc-agent 的核心设计是 “Agent 不碰你的源码,只通过声明式 API 操作状态”。Peep 用 Zod Schema 驱动函数定义,自动生成 OpenAPI 文档供 AI 理解:

src/core/rtcAgent/personFunctions.ts
import { z, withMeta } from '@rtc-agent/component';
export const personCreateFunction = {
name: 'PersonCreate',
description:
'创建新的命主人物档案并自动切换为该人物。' +
'示例调用:PersonCreate({ name: "张三", date: "1990-05-15", timeIndex: 4, gender: "男" })',
zodSchema: z.object({
name: withMeta(z.string(), { example: '张三' }).describe('姓名'),
date: withMeta(z.string(), { example: '1990-05-15' })
.describe('公历出生日期,格式 YYYY-MM-DD'),
timeIndex: withMeta(z.number().int().min(0).max(12), { example: 4 })
.describe('时辰索引 0-12,对应早子时(0)到晚子时(12)'),
gender: withMeta(z.enum(['男', '女']), { example: '男' }).describe('性别'),
calendar: withMeta(z.enum(['solar', 'lunar']), { example: 'solar' })
.optional().describe('历法类型,默认 solar(公历)'),
confirmed: CONFIRM_FIELD, // 写操作确认标志
}),
handler: createConfirmHandler(schema, (p) => peepApi().PersonCreate(p)),
returns: {
zodSchema: z.object({
id: z.number().describe('人物 ID'),
name: z.string().describe('姓名'),
// ... 其他字段
}),
},
};

6 组业务函数:

分组函数数能力
person6命主档案 CRUD(List / Get / Create / Update / Delete / SetDefault)
ziwei3紫微斗数排盘(ZiWei / GetScopeData / SetHoroscopeTime)
daliuren7大六壬起课(Create / List / View / Delete / BatchView / UpdateTags / UpdateNote)
liuyao7六爻起卦(Create / List / View / Delete / BatchView / UpdateTags / UpdateNote)
wiki7知识库文档(List / View / Create / Update / BatchView / ReplaceContent / InsertContent)
lunar10日历计算(SolarToLunar / LunarToSolar / GetEightCharacters / GetSolarTerms 等)

对 Create / Update / Delete 等破坏性操作,Peep 实现了两步确认机制,防止 AI 误操作:

src/core/rtcAgent/shared.ts
export const CONFIRM_FIELD = withMeta(z.boolean(), { example: true })
.optional()
.describe(
'⚠️ 安全确认标志:首次调用时不传此字段,函数会返回操作摘要供用户确认;' +
'用户确认后,AI 再次调用并传入 confirmed: true 才会真正执行。'
);
export function createConfirmHandler(schema, handler) {
return async (params) => {
// 首次调用:未传 confirmed,返回操作摘要
if (!params.confirmed) {
return {
needsConfirm: true,
summary: `即将创建人物「${params.name}」,出生日期 ${params.date}`,
};
}
// 二次调用:已确认,真正执行
return handler(params);
};
}

工作流程:

  1. AI 首次调用 PersonCreate({ name: '张三', ... })
  2. Handler 检测到 confirmed 未传,返回 { needsConfirm: true, summary: '...' }
  3. AI 向用户展示操作摘要,等待确认
  4. 用户确认后,AI 再次调用 PersonCreate({ ..., confirmed: true })
  5. Handler 检测到 confirmed: true,真正执行创建操作

💡 这样即使 AI 幻觉或 prompt 注入,也不会直接执行不可逆操作。

Peep 支持明暗双主题和中英双语,需要与 rtc-agent 保持同步:

// 主题同步:轮询检查(500ms 间隔)
export function startThemeSync(): () => void {
let lastTheme = getTheme();
const timer = setInterval(() => {
const current = getTheme();
if (current !== lastTheme) {
lastTheme = current;
syncTheme(); // 设置 agent.theme
}
}, 500);
return () => clearInterval(timer);
}
// 语言同步:通过 API 切换
export async function syncLocale(locale: Locale): Promise<void> {
if (!_agent) return;
await switchLocale(locale); // 触发 @lit/localize 重新渲染
}
// App.tsx — 启动同步
useEffect(() => {
const agent = createPeepRtcAgent();
slot.replaceChildren(agent);
const cleanupThemeSync = startThemeSync();
return () => {
cleanupThemeSync();
agent.destroy();
};
}, []);
// 语言切换时同步
const { locale } = useI18n();
useEffect(() => {
syncLocale(locale);
}, [locale]);
层技术
框架React 19 · TypeScript 6 · Vite 8
样式Tailwind CSS 4 · 明暗双主题
组件Radix UI · shadcn/ui · Lucide Icons
数据Dexie (IndexedDB) · Zustand
命理引擎iztro · lunar-lite · lunar-typescript
AI 集成rtc-agent (npm) · Zod Schema
编辑器ByteMD (Markdown + GFM)
测试Playwright E2E
部署GitHub Pages

除了上述核心文件,还涉及:

文件用途
src/core/rtcAgent/*.ts按业务域拆分的函数定义(person / ziwei / daliuren / liuyao / wiki / lunar)
src/core/auth/AuthContext.tsx自定义 AuthProvider,rtc-agent 完全委托
public/scenarios/预置场景模板 + manifest
public/auth/callback.htmlOAuth 回调页

整个接入 不修改 rtc-agent 任何代码,@rtc-agent/component 通过 npm 安装,打包进宿主 bundle。

PineappleBond/peep

在线体验:pineapplebond.github.io/peep