Skip to content

02. 基础脚手架搭建

目标

搭建一个清晰、可扩展且符合现代工程化规范的 TypeScript Monorepo 结构。它将作为我们后续所有核心 Harness、内置工具和外接通道的基础基座。


最终目录结构

我们将使用 pnpm workspace 来管理多个子包,整体目录结构规划如下:

text
hachimi/
├── packages/
│   ├── core/                 # 核心 Harness (与接入渠道完全解耦)
│   │   ├── src/
│   │   │   ├── agent/        # Agent Loop 循环与 Provider
│   │   │   ├── memory/       # 分层 MemoryManager
│   │   │   ├── session/      # SessionManager
│   │   │   ├── skills/       # SkillRegistry 技能系统
│   │   │   ├── tools/        # ToolRegistry 工具核心
│   │   │   └── types/        # 核心 TS 类型定义
│   │   └── package.json
│   ├── shared/               # 跨模块公共工具包 (如 generateId)
│   │   ├── src/
│   │   └── package.json
│   ├── channels/             # 接入渠道包 (如 Web, Telegram, Slack 适配器 - 预留)
│   ├── tools-builtin/        # 内置基础工具包 (如文件读写、网页剪裁 - 预留)
│   └── storage/              # 存储中间层 (SQLite, PostgreSQL - 预留)
├── apps/
│   ├── cli/                  # CLI 命令行交互客户端
│   ├── desktop/              # 桌面端 (Tauri/Electron - 预留)
│   └── server/               # REST API 服务器 (预留)
├── scripts/                  # 辅助开发/测试脚本
├── package.json              # 根目录 package.json
├── pnpm-workspace.yaml       # pnpm 独有的多包定义
└── tsconfig.json             # 根目录全局 TS 配置

初始化项目

首先,在你的 workspace 目录中创建项目文件夹并初始化:

bash
mkdir hachimi
cd hachimi
pnpm init

1. 根目录 package.json 配置

修改根目录下的 package.json,使其作为一个私有的 monorepo 根节点,并引入全局开发依赖。请注意,我们强制使用 ESM ("type": "module"):

json
{
  "name": "hachimi",
  "private": true,
  "type": "module",
  "scripts": {
    "typecheck": "tsc --noEmit"
  },
  "devDependencies": {
    "typescript": "^5.8.2",
    "@types/node": "^22.13.10"
  }
}

2. 定义 pnpm-workspace.yaml

在根目录下创建 pnpm-workspace.yaml,指定子包和应用的放置范围,使 pnpm 能识别本地多包链接:

yaml
packages:
  - 'packages/*'
  - 'packages/channels/*'
  - 'packages/tools-builtin/*'
  - 'packages/storage/*'
  - 'apps/*'

创建子包

接下来,我们将创建两个最基础的子包:@hachimi/shared@hachimi/core

1. 公共工具包:packages/shared

这个子包存放没有任何外部依赖的底层纯函数工具。

bash
mkdir -p packages/shared/src

创建 packages/shared/package.json

json
{
  "name": "@hachimi/shared",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "main": "./src/index.ts",
  "types": "./src/index.ts",
  "exports": {
    ".": "./src/index.ts"
  }
}

packages/shared/src/index.ts 中编写我们第一个辅助函数(后续用于生成消息 ID 与会话 ID):

ts
/**
 * 生成带前缀的唯一标识符
 */
export function generateId(prefix = ""): string {
  return `${prefix}${crypto.randomUUID()}`;
}

/**
 * 获取当前高精度时间戳
 */
export function now(): number {
  return Date.now();
}

2. 核心架构包:packages/core

这是项目的绝对心脏,存放 Agent 所有的业务逻辑模型。

bash
mkdir -p packages/core/src/{agent,memory,tools,skills,session,permissions,hooks,types}

创建 packages/core/package.json,并声明对本地公共包 @hachimi/shared 的依赖。我们使用 workspace:* 告诉 pnpm 引用本地源码而不是去 npm 线上拉取:

json
{
  "name": "@hachimi/core",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "main": "./src/index.ts",
  "types": "./src/index.ts",
  "exports": {
    ".": "./src/index.ts"
  },
  "dependencies": {
    "@hachimi/shared": "workspace:*"
  }
}

TypeScript 全局配置

为了让 TypeScript 能够完美处理 ESM 导入、支持绝对路径别名解析,并在多包之间自由跳转开发,我们需要精细化配置根目录的 tsconfig.json

json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "outDir": "dist",
    "baseUrl": ".",
    "paths": {
      "@hachimi/core": ["packages/core/src"],
      "@hachimi/shared": ["packages/shared/src"]
    }
  },
  "include": ["packages/**/*", "apps/**/*", "scripts/**/*"]
}

IMPORTANT

这里的 "module": "NodeNext""moduleResolution": "NodeNext" 至关重要。它们强制约束了我们在导入本地文件时,必须写明扩展名(例如 import { Agent } from "./agent.js"),这符合现代 Node.js 的原生 ESM 加载标准,能有效防止打包后的各种运行时模块未找到错误。

在子包中(如 packages/core/tsconfig.json),我们只需继承根配置即可:

json
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "composite": true,
    "rootDir": "src"
  },
  "include": ["src/**/*"]
}

为什么采用这种结构?

  1. 完全解耦核心(Core)与接入渠道(Channels):这是最核心的工程决策。@hachimi/core 纯粹由输入输出驱动,任何环境都可以轻量级地调用它。
  2. 本地包热更新:借由 pnpm workspace 与 TS 的 paths 别名配置,你在修改 @hachimi/shared 里的代码时,@hachimi/core 能够实时生效,无需重复执行编译与打包命令。
  3. 清晰的职责拆分:随着项目演进,你随时可以在 packages/tools-builtin 目录中追加第三方依赖(如 cheerio、sharp),而不必污染 core 的极简运行时。

本章总结

在本章中,我们:

  • 使用 pnpm Workspace 完成了多包 monorepo 基础配置。
  • 引入并配置了严格支持 ESM 与绝对路径别名导入的 TypeScript 全局规则。
  • 划分了核心包与共享工具包,写下了第一个本地包依赖关系。

基础脚手架已经稳固。在下一章,我们将实现第一个里程碑:最小可用核心