API、SSE、扩展方式和框架约束。运行、调试、打包与部署见 项目 README。以下源码命令均在项目根目录执行,使用项目虚拟环境。
当前运行策略:所有 Agent 独立执行,已关闭扩展子 Agent 和框架默认 general-purpose,模型无 task 工具。业务注册定义继续支持独立业务 API、网关与批次入口。下文的子 Agent、委派和跟踪章节保留为历史设计参考,不代表当前启用能力。
主入口与 1.5 一致,额外新增了若干辅助接口。
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/chat |
主入口,SSE 流式 |
| POST | /api/chat/resume |
恢复被中断挂起的对话 |
| GET | /api/chat/interrupt |
查挂起的中断(刷新页面补渲染卡片);有则 200,无则 204 |
| GET | /api/chat/history/{session_id} |
会话历史 |
不提供 /api/chat/only,统一使用 /api/chat。
POST /api/chat 请求体是 multipart/form-data:
| 字段 | 必填 | 说明 |
|---|---|---|
message |
是 | 用户消息,支持 #skill:<name> 与 #session:<uuid> 前缀 |
session_id |
否 | 缺省则新建会话 |
thread_id |
否 | 必须等于 session_id,否则 404 |
mode |
否 | plan(先出计划待批)或 full(直接执行) |
file |
否 | 附件,落到 /workspace/uploads/ |
request_id |
否 | 客户端生成的请求唯一键;同一用户重复提交返回 409 和原 session_id、run_id |
user_name |
否 | 归属用户,缺省 default(2.0 新增,替代 1.5 的鉴权依赖) |
响应是 text/event-stream,头含 X-Session-Id、X-Trace-Id 与 X-Run-Id。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/sessions |
会话列表(置顶优先) |
| GET | /api/sessions/search?q= |
标题模糊搜索 |
| DELETE | /api/sessions/{id} |
删除会话(同时清图状态) |
| DELETE | /api/messages/{id} |
删除单条消息 |
| GET/PUT | /api/sessions/{id}/mode |
权限模式 |
| PUT | /api/sessions/{id}/pin · /title |
置顶 · 重命名 |
| GET | /api/sessions/{id}/runs |
最近 50 次执行的状态与错误 |
| GET | /api/sessions/{id}/context |
上下文用量明细与压缩存档索引 |
| GET | /api/usage/today |
当日用量与额度 |
| GET | /api/usage/recent |
最近用量流水 |
| PUT | /api/usage/quota |
调整每日额度(0 = 不限) |
| GET | /api/usage/pricing |
模型单价表 |
| GET | /api/preferences · PUT · DELETE |
用户偏好(每轮自动注入) |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/model |
模型配置与 agent 组装摘要 |
| GET | /api/model/tools |
工具清单(按分组) |
| POST | /api/model/thinking |
切换思考级别(触发 agent 重建) |
| POST | /api/model/reload |
强制重建 agent |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET/POST | /api/schedules |
列表 / 新建 |
| GET/PUT/DELETE | /api/schedules/{id} |
详情 / 更新 / 删除 |
| POST | /api/schedules/{id}/pause · /resume · /run |
暂停 / 恢复 / 立即执行 |
| GET | /api/schedules/{id}/runs |
执行记录 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /health |
逐子系统健康状态 |
| GET | /api/health/billing |
计费自检(单价是否覆盖当前模型) |
| GET | / |
重定向到测试页 |
| GET | /static/index.html |
Vue工作台:对话与智能体测试 |
14 种业务事件 + 1 种通用错误。字段名是从 1.5 前端逐行反查出来的, 不是猜的。
| 事件 | 前端读取的字段 |
|---|---|
agent_start |
agent · cn_agent |
thinking |
message · cn_agent |
thinking_token |
content |
executing |
message · cn_agent · tools[] · cn_tools[] |
tool_call |
tool · cn_tool · args · agent_id |
tool_result |
tool · cn_tool · content · error · agent_id |
token |
content |
updated_todo_list |
todos |
progress |
message |
interrupt |
tool_approval{tool,args,reason,side_effect} 或 ask_user{questions[]} 或 message |
citations |
items[] · model_only |
agent_done |
agent · cn_agent · duration_ms · progress · context_tokens |
agent_error |
agent · cn_agent · message · duration_ms |
done |
duration_ms · usage{} |
error |
message · code · retry_after |
帧格式:data: {json}\n\n,每帧恒带 session_id,绑定 trace 时带 trace_id。
HTTP 错误语义:额度超限 429;并发超限 429(带 retry_after);上传 400/413;
会话不存在或越权 404;恢复时无挂起中断 409。
事件字段统一定义在 runtime/events.py,SSE 编码只在 api/sse_events.py。
其他模块不手拼 data: 字符串。改字段时同步检查 tests/test_sse_contract.py。
这是 2.0 与 1.5 的主要差异所在。
| 做法 | |
|---|---|
| 1.5 | 从子图命名空间字符串 tools:<X> 反查 subagent_type;id 被上下文压缩裁掉后反查落空,子智能体身份丢失 |
| 2.0 | 收到 task 调用的那一刻就建 tool_call_id → {类型, 描述} 表,之后只按 id 查表;表由跟踪器持有,不受历史窗口影响 |
见 runtime/subagent_tracker.py。
| 做法 | |
|---|---|
| 1.5 | tiktoken 本地估算优先,为此专门开了 fix/context-usage-accounting 分支修口径 |
| 2.0 | 真实 usage 优先(usage_metadata),provider 不给才回落估算并标 usage_source=estimate |
两个量刻意分开:input_tokens 累加值是计费口径,context_tokens(最近一次
调用的 input)是当前上下文占用。
见 runtime/context_meter.py。
| 做法 | |
|---|---|
| 1.5 | 自研 auto_compact.py,默认关闭,未被信任到敢开 |
| 2.0 | 直接用 deepagents 的 SummarizationMiddleware:85% 窗口触发,把被淘汰的历史追加落盘到 /conversation_history/{thread_id}.md 并把路径嵌进摘要,agent 能自己读回来 |
调节旋钮是 CONTEXT_LIMIT(声明窗口),不是阈值比例——阈值由框架固定为
85%。压缩存档位置可在 GET /api/sessions/{id}/context 的 archives 里查。
| 做法 | |
|---|---|
| 1.5 | 靠提示词让模型自觉在正文写引用,再用启发式规则去正文里刮 |
| 2.0 | 工具侧结构化产出:工具返回 sourced(data, sources),SSE 层从 ToolMessage 抽取、聚合、去重 |
模型漏写和编造这两种失败模式,在读工具返回值的路径上根本不存在。
见 runtime/source_tracker.py 与 tools/registry.py。
网页聊天 / 恢复 → runtime 执行事件 → api/sse_core.py 编码 SSE → 浏览器
定时任务 → runtime 执行任务 → ExecutionResult → 调度记录
批处理 → runtime.execute → ExecutionResult → 调用方处理
runtime/ 统一处理准入、会话互斥、Agent 事件、取消、超时、审批、持久化和计费;不导入 api、FastAPI 或 Starlette。API 层保留请求参数、HTTP 错误转换和 SSE 编码,现有客户端字段不变。
独立脚本使用公共运行环境;以下示例保存为项目根目录的 Python 文件后即可运行:
import asyncio
from db import chat_store
from runtime.environment import RuntimeEnvironment
from runtime.execution import execute
async def main():
async with RuntimeEnvironment():
session = await asyncio.to_thread(chat_store.create_session, "report-user")
result = await execute(
user_name="report-user",
session_id=session["id"],
message="生成本次报告",
kind="batch",
graph_config={"recursion_limit": 30},
)
if result.status == "success":
print(result.output)
elif result.status == "waiting_input":
# 保存任务状态,等待用户确认。
print(result.interrupts)
else:
print(result.error)
if __name__ == "__main__":
asyncio.run(main())
execute() 复用会话归属校验、额度及并发闸,最后释放资源。已有审批未处理时拒绝开始新任务。ExecutionResult:run_id、session_id、status、output、error、usage、citations、interrupts。status="failed",取消则继续向调用方传播取消异常。admit()、build_execution() 和 Execution.events();事件为字典,使用 contextlib.aclosing() 包住迭代,提前结束时也要关闭。若尚未开始迭代,则由调用方释放 admission。runtime.interrupts.ResumeDecision / resume_value(),并在持有会话锁期间读取和校验最新图状态,再将结果作为 resume_payload 交给 build_execution()。Execution 只能消费一次;run() 消费事件并清理后返回结果,events() 供逐条消费,不能对同一个对象重复调用。RuntimeEnvironment 负责加载和校验扩展、初始化业务库与计费单价、标记上次意外中断的执行、打开 checkpoint,并在后台预热 Agent、重试待结算流水。退出时停止可选调度器,等待维护和编译完成,清除 Agent 缓存,关闭扩展服务、checkpoint 与业务库连接。启动失败或调用方取消时同样清理。
| 参数 | 默认值 | 用途 |
|---|---|---|
scheduler |
False |
网页服务传 True;脚本默认不触发定时任务 |
prewarm |
True |
后台编译 Agent;关闭后首次执行时编译 |
billing_retry |
True |
后台重试待结算记录;测试可关闭,本轮执行仍正常计费 |
recover_interrupted |
True |
将上次残留的运行中记录标为中断;自检时关闭 |
main.py 与自检脚本已使用这一入口。网页请求内部直接调用执行层,不要为每个请求创建环境。asyncio.TaskGroup();退出前必须等待自己的任务完成,或取消并等待其收尾。RuntimeEnvironment(prewarm=False, billing_retry=False, recover_interrupted=False),并在导入模块前设置测试数据库与 checkpoint 路径。配置仍按进程加载,环境重入不会重新读取 .env。继续使用 LOG_LEVEL(默认 INFO)与 LOG_FORMAT(text / json)。业务日志和 Uvicorn 日志使用相同格式、毫秒时间戳与时区;发布脚本也读取配置中的日志级别。输出仍走标准输出,部署脚本写入 shared/logs/server.log,systemd 使用 journal。
| 级别 | 内容 |
|---|---|
| INFO | 运行环境/扩展服务就绪、执行开始与结束、计费、业务请求汇总 |
| WARNING | 额度不足、取消、4xx、估算用量等需要留意的情况 |
| ERROR | 执行或收尾失败、5xx、落库与结算异常;关键异常保留堆栈 |
| DEBUG | 并发位申请释放、缓存失效、健康检查及静态资源成功请求 |
常见第三方 HTTP 客户端、数据库驱动和调度器内部日志仍保持 WARNING,避免调试业务时输出大量底层细节。
X-Trace-Id 或 SSE 事件取得 trace_id。也可以传入由字母、数字、点、下划线、冒号和连字符组成的 1–128 位 ID;非法值会替换为新 ID。run_id 区分具体执行、按 session_id 关联会话。定时任务还包含 schedule_id,其调度记录 ID 为 schedule_run_id。execution.completed 的 status、duration_ms、输入/输出 token 和 error_code,异常详情看同一 trace 下的堆栈。http.request.completed 在响应流和应用清理结束后记录一次,因此 SSE 耗时包含整个连接过程。HTTP 200 只代表流已建立,Agent 是否成功要看 execution.completed。健康接口当前可能以 HTTP 200 返回 status=down;日志降噪按 HTTP 状态码判断,监控仍需检查响应内容。
独立批处理自动生成 trace;定时执行和后台结算重试也会携带关联 ID。并发请求的上下文互相隔离,传到 asyncio.to_thread() 的数据库/计费日志沿用同一上下文。
JSON 日志可这样筛选(服务器装有 jq 时):
jq -c 'select(.trace_id == "这里填写trace_id")' ~/vent-agent/shared/logs/server.log
jq -c 'select(.event == "execution.completed" and .status == "failed")' ~/vent-agent/shared/logs/server.log
以上命令适用于文件内容均为 JSON 的情况;切换格式前留下的文本日志可用 rg 'trace_id值' 文件路径 搜索。生产文件的保留与轮转由部署环境管理,本次没有新增第二份应用日志文件。
from obs import get_logger
logger = get_logger(__name__)
logger.info("检索完成", extra={"kv": {
"event": "rag.search.completed", "hits": 5, "duration_ms": 38,
}})
kv 在文本和 JSON 中都会输出。ts、level、logger、trace_id、message、exc 是保留字段,不能通过 kv 覆盖;当前绑定的执行身份优先。异常处理使用 logger.exception() 保留堆栈。
请求日志不记录正文、查询参数、Cookie 或 Authorization;任务摘要不记录提示词、回答和工具参数。框架输出会遮蔽结构化凭证字段,以及消息/堆栈中常见的密钥赋值、Bearer 凭证和 URL 用户密码。扩展仍应只记录排查所需元数据,避免把整段业务数据或客户端配置直接写入日志。
在 extensions/<业务名>/ 下添加 __init__.py 和 extension.py。启动按目录名排序,自动加载每个 extension.py 的 setup(registry),不需要修改工具模块列表、agent_factory.py 或主提示词。
例如 extensions/ventilation/extension.py:
from extensions import define_tool
@define_tool(group="通风", label="计算风量", name="calculate_airflow")
def airflow(area: float, speed: float) -> float:
"""根据断面积和平均风速计算风量,返回立方米每秒。
Args:
area: 断面积,单位平方米,必须大于零。
speed: 平均风速,单位米每秒,不能为负。
"""
if area <= 0 or speed < 0:
raise ValueError("断面积必须大于零,风速不能为负")
return area * speed
def setup(registry):
registry.register_agent(
name="ventilation",
description="处理矿井通风量计算;需要用断面积和风速求风量时委派。",
prompt="你是通风计算助手。先确认单位和输入,调用工具计算,说明结果单位。",
tools=[airflow],
)
define_tool 只声明元数据,不在导入时注册,函数可以直接单测。setup() 收集声明,全部通过校验后统一生效;不要在其中访问外部服务或执行业务操作。
description 自动加入主 Agent 的委派说明;prompt 是子 Agent 的系统提示词,不继承父级提示词。tools 支持函数或已注册工具名。函数自动注册;字符串引用在所有扩展加载完成后校验,允许跨模块引用。setup() 调用 registry.register_tool(airflow)。已有工具传给多个 Agent 时会复用同一个注册名。name 为实际工具调用名,可以与 Python 函数名不同;异步工具使用 async def。Path(__file__).with_name("prompt.md").read_text(encoding="utf-8") 读取;发布包会包含这些文件。内置示例见 extensions/demo/extension.py;extensions/core/extension.py 注册计划审批和询问用户工具,主提示词与恢复流程依赖它们,需保留。
RAG 客户端、记忆存储等共享资源通过 register_service() 声明,由 RuntimeEnvironment 启动和关闭。模型调用前后处理通过 register_middleware() 接入,无需修改主入口和 Agent 工厂。
完整离线示例见 extensions/context_demo/extension.py:共享术语表 → 检索工具 → 模型调用前补充上下文。在 .env 添加 CONTEXT_DEMO_ENABLED=1 后重启即可体验;默认关闭,不访问外部服务。这只是接口示例,真实知识库的索引、长期记忆存储和上下文预算仍需业务模块实现。
扩展可提供三个同步入口:
def enabled():
return config.get_bool("MY_RAG_ENABLED", False)
def validate_config():
# 仅检查配置,不开连接;抛 ValueError 会阻止启动。
if not config.get_str("MY_RAG_URL", ""):
raise ValueError("启用 RAG 时必须配置 MY_RAG_URL")
def setup(registry):
registry.register_service("rag_client", open_rag_client)
registry.register_tool(search_knowledge, requires=["rag_client"])
registry.register_middleware(
"RecallMiddleware", make_recall_middleware,
requires=["rag_client"], targets=["main", "researcher"],
)
以上是接口示意;researcher 必须由当前或其他启用的扩展注册。enabled() 和 validate_config() 都可省略;省略开关默认启用。关闭模块会跳过配置校验与 setup(),因此也不会注册其工具、服务或中间件。模块本身仍需导入才能读取开关,可选第三方依赖应放到服务工厂或中间件工厂内延迟导入。
服务工厂采用异步上下文管理器:
from contextlib import asynccontextmanager
from extensions import get_service
@asynccontextmanager
async def open_rag_client(dependencies):
# Client 为所选知识库 SDK 的客户端,在这里延迟导入。
client = Client(config.get_str("MY_RAG_URL"))
try:
yield client
finally:
await client.aclose()
async def search_knowledge(query: str) -> dict:
"""检索业务知识库,返回片段与来源。"""
client = get_service("rag_client")
return await client.search(query)
requires 声明的服务。服务可以依赖其他服务,按依赖顺序启动,逆序关闭。get_service() 获取资源;不要在导入或 setup() 阶段取服务。有服务依赖的工具先显式注册,再用名称或函数绑定到子 Agent。yield 前失败时,必须在自己的 try/finally 中释放已申请的资源。ToolRuntime.config["configurable"] 中读取 workspace_user、workspace_session。中间件工厂使用同步函数 factory(services),返回一个 AgentMiddleware,实例的 name 必须与注册名一致。
targets 默认 ["main"];可填写已注册子 Agent 名称。没有隐式继承,不支持向框架自动创建的 general-purpose 注入。requires 声明需要使用的服务。模型或工具包装方法应实现对应的异步钩子,框架实际通过异步路径执行。before=["OtherMiddleware"]、after=["OtherMiddleware"] 控制扩展中间件列表顺序;引用必须存在并有共同目标。没有顺序约束时按名称稳定排序。wrap_model_call,列表靠前者在外层:进入时按顺序,返回时逆序。它们仍位于框架内置中间件之后;主 Agent 的动态时间中间件位于扩展之前。CTX_COMPACT_ENABLED=0),同时自行保留历史归档与工具调用配对等约束;当前没有通用的内置中间件替换接口。GET /api/model 的 agent 对象会列出 services、有序的 middleware 及其目标,以及 disabled_extensions;不返回服务实例和连接配置。修改扩展或启用开关后重启服务。
有写入或外部副作用的工具使用 @define_tool(..., requires_approval=True)。也可以在 Agent 定义中设置 approval_tools=[工具函数或名称],但必须包含在该 Agent 的 tools 中。
审批要求按工具合并: 任意扩展要求某个工具审批后,主 Agent 和所有使用该工具的子 Agent 都会先等待批准。HITL_TOOLS 环境变量可以追加工具名;拼写错误会阻止启动。
启动会在数据库初始化、调度器启动之前检查:
setup() 缺失或加载失败、目录/入口符号链接等问题。数据库与 checkpoint 初始化后,按依赖启动扩展服务,再验证中间件工厂的实际返回类型与名称;失败会释放已启动资源,不进入调度与预热阶段。
新增或修改扩展后重启服务,再用 GET /api/model 查看 agent.extensions 和子 Agent 列表,用 GET /api/model/tools 查看工具。/api/model/reload 只重建已加载定义,不重新导入业务代码。
开发时运行:
.venv/bin/python -m pytest
.venv/bin/python scripts/selfcheck.py
自检使用当前数据库配置,建议在开发环境执行。扩展代码及 .md/.txt/.json/.yaml/.yml 资源自动进入发布白名单;其他资源类型需调整 release/build.py。
旧 tools.registry.register 保留兼容;新业务统一使用 define_tool 与 setup(registry),避免导入时修改全局注册状态。
工具需要返回引用来源时,使用 tools.registry.sourced(data=..., sources=[source(...)]),SSE 层自动生成引用事件。
技能仍放在 skills/<name>/SKILL.md,带有 name、description 的 YAML frontmatter。用户可以用 #skill:<name> 指定技能;技能是操作手册,不会替代工具注册。
全部配置项见 .env.example,每项都有默认值。几个容易搞错的:
| 变量 | 说明 |
|---|---|
DEEP_AGENT_ENV_PRIORITY |
.env 与进程环境谁优先。dotenv(默认)/ process / none。跑变体实例用 process:DEEP_AGENT_ENV_PRIORITY=process DB_DIALECT=mysql PORT=8081 python main.py |
DB_DIALECT |
sqlite(默认,业务库无需外部服务)/ mysql(旧部署显式兼容) |
DB_NAME |
MySQL 库名;库不存在时启动会自动建库,不需要手工 CREATE DATABASE |
CONTEXT_LIMIT |
声明的上下文窗口,压缩的唯一调节旋钮。0 = 用模型登记值 |
OPENAI_MAX_RETRIES |
模型 API 的 SDK 退避重试次数,默认 8,不含首次请求;0 禁用。针对 429 等临时错误重试请求,已开始的流不重放;修改后重启 |
CTX_COMPACT_ENABLED |
关掉会同时失去历史落盘能力,长会话会超窗报错 |
CHECKPOINTER |
sqlite(默认,落盘)/ memory(重启丢图状态) |
HITL_TOOLS |
追加需要人工审批的工具名,逗号分隔。如 calculate 便于自测中断闭环 |
BILLING_ALLOW_UNPRICED |
模型未定价时是否按 0 计费。默认 0 = 直接报错(fail-closed) |
TFAGENTS_DAILY_TOKEN_LIMIT |
每用户每日 token 限额,0 = 不限 |
配置冲突会被大声报出来:若 .env 与进程环境里有同名但不同的值,启动
日志会打 WARNING(含脱敏后的生效值),/health 的 config_conflicts 也能
看到。这解决的是一个很费时间的现象:改了配置却不生效,却没有任何提示。
# 离线单元 + SSE 契约测试(不需要密钥,不需要数据库服务)
python -m pytest
# 结构自检:导入、建表、agent 编译、工具注册
python scripts/selfcheck.py
# HTTP 层端到端(需服务已启动 + 可用密钥)
python scripts/http_e2e.py
http_e2e.py 覆盖:SSE 帧格式与响应头、落库与顺序、错误语义
(404/400/409)、中断/恢复闭环、定时任务的手动触发与 cron 校验。
main.py FastAPI 入口(lifespan / 健康检查 / 静态挂载)
config.py 配置唯一入口(.env 读取与冲突检测)
api/ HTTP 参数、路由与协议适配
sse_events.py SSE 编码(保留前端字段)
sse_core.py 事件转 SSE、断连关闭内层生成器
algorithms/ 独立算法源码及调用封装(特性曲线识别等,见 algorithms/README.md)
runtime/ 公共生命周期与传输无关的执行层
execution.py 准入、装配、execute() 非 HTTP 入口
engine.py ★ 事件流、运行结果、落库与收尾
events.py ★ 统一事件字段
interrupts.py 审批载荷、校验与恢复决策
concurrency.py 会话互斥与并发限制
context_meter.py 用量计量
source_tracker.py 来源聚合
subagent_tracker.py 子 Agent 跟踪
agents/ agent_factory · agent_registry · model_factory · prompts · middleware · agent_memory
extensions/ registry · core/extension.py · demo/extension.py(统一业务扩展)
tools/ registry(工具元数据与转换)· demo_tools(示例工具)
skills/demo-skill/ 示例技能
db/ db_conn(双方言)· mysql_schema.sql · schema_sqlite.sql · 三个 store
billing/ pricing · guard(预检)· meter(入账)
scheduler/ scheduler(APScheduler)· runner(独立会话执行)
obs/ log_setup · tracing(trace_id)
frontend/ Vue单文件组件、流式客户端与工作台测试
static/index.html Vue生产构建入口;static/ui为打包后的本地资源
tests/ pytest(离线单元、HTTP 与真实图引擎回归)
scripts/ selfcheck.py · http_e2e.py · start_server.sh
标 ★ 的两个文件是核心,改动时同步检查 SSE 契约与流式执行测试。
execution_runs 保存 running / waiting_input / success / failed / cancelled / interrupted。中断只表示等待输入,不发送完成事件;启动时将崩溃遗留的 running 标记为 interrupted。request_id(1–128 字符)。相同用户的相同键不会再次运行,返回 409 和原会话、运行 ID;不重放旧 SSE。新任务应使用新键。billing_pending,服务启动后及每分钟重试。数据库完全不可写时仍只能日志告警;进程被强杀前尚未观测到的 provider 用量需要外部账单对账。/workspace/、/conversation_history/ 映射到 data/workspaces/<用户哈希>/<会话哈希>/。子智能体共享其父会话目录,其他会话不可见;/skills/ 共享只读。waiting_input,从聊天恢复后同步结果状态。cron 显式使用 Asia/Shanghai。ask_user(questions):提出一至三个必要问题并暂停,前端显示输入框;刷新页面可恢复问题卡。request_plan_approval(plan):提交计划等待确认。plan 模式提示词会准确指向这个工具。interrupt_token;恢复接口必须原样携带。服务端在会话锁内核对当前中断,过期、缺失和重复确认返回 409。@define_tool(..., requires_approval=True) 声明强制审批,组装时自动进入框架 HITL。plan 提示词本身不构成所有工具的硬权限边界,真实写操作应声明强制审批。启动自动增建 execution_runs、billing_pending、request_keys,不会删除旧表。
修改前端后执行 cd frontend && npm run build,同步发布 static/index.html 和 static/ui/;自定义客户端需补传 interrupt_token。定时任务的详情、修改、删除、运行接口需携带其归属 user_name。
旧的共享 workspace/、conversation_history/ 原样保留,不会自动猜测文件归属或复制到新目录。
旧会话需要的附件可重新上传;需要迁移的历史文件应先确认用户和会话归属。
未登记的新模型现在会在执行前拒绝请求,不再自动写入零价;既有单价记录不会被覆盖。
当前只适合内网或开发环境,主要因为:
user_name 由客户端自报,可伪造。计费与会话归属都建立在
这个值上。需要为所有路由接入统一身份依赖,并区分普通用户与管理操作。尚未提供业务工具/技能、MCP 接入及 remember;默认标题使用文本截断。