- AI Agent
- 人工智能
- 大模型
- AI 应用
- 工具调用
- 本地部署
- MCP Clients
- Agent 记忆
【免费下载链接】Operit
The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent
本文以 Operit 仓库 docs/TODO/character_card_software_settings_20260805 系列文档为主线,系统讲解 Operit 如何把角色卡(Character Card)的完整管理能力下沉到原生层,并以Tools.SoftwareSettings的形式开放给脚本与 AI Agent 使用。读完本文,你将掌握九个角色卡原生工具的名称、参数、返回值与字段边界,理解 JavaScript 桥、TypeScript 声明与 Operit Editor 三者的同步契约,并能直接编写脚本完成角色卡的列出、详情、创建、更新、删除、激活以及酒馆 JSON 导入导出。
背景:角色卡在 Operit 中的定位与旧实现的局限
角色卡是 Operit 会话人格的核心载体,承载角色的描述、开场白、聊天/语音附加内容、绑定的模型配置与记忆档案,以及工具访问白名单等完整配置。在本次改造之前,角色卡存在一个明显的断层:
- 底层管理器
CharacterCardManager已经具备角色卡的读取、创建、更新、删除、激活以及酒馆 JSON(Tavern JSON)导入导出能力; - 但脚本侧只有
Tools.Chat.listCharacterCards()一个只读方法,其返回内容仅用于创建会话时的选卡,缺少完整字段与管理操作; Tools.SoftwareSettings与operit_editor均无法管理角色卡,角色卡主要由设置 UI 消费。
这意味着 AI Agent 或第三方脚本无法通过工具调用完成"创建一张新卡、编辑人格设定、切换当前活跃卡、把卡片导出为酒馆 JSON"这类操作,管理能力被 UI 独占。本次改造的目标正是把CharacterCardManager的能力通过原生工具、JavaScript 桥与编辑器三层完整暴露出来。
九个原生工具:完整 API 契约
改造的核心动作是在StandardSoftwareSettingsModifyTools中直接调用CharacterCardManager,并由ToolRegistration注册对应原生工具,同时新增结构化结果类型,完整传递角色卡的配置字段与当前活跃角色卡 ID。完整的工具清单如下:
| 原生工具名 | 语义 | 关键参数 | 返回内容 |
|---|---|---|---|
list_character_cards_settings | 列出全部角色卡与活跃卡 ID | 无 | totalCount、activeCharacterCardId、cards[] |
get_character_card | 获取指定角色卡 | character_card_id | 单张卡片 +activeCharacterCardId |
create_character_card | 创建角色卡(只接受可编辑字段) | name(必填)+ 可编辑字段 | created、card、activeCharacterCardId、changedFields |
update_character_card | 合并更新指定可编辑字段 | character_card_id+ 可编辑字段 | updated、card、activeCharacterCardId、changedFields |
delete_character_card | 删除非默认角色卡 | character_card_id | deleted、characterCardId、activeCharacterCardId |
set_active_character_card | 设置当前活跃角色卡 | character_card_id | activeCharacterCardId |
clear_active_character_card | 清除活跃角色卡 | 无 | activeCharacterCardId = null |
import_character_card_from_tavern_json | 从酒馆 JSON 导入一张卡 | tavern_json | imported、card、activeCharacterCardId |
export_character_card_to_tavern_json | 导出指定卡为酒馆 JSON | character_card_id | characterCardId、tavernJson |
这九个工具的注册集中在 app/src/main/java/com/ai/assistance/operit/core/tools/ToolRegistration.kt,每个工具都通过handler.registerTool注册,executor统一从ToolGetter.getSoftwareSettingsModifyTools(context)获取执行器并在Dispatchers.IO上运行。例如create_character_card的描述生成器会动态拼出Create character card: <name>,get_character_card则拼出Get character card settings: <id>,方便模型在工具调用时直接理解上下文。
实现细节:每个工具的执行器做了什么
执行器实现位于 StandardSoftwareSettingsModifyTools.kt,几个关键行为可以从源码确认:
- 列表(listCharacterCards):调用
CharacterCardManager.getInstance(context).getAllCharacterCards()后逐张映射为CharacterCardResultItem,同时通过observeActiveCharacterCardId().first()读取当前活跃卡 ID。映射后的条目包含id、name、description、characterSetting、openingStatement、otherContentChat、otherContentVoice、attachedTagIds、advancedCustomPrompt、marks、模型/记忆绑定字段、toolAccessConfig以及isDefault、createdAt、updatedAt。 - 详情(getCharacterCard):
character_card_id缺失时返回Missing required parameter: character_card_id;卡片不存在时抛出Character card not found: <id>。 - 创建(createCharacterCard):以
CharacterCard(id = "", name = "")为底稿,要求name必填且不能为空白,其余可编辑字段按需叠加;随后由管理器分配 ID 并持久化。 - 更新(updateCharacterCard):先按 ID 取出当前卡,仅合并调用方传入的字段;如果没有任何字段变更,会直接报错
At least one character card update field is required,避免空操作。 - 删除(deleteCharacterCard):默认卡
default_character不可删除,源码中对此有显式拦截:The default character card cannot be deleted。 - 导入/导出(Tavern JSON):分别委托
CharacterCardManager.createCharacterCardFromTavernJson(tavernJson)与exportCharacterCardToTavernJson(id),失败路径会返回Result中的异常信息,成功路径返回完整卡片或 JSON 字符串。
字段边界:哪些可写,哪些由管理器维护
SoftwareSettings负责角色卡配置本身,Chat只在创建会话或发送消息时引用角色卡。更重要的是写入接口只接受可编辑字段:角色卡 ID、创建时间、默认角色卡属性由CharacterCardManager维护,调用方无法通过更新接口改变它们。这一点在 index.md 中被明确为接口边界。
可编辑字段(即CharacterCardWriteOptions,定义于 examples/types/software_settings.d.ts):
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | 创建时必填;更新时可选 |
description | string | 角色简介 |
character_setting | string | 角色设定(人格描述) |
opening_statement | string | 开场白 |
other_content_chat | string | 聊天附加内容 |
other_content_voice | string | 语音附加内容 |
advanced_custom_prompt | string | 高级自定义提示词 |
marks | string | 标记/备注 |
attached_tag_ids | string \| string[] | 关联标签 ID,支持字符串数组或其 JSON 字符串表示 |
chat_model_binding_mode | 'FOLLOW_GLOBAL' \| 'FIXED_CONFIG' | 会话模型绑定模式 |
chat_model_config_id | string | 绑定的模型配置 ID,空字符串清除绑定 |
chat_model_index | number | 绑定模型在配置中的索引,须大于等于 0 |
memory_profile_binding_mode | 'FOLLOW_GLOBAL' \| 'FIXED_PROFILE' | 记忆档案绑定模式 |
memory_profile_id | string | 绑定的记忆档案 ID,空字符串清除绑定 |
tool_access_enabled | boolean | 是否启用该卡的工具访问控制 |
allowed_builtin_tools | string \| string[] | 允许的内置工具白名单 |
allowed_packages | string \| string[] | 允许的沙箱包白名单 |
allowed_skills | string \| string[] | 允许的技能白名单 |
allowed_mcp_servers | string \| string[] | 允许的 MCP 服务器白名单 |
从 StandardSoftwareSettingsModifyTools.kt 的applyCharacterCardUpdates实现可以看到字段校验的细节:
- 布尔参数(
tool_access_enabled)复用parseBooleanParameter,接受1/true/yes/y/on与0/false/no/n/off的宽松写法; - 字符串数组参数(
attached_tag_ids、allowed_*)通过parseStringArrayParameter解析,必须是合法 JSON 数组且每个元素为字符串,解析后自动去重、剔除空项; chat_model_binding_mode与memory_profile_binding_mode只接受各自枚举的两个取值,其余值直接抛Invalid ... mode异常;chat_model_index必须是整数且>= 0;- 所有数组/绑定白名单在写入前会经过
normalized()处理,保证存储形态一致。
原生层设计:单一实现,副作用不分散
文档明确要求:"角色卡工具使用既有管理器,确保主题、Waifu 设置、自定义表情、聊天绑定和提示词标签的副作用仍由单一实现处理。" 这意味着工具层只做参数解析、校验与结果映射,所有副作用(如切换活跃卡时联动切换 Waifu 设置、头像、提示词标签、模型绑定刷新)都收敛在CharacterCardManager内部。
这一设计在 CharacterCardManager.kt 中可以得到印证:DEFAULT_CHARACTER_CARD_ID = "default_character"(第 96 行),createCharacterCard为非默认卡分配UUID.randomUUID(),setActiveCharacterCard/clearActiveCharacterCard会同步处理活跃状态与关联的 Waifu、头像等衍生数据;删除活跃卡后活跃状态会回落到默认卡。工具层绝不复制这套副作用逻辑。
脚本层:JavaScript 桥与调用方式
原生工具通过 app/src/main/java/com/ai/assistance/operit/core/tools/javascript/JsTools.kt 暴露为Tools.SoftwareSettings的方法,方法名与原生工具一一对应:
// 列出全部角色卡与活跃卡 ID Tools.SoftwareSettings.listCharacterCards(); // 获取指定角色卡 Tools.SoftwareSettings.getCharacterCard(characterCardId); // 创建角色卡(options 中 name 必填) Tools.SoftwareSettings.createCharacterCard({ name: "新角色", description: "..." }); // 合并更新(第二个参数只传要改的字段) Tools.SoftwareSettings.updateCharacterCard(characterCardId, { marks: "v2" }); // 删除非默认卡 Tools.SoftwareSettings.deleteCharacterCard(characterCardId); // 激活 / 清除活跃卡 Tools.SoftwareSettings.setActiveCharacterCard(characterCardId); Tools.SoftwareSettings.clearActiveCharacterCard(); // 酒馆 JSON 导入 / 导出 Tools.SoftwareSettings.importCharacterCardFromTavernJson(tavernJson); Tools.SoftwareSettings.exportCharacterCardToTavernJson(characterCardId);桥层实现为toolCall("get_character_card", { character_card_id: characterCardId })这类透传调用,把 JS 参数原样映射为原生工具的parameters。值得注意的是,会话工具Tools.Chat.listCharacterCards()(对应list_character_cards)继续保留,会话选卡场景的既有调用方式不受影响,这与 index.md 中"继续保留Tools.Chat.listCharacterCards()"的预期结果一致。
TypeScript 声明:严格类型约束
类型声明位于 examples/types/software_settings.d.ts,为输入输出提供严格类型:
- 两个绑定模式枚举:
CharacterCardChatModelBindingMode = 'FOLLOW_GLOBAL' | 'FIXED_CONFIG'、CharacterCardMemoryProfileBindingMode = 'FOLLOW_GLOBAL' | 'FIXED_PROFILE'; CharacterCardWriteOptions覆盖全部可编辑字段(见上文字段表);- 九个方法均返回带
Promise的结构化结果类型:CharacterCardsResultData、CharacterCardResultData、CharacterCardCreateResultData、CharacterCardUpdateResultData、CharacterCardDeleteResultData、CharacterCardActivationResultData、CharacterCardImportResultData、CharacterCardExportResultData。
例如创建接口的签名是createCharacterCard(options: CharacterCardWriteOptions & { name: string }): Promise<CharacterCardCreateResultData>,在类型层强制name必填;更新接口则是updateCharacterCard(characterCardId: string, updates: CharacterCardWriteOptions),对应原生实现的合并语义。
Operit Editor:九个角色卡管理工具的注册
脚本侧之外,operit_editor也同步注册了九个角色卡管理工具,形成"包内可调用"的管理能力。同步范围在 2_ScriptApiAndOperitEditor.md 中被明确为三个文件,契约必须一致:
examples/operit_editor.ts:编辑器源文件;examples/operit_editor.js:其 JavaScript 产物;app/src/main/assets/packages/operit_editor.js:内置包副本。
editor 侧同样覆盖列卡、详情、创建、更新、删除、激活、清除活跃、导入、导出九个工具,且元数据统一使用 JSON 字符串数组来表示数组参数(如attached_tag_ids、allowed_builtin_tools等),与原生parseStringArrayParameter的解析约定对齐。
数组参数的两种写法约定
文档特别强调:"数组参数支持 string array 及其 JSON 字符串表示,editor 元数据统一使用 JSON 字符串数组。" 也就是说,在脚本与 editor 层,同一个数组字段有两种等价写法:
// 写法一:直接传字符串数组 Tools.SoftwareSettings.updateCharacterCard(id, { allowed_packages: ["worldbook", "deepsearching"] }); // 写法二:传 JSON 字符串(editor 元数据统一采用此形态) Tools.SoftwareSettings.updateCharacterCard(id, { allowed_packages: '["worldbook", "deepsearching"]' });原生parseStringArrayParameter对二者统一处理:JSON 数组逐元素校验为字符串、去空、去重后再持久化。
源码复核与交付检查
交付环节(3_VerificationAndDelivery.md)围绕五个检查项完成静态复核,不执行编译、构建或测试命令:
- 原生工具注册名、软件设置执行器和 JavaScript 桥一一对应(ToolRegistration 九个名字 ↔
StandardSoftwareSettingsModifyTools九个方法 ↔ JsTools 九个桥方法); - TypeScript 参数和结果类型与原生字段一致;
operit_editor.ts的工具定义、参数校验和调用覆盖全部角色卡接口;- JavaScript 产物与应用内置副本同步(
examples/operit_editor.js与app/src/main/assets/packages/operit_editor.js哈希一致,git diff --check无空白错误); - 现有
Tools.Chat.listCharacterCards()继续保留。
复核确认了原生注册名、JavaScript 桥、TypeScript 声明和 editor 的接口数量与名称完全一致,工具注册、JavaScript 桥、类型声明和 editor 入口四条链路闭环。
小结:从 UI 独占到工具开放
本次改造把角色卡从"UI 独占、脚本只读"升级为"原生工具 + JavaScript 桥 + TypeScript 声明 + Operit Editor"四层完整开放的配置管理接口:
- 原生层由
StandardSoftwareSettingsModifyTools承载九个工具,ToolRegistration注册,CharacterCardManager作为唯一副作用实现; - 脚本层以
Tools.SoftwareSettings九个方法提供完整增删改查与激活/导入导出能力,会话层Tools.Chat.listCharacterCards()保持兼容; - 类型层用
CharacterCardWriteOptions与九个结构化结果类型约束输入输出; - 编辑器层同步注册九个工具,三份产物契约一致。
对开发者而言,这意味着任何沙箱脚本、toolpkg 包或 AI Agent 都可以在运行期以结构化数据驱动角色卡全生命周期管理——从批量整理卡片、按规则切换活跃角色,到跨设备以酒馆 JSON 迁移人格设定,都不再需要依赖人工在设置 UI 中逐项操作。相关文档与代码可继续参阅 任务总览、原生实现 与 类型声明。
- AI Agent
- 人工智能
- 大模型
- AI 应用
- 工具调用
- 本地部署
- MCP Clients
- Agent 记忆
【免费下载链接】Operit
The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent
相关推荐
Operit 角色卡全量管理接口落地实录:SoftwareSettings 原生工具、JavaScript 桥与 Operit Editor 三端打通
Operit 角色卡全量管理接口落地实录:SoftwareSettings 原生工具、JavaScript 桥与 Operit Editor 三端打通 本指南围
AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化独角数卡(dujiaoka)用户权限系统:RBAC模型与多角色管理完整指南
独角数卡 dujiaoka 用户权限系统:RBAC模型与多角色管理完整指南 独角数卡 dujiaoka 作为开源站长自动化售货解决方案,其用户权限系统采用了灵活
后端电商laravel-permission 角色与权限实战指南:从角色分配、角色侧批量管理到直接权限判定
laravel permission 角色与权限实战指南:从角色分配、角色侧批量管理到直接权限判定 本指南聚焦 spatie/laravel permissio
后端认证鉴权
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考