杠精派
派聪明
0/512

热门评论

Lee 2026年07月02日 09:20
引用原文:定义如下
@派聪明 ApprovalRequest中的callerContext是什么?怎么来的?
1
回复3 取消回复
派聪明 2026年07月02日 09:20
从你截取的那段代码可以清晰看到 **`callerContext` 是 `ApprovalRequest` record 中的一个预留字段**,但**当前实现中并没有给它赋值**。 具体来看源码: ```java public record ApprovalRequest( String toolName, String arguments, String dangerLevel, String riskDescription, String suggestion, String callerContext // ← 这个字段 ) { public static ApprovalRequest of(String toolName, String arguments, String suggestion) { return new ApprovalRequest( toolName, arguments, ApprovalPolicy.getDangerLevel(toolName), ApprovalPolicy.getRiskDescription(toolName), suggestion, null // ← 这里直接传了 null ); } } ``` --- ### 它是干什么的? 从命名推断——**`callerContext` = "调用者上下文"**。它的设计意图是记录**这次审批请求是由谁发起的**,比如: - 在 **Multi-Agent** 模式下,是 `SubAgentA` 还是 `SubAgentB` 调的危险操作? - 在 **Plan-and-Execute** 模式下,是哪个 Step 的哪个工具调用? 有了这个信息,审批框里就能告诉用户**"哪个 Agent、在哪一步、想干什么"**,帮助用户更精准地判断是否放行。 ### 为什么目前传 `null`? 因为文章介绍的 HITL 还是 **V1 版本**,最基础的能力是先跑通“拦截→展示→决策”这个闭环。文章中明确说了: > `toDisplayText()` 方法展示的信息已经包含了工具名、等级、风险描述、参数,在当前单 Agent 场景下足够用。 `callerContext` 属于**扩展预留字段**,为后续 Multi-Agent 下精细化审批做准备——等后面需要区分“谁调的”时,直接在这个字段里塞入 Agent ID + 步骤名即可,record 结构不用改。
点赞
回复 取消回复
展开剩余 2 条回复

29 条评论

枫原万叶 2026年09月23日 22:29
引用原文:这个等级信息后面会展示在审批框里,让用户一眼看出当前操作的危险指数
@派聪明 这个危险等级是写死的吗?那么新增工具都需要手动配置吗?如果每次都请求用户审批,用户会感觉被打扰,该怎么处理
点赞
回复 取消回复
勤恳的冬瓜 2026年09月23日 14:40
@派聪明 如果加了新的工具怎么办,这里都是写死的
点赞
回复1 取消回复
派聪明 2026年09月23日 14:40
#静态规则写死了怎么办? 确实,当前的 `ApprovalPolicy` 把工具名硬编码在 `DANGEROUS_TOOLS` 里,新增危险工具时必须改代码重新编译。如果工具是动态增长的(比如插件式 Agent),可以按需升级为以下几种方案: ## 方案一:把配置外置(最简单) 将名单从 Java代码搬到配置文件(如 `approval-policy.json` 或 `yaml`): ```json{ "dangerous": { "execute_command": { "level": "HIGH", "desc": "执行 Shell命令" }, "write_file": { "level": "MEDIUM", "desc": "覆写文件" } } } ``` 启动时加载,新增工具只改配置,不改代码。风险等级、描述也能一并配置。 ## 方案二:注解驱动在工具类上打注解,注册时自动扫描: ```java@Dangerous(level = Level.HIGH, desc = "执行 Shell命令") public class ExecuteCommandTool implements Tool { ... } ``` `ToolRegistry` 注册工具时读取注解,自动填充审批策略。好处是工具和它的风险声明放在一起,不会“加了工具忘了加名单”。 ## 方案三:按默认策略兜底把判断逻辑反转:**默认全部需要审批,只把白名单里的只读工具放行**。这样任何新增工具天然走审批流程,漏配最多是“多问一次”,而不是“漏掉危险操作”——安全性更高的 fail-safe 方向。 ## 方案四:暴露动态注册 API```javaApprovalPolicy.register("deploy_service", Level.HIGH, "部署到生产环境"); ``` 适合 Multi-Agent场景下,运行时由子 Agent声明自己带来的工具。 ## 建议- **工具集合稳定**:维持现状即可,写死反而是优点(可预期、零开销) - **工具会增长**:注解(方案二)+默认危险兜底(方案三)组合最好- **工具由用户/插件提供**:配置外置(方案一)+动态注册(方案四) 注意不建议为此
点赞
回复 取消回复
呃呃呃 2026年09月21日 16:23
引用原文: flush
flush是什么@派聪明
点赞
回复1 取消回复
派聪明 2026年09月21日 16:23
# flush 是什么**flush(刷新/冲刷缓冲区)** 是一个通用的 I/O 编程概念:**把缓冲区中暂存的数据强制真正输出出去**。 ## 为什么会有 flush程序写数据时,为了性能,通常不是写一个字节就立刻输出一次,而是先攒在**缓冲区(Buffer)**里,攒够一批(或遇到换行符等条件)才真正输出。 -好处:减少 I/O次数,性能高-坏处:数据可能“滞留”在缓冲区里,没有被真正写出去**flush 就是强制命令:别攒了,现在立刻把缓冲区里的内容全部输出!** ## 在这篇文章中的具体含义文章里出现了两处 flush,都和 PaiCLI 的流式输出渲染有关: **1.渲染器的缓冲区 flush** > `TerminalMarkdownRenderer`渲染器遇到 `\n`才 flush 一行内容意思是:Agent 流式输出时,Markdown渲染器会把收到的文字先存在缓冲区,直到遇到换行符才把这一行渲染打印出来。 问题在于:如果缓冲区还有未 flush 的半行文字,HITL审批框就直接打印,审批框会**插在半行文字中间**,导致排版错乱。所以文章的做法是在执行工具前调用 `renderer.resetBetweenIterations()` 强制 flush。 **2.终端交互前的 `out.flush()`** ```javaout.print("> "); out.flush(); ``` 打印提示符 `> ` 后立刻 flush,确保用户能马上看到提示符,然后才能正确接收键盘输入。注意 `print` 后面没有换行符,如果不手动 flush,提示符可能一直卡在缓冲区里不显示。 ## 一个类比就像你往水池倒水(写入数据),下水口先堵着(缓冲),水攒多了再放(批量输出)。**flush 就是手动拔掉塞子,把水全放掉**。
点赞
回复 取消回复
是你啊 2026年08月30日 10:58
引用原文:基于静态规则实现 HITL 危险操作拦截策略(ApprovalPolicy),将 write_file、execute_command、create_project 标记为需要人工确认的工具,避免 Agent 无监督覆写文件或执行 shell 命令 设计并实现继承式拦截层,通过覆写 executeTool() 注入审批逻辑,HITL 关闭时零开销,共享于 ReAct、Plan-and-Execute 和 Multi-Agent 三条执行路径 使用 Record 定义数据类型,实现显示宽度算法(CJK/全角/emoji 按 2 列计算)确保中文内容在 ASCII 审批框中渲染对齐 通过 synchronized + ConcurrentHashMap 保证多 Agent 并发场景下审批请求串行展示,支持“全部放行”会话级缓存降低重复确认频率 使用 Mockito 验证拦截逻辑、策略覆盖率和 fail-safe 行为,随项目总测试用例数增至 152 个
@派聪明 解释这几条
点赞
回复1 取消回复
派聪明 2026年08月30日 10:58
这段是 PaiCLI 项目(Java Agent CLI)中 HITL人工审批模块的简历描述,逐条解释如下: ## 第1 条:静态规则拦截策略用 Java 写死一个白名单(`Set`集合),把会修改磁盘或执行命令的工具(`write_file` 写文件、`execute_command`跑 shell、`create_project`建目录)标记为“危险”,执行前必须人工确认;只读工具(`read_file` 等)直接放行。 **为什么用静态规则而不是让 LLM判断?** 因为 LLM判断有随机性(今天说危险明天说安全),且每次调用都问一遍模型很慢。`Set.contains()` 是确定性的,快且可靠——“简单的东西比聪明的东西更靠谱”。 ## 第2 条:继承式拦截层`HitlToolRegistry`继承原有的 `ToolRegistry`,只覆写 `executeTool()` 一个方法:执行工具前先检查“HITL 开启 +工具危险”这两个条件,都满足才弹审批框,否则直接调 `super`执行。 好处: - 不用改任何老代码,ReAct、Plan-and-Execute、Multi-Agent 三种 Agent模式共用同一个注册表,自动都获得审批能力; - HITL 关闭时开销只是一次 boolean读取 +一次 Set查找,几乎为零。 ## 第3 条:显示宽度算法终端审批框用 ASCII 字符画边框(如 `│ ... │`),但中文字符和 emoji 在终端占2列,英文占1列。如果直接用字符串长度补空格,右边框会被中文挤歪。所以实现了 `displayWidth()` 方法,识别 CJK 字符、全角符号、emoji 并按2列计算,保证框线始终对齐。数据载体用 Record(不可变、简洁)。 ## 第4 条:并发安全Multi-Agent模式下多个 Worker线程可能同时触发危险操作,若不加控制,两个审批框会同时打印、抢占输入流。解决方式: - `requestApproval` 加 `synchronized`,保证审批请求串行展示,用户决策完一个再弹下一个; - "全部放行”的记录用 `ConcurrentHashMap.newKeySet()` 存储,多线程下读写安全。用户按
点赞
回复 取消回复
柒 2026年08月29日 14:24
@派聪明 在一个终端中开启了这个审批模式,关闭后再开一个新的终端这个模式是开启状态还是关闭的
点赞
回复1 取消回复
派聪明 2026年08月29日 14:25
关闭的。 HITL 的开关状态和“全部放行”(APPROVED_ALL)记录都只保存在**内存**中,是会话级别的,没有做任何持久化(不落盘、不写配置文件): - `TerminalHitlHandler hitlHandler = new TerminalHitlHandler(false)` ——每次启动新终端时,Main.java 都会用 `false`默认初始化,即**默认关闭** - `/hitl on` 开启后,状态只存在于当前进程内存- 关闭终端后进程结束,状态随之消失所以即使你在上一个终端用 `/hitl on` 开启了审批模式,关闭后新开一个终端,HITL仍然是**关闭状态**,需要重新执行 `/hitl on`才能启用。 同理,`/clear` 会清除会话内的“全部放行”记录,但如果直接关终端重开,所有审批状态也都会重置,不会有残留。
1
回复 取消回复
叶当当 2026年08月21日 13:36
引用原文:通过 synchronized + ConcurrentHashMap 保证多 Agent 并发场景下审批请求串行展示,支持“全部放行”会话级缓存降低重复确认频率
@派聪明 这个是怎么做的
点赞
回复1 取消回复
派聪明 2026年08月21日 13:36
这是我从文章中选择的一段文本:"通过 synchronized + ConcurrentHashMap 保证多 Agent 并发场景下审批请求串行展示,支持“全部放行”会话级缓存降低重复确认频率" 这个是怎么做的 当前模型还在开发当中,请右上角下拉框切换到其他模型
点赞
回复 取消回复
安安 2026年08月10日 16:18
引用原文:作用域是工具级别,不是全局
具体代码是怎么做的?@派聪明
点赞
回复1 取消回复
安安 2026年09月15日 11:42
具体代码是怎么做的?@派聪明
点赞
回复 取消回复
枫原万叶 2026年07月22日 21:13
引用原文:这个接口分离不是为了“设计模式好看”,是为了让测试能跑通。终端交互是一个非常难测的东西,一旦代码里硬编码了 System.in,测试就必须模拟键盘输入,极其麻烦。 接口抽象之后,TerminalHitlHandler 的包级别构造器允许注入自定义的 BufferedReader 和 PrintStream,测试可以直接喂字符串
@派聪明 这个问题具体是怎么产生的?
点赞
回复1 取消回复
派聪明 2026年07月22日 21:13
小派正在努力回答中, 耐心等待一下吧...
点赞
回复 取消回复
枫原万叶 2026年07月22日 21:01
引用原文:权衡四:接口隔离测试 HitlHandler 是接口,TerminalHitlHandler 是实现。测试里用的是 Mockito 的 mock,不需要真实的终端 stdin/stdout。 这个接口分离不是为了“设计模式好看”,是为了让测试能跑通。终端交互是一个非常难测的东西,一旦代码里硬编码了 System.in,测试就必须模拟键盘输入,极其麻烦。 接口抽象之后,TerminalHitlHandler 的包级别构造器允许注入自定义的 BufferedReader 和 PrintStream,测试可以直接喂字符串:
@派聪明 这是在说什么问题
点赞
回复1 取消回复
派聪明 2026年07月22日 21:01
小派正在努力回答中, 耐心等待一下吧...
点赞
回复 取消回复
枫原万叶 2026年07月22日 20:56
引用原文:PaiCLI 的 Agent 在流式输出 reasoning_content 的时候,TerminalMarkdownRenderer 有缓冲区。渲染器遇到 \n 才 flush 一行内容。 问题是:Agent 在工具调用迭代之间,渲染器里可能还有未 flush 的文字缓冲。 如果这时候 HITL 审批框直接弹出来,会从半行文字的中间插入,审批框的标题和内容就错位了。 解决方式是在进入 tool-call 迭代前,先调用 renderer.resetBetweenIterations(),把缓冲区强制 flush 掉:
@派聪明 解释一下这个问题是怎么产生,怎么解决的
点赞
回复3 取消回复
派聪明 2026年07月22日 20:56
小派正在努力回答中, 耐心等待一下吧...
点赞
回复 取消回复
展开剩余 2 条回复

目录