mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-05 16:57:32 +00:00
## 一、插件声明层(用户裁定)
> 「敏感词策略也应该是插件声明啊,每个插件声明自己轮次以及自己工具的
> 敏感词策略」
SecurityProfile 新增两个字段:
ToolSensitiveWords map[string][]string // key=工具名模式(尾 *),value=词表
DetectInjection bool // 开启内核内置注入迹象识别
语义是**追加不覆盖**:整轮 SensitiveWords 仍然生效。
取宽不取窄的理由:当前处置是 B 档(标记+警示,不阻断),
多标一条只是多一句警示,漏标则是安全缺口。
## 二、修掉我自己造的「莲藕式分层」
前一版把 *security.Round 从 runInputTask 一路搬到工具链
(prepareInputTask(evt, round) → TaskFrame.SecRound →
executeToolCallOutcomeWithRound),并在 rtchild 里另补一条。
那是把**分层策略实现成跨层传参**:每多一条工具执行路径就要求调用方
多传一次句柄。profileActivator 的设计注释早就写明了正路:
> 工具调用发生在 LLM 决定之后,此时插件已不在调用栈上。
> 唯一可靠的传递方式是**内核在输入进来时记住**,在该轮的工具判定里使用。
改为 security 自取:
Runtime.ScanContentForAgent(agentID, isChild, ins, text)
→ ForAgent(agentID, isChild) // per-agent 视图,天然不串档
→ EffectiveProfile() // session > longtime
→ 合并 全局词表 + 档位词表 + 工具词表 + DetectInjection
core 只递交**事实**(我是谁 / 哪个工具 / 什么文本),不认识 Round。
与 CheckToolForAgent 同一套身份推断,不引入第二个入口语义。
TaskFrame.SecRound / executeToolCallOutcomeWithRound 全部撤除。
## 三、目录名统一
third_party/homeagent-sdk → third_party/hologramagentsdk
module 与远端仓早已是 hologramagentsdk,只剩物理目录是遗留名。
同步改了 go.mod replace、构建/打包/部署脚本、5 处测试里的分段路径。
注意:这里改的是**主仓 vendored 的 SDK 源码路径**,example/ 由 SDK
独立仓自己跟踪,主仓不涉及。
## 判据
TestToolSensitiveWordsAppendOnlyForThatTool 词表只对目标工具生效 + 追加语义
TestToolSensitiveWordsUseAgentScope security 按 agent scope 自取生效
(变异验证:删掉 ForAgent 查询 → 红)
## 验证
go build ./... → 0
go vet ./... → 0
go test ./... -count=1 -race → 62 ok / 0 FAIL / 0 DATA RACE
gofmt -l internal/ → 干净
98 lines
4.2 KiB
C
98 lines
4.2 KiB
C
#ifndef HA_CODEC_H
|
||
#define HA_CODEC_H
|
||
|
||
/*
|
||
* ha_codec — HomeAgent 内核编解码层(C 实现)
|
||
*
|
||
* ============================ 接口冻结声明 ============================
|
||
* 本头文件是对外契约。函数签名、语义、返回值一经发布即为冻结接口,
|
||
* 修改必须走大版本流程(与 third_party/hologramagentsdk 同一冻结标准)。
|
||
*
|
||
* 设计约束(见 docs/zh/c-core/llm-orchestration-c.md §四):
|
||
* 1. 只吃 const char* + **显式长度**,出数值/字节偏移 —— 不回调 Go、
|
||
* 不传 Go 指针、不要求 NUL 结尾
|
||
* 2. **不 malloc**:不需要出参缓冲区,需要「结果」时返回字节偏移/长度,
|
||
* 由调用方在自己的缓冲上切片(零拷贝)
|
||
* 3. 无状态、纯函数、线程安全(不写全局可变状态)
|
||
*
|
||
* 当前覆盖:L1 协议编解码层中的纯计算部分(第一个最小切片)。
|
||
*
|
||
* ============================ 为什么签名带长度 ============================
|
||
* 初版签名用 `const char*` 隐含「NUL 结尾」,于是每次调用都要:
|
||
* Go `C.CString` 分配+拷贝一遍 → C `strlen` 再扫一遍。
|
||
* 实测这部分开销占单次调用的 80% 以上(cgo 边界本身仅 ~30ns,
|
||
* 而初版 ModelContextWindow 实测 175ns)。
|
||
* 改为「指针 + 长度」后,Go 侧用 unsafe.StringData 直接传底层数组,
|
||
* 零分配零拷贝。这是设计约束第 1 条的字面要求。
|
||
*/
|
||
|
||
#include <stddef.h>
|
||
|
||
#include "ha_abi.h"
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* ==================== ABI 自述(供 Go 侧与日志核对) ==================== */
|
||
|
||
/* 返回 HA_CODEC_ABI_VERSION(major*1000 + minor)。
|
||
*
|
||
* 存在的意义:Go 侧不该靠 `#include` 宏做版本断言(cgo 头文件里的宏在
|
||
* 预处理后不可见),而要**运行期/测试期**能问 C 侧「你自称什么版本」。
|
||
* 由 codec_cgo.go 绑定、codec_abiversion_test.go 与 C 侧宏三方比对。 */
|
||
int ha_codec_abi_version(void);
|
||
|
||
/* ==================== 模型上下文窗口推断 ==================== */
|
||
|
||
/* 无法从模型名推断时的哨兵值(与 Go 侧一致)。
|
||
*
|
||
* 为什么返回哨兵而不是直接给兜底值:调用方需要区分「真推断出了」与
|
||
* 「推断不出、只能兜底」——后者要打一行日志(窗口被低估必须可见),
|
||
* 并提示部署方用 per-source context_window 显式声明。
|
||
* 若 C 侧直接返回兜底值,调用方就永远分不清这两种情况。 */
|
||
#define HA_CODEC_CONTEXT_WINDOW_UNKNOWN (-1)
|
||
|
||
/* 由模型名推断最大上下文窗口(token 数);推断不出返回
|
||
* HA_CODEC_CONTEXT_WINDOW_UNKNOWN。
|
||
*
|
||
* model 为 UTF-8 字节序列,**不需要 NUL 结尾**;model_len 是字节数。
|
||
* model 为 NULL 或 model_len 为 0 时返回 UNKNOWN。
|
||
*
|
||
* 匹配大小写不敏感(仅对 ASCII 字母做折叠;非 ASCII 字节按原样比较,
|
||
* 与 Go 侧对模型名的实际输入一致)。
|
||
*
|
||
* 语义必须与 Go 侧 modelContextWindowPure 逐值一致(黄金对照测试钉死)。 */
|
||
int ha_codec_model_context_window(const char *model, size_t model_len);
|
||
|
||
/* ==================== token 估算与截断 ==================== */
|
||
|
||
/* 粗略估算 token 数。
|
||
*
|
||
* 规则(与 Go 侧 EstimateTokens 一致):保守取 max(1, runeCount * 2)。
|
||
* 按 UTF-8 **字符数**(rune)计,不是字节数。
|
||
* text 为 NULL 或 text_len 为 0 返回 0。
|
||
*
|
||
* 非法 UTF-8 序列按 Go 的 utf8 解码语义处理(每字节一个 rune),
|
||
* 保证与 Go 侧逐值一致。 */
|
||
int ha_codec_estimate_tokens(const char *text, size_t text_len);
|
||
|
||
/* 按 token 预算计算「应保留的字节数」。
|
||
*
|
||
* ★ 返回的是**字节数**而非字符串:截断结果必然是输入的前缀,
|
||
* 调用方直接在自己的缓冲上切片即可(零拷贝、无出参缓冲区、无 malloc)。
|
||
*
|
||
* 语义与 Go 侧 TruncateByTokens 一致:从开头保留 maxTokens/2 个 rune;
|
||
* 未超预算时返回 text_len(即整串)。
|
||
* max_tokens <= 0 或 text 为 NULL/text_len 为 0 时返回 0。
|
||
*
|
||
* 返回值保证 <= text_len。 */
|
||
size_t ha_codec_truncate_by_tokens(const char *text, size_t text_len,
|
||
int max_tokens);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* HA_CODEC_H */
|