feat(agent): 实现 Agent Runtime LLM 配置与消息元数据结构化支持
This commit is contained in:
@@ -0,0 +1,746 @@
|
||||
# UserAgentContext & ProfileSettings v1 设计
|
||||
|
||||
**Date:** 2026-03-05
|
||||
**Status:** Approved
|
||||
|
||||
---
|
||||
|
||||
## 目标
|
||||
|
||||
为 Agent Runtime 提供完整的用户画像上下文,通过 Pydantic 约束 profiles.settings 结构,确保:
|
||||
|
||||
1. 运行时入口读取 profile(username/bio/settings)
|
||||
2. settings 结构类型安全、版本可演进
|
||||
3. 关键配置(语言/时区/国家)符合标准格式
|
||||
|
||||
---
|
||||
|
||||
## 架构
|
||||
|
||||
```
|
||||
Profile (DB JSONB)
|
||||
↓
|
||||
ProfileSettings (Pydantic)
|
||||
↓
|
||||
UserAgentContext (DataClass)
|
||||
↓
|
||||
build_global_system_prompt(ctx)
|
||||
```
|
||||
|
||||
**设计原则:**
|
||||
- 唯一入口:`get_user_agent_context(user_id)` 读取并构造上下文
|
||||
- 不可变:UserAgentContext 使用 frozen dataclass
|
||||
- 向后兼容:version 字段预留未来演进
|
||||
|
||||
---
|
||||
|
||||
## ProfileSettings v1 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"preferences": {
|
||||
"interface_language": "zh-CN",
|
||||
"ai_language": "zh-CN",
|
||||
"timezone": "Asia/Shanghai",
|
||||
"country": "CN"
|
||||
},
|
||||
"privacy": {},
|
||||
"notification": {}
|
||||
}
|
||||
```
|
||||
|
||||
### 字段说明
|
||||
|
||||
| 字段 | 类型 | 默认值 | 约束 |
|
||||
|------|------|--------|------|
|
||||
| `version` | int | 1 | 必须为 1(v1 锁定) |
|
||||
| `preferences.interface_language` | str | "zh-CN" | BCP-47 格式 |
|
||||
| `preferences.ai_language` | str | "zh-CN" | BCP-47 格式 |
|
||||
| `preferences.timezone` | str | "Asia/Shanghai" | IANA 时区 |
|
||||
| `preferences.country` | str | "CN" | ISO 3166-1 alpha-2 |
|
||||
| `privacy` | dict | {} | 空对象(预留) |
|
||||
| `notification` | dict | {} | 空对象(预留) |
|
||||
|
||||
### 约束规则
|
||||
|
||||
**1. BCP-47 语言格式**
|
||||
|
||||
正则:`^[a-z]{2,3}(-[A-Z][a-z]{3})?(-[A-Z]{2})?$`
|
||||
|
||||
示例:
|
||||
- ✅ zh-CN, en-US, zh-TW, ja-JP
|
||||
- ❌ zh_CN, EN, chn
|
||||
|
||||
**2. IANA 时区**
|
||||
|
||||
使用 `zoneinfo.ZoneInfo` 校验。
|
||||
|
||||
示例:
|
||||
- ✅ Asia/Shanghai, America/New_York, UTC
|
||||
- ❌ CST, GMT+8
|
||||
|
||||
**3. ISO 3166-1 alpha-2 国家代码**
|
||||
|
||||
使用 `pycountry.countries.get(alpha_2=...)` 校验。
|
||||
|
||||
示例:
|
||||
- ✅ CN, US, JP, GB
|
||||
- ❌ CHN, USA, zz
|
||||
|
||||
---
|
||||
|
||||
## UserAgentContext 结构
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class UserAgentContext:
|
||||
user_id: UUID
|
||||
username: str
|
||||
bio: str | None
|
||||
settings: ProfileSettings
|
||||
```
|
||||
|
||||
**设计要点:**
|
||||
- 不可变(frozen=True):防止运行时修改
|
||||
- 完整画像:包含身份(username/bio)和配置(settings)
|
||||
- 唯一构造入口:`get_user_agent_context(user_id)`
|
||||
|
||||
---
|
||||
|
||||
## Pydantic 模型实现
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
from dataclasses import dataclass
|
||||
from uuid import UUID
|
||||
import re
|
||||
|
||||
class PreferenceSettings(BaseModel):
|
||||
interface_language: str = "zh-CN"
|
||||
ai_language: str = "zh-CN"
|
||||
timezone: str = "Asia/Shanghai"
|
||||
country: str = "CN"
|
||||
|
||||
@field_validator("interface_language", "ai_language")
|
||||
@classmethod
|
||||
def validate_bcp47(cls, v: str) -> str:
|
||||
pattern = r"^[a-z]{2,3}(-[A-Z][a-z]{3})?(-[A-Z]{2})?$"
|
||||
if not re.match(pattern, v):
|
||||
raise ValueError(f"Invalid BCP-47 language tag: {v}")
|
||||
return v
|
||||
|
||||
@field_validator("timezone")
|
||||
@classmethod
|
||||
def validate_iana_timezone(cls, v: str) -> str:
|
||||
import zoneinfo
|
||||
try:
|
||||
zoneinfo.ZoneInfo(v)
|
||||
except Exception:
|
||||
raise ValueError(f"Invalid IANA timezone: {v}")
|
||||
return v
|
||||
|
||||
@field_validator("country")
|
||||
@classmethod
|
||||
def validate_iso_country(cls, v: str) -> str:
|
||||
import pycountry
|
||||
if not pycountry.countries.get(alpha_2=v.upper()):
|
||||
raise ValueError(f"Invalid ISO 3166-1 alpha-2 country code: {v}")
|
||||
return v.upper()
|
||||
|
||||
class ProfileSettings(BaseModel):
|
||||
version: int = Field(default=1, ge=1, le=1)
|
||||
preferences: PreferenceSettings = Field(default_factory=PreferenceSettings)
|
||||
privacy: dict = Field(default_factory=dict)
|
||||
notification: dict = Field(default_factory=dict)
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class UserAgentContext:
|
||||
user_id: UUID
|
||||
username: str
|
||||
bio: str | None
|
||||
settings: ProfileSettings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 依赖项
|
||||
|
||||
需要添加到 `backend/pyproject.toml`:
|
||||
|
||||
```toml
|
||||
[project.dependencies]
|
||||
pycountry = ">=23.0.0"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 迁移策略
|
||||
|
||||
**数据库层:**
|
||||
- profiles.settings 保持 JSONB,不做 schema 变更
|
||||
- 现有数据默认值:`{"version": 1, "preferences": {"country": "CN"}}`
|
||||
|
||||
**应用层:**
|
||||
- 读取时:`ProfileSettings.model_validate(profile.settings or {})`
|
||||
- 写入时:`profile.settings = settings.model_dump()`
|
||||
|
||||
---
|
||||
|
||||
## 未来演进
|
||||
|
||||
|
||||
**版本迁移:**
|
||||
- Pydantic 支持多版本共存
|
||||
- 数据库不做破坏性变更
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## AG-UI 事件转发与落库策略
|
||||
|
||||
### 核心原则
|
||||
|
||||
**1. 事件转发时机:**
|
||||
- 只有 organization 阶段完成后转发 AG-UI 事件
|
||||
- AG-UI bridge 已实现底层机制,编排层控制转发时机
|
||||
|
||||
**2. 落库时机:**
|
||||
- 意图识别和任务执行阶段:落库但 seq 取负数(用于审计)
|
||||
- 结果反馈阶段:seq 取最新 seq 的绝对值 +1(用于展示)
|
||||
|
||||
### Seq 设计细节
|
||||
|
||||
**意图识别和任务执行阶段(审计用):**
|
||||
- seq 取负数(如 -1, -2)
|
||||
- role: "assistant"(标记为 agent 输出)
|
||||
- content: 阶段的完整输出(用于审计/调试)
|
||||
- 重建会话时通过 `WHERE seq > 0` 过滤,不展示给用户
|
||||
|
||||
**结果反馈阶段(展示用):**
|
||||
- seq 取正数(取最新负数的绝对值 +1)
|
||||
- role: "assistant"
|
||||
- content: OrganizationResult.assistant_text
|
||||
- 重建会话时通过 `WHERE seq > 0` 展示给用户
|
||||
|
||||
**示例:**
|
||||
```
|
||||
| seq | role | content | 展示 |
|
||||
|------|----------|----------------------------|------|
|
||||
| -2 | assistant| ExecutionResult (完整) | 否 |
|
||||
| -1 | assistant| IntentResult (完整) | 否 |
|
||||
| 1 | user | 用户输入 | 是 |
|
||||
| 2 | assistant| OrganizationResult | 是 |
|
||||
```
|
||||
|
||||
### 编排层职责
|
||||
|
||||
```python
|
||||
@listen(intent_stage)
|
||||
async def persist_intent(self, state: FlowState) -> FlowState:
|
||||
# seq 取负数
|
||||
seq = await message_repo.get_next_negative_seq(state.session_id)
|
||||
await message_repo.create(
|
||||
session_id=state.session_id,
|
||||
seq=seq, # 负数
|
||||
role="assistant",
|
||||
content=state.intent_result.model_dump_json(),
|
||||
...
|
||||
)
|
||||
return state
|
||||
|
||||
@listen(execution_stage)
|
||||
async def persist_execution(self, state: FlowState) -> FlowState:
|
||||
# seq 取负数
|
||||
seq = await message_repo.get_next_negative_seq(state.session_id)
|
||||
await message_repo.create(
|
||||
session_id=state.session_id,
|
||||
seq=seq, # 负数
|
||||
role="assistant",
|
||||
content=state.execution_result.model_dump_json(),
|
||||
...
|
||||
)
|
||||
return state
|
||||
|
||||
@listen(organization_stage)
|
||||
async def finalize_flow(self, state: FlowState) -> FlowState:
|
||||
result = state.organization_result
|
||||
|
||||
# seq 取正数(最新负数绝对值+1)
|
||||
seq = await message_repo.get_next_positive_seq(state.session_id)
|
||||
await message_repo.create(
|
||||
session_id=state.session_id,
|
||||
seq=seq, # 正数
|
||||
role="assistant",
|
||||
content=result.assistant_text,
|
||||
...
|
||||
)
|
||||
|
||||
# 触发 AG-UI 事件(由 bridge 处理)
|
||||
return state
|
||||
```
|
||||
|
||||
### Token 和 Cost 累加
|
||||
|
||||
**策略:在内存中累加所有阶段的 token 和 cost,organization 完成后统一落库。**
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class FlowState:
|
||||
# ...
|
||||
tokens: dict[str, dict] = field(default_factory=dict)
|
||||
cost: Decimal = Decimal("0")
|
||||
currency: str = "CNY"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CrewAI Flow 三阶段设计
|
||||
|
||||
### 架构概览
|
||||
|
||||
```
|
||||
User Input + UserAgentContext
|
||||
↓
|
||||
@start() begin()
|
||||
↓
|
||||
@listen() intent_stage() → 判断 can_answer_directly
|
||||
↓ (router)
|
||||
├─ DIRECT_RESPONSE → 直接返回
|
||||
└─ NEEDS_EXECUTION
|
||||
↓
|
||||
@listen() execution_stage() → 任务执行/工具调用
|
||||
↓
|
||||
@listen() organization_stage() → 结果组织与表达
|
||||
↓
|
||||
返回给用户
|
||||
```
|
||||
|
||||
### 三阶段职责
|
||||
|
||||
**1. Intent Recognition(意图识别)**
|
||||
- Agent Type: `INTENT_RECOGNITION`
|
||||
- 输出结构(最小化设计):
|
||||
```python
|
||||
class IntentResult(BaseModel):
|
||||
direct_answer: bool # 是否可以直接回答
|
||||
intent_analysis: str # 意图分析文本(用于调试/审计)
|
||||
execution_prompt: str # 给 execution 阶段的提示词(direct_answer=false时使用)
|
||||
direct_response: str # 直接回复文本(direct_answer=true时使用)
|
||||
```
|
||||
- 短路逻辑:
|
||||
- `direct_answer=true` → 完全跳过 execution 和 organization,直接返回 direct_response
|
||||
- `direct_answer=false` → 进入 execution 阶段
|
||||
- 输出约束:使用 `output_pydantic=IntentResult`
|
||||
- **落库策略**:落库到 messages 表,但重建会话时不展示
|
||||
|
||||
**2. Task Execution(任务执行)**
|
||||
- Agent Type: `TASK_EXECUTION`
|
||||
- 输入:IntentResult.execution_prompt + IntentResult.intent_analysis
|
||||
- 职责:
|
||||
- 执行复杂任务(查询数据库、调用工具、多步骤推理)
|
||||
- 返回结构化执行结果
|
||||
- 输出结构(最小化设计):
|
||||
```python
|
||||
class ExecutionResult(BaseModel):
|
||||
execution_summary: str # 任务执行摘要(用于调试/审计)
|
||||
organization_prompt: str # 给 organization 阶段的提示词
|
||||
execution_data: dict = {} # 执行结果的结构化数据
|
||||
```
|
||||
- 输出约束:使用 `output_pydantic=ExecutionResult`
|
||||
- **落库策略**:落库到 messages 表,但重建会话时不展示
|
||||
|
||||
**3. Result Reporting(结果报告)**
|
||||
- Agent Type: `RESULT_REPORTING`
|
||||
- 输入:
|
||||
- IntentResult(意图识别结果)
|
||||
- ExecutionResult(任务执行情况)
|
||||
- 职责:
|
||||
- 结合意图分析和执行结果,格式化为用户友好的响应
|
||||
- 应用个性化模板(基于 UserAgentContext)
|
||||
- 输出结构(最小化设计):
|
||||
```python
|
||||
class OrganizationResult(BaseModel):
|
||||
assistant_text: str # 最终回复文本
|
||||
response_metadata: dict = {} # 响应元数据(可选)
|
||||
```
|
||||
- 输出约束:使用 `output_pydantic=OrganizationResult`
|
||||
- **唯一展示阶段**:重建会话时只展示此阶段的 message
|
||||
- **唯一转发阶段**:只有此阶段的输出需要通过 AG-UI 事件转发
|
||||
|
||||
### Flow 状态管理
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class FlowState:
|
||||
user_input: str
|
||||
context: UserAgentContext
|
||||
stage_trace: list[str] = field(default_factory=list)
|
||||
intent_result: IntentResult | None = None
|
||||
execution_result: ExecutionResult | None = None
|
||||
organization_result: OrganizationResult | None = None
|
||||
assistant_text: str = ""
|
||||
tokens: dict = field(default_factory=dict)
|
||||
cost: Decimal = Decimal("0")
|
||||
```
|
||||
|
||||
### 数据流向
|
||||
|
||||
```
|
||||
User Input + UserAgentContext
|
||||
↓
|
||||
@start() begin()
|
||||
↓
|
||||
@listen() intent_stage()
|
||||
├─ IntentResult.direct_answer=true
|
||||
│ ↓
|
||||
│ 跳过 execution,直接 organization
|
||||
│ ↓
|
||||
│ organization_stage(IntentResult.next_stage_prompt, IntentResult.metadata)
|
||||
│ ↓
|
||||
│ OrganizationResult → AG-UI 事件 + 落库
|
||||
│
|
||||
└─ IntentResult.direct_answer=false
|
||||
↓
|
||||
execution_stage(IntentResult.next_stage_prompt, IntentResult.metadata)
|
||||
↓
|
||||
ExecutionResult
|
||||
↓
|
||||
organization_stage(ExecutionResult.next_stage_prompt, ExecutionResult.metadata)
|
||||
↓
|
||||
OrganizationResult → AG-UI 事件 + 落库
|
||||
```
|
||||
|
||||
### 三阶段输出约束
|
||||
|
||||
**所有阶段使用 `output_pydantic` 约束输出:**
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
|
||||
class IntentResult(BaseModel):
|
||||
direct_answer: bool
|
||||
next_stage_prompt: str
|
||||
metadata: dict = {}
|
||||
|
||||
class ExecutionResult(BaseModel):
|
||||
next_stage_prompt: str
|
||||
metadata: dict = {}
|
||||
|
||||
class OrganizationResult(BaseModel):
|
||||
assistant_text: str
|
||||
metadata: dict = {}
|
||||
|
||||
# Task 定义
|
||||
intent_task = Task(
|
||||
description="Analyze user intent",
|
||||
expected_output="Intent analysis",
|
||||
agent=intent_agent,
|
||||
output_pydantic=IntentResult,
|
||||
)
|
||||
|
||||
execution_task = Task(
|
||||
description="Execute tasks",
|
||||
expected_output="Execution result",
|
||||
agent=execution_agent,
|
||||
output_pydantic=ExecutionResult,
|
||||
)
|
||||
|
||||
organization_task = Task(
|
||||
description="Format response",
|
||||
expected_output="User-friendly response",
|
||||
agent=organization_agent,
|
||||
output_pydantic=OrganizationResult,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 系统选模逻辑设计
|
||||
|
||||
### 问题背景
|
||||
|
||||
旧逻辑:`order_by(...).limit(1)` 随机选择一个系统 agent,不区分阶段。
|
||||
|
||||
新逻辑:按 `agent_type` 显式映射到三阶段。
|
||||
|
||||
### 选模规则
|
||||
|
||||
**必需的 Agent Types:**
|
||||
- `INTENT_RECOGNITION` → 用于 intent_stage
|
||||
- `TASK_EXECUTION` → 用于 execution_stage
|
||||
- `RESULT_REPORTING` → 用于 organization_stage
|
||||
|
||||
**查询逻辑:**
|
||||
|
||||
```python
|
||||
REQUIRED_TYPES = {"INTENT_RECOGNITION", "TASK_EXECUTION", "RESULT_REPORTING"}
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StageModels:
|
||||
intent: SystemAgentCatalog
|
||||
execution: SystemAgentCatalog
|
||||
organization: SystemAgentCatalog
|
||||
|
||||
def resolve_stage_models(rows: list[SystemAgentCatalog]) -> StageModels:
|
||||
by_type = {row.agent_type: row for row in rows}
|
||||
missing = REQUIRED_TYPES - set(by_type.keys())
|
||||
if missing:
|
||||
raise ValueError(f"Missing required agent types: {missing}")
|
||||
|
||||
return StageModels(
|
||||
intent=by_type["INTENT_RECOGNITION"],
|
||||
execution=by_type["TASK_EXECUTION"],
|
||||
organization=by_type["RESULT_REPORTING"],
|
||||
)
|
||||
```
|
||||
|
||||
**初始化数据约束:**
|
||||
- `system_agents` 表必须包含三种类型的记录
|
||||
- 运行时启动时验证完整性
|
||||
|
||||
---
|
||||
|
||||
## 人民币结算策略设计
|
||||
|
||||
### 设计原则
|
||||
|
||||
1. **保留 LiteLLM 语义**:`completion_cost()` 始终返回 USD
|
||||
2. **业务层映射**:根据用户国家(`profiles.settings.preferences.country`)决定落库货币
|
||||
3. **默认人民币**:中国用户或无国家信息默认 CNY
|
||||
4. **汇率配置**:USD/CNY 汇率通过环境变量配置
|
||||
|
||||
### 货币来源
|
||||
|
||||
```
|
||||
UserAgentContext.settings.preferences.country
|
||||
↓
|
||||
resolve_billing_currency(country)
|
||||
↓
|
||||
CN → CNY
|
||||
US → USD
|
||||
其他 → USD
|
||||
```
|
||||
|
||||
### 结算流程
|
||||
|
||||
```
|
||||
LiteLLM completion_cost()
|
||||
↓ (USD)
|
||||
resolve_billing_cost(usd_cost, country)
|
||||
↓
|
||||
├─ country="CN" or None → CNY (乘以汇率)
|
||||
└─ country="US" → USD (保持原值)
|
||||
↓
|
||||
messages.cost + messages.currency
|
||||
sessions.total_cost (同一货币)
|
||||
```
|
||||
|
||||
### 汇率配置
|
||||
|
||||
```python
|
||||
# 环境变量
|
||||
BILLING_USD_CNY_RATE=7.2
|
||||
|
||||
# 默认值
|
||||
DEFAULT_USD_CNY_RATE = Decimal("7.2")
|
||||
```
|
||||
|
||||
### 结算模型
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class BillingCost:
|
||||
currency: str # "CNY" or "USD"
|
||||
cost: Decimal # 6位小数精度
|
||||
|
||||
def resolve_billing_cost(
|
||||
usd_cost: Decimal,
|
||||
country: str | None,
|
||||
usd_cny_rate: Decimal = DEFAULT_USD_CNY_RATE,
|
||||
) -> BillingCost:
|
||||
currency = "CNY" if (country or "CN").upper() == "CN" else "USD"
|
||||
if currency == "CNY":
|
||||
cost = usd_cost * usd_cny_rate
|
||||
else:
|
||||
cost = usd_cost
|
||||
return BillingCost(
|
||||
currency=currency,
|
||||
cost=cost.quantize(Decimal("0.000001"))
|
||||
)
|
||||
```
|
||||
|
||||
### 数据库落库
|
||||
|
||||
**messages 表:**
|
||||
- `cost`: NUMERIC(12,6) - 业务货币金额
|
||||
- `currency`: VARCHAR(3) - "CNY" or "USD"
|
||||
|
||||
**sessions 表:**
|
||||
- `total_cost`: NUMERIC(12,6) - 同一货币累计
|
||||
|
||||
**约束:**
|
||||
- 同一 session 内所有 messages 的 currency 必须一致
|
||||
- sessions.total_cost 累加时保持货币一致
|
||||
|
||||
---
|
||||
|
||||
## Session 状态一致性设计
|
||||
|
||||
### 问题背景
|
||||
|
||||
旧逻辑:
|
||||
- `sessions.status` 与 `state_snapshot.status` 不同步
|
||||
- 失败时状态不一致
|
||||
- title 未自动赋值
|
||||
|
||||
### 状态机
|
||||
|
||||
```
|
||||
pending (创建)
|
||||
↓
|
||||
running (开始执行)
|
||||
↓
|
||||
├─ completed (成功)
|
||||
└─ failed (异常)
|
||||
```
|
||||
|
||||
### 状态同步规则
|
||||
|
||||
**创建时:**
|
||||
```python
|
||||
session = AgentChatSession(
|
||||
user_id=user_uuid,
|
||||
status=AgentChatSessionStatus.PENDING,
|
||||
state_snapshot={
|
||||
"status": "pending",
|
||||
"pending_tool_call_id": None,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**运行时:**
|
||||
```python
|
||||
# 开始执行
|
||||
session.status = AgentChatSessionStatus.RUNNING
|
||||
session.state_snapshot["status"] = "running"
|
||||
|
||||
# 成功完成
|
||||
session.status = AgentChatSessionStatus.COMPLETED
|
||||
session.state_snapshot["status"] = "completed"
|
||||
|
||||
# 失败
|
||||
session.status = AgentChatSessionStatus.FAILED
|
||||
session.state_snapshot["status"] = "failed"
|
||||
session.state_snapshot["error_id"] = error_id
|
||||
```
|
||||
|
||||
### 自动 Title 赋值
|
||||
|
||||
**规则:**
|
||||
- 首次运行时,如果 `session.title` 为空,使用 `user_input[:255]` 赋值
|
||||
- 只在第一次运行时赋值,后续不覆盖
|
||||
|
||||
**实现:**
|
||||
```python
|
||||
async def _set_title_if_empty(self, session_id: UUID, title: str) -> None:
|
||||
stmt = (
|
||||
update(AgentChatSession)
|
||||
.where(AgentChatSession.id == session_id)
|
||||
.where(AgentChatSession.title.is_(None))
|
||||
.values(title=title[:255])
|
||||
)
|
||||
await self.db.execute(stmt)
|
||||
```
|
||||
|
||||
### Repository 方法
|
||||
|
||||
```python
|
||||
class SessionRepository:
|
||||
async def mark_running(self, session_id: UUID) -> None: ...
|
||||
async def mark_completed(self, session_id: UUID) -> None: ...
|
||||
async def mark_failed(self, session_id: UUID, error_id: str) -> None: ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 全局 Prompt 构建设计
|
||||
|
||||
### 分层结构
|
||||
|
||||
```
|
||||
全局系统 Prompt
|
||||
├─ 身份段(username/bio)
|
||||
├─ 偏好段(language/timezone/country)
|
||||
└─ 阶段段(动态注入)
|
||||
├─ intent stage prompt
|
||||
├─ execution stage prompt
|
||||
└─ organization stage prompt
|
||||
```
|
||||
|
||||
### 构建函数
|
||||
|
||||
```python
|
||||
def build_global_system_prompt(ctx: UserAgentContext) -> str:
|
||||
lines = [
|
||||
"# User Identity",
|
||||
f"username: {ctx.username}",
|
||||
f"bio: {ctx.bio or 'N/A'}",
|
||||
"",
|
||||
"# User Preferences",
|
||||
f"interface_language: {ctx.settings.preferences.interface_language}",
|
||||
f"ai_language: {ctx.settings.preferences.ai_language}",
|
||||
f"timezone: {ctx.settings.preferences.timezone}",
|
||||
f"country: {ctx.settings.preferences.country}",
|
||||
"",
|
||||
"# Instructions",
|
||||
"Use the user's preferences to personalize responses.",
|
||||
"Respond in the user's preferred AI language.",
|
||||
"Consider the user's timezone for time-related queries.",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
```
|
||||
|
||||
### 阶段注入
|
||||
|
||||
每个阶段运行时,在全局 prompt 基础上追加阶段特定的指令:
|
||||
|
||||
```python
|
||||
def build_stage_prompt(
|
||||
base_prompt: str,
|
||||
stage: str, # "intent" | "execution" | "organization"
|
||||
ctx: UserAgentContext,
|
||||
) -> str:
|
||||
stage_prompts = {
|
||||
"intent": "Analyze the user's intent and decide if direct response is possible.",
|
||||
"execution": "Execute the required tasks and tools to fulfill the user's request.",
|
||||
"organization": "Format the execution results into a user-friendly response.",
|
||||
}
|
||||
return f"{base_prompt}\n\n# Stage: {stage}\n{stage_prompts[stage]}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 依赖关系图
|
||||
|
||||
```
|
||||
UserAgentContext (核心上下文)
|
||||
↓
|
||||
├─ ProfileSettings (用户配置)
|
||||
│ └─ preferences.country → 人民币结算
|
||||
│
|
||||
├─ build_global_system_prompt() (全局 Prompt)
|
||||
│ └─ 三阶段 Flow 使用
|
||||
│
|
||||
└─ resolve_stage_models() (选模逻辑)
|
||||
└─ 三阶段 Agent 配置
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Runtime Database Schema](../runtime/runtime-database.md)
|
||||
- [AG-UI Protocol](.opencode/skills/ag-ui/SKILL.md)
|
||||
- [CrewAI Framework](.opencode/skills/crewai/SKILL.md)
|
||||
Reference in New Issue
Block a user