Skip to content

feat: 改善 Agent 状态保持与工具失败语义 #1655

@massif-01

Description

@massif-01

Summary

本 issue 承接 #1595 中拆出的 P4-P6 可靠性主线,目标是提升长链路 Agent 和分析报告在状态保持、工具失败解释、边界场景处理上的稳定性。

它不以 prompt cache 命中率为主要目标,也不阻塞 #1595 的 P0-P3.5 工程闭环。#1595 关注“输入前缀可观测、可冻结、可安全发送 provider cache hint”;本 issue 关注“长任务里模型不要忘记目标、不要把工具失败误判成无数据、不要在数据冲突/缺失时给出过度确定的金融建议”。

背景

当前项目里,普通分析、Agent 问股、预取数据注入、工具调用和最终报告生成都依赖 LLM 对上下文的正确理解。随着工具调用变长,容易出现几类问题:

  • 初始目标、用户约束或已确认事实位于长上下文中部,后续模型容易忽略。
  • 工具失败、数据源超时、fallback、stale 数据和真实空数据在 prompt 中表达不够统一。
  • LLM 可能把 None、空列表、N/A、provider timeout、数据过期都理解成“没有风险”或“没有数据”。
  • 同一失败路径如果没有 fingerprint 和 retry 预算,Agent 可能重复尝试;如果完全禁止重试,又可能错过 transient failure 恢复。
  • 边界场景下,最终报告虽然 JSON schema 通过,但金融建议可能过度自信。

Scope

本 issue 覆盖:

  • P4:Agent 显式状态复述与 lost-in-the-middle 控制。
  • P5:工具失败上下文、负样本保留、数据质量语义层。
  • P6:边界场景策略与质量回归,不破坏最终 JSON schema。

本 issue 不覆盖:

  • provider prompt cache 参数接入、cache usage 归一化、PromptBlock Compiler 主线;这些属于 perf: 提高 LLM 分析输入的 prompt cache 利用率 #1595
  • 论文 benchmark、ablation、真实 provider controlled experiment;这些属于研究 issue。
  • 一次性重写整个 Agent 框架或所有数据源适配器。

P4:Agent 显式状态复述与长上下文保持

为什么要做

Agent 工具循环 append-only 可以保留证据,但不能自动解决 lost-in-the-middle。随着消息增长,最初目标、关键失败路径和中间确认过的事实可能落在上下文中部,后续模型更容易漏看。

设计方向

把 Agent 上下文分成四层:

  1. stable_prefix:角色、工具使用原则、输出契约,尽量稳定。
  2. transcript_log:append-only 原始对话和工具证据,作为审计来源,不必每轮全量进入 prompt。
  3. external_agent_state:runner / orchestrator 持有的可变结构化状态。
  4. prompt_tail_state:每轮编译 prompt 时插入尾部的最新状态快照,不写回历史 transcript。

状态快照至少包含:

  • goal:当前任务目标。
  • confirmed_facts:已确认事实。
  • open_questions:待确认问题。
  • failed_paths:最近失败路径和原因摘要。
  • data_limitations:缺失、过期、fallback、低置信数据限制。
  • next_step:下一步计划。

每个状态项应包含:

  • source_refs: message_id / tool_call_id / data_source_id
  • evidence_type: observed | inferred | user_provided | tool_reported | model_generated
  • confidence_level: high | medium | low | unknown
  • status: active | superseded | retracted
  • superseded_by: 新状态项 id 或 null
  • updated_at

AgentStateItem schema:

AgentStateItem(
    id: str,
    item_type: Literal[
        "goal",
        "confirmed_fact",
        "open_question",
        "failed_path",
        "data_limitation",
        "next_step",
    ],
    content: str,
    source_refs: list[str],
    evidence_type: Literal[
        "observed",
        "inferred",
        "user_provided",
        "tool_reported",
        "model_generated",
    ],
    confidence_level: Literal["high", "medium", "low", "unknown"],
    status: Literal["active", "superseded", "retracted"],
    superseded_by: str | None,
    created_at: str,
    updated_at: str,
)

规则:

  • confirmed_fact 必须有至少一个 source_ref
  • confirmed_fact.evidence_type 不能只是无来源的 model_generated
  • data_limitation 不能静默删除;只能被更新、更高质量的数据 supersede。
  • failed_path 可以压缩,但必须保留 fingerprint、retry count、latest reason 和 impact。
  • inferred facts 不能在无证据时提升成 confirmed facts。

prompt_tail_state 必须有 token budget,避免尾部状态本身无限增长:

max_prompt_tail_state_tokens: 800
max_confirmed_facts: 20
max_failed_paths: 10
max_data_limitations: 10
max_open_questions: 10
max_next_steps: 5

Overflow policy:

  1. 始终保留当前 goal
  2. 保留 blocking failures 和 high-impact data limitations。
  3. 保留最新 source-backed confirmed facts。
  4. repeated failures 按 fingerprint 合并。
  5. low-impact repeated failures 可摘要。
  6. resolved open questions 可丢弃。
  7. unresolved blocking data_limitations 不得静默丢弃。
  8. failed_paths 只能 marked superseded / compacted,不能无痕删除。

不该怎么做

  • 不把每轮 recap 当成新历史消息无限追加,避免旧 recap 和新 recap 相互矛盾。
  • 不让模型把 inferred fact 自动提升成 confirmed fact。
  • 不把全局状态无差别发给每个 specialist;多 Agent 场景只传相关 state slice。

Cache-compatible compaction and session boundaries

Reasonix 的设计提醒我们:long-context reliability 不能只问“是否压缩”,还要问“何时允许破坏缓存前缀,以及压缩后如何追溯原始证据”。本 issue 应把 compaction 视为显式状态转移,而不是隐式 prompt 变短。

新增概念:

CacheResetPoint(
    reason: Literal[
        "manual_compaction",
        "token_budget_compaction",
        "prompt_builder_version_change",
        "tool_schema_change",
        "structured_schema_change",
        "provider_route_change",
    ],
    affected_prefix_hash_before: str,
    affected_prefix_hash_after: str,
    source_refs: list[str],
    created_at: str,
)

规则:

  • compaction 必须生成 declared CacheResetPoint
  • compaction 前后的 state summary 必须保留 source_refs,否则不能成为 confirmed facts;
  • dropped transcript originals 应进入 archive,至少保留可审计引用,不要求第一阶段实现完整检索 UI;
  • 后续可增加 read-only history retrieval,用于从 archive 查回被压缩的工具证据;
  • planner / executor / specialist 如果使用不同 model、provider、tool schema 或 stable prefix,应使用 separate sessions,只通过 structured handoff 传递 plan / state slice;
  • 如果同一模型、同一 schema、同一 stable prefix 下只是普通连续任务,不需要为了形式上“多 Agent”强拆 session。

这部分与 #1595 的关系是:#1595 负责识别 prefix drift 和 cache diagnostics;本 issue 负责定义哪些 drift 是 long-context compaction 下被声明、被审计、可追溯的 reset point。

P5:工具失败上下文与数据质量语义层

为什么要做

工具失败不是噪声。它能告诉模型“路径 A 已失败”,但如果直接塞 traceback,会引入敏感信息、噪声和 prompt injection 风险。更大的问题是:失败、缺失、空结果、过期、fallback 在 prompt 里如果没有结构化区分,模型会误用数据。

设计方向

不要用单个 DataStatus enum 表达数据质量。retrieval result、availability、freshness、provenance、confidence 是互相独立的维度。

DataQuality semantic layer:

DataQuality(
    retrieval_status: Literal[
        "ok",
        "provider_failure",
        "timeout",
        "rate_limited",
        "parse_error",
        "unknown",
    ],
    availability: Literal[
        "present",
        "missing",
        "empty",
        "partial",
        "unknown",
    ],
    freshness: Literal["fresh", "stale", "unknown"],
    provenance: Literal[
        "primary",
        "fallback",
        "cached",
        "synthetic",
        "manual",
        "unknown",
    ],
    confidence_level: Literal["high", "medium", "low", "unknown"],
)

DataValue(Generic[T]):
    quality: DataQuality
    value: T | None
    source: str | None
    observed_at: str | None
    freshness_seconds: int | None
    error_ref: str | None

示例:

fresh_empty_news:
  retrieval_status: ok
  availability: empty
  freshness: fresh
  provenance: primary
  confidence_level: high

fallback_stale_quote:
  retrieval_status: ok
  availability: present
  freshness: stale
  provenance: fallback
  confidence_level: low

primary_timeout_no_fallback:
  retrieval_status: timeout
  availability: unknown
  freshness: unknown
  provenance: primary
  confidence_level: unknown

工具失败结构建议:

tool_name
provider
error_type
sanitized_reason
affected_fields
data_freshness
fallback_used
data_impact: none | low | medium | high | blocking
trusted_model_hint_code
failure_fingerprint
retry_count
retry_budget
first_seen_at
last_seen_at
suppressed_duplicates_count
next_retry_after

关键约束:

  • trusted_model_hint_code 只能由项目内部 wrapper 生成,不能复制 provider 返回文本。
  • provider 原始错误只能进入 sanitized_reason,且不能包含 imperative prompt。
  • failure_fingerprint 必须使用 HMAC,而不是裸 hash。
  • failure_fingerprint = HMAC_SHA256(secret, tool_name + provider + error_type + redacted_param_signature)
  • Prompt 中只放最近 N 个失败摘要,不暴露完整 traceback。

redacted_param_signature 可包含参数名、粗粒度 value class、market group、request type、time bucket、provider name;不得包含 raw user text、holdings、webhook、API key、API URL、完整请求/响应体、raw stock portfolio、raw prompt 或 raw dynamic context。

trusted_model_hint_code 使用受控枚举和内部模板:

DO_NOT_INFER_INTRADAY_TREND
DISCLOSE_FALLBACK_SOURCE
LOWER_CONFIDENCE_FOR_STALE_DATA
AVOID_PRECISE_VALUATION_WITH_MISSING_FINANCIALS
REPORT_PROVIDER_FAILURE_NOT_NO_DATA
WAIT_FOR_CONFIRMATION_UNDER_CONFLICT

测试断言 hint code,而不是脆弱自然语言文本。Provider error text 不能变成 hint。

data_impact 需要 deterministic mapping,例如:

  • 实时行情失败对盘中交易建议可为 blocking,对长期基本面分析可为 medium
  • 新闻失败对事件驱动分析可为 high,对纯技术分析可为 low
  • 财报失败对估值任务可为 blocking,对短线价格任务可为 medium

不该怎么做

  • 不用空值、空列表、N/A暂无 掩盖 provider failure。
  • 不把 provider 错误中的 URL、API path、文件路径、请求体、响应体、token、webhook 写进 prompt 或普通日志。
  • 不让每个工具自由定义 impact 等级;必须有统一 mapping 和测试。

P6:边界场景策略与质量回归

为什么要做

最终 JSON schema 稳定并不等于金融分析质量稳定。数据缺失、新闻冲突、技术面和基本面冲突、盘中/盘后状态差异、stale 数据等场景下,模型最容易给出过度确定或互相矛盾的建议。

设计方向

覆盖以下边界场景:

  • 数据缺失:明确区分无法判断和真实无数据。
  • 新闻冲突:利好和利空同现时,不得只选择单边叙事。
  • 技术面与基本面冲突:需要说明冲突来源和时间尺度差异。
  • 盘中/盘后状态不同:盘中指标不能误用于盘后结论。
  • stale 数据:过期行情不得生成高置信盘中建议。
  • fallback 数据:最终报告必须披露 fallback 来源和限制。
  • 无法判断:允许输出低置信、等待确认,而不是强行买/卖。

质量回归指标:

  • data_limitation_disclosure_rate
  • false_no_data_rate
  • unsupported_inference_rate
  • stale_data_misuse_rate
  • fallback_disclosure_rate
  • financial_safety_regression

金融安全回归至少覆盖:

  • 严重风险下不得无条件高置信买入。
  • stale 行情不得生成高置信盘中交易建议。
  • 财报缺失不得生成精确估值结论。
  • fallback 数据必须披露。

BoundaryCaseFixture schema:

BoundaryCaseFixture:
  id: string
  market_identity: object
  scenario_tags:
    - stale_quote
    - missing_financials
    - provider_failure
    - fallback_news
    - technical_fundamental_conflict
    - bullish_bearish_news_conflict
    - trading_halt
    - cross_market_holiday
  provided_context:
    quote: object | null
    news: list
    financials: object | null
    technicals: object | null
    tool_failures: list
  expected_disclosures:
    - stale_data
    - fallback_source
    - provider_failure
    - missing_financials
    - uncertainty
  forbidden_claims:
    - high_confidence_intraday_buy
    - precise_valuation_without_financials
    - no_risk_when_provider_failed
    - claim_news_absent_when_provider_failed
  required_reasoning_elements:
    - conflict_source
    - uncertainty_statement
    - data_limitation_reference

FinancialDecision evaluator abstraction:

如果现有 report JSON schema 没有直接暴露这些字段,evaluator 从现有字段抽取,不要求第一版修改最终报告 schema。

FinancialDecision(
    decision_type: Literal["buy", "hold", "sell", "watch", "unknown"],
    confidence_level: Literal["high", "medium", "low", "unknown"],
    horizon: Literal["intraday", "short_term", "medium_term", "long_term", "unknown"],
    action_strength: Literal["strong", "moderate", "weak", "wait", "unknown"],
    data_limitation_refs: list[str],
)

不该怎么做

  • 不通过随机化 JSON 字段、字段顺序、字段名或枚举来制造“结构熵”。
  • 不把 few-shot 样例变成格式噪声。样例多样性只能体现在场景覆盖和判断路径上,最终输出 contract 必须稳定。
  • 不在普通分析模式暴露完整 traceback。

Progress

Phase 状态 PR 说明
P4 未开始 - Agent 显式状态复述与 long-context fidelity
P5 未开始 - 工具失败上下文、DataQuality、retry/fingerprint
P6 未开始 - 边界场景策略与金融安全质量回归

Acceptance Criteria

  • [P4] Agent prompt 每轮只注入最新 prompt_tail_state,旧 recap 不在 prompt 中无限累积。

  • [P4] 状态项包含 source_refsevidence_typeconfidence_levelstatussuperseded_by

  • [P4] confirmed facts 必须有 source refs,不能由无来源 model_generated 事实直接生成。

  • [P4] prompt_tail_state 渲染遵守 token budget。

  • [P4] Overflow tests 验证 goal、blocking failures 和 unresolved data limitations 被保留。

  • [P4] data_limitationsfailed_paths 不会在状态更新中静默丢失。

  • [P4] 多 Agent / specialist 只接收相关 state slice。

  • [P4] Compaction 生成 declared CacheResetPoint,记录 reason、before/after prefix hash 和 source refs。

  • [P4] Compaction 后 dropped originals 至少进入可审计 archive,并保留从 summary 回到原始 transcript/tool evidence 的引用。

  • [P4] planner / executor / specialist 在不同 model、provider、tool schema 或 stable prefix 下使用 separate sessions,并通过 structured handoff 传递必要 state slice。

  • [P5] DataQuality 分离 retrieval_statusavailabilityfreshnessprovenanceconfidence_level

  • [P5] Tool error tests 覆盖 fallback+fresh、fallback+stale、empty+fresh、timeout+unknown、partial+fresh。

  • [P5] 工具失败包含 sanitized_reasonaffected_fieldsdata_impacttrusted_model_hint_codefailure_fingerprint、retry 相关字段。

  • [P5] failure_fingerprint 使用 HMAC 和 redacted parameter signature,不使用 raw hash 或 raw params。

  • [P5] trusted_model_hint_code 只由内部 wrapper 生成,rendered hint text 只来自内部模板。

  • [P5] 错误 reason 有 redaction 和长度限制,不泄露 token、webhook、路径、请求体或响应体。

  • [P5] data_impact 由 deterministic mapping 产生,并有单元测试覆盖。

  • [P6] 最终报告能区分数据不存在、数据源失败、数据过期和 fallback 数据。

  • [P6] BoundaryCaseFixture 与研究 benchmark 共用。

  • [P6] Financial safety tests 使用显式 decision labels 或 evaluator-extracted labels。

  • [P6] Provider failure 不能被报告为 true no-data。

  • [P6] 边界场景下 JSON schema 保持稳定,不随机化字段或格式。

  • [P6] 金融安全回归覆盖 stale、缺财报、fallback、重大风险等场景。

Test Plan

  • Agent state tests:长链路工具调用后 prompt_tail_state 位于 prompt 尾部,旧 recap 不累积。

  • State faithfulness tests:confirmed facts 必须有 source refs;inferred facts 不得自动提升;superseded/retracted items 有测试覆盖。

  • Tail state budget tests:overflow 时保留 goal、blocking failures、unresolved data limitations,并按 fingerprint 压缩 repeated failures。

  • CacheResetPoint tests:token budget compaction、prompt builder version change、tool schema change 能生成 declared reset point;普通 tail state 更新不应被误标成 reset。

  • Archive traceability tests:compaction summary 中的 confirmed facts / data limitations 能通过 source refs 回到 archive 或 transcript evidence。

  • Separate session tests:不同 planner/executor model 或 tool schema 不混入同一 session;同 stable prefix 的连续普通任务不被强制拆 session。

  • Tool error tests:timeout、empty、missing、stale、fallback、partial、provider failure 分别生成可区分 DataQuality。

  • Redaction tests:错误 reason 清理 API key、webhook、URL query、文件路径、请求/响应体,并限制长度。

  • Fingerprint privacy tests:failure fingerprint 输入清理 raw user text、holdings、URL、request/response body、token、webhook。

  • Trusted hint tests:断言 trusted_model_hint_code,并验证 provider text 不能注入指令。

  • Retry tests:相同 HMAC failure fingerprint 聚合计数,不重复塞满 prompt;transient failure 仍按 retry budget 允许重试。

  • Data impact tests:按任务类型验证 deterministic impact mapping。

  • Boundary fixture tests:stale quote、missing financials、provider failure、fallback news、技术/基本面冲突、新闻冲突均有 fixture。

  • Report contract tests:边界场景下最终 JSON schema、字段枚举和语言规则不回归。

  • Financial safety tests:严重风险、stale 数据、财报缺失、fallback 数据下不输出过度确定建议。

Related

说明:如果论文实验消费本 issue 的 AgentStateItem、DataQuality、ToolFailure 或 BoundaryCaseFixture,必须在 massif-01#4ResearchArtifactContract 中记录对应 schema version、commit、issue revision 和 privacy profile。

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions