Merge pull request 'fix:Jev 观测改为 evaluator,会话视图不再被 JSON 分类结果占位' (#14) from feat/0.3.0 into main
my-v0-server / deploy (push) Successful in 45s
my-v0-server / deploy (push) Successful in 45s
Reviewed-on: #14
This commit was merged in pull request #14.
This commit is contained in:
@@ -63,8 +63,8 @@ class DecisionEngine:
|
|||||||
from app.observability.tracing import observe_decision, record_decision
|
from app.observability.tracing import observe_decision, record_decision
|
||||||
|
|
||||||
# 观测包在请求外面:成功才记结果,失败仍抛原来的 DecisionError
|
# 观测包在请求外面:成功才记结果,失败仍抛原来的 DecisionError
|
||||||
# generation 为 None 时 record_decision 什么都不做
|
# observation 为 None 时 record_decision 什么都不做
|
||||||
with observe_decision(request) as generation:
|
with observe_decision(request) as observation:
|
||||||
try:
|
try:
|
||||||
response = await client.system_one(
|
response = await client.system_one(
|
||||||
state=request.state,
|
state=request.state,
|
||||||
@@ -93,7 +93,7 @@ class DecisionEngine:
|
|||||||
except TypeSafeError as error:
|
except TypeSafeError as error:
|
||||||
raise DecisionError("Jev SDK 调用失败", kind="sdk") from error
|
raise DecisionError("Jev SDK 调用失败", kind="sdk") from error
|
||||||
parsed = self._parse_response(request, response)
|
parsed = self._parse_response(request, response)
|
||||||
record_decision(generation, parsed)
|
record_decision(observation, parsed)
|
||||||
return parsed
|
return parsed
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""一次大屏图调用的 Langfuse 追踪
|
"""一次大屏图调用的 Langfuse 追踪
|
||||||
|
|
||||||
不属于处理管道的任一步。把图调用收成可过滤的 trace,并把 Jev 记成当前节点下的 generation
|
不属于处理管道的任一步。把图调用收成可过滤的 trace,并把 Jev 记成当前节点下的 evaluator
|
||||||
每轮调用单独记录,首次请求与澄清恢复通过 session 关联,不修改业务状态
|
每轮调用单独记录,首次请求与澄清恢复通过 session 关联,不修改业务状态
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ def _screen_trace(payload, thread_id, session_id, user_id=None):
|
|||||||
|
|
||||||
# 明确回调所属项目,避免进程中存在多个客户端时跳过追踪
|
# 明确回调所属项目,避免进程中存在多个客户端时跳过追踪
|
||||||
handler = CallbackHandler(public_key=os.getenv("LANGFUSE_PUBLIC_KEY"))
|
handler = CallbackHandler(public_key=os.getenv("LANGFUSE_PUBLIC_KEY"))
|
||||||
# 在当前异步上下文执行回调,手动记录的 Jev generation 才会挂到所属节点。
|
# 在当前异步上下文执行回调,手动记录的 Jev 观测才会挂到所属节点。
|
||||||
handler.run_inline = True
|
handler.run_inline = True
|
||||||
resume = isinstance(payload, Command) and payload.resume is not None
|
resume = isinstance(payload, Command) and payload.resume is not None
|
||||||
trace_input = (
|
trace_input = (
|
||||||
@@ -162,9 +162,10 @@ def _run_output(state: dict[str, Any]) -> dict[str, Any]:
|
|||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def observe_decision(request: Any) -> Iterator[Any]:
|
def observe_decision(request: Any) -> Iterator[Any]:
|
||||||
"""在当前节点下面记一次 Jev 调用,yield 的是 generation,未追踪时是 None
|
"""在当前节点下面记一次 Jev 调用,yield 的是观测对象,未追踪时是 None
|
||||||
|
|
||||||
TypeSafe 不走 LangChain callback,所以要手记。类型必须是 generation,面板才能算模型和 token
|
TypeSafe 不走 LangChain callback,所以要手记。类型不能是 generation:会话视图只用 generation
|
||||||
|
拼对话,且只显示最浅的一条对话线,Jev 比 agent 的模型调用浅,用 generation 会把真正的对话挤掉
|
||||||
只有图回调已经挂上当前 span 时才记。没有父 span 就跳过,避免 Jev 单独变成一条 trace
|
只有图回调已经挂上当前 span 时才记。没有父 span 就跳过,避免 Jev 单独变成一条 trace
|
||||||
这里不吞异常:失败时标 ERROR 后原样抛出,调用方的 DecisionError 类型不变
|
这里不吞异常:失败时标 ERROR 后原样抛出,调用方的 DecisionError 类型不变
|
||||||
"""
|
"""
|
||||||
@@ -177,7 +178,7 @@ def observe_decision(request: Any) -> Iterator[Any]:
|
|||||||
name = "classify-intent" if set(request.decisions) == {"intent"} else "run-decision"
|
name = "classify-intent" if set(request.decisions) == {"intent"} else "run-decision"
|
||||||
# 复用本应用已经选定的客户端
|
# 复用本应用已经选定的客户端
|
||||||
with client.start_as_current_observation(
|
with client.start_as_current_observation(
|
||||||
as_type="generation",
|
as_type="evaluator",
|
||||||
name=name,
|
name=name,
|
||||||
model=model,
|
model=model,
|
||||||
input={
|
input={
|
||||||
@@ -187,11 +188,11 @@ def observe_decision(request: Any) -> Iterator[Any]:
|
|||||||
},
|
},
|
||||||
"state": request.state,
|
"state": request.state,
|
||||||
},
|
},
|
||||||
) as generation:
|
) as observation:
|
||||||
try:
|
try:
|
||||||
yield generation
|
yield observation
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
generation.update(
|
observation.update(
|
||||||
level="ERROR",
|
level="ERROR",
|
||||||
status_message=str(error),
|
status_message=str(error),
|
||||||
metadata=_error_metadata(error),
|
metadata=_error_metadata(error),
|
||||||
@@ -199,29 +200,26 @@ def observe_decision(request: Any) -> Iterator[Any]:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def record_decision(generation: Any, response: Any) -> None:
|
def record_decision(observation: Any, response: Any) -> None:
|
||||||
"""把成功的 Jev 结果写回刚才那条 generation
|
"""把成功的 Jev 结果写回刚才那条观测
|
||||||
|
|
||||||
generation 为 None 表示这次没追踪,直接返回,不影响判断结果
|
observation 为 None 表示这次没追踪,直接返回,不影响判断结果
|
||||||
只写选项、置信度和 token。不写原始 HTTP 响应
|
只写选项、置信度和 token。不写原始 HTTP 响应
|
||||||
"""
|
"""
|
||||||
if generation is None:
|
if observation is None:
|
||||||
return
|
return
|
||||||
usage: dict[str, int] = {}
|
# evaluator 没有 model/usage 字段,模型和 token 只能放 metadata
|
||||||
|
metadata = {"model": response.model, "request_id": response.request_id or ""}
|
||||||
if response.usage.input_tokens is not None:
|
if response.usage.input_tokens is not None:
|
||||||
usage["input_tokens"] = response.usage.input_tokens
|
metadata["input_tokens"] = str(response.usage.input_tokens)
|
||||||
if response.usage.output_tokens is not None:
|
if response.usage.output_tokens is not None:
|
||||||
usage["output_tokens"] = response.usage.output_tokens
|
metadata["output_tokens"] = str(response.usage.output_tokens)
|
||||||
|
|
||||||
generation.update(
|
observation.update(
|
||||||
model=response.model,
|
|
||||||
output={
|
output={
|
||||||
name: _result_summary(result) for name, result in response.results.items()
|
name: _result_summary(result) for name, result in response.results.items()
|
||||||
},
|
},
|
||||||
usage_details=usage or None,
|
metadata=metadata,
|
||||||
metadata={
|
|
||||||
"request_id": response.request_id or "",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user