Files
Cyrene_v2/plugins/tools/io/doc.go
T
AskaEth ce9af42945
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(search): 新增腾讯云·元宝搜索与秘塔搜索两家 provider
接入位表六家扩到八家,端点形状与错误码一字未改(契约 v0.87,仅两处名单)。

- tencent_wsa:POST {base}/SearchPro + Authorization Bearer,零签名。
  两条官方口径决定实现:HTTP 状态码恒 200(判失败只能读 Response.Error.Code);
  Pages 是「JSON 字符串数组」需二次解析。错误码按官方表分类
  (UnauthorizedOperation/AuthFailure.* = 凭据、ResourceUnavailable = 欠费、
  RequestLimitExceeded* = 限流),文案只带 code 不带上游 Message。
- metaso:POST {base}/api/v1/search,响应 schema 仅第三方来源且互相打架,
  实现走宽松解析(数组候选与字段名回退),逐处标「未实测」。
- 零新 env 键:两家只走 $DATA_DIR/config/plugins/web-search.json,
  .env / .env.example 未动;组合根 websearch_providers.go 零功能改动。
- 契约 v0.87:values.provider 合法闭集与 plugin.providers 候选各加两条,
  计数不变(14 + 25 + 40 = 79)。
2026-10-07 13:42:41 +08:00

59 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package io implements the built-in I/O and network tools for Cyrene v2
// (DevDocs/design/09-插件生态.md). It exposes file, image-crop, document-read, http,
// web-search, web-fetch and knowledge as pkg/plugins/sdk.Tool values, wired to the
// process-internal config, logger and runtime/memory retriever.
//
// Build the full configured tool set with New(cfg, opts...). Tools() and
// Register(reg) are convenience wrappers that use platform defaults; production
// wiring should call New(cfg, opts...) so the tools honor the real DataDir,
// search endpoint and knowledge retriever.
//
// Security constraints
//
// - file: every path is cleaned and validated to stay inside the configured
// data directory or an explicit whitelist; ".." traversal and symlink escape
// are rejected; reads and writes are size-capped.
// - image-crop: crops a region out of a large image so the agent can read it in
// parts (an image service downsamples big images); path sources go through the
// file sandbox, URL sources through the SSRF guard, and the returned data URI
// is size-capped.
// - http: GET/POST only, timeouts, response truncation and SSRF protection
// (loopback/private/link-local addresses and localhost are refused unless
// the host is on an explicit allowlist).
// - web-search: queries the configured search **provider** through the registry in
// plugins/tools/io/search (searxng / zhipu / aliyun_iqs / bocha / volcengine /
// baidu_qianfan / tencent_wsa / metaso; one provider per file, design/27 §4). The
// model sees exactly one
// tool whose schema never changes with the backend. A configured fallback chain is
// tried in order, and only an *unavailable* provider (missing key, auth / rate
// limit / quota / network failure) advances the chain — an empty result set does
// not, and it is reported as "no results" rather than as success. The result names
// the provider that actually answered. When no usable backend is configured the
// composition root passes WithoutSearch, so the tool is not registered at all — the
// model never sees a tool that could only error. Provider endpoints are operator
// configuration (the model only supplies the query), hence their hosts are trusted
// by this tool alone: a loopback fallback endpoint stays reachable without widening
// the shared http/web-fetch allowlist. API keys never reach logs, results or prompts.
// - web-fetch: size- and content-type-limited readable text extraction. 取正文走
// **降级链**(design/28 §1 M5 / §8 B5):直连 fetch →(失败 / 判定为动态页)
// ⇒ 浏览器渲染取正文,返回体用 `fetchedBy`(direct / browser)**如实标注实际
// 走了哪条**,两条档的失败原文分别留在 directError / browserError 里 —— 绝不静默
// 替换、绝不把失败说成成功。这一档**默认关**(WithBrowserFallback);被 SSRF
// 判定拒绝的目标**不换路**(换条路访问同一目标是绕过判定,不是降级)。
// - knowledge: RAG lookup over the runtime memory retriever; a clear message
// when no knowledge base is configured.
//
// # File-level mutual exclusion
//
// The two tools that touch files declare the file each call will touch
// (tool.FileTargeter, implemented in file_targets.go): file declares read for
// read/list/search and write for write; image-crop declares a read when its image
// is a local path. When another in-flight parallel leg is already operating on that
// same file, the call returns tool.ErrFileBusy with the file's state instead of
// running or queueing; two reads of one file still run in parallel, and calls that
// cannot touch a file (a sandbox-rejected path, an HTTP or data: URI source) declare
// nothing and keep their previous behavior.
//
// All tools are safe for concurrent use, as required by sdk.Tool.
package io