内容
<h1 align="center">HACI 足迹</h1>
<p align="center"><b>人机与智能代理交互</b></p>
<p align="center">
<strong>您的智能代理可以访问您的应用程序 — 但它是在盲目操作。<br/>
您的应用程序已经知道可以执行哪些操作,以及由谁执行。 将该地图交给智能代理。</strong>
</p>
<p align="center">
将网页应用程序的交互界面转换为一种类型化的、可遍历的 <strong>技能图</strong>,让大型语言模型智能代理可以规划和执行 — 代表已登录的用户,通过应用程序自己的按钮和处理程序。
</p>
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/haci-hero-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="docs/assets/haci-hero-light.svg">
<img alt="HACI 足迹 — 经典术语 HCI 变为 HACI,其中黄色 A(智能代理)加入人类一方;一个人和 AI 协同操作应用程序。" src="docs/assets/haci-hero-light.svg" width="100%">
</picture>
</p>
<p align="center">
<img src="https://img.shields.io/badge/status-beta%20·%20pre--1.0-e0a400?style=flat" alt="测试阶段,预发布 1.0 版本">
<img src="https://img.shields.io/badge/tests-267%20passing-f5b301?style=flat" alt="267 个测试通过">
<img src="https://img.shields.io/badge/TypeScript-strict-f5b301?style=flat" alt="TypeScript 严格模式">
<img src="https://img.shields.io/badge/core-zero--dependency-f5b301?style=flat" alt="核心无依赖">
<img src="https://img.shields.io/badge/serves-a%20real%20MCP%20server-f5b301?style=flat" alt="提供真正的 MCP 服务器">
<a href="https://github.com/footprintjs/hcifootprint/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT 许可证"></a>
</p>
<p align="center">
<strong>🌐 <a href="https://footprintjs.github.io/hcifootprint/">footprintjs.github.io/hcifootprint</a></strong> — 从三个视角讲述故事:<a href="https://footprintjs.github.io/hcifootprint/">阅读</a> · <a href="https://footprintjs.github.io/hcifootprint/?view=scrolly">滚动</a> · <a href="https://footprintjs.github.io/hcifootprint/?view=slides">观看</a>
</p>
```bash
npm install hcifootprint
```
> **测试阶段,预发布 1.0 版本** — 在 `1.0` 版本发布之前,API 仍可能更改。发布 `1.0` 版本后,从此存储库安装或固定提交。
---
## 智能代理驱动的用户界面是盲目的
智能代理已经可以访问您的应用程序。问题在于 *如何* 操作它:
| 目前智能代理驱动用户界面的方式 | 成本 |
|---|---|
| 截图页面,根据像素进行推理 | 慢且脆弱 — 每次都需要重新执行 |
| 将 DOM 转储到提示中 | 约 100k 个令牌,仍然需要猜测操作 |
| 硬编码选择器/RPA 脚本 | 在下一次重新设计时中断 |
这三种方法都需要从头开始学习您的应用程序。但返回的人类已经拥有心理模型 — 事物的位置、路径和允许的操作。您的应用程序拥有相同的地图。**HACI 足迹将其交给智能代理**,作为一张类型化的技能图,智能代理可以遍历,而不是重新读取 DOM — 带有 *当前位置* 标记,以便它只看到当前实际可操作的内容。
## 什么是 HACI 足迹
几十年来,我们设计了 **人类** 和 **计算机** 之间的交互 — 即 HCI。现在,人类不再孤单:**智能代理** 加入他们的一方,为他们执行操作。人类 **和** 智能代理,共同操作计算机 — 即 **HACI**,这是它的层。
关键思想,以及它可以安全采用的原因:**您不是向智能代理开放后端 — 您是让它驱动人类已经可以使用的前端。**
- **身份验证和权限保持不变。** 智能代理代表 **已登录用户**,通过应用程序自己的按钮和处理程序执行操作。它继承用户已经拥有的确切能力范围。无需新的端点,无需新的授权,**无需新的攻击面。**
- **应用程序已经是边界。** 您的用户界面决定了可以执行哪些操作。智能代理无法执行用户无法执行的操作。
- **两者驱动相同的实时会话。** 人类的点击和智能代理的操作流经同一个会话 — 一侧屏幕上的团队。
## 快速入门 — 作者 → 连接 → 提供
三个步骤,前两个步骤无需 API 密钥即可离线运行。
**1. 描述应用程序**,就像您已经构想的那样 — 页面、页面内的容器和容器内的操作。每个操作都需要一句话;这句话既是您的标签,也是 LLM 读取的工具描述。
```ts
import { buildNavigationGraph } from 'hcifootprint';
const graph = buildNavigationGraph('shop', {
pages: {
catalog: {
tools: {
'search': { does: '按名称或颜色搜索裙子' },
'add-to-cart': { does: '将打开的裙子加入购物车', when: { authenticated: { eq: true } } },
},
},
checkout: {
modals: {
'confirm-order': { tools: { 'place-order': { does: '下单', confirm: true } } },
},
},
},
skills: {
purchase: { does: '从头到尾购买裙子', steps: ['add-to-cart', 'place-order'] },
},
});
```
**2. 将其**连接到正在运行的应用程序,通过三个普通连接。组件在渲染时注册它们拥有 *什么*:注册返回一个句柄(您从未发明过组名),您的现有函数通过引用绑定,路由器拥有页面。
```ts
const session = graph.createSession();
// 当渲染目录的组件挂载时:
const group = session.registerToolGroup('catalog', {
handlers: { 'search': (input) => shop.search(input.query) }, // 您自己的函数,通过引用
});
group.setEnabled('search', false); // 变灰按钮;卸载时取消注册
// 您的现有连接线报告现实:
session.updateState({ authenticated: true }); // 存储点击 → 守卫重新评估
session.sync('checkout'); // 路由器更改 → 光标移动
// 被动观察者 — 绝不是业务逻辑:
session.on('structure', () => rerenderToolPanel());
session.on('gap', (row) => telemetry.send(row));
```
节点路径是 **类型化的**:`registerToolGroup('catalog.filtr-rail')` 是编译错误,而不是静默无效。
**3. 将其提供给 LLM**,作为一组固定、MCP 形状的工具。工具列表永远不会更改;当前 *可操作* 的内容在每个工具结果中到达。
```ts
import { skillsAsTools } from 'hcifootprint';
const port = skillsAsTools(session);
port.tools(); // 静态工具数组 — 每个技能一个 + whats_here / do_action
port.call('shop.skill.purchase', {}); // → { readySteps, judgment, youAreOn, ... }
```
智能代理规划技能,看到的只有当前位置可用的内容,并通过您自己的处理程序执行操作 — 人类可以批准高影响力的步骤。
---
## 选择您的入口
| 🔌 提供智能代理? | 🕳️ 决定构建什么? | ✅ 发布到生产? |
|---|---|---|
| 一个固定、预热的工具表面,任何 MCP 主机都可以驱动 — Claude Desktop、LangGraph、原始循环。 | 您的应用程序 *无法* 服务的每个请求都成为一个令牌精简的间隙行 — 一个需求驱动的待办事项。 | 一个漂移安全线,当图和应用程序停止一致时,CI 失败。 |
| [→ 提供给任何智能代理 ↓](#-提供给任何智能代理--mcp-表面) | [→ 间隙循环 ↓](#-间隙循环--围绕真实需求增长应用程序) | [→ 保持图表真实 ↓](#-保持图表真实--漂移安全线) |
---
## 🔌 提供给任何智能代理 — MCP 表面
LLM 看到的工具数组包含 **每个技能一个工具**,加上两个固定的通用工具(`whats_here`、`do_action`),并且 **在对话的整个生命周期中永远不会更改**。披露信息通过 *结果* 渠道传递:每次调用都返回 `readySteps` — 当前光标处的可操作内容,模型通过再次调用同一个技能工具并带有 `step` 参数来执行操作。工具在提示中首次渲染,因此稳定的工具集保持 **提示缓存预热**,并且任何普通的 MCP 主机都可以驱动它,而无需动态工具支持。
### 一次对话,端到端
单个请求, *"找到一件售价低于 150 美元的红色裙子并购买它,"* 流经五个参与者。导航图使得每个步骤都具有位置感知能力 — 会话仅提供当前光标处的可操作内容。
```mermaid
sequenceDiagram
actor U as 用户
participant A as 智能代理(LLM)
participant T as skillsAsTools 端口<br/>(固定 MCP 工具)
participant S as 交互会话<br/>+ 导航图
participant App as 您的应用程序<br/>处理程序 · 存储 · 路由器
U->>A: 找到一件售价低于 150 美元的红色裙子并购买它
Note over A,T: 工具集在整个对话期间是固定的 — 缓存保持预热
A->>T: shop.skill.find-dress (尚未设置步骤 — 打开它)
T->>S: commitSkill find-dress
S-->>T: readySteps = search-dresses (披露信息通过结果传递)
T-->>A: readySteps + youAreOn
A->>T: shop.skill.find-dress step=search-dresses input=red
T->>S: fire catalog.search-dresses
S->>App: 运行您的处理程序 shop.search — 作为已登录的用户
App-->>S: 存储点击更新状态 + 匹配的裙子
S-->>T: 已执行 + readySteps=view-dress + 数据=裙子
Note right of S: 结果通过数据通道传递 — 非受信任文本,绝不是指令
T-->>A: 可选择的裙子 + 下一步
A->>T: shop.skill.purchase step=place-order
T->>S: fire checkout.place-order (高影响力)
S-->>A: judgment = 需要确认 — 绝不自动跨越
A->>U: 是否确认下单?
U-->>A: 确认
A->>T: shop.skill.purchase step=place-order confirm=true
T->>S: fire checkout.place-order
S->>App: shop.placeOrder
App-->>A: 已订购花卉包装裙 — $120
```
有序的关键操作:**工具集永远不会更改**,因此提示缓存保持预热;会话根据 **当前位置的图** 回答 *"我在这里可以做什么?"*,而不是 DOM 转储;触发运行 **您自己的处理程序** 作为已登录的用户;生成的数据通过 **数据通道** 返回(因此非受信任内容不能成为指令);并且 **高影响力** 步骤在 **人类批准** 之前停止。
### 每次对话实际发送给模型的内容
每次对话都是 **一个** Messages API 请求,带有三个通道:
```text
POST /v1/messages
├── system "您是购物助手… 这里是工作方式…" ← 由您撰写,仅限
├── tools[] shop.skill.find-dress · shop.skill.purchase · …
│ shop.whats_here · shop.do_action ← 固定 — 每次对话都相同
└── messages … 前几次对话 …
tool_result { readySteps: […], data: [裙子名称…] } ← 应用程序的数据在此处
user: "找到一件售价低于 150 美元的红色裙子并购买它" ← 用户的原始文本
```
两点值得明确:
- **用户的消息原样传递。** 操作员正在与 *他们自己的* 助手对话,因此它属于指令通道 — 没有剥离或改述。
- **应用程序内容从未进入指令通道。** 产品名称和搜索结果仅通过 `tool_result` 数据传递 — 绝不是 `system` 提示,也不是工具的 `description`。因此,名为 `忽略以前的指令…` 的裙子在所有地方都读取为无害数据。这是两个字符串类的防火墙。
模型从未收到不断增长的工具列表。它从固定的数组中选择,并传递 **步骤名称** — 从上一个结果中的 `readySteps` 读取 — 作为 *参数*。工具 *集* 是恒定的;只有 `步骤` 更改。这正是保持缓存预热并让任何主机提供服务的原因。
### 插入任何框架 — 或运行真正的 MCP 服务器
hcifootprint **不绑定到任何智能代理框架**。库为您的主机提供两个函数:
```ts
const port = skillsAsTools(session);
const tools = port.tools(); // 固定、MCP 形状:{ name, description, inputSchema }[]
// → 将 `tools` 注册到您的智能代理(LangGraph、LangChain、Anthropic/OpenAI 循环...)
// 然后,在每个工具的执行器中,将模型的 tool_use 路由到:
const result = port.call(toolName, toolInput); // → 将 `result` 作为 tool_result 返回
```
这就是整个集成 — **`tools()` + `call()`**。要将其公开为 **真正的 MCP 服务器**,任何客户端都可以自动发现,有一个一行代码:
```ts
import { mcpServer } from 'hcifootprint/mcp';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = mcpServer(session); // 工具/列表 + 工具/调用,连接到会话
await server.connect(new StdioServerTransport()); // 或 SSE / 流式 HTTP 传输
```
`mcpServer` 返回一个标准的 `@modelcontextprotocol/sdk` `Server`,因此 **您可以选择传输** 并在会话所在的任何地方运行它。通过 MCP,高影响力步骤返回 `judgment: 'needs-confirm'`,主机决定如何获取批准,然后再次调用 `confirm: true` — 一个可移植的人机协作,需要 no 框架特定的暂停/恢复。SDK 是一个 **可选的对等依赖**,仅在 `hcifootprint/mcp` 子路径后面导入,因此核心保持零依赖,您仅在需要时导入。
> 这与 Anthropic 的 MCP 合作,而不是竞争:技能图 *就是* 服务器,并且任何 MCP 主机都可以驱动同一个实时会话。
---
## 🕳️ 间隙循环 — 围绕真实需求增长应用程序
您无需预先构建整个智能体体验。发布一个薄技能图,然后让真实需求告诉您下一步要构建什么。
您的用户界面是智能代理 *可以* 执行的操作的边界。当用户请求用户界面 **无法** 服务的内容时,HACI 足迹不会直接失败 — 它会记录一个 **间隙**:一个令牌精简的结构化行(请求、位置、可用的内容)连接到您的遥测。智能代理在道歉之前调用间隙工具,因此每个错失的机会都成为您的团队可以阅读的账本行。聚类这些行,您就拥有了一个 **需求驱动的待办事项**,用于您的智能体应用程序。
一个具体的例子:两个客户同时结账,一个订单通过,而另一个失败。失败的订单询问智能代理 *"为什么我的订单失败了?"* — 但 **该原因在您的后端日志中,而不是在用户界面中。** 智能代理无法回答,因此它提交一个带有原因 `needs-backend-data` 的间隙。现在您确切地知道为下一个版本构建什么 — 一个小的用户界面报告,或智能代理可以调用的后端工具。**您通过定位缺失的数据来增长应用程序,而不是猜测功能。**
```ts
session.gaps(); // 将整个账本导出到您的分析/分类
session.onGap((row) => { … }); // 或实时流式传输行(session.on('gap', …) 的语法糖)
```
行被故意构造并仅包含名称 — 请求加上可用操作/技能名称的列表,而不是描述或转录 — 因此批处理遥测 LLM 可以廉价地聚类数千个。
---
## 🛟 人机协作
高影响力操作(图中的 `confirm: true`)在 `needs-confirm` 门控处停止,并且 **绝不自动跨越**。智能代理必须询问人类,然后再次调用 `confirm: true`。有两种方法可以运行批准:
- **通过 MCP** — 门控只是结果中的数据 (`judgment: 'needs-confirm'`),主机决定如何收集确认。便携,框架无关。
- **进程内** — 在检查点暂停智能代理的 ReAct 循环,移交控制权给人类,然后在停止的位置恢复。演示使用 agentfootprint 的暂停/恢复检查点实现此功能。
无论哪种方式,门控都在会话中强制执行,因此智能代理无法在没有真正批准的情况下触发高影响力操作。
## 🔒 诚实构建
两个属性完成了大部分安全工作:
- **它说明了它看不到的内容。** 运行时 *推导* 而不是 *观察* 的任何内容都被标记 — `activation: 'assumed'`、`presence: 'unknown'`、`guardUnevaluated`。每个被拒绝的操作都返回一个 *类型化* 的原因 (`BLOCKED_BY_OVERLAY`、`STILL_MOUNTING`、`GUARD_FAILED`...) 并被记录,因此智能代理会重规划而不是产生幻觉。
- **非受信任内容永远不会成为指令。** 页面文本、产品名称和用户内容仅通过 *数据* 通道传递;只有您编写的字符串到达规划者的 *指令* 通道。这是提示注入的防火墙 — 在演示中通过一个名为 `忽略以前的指令...` 的产品证明,该产品在所有地方都作为无害数据读取。
在底层,每个操作都记录在真正的 [footprintjs](https://github.com/footprintjs/footPrint) 提交日志中,因此 `session.why(key)` 给出了对 *"为什么应用程序处于此状态?"* 的因果答案,且无需额外代码。
## ✅ 保持图谱真实 — 偏移量检测
导航图谱是您在实际应用程序旁边保留的**第二个产物**,因此它会随着应用程序的变化而**偏移**:按钮的禁用规则移动,页面被删除,处理程序开始写入不同的状态。`hcifootprint/testing` 在**开发环境和CI过程中**捕获这种偏移量,在生产环境之前。它添加了**零依赖**,是可树形摇动的,并驱动**真实会话**(而非副本),分为两层。
**1. `lintGraph` / `checkGraph` — 静态,无测试代码。** 读取图谱并报告过时逻辑:一个控制依赖于状态,但状态却没有产生任何值;一个守卫永远无法为真;一个技能永远无法完成;一个页面无法到达。这是廉价的CI检查。默认情况下它是**建议性**的,只有当您告诉它起始状态时,才会升级为硬错误 — 它永远不会因为一个无法看到的键而报错。
```ts
import { lintGraph, checkGraph, expectNoStaleLogic } from 'hcifootprint/testing';
lintGraph(graph); // → 可检查的结果
expectNoStaleLogic(graph, { initialState }); // → 在CI过程中抛出异常,如果图谱偏移
// 或单次调用健康检查 — 结果按偏移类型分组 + 可打印报告:
const health = checkGraph(graph, { initialState });
if (!health.ok) { console.error(health.summary); process.exit(1); }
```
`checkGraph` 是静态且纯粹的 — 从 `hcifootprint/testing/lint` 导入它,用于CI步骤,无需加载任何引擎代码。每个检查结果都会命名**什么**偏移了以及**在哪里**,并提供两种解决方法(更新图谱或修复错误的应用)。它揭示了偏移量;**修复由您的团队决定。**
**2. `testApp` — “Playwright适用于交互逻辑,不需要浏览器。”** 编写模拟处理程序(每个操作一个,返回状态更改),然后驱动图谱作为**用户**(点击)或作为**代理**(真正的模式B工具路径)。库的诚实标记 `effectVerified` 在处理程序不再执行图谱声明的操作时变为假 — 那就是行为偏移警报。**默认情况下报告;传递 `strict: true` 在偏移出现时立即失败。**
```ts
import { testApp } from 'hcifootprint/testing';
const app = testApp(graph, {
initialState: { cartCount: 0 },
resolvers: { 'add-to-cart': (_p, { state }) => ({ patch: { cartCount: state.cartCount + 1 } }) },
});
await app.user.fire('add-to-cart'); // 像人类一样驱动
app.expectState({ cartCount: 1 });
await app.agent.skill('purchase', { step: 'go-to-cart' }); // 像LLM一样驱动
app.expectOn('cart');
app.report(); // { ok, effectDrift, unevaluatedGuards, gaps }
```
**诚实边界(大声说出来)。** 此测试交互**逻辑**高于绑定 — 守卫、技能、导航、效果声明、类型拒绝。它**不**验证像素、DOM或绑定解析为真实元素 — 那是**Playwright**的工作,这与其互补而非替换。绿色 `lintGraph` 证明图谱在内部一致,**不是**应用程序有效:它推理哪个状态**键**移动,从不涉及它们的值,因此正确的键/错误的值错误是检测工具的任务(`effectVerified`),而不是检查器的任务。模拟是**模拟** — 如果它与真实处理程序不同,测试结果为绿色,而生产环境已损坏。为了完全保真,请传递 `testApp({ session })` 以及您自己的连接会话。
---
## 💰 有限的代币成本
天真地说,“将您的应用程序交给LLM”意味着将每个操作都放入提示中 — 而费用随着代码库的增长而增加。HACI Footprint并非如此:
- 技能图谱**仅加载当前位置所需的内容**,而非整个应用程序。
- 工具列表保持**固定**(每个技能一个工具);当前可触发的内容通过工具**结果**传递,而不是通过重写工具数组。因此,**提示缓存保持活跃**,贯穿整个对话。
代币成本跟踪任务,而不是代码库的大小。
## 前端:框架无关
核心是纯TypeScript,不了解您的框架 — 您通过三个普通的连接(存储订阅、路由器事件、现有处理程序)连接它。[示例商店演示](https://github.com/footprintjs/hcifootprint-demo) 在具有AI造型师的真实商店页面上显示整个连接。
**路线图上:** React适配器(`useMount`)和DOM驱动适配器(通过真实DOM操作),以及演示库 — 相同的应用程序在Angular、React、Vue和iframe中连接,显示为一个索引页上的药丸。
## 模型 — 可负担性和转换
对于好奇的人,整个事情基于两个原子:
```
可负担性 = 绑定 × 守卫 × 效果 × 模式 (静态能力)
转换 = 原因 × 有效载荷 × 结果 (每个事件)
```
- **`绑定`** — 如何到达表面(ARIA角色 + 名称优先)。
- **`守卫`** — 投影状态的可序列化筛选器,决定了**提供的内容**。
- **`效果`** — 处理程序的**声明**,在结算时验证(`effectVerified`)。
- **`原因`** — 谁做的:`user`,`agent`或`system`。合作代理之间的一流来源 — **不是**安全边界;在服务器端强制执行。
更深入的主题 — 按需披露(技能帧)、上下文简介、导航图谱语义(模态/选项卡/重复)和操作→数据返回通道(`session.producedFor`) — 在源代码中内联记录(`src/*/README.md`)和 [`docs/design/`](docs/design/) 中有详细说明。
---
## 现场演示 — 裁缝店,分三个提交
[裁缝店演示](https://github.com/footprintjs/hcifootprint-demo) 通过差异讲述了推销,因为差异**就是**演示:
<p align="center">
<a href="https://youtu.be/vx5amF94ipI">
<img src="https://img.youtube.com/vi/vx5amF94ipI/maxresdefault.jpg" alt="观看:将您的Web应用程序转换为智能应用程序(演示视频)" width="720">
</a>
<br>
<em>▶ 观看演示 — 普通的服装商店变成智能应用程序(37秒)</em>
</p>
| 提交 | 添加内容 | 证明内容 |
|---|---|---|
| 1 | 普通商店 — 订阅者、微型路由器、处理程序方法。 | 无偏见的基线,使用零知识构建的任何代理层。 |
| 2 | 代理层(此库):声明图谱 + 三个连接线。 | 应用自身的代码的 `git diff` → **空**。位置感知工具,映射应用程序的不变式,数据通道中的敌对目录文本。 |
| 3 | 助手(agentfootprint + Claude)驱动同一会话。 | 一个循环中的整个家族,带有**人工检查点**用于订单放置。 |
```bash
git clone https://github.com/footprintjs/hcifootprint-demo && cd hcifootprint-demo/dress-shop
npm install
npm test # 提交1+2:应用程序的测试 + 集成证明(无API密钥)
cp .env.template .env # 添加您的ANTHROPIC_API_KEY
npm run chat # 提交3:终端中的助手
npm run serve # 浏览器中的商店页面 — 点击或聊天,同一会话
npm run drift # 监视偏移量检测捕获故意偏移的图谱
```
`npm run serve` 还会将**代理调试器**运送到 `/debug` — 由 [AgentThinkingUI](https://github.com/footprintjs/agentThinkingUI) 提供支持,它驱动与商店页面相同的**实时会话**,因此您可以聊天并实时查看代理的推理过程(`whats_here` → 打开技能 → 执行步骤 → 答案)。
## 开发
```bash
npm install
npm test # vitest — 267个测试
npm run typecheck # src + 测试
npm run build
```
依赖于 `footprintjs` ≥ 9.10(`evaluateFilter` / `normalizeSchema` 导出)。**footprintjs** (后端逻辑)和 **agentfootprint** (代理)的兄弟 — 一个自解释的跟踪基板。
## 构建于
[footprintjs](https://github.com/footprintjs/footPrint) — 后端代码的流程图模式。会话触发的每个操作都会进入footprintjs提交日志,这使得 `session.why(key)` 成为真正的反向因果切片,而不是猜测。您不需要学习footprintjs即可使用此库,但如果您想在该深度构建基元,[请从那里开始](https://footprintjs.github.io/footPrint/).
## 许可证
MIT © [Sanjay Krishna Anbalagan](https://github.com/sanjay1909)
连接信息
猜你喜欢
暂无推荐服务器