mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-05 16:57:32 +00:00
feat(outputch): 提示词显式声明「必须调 outputch 才送达」+ 契约值改名 Agent
## ① 提示词:只有显式调用 outputch 才会送达用户(用户明确要求)
模型最常见的错误不是不会调工具,而是**以为返回文本就等于回复了** ——
在异步通道(qq/wechat/群聊)上那等于自言自语,用户永远收不到。
改动前【输出规则】开篇是「消息不会自动发送到对话来源通道,你必须自己决定
如何回复」,语气偏描述。改为开篇总述句:
**只有显式调用 `output_send__{通道名}` 才会把内容送达用户。**
返回纯文本只是在这次推理里说话,用户看不到 —— 除非是下面说的同步通道。
同时补上本次内核新增能力:payload 传数组可一次发多条。
## ② 契约值改名:ProxyAuthHomeAgent="homeagent" → ProxyAuthAgent="agent"
用户指示契约值一起改。常量名与字符串值同时改,但**保留旧值读取路径**:
ProxyAuthAgent = "agent" ← 新值
ProxyAuthAgentLegacy = "homeagent" ← 只用于兼容读取
ProxyAuthHomeAgent = ProxyAuthAgent (Deprecated 别名,存量源码可编译)
为什么必须保留:`Auth` 是**写进 plugin.json 的持久化声明值**。存量配置里
写着旧值 —— 本机实测 `/home/newqqagent/plugins/huawei_smarthome/plugin.json:19`
就是 `"auth": "homeagent"`。只认新值 ⇒ 该插件加载时被判为非法声明 ⇒
反代不生效,症状是「插件好好的但页面打不开」,且无任何报错指向改名。
### 归一化是安全关键
`EffectiveProxyAuth` 把**空串与旧值都归一化为 ProxyAuthAgent**。
判断点全都写成 `auth == ProxyAuthAgent`。若不归一化,就得每点写两遍比较
—— **漏一处就是鉴权绕过**:旧值被判为「非门户鉴权」⇒ 跳过 authorizeProxy
⇒ 请求直接透传给上游。所以兼容收在一个函数里,不散落在调用点。
### 顺带修掉两处隐患
1. `webui/proxy.go` 的裸字面量 `"homeagent"`(非法行占位)改为
`sdk.ProxyAuthAgent` —— 裸字面量在改名时会被漏掉,届时它悄悄变成
未知值,而未知值在旧代码里等于「无鉴权」。
2. 两处鉴权判断改用 `EffectiveProxyAuth(...)`,堵住旧值绕过。
## 判据
| 判据 | 覆盖 | 变异自证 |
|---|---|---|
| `TestSystemPromptRequiresExplicitOutputCall` | 总述句必须存在、必须说明「返回文本用户看不到」、同步通道是唯一例外、payload 可传数组 | 删掉总述句 ⇒ 红 |
| `TestProxyAuthLegacyValueStillAccepted` | 旧值仍被接受 + 必须归一化为新值 | 去掉归一化分支 ⇒ 「旧值应归一化为 "agent",实际 "homeagent"」红 |
## 验证
- `go build ./...` / `go vet ./...` 干净
- webui / internal-sdk / agent-core 全绿
- SDK 仓(独立提交 acd1166):`go build` + `go test ./...` 0 失败
This commit is contained in:
@ -589,7 +589,10 @@ func (h *Handler) serveProxyHost(w http.ResponseWriter, r *http.Request) bool {
|
||||
})
|
||||
return true
|
||||
}
|
||||
if rt.Auth == sdk.ProxyAuthHomeAgent && !h.authorizeProxy(w, r) {
|
||||
// 用 EffectiveProxyAuth 而不是直接比常量:存量 plugin.json 里可能写着
|
||||
// 旧值(ProxyAuthAgentLegacy)。直接比常量 ⇒ 旧值被判为"非门户鉴权"
|
||||
// ⇒ **鉴权被绕过**,这是本仓最不能出的错。
|
||||
if sdk.EffectiveProxyAuth(rt.Auth) == sdk.ProxyAuthAgent && !h.authorizeProxy(w, r) {
|
||||
return true
|
||||
}
|
||||
if rt.reverse == nil {
|
||||
@ -622,7 +625,7 @@ func (h *Handler) serveProxyHost(w http.ResponseWriter, r *http.Request) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if route.Auth == sdk.ProxyAuthHomeAgent && !h.authorizeProxy(w, r) {
|
||||
if sdk.EffectiveProxyAuth(route.Auth) == sdk.ProxyAuthAgent && !h.authorizeProxy(w, r) {
|
||||
return true // 已写 401
|
||||
}
|
||||
|
||||
@ -842,7 +845,9 @@ func parseManualRoutes(text string) []proxyDecl {
|
||||
out = append(out, proxyDecl{
|
||||
Plugin: "manual", Name: "invalid", Host: "invalid",
|
||||
Target: "",
|
||||
Auth: "homeagent",
|
||||
// 非法行占位:用默认安全值(不是裸字面量 —— 契约值改名时
|
||||
// 裸字面量会被漏掉,届时这里悄悄变成"无鉴权")。
|
||||
Auth: sdk.ProxyAuthAgent,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user