在原有只读抓取 (browser_fetch_page) 基础上,新增 observe→act 循环, 让 Agent 能在用户的 Chrome 中实际操作网页:点击/输入/滚动/等待/页内跳转。 后端: - 新工具 browser_act,按 session 复用标签页,返回带编号的可交互元素清单+正文 - browser_context 任务队列扩展 kind/session_id/action 与 observation 结果 - /api/browser-context 路由下发 act 任务、接收 observation - BrowserContextConfig 增加 actions_enabled 等开关,tools.py 两级门控 - 接线 app.py 路由挂载、app_config.py 配置字段、config.yaml 工具注册 前端: - ChatPage 处理「发送当前页」回跳的 browser_context query 参数并自动提交 扩展 (browser-extension/deer-web-clipper): - shared.js 自包含注入函数 deerObservePage / deerPerformAction - background.js 按 kind 分发任务,session→tab 映射存 chrome.storage.session 测试:tests/test_browser_act.py(队列往返 + 工具校验 + 模拟扩展成功路径,8 passed) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
345 lines
13 KiB
YAML
345 lines
13 KiB
YAML
# Offline backend template for DeerFlow
|
||
config_version: 8
|
||
log_level: info
|
||
|
||
# Gateway FastAPI 监听端口。圆桌三路由(intent / recommend / multi_agent)的同进程
|
||
# loopback 调用会拼 http://127.0.0.1:<port> 打到本机其它 endpoint,所以必须与
|
||
# 实际监听端口一致(scripts/start-all.sh、Makefile 默认 8001)。
|
||
# 部署机改了端口请同步改这里;也可设环境变量 DEER_FLOW_GATEWAY_PORT 临时覆盖。
|
||
gateway:
|
||
port: 8001
|
||
|
||
models:
|
||
# - name: deepseek-chat
|
||
# display_name: DeepSeek Chat
|
||
# use: langchain_openai:ChatOpenAI
|
||
# model: deepseek-chat
|
||
# api_key: sk-1520028668cb4cc388ed8315ff1e856e
|
||
# base_url: https://api.deepseek.com
|
||
# request_timeout: 600.0
|
||
# max_retries: 2
|
||
# max_tokens: 4096
|
||
# temperature: 1.0
|
||
# supports_vision: false
|
||
# supports_thinking: true
|
||
|
||
# - name: deepseek-v4-flash
|
||
# display_name: DeepSeek V4 Flash
|
||
# use: langchain_openai:ChatOpenAI
|
||
# model: deepseek-v4-flash
|
||
# api_key: sk-e1c22b7931dc446f8b67e57878edd105
|
||
# base_url: https://api.deepseek.com
|
||
# request_timeout: 600.0
|
||
# max_retries: 2
|
||
# max_tokens: 4096
|
||
# temperature: 1.0
|
||
# supports_vision: false
|
||
# supports_thinking: true
|
||
|
||
# - name: deepseek-v4-pro
|
||
# display_name: DeepSeek V4 Pro
|
||
# use: langchain_openai:ChatOpenAI
|
||
# model: deepseek-v4-pro
|
||
# api_key: sk-e1c22b7931dc446f8b67e57878edd105
|
||
# base_url: https://api.deepseek.com
|
||
# request_timeout: 600.0
|
||
# max_retries: 2
|
||
# max_tokens: 4096
|
||
# temperature: 1.0
|
||
# supports_vision: false
|
||
# supports_thinking: true
|
||
# - name: MiniMax-M2.7
|
||
# display_name: MiniMax-M2.7
|
||
# use: langchain_openai:ChatOpenAI
|
||
# model: MiniMax-M2.7
|
||
# api_key: sk-cp-mrsAGtzDpUl0TpOBPVoiRG_pWF1kDDN9_mDAnONBs6HIPcNUexSKtSbx2QOxfKBvJT6Xw6hDTV6hdkJYFWfNA8UtHQ08TiGJiosdRoW6q1sGb6Cb7D85NHk
|
||
# base_url: https://api.minimaxi.com/v1
|
||
# max_tokens: 4096
|
||
# temperature: 1.0
|
||
# supports_vision: true
|
||
# supports_thinking: true
|
||
# - name: glm-5.1
|
||
# display_name: glm-5.1
|
||
# use: langchain_openai:ChatOpenAI
|
||
# model: glm-5.1
|
||
# api_key: 75121e4bed7640d5b71ac95cf8b4310a.RA3GyRBAR3kv7VrR
|
||
# base_url: https://open.bigmodel.cn/api/paas/v4/
|
||
# max_tokens: 4096
|
||
# temperature: 1.0
|
||
# supports_vision: true
|
||
# supports_thinking: true
|
||
# - name: deepseek-ai/DeepSeek-OCR
|
||
# display_name: deepseek-ai/DeepSeek-OCR
|
||
# use: langchain_openai:ChatOpenAI
|
||
# model: deepseek-ai/DeepSeek-OCR
|
||
# api_key: sk-beshfxfxfrllsczpqfrgjbugfbpzxgqgedbehceytpcvvtyb
|
||
# base_url: https://api.siliconflow.cn/v1/
|
||
# max_tokens: 4096
|
||
# temperature: 1.0
|
||
# supports_vision: true
|
||
# supports_thinking: true
|
||
# - name: zai-org/GLM-5-FP8
|
||
# display_name: zai-org/GLM-5-FP8
|
||
# use: langchain_openai:ChatOpenAI
|
||
# model: zai-org/GLM-5-FP8
|
||
# api_key: modalresearch_gWu46FDpVLYZWPVEA3b_2fq8Jw2UJiKMgEm_Rxmn1SA
|
||
# base_url: https://api.us-west-2.modal.direct/v1
|
||
# request_timeout: 600.0
|
||
# max_retries: 2
|
||
# max_tokens: 4096
|
||
# temperature: 1.0
|
||
# supports_vision: true
|
||
# supports_thinking: true
|
||
- name: deepseek-ai/DeepSeek-V4-Flash
|
||
display_name: deepseek-ai/DeepSeek-V4-Flash
|
||
use: langchain_openai:ChatOpenAI
|
||
model: deepseek-ai/DeepSeek-V4-Flash
|
||
api_key: sk-ighylafootabtuthwueirmoycccyqtdgbifyhozduvolkdyt
|
||
base_url: https://api.siliconflow.cn/v1/
|
||
max_tokens: 4096
|
||
temperature: 1.0
|
||
supports_vision: true
|
||
supports_thinking: true
|
||
# 识图(图片识别)配置。
|
||
# - model_name:指定一个 models[] 中的模型名,作为“默认识图大模型”。view_image 工具会把
|
||
# 图片交给该模型识别,并把识别出的文字结果返回给主对话模型 —— 这样即使主对话模型不支持
|
||
# 视觉,也能“看图”。请改成你实际用于识图的模型名。
|
||
# - 若留空 / 注释掉 model_name,则回退到主对话模型自身的视觉能力(需该模型 supports_vision: true)。
|
||
# - prompt:不带具体问题时使用的默认识图指令(可选)。
|
||
# - max_tokens:识图模型输出上限(可选,默认用该模型自身的配置)。
|
||
vision:
|
||
model_name: deepseek-ai/DeepSeek-OCR
|
||
# prompt: 请详细描述这张图片的内容。
|
||
# max_tokens: 2048
|
||
|
||
# 上传文件识别配置。
|
||
# - auto_convert_documents: true 时,上传的 PDF / Word / Excel / PPT 会在服务端自动转换为
|
||
# Markdown(.md),agent 通过 read_file / grep 读取其内容 —— 即“识别”文档内容。
|
||
# 纯文本类(txt / csv / md / json / 代码等)无需转换,agent 用 read_file 直接读取。
|
||
# 图片(png/jpg/webp)由 view_image + 上面的 vision 识图模型处理。
|
||
# - pdf_converter: auto(优先 pymupdf4llm,缺失时回退 markitdown)| pymupdf4llm | markitdown
|
||
uploads:
|
||
auto_convert_documents: true
|
||
pdf_converter: auto
|
||
|
||
# Chrome 插件「当前网页发送到 Deer」入口。默认关闭,打开后才启用
|
||
# /api/browser-context/capture。它只保存当前页 Markdown 到线程 uploads,
|
||
# 不会改变普通聊天、上传或工具调用流程。
|
||
browser_context:
|
||
enabled: true
|
||
max_content_chars: 200000
|
||
max_title_chars: 300
|
||
max_url_chars: 2048
|
||
filename_prefix: webpage
|
||
route_base: /page/workspace
|
||
task_result_timeout_seconds: 60
|
||
task_poll_interval_seconds: 1
|
||
task_lease_seconds: 120
|
||
tool_result_max_chars: 60000
|
||
# 交互式页面操作(browser_act:点击/输入/滚动/等待/跳转)。关闭后只保留只读抓取。
|
||
actions_enabled: true
|
||
action_result_timeout_seconds: 45
|
||
max_observed_elements: 150
|
||
observation_text_max_chars: 12000
|
||
|
||
title:
|
||
enabled: true
|
||
max_words: 6
|
||
max_chars: 60
|
||
prompt_template: |
|
||
请为下面这段对话生成一个简洁的标题,概括对话的核心主题。
|
||
|
||
要求:
|
||
- 标题必须使用简体中文
|
||
- 尽量精炼,不超过 {max_words} 个词
|
||
- 只返回标题本身,不要加引号、不要解释、结尾不要标点
|
||
|
||
User: {user_msg}
|
||
Assistant: {assistant_msg}
|
||
|
||
tool_groups:
|
||
- name: web
|
||
- name: browser
|
||
- name: file:read
|
||
- name: file:write
|
||
- name: bash
|
||
|
||
tools:
|
||
- name: web_search
|
||
group: web
|
||
use: deerflow.community.ddg_search.tools:web_search_tool
|
||
max_results: 5
|
||
|
||
- name: web_fetch
|
||
group: web
|
||
use: deerflow.community.jina_ai.tools:web_fetch_tool
|
||
timeout: 15
|
||
|
||
- name: browser_fetch_page
|
||
group: browser
|
||
use: deerflow.tools.builtins.browser_context_tool:browser_fetch_page_tool
|
||
|
||
- name: browser_act
|
||
group: browser
|
||
use: deerflow.tools.builtins.browser_act_tool:browser_act_tool
|
||
|
||
- name: ls
|
||
group: file:read
|
||
use: deerflow.sandbox.tools:ls_tool
|
||
|
||
- name: read_file
|
||
group: file:read
|
||
use: deerflow.sandbox.tools:read_file_tool
|
||
|
||
- name: glob
|
||
group: file:read
|
||
use: deerflow.sandbox.tools:glob_tool
|
||
max_results: 200
|
||
|
||
- name: grep
|
||
group: file:read
|
||
use: deerflow.sandbox.tools:grep_tool
|
||
max_results: 100
|
||
|
||
- name: write_file
|
||
group: file:write
|
||
use: deerflow.sandbox.tools:write_file_tool
|
||
|
||
- name: str_replace
|
||
group: file:write
|
||
use: deerflow.sandbox.tools:str_replace_tool
|
||
|
||
- name: bash
|
||
group: bash
|
||
use: deerflow.sandbox.tools:bash_tool
|
||
|
||
sandbox:
|
||
use: deerflow.sandbox.local:LocalSandboxProvider
|
||
allow_host_bash: true
|
||
|
||
skills:
|
||
path: skills
|
||
security_scan:
|
||
enabled: false
|
||
|
||
agents_api:
|
||
enabled: true
|
||
|
||
# 登录鉴权扩展:在基础登录之上叠加两种自定义模式。
|
||
auth_login:
|
||
# 用户名直登口令门:开关打开后,/login/<用户名> 直登必须再带 ?password=口令,
|
||
# 口令不对直接 401。password 留空则默认口令为 123ewq。仅作用于用户名直登。
|
||
username_login:
|
||
require_password: false
|
||
password: ""
|
||
# token 换登录:前端用 ?authToken=上游token 进入,后端调用 token_info_url
|
||
# (把该 token 直接拼成 Authorization: Bearer <token> 头)换出用户名,再登录;
|
||
# 用户名不在库里时自动注册。
|
||
token_login:
|
||
enabled: false
|
||
token_info_url: "https://ch1.b.uat.4.cn/consumer/login/getTokenInfo"
|
||
# 已弃用:鉴权头现由前端传入的 token 拼成 "Bearer <token>",此项不再生效,保留仅兼容。
|
||
service_authorization: "Bearer admin"
|
||
timeout_seconds: 10
|
||
# 上游是 HTTPS。UAT/内网常用自签或私有 CA 证书,默认 verify=true 会握手失败导致
|
||
# 永远 401。此处 uat 域名按自签处理,先关校验;生产请置 true 或配 ca_cert_path。
|
||
verify_ssl: false
|
||
ca_cert_path: "" # 可选:PEM 格式 CA 包路径,设置后优先生效并启用校验
|
||
|
||
# 日志系统入口(仅管理员可见):右下角「设置」菜单中会多出一个「日志系统」入口,
|
||
# 点击后在新标签页打开下面的 url。enabled 关掉则不显示该入口。
|
||
|
||
|
||
database:
|
||
backend: sqlite
|
||
sqlite_dir: /app/backend/.deer-flow/data
|
||
pool_size: 20 # 基础连接池(postgres/mysql 生效;sqlite 忽略)
|
||
max_overflow: 20 # 高峰期可临时超出 pool_size 的额外连接数,真实上限 = pool_size + max_overflow
|
||
pool_timeout: 30 # 池满时等待空闲连接的秒数,超时报错
|
||
echo_sql: false
|
||
|
||
checkpointer:
|
||
type: sqlite
|
||
connection_string: /app/backend/.deer-flow/data/deerflow.db
|
||
|
||
# 记忆子系统:跨会话持久化用户画像与 agent 私有记忆。
|
||
# 三层覆盖优先级(高->低):
|
||
# users/{user_id}/memory_config.json > .deer-flow/memory_user_overrides.json > 此处
|
||
memory:
|
||
enabled: true
|
||
version: v1 # 记忆版本: 'v1'(原版 DeerFlow, memory.json + LLM 自动提取)
|
||
# 'v2'(USER.md/MEMORY.md + 可选 Hindsight)
|
||
provider: hindsight # 'builtin' | 'hindsight'(外部 provider 名)
|
||
# builtin 始终启用,这里只决定 external 选哪个
|
||
# (version=v1 时此字段忽略)
|
||
|
||
# V1 专属配置 —— version=v1 时生效
|
||
v1:
|
||
max_facts: 100 # 最多保留的事实条数,超出时驱逐最低置信度的事实
|
||
fact_confidence_threshold: 0.7 # 事实置信度阈值,低于此值不保存
|
||
debounce_seconds: 30 # 对话结束后延迟多少秒才触发 LLM 提取
|
||
model_name: "" # 用于事实提取的 LLM 模型名;空则继承全局默认模型
|
||
token_budget: 2000 # 注入系统提示词的 token 预算
|
||
builtin:
|
||
enabled: true
|
||
memory_char_limit: 2200
|
||
user_char_limit: 1375
|
||
deduplicate_on_load: true
|
||
|
||
hindsight: # provider=hindsight 时生效,需另行部署 Hindsight 服务
|
||
mode: local_external # 'cloud' | 'local_embedded' | 'local_external'
|
||
api_url: http://47.88.25.99:18888
|
||
api_key: "" # 部署 Hindsight 后填,或改成 $HINDSIGHT_API_KEY 并设同名环境变量
|
||
# 注意:写成 $VAR 但环境变量未设置会导致配置加载失败
|
||
bank_id_template: "deerflow-user-{user_id}"
|
||
work_bank_id_template: "deerflow-work-{user_id}-{agent_id}"
|
||
memory_mode: context # 'context' | 'tools' | 'hybrid'
|
||
bank_mission: "你是一个中文记忆助手,负责从对话中提炼和存储重要信息。所有记忆内容必须使用中文记录。"
|
||
bank_retain_mission: "请从以下对话中提炼重要信息,包括用户的偏好、需求、背景知识和关键事实。所有提炼的内容必须用中文表达。"
|
||
recall_budget: mid # 'low' | 'mid' | 'high'
|
||
recall_max_tokens: 4096
|
||
recall_max_input_chars: 800
|
||
auto_recall: true
|
||
auto_retain: true
|
||
retain_async: true
|
||
retain_every_n_turns: 1
|
||
retain_user_prefix: "User"
|
||
retain_assistant_prefix: "Assistant"
|
||
|
||
# background_extraction:预留功能,代码尚未实现 —— 仅设计文档存在。
|
||
# 待对应版本支持后,取消下面整段注释即可启用(漏记兜底)。
|
||
# background_extraction:
|
||
# enabled: false # 默认关闭,作为漏记兜底
|
||
# debounce_seconds: 30
|
||
# model_name: ~ # null = 用默认模型
|
||
# fact_confidence_threshold: 0.7
|
||
# max_facts_per_extraction: 10
|
||
# correction_detection: true
|
||
# reinforcement_detection: true
|
||
|
||
injection:
|
||
enabled: true
|
||
max_tokens: 2000
|
||
include_builtin: true
|
||
include_hindsight: true
|
||
context_tag: "memory-context" # 包裹 Hindsight recall 的标签名
|
||
|
||
security:
|
||
scan_content: true # 注入/外渗模式扫描
|
||
block_invisible_unicode: true
|
||
# streaming_scrubber:预留字段,代码尚未实现 —— 待版本支持后取消注释。
|
||
# streaming_scrubber: true # SSE 流前清洗 <memory-context>
|
||
|
||
# AI 写作子系统 —— 素材收集专家专用配置。
|
||
# 注意:这三项必须嵌套在 ai_writing: 段下;写到顶层会被 _ai_writing_config()
|
||
# (app/gateway/routers/ai_writing.py:_ai_writing_config) 直接忽略。
|
||
ai_writing:
|
||
keyword_model: deepseek-chat # 检索词生成专用模型,留空回退写作主模型
|
||
rank_model: deepseek-chat # 素材排序专用模型,留空回退写作主模型
|
||
max_materials: 20 # 合并去重后的素材总量上限,范围 [1, 100],默认 20
|
||
|
||
log_system:
|
||
enabled: true
|
||
url: "https://your-log-system.example.com"
|
||
label: "日志系统"
|