npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

fraq-plugin-chatsalt

v0.16.0

Published

通用 AI 对话插件

Readme

fraq-plugin-chatsalt

通用 AI 对话插件

安装与配置

将插件添加到 fraq.yml 的 plugins 字段下:

plugins:
  # 本插件依赖 fraqjs/ai 和 fraqjs/kysely 插件,因此需要一并安装

  fraqjs/ai:
    # 在这里传入 AiPlugin 的配置选项

  fraqjs/kysely:
    # 在这里传入 KyselyPlugin 的配置选项

  # 推荐配合 fraqjs/message-store 插件使用,以便在数据库中持久化消息,减少远程拉取消息上下文的次数
  fraqjs/message-store:
    # 在这里传入 MessageStorePlugin 的配置选项

  # 可选,安装后 chatsalt 会自动注册 WebUI,未安装时不影响对话功能
  fraqjs/webui-gateway:
    # 在这里传入 WebuiGateway 的配置选项

  chatsalt:
    # 角色设定文本,这里表示从 persona.md 文件中读取,见下方「编写人设」一节
    persona: ${{ text:persona.md }}
    # 主人(管理员)列表
    owners: [10001, 10002]
    # 消息中包含这些关键词时触发对话
    trigger:
      keywords: [salt, 机器人]

以上就是一个可以运行的配置,未列出的选项都会使用默认值。完整选项见下方「配置参考」一节。

安装并配置 fraqjs/webui-gateway 后,可通过 /webui/chatsalt/ 查看对话、警告和记忆记录。WebUI 由 Gateway 统一鉴权;如果未安装 Gateway,Chatsalt 不会注册任何 WebUI 路由。

配置参考

以下选项都写在 chatsalt 字段下,嵌套的选项用 . 表示层级,例如 memory.enabled 对应:

chatsalt:
  memory:
    enabled: true

表情和 @(richText)、自动模式(auto)的配置分别见对应章节。

基础

| 选项 | 默认值 | 说明 | | --- | --- | --- | | persona | 必填 | 角色设定文本,会在每次对话开始时注入上下文。推荐使用 Markdown 格式,并用 ${{ text:persona.md }} 从另一个文件读取,以保持配置文件可读性 | | chatModel | ctx.ai.model() | 用于对话的语言模型,可以指定在 fraqjs/ai 插件中配置的模型名称或别名,例如 google/gemini-3.5-flash | | visionModel | 同 chatModel | 用于图像识别的语言模型,取值同上 | | owners | [] | 主人(管理员)QQ 号列表,可用于执行部分命令,但不会将相关信息注入提示词 | | temperature | 0.8 | 对话的温度参数 | | maxToolSteps | 10 | 调用工具时最多允许的步骤数 | | trigger.keywords | [] | 触发对话的关键词列表,消息中包含任一关键词即触发对话 |

上下文

| 选项 | 默认值 | 说明 | | --- | --- | --- | | contextWindow | 20 | 上下文窗口大小,即提供给大模型的最少消息数 | | contextStep | 10 | 上下文窗口起点的移动步长,见下文 | | maxForwardDepth | 0 | 上下文中包含合并转发消息时最多展开的层数,0 表示不展开 |

窗口起点每隔 contextStep 条消息才向后移动一次,期间的对话只在上下文末尾追加消息,便于命中模型提供商的提示词缓存。因此实际提供的消息数在 contextWindow 到 contextWindow + contextStep - 1 之间浮动(撤回等缺失的消息不计入)。设为 1 则每条新消息都会移动窗口起点,即固定提供 contextWindow 条消息,但几乎无法命中缓存。

记忆

| 选项 | 默认值 | 说明 | | --- | --- | --- | | memory.enabled | true | 是否启用记忆功能 | | memory.maxWindow | 20 | 在对话中最多注入的记忆条数 | | memory.maxScopeCount | 50 | 每个对话场景最多允许的记忆条数 |

工具

| 选项 | 默认值 | 说明 | | --- | --- | --- | | builtinWebSearch.enabled | false | 是否启用模型内置的网页搜索工具,见下文 | | externalWebSearch.enabled | false | 是否启用外部网页搜索工具,即由另一个模型执行搜索 | | externalWebSearch.model | 同 chatModel | 执行外部网页搜索的模型,同样只支持下文列出的三种提供商 | | webPage.enabled | false | 是否允许模型读取上下文中明确提供的 HTTP 或 HTTPS URL | | webPage.timeoutMs | 10000 | 单次请求超时时间(毫秒) | | webPage.maxResponseBytes | 2097152(2 MiB) | 最大响应正文大小 | | webPage.maxContentLength | 20000 | 返回给模型的最大正文字符数 | | webPage.maxRedirects | 3 | 最大重定向次数,每次重定向都会重新校验目标地址 | | github.enabled | false | 是否启用 GitHub 工具 | | github.token | — | GitHub 访问令牌,启用 GitHub 工具时必填,例如 ${{ env:GITHUB_TOKEN }} |

模型内置网页搜索目前仅支持以下 AI SDK 提供商的模型:@ai-sdk/anthropic、@ai-sdk/google、@ai-sdk/openai。启用前请确认你使用的模型本身支持网页搜索工具,否则可能导致模型无法正常工作。

网页读取支持 HTML、JSON、XML、Markdown、CSV 和纯文本;XML 只作为文本读取,不会解析外部实体。

WebUI

| 选项 | 默认值 | 说明 | | --- | --- | --- | | webui.enabled | true | 是否注册 WebUI(需要安装 fraqjs/webui-gateway) | | webui.conversationLimit | 100 | 内存中最多保留的对话记录数 | | webui.warningLimit | 100 | 内存中最多保留的警告记录数 | | webui.memoryLimit | 500 | WebUI 最多查询的记忆条数 | | webui.autoDecisionLimit | 300 | 内存中最多保留的自动判断记录数 |

编写人设

角色设定文本是指为大模型提供的角色设定信息,通常包括但不限于以下内容:

  • 身份:姓名、年龄、性别、职业等基本信息
  • 性格:是否开朗/内向、是否善于表达、是否有幽默感等
  • 语言风格:口头禅、口癖、常用词汇等,可以包含例句
  • 背景故事:家庭、成长经历、教育背景、人际关系、兴趣爱好等
  • 知识边界:对于什么样的话题应该回答/给出模糊回答/拒绝回答等
  • 互动目标:在角色视角看来,用户的身份、场景、目的等

一个好的角色设定文本可以让大模型更好地生成更符合预期的回答。仓库中包含一个示例角色设定文本,可以作为参考。

开始对话

通过在消息中 @ 机器人、回复机器人的消息或使用触发关键词来开始对话。机器人会根据角色设定文本和上下文信息生成回答。例如,对于上面的示例角色设定文本中的角色【纱露朵】,一个可能的对话是这样的:

用户: @纱露朵 你好呀~
纱露朵: 你好呀,我是纱露朵,请多关照的说喵~

表情和 @

启用 richText 后,模型可以在回复中写类似 CQ 码的 milky 码,发送前会被转换成对应的 Milky 消息段:

| milky 码 | 效果 | | --- | --- | | [milky:face-表情ID] | 发送一个 QQ 表情,例如 [milky:face-264] 是「捂脸」 | | [milky:mention-QQ号] | @ 某人,例如 [milky:mention-10001] |

用户: @纱露朵 我又寸了
纱露朵: 下次一定能过的喵[milky:face-111]

转换规则:

  • 表情可以使用插件内置表情表中的任意 ID。recommendedFaces 只决定提示词里推荐哪些表情,并不限制模型发送其他表情,比如它在上下文里看到的表情。
  • @ 只在群聊中有效,并且只能 @ 当前上下文中出现过的人(发过消息或被 @ 过);不能 @ 机器人自己,也不能 @ 全体成员。
  • 格式错误、表情 ID 不存在或不允许的 milky 码会被直接删除,不会原样发到群里,同时在 WebUI 中记录一条警告。如果删除后回复为空,则不发送。

| 选项 | 默认值 | 说明 | | --- | --- | --- | | richText.enabled | true | 是否允许模型在回复中发送 QQ 表情和 @ | | richText.recommendedFaces | [111, 479, 478, 264, 181, 277, 344, 297] | 在提示词中推荐给模型的表情 ID 列表,默认依次为可怜、不对不对、对的对的、捂脸、戳一戳、汪汪、大怨种、拜谢。填写不存在的表情 ID 会在启动时报错 |

自动模式(实验性)

启用后,机器人会在白名单群里自己判断是否开口,不需要 @。判断可使用 TypeSafe 的 System One API 或 OpenAI 的 Decisions API,只返回分类、概率和评分。

机器人只会在三种情况下开口:

  1. 被叫到:没有 @,但叫了 names 中的名字并且在跟机器人说话。
  2. 接话:机器人刚说过话,有人没引用就接着说。
  3. 主动插话:最近几条消息中,有一条的类型在 interject 里(默认是提问和分享成就),话题属于 expertise,并且不是别人之间的私聊。这种情况受 budget 配额限制。

此外:

  • 群里刷屏时,机器人会等安静下来再判断,同一批消息最多回复一条。
  • 敏感话题一律沉默。如果有人嫌机器人烦,自动模式会在该群静音一段时间。
  • 对话模型仍可以输出 no_reply (理由) 放弃发言,理由会显示在 WebUI 的对话记录里。在自动模式下,这不会产生警告。
  • 主动插话时,对话模型会按消息类型得到不同的规范:提问时只给确切答案;分享成就时可以直接恭喜,了解情况才带细节;分享挫折时只安慰,不随意指点;讨论只在能补充具体内容时才接话。所有类型都不允许编造事实,也不允许因为没听说过就断言某样东西不存在。
  • 自动模式下机器人不会给消息加表情回应。主动插话回复的是最新消息时,不会引用原消息。

建议先开启 shadow: true 运行一段时间,在 WebUI 的「自动判断」页查看每条消息的各项概率和最终决定,确认没问题后再关闭影子模式。切换供应商后建议重新观察概率分布并校准阈值。

自动模式会把白名单群中的消息发送给所配置的决策服务进行判断,请在启用前告知群成员。

自动模式配置

一个最小的自动模式配置如下,以舞萌(maimai DX)群为例:

chatsalt:
  auto:
    enabled: true
    groups: [123456789]
    shadow: true
    decisions:
      type: systemone
      apiKey: ${{ env:TYPESAFE_API_KEY }}
      model: jev-1.14.0
    names: [纱露朵, 盐, bot, 机器人]
    expertise: 'maimai DX (舞萌) rhythm game: charts, difficulty constants, rating, song unlocks, play technique.'
    intentHints:
      share_success: ['我把 xx 收了', '鸟加了', 'AP 了', 'rating 推到 15000 了']
      share_setback: ['又寸了', '这谱根本打不过']
      question: ['这谱怎么打', '定数多少']

使用 OpenAI 时,将上述 auto.decisions 替换为:

decisions:
  type: decisions
  apiKey: ${{ env:OPENAI_API_KEY }}
  model: gpt-6-luna

基本选项

| 选项 | 默认值 | 说明 | | --- | --- | --- | | auto.enabled | false | 是否启用自动模式 | | auto.groups | 必填 | 启用自动模式的群号白名单 | | auto.shadow | false | 影子模式:照常判断和生成回复,但不发送,只记录到 WebUI,用于测试参数 | | auto.decisions.type | 必填 | systemone(TypeSafe)或 decisions(OpenAI) | | auto.decisions.apiKey | 必填 | 所选服务的 API Key | | auto.decisions.model | 必填 | 所用的决策模型,例如 jev-1.14.0、gpt-6-luna | | auto.decisions.baseURL | 随类型选择 | systemone 默认 https://api.typesafe.ai/v1,decisions 默认 https://api.openai.com/v1。必须以 /v1 结尾,分别追加 /systemone 或 /decisions,可使用兼容对应协议的代理 | | auto.decisions.timeoutMs | 5000 | 单次请求超时(毫秒);最终失败时不发言 | | auto.decisions.maxRetries | 2 | 网络错误、超时或可重试 HTTP 错误的额外重试次数,0 表示不重试 | | auto.names | [] | 机器人的名字和别名,用于识别「没有 @ 但在叫机器人」的消息 | | auto.expertise | 必填 | 机器人擅长的领域,只有在这个范围内才会主动插话。推荐用英文描述,可以夹带中文专有名词 | | auto.avoidTopics | [politics, history, romance or sexual content] | 不参与的话题 | | auto.intentHints | {} | 各消息类型在本领域中的典型说法,会追加到决策模型的判断标准中。内置标准只包含通用的中文例子,圈内黑话(模型不一定认识)需要在这里补充 |

主动插话

| 选项 | 默认值 | 说明 | | --- | --- | --- | | auto.interject | { question: 0.6, share_success: 0.6 } | 对哪些类型的消息主动插话,见下文 | | auto.interjectWithin | 3 | 主动插话的消息必须在群里最近几条消息之内 | | auto.budget.perHour | 3 | 每个群每小时最多主动插话次数 | | auto.budget.minIntervalMs | 600000(10 分钟) | 两次主动插话的最小间隔 | | auto.budget.perUserIntervalMs | 1800000(30 分钟) | 对同一个群成员的消息两次主动插话的最小间隔 |

budget 只限制主动插话,被叫到和接话不受限制。

interject 的每一项可以写成一个数字,表示决策模型判定为该类型的最低概率,话题门槛沿用 thresholds.topicFit;也可以写成 { probability, topicFit },为该类型单独设置话题门槛(0-3,设为 0 表示不要求擅长领域)。未列出的类型不会触发主动插话。可选类型:

| 类型 | 含义 | | --- | --- | | chatter | 闲聊、玩梗、表情、日常 | | share_setback | 分享自己的失败、倒霉或烦恼,没有求助 | | share_success | 分享自己的成就、进展或好运 | | discussion | 分享信息,或发表看法、发起讨论 | | question | 提问、求助、求推荐 |

对话模型拿到的是决策模型判断出的最可能类型:如果这个类型在 interject 中列出,模型会得到正面的回应规范(怎样接得好);如果没有列出(消息是靠别的类型的规则通过的),模型会得到偏谨慎的规范,大多会放弃发言。例如设置 question: 0 后,擅长领域内的闲聊也会进入生成,但模型会被告知「这只是闲聊,并不需要你回应」。

时机与静音

| 选项 | 默认值 | 说明 | | --- | --- | --- | | auto.stateMessages | 8 | 提供给决策模型 的上下文消息条数 | | auto.debounceMs | 3000 | 群里安静多久后才做决定(毫秒) | | auto.maxBurstWaitMs | 10000 | 刷屏时最多等待多久后做决定(毫秒) | | auto.muteOnComplaintMs | 21600000(6 小时) | 有人嫌机器人烦时,自动模式在该群静音的时长 |

判断阈值

一般不需要修改,可在 WebUI「自动判断」页观察概率后调整。

| 选项 | 默认值 | 说明 | | --- | --- | --- | | auto.thresholds.addressed | 0.75 | 判定「在叫机器人」的最低概率 | | auto.thresholds.continuation | 0.7 | 判定「在接机器人的话」的最低概率 | | auto.thresholds.topicFit | 2.3 | 主动插话要求的话题相关度(0-3) | | auto.thresholds.privateExchange | 0.4 | 高于此值视为别人在私聊,不插话 | | auto.thresholds.sensitive | 0.6 | 高于此值视为敏感话题,一律沉默 | | auto.thresholds.wantsBotQuiet | 0.7 | 高于此值视为嫌机器人烦,触发静音 |

配置一个爱插话的机器人

默认配置偏安静:只在有人提问、或有人分享成就时插话,并且要求话题在 expertise 范围内。如果你希望机器人更像一个活跃的群友,建议:

  • 在 interject 中加入 chatter、share_setback、discussion 等类型。列出的类型会得到正面的回应规范,模型不会被劝退。
  • 为闲聊类的类型设置 topicFit: 0,让它们不受擅长领域限制。提问和讨论仍然只限于擅长领域(提示词里也会这样要求),以免不懂装懂。
  • 主要通过 budget 控制发言频率,而不是靠提高概率阈值。
auto:
  interject:
    question: 0.6
    discussion: 0.6
    share_success: { probability: 0.6, topicFit: 0 }
    share_setback: { probability: 0.7, topicFit: 0 }
    chatter: { probability: 0.6, topicFit: 0 }
  budget:
    perHour: 6
    minIntervalMs: 180000
    perUserIntervalMs: 600000

无论怎样配置,以下规则都不会放宽:不编造事实、一到两句话、不复述别人说过的话、话题过去了就不再回应、敏感话题一律沉默、有人嫌机器人烦时自动静音。

使用 chatsalt 命令

chatsalt 可以提供一些除对话之外的功能,目前有如下用法:

  • chatsalt inspect:查看插件的版本、对话模型和视觉模型等信息。
  • chatsalt rename <新名片>:更改机器人在当前群的群名片。