docs(comments): 注释中文化 · internal/runtime/orchestration(29 文件,纯替换)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 只翻注释/文档字符串:剥离注释后代码行逐字节不变(机械校验只剩允许的行尾注释形态)
- 裁决编号与 design 节号(02.4.3 / 11.4.2 / design/05 §9 等)逐字保留
- 复核:go build 绿;orchestration 及其子包(audit/comm)测试绿;gofmt 干净
This commit is contained in:
AskaEth committed 2026-10-04 13:30:04 +08:00
1 parent fe90f64a56
commit eb1828ee7a
29 files changed
+751 -949

No files matched your search

@@ -11,43 +11,40 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/logger"
)
// Sentinel errors returned by Approver. Use errors.Is to test for them.
// Approver 返回的哨兵错误。用 errors.Is 判断它们。
var (
// ErrActionMissing is returned when the action is blank.
// ErrActionMissing 在 action 为空时返回。
ErrActionMissing = errors.New("audit: action is missing")
// ErrUnknownActor is returned when the acting role name is blank. This
// package has no role registry, so a blank name is the only "unknown" it
// can detect; whether a role is truly authorized is decided upstream.
// ErrUnknownActor 在扮演角色的名字为空时返回。本包没有角色注册表,因此空名字是它
// 唯一能检出的「未知」;某个角色是否真的获授权由上游决定。
ErrUnknownActor = errors.New("audit: actor is unknown")
// ErrExplainMissing is returned when the explanation is blank.
// ErrExplainMissing 在说明为空时返回。
ErrExplainMissing = errors.New("audit: explanation is missing")
)
// reviewerMain is the name recorded for the main agent that performs the review.
// reviewerMain 是执行该审查的主 agent 所记录的名字。
const reviewerMain = "main"
// Approval is the immutable record of a single main-agent review-and-approve of
// a subagent's sensitive action.
// Approval 是对子代理敏感操作的一次主代理代审的不可变记录。
type Approval struct {
// Actor is the acting role name (角色名).
// Actor 是扮演角色的名字(角色名)。
Actor string
// Action is the sensitive action being approved.
// Action 是被批准的敏感操作。
Action string
// Description is the explanation of what the actor wants to do (what the
// action means). It is named Description rather than Explain because the
// Explain method (below) returns the frontend notice.
// Description 是对扮演者想做什么的说明(该操作意味着什么)。它命名为
// Description 而非 Explain,因为 Explain 方法(见下)返回的是前端提示短句。
Description string
// Allowed reports whether the action was granted.
// Allowed 报告该操作是否获准。
Allowed bool
// Reviewer is the main agent that performed the review.
// Reviewer 是执行该审查的主 agent。
Reviewer string
// At is when the review-and-approve happened.
// At 是代审发生的时刻。
At time.Time
}
// Explain returns the short frontend notice describing the review outcome. For
// an allowed action it is "「<actor> 想要干什么,我审查后批准了」"; for a denied
// action it uses "我审查后拒绝了". Actor is interpolated in place of <actor>.
// Explain 返回描述审查结果的前端提示短句。获准时是
// "「<actor> 想要干什么,我审查后批准了」";被拒时用 "我审查后拒绝了"。<actor> 处会
// 代入 Actor。
func (a Approval) Explain() string {
if a.Allowed {
return "「" + a.Actor + " 想要干什么,我审查后批准了」"
@@ -55,26 +52,23 @@ func (a Approval) Explain() string {
return "「" + a.Actor + " 想要干什么,我审查后拒绝了」"
}
// Approver records main-agent review-and-approve events and their audit logs.
// Approver 记录主代理代审事件及其审计日志。
type Approver struct {
log *slog.Logger
}
// NewApprover builds an Approver that writes audit logs through log. The logger
// is tagged with source "orchestration/audit". A nil logger is allowed, making
// the approver record silently (no log output).
// NewApprover 构建一个经 log 写入审计日志的 Approver。该 logger 会被标注来源
// "orchestration/audit"。允许传入 nil logger,此时 approver 静默记录(无日志输出)。
func NewApprover(log *slog.Logger) *Approver {
return &Approver{log: logger.WithSource(log, "orchestration/audit")}
}
// ReviewAndApprove validates the request, checks ctx, writes a structured audit
// log (who / what / when / result), and returns the recorded approval with
// Allowed set to true.
// ReviewAndApprove 校验请求、检查 ctx、写入结构化审计日志(谁 / 什么 / 何时 / 结果),
// 并返回记录下来的 approval,其 Allowed 置为 true。
//
// Whether an action is actually sensitive and whether it should be granted is
// decided by the upper policy layer; this method only records, narrates and
// returns the result model. It returns ErrUnknownActor / ErrActionMissing /
// ErrExplainMissing for blank inputs and wraps ctx.Err() when ctx is canceled.
// 某项操作是否真的敏感、是否应予批准由上层策略决定;本方法只记录、叙述并返回结果模型。
// 入参为空时返回 ErrUnknownActor / ErrActionMissing / ErrExplainMissing,ctx 被取消时
// 包装 ctx.Err()。
func (a *Approver) ReviewAndApprove(ctx context.Context, actor, action, describe string) (*Approval, error) {
if err := validateApproval(actor, action, describe); err != nil {
return nil, err
@@ -94,7 +88,7 @@ func (a *Approver) ReviewAndApprove(ctx context.Context, actor, action, describe
return ap, nil
}
// logAudit writes the structured who/what/when/result audit record.
// logAudit 写入结构化的「谁/什么/何时/结果」审计记录。
func (a *Approver) logAudit(ap *Approval) {
if a.log == nil {
return
@@ -109,7 +103,7 @@ func (a *Approver) logAudit(ap *Approval) {
)
}
// validateApproval rejects blank inputs with the matching sentinel.
// validateApproval 以对应的哨兵错误拒绝空入参。
func validateApproval(actor, action, describe string) error {
if strings.TrimSpace(actor) == "" {
return fmt.Errorf("%w: actor is blank", ErrUnknownActor)
@@ -11,7 +11,7 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/logger"
)
// newSilentApprover builds an Approver whose audit log is discarded.
// newSilentApprover 构建一个审计日志被丢弃的 Approver。
func newSilentApprover() *Approver {
return NewApprover(logger.New(logger.Options{Out: io.Discard}))
}
+14 -19
View File
@@ -1,24 +1,19 @@
// Package audit provides the subagent-auditing primitives specified by the
// Cyrene orchestration design (DevDocs/design/05-子代理.md §3 and §6) and the
// security/audit requirements in DevDocs/design/11-安全与权限.md §4 and §8.
// Package audit 提供 Cyrene 编排设计所规定的子代理审计原语
// (DevDocs/design/05-子代理.md §3 与 §6),以及
// DevDocs/design/11-安全与权限.md §4 与 §8 里的安全/审计要求。
//
// It has two halves:
// 它有两半:
//
// - ContextAuditor gives a main agent read-only, traceable access to a
// subagent's context (a []model.Message) so it can audit and pinpoint what
// the subagent did: the full context, a tail window, or a keyword hit with
// its surrounding lines. It is a pure value type over the message list the
// orchestral layer hands it; it holds no storage and no policy.
// - ContextAuditor 给主 agent 只读、可追溯地访问某个子代理的上下文
// (一个 []model.Message),使其能审计并定位子代理做过什么:整段上下文、
// 尾部窗口,或带上下行的关键词命中。它是作用于编排层交给它的消息列表之上的
// 纯值类型;不持有存储,也不持有策略。
//
// - Approver records a main-agent review-and-approve (代审授权) of a
// subagent's sensitive action. It writes a structured audit log (who / what
// / when / result) and produces the short frontend notice "「xxx 想要干什么,
// 我审查后批准了」". Whether an action is actually sensitive and whether it is
// allowed is decided by the upper policy layer; this package only records,
// narrates, and returns the result model — it never judges permission.
// - Approver 记录对子代理敏感操作的「主代理代审授权」。它写入结构化审计日志
// (谁 / 什么 / 何时 / 结果),并产出前端短句「「xxx 想要干什么,
// 我审查后批准了」」。某项操作是否真的敏感、是否获准由上层策略决定;本包只
// 记录、叙述并返回结果模型 —— 绝不判断权限。
//
// The package is a leaf: it depends only on the standard library,
// internal/runtime/model and internal/logger, and never imports the agent loop
// or other runtime packages, so it can sit inside runtime/orchestration without
// an import cycle.
// 本包是叶子:只依赖标准库、internal/runtime/model 与 internal/logger,绝不导入 agent
// 循环或其他运行时包,因此能安放在 runtime/orchestration 内而不形成导入环。
package audit
+21 -29
View File
@@ -9,52 +9,47 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
)
// ErrQueryEmpty is returned by ContextAuditor.Keyword when the query is empty
// or blank. Use errors.Is to test for it.
// ErrQueryEmpty 在 ContextAuditor.Keyword 的 query 为空或全为空白时返回。用 errors.Is
// 判断它。
var ErrQueryEmpty = errors.New("audit: query is empty")
// contextRadius is how many messages on each side of a keyword hit are included
// in the surrounding window (one above and one below).
// contextRadius 是关键词命中处两侧各纳入多少条消息作为上下行窗口(上下各一条)。
const contextRadius = 1
// Located describes a single keyword match within a subagent context.
// Located 描述子代理上下文中的单次关键词命中。
type Located struct {
// Line is the 1-based index of the matched message in the full context.
// Line 是命中消息在完整上下文中的 1 基下标。
Line int
// Surrounding is a window centered on the matched message: the message
// immediately before it, the matched message, and the message immediately
// after it, clamped at the context boundaries. It always contains the
// matched message.
// Surrounding 是以命中消息为中心的窗口:紧邻其前的那条消息、命中消息本身,
// 以及紧随其后的一条消息,在上下文边界处截断。它始终包含命中消息。
Surrounding []model.Message
}
// ContextAuditor provides read-only, traceable access to a subagent's context.
// It snapshots the message list at construction so later mutation by the caller
// does not change what the auditor reports. It is a value type that holds no
// storage or policy; the orchestral layer decides which context to inspect.
// ContextAuditor 提供对某个子代理上下文的只读、可追溯访问。它在构造时快照消息列表,
// 因此调用方此后的修改不会改变审计器所报告的内容。它是值类型,不持有存储或策略;由
// 编排层决定要检视哪段上下文。
type ContextAuditor struct {
messages []model.Message
}
// NewContextAuditor snapshots messages so subsequent caller mutations do not
// affect the auditor. A nil input is allowed and yields an empty context.
// NewContextAuditor 快照 messages,使调用方后续的修改不影响审计器。允许传入 nil,
// 此时得到一个空上下文。
func NewContextAuditor(messages []model.Message) ContextAuditor {
cp := make([]model.Message, len(messages))
copy(cp, messages)
return ContextAuditor{messages: cp}
}
// Full returns a defensive copy of the entire context.
// Full 返回整段上下文的防御性拷贝。
func (a ContextAuditor) Full() []model.Message {
cp := make([]model.Message, len(a.messages))
copy(cp, a.messages)
return cp
}
// Tail returns the last n messages of the context in their original order.
// Tail 按原顺序返回上下文最后 n 条消息。
//
// n <= 0 yields an empty, non-nil slice; n >= len returns a copy of the full
// context. The result is a defensive copy.
// n <= 0 得到空但非 nil 的切片;n >= len 返回整段上下文的拷贝。结果是防御性拷贝。
func (a ContextAuditor) Tail(n int) []model.Message {
if n <= 0 {
return []model.Message{}
@@ -68,13 +63,11 @@ func (a ContextAuditor) Tail(n int) []model.Message {
return out
}
// Keyword locates the first message whose text contains query, using a
// case-insensitive substring match against the message's text parts (image
// payloads are ignored). It returns the message's 1-based index and a
// surrounding window, or nil, nil when there is no match.
// Keyword 定位首个文本包含 query 的消息,对消息的文本分段做大小写不敏感的子串匹配
// (图片载荷被忽略)。它返回该消息的 1 基下标与一个上下行窗口;无命中时返回
// nil, nil。
//
// It returns ErrQueryEmpty for a blank query and wraps ctx.Err() when ctx is
// canceled. Only the earliest match is reported.
// query 为空时返回 ErrQueryEmpty,ctx 被取消时包装 ctx.Err()。只报告最早的命中。
func (a ContextAuditor) Keyword(ctx context.Context, query string) (*Located, error) {
if strings.TrimSpace(query) == "" {
return nil, fmt.Errorf("%w: query is blank", ErrQueryEmpty)
@@ -91,8 +84,7 @@ func (a ContextAuditor) Keyword(ctx context.Context, query string) (*Located, er
return nil, nil
}
// window returns up to 2*contextRadius+1 messages centered on index i, clamped
// to the context boundaries.
// window 返回以 index i 为中心、至多 2*contextRadius+1 条消息,并在上下文边界处截断。
func (a ContextAuditor) window(i int) []model.Message {
lo := i - contextRadius
hi := i + contextRadius
@@ -110,7 +102,7 @@ func (a ContextAuditor) window(i int) []model.Message {
return out
}
// messageText returns the concatenated text of a message's text parts.
// messageText 返回一条消息各文本分段拼接后的文本。
func messageText(m model.Message) string {
var b strings.Builder
for _, c := range m.Content {
@@ -8,7 +8,7 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
)
// texts builds a context of text-only messages with the given body texts.
// texts 用给定的正文文本构建一个仅含文本消息的上下文。
func texts(role model.Role, bodies ...string) []model.Message {
out := make([]model.Message, 0, len(bodies))
for _, b := range bodies {
@@ -17,8 +17,8 @@ func texts(role model.Role, bodies ...string) []model.Message {
return out
}
// sameMessage compares a message by role and concatenated text; the test
// messages are text-only, so this is sufficient for equality.
// sameMessage 按角色与拼接后的文本比较两条消息;测试消息都是纯文本,因此这足以判定
// 相等。
func sameMessage(a, b model.Message) bool {
return a.Role == b.Role && messageText(a) == messageText(b)
}
@@ -29,7 +29,7 @@ func TestNewContextAuditor_Snapshot(t *testing.T) {
msgs := texts(model.RoleUser, "alpha", "bravo", "charlie")
aud := NewContextAuditor(msgs)
// Mutating the caller's slice after construction must not affect the audit.
// 构造之后再改调用方的切片,不得影响审计结果。
msgs[0].Role = model.RoleSystem
got := aud.Full()
if got[0].Role != model.RoleUser {