Skip to content

Hello Agent 教程(GL-05 · 二开方 curl 级全流程)

状态基准:2026-08-30(SM 波次收官口径;本文自 DX1-2 起归属对外指南树 docs/guides/tutorial/,原路径 docs/design/z.reference/hello-agent-tutorial.md 已留迁移注记)。 读者:拿到租户 + M2M client 凭据的二开方开发者(开通步骤见 tenant-onboarding-runbook.md)。 目标:30 分钟内独立跑通「取 token → 提交含真实 LLM 节点的 SOP 任务 → SSE 收全程事件 → 查用量」。 底稿:以下命令 2026-07-07 全部在 ECS-B 真实栈实测跑通(scripts/gl-acceptance.sh --hello-agent 即其脚本化形态)。 环境变量约定BASE=https://<EIP>(单源 TLS 网关);自签证书阶段 curl 需 -k

0. 你需要的东西

来源
CLIENT_ID / CLIENT_SECRETops 发放(M2M client,client credentials grant)
租户/用户 UUID已配置在 token claims 里,无需手工传参(API 从 JWT 取,绝不从 body 取)
scopes本教程需要 task:submit + task:read(建模另需 builder:* / sop:* / agent:create

1. 取 token(client credentials)

bash
TOKEN=$(curl -sk -X POST "$BASE/auth/realms/agenthub/protocol/openid-connect/token" \
  -d "grant_type=client_credentials&client_id=$CLIENT_ID" \
  --data-urlencode "client_secret=$CLIENT_SECRET" | python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')
  • token 是 RS256 JWT,iss=https://<EIP>/auth/realms/agenthubaudagenthub-console
  • 常见 401:直连 Keycloak :8080 取 token(iss 不匹配)、client secret 轮换后未更新、permissions 属性未配置。

2. 建模(项目 → Agent → SOP → 发布)

已有发布 SOP 的租户可跳到 §3。全部端点均为 Envelope 响应({"code","message","data"})。

UI 路径(Tier 0 配置台,ADR-0043):§2.1/§2.2 的项目与 Agent 建模、以及模型组/模型注册/路由与知识库文档上传,也可经 web-builder 配置台在浏览器完成——部署 --with-frontend 后打开 https://<EIP>/builder/ → OIDC 登录 → 项目列表 → 项目详情 → Resource Modeling(Models / Knowledge Bases / Agents 三个 Tab;Agent 发布流含模型组+KB 绑定与乐观锁)。SOP 建模(§2.3/§2.4)自 Tier 1 起也可全程走画布(Tier 1 建模控制台波次,2026-06 落地):项目详情 → SOP Design 列表页新建 SOP → 打开画布(左侧节点面板拖 start → single_agent → tool(kb_search) → branch → end,右侧面板配 agent/input_mapping/CEL 边条件,底栏配 Globals)→ 保存草稿(刷新可回读)→ 发布;调试走 Debug Panel(单步 + run-e2e 端到端时间轴)。本节 curl 路径保留为 API/自动化基线。运维健康检查:bash scripts/verify-testbed.sh --frontend-health

bash
# 2.1 项目(scope builder:project:write)
curl -sk -X POST "$BASE/api/v1/builder/projects" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{"name":"hello-agent","slug":"hello-agent"}'

# 2.2 Agent(scope agent:create)
curl -sk -X POST "$BASE/api/v1/builder/agents" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"project_id":"<project-uuid>","name":"hello","system_prompt":"You are a helpful assistant."}'

# 2.3 SOP + 版本(scope sop:create):最小 start → agent → end 工作流
curl -sk -X POST "$BASE/api/v1/builder/sops" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{"project_id":"<project-uuid>","slug":"hello-sop","name":"Hello SOP"}'

curl -sk -X POST "$BASE/api/v1/builder/sop-versions" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{
    "sop_id": "<sop-uuid>",
    "workflow_spec": {
      "spec_version": "1.0",
      "entry_node_id": "start",
      "nodes": [
        {"node_id": "start", "node_type": "system", "executor_type": "SystemNodeExecutor", "system_action": "start"},
        {"node_id": "agent_node", "node_type": "single_agent", "executor_type": "AgentNodeExecutor",
         "agent_id": "<agent-uuid>", "prompt": "Answer briefly.", "input_mapping": {"q": "topic"}},
        {"node_id": "end", "node_type": "system", "executor_type": "SystemNodeExecutor", "system_action": "end"}
      ],
      "edges": [{"from": "start", "to": "agent_node"}, {"from": "agent_node", "to": "end"}]
    }
  }'

# 2.4 发布(scope sop:publish)
curl -sk -X POST "$BASE/api/v1/builder/sops/<sop-uuid>/publish" -H "Authorization: Bearer $TOKEN"

条件分支:edges[] 支持 CEL 条件(如 ctx.score > 80),由 LangGraph 条件路由求值(H-A05 修复后 节点输出会合并进 context 供 CEL 读取)。

3. 提交任务(免计费默认:estimated_points=0

bash
SUBMIT=$(curl -sk -X POST "$BASE/api/v1/app/workflow-tasks" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{
    "sop_version_id": "<sop-version-uuid>",
    "project_id": "<project-uuid>",
    "app_id": "hello-agent",
    "channel": "api",
    "input_data": {},
    "context_variables": {"topic": "Say hello to AgentHub"},
    "idempotency_key": "hello-'$(date +%s)'",
    "estimated_points": 0
  }')
TASK_ID=$(echo "$SUBMIT" | python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["task_id"])')
  • 需要 scope task:submit(缺失 → 403 AUTH_SCOPE_INSUFFICIENT,实测);
  • estimated_points=0 = 免计费运行(不冻结积分;见 billing-free-run-mode.md);
  • idempotency_key 相同的重复提交幂等返回同一 task。

4. SSE 实时进度(一次性 ticket,浏览器 EventSource 友好)

bash
# 4.1 mint 30s 一次性 ticket(scope task:read;避免长效 JWT 进 URL)
TICKET=$(curl -sk -X POST "$BASE/api/v1/app/workflow-tasks/$TASK_ID/events/ticket" \
  -H "Authorization: Bearer $TOKEN" | python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["ticket"])')

# 4.2 打开事件流(终态事件后自动关闭;支持 Last-Event-ID 断点续传)
curl -skN "$BASE/api/v1/app/workflow-tasks/$TASK_ID/events?ticket=$TICKET"

实测事件序列(完整链):

text
event: workflow_task.submitted
event: workflow_task.started
event: node.started      {"node_id": "start"}
event: node.completed    {"node_id": "start", "status": "succeeded"}
event: node.started      {"node_id": "agent_node"}
event: node.completed    {"node_id": "agent_node", "status": "succeeded", "duration_ms": 5593}
event: node.started      {"node_id": "end"}
event: node.completed    {"node_id": "end", "status": "succeeded"}
event: workflow_task.succeeded

5. 查结果与用量

bash
# 终态(completed);output_data 为驱动器终态标记,节点级输出经 context/检查点承载
curl -sk "$BASE/api/v1/app/workflow-tasks/$TASK_ID" -H "Authorization: Bearer $TOKEN"

# 积分余额(scope billing:points:read)
curl -sk "$BASE/api/v1/billing/points/balance" -H "Authorization: Bearer $TOKEN"

模型用量:每次 LLM 调用落一条 cost_recordscall_kind='llm'、真实 tokens、免计费期 cost_cents=0)。 实测样例:llm | tokens_in=275 | tokens_out=377 | cost_cents=0 | model=mimo-v2.5

5.5 计费运行与终端用户归属(T2 起 · ADR-0044)

§3 的 estimated_points=0 免计费默认仍然保留(试跑/联调场景);本节是接入积分闭环后的正式口径。

5.5.1 影子用户 + 积分入账(tenant scope)

你的终端用户 100% 在你自己的 SaaS 侧注册/登录(模式 A);在 agent_hub 只需要一个影子用户

bash
# 1) 影子用户(username = 你侧不可变用户 ID;重复调用幂等返回既有用户)
curl -sk -X POST "$BASE/api/v1/identity/users:provision" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"username": "saas-user-10086"}'
# → data.id 即 shadow_user_id

# 2) 给影子用户入账(scope billing:points:tenant_grant;operation_id 幂等,两侧对账键)
curl -sk -X POST "$BASE/api/v1/billing/points:tenant-grant" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{
    "user_id": "<shadow_user_id>",
    "project_id": "<project-uuid>",
    "amount": 1000,
    "operation_id": "grant:order-20260711-0001",
    "expires_at": "2027-07-11T00:00:00Z",
    "reason": "用户充值订单 20260711-0001"
  }'
  • 目标 user 无长期账户时自动开户;重复 operation_id 返回 idempotent_replay: true(安全重试);
  • 跨租户的 user_id → 403 AUTH_TENANT_MISMATCH(fail-closed);
  • 平台→租户的商务结算入账由 ops 走 POST /billing/points:grant(ops scope,web-ops 积分发放页)。

5.5.2 API-key + on-behalf-of 提交(计费任务归属终端用户)

bash
# 1) 领 API-key(scope delivery:api_key:write;明文仅本响应返回一次,妥善保存)
curl -sk -X POST "$BASE/api/v1/delivery/api-keys" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name": "saas-prod"}'
# → data.api_key = "ahk_..."(轮转 :rotate / 吊销 :revoke)

# 2) headless 提交任务并归属到影子用户(binding billing_rules 配置 estimated_points>0 即计费)
curl -sk -X POST "$BASE/api/v1/app/<app_id>/tasks" \
  -H "X-AgentHub-Api-Key: ahk_..." \
  -H "X-AgentHub-On-Behalf-Of: <shadow_user_id>" \
  -H 'Content-Type: application/json' \
  -d '{"workflow_key": "chat", "input": {"message": "hi"}}'
  • X-AgentHub-On-Behalf-Of 的 user 必须属于 API-key 解析出的租户且 active,否则 403(不降级);
  • 任务 → 冻结 → 结算 → 流水全链以该影子用户 user_id 归属;不带头时维持项目 owner 兜底(向后兼容);
  • 流水核对:GET /api/v1/billing/points/ledger?user_id=<shadow_user_id>(scope billing:ledger:read)—— 完整链可见 manual_adjust(入账)/ freeze / deduct / unfreeze

6. 注册 HTTP Tool(ADR-0042 反向接缝 · 过渡形态)

工具目录 CRUD(PT-A06)尚未实装;过渡期用 TOOL_REGISTRY env JSON(operator 配置,非租户自助):

jsonc
// api/worker 容器环境变量 TOOL_REGISTRY(single-line JSON)
{
  "<tool-uuid>": {
    "url": "https://tools.example.com/ocr",   // 仅 https
    "method": "POST",
    "timeout_s": 10,
    "max_bytes": 1048576,
    "allow_hosts": ["tools.example.com"],      // SSRF 白名单(必填;DNS 解析后还会做私网 IP 校验 + IP pin)
    "auth_header": "Authorization",
    "auth_scheme": "Bearer"                     // 凭据经 OpenBao CredentialsStore 解析,不写进 JSON
  }
}

SOP 里以 {"node_id":"my_tool","node_type":"tool","executor_type":"ToolNodeExecutor","tool_id":"<tool-uuid>"} 引用;内置 kb_search 工具的 sentinel UUID 为 00000000-0000-0000-0001-000000000001。 未知 tool_id / 调用失败一律 fail-closed(任务 FAILED,积分解冻)。

7. Webhook 验签(任务结果回推)

平台外呼 webhook 携带 X-AgentHub-Signature(HMAC-SHA256 hex)等头;验签算法(app/delivery/headless/webhook_signer.py):

text
material = f"{event_id}.{ts}.{nonce}.{sha256_hex(body)}"
signature == HMAC_SHA256(secret, material)          # 常数时间比较
abs(now - ts) > 300 → 重放拒绝(400 WEBHOOK_REPLAY_DETECTED)

TS/Python 参考实现见 @agent-hub/sdksdk/python/agenthub_sdk(GL-06 薄 SDK)。

8. 排障速查

症状原因修复
401 AUTH_UNAUTHENTICATEDtoken 直连 :8080 取的(iss 不匹配)/ HS256 伪造 / 过期经网关取 token;只用 RS256
403 AUTH_SCOPE_INSUFFICIENTservice account permissions 缺对应 scopeops 补属性后重取 token
任务 retryingfailedfailure_reason=agent_unavailable:model_credential模型 key 未 provisioning(fail-closed 预期)ops 按 onboarding §3 写 OpenBao key
SSE 404ticket 过期(30s)/ 已消费 / task 不属于你重新 mint

9. Starter SaaS 验证台用法(T6)

定位web-starter 是基座端到端验证台 / 终端渠道参考实现,不是官方二开集成面(官方通道仍是 Headless API,见 ADR-0041/0042)。

  1. 浏览器打开 Starter → 顶栏 App 下拉(数据源 GET /api/v1/app/manifests)选择已发布 App;VITE_APP_ID / VITE_PROJECT_ID 仅作默认值。
  2. 「发起任务」页:多绑定时选择 feature_key;提交前展示目录摘要中的 estimated_points 预扣。
  3. 「积分与会员」页:余额 + GET /billing/points/ledger 流水(freeze / deduct / unfreeze / manual_adjust 徽章)。
  4. 余额不足时提交返回 HTTP 402 POINTS_INSUFFICIENT,UI 提示「积分不足,请联系运营充值」。
  5. 真栈验收脚本:bash scripts/t6-starter-verification.sh --verify(依赖 P1 计费点火与 deploy-from-cloud-agent.sh --with-celery)。

10. 会话式接入(CV 波次 · 会话 Agent + Headless 多轮对话)

定位:任务型 SOP 之外的第二条通道——会话 Agent(agent_type=conversation),跑 AgentScope 2.0 运行时,按轮计费(operation_id=turn:*,与 SOP 的 task:* 分账)。完整设计见 CV 系列 program(内部过程文档,不对外分发)。

10.1 建模:发布一个会话 Agent

会话 Agent 复用 agents 目录(agent_type=conversation 判别)与发布快照机制,发布时必须conversation 配置块(单价与护栏强制配对,CV3-1 发布校验):

bash
# 注册会话 Agent(category 任意;agent_type=conversation)
curl -sk -X POST "$BASE/api/v1/builder/agents" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"project_id":"'$PROJECT_ID'","code":"chat-hello","name":"会话助手","agent_type":"conversation"}'

# 发布:role_card + bindings(KB/Skill/工具)+ conversation 块(单价+护栏+SOP 白名单+卡片)
curl -sk -X POST "$BASE/api/v1/builder/agents/$AGENT_ID/publish" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "agent_id":"'$AGENT_ID'","expected_agent_version":0,
    "role_card":{"persona_prompt":"你是耐心的客服助手。"},
    "bindings":{"primary_model_group_id":"'$MODEL_GROUP_ID'","knowledge_base_ids":[],"skill_ids":[],"tool_ids":[]},
    "conversation":{
      "points_per_turn":10,
      "guardrails":{"max_input_tokens":8000,"max_attachments":5,"max_tool_calls":10,"max_sop_invocations":2,"turn_timeout_seconds":120},
      "space_permissions":{"user_space":"read_write","project_space":"read"},
      "sop_allowlist":[],"sop_budget_per_conversation":500,
      "card_types_enabled":["choice_card"]
    }
  }'
  • SOP 节点绑会话 Agent、或会话通道收到非 conversation Agent → 发布/开会话 409(program §三 四条校验规则);
  • 单价缺护栏(或反之)→ 发布 409 AGENT_PUBLISH_VALIDATION_FAILED

10.2 chat-dry-run 预览(发布前冒烟,零计费/零持久化)

bash
curl -sk -X POST "$BASE/api/v1/builder/agents/$AGENT_ID/chat-dry-run" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"message":"你能做什么?"}'
# → data.assistant_text + 解析后的配置摘要(points_per_turn / guardrails / kb_count / tool_count / sop_allowlist)

10.3 开会话 + 多轮对话(Headless,API-key + On-Behalf-Of)

会话通道复用 T2 的 API-key + 影子用户委托模型(§5.5)。开会话与轮次端点在 /api/v1/app/conversations

bash
# 开会话(幂等键 client_conversation_key 可选)
curl -sk -X POST "$BASE/api/v1/app/conversations" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -H 'Content-Type: application/json' -d '{"agent_id":"'$AGENT_ID'"}'
# → 201 data.id = CONVERSATION_ID

# 一轮对话(SSE:turn_started → turn_delta* → [turn_card] → turn_completed)
curl -sk -N -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/turns" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -H 'Content-Type: application/json' \
  -d '{"content_blocks":[{"type":"text","text":"帮我查退款政策"}]}'

输入内容块支持 text | file | page_ref | activity(后三类第三方内容以 <data> 标签包裹注入防御,CV4-3)。选项卡片下发后,下一轮回传 {"type":"choice_selection","card_message_id":"...","selected":["<option_id>"]}(CV2-4)。

10.4 计费 / 余额 / 断连重拉

  • 轮初 freeze(operation_id=turn:{turn_id}, billing_model=upfront-once) → 轮末 settle;失败 unfreeze 全额退(CV3-2);
  • 余额不足开轮 → 402 POINTS_INSUFFICIENT(零副作用);结清后可用余额 < 3×单价 → turn_completedbalance_warning:true(ADR-0050 D4);
  • SSE 断连凭首帧 turn_idGET /api/v1/app/conversations/{id}/turns/{turn_id} 重拉,不重跑、不重复扣费(CV1-2/CV3-2);
  • token 计量入 cost_recordstask_id=turn_id);md 编制的 LLM 成本归平台(task_id 空,ADR-0050 D5)。

10.5 SOP 三件套 / 工具 / MCP(会话内调用)

会话 Agent 经工具白名单调用已发布 SOP(sop_submit/sop_status/sop_result,白名单 + 单会话预算闸,CV2-2);HTTP 工具与 MCP 服务器经目录(/api/v1/builder/tools,CV2-3/CV5-1)注册,MCP 保存与运行时双重 allowlist + SSRF + 每租户配额 + 审计(ADR-0051 D3)。越权(未白名单 SOP / 只读空间写 / 未 allowlist MCP)在执行器 fail-closed 拒绝并入 WORM 审计。

10.6 结束会话与 md 资产

POST /api/v1/app/conversations/{id}:end(或空闲超时)→ closing → 编制链把转写/画像/笔记/记忆写入用户空间(版本+1,CONVERSATION_MD_COMPILATION_ENABLED=true 时启用真实 LLM 编制,CV4-2)→ closed 只读。用户空间在影子用户 provision 时幂等初始化、注销时级联关闭(CV4-1)。

10.7 验收链

mock 段(本地/CI):backend/tests/integration/runtime/conversation/test_cv5_3_acceptance_chain.py 跑通「开会话→5 轮→freeze/settle→402→断连重拉→closed 拒新轮」的计费/生命周期骨架;KB/附件/卡片/SOP 三件套/MCP 各段由对应 CV 集成套件覆盖。

真栈段(ECS-B,program §十一):

bash
bash scripts/deploy-from-cloud-agent.sh --with-celery   # worker/beat 负责 :end 闭合与 idle sweep
bash scripts/verify-testbed.sh --worker-health
bash scripts/smoke-conversation-testbed.sh              # S1~S10;固定 UUID 租户,结束即删

冒烟种子强制 agent_type=conversation(否则开会话 409)。运维细节与踩坑见 docs/ops/testbed-runbook.md §10。

10.8 多模态接入(MM 波次 · 语音与附件,MM5-1 真栈实证)

凭据布局(实证):语音/文生图走经典版 DashScopedashscope.aliyuncs.com,按量 key,OpenBao 租户 vault ref);chat 走平台 fallback(MODEL_FALLBACK_CHAT_*)。Token Plan 网关(token-plan.*.maas.aliyuncs.com/compatible-mode/v1)仅服务 chat——非 chat 端点一律 url error,订阅型 sk-sp- key 在经典原生树 InvalidApiKey,两把 key 不可混用。

模型选型(2026-08-19 ECS-B 实测可用;STT 形状 2026-08-23 修正)

模态model_type模型备注
STTsttqwen3-asr-flash同步端点;input.messages[].content[].audio 收 Data URI(#296 修正:原 file_urls+prompt 形状实为纯文本调用,模型逐字复述 prompt)
TTSttscosyvoice-v3-flashvoice 如 longanyanglongwan_v2 被 engine 418 拒,#293);24h OSS URL 二次下载
T2Iimage_genqwen-image-3.0-promultimodal 形状;OSS 预签名 URL 二次下载
视觉理解llmqwen3-vl-plus / qwen-vl-maxcapabilities 勾 vision;放 chat 路由 fallback 位——带图轮次独占 vision 端点,纯文本轮次走主模型(MM4-1,2026-08-23 真栈实证)
chatllmdeepseek-v4-procompatible-mode

租户在 web-builder「资源建模 → Model Registry」注册上述模型(vendor 选 dashscope;key 只入 OpenBao 引用),并在 Model Routes 为每个 model_type 设主模型(unit_price/price_unit:second/char/image)。

三模态 curl(接 §10.3 会话之后;snapshot conversation 块需带 points_per_transcription/points_per_speech/voiceguardrails 需带 max_images_per_turn

bash
# ① STT:录音 multipart 直传 → 文本回填(用户可改)→ 作为普通 text 轮次发送
curl -sk -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/transcriptions" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -F "audio=@speech.mp3;type=audio/mpeg"
# → data.text / points_charged(快照 speech 组价)

# ② TTS:助手消息按需合成;(message_id, voice) 缓存,重放零扣点
curl -sk -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/messages/$MESSAGE_ID/speech" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID"
# → data.url(签名下载)/ cached / points_charged

# ③ 附件前门:authorize → 直传 PUT → commit(服务端抽取摘录)
curl -sk -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/attachments:authorize" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -H 'Content-Type: application/json' \
  -d '{"filename":"notes.txt","mime":"text/plain","size_bytes":123}'
# → data.fid + data.upload_url;PUT 字节后 :commit 带 sha256 → data.excerpt
# 发轮时 content_blocks 携带 {"type":"file","file_id":"...","filename":"...","excerpt":"..."}

# ④ 文生图:轮内触发内置工具 generate_image → SSE 追加 turn_file 帧
#    → GET /api/v1/app/conversations/{id}/files/{file_id} 取签名 URL 下载 PNG

计费维度:stt:(秒,向上取整)/tts:(字符)/image_gen(张);全部经 ModelUsageEventcost_records 归因,配对护栏(价↔上限 XOR 拒绝)沿用 ADR-0055 D4。图片输入理解(vision content parts)MM4-1 已落地(ADR-0056):chat 路由含 vision 能力端点时图片以原生 image_url parts 随轮(7MB/图守门),无 vision 端点时降级 vision_deferred 占位文本(非 fail-closed,2026-08-23 真栈实证含 fid 读取修复 #297)。

真栈验收:bash scripts/smoke-conversation-testbed.sh(S1~S14;脚本前置把经典 key 双路径写入冒烟租户 vault,密钥不落盘不回显)。

11. 界面化配置与用户试用(CU 波次 · web-builder / web-starter)

定位:§10 的 curl 链路在 CU 波次已全部接到界面上——建设者在 web-builder 完成会话 Agent 的建/改/发布/试跑,终端用户在 web-starter 的「对话陪练」里直接对话。端点契约不变(本章只引用,不重复),变的是操作面。方案见 CU 波次 program(内部过程文档,不对外分发)。

11.1 建设者:在 web-builder 界面管理会话 Agent

入口:登录 web-builder → 项目列表 → 进入项目 → 资源建模Agents 页签(/projects/{projectId}/resources)。

  1. 新建:点 Create Agent → 填 code / name → 类型选「会话 Agent(多轮)」(即 §10.1 的 agent_type=conversation)。列表「类型」列显示紫色「会话 Agent」标签。
  2. 发布:行内 Details / Publish 打开抽屉 → 填人设(persona_prompt)、选主模型组 → 会话配置块:轮次单价(如 10 分/轮)、五项护栏、空闲超时秒数,按需勾选「启用 choice_card」;工具多选tool_ids)来自工具目录(ADR-0051,面板见 CV5-1/CU 波次),选中后随发布冻结进版本快照——发布成功横幅里的快照 JSON 可见 bindings.toolIds 非空。漏填单价/护栏任一 → 前端内联红框拦截(与 §10.1 的 409 AGENT_PUBLISH_VALIDATION_FAILED 同语义);故意漏填护栏提交会被拦下并给出可读 409 文案。
  3. :行内「编辑」可改名称/描述,保存后列表即刻反映;已发布 Agent 的类型字段置灰并提示「类型不可修改」(ADR-0053 D4,对应 PATCH /api/v1/builder/agents/{id}agent_type409 AGENT_TYPE_IMMUTABLE)。
  4. 归档 / 删:行内「归档 / 删除」弹窗默认归档(软删,ADR-0053:归档后立即从用户侧目录消失、不可开新会话 409 CONVERSATION_AGENT_ARCHIVED、既有会话仍可读);勾选「彻底删除」走硬删守卫——有已发布版本/会话/团队引用 → 409 AGENT_DELETE_CONFLICT 且弹窗内列出中文冲突原因;从未发布的空壳 → 204 真删。

11.2 两级试跑:预览(免费)vs 试用(计费)

会话 Agent 的详情抽屉比 SOP Agent 多两个页签,语义强区分,别混用

预览(免费)试用(真实会话)
端点POST …/chat-dry-run(§10.2)/api/v1/app/conversations*(§10.3,平台 JWT 通道,ADR-0052)
计费零计费、零落库、无工具(横幅明示)按轮真实扣减积分,单价前置红色警示 + Popconfirm 二次确认
用途发布前后冒烟:发一句话拿回显 + 配置读数(单价/五护栏/KB 数/工具数/SOP 白名单/卡片类型)全链体验:≥3 轮流式(逐字流出)、choice_card 卡片往返、每轮扣费回显与累计、余额预警条、历史分页、断连重拉(不重跑不重复扣费)、结束会话

试用页签的每轮扣费与 points_ledgerturn:* 流水对得上(§10.4);余额不足开轮 → 402 POINTS_INSUFFICIENT 零副作用。

11.3 终端用户:web-starter「对话陪练」

入口:saas_user 登录 web-starter → 侧边栏「对话陪练」(/chat)→ 目录选 Agent「开始对话」→ /chat/{conversationId} 多轮对话。行为与 §10.3/§10.4 的契约一一对应:开会话幂等(重复打开/刷新得到同一 conversation id)、回复逐字流出、卡片选择回传 choice_selection 块、扣费行逐轮回显、断连提示「连接中断,已重拉完整结果」且不重复扣费、余额耗尽 → 402 + 「前往充值」引导、「结束会话」后输入区禁用。「我的会话」列表为本浏览器本地记忆(封顶 20 条),回到会话后从服务端拉状态与历史。

11.4 用户侧目录的字段收敛边界

终端用户目录的数据源是 GET /api/v1/app/agents?agent_type=conversation(CU3-2),响应恰好五个字段

json
{"id":"…","name":"英语陪练小助手","description":"…","points_per_turn":10,"idle_timeout_seconds":900}
  • 看不到 sop_node Agent、未发布、已归档的 Agent(服务端过滤,fail-closed:快照缺会话块/缺价格键宁可少列不可错价);
  • 看不到人设、模型组、KB/Skill/工具绑定、护栏、SOP 白名单——这些是会话 Agent 的「配方」,属建设者侧资产;响应体对 24 个敏感键(persona_prompt / bindings / tool_ids / guardrails / sop_allowlist / config_snapshot_jsonb 等)做递归零命中断言(CU3-2 AC5 探针)。终端用户只需要知道「它是谁、每轮多少钱、多久不聊会自动结束」,其余一概不下发;
  • 该端点仅平台 JWT 通道conversation:read scope):API-Key 直连 → 401,缺 scope → 403,跨租户 Agent 永不出现(CU3-2 AC3/AC4)。

11.5 验收链与演示

§十 13 条端到端验收链的逐条证据(mock 段 Playwright/vitest/契约守卫 + 真栈段状态)收口在 CU7-1 任务包验证报告(内部过程文档,不对外分发);两条界面演示录屏(web-builder 试用全链 / web-starter 对话陪练全链)在同目录 demo/ 下。