Commit Graph

14 Commits

Author SHA1 Message Date
caoqianming af2ad3cef1 feat(usage): 记账给前缀缓存命中折价 + 前端体现缓存命中/真实成本
排查"rust→PPT"task(flash,34 轮)发现累计 tokens_in 69.9 万里 88.6% 是缓存
命中,但 _fallback_chat_cost_cny 把命中段也按 input 全价算,记账虚高 2-3x。

- capabilities: 加 cache_hit_cny_per_mtoken(deepseek flash 0.1 / pro 0.2;
  0=不区分按全价兜底,绝不少记)
- usage: 成本公式拆三段「命中×缓存价 + (input−命中)×input价 + output×output价」;
  loop 把 cache_hit_tokens + 缓存单价透传进 record_chat_usage
- web: 不加 DB 列。app.py 加 _usage_aggregates(单查询 GROUP BY usage_events,
  复用列表 msg_counts 批量范式,无 N+1)on-the-fly 算每 task 真实成本 + 缓存命中,
  _task_dict 带出;dev.html 列表行显 ¥、顶栏 formatTaskUsage 显「tok·缓存命中%·¥」
- scripts: backfill_chat_cost_cache_discount.py 按 units 已存 token 重算历史
  cost_cny(只改成本列,默认 dry-run,--apply 落库)

折价只对新 chat 事件即时生效;历史走 backfill 脚本(部署后跑)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 08:26:09 +08:00
caoqianming 1c30a9e54e Reduce chat context token usage 2026-06-04 16:41:14 +08:00
caoqianming cf23c9d178 models: 加 local.{r1,qwen3} 内网模型档案,涉密任务用
DeepSeek-R1 (满血) + Qwen3-30B-A3B(服务端 alias 名是 Qwen/QwQ-32B,实际后端
Qwen3) 部署在 http://182.54.21.126:9000/v1,OpenAI 兼容,共享 LOCAL_LLM_API_KEY
env。thinking_mode=false(R1/Qwen3 默认就思考,不通过 reasoning_effort 控制)。

local.qwen3 已通连通性,local.r1 服务器侧调试中。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 15:55:37 +08:00
caoqianming 3a3f8d86cc sandbox: yaml sandbox.dns 显式 --dns 注入(绕开 daemon 上游探测)
腾讯云轻量等场景 docker daemon 探测 host systemd-resolved 上游 DNS 不稳,
即使 init.sh ACCEPT 127.0.0.11:53 例外,embedded DNS 自己 forward 不出去
仍跪。显式 docker run --dns 8.8.8.8 --dns 114.114.114.114 直接写容器
/etc/resolv.conf 绕开上游探测。

- agent.yaml 加 sandbox.dns 列表,默 [8.8.8.8, 114.114.114.114]
- SandboxPool 加 dns 字段(env: ZCBOT_SANDBOX_DNS 逗号分隔 override),
  _docker_run 每个 ip 加 --dns flag
- RUN.md 故障兜底 DNS 失败那行补充第二层根因 + 解法

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 11:28:53 +08:00
caoqianming eaf7f3ea1e Stage C 收尾包:资源 yaml + 磁盘配额 + 网络放开 + 容器内源持久化
dogfood + 信任同事白名单阶段 Step 4 完整 egress proxy 暂不做(沉淀为升级触发
信号:任一陌生用户注册 / 模型异常 outbound / 信任白名单出现非密切相识者 → 必上)。
本批 3 件:

(A) 容器资源 yaml 化(可调不重 build):
- agent.yaml 加 sandbox 段(memory/cpus/pids_limit)
- SandboxPool ctor 加三字段,优先级 env > yaml > 默(2g/1.0/256)
- setup_pool/init_pool 透传 sandbox_cfg
- sandbox check 输出加 [info] 4 行给运维一眼对账

(B) 应用层磁盘配额(§7.5 #4 软配额):
- migration 0008 user_disk_usage 单行 per user
- core/storage/disk_quota.py:parse_bytes("5gb"/int)+ scan_user_dir
  (os.scandir 跳顶层 .zcbot_tmp / .memory)+ upsert ON CONFLICT
  + check_disk_quota + scan_all_users 串行
- lifespan _disk_scanner 后台 task(启动跑一次 + 默 15min 周期)
- DockerExecutor write/edit 起手 gate 超额 [Error] 不调容器
- /v1/files/upload 同款 gate 超额 HTTP 413
- yaml `quotas.disk_bytes_per_user: 5gb` + `disk_scan_interval_seconds: 900`
- race 接受:扫描间隙写入轻微突破(image/video 配额同款 race-tolerant);
  外部用户开放前 OS 层 xfs prjquota 兜底
- 11 测试 covered parse_bytes / scan / 跳 dotfile

(C) 网络放开 + 容器内源持久化:
- network.py 去 --internal flag,容器走 docker bridge default 有 NAT outbound
- 已存在 internal network 不自动 rm 仅 warn,RUN.md 给迁移命令(避免破现有容器)
- iptables 红线段不动(169.254/127/10/172.16/192.168/100.64/PG_IP DROP),
  挡 cloud metadata + 内网扫描 + loopback,基线不依赖 proxy
- Dockerfile 加 /etc/pip.conf(global index-url + timeout 60) + /etc/npmrc
  (global registry),让运行时模型 `pip install foo` / `npm install bar`
  也走 mirror(此前 --build-arg 只 build 时生效)

unittest discover 46/46 PASS(原 35 + 新 11)。
DESIGN 不动(延后决策仍在 §7.7 Stage C 阶段语义内,触发信号沉淀进
PROGRESS / RUN);RUN.md 加 env 列表 + 网络迁移 + 配额 + 故障兜底 3 行。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 08:35:53 +08:00
caoqianming fe95df0b9d Add web_search and web_fetch tools via Bocha AI search API
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 11:37:33 +08:00
caoqianming 758486e2cd feat(quotas): 媒体生成每账号每日上限 (默 20 图 / 5 视频, yaml 可配)
config/agent.yaml 加 quotas 段;core/storage/usage.py 加 check_daily_quota
(COUNT usage_events WHERE user_id+kind+created_at>=本地今日 00:00);
SeedreamTool / SeedanceTool ctor 收 daily_limit, execute() 起手 if 超额
返 [Error] 不调远端不烧钱。错误串只暴露已用/上限 + 重置时间,不写
yaml 路径 (避免 LLM 转述泄漏内部 schema 给外部用户)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:21:39 +08:00
caoqianming 5faff8a127 feat(seedance): 加 seedance_2_pro variant + smoke 支持 --variant 参数
- yaml 加 seedance_2_pro (model_id=doubao-seedance-2-0-260128, ¥46/Mtok 文生,
  支持 480p/720p/1080p);放在 fast 后面,fast 仍是默认 (yaml 首位 = agent fallback)
- Pro 出片慢,poll_timeout_s 拉到 900s (实测 480p 4s 等了 177s)
- smoke_seedance.py 加 sys.argv[1] 选 variant:`smoke_seedance.py seedance_2_pro`
- SKILL 把 "30-90s" 校准到 "Fast 30-90s / Pro 2-3min" (实测)

smoke Pro 跑通:body schema 完全对,响应带真 usage.total_tokens (40594),tool
_extract_tokens 命中走真值不估算;480p 4s 实测 ¥1.87,与官方源数据线性外推吻合。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:11:31 +08:00
caoqianming da71daa789 feat(seedance): 加 generate_audio 参数对齐官方 API
火山方舟官方 curl 例子 (CreateContentsGenerationsTasks) body 里有 generate_audio 字段
— Seedance 2.0 旗舰特性,原生 AI 生成背景音 / 角色对白 / 音效。我之前没接,补上。

- yaml 加 default_generate_audio: false (控成本默认关)
- tool execute 加 generate_audio kw + 进 body / meta / usage_events extra_units
- tool banner 加 audio=<bool> 字段
- SKILL 加参数段说明 + prompt 写法变化 (开音轨时要写背景音/对白具体内容)
  + 贴 prompt 模板 + 反模式各加一条

resolution / watermark 官方例子里没发但其他文档源确认 fast 必传 + 有效,保留。
参考媒体 (image_url/video_url/audio_url + role:reference_*) 是 i2v/v2v/r2v 路径,
phase 1 不接,延后。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:44:15 +08:00
caoqianming 7ff58c488e feat: 接入豆包 Seedance 2.0 Fast 视频生成 (文生视频) + videogen skill
- tools/seedance.py: 异步 submit /contents/generations/tasks → 5s 轮询 → succeeded
  后 download mp4 + meta.json 落 <wd>/videos/;失败/cancel 不计费;cancel_check 在
  轮询间检查,响应用户停止按钮
- config/media/doubao.yaml: 展开 video.seedance_2_fast (¥37/Mtok 文生 / ¥22/Mtok
  图生,token 公式校验 720p 5s = ¥4.00 完全对上源数据)
- core/storage/usage.py: record_video_usage,kind=video,units jsonb snapshot
  resolution/duration/ratio/fps/tokens/单价
- core/agent_builder.py: build_agent 加 video_variant + cancel_check 形参,
  cancel_check 必须 build 阶段传 (SeedanceTool ctor 持有用于轮询)
- web/app.py: GET /v1/video_models + MessageRequest.video_model + 透传
- web/static/dev.html: 顶栏第三下拉 (image 旁边) + state.videoModels/videoModel
- skills/videogen/SKILL.md: 六维诊断 (运动+镜头 替代 imagegen 的光线);BLOCKING
  门槛比 imagegen 更严 (¥4 vs ¥0.22) + 等 30-90s 出片
- prompts/system/general_v1.md: 加 seedance 触发指引 (平行 seedream)

phase 1 仅 t2v 文生视频,fast 上限 720p。API 端到端 smoke 跑过:路径/auth/错误解析
全通,body schema 待用户在火山方舟控制台开通模型后真出片才能验。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:30:54 +08:00
caoqianming c04b8ba05e feat(media): 接入豆包 Seedream 5.0 图像生成 tool + 0007 cost_usd→cost_cny 全表统一币种
- 新 tools/seedream.py:调 ark /images/generations 同步生成,产物落 figures/<ts>-<rand>.png + 同名 .meta.json
- 新 core/ark_client.py:火山方舟 HTTP 封装(base URL + bearer auth + 异常翻译 + download),共享给后续 seedance
- 新 config/media/doubao.yaml:独立命名空间;价格表注释 last_updated + 调价路径说明
- core/storage/usage.py 加 record_image_usage:单价 snapshot 进 units jsonb,防调价污染历史
- agent_builder.py 注册 SeedreamTool:仅当 ARK_API_KEY 设了才挂(无 key 用户无感)
- 0007 migration:tasks/usage_events 双 rename cost_usd → cost_cny,×7.2 一次性折算;
  record_chat_usage 内部把 litellm USD 同样 ×7.2 落 CNY,免分类汇总
- prompts/system/general_v1.md 加「媒体生成工具」段,提示按需调用、不主动装饰
- dev SPA tool_result 折叠态显示 banner(model/size/cost/elapsed 徽章),不展开就透明
- scripts/smoke_seedream.py:端到端走通(待 ARK_API_KEY 配齐真跑会产生 ~¥0.22)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:20:34 +08:00
caoqianming 7fc9570ffe model: 加 GLM 5.1 档案 (zai provider + 国内站 bigmodel.cn)
- config/models/glm.yaml: family=glm, variant=pro, model_id=zai/glm-5.1,
  api_base 覆盖到 https://open.bigmodel.cn/api/paas/v4,env ZHIPUAI_API_KEY
- thinking_mode 暂不开:GLM 协议是 body {"type":"enabled"} 而非
  reasoning_effort 等级,需 core/llm.py 加 family 分支才能透传,留 TODO
- requirements.txt: litellm 下限 1.50→1.83(zai provider PR #17307
  merge 后才内置,旧版不识别 zai/ 前缀会炸)
- RUN.md: .env 例子加 ZHIPUAI_API_KEY,probe 命令加 glm.pro

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:32:15 +08:00
caoqianming 781a216ca6 model: 同 task 内切模型(c 模式 task 级 / A 粒度)+ usage_events v2 表(0006); GET /v1/models; 前端顶栏下拉 + 历史 model 切换点小标
- DB(0006): messages 加 model_profile 列(assistant 行有值); 重建 usage_events 表 v2 形态(event_id/user_id/task_id/message_id/kind/model_profile/units jsonb/cost_usd + 三索引), 0004 删的旧 schema 字段不够多态; tasks.tokens_prompt/completion/cost_usd 保留作粗概览
- ModelCapabilities 加 display_name; deepseek_v4.yaml flash/pro 各填名
- GET /v1/models: 扫 config/models/*.yaml 列可选项(profile/display_name/family/thinking_mode/is_default); POST /v1/tasks + PATCH 接受 model_profile(不传 → cfg["default_model"]; 校验走 ModelCapabilities.load 失败 400)
- build_agent: resume 时优先 task.model_profile 而非 cfg default; AgentLoop 加 user_id 透传, 每轮 assistant 入库后调 record_chat_usage(litellm cost map 算钱, 失败吞掉 emit warn 不阻 loop)
- core/storage/usage.py 新文件: record_chat_usage(双写 messages.tokens_in/out + model_profile + insert usage_events 一行)
- session.append() 返回 message_id(供 usage 关联)
- 前端 dev.html: chat-meta 加模型下拉(切了 PATCH + running 中提示"跑完后生效"); 新建对话框 modal 加 nt-model select; renderMessages 按 model_profile 切换点画小标 "── DeepSeek V4 Pro ──"
- CLAUDE.md: 加"开发测试期 / 不删现有数据 / DROP COLUMN 两种情况"规则
- DESIGN §7.4 schema 加 messages.model_profile + usage_events v2 段; PROGRESS 加 0006 条目 + 文件清单

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 21:43:13 +08:00
caoqianming 3a66849953 Initial import: zcbot personal task agent
DESIGN.md / PROGRESS.md 落地 Phase 1-3:
- core/: LiteLLM 封装 + ReAct loop + 会话持久化 + Anthropic skill registry
- tools/: read/write/edit/glob/grep/shell/run_python/load_skill (Hybrid 范式)
- skills/coding | proposal: WHY+WHAT 风格 SKILL.md
- skills/ppt: 完整渐进披露 (SKILL + 4 references + 3 scripts)
  · 借鉴 hugohe3/ppt-master 的两阶段 + spec lock 思路
  · MSO_SHAPE 图标体系 + 安全区 + 越界检测
  · 默认商务红主题 (#C00000 / #E15554 / #FFC107)
- config/models/: DeepSeek V4 flash/pro 档案

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:02:59 +08:00