- Fix delete agent 500: clean up FK records (agent_llm_logs, permissions, schedules, executions, team_members) and unbind goals/tasks before delete - Remove hardcoded personality templates in Android, replace with dynamic system prompt generation from name + description - Set promptSectionsEnabled=false to bypass PromptComposer for personality - Add Tencent Cloud Linux deployment guide (Docker Compose) - Accumulated backend service updates, frontend UI fixes, Android app changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
194 lines
7.3 KiB
Python
194 lines
7.3 KiB
Python
"""
|
||
升级后 Agent 体验脚本 — 展示 P0-P4 + 记忆增强的全部能力
|
||
"""
|
||
import asyncio
|
||
import sys
|
||
import os
|
||
|
||
# 修复 Windows GBK 终端编码问题
|
||
if sys.platform == "win32":
|
||
import io
|
||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
||
|
||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "backend"))
|
||
|
||
from app.agent_runtime.core import AgentRuntime
|
||
from app.agent_runtime.schemas import (
|
||
AgentConfig,
|
||
AgentLLMConfig,
|
||
AgentToolConfig,
|
||
AgentBudgetConfig,
|
||
AgentMemoryConfig,
|
||
)
|
||
from app.core.hooks import HookManager, HookConfig, HookEvent, HookContext
|
||
|
||
DEMO_MEMORY_DIR = os.path.join(os.path.dirname(__file__), ".demo_memory")
|
||
|
||
|
||
def setup_demo_memories():
|
||
"""预置演示记忆文件"""
|
||
from app.core.memdir import MemoryDir, MemoryType
|
||
|
||
md = MemoryDir(DEMO_MEMORY_DIR)
|
||
|
||
# 清除旧记忆
|
||
for f in os.listdir(DEMO_MEMORY_DIR):
|
||
if f.endswith(".md"):
|
||
os.remove(os.path.join(DEMO_MEMORY_DIR, f))
|
||
|
||
if os.path.exists(os.path.join(DEMO_MEMORY_DIR, "MEMORY.md")):
|
||
os.remove(os.path.join(DEMO_MEMORY_DIR, "MEMORY.md"))
|
||
|
||
md.save_memory(
|
||
"user_preferences.md",
|
||
MemoryType.USER,
|
||
"用户: Python全栈开发者",
|
||
"用户有8年Python后端经验,偏好简洁的函数式风格,不喜欢过度抽象的类层次",
|
||
"用户是一位资深Python全栈开发者,习惯使用 FastAPI + SQLAlchemy 技术栈。\n"
|
||
"代码风格偏好:偏好简洁、扁平、显式的代码,不喜欢深层继承和过度抽象。\n"
|
||
"Why: 用户在代码评审中多次提到'不要为未来设计'和'三行重复比一个错误抽象好'。\n"
|
||
"How to apply: 新功能直接用函数实现,除非有明确的3+处复用才提取类。",
|
||
)
|
||
|
||
md.save_memory(
|
||
"feedback_testing.md",
|
||
MemoryType.FEEDBACK,
|
||
"测试规范: 集成测试用真实数据库",
|
||
"集成测试必须使用真实数据库而非 mock,因为 mock 与生产环境不一致曾导致故障",
|
||
"集成测试必须使用真实数据库,不要 mock。\n"
|
||
"Why: 2026年3月有一次生产迁移失败,因为mock测试通过但真实schema不兼容。\n"
|
||
"How to apply: 所有涉及 SQLAlchemy session 的测试用例必须使用测试数据库。",
|
||
)
|
||
|
||
md.save_memory(
|
||
"project_release.md",
|
||
MemoryType.PROJECT,
|
||
"项目: v2.1 6月25日发布",
|
||
"当前版本v2.1计划于2026-06-25发布,冻结日前需优先完成安全审计和API文档",
|
||
"v2.1 发布日期: 2026年6月25日。\n"
|
||
"Why: 客户合同中约定的交付日期,延期每天有违约金。\n"
|
||
"How to apply: 优先完成P0 Security Audit和API Documentation,非关键UI调整推迟到v2.2。",
|
||
)
|
||
|
||
md.save_memory(
|
||
"reference_monitoring.md",
|
||
MemoryType.REFERENCE,
|
||
"参考: 生产监控面板地址",
|
||
"Grafana监控面板和Sentry错误追踪的外部地址",
|
||
"Grafana: http://grafana.internal:3000/d/api-latency\n"
|
||
"Sentry: https://sentry.internal/organizations/myorg/projects/api\n"
|
||
"Kibana: http://kibana.internal:5601/app/logs",
|
||
)
|
||
|
||
print(f" [预置] 4条演示记忆已写入 {DEMO_MEMORY_DIR}")
|
||
return md
|
||
|
||
|
||
async def main():
|
||
print("=" * 60)
|
||
print("天工 Agent 升级体验 — 全部新特性已启用")
|
||
print("=" * 60)
|
||
|
||
# 1. 预置记忆
|
||
print("\n[1/5] 初始化文件式记忆系统 (MEMORY.md)")
|
||
md = setup_demo_memories()
|
||
manifest = md.scan()
|
||
print(f" 已加载 {manifest.total_files} 条记忆")
|
||
|
||
# 2. 注册审计 Hook
|
||
print("\n[2/5] 注册安全审计 Hook")
|
||
tool_log: list = []
|
||
|
||
async def audit_hook(ctx: HookContext):
|
||
tool_log.append(f"[审计] {ctx.event.value} tool={ctx.tool_name}")
|
||
return None # 不拦截
|
||
|
||
hooks = HookManager(hooks=[
|
||
HookConfig(
|
||
event=HookEvent.PRE_TOOL_USE, matcher="*",
|
||
description="审计所有工具调用", python_handler=audit_hook,
|
||
),
|
||
HookConfig(
|
||
event=HookEvent.POST_TOOL_USE, matcher="*",
|
||
description="审计工具结果", python_handler=audit_hook,
|
||
),
|
||
])
|
||
print(f" 已注册 {len(hooks.get_hooks(HookEvent.PRE_TOOL_USE))} 个 PreToolUse Hook")
|
||
|
||
# 3. 构建配置 (所有新特性开启)
|
||
print("\n[3/5] 构建 Agent 配置")
|
||
config = AgentConfig(
|
||
name="升级体验Agent",
|
||
system_prompt=(
|
||
"你是一个智能助手,运行在升级后的天工平台上。\n"
|
||
"你可以使用工具的读写能力帮助用户完成任务。\n"
|
||
"注意系统提示词中包含的记忆信息,根据需要使用它们。"
|
||
),
|
||
llm=AgentLLMConfig(
|
||
provider="deepseek",
|
||
model="deepseek-v4-flash",
|
||
temperature=0.7,
|
||
max_iterations=8,
|
||
),
|
||
tools=AgentToolConfig(
|
||
permission_level="acceptEdits",
|
||
),
|
||
memory=AgentMemoryConfig(
|
||
enabled=True,
|
||
max_history_messages=20,
|
||
memory_dir_enabled=True,
|
||
memory_dir_path=DEMO_MEMORY_DIR,
|
||
persist_to_db=False, # 演示模式不写DB
|
||
vector_memory_enabled=False,
|
||
learning_enabled=False,
|
||
),
|
||
budget=AgentBudgetConfig(
|
||
max_llm_invocations=20,
|
||
max_tool_calls=30,
|
||
),
|
||
)
|
||
|
||
# 4. 创建 Runtime
|
||
runtime = AgentRuntime(config=config, hook_manager=hooks)
|
||
|
||
print(f" ✓ 权限级别: {runtime.tool_manager._permission.level.value}")
|
||
print(f" ✓ 文件式记忆: {'启用' if runtime._memdir else '禁用'} ({runtime._memdir_manifest.total_files if runtime._memdir_manifest else 0}条)")
|
||
print(f" ✓ Hook 管理: {len(hooks.get_hooks(HookEvent.PRE_TOOL_USE))} PreToolUse + {len(hooks.get_hooks(HookEvent.POST_TOOL_USE))} PostToolUse")
|
||
print(f" ✓ 计划模式: {'启用' if runtime.plan_mode else '禁用'}")
|
||
print(f" ✓ 崩溃恢复: 已初始化")
|
||
|
||
# 5. 运行对话
|
||
print("\n[4/5] 开始对话...")
|
||
print("-" * 40)
|
||
|
||
test_queries = [
|
||
"你好!请用 list_files 看看当前目录有什么文件,简单列出即可",
|
||
"根据你的记忆,我应该用什么代码风格来写一个新功能?",
|
||
]
|
||
|
||
for i, query in enumerate(test_queries, 1):
|
||
print(f"\n--- 第 {i} 轮 ---")
|
||
print(f"用户: {query}")
|
||
result = await runtime.run(query)
|
||
print(f"Agent: {result.content[:300]}...")
|
||
print(f"(迭代{result.iterations_used}次, 调用{result.tool_calls_made}个工具)")
|
||
|
||
# 6. 总结
|
||
print("\n" + "=" * 60)
|
||
print("[5/5] 新特性实战验证结果")
|
||
print("=" * 60)
|
||
print(f" 权限检查: AgentToolManager 内置 PermissionChecker (acceptEdits)")
|
||
print(f" Hook 审计: 记录了 {len(tool_log)} 次工具调用")
|
||
if tool_log:
|
||
for log in tool_log[:5]:
|
||
print(f" {log}")
|
||
print(f" 文件记忆: MEMORY.md + {manifest.total_files} 条分类记忆")
|
||
print(f" 记忆目录: {DEMO_MEMORY_DIR}")
|
||
print(f" 对话轮数: {len(test_queries)}")
|
||
print(f" 会话 ID: {runtime.context.session_id}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|