feat(cloud-agent): 看图工具 canvas_inspect_image 与同批多图工具结果配对
云端 Agent 过去只能凭标题或提示词猜画面:上游既有的 image_text_detect / image_annotation_render 是"组织 OCR 请求"和"渲染标注图当参考"两种不同能力,模型自己从不看像素。本 PR 新增只读工具 canvas_inspect_image,把画面本身交给模型。 - 交付走与生成任务参考图同一套短时签名链接(providerResourceURL),Agent 上下文只增长一个 URL,不内联 base64:一张 500 KB 的内联图片会同时顶穿请求体硬限与压缩阈值。 - 能力门控:仅当本轮渠道模型声明 text.references.maxImages > 0 时暴露该工具;VisionEnabled 在 建 run 时按渠道模型合同定格并持久化(工具授权每步从落库状态重建,客户端传入值被忽略)。 - 上下文治理:图片保留最近 3 个工具轮次,同一张图一轮内最多附图 2 次(之后只回执文字,确需 重看传 refresh=true),超出保留轮次换成逐图文字占位符并落 context_images_pruned 事件。 - 视觉锚点:模型看过后写进正文的一句观察记入 ReferenceAssets[].VisualNote 并跨轮继承,下一轮 不必重复看图,也不再对这张图声称"没有视觉识别证据"。 - 同批多图合并成一条 user 消息:图片只能挂 user 消息(tool 角色只接受字符串),逐张 append 会让 历史退化成 tool → user(图) → tool → user(图),被上游以"tool_calls 之后缺少对应 tool 消息" 拒绝。改为先缓冲到 PendingImageInspections,等整批 tool 结果入历史后合并成一条;两个 flush 点 共用同一个幂等助手,ask_user 提前结束路径单独 flush。缓冲必须进检查点:一次推进只执行一个 调用,整批跨越多次转移,每次转移都从 StateJSON 重新解码。 - 附带把上述 400 从"渠道余额不足"归类里拆出来(原文含 insufficient 会落进额度类目掩盖真因), 归为"会话里的工具调用与结果不匹配"。 - 文档补该能力口径与工具全集条目;前端零改动。
This commit is contained in:
1 parent
6f45240599
commit
080e4b901e
11 files changed
+1322
-7
No files matched your search
@@ -42,6 +42,11 @@ type CloudAgentRequest struct {
|
||||
MaxSteps int `json:"maxSteps,omitempty"`
|
||||
} `json:"budget"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
// VisionEnabled 决定是否给模型暴露看图工具,由服务端在创建 run 时按渠道模型合同
|
||||
// (text.references.maxImages > 0)重新推导并覆盖,客户端传入值一律被忽略;
|
||||
// 必须持久化:工具授权校验每步都从落库状态重建,丢掉这个标记会让模型看得见工具
|
||||
// 却被判为"未获本轮权限授权"。
|
||||
VisionEnabled bool `json:"visionEnabled,omitempty"`
|
||||
}
|
||||
|
||||
const cloudAgentMaxStepsLimit = 9999
|
||||
@@ -377,6 +382,9 @@ func (s *Service) CreateCloudAgentRun(userID string, req CloudAgentRequest, pare
|
||||
return nil, WrapAppError(409, "上一轮 Agent 历史记录不完整,无法继续对话;请新建对话", err)
|
||||
}
|
||||
inheritedPlan = parentState.Plan
|
||||
// 视觉事实跨轮继承:这一轮已经看过的画面与模型自己写下的观察随锚点带过来,
|
||||
// 否则新轮会把看过的图重新标成"没有视觉识别证据"并再花一次视觉 token。
|
||||
creativeAnchor = parentState.CreativeAnchor
|
||||
history = parentState.TextHistory
|
||||
if history == nil {
|
||||
history = cloudAgentLegacyHistory(parentState.Canonical.Messages, parent.Prompt)
|
||||
@@ -406,7 +414,11 @@ func (s *Service) CreateCloudAgentRun(userID string, req CloudAgentRequest, pare
|
||||
if len(encodedHistory) > cloudAgentHistoryMaxBytes {
|
||||
return nil, BadAuthRequest("对话上下文超过 64KB,请新建对话")
|
||||
}
|
||||
creativeAnchor, err = cloudAgentCreativeAnchorForCanvas(s.repo, userID, canvas, req.Prompt)
|
||||
var inheritedAnchor *cloudAgentCreativeAnchor
|
||||
if creativeAnchor.Version > 0 {
|
||||
inheritedAnchor = &creativeAnchor
|
||||
}
|
||||
creativeAnchor, err = cloudAgentCreativeAnchorForCanvas(s.repo, userID, canvas, req.Prompt, inheritedAnchor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -414,6 +426,9 @@ func (s *Service) CreateCloudAgentRun(userID string, req CloudAgentRequest, pare
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 看图能力取决于本轮渠道模型自己的合同(text.references.maxImages),在建 run 时定格并
|
||||
// 持久化:工具授权每步都从落库状态重建,运行期间不再变化,客户端传入值被忽略。
|
||||
req.VisionEnabled = s.cloudAgentVisionEnabled(req)
|
||||
canvasSummary := ""
|
||||
if len(req.ContextScope) != 0 {
|
||||
canvasSummary, err = cloudAgentCanvasSummary(canvas)
|
||||
|
||||
Reference in new issue
Block a user